feat(session): persist handoff notes on end
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
6cb5a9f39a
commit
ff24898cd4
6 changed files with 77 additions and 7 deletions
|
|
@ -16,10 +16,11 @@ use Core\Mod\Agentic\Models\AgentSession;
|
|||
use Core\Mod\Agentic\Services\AgentSessionService;
|
||||
|
||||
/**
|
||||
* End an agent session with a final status and optional summary.
|
||||
* End an agent session with a final status, summary, and optional handoff notes.
|
||||
*
|
||||
* Usage:
|
||||
* $session = EndSession::run('ses_abc123', 'completed', 'All phases done');
|
||||
* $session = EndSession::run('ses_abc123', 'handed_off', 'Ready for review', ['summary' => 'Ready for review']);
|
||||
*/
|
||||
class EndSession
|
||||
{
|
||||
|
|
@ -32,7 +33,12 @@ class EndSession
|
|||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function handle(string $sessionId, string $status, ?string $summary = null): AgentSession
|
||||
public function handle(
|
||||
string $sessionId,
|
||||
string $status,
|
||||
?string $summary = null,
|
||||
?array $handoffNotes = null,
|
||||
): AgentSession
|
||||
{
|
||||
if ($sessionId === '') {
|
||||
throw new \InvalidArgumentException('session_id is required');
|
||||
|
|
@ -45,7 +51,7 @@ class EndSession
|
|||
);
|
||||
}
|
||||
|
||||
$session = $this->sessionService->end($sessionId, $status, $summary);
|
||||
$session = $this->sessionService->end($sessionId, $status, $summary, $handoffNotes);
|
||||
|
||||
if (! $session) {
|
||||
throw new \InvalidArgumentException("Session not found: {$sessionId}");
|
||||
|
|
|
|||
|
|
@ -122,16 +122,24 @@ class SessionController extends Controller
|
|||
$validated = $request->validate([
|
||||
'status' => 'required|string|in:completed,handed_off,paused,failed',
|
||||
'summary' => 'nullable|string|max:10000',
|
||||
'handoff_notes' => 'nullable|array',
|
||||
]);
|
||||
|
||||
try {
|
||||
$session = EndSession::run($id, $validated['status'], $validated['summary'] ?? null);
|
||||
$session = EndSession::run(
|
||||
$id,
|
||||
$validated['status'],
|
||||
$validated['summary'] ?? null,
|
||||
$validated['handoff_notes'] ?? null,
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'data' => [
|
||||
'session_id' => $session->session_id,
|
||||
'status' => $session->status,
|
||||
'duration' => $session->getDurationFormatted(),
|
||||
'final_summary' => $session->final_summary,
|
||||
'handoff_notes' => $session->handoff_notes,
|
||||
],
|
||||
]);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ class SessionEnd extends AgentTool
|
|||
'type' => 'string',
|
||||
'description' => 'Final summary',
|
||||
],
|
||||
'handoff_notes' => [
|
||||
'type' => 'object',
|
||||
'description' => 'Optional handoff details for the next agent',
|
||||
],
|
||||
],
|
||||
'required' => ['status'],
|
||||
];
|
||||
|
|
@ -57,6 +61,7 @@ class SessionEnd extends AgentTool
|
|||
$sessionId,
|
||||
$args['status'] ?? '',
|
||||
$args['summary'] ?? null,
|
||||
$args['handoff_notes'] ?? null,
|
||||
);
|
||||
|
||||
return $this->success([
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ class AgentSession extends Model
|
|||
/**
|
||||
* End the session with a status.
|
||||
*/
|
||||
public function end(string $status, ?string $summary = null): self
|
||||
public function end(string $status, ?string $summary = null, ?array $handoffNotes = null): self
|
||||
{
|
||||
$validStatuses = [self::STATUS_COMPLETED, self::STATUS_FAILED, self::STATUS_HANDED_OFF];
|
||||
|
||||
|
|
@ -254,6 +254,7 @@ class AgentSession extends Model
|
|||
$this->update([
|
||||
'status' => $status,
|
||||
'final_summary' => $summary,
|
||||
'handoff_notes' => $handoffNotes ?? $this->handoff_notes,
|
||||
'ended_at' => now(),
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -125,7 +125,12 @@ class AgentSessionService
|
|||
/**
|
||||
* End a session.
|
||||
*/
|
||||
public function end(string $sessionId, string $status = AgentSession::STATUS_COMPLETED, ?string $summary = null): ?AgentSession
|
||||
public function end(
|
||||
string $sessionId,
|
||||
string $status = AgentSession::STATUS_COMPLETED,
|
||||
?string $summary = null,
|
||||
?array $handoffNotes = null
|
||||
): ?AgentSession
|
||||
{
|
||||
$session = $this->get($sessionId);
|
||||
|
||||
|
|
@ -133,7 +138,7 @@ class AgentSessionService
|
|||
return null;
|
||||
}
|
||||
|
||||
$session->end($status, $summary);
|
||||
$session->end($status, $summary, $handoffNotes);
|
||||
|
||||
// Remove from active cache
|
||||
$this->clearCachedSession($session);
|
||||
|
|
|
|||
45
php/tests/Feature/SessionControllerTest.php
Normal file
45
php/tests/Feature/SessionControllerTest.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Core\Mod\Agentic\Controllers\Api\SessionController;
|
||||
use Core\Mod\Agentic\Models\AgentSession;
|
||||
use Core\Tenant\Models\Workspace;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
it('ends a session with handoff notes', function (): void {
|
||||
$workspace = Workspace::factory()->create();
|
||||
$session = AgentSession::factory()->active()->create([
|
||||
'workspace_id' => $workspace->id,
|
||||
]);
|
||||
|
||||
$request = Request::create('/v1/sessions/'.$session->session_id.'/end', 'POST', [
|
||||
'status' => 'handed_off',
|
||||
'summary' => 'Ready for review',
|
||||
'handoff_notes' => [
|
||||
'summary' => 'Ready for review',
|
||||
'next_steps' => ['Run the verifier'],
|
||||
'blockers' => ['Need approval'],
|
||||
'context_for_next' => ['repo' => 'core/go-io'],
|
||||
],
|
||||
]);
|
||||
$request->attributes->set('workspace', $workspace);
|
||||
|
||||
$response = app(SessionController::class)->end($request, $session->session_id);
|
||||
|
||||
expect($response->getStatusCode())->toBe(200);
|
||||
|
||||
$payload = $response->getData(true);
|
||||
|
||||
expect($payload['data']['session_id'])->toBe($session->session_id)
|
||||
->and($payload['data']['status'])->toBe(AgentSession::STATUS_HANDED_OFF)
|
||||
->and($payload['data']['final_summary'])->toBe('Ready for review')
|
||||
->and($payload['data']['handoff_notes']['summary'])->toBe('Ready for review')
|
||||
->and($payload['data']['handoff_notes']['next_steps'])->toBe(['Run the verifier']);
|
||||
|
||||
$fresh = $session->fresh();
|
||||
|
||||
expect($fresh?->status)->toBe(AgentSession::STATUS_HANDED_OFF)
|
||||
->and($fresh?->final_summary)->toBe('Ready for review')
|
||||
->and($fresh?->handoff_notes['context_for_next'])->toBe(['repo' => 'core/go-io']);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue