@agentuity/coder 2.0.0-beta.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/protocol.ts CHANGED
@@ -94,7 +94,13 @@ export interface HubConfig {
94
94
 
95
95
  export interface InitMessage {
96
96
  type: 'init';
97
+ role: 'lead' | 'sub_agent' | 'controller';
97
98
  sessionId?: string;
99
+ resume?: {
100
+ sessionFile: string;
101
+ piSessionId?: string;
102
+ cwd?: string;
103
+ };
98
104
  tools?: HubToolDefinition[];
99
105
  commands?: HubCommandDefinition[];
100
106
  agents?: AgentDefinition[];
@@ -105,6 +111,7 @@ export interface InitMessage {
105
111
  };
106
112
  thinkingLevel?: string;
107
113
  task?: string;
114
+ agentRole?: string;
108
115
  }
109
116
 
110
117
  export interface EventRequest {
@@ -159,10 +166,15 @@ export interface PingMessage {
159
166
  timestamp: number;
160
167
  }
161
168
 
169
+ export interface BootstrapReadyMessage {
170
+ type: 'bootstrap_ready';
171
+ }
172
+
162
173
  export type HubClientMessage =
163
174
  | HubRequest
164
175
  | SessionEntryMessage
165
176
  | SessionWriteMessage
177
+ | BootstrapReadyMessage
166
178
  | RpcCommandMessage
167
179
  | RpcUiResponseMessage
168
180
  | PingMessage;
@@ -195,6 +207,14 @@ export interface ConnectionRejectedMessage {
195
207
  timestamp: number;
196
208
  }
197
209
 
210
+ export interface ProtocolErrorMessage {
211
+ type: 'protocol_error';
212
+ code: string;
213
+ message: string;
214
+ sessionId?: string;
215
+ timestamp: number;
216
+ }
217
+
198
218
  export interface ConversationEntry {
199
219
  type:
200
220
  | 'message'
@@ -202,6 +222,9 @@ export interface ConversationEntry {
202
222
  | 'tool_call'
203
223
  | 'tool_result'
204
224
  | 'task_result'
225
+ | 'runtime_status'
226
+ | 'runtime_output'
227
+ | 'runtime_preview'
205
228
  | 'turn'
206
229
  | 'user_prompt';
207
230
  agent?: string;
@@ -210,6 +233,29 @@ export interface ConversationEntry {
210
233
  toolName?: string;
211
234
  toolArgs?: Record<string, unknown>;
212
235
  toolCallId?: string;
236
+ runtime?:
237
+ | {
238
+ id?: string;
239
+ command?: string;
240
+ status?: string;
241
+ stream?: string;
242
+ exitCode?: number;
243
+ }
244
+ | undefined;
245
+ preview?:
246
+ | {
247
+ id?: string;
248
+ url?: string;
249
+ status?: string;
250
+ label?: string;
251
+ }
252
+ | undefined;
253
+ attachments?: Array<{
254
+ id?: string;
255
+ filename?: string;
256
+ mime?: string;
257
+ size?: number;
258
+ }>;
213
259
  isError?: boolean;
214
260
  taskId?: string;
215
261
  turnId?: string;
@@ -219,16 +265,28 @@ export interface ConversationEntry {
219
265
  timestamp: number;
220
266
  }
221
267
 
222
- export interface HydrationTaskState {
268
+ export interface SessionTaskState {
223
269
  taskId: string;
224
270
  agent: string;
225
271
  status: 'running' | 'completed' | 'failed';
226
272
  prompt: string;
273
+ startedAt?: string;
274
+ completedAt?: string;
227
275
  duration?: number;
228
276
  result?: string;
229
277
  error?: string;
230
278
  }
231
279
 
280
+ export type HydrationTaskState = SessionTaskState;
281
+
282
+ export interface SessionTaskProjection<TTaskState = SessionTaskState> {
283
+ tasks: TTaskState[];
284
+ }
285
+
286
+ export interface SessionStreamWorkExtension {
287
+ stream?: SessionStreamProjection;
288
+ }
289
+
232
290
  export interface SessionParticipant {
233
291
  id: string;
234
292
  role: 'lead' | 'observer' | 'controller';
@@ -247,59 +305,252 @@ export interface SessionStreamProjection extends SessionStreamBlock {
247
305
  tasks: Record<string, SessionStreamBlock>;
248
306
  }
249
307
 
250
- export interface SessionSnapshot {
308
+ export type WorkflowMode = 'standard' | 'loop';
309
+ export type LoopStatus =
310
+ | 'idle'
311
+ | 'starting'
312
+ | 'running'
313
+ | 'paused'
314
+ | 'awaiting_input'
315
+ | 'blocked'
316
+ | 'completed'
317
+ | 'failed'
318
+ | 'cancelled';
319
+
320
+ export interface SessionLoopState {
321
+ workflowMode: WorkflowMode;
322
+ loopId?: string;
323
+ status: LoopStatus;
324
+ goal?: string;
325
+ summary?: string;
326
+ nextAction?: string;
327
+ blockers: string[];
328
+ iteration: number;
329
+ maxIterations: number;
330
+ autoContinue: boolean;
331
+ allowDetached: boolean;
332
+ recoveryAttempts: number;
333
+ awaitingUser: boolean;
334
+ activePrdKey?: string;
335
+ activePrdTaskId?: string;
336
+ coordinationJobId?: string;
337
+ currentWaveId?: string;
338
+ currentWaveLabel?: string;
339
+ lastCheckpointSummary?: string;
340
+ startedAt?: number;
341
+ updatedAt?: number;
342
+ lastCheckpointAt?: number;
343
+ completedAt?: number;
344
+ lastError?: string;
345
+ }
346
+
347
+ export type SessionBucket = 'running' | 'paused' | 'provisioning' | 'history';
348
+
349
+ export interface SessionSkillRef {
350
+ skillId: string;
351
+ repo: string;
352
+ name?: string;
353
+ url?: string;
354
+ }
355
+
356
+ export interface SessionListDiagnostics {
357
+ inactiveRunningTasks: Array<{
358
+ taskId: string;
359
+ agent: string;
360
+ inactivityMs: number;
361
+ startedAt: string;
362
+ lastActivityAt?: string;
363
+ }>;
364
+ }
365
+
366
+ export interface SessionListItem {
251
367
  sessionId: string;
252
368
  label: string;
253
- status: 'active' | 'paused' | 'shutdown' | 'archived' | 'error' | 'stopped';
369
+ status: string;
370
+ mode: 'sandbox' | 'tui';
371
+ sessionKind?: string;
372
+ parentSessionId?: string;
373
+ coordinationJobId?: string;
374
+ workflowMode: WorkflowMode;
375
+ loopStatus?: SessionLoopState['status'];
376
+ loopIteration?: number;
377
+ createdAt: string;
378
+ taskCount: number;
379
+ subAgentCount: number;
380
+ observerCount: number;
381
+ participantCount: number;
382
+ tags: string[];
383
+ skills: SessionSkillRef[];
384
+ defaultAgent?: string;
385
+ bucket: SessionBucket;
386
+ runtimeAvailable: boolean;
387
+ controlAvailable: boolean;
388
+ historyOnly: boolean;
389
+ diagnostics?: SessionListDiagnostics;
390
+ }
391
+
392
+ export interface SandboxSessionListItem {
393
+ sessionId: string;
394
+ sandboxId: string;
395
+ status: string;
396
+ task?: string;
397
+ metadata?: Record<string, unknown>;
398
+ wsConnected: boolean;
399
+ }
400
+
401
+ export interface SessionListResponse {
402
+ sessions: {
403
+ websocket: SessionListItem[];
404
+ sandbox: SandboxSessionListItem[];
405
+ };
406
+ total: number;
407
+ }
408
+
409
+ export interface SessionDetailParticipant {
410
+ id: string;
411
+ role: string;
412
+ transport: string;
413
+ connectedAt: string;
414
+ idle?: boolean;
415
+ }
416
+
417
+ export interface SessionAgentActivity {
418
+ name?: string;
419
+ status: string;
420
+ currentTool?: string;
421
+ currentToolArgs?: string;
422
+ toolCallCount: number;
423
+ lastActivity: number;
424
+ totalElapsed?: number;
425
+ }
426
+
427
+ export interface SessionObservedProjection {
428
+ turnCount: number;
429
+ lastAgentModel?: string;
430
+ compactionCount: number;
431
+ }
432
+
433
+ export interface SessionDeckGenerationState {
434
+ state: string;
435
+ requestedAt?: number;
436
+ startedAt?: number;
437
+ completedAt?: number;
438
+ title?: string;
439
+ deckType?: string;
440
+ prdKey?: string;
441
+ prdTaskId?: string;
442
+ todoId?: string;
443
+ error?: string;
444
+ url?: string;
445
+ }
446
+
447
+ export interface SessionProductProjection {
448
+ activePrdKey?: string;
449
+ activePrdTaskId?: string;
450
+ deckGeneration?: SessionDeckGenerationState;
451
+ }
452
+
453
+ export interface SessionActivityWorkExtensions {
454
+ agentActivity?: Record<string, SessionAgentActivity>;
455
+ diagnostics?: SessionListDiagnostics;
456
+ }
457
+
458
+ export interface SessionWorkProjection<TTaskState = SessionTaskState>
459
+ extends SessionTaskProjection<TTaskState>,
460
+ SessionStreamWorkExtension,
461
+ SessionActivityWorkExtensions {
462
+ workflowMode: WorkflowMode;
463
+ }
464
+
465
+ export interface SessionWorkflowProjection {
466
+ workflowMode: WorkflowMode;
467
+ loop?: SessionLoopState;
468
+ }
469
+
470
+ export interface SessionLoopResponse extends Omit<SessionWorkflowProjection, 'loop'> {
471
+ sessionId: string;
472
+ loop: SessionLoopState | null;
473
+ }
474
+
475
+ export interface SessionUsageAgentSummary {
476
+ inputTokens: number;
477
+ outputTokens: number;
478
+ reasoningTokens: number;
479
+ costUsd: number;
480
+ updatedAt: number;
481
+ }
482
+
483
+ export interface SessionUsageSummary {
484
+ inputTokens: number;
485
+ outputTokens: number;
486
+ reasoningTokens: number;
487
+ costUsd: number;
488
+ updatedAt: number;
489
+ byAgent?: Record<string, SessionUsageAgentSummary>;
490
+ }
491
+
492
+ export interface SessionSnapshotCore extends SessionTaskProjection {
493
+ sessionId: string;
494
+ label: string;
495
+ status: string;
254
496
  createdAt: string;
255
497
  mode: 'sandbox' | 'tui';
498
+ workflowMode: WorkflowMode;
499
+ participants: SessionDetailParticipant[];
256
500
  task?: string;
257
501
  error?: string;
258
- streamId?: string | null;
259
- streamUrl?: string | null;
502
+ bucket: SessionBucket;
503
+ runtimeAvailable: boolean;
504
+ controlAvailable: boolean;
505
+ historyOnly: boolean;
506
+ }
507
+
508
+ export interface SessionSnapshotHistoryExtensions {
509
+ streamId: string | null;
510
+ streamUrl: string | null;
511
+ }
512
+
513
+ export interface SessionSnapshotWorkExtensions
514
+ extends SessionStreamWorkExtension,
515
+ SessionActivityWorkExtensions {
516
+ agentActivity: Record<string, SessionAgentActivity>;
517
+ usage: SessionUsageSummary;
518
+ }
519
+
520
+ export interface SessionSnapshotWorkflowExtensions
521
+ extends Pick<SessionWorkflowProjection, 'loop'> {}
522
+
523
+ export interface SessionSnapshotMetadataExtensions {
260
524
  context: {
261
525
  branch?: string;
262
526
  workingDirectory?: string;
263
527
  };
264
- participants: SessionParticipant[];
265
- tasks: Array<{
266
- taskId: string;
267
- agent: string;
268
- status: 'running' | 'completed' | 'failed';
269
- prompt: string;
270
- duration?: number;
271
- startedAt?: string;
272
- completedAt?: string;
273
- }>;
274
- agentActivity: Record<
275
- string,
276
- {
277
- name?: string;
278
- status: string;
279
- currentTool?: string;
280
- currentToolArgs?: string;
281
- toolCallCount: number;
282
- lastActivity: number;
283
- totalElapsed?: number;
284
- }
285
- >;
286
- stream?: SessionStreamProjection;
287
- tags?: string[];
528
+ observed?: SessionObservedProjection;
529
+ product?: SessionProductProjection;
530
+ tags: string[];
531
+ skills: SessionSkillRef[];
288
532
  defaultAgent?: string;
289
- bucket?: 'running' | 'paused' | 'provisioning' | 'history';
290
- runtimeAvailable?: boolean;
291
- controlAvailable?: boolean;
292
- historyOnly?: boolean;
533
+ workers?: SessionListItem[];
293
534
  }
294
535
 
295
- export interface CoderHubHydrationMessage {
536
+ export interface SessionSnapshotExtensions
537
+ extends SessionSnapshotHistoryExtensions,
538
+ SessionSnapshotWorkExtensions,
539
+ SessionSnapshotWorkflowExtensions,
540
+ SessionSnapshotMetadataExtensions {}
541
+
542
+ export interface SessionSnapshot extends SessionSnapshotCore, SessionSnapshotExtensions {}
543
+
544
+ export interface CoderHubHydrationMessage
545
+ extends SessionTaskProjection<HydrationTaskState>,
546
+ SessionStreamWorkExtension {
296
547
  type: 'session_hydration';
297
548
  sessionId: string;
549
+ label?: string;
298
550
  resumedAt: number;
299
551
  entries: ConversationEntry[];
300
- tasks: HydrationTaskState[];
301
- stream?: SessionStreamProjection;
302
552
  task?: string;
553
+ leadConnected?: boolean;
303
554
  streamingState?: {
304
555
  isStreaming?: boolean;
305
556
  activeTasks?: Array<{
@@ -327,6 +578,133 @@ export interface BroadcastEventMessage {
327
578
  timestamp?: number;
328
579
  }
329
580
 
581
+ export type ReplayEntry = ConversationEntry;
582
+
583
+ export interface ReplayHistoryResponse {
584
+ sessionId: string;
585
+ entriesSource: 'durable_stream' | 'session_entries' | 'event_history' | 'none';
586
+ sourceCounts?: {
587
+ durableStream: number;
588
+ sessionEntries: number;
589
+ eventHistory: number;
590
+ };
591
+ entries: ReplayEntry[];
592
+ }
593
+
594
+ export interface SessionEventHistoryItem {
595
+ id: number;
596
+ event: string;
597
+ category: string;
598
+ agent?: string;
599
+ taskId?: string;
600
+ payload: Record<string, unknown>;
601
+ occurredAt: string;
602
+ ingestedAt: string;
603
+ }
604
+
605
+ export interface SessionEventHistoryResponse {
606
+ sessionId: string;
607
+ events: SessionEventHistoryItem[];
608
+ }
609
+
610
+ export interface SessionParticipantHistoryItem {
611
+ id: string;
612
+ role: string;
613
+ transport: string;
614
+ agentRole?: string;
615
+ connectedAt: string;
616
+ disconnectedAt?: string;
617
+ lastActivityAt: string;
618
+ metadata?: Record<string, unknown>;
619
+ }
620
+
621
+ export interface SessionParticipantsResponse {
622
+ sessionId: string;
623
+ participants: SessionParticipantHistoryItem[];
624
+ }
625
+
626
+ export interface SessionTodoSummary {
627
+ open: number;
628
+ in_progress: number;
629
+ done: number;
630
+ closed: number;
631
+ cancelled: number;
632
+ }
633
+
634
+ export interface SessionTodoAssignee {
635
+ id: string;
636
+ name: string;
637
+ type?: string;
638
+ }
639
+
640
+ export interface SessionTodoItem {
641
+ id: string;
642
+ title: string;
643
+ status: 'open' | 'in_progress' | 'done' | 'closed' | 'cancelled';
644
+ type?: string | null;
645
+ priority?: string | null;
646
+ parentTaskId?: string | null;
647
+ prdKey?: string | null;
648
+ externalRef?: string | null;
649
+ taskKey?: string | null;
650
+ origin?: string | null;
651
+ kind?: string | null;
652
+ assignee?: SessionTodoAssignee | null;
653
+ lastSessionId?: string | null;
654
+ sessionIds: string[];
655
+ tags: string[];
656
+ memoryKeys: string[];
657
+ memoryIds: string[];
658
+ touchedByAgents: string[];
659
+ lastTouchedByAgent?: string | null;
660
+ attachments: Array<Record<string, unknown>>;
661
+ attachmentCount: number;
662
+ createdAt: string;
663
+ updatedAt: string;
664
+ }
665
+
666
+ export interface SessionTodoListResponse {
667
+ sessionId: string;
668
+ ok: boolean;
669
+ op: 'session_todo_list';
670
+ count: number;
671
+ summary: SessionTodoSummary;
672
+ todos: SessionTodoItem[];
673
+ unavailable?: boolean;
674
+ message?: string;
675
+ }
676
+
677
+ export type SseSessionSnapshotParticipant = SessionDetailParticipant;
678
+
679
+ export interface SseHydrationTaskState {
680
+ taskId: string;
681
+ agent: string;
682
+ status: SessionTaskState['status'];
683
+ prompt: string;
684
+ }
685
+
686
+ export interface SseSessionSnapshotMessage {
687
+ type: 'snapshot';
688
+ sessionId: string;
689
+ label: string;
690
+ status: string;
691
+ createdAt: string;
692
+ mode: 'sandbox' | 'tui';
693
+ participants: SseSessionSnapshotParticipant[];
694
+ taskCount: number;
695
+ agentActivity: Record<string, SessionAgentActivity>;
696
+ stream?: SessionStreamProjection;
697
+ }
698
+
699
+ export interface SseHydrationMessage
700
+ extends SessionTaskProjection<SseHydrationTaskState>,
701
+ SessionStreamWorkExtension {
702
+ type: 'hydration';
703
+ sessionId: string;
704
+ entries: ConversationEntry[];
705
+ task?: string;
706
+ }
707
+
330
708
  export interface RpcEventMessage {
331
709
  type: 'rpc_event';
332
710
  event: Record<string, unknown>;
@@ -352,6 +730,9 @@ export type ServerMessage =
352
730
  | CoderHubStreamReadyMessage
353
731
  | CoderHubSessionResumeMessage
354
732
  | ConnectionRejectedMessage
733
+ | ProtocolErrorMessage
734
+ | SseSessionSnapshotMessage
735
+ | SseHydrationMessage
355
736
  | PresenceEventMessage
356
737
  | BroadcastEventMessage
357
738
  | RpcEventMessage
@@ -265,11 +265,6 @@ export class RemoteSession {
265
265
 
266
266
  this.ws.onopen = () => {
267
267
  log('WebSocket connected');
268
- try {
269
- this.ws?.send(JSON.stringify({ type: 'controller_ready' }));
270
- } catch {
271
- // Let the normal init timeout surface if bootstrap cannot start.
272
- }
273
268
  };
274
269
 
275
270
  this.ws.onmessage = (event: MessageEvent) => {
@@ -292,11 +287,17 @@ export class RemoteSession {
292
287
  this.connected = true;
293
288
  this.reconnectAttempts = 0;
294
289
  if (data.sessionId) this.sessionId = data.sessionId as string;
290
+ if (typeof data.label === 'string') this.label = data.label;
295
291
  this.applyLifecycle({
296
292
  type: 'init',
297
293
  sessionId: typeof data.sessionId === 'string' ? data.sessionId : undefined,
298
294
  label: typeof data.label === 'string' ? data.label : undefined,
299
295
  });
296
+ try {
297
+ this.ws?.send(JSON.stringify({ type: 'bootstrap_ready' }));
298
+ } catch {
299
+ // Let the close/error path surface bootstrap failure.
300
+ }
300
301
  log(`Connected to session ${this.sessionId}`);
301
302
  this.notifyConnectionChange('connected');
302
303
  resolve();
@@ -316,6 +317,25 @@ export class RemoteSession {
316
317
  return;
317
318
  }
318
319
 
320
+ if (type === 'protocol_error') {
321
+ clearTimeout(connectTimeout);
322
+ const msg = (data.message as string) || 'Hub protocol error';
323
+ this.applyLifecycle({
324
+ type: 'rpc_command_error',
325
+ error: msg,
326
+ paused: false,
327
+ });
328
+ this.dispatchEvent({
329
+ type: 'protocol_error',
330
+ ...data,
331
+ _source: 'hub',
332
+ } as RpcEvent);
333
+ if (!this.connected) {
334
+ reject(new Error(msg));
335
+ }
336
+ return;
337
+ }
338
+
319
339
  if (type === 'session_resume') {
320
340
  this.applyLifecycle({
321
341
  type: 'session_resume',
@@ -405,7 +425,7 @@ export class RemoteSession {
405
425
  return;
406
426
  }
407
427
 
408
- // Raw RPC messages (from Durable Stream replay historical, not live)
428
+ // Legacy/raw RPC messages tolerated but not expected on the controller path.
409
429
  if (type === 'rpc_event') {
410
430
  const rpcEvent = data.event as RpcEvent;
411
431
  if (rpcEvent) {