fix(runtime): add queued agent run state
This commit is contained in:
parent
152b294d38
commit
ff8c5eb704
@ -246,17 +246,24 @@ These should be resolved before claiming the ecosystem docs are fully implementa
|
||||
- `b8242b4` adds `GET /api/agent-runtime/actions` with canonical `AgentActionLog` projection
|
||||
- FlowMonk local installs now resolve `@bytelyst/*` from the sibling `learning_ai_common_plat` workspace instead of the dead localhost registry
|
||||
- `1ccafa7` adds FlowMonk direct runtime projections for sessions, tasks, runs, action logs, and dispatch validation
|
||||
- [x] promote `queued` to a first-class `AgentRun` state and preserve it in shared runtime projections
|
||||
Commits:
|
||||
- `pending current commit`
|
||||
Status note:
|
||||
- `AgentRun` now supports `queued` directly in `@bytelyst/events`
|
||||
- platform-service now preserves queued platform runs as `queued`
|
||||
- cowork-service now projects pending IPC tasks as queued runs
|
||||
- FlowMonk now projects scheduled entries as queued runs
|
||||
|
||||
### 6.1 Remaining Direct Runtime TODOs
|
||||
|
||||
- Cowork: add `AgentTodo` direct projection once the product exposes first-class todo entities.
|
||||
- Cowork: attach canonical event IDs to approval and audit trails so ActionTrail lineage can stop using fallback/null semantics.
|
||||
- FlowMonk: add direct `AgentApprovalCheckpoint` and `AgentTodo` projections once the product exposes first-class approval/todo primitives.
|
||||
- Platform-service: refine the `queued -> paused` projection fallback once run-vs-session semantics are finalized.
|
||||
- Shared docs: clarify run-vs-session lifecycle guidance now that `queued` is first-class.
|
||||
|
||||
### 6.2 Explicit Blockers And Questions
|
||||
|
||||
- Question: should the shared runtime contract add a first-class `queued` run state rather than continuing the current `queued -> paused` fallback projection?
|
||||
- Question: should Cowork approval/audit records emit canonical event IDs from Rust so runtime projections and ActionTrail lineage can share the same identifiers?
|
||||
|
||||
---
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Phase 5 Execution Plan
|
||||
|
||||
> **Flow:** Shared agent runtime contract baseline
|
||||
> **Status:** Baseline implemented, Cowork product integration implemented, FlowMonk follow-up pending
|
||||
> **Status:** Baseline implemented, Cowork product integration implemented, FlowMonk product integration implemented
|
||||
> **Owner:** `learning_ai_common_plat`
|
||||
> **Purpose:** Turn the runtime contract draft into concrete schemas for sessions, tasks, todos, runs, approvals, dispatch, and action logs.
|
||||
|
||||
@ -98,14 +98,15 @@ Observed baseline:
|
||||
- `01201f8` cowork-service runtime task projection
|
||||
- `b8242b4` cowork-service runtime action-log projection
|
||||
- `1ccafa7` FlowMonk local shared-package resolution + runtime projection routes
|
||||
- `a3ae6fe` FlowMonk queued-run projection preservation
|
||||
- `pending current commit` shared runtime queued-run state
|
||||
|
||||
## 7. Remaining Gaps
|
||||
|
||||
- Cowork now emits shared runtime projections from cowork-service, but Rust-side canonical event IDs are still missing on approval/audit records and `AgentTodo` still has no first-class product source.
|
||||
- FlowMonk now emits direct runtime projections for planning sessions, tasks, runs, and action logs, but it still has no first-class approval checkpoint or todo primitive.
|
||||
- run-vs-session semantics for queued work still need a stricter mapping than the current projection fallback.
|
||||
- run-vs-session semantics for queued work now preserve `queued` directly in the shared runtime contract, but broader session/run lifecycle guidance still needs to be documented.
|
||||
|
||||
## 8. Explicit Blockers And Questions
|
||||
|
||||
- Question: should queued work remain represented as `paused`, or should the shared runtime contract gain a first-class `queued` run state?
|
||||
- Question: should Cowork approval and audit records start emitting canonical event IDs from Rust so ActionTrail and runtime lineage can share the same identifiers?
|
||||
|
||||
@ -117,4 +117,19 @@ describe('agent runtime contract baseline', () => {
|
||||
expect(todo.status).toBe('in-progress');
|
||||
expect(actionLog.eventName).toBe('agent.run.started');
|
||||
});
|
||||
|
||||
it('accepts queued runs as a first-class runtime state', () => {
|
||||
const run = AgentRunSchema.parse({
|
||||
runId: 'run_queued_1',
|
||||
sessionId: 'sess_queued_1',
|
||||
productId: 'clawcowork',
|
||||
status: 'queued',
|
||||
startedAt: '2026-04-04T10:00:00.000Z',
|
||||
completedAt: null,
|
||||
checkpointArtifactId: null,
|
||||
correlationId: 'corr_queued_1',
|
||||
});
|
||||
|
||||
expect(run.status).toBe('queued');
|
||||
});
|
||||
});
|
||||
|
||||
@ -63,6 +63,7 @@ export const AgentTodoSchema = z.object({
|
||||
});
|
||||
|
||||
export const AgentRunStatusSchema = z.enum([
|
||||
'queued',
|
||||
'running',
|
||||
'paused',
|
||||
'waiting-approval',
|
||||
|
||||
@ -100,6 +100,31 @@ describe('agent runtime routes', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('projects pending IPC tasks into queued AgentRun objects', async () => {
|
||||
call.mockResolvedValue({
|
||||
result: {
|
||||
tasks: [
|
||||
{
|
||||
id: 'task-queued',
|
||||
status: 'pending',
|
||||
createdAt: '2026-04-04T08:00:00.000Z',
|
||||
updatedAt: '2026-04-04T08:10:00.000Z',
|
||||
sessionId: 'sess-queued',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const res = await app.inject({ method: 'GET', url: '/api/agent-runtime/runs' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = JSON.parse(res.payload);
|
||||
expect(body.runs[0]).toMatchObject({
|
||||
runId: 'task-queued',
|
||||
sessionId: 'sess-queued',
|
||||
status: 'queued',
|
||||
});
|
||||
});
|
||||
|
||||
it('projects IPC tasks into shared AgentTask objects', async () => {
|
||||
call.mockResolvedValue({
|
||||
result: {
|
||||
|
||||
@ -30,6 +30,8 @@ function asIsoString(value: unknown, fallback: string): string {
|
||||
|
||||
function mapTaskStatus(status: unknown): AgentRun['status'] {
|
||||
switch (status) {
|
||||
case 'pending':
|
||||
return 'queued';
|
||||
case 'running':
|
||||
return 'running';
|
||||
case 'completed':
|
||||
@ -38,7 +40,6 @@ function mapTaskStatus(status: unknown): AgentRun['status'] {
|
||||
return 'failed';
|
||||
case 'cancelled':
|
||||
return 'cancelled';
|
||||
case 'pending':
|
||||
default:
|
||||
return 'paused';
|
||||
}
|
||||
|
||||
@ -101,6 +101,38 @@ describe('agentRuntimeRoutes', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('GET /agent-runtime/runs preserves queued runs as queued', async () => {
|
||||
runRepoMock.listRuns.mockResolvedValue([
|
||||
{
|
||||
id: 'run_queued',
|
||||
productId: 'lysnrai',
|
||||
kind: 'agent',
|
||||
name: 'queued-run',
|
||||
source: 'dispatch',
|
||||
status: 'queued',
|
||||
createdAt: '2026-04-04T18:00:00.000Z',
|
||||
updatedAt: '2026-04-04T18:10:00.000Z',
|
||||
metadata: {
|
||||
sessionId: 'sess_queued',
|
||||
},
|
||||
},
|
||||
]);
|
||||
const app = await buildApp({ sub: 'admin_1', productId: 'lysnrai', role: 'admin' });
|
||||
|
||||
const res = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/api/agent-runtime/runs?limit=10',
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = JSON.parse(res.body);
|
||||
expect(body.runs[0]).toMatchObject({
|
||||
runId: 'run_queued',
|
||||
sessionId: 'sess_queued',
|
||||
status: 'queued',
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /agent-runtime/dispatch/validate validates the shared dispatch contract', async () => {
|
||||
const app = await buildApp({ sub: 'user_1', productId: 'lysnrai' });
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@ function mapSessionStatus(session: {
|
||||
|
||||
function mapRunStatus(status: string): AgentRun['status'] {
|
||||
switch (status) {
|
||||
case 'queued':
|
||||
return 'queued';
|
||||
case 'running':
|
||||
return 'running';
|
||||
case 'succeeded':
|
||||
@ -46,7 +48,6 @@ function mapRunStatus(status: string): AgentRun['status'] {
|
||||
return 'failed';
|
||||
case 'cancelled':
|
||||
return 'cancelled';
|
||||
case 'queued':
|
||||
default:
|
||||
return 'paused';
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user