@zq-silk/yui 0.13.7 → 0.13.9

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.
Files changed (78) hide show
  1. package/ARCHITECTURE.md +40 -4
  2. package/README.md +23 -4
  3. package/dist/cli/commandCatalog.js +21 -2
  4. package/dist/cli/updatePorts.js +4 -4
  5. package/dist/cli.js +43 -14
  6. package/dist/commands/agentCommands.js +1 -1
  7. package/dist/commands/configCommands.js +1 -86
  8. package/dist/commands/executionAuditCommands.js +17 -16
  9. package/dist/commands/globalRoleCommands.js +4 -4
  10. package/dist/commands/sessionCommands.js +2 -6
  11. package/dist/commands/taskActor.js +1 -2
  12. package/dist/commands/taskCommands.js +93 -21
  13. package/dist/commands/taskContextCommand.js +1 -1
  14. package/dist/commands/taskExecutionCommands.js +0 -5
  15. package/dist/commands/taskOverviewCommand.js +5 -1
  16. package/dist/commands/taskRoleRuntimeStatus.js +3 -16
  17. package/dist/config/configCatalog.js +1 -6
  18. package/dist/config/yuiConfig.js +0 -79
  19. package/dist/context/sessionBootstrapManifest.js +26 -23
  20. package/dist/controller/clientRuntime.js +40 -3
  21. package/dist/controller/controller.js +29 -54
  22. package/dist/controller/fileSchedulerStoreAdapter.js +372 -1117
  23. package/dist/controller/runtime.js +12 -5
  24. package/dist/controller/runtimeHookRunFence.js +3 -9
  25. package/dist/controller/runtimeLaunchCoordinator.js +44 -67
  26. package/dist/controller/structuredProviderObservation.js +18 -5
  27. package/dist/coordination/workMailbox.js +4 -4
  28. package/dist/execution/executionHealth.js +1 -1
  29. package/dist/executor/agentExecutor.js +103 -93
  30. package/dist/executor/executorRegistry.js +8 -20
  31. package/dist/executor/fileRoleLaunchPlanner.js +26 -93
  32. package/dist/executor/turnCompletion.js +5 -5
  33. package/dist/lifecycle/exactRunTerminalization.js +2 -4
  34. package/dist/observability/executionAudit.js +40 -94
  35. package/dist/operator/operatorSessionHistory.js +7 -5
  36. package/dist/output/rolePresentation.js +0 -1
  37. package/dist/repository/taskWorkspacePreparer.js +0 -1
  38. package/dist/role/role.js +13 -21
  39. package/dist/run/agentRun.js +4 -54
  40. package/dist/runtime/agentDriver.js +2 -0
  41. package/dist/runtime/agentError.js +114 -0
  42. package/dist/runtime/agentHost.js +55 -82
  43. package/dist/runtime/builtinAgentDrivers.js +21 -9
  44. package/dist/runtime/builtinAgentErrorMappers.js +150 -0
  45. package/dist/runtime/exactControlPlane.js +6 -12
  46. package/dist/runtime/index.js +1 -2
  47. package/dist/runtime/launchBroker.js +5 -19
  48. package/dist/runtime/lifecycleReservation.js +20 -4
  49. package/dist/runtime/providerRuntimeIdentity.js +11 -19
  50. package/dist/runtime/runtimeBinding.js +0 -27
  51. package/dist/runtime/runtimeObservation.js +7 -16
  52. package/dist/runtime/runtimeSessionCandidate.js +3 -10
  53. package/dist/runtime/sessionLaunchRequest.js +1 -2
  54. package/dist/runtime/sessionReconciliation.js +2 -2
  55. package/dist/runtime/structuredProviderHost.js +44 -79
  56. package/dist/runtime/taskRuntimeIsolation.js +0 -7
  57. package/dist/runtime/tmuxAdapters.js +6 -49
  58. package/dist/scheduler/activeRoleRunDelivery.js +220 -178
  59. package/dist/scheduler/activeTaskProgress.js +1 -4
  60. package/dist/scheduler/leaderWakeupProcessor.js +123 -88
  61. package/dist/scheduler/roleRunLiveness.js +4 -1
  62. package/dist/scheduler/roleRunStall.js +10 -14
  63. package/dist/scheduler/wakeReason.js +4 -0
  64. package/dist/storage/migration/productionRegistry.js +475 -0
  65. package/dist/storage/sqliteSchema.js +54 -2
  66. package/dist/storage/sqliteStore.js +11 -44
  67. package/dist/storage/taskStore.js +8 -36
  68. package/dist/web/webSnapshot.js +3 -0
  69. package/i18n/README.zh-CN.md +11 -3
  70. package/package.json +1 -1
  71. package/skills/yui-leader/SKILL.md +30 -8
  72. package/skills/yui-operator/SKILL.md +14 -6
  73. package/skills/yui-runtime/SKILL.md +11 -8
  74. package/dist/lifecycle/providerErrorClass.js +0 -152
  75. package/dist/run/providerRetry.js +0 -226
  76. package/dist/run/providerRetryConfig.js +0 -27
  77. package/dist/runtime/providerErrorCodes.js +0 -278
  78. package/dist/runtime/providerRecoveryDecision.js +0 -55
@@ -10,11 +10,6 @@ import { activeRoleAgentBinding } from "../role/role.js";
10
10
  import { writeTextFileAtomically } from "../storage/durableFile.js";
11
11
  import { compileRoleSessionContext, roleSessionKind } from "../context/roleSessionContext.js";
12
12
  import { materializeSessionBootstrap } from "../context/sessionBootstrapManifest.js";
13
- import { serializeRunBootstrapEnvelope, serializeRunHostRecoveryEnvelope } from "../context/runContextContract.js";
14
- import { serializeProviderRetryEnvelope } from "../run/providerRetry.js";
15
- import { serializeWorkflowOutcomeRequestEnvelope } from "../run/runControlRequest.js";
16
- import { prefixYuiTitleInput } from "../run/runIdentity.js";
17
- import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
18
13
  import { resolveAgentAdapter } from "./agentAdapter.js";
19
14
  import { resolveTaskRoleSessionTitle } from "../runtime/sessionTitle.js";
20
15
  import { nativeSessionIdForLaunch } from "../runtime/preallocatedNativeSession.js";
@@ -302,7 +297,6 @@ export class FileRoleLaunchPlanner {
302
297
  if (runtimeIsolation !== undefined && (owner.scope !== "task"
303
298
  || runtimeIsolation.taskId !== owner.taskId
304
299
  || runtimeIsolation.workspace.root !== effectiveWorkspace
305
- || runtimeIsolation.generation.runId !== input.runId
306
300
  || runtimeIsolation.generation.launchId !== input.launchId)) {
307
301
  throw new Error("Role launch does not match its Task runtime isolation descriptor.");
308
302
  }
@@ -478,41 +472,31 @@ export class FileRoleLaunchPlanner {
478
472
  if (owner.scope === "task" && (input.mode === "new" || input.mode === "resume")) {
479
473
  jobCallerKey = randomBytes(32).toString("hex");
480
474
  }
481
- // Every managed launch for an unpushed Run owns that Run's first Provider
482
- // write, whether it creates or resumes the native Conversation. Keeping
483
- // resume on the same launch handshake prevents a durable AgentRun from
484
- // existing without a corresponding Provider Turn.
475
+ // Session lifecycle and Turn submission are separate atomic operations.
476
+ // Planning only starts or restores the exact native Session; delivery
477
+ // submits the Run input after that Session fact is durable.
485
478
  if (managedControl && (managedRun === null || managedRun.status !== "active")) {
486
479
  throw new Error(`Managed Run is no longer active: ${input.runId}.`);
487
480
  }
488
- const carriesInitialTurn = managedControl && (managedRun.pushedAt === undefined
489
- || managedRun.providerRetry?.state === "dispatching"
490
- || managedRun.controlRequest?.state === "dispatching");
491
481
  const providerAuthority = managedControl
492
482
  ? this.#providerAuthorityForLaunch(owner.taskId, role.name, input.launchId, input.mode)
493
483
  : undefined;
494
- const providerOwnedTurn = managedControl && !carriesInitialTurn
484
+ const providerOwnedTurn = managedControl && input.mode === "resume"
495
485
  ? this.#providerOwnedTurnForLaunch(owner.taskId, role.name, input.runId)
496
486
  : undefined;
497
487
  const providerNativeSessionId = binding.adapterId === "claude"
498
488
  ? preallocatedNativeSessionId
499
489
  : resumeNativeSessionId;
500
- const initialTurn = carriesInitialTurn
501
- ? {
502
- attemptId: agentRunDeliveryReceiptId(managedRun),
503
- boundedText: managedRunLaunchEnvelope(managedRun, input.mode, sessionContext.sessionManifestPath, sessionTitle)
504
- }
505
- : undefined;
506
490
  const providerControl = !managedControl
507
491
  ? undefined
508
- : initialTurn === undefined
492
+ : input.mode === "resume"
509
493
  ? {
510
494
  schemaVersion: 1,
511
495
  adapterId: binding.adapterId,
512
496
  transport: managedCompiled.transport,
513
- kind: "ensure",
497
+ kind: "restore",
514
498
  mode: "resume",
515
- nativeSessionId: requireText(providerNativeSessionId, "Managed Provider ensure native session id"),
499
+ nativeSessionId: requireText(providerNativeSessionId, "Managed Provider restore native session id"),
516
500
  ...(sessionTitle === undefined ? {} : { sessionTitle }),
517
501
  ...(managedCompiled.codexThread === undefined
518
502
  ? {}
@@ -520,44 +504,24 @@ export class FileRoleLaunchPlanner {
520
504
  ...(providerOwnedTurn === undefined ? {} : { ownedTurn: providerOwnedTurn }),
521
505
  authority: providerAuthority
522
506
  }
523
- : resumeNativeSessionId === undefined
524
- ? {
525
- schemaVersion: 1,
526
- adapterId: binding.adapterId,
527
- transport: managedCompiled.transport,
528
- kind: "new",
529
- mode: "new",
530
- ...(providerNativeSessionId === undefined
531
- ? {}
532
- : { nativeSessionId: providerNativeSessionId }),
533
- ...(sessionTitle === undefined ? {} : { sessionTitle }),
534
- ...(managedCompiled.codexThread === undefined
535
- ? {}
536
- : { codexThread: managedCompiled.codexThread }),
537
- authority: providerAuthority,
538
- initialTurn
539
- }
540
- : {
541
- schemaVersion: 1,
542
- adapterId: binding.adapterId,
543
- transport: managedCompiled.transport,
544
- kind: "resume",
545
- mode: "resume",
546
- nativeSessionId: requireText(providerNativeSessionId, "Managed Provider resume native session id"),
547
- ...(sessionTitle === undefined ? {} : { sessionTitle }),
548
- ...(managedCompiled.codexThread === undefined
549
- ? {}
550
- : { codexThread: managedCompiled.codexThread }),
551
- authority: providerAuthority,
552
- initialTurn
553
- };
507
+ : {
508
+ schemaVersion: 1,
509
+ adapterId: binding.adapterId,
510
+ transport: managedCompiled.transport,
511
+ kind: "start",
512
+ mode: "new",
513
+ ...(providerNativeSessionId === undefined
514
+ ? {}
515
+ : { nativeSessionId: providerNativeSessionId }),
516
+ ...(sessionTitle === undefined ? {} : { sessionTitle }),
517
+ ...(managedCompiled.codexThread === undefined
518
+ ? {}
519
+ : { codexThread: managedCompiled.codexThread }),
520
+ authority: providerAuthority
521
+ };
554
522
  const launch = {
555
523
  command,
556
524
  args,
557
- ...(managedRun?.providerRetry !== undefined
558
- && managedRun.providerRetry.state !== "dispatching"
559
- ? { deferProviderStart: true }
560
- : {}),
561
525
  ...(providerControl === undefined ? {} : { providerControl }),
562
526
  env: {
563
527
  ...launchEnvironment,
@@ -608,15 +572,11 @@ export class FileRoleLaunchPlanner {
608
572
  role: {
609
573
  name: role.name,
610
574
  workspace: effectiveWorkspace,
611
- ...(agentWorkspace === effectiveWorkspace ? {} : { cwd: agentWorkspace }),
612
- ...(owner.scope === "task" ? { status: role.status } : {})
575
+ ...(agentWorkspace === effectiveWorkspace ? {} : { cwd: agentWorkspace })
613
576
  },
614
577
  launch: ordinaryConversationLaunch,
615
578
  session,
616
- ...(sessionTitle === undefined ? {} : { sessionTitle }),
617
- ...(carriesInitialTurn && input.runId !== undefined
618
- ? { initialTurnRunId: input.runId }
619
- : {})
579
+ ...(sessionTitle === undefined ? {} : { sessionTitle })
620
580
  };
621
581
  }
622
582
  #providerAuthorityForLaunch(taskId, roleName, launchId, mode) {
@@ -659,7 +619,7 @@ export class FileRoleLaunchPlanner {
659
619
  }
660
620
  #providerOwnedTurnForLaunch(taskId, roleName, runId) {
661
621
  const binding = this.store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding;
662
- if (binding === null || binding === undefined || binding.runId !== runId)
622
+ if (binding === null || binding === undefined || binding.turn?.runId !== runId)
663
623
  return undefined;
664
624
  const turn = binding.turn;
665
625
  if (turn === null || !["accepted", "running"].includes(turn.status))
@@ -706,33 +666,6 @@ export class FileRoleLaunchPlanner {
706
666
  return selectEnvironment(source, names);
707
667
  }
708
668
  }
709
- function managedRunLaunchEnvelope(run, mode, sessionManifestPath, title) {
710
- const body = run.providerRetry?.state === "dispatching"
711
- ? serializeProviderRetryEnvelope({
712
- taskId: run.taskId,
713
- runId: run.id,
714
- roleName: run.roleName,
715
- retry: run.providerRetry
716
- })
717
- : run.controlRequest?.state === "dispatching"
718
- ? serializeWorkflowOutcomeRequestEnvelope({
719
- taskId: run.taskId,
720
- runId: run.id,
721
- roleName: run.roleName,
722
- request: run.controlRequest
723
- })
724
- : mode === "resume" && run.pushedAt !== undefined
725
- ? serializeRunHostRecoveryEnvelope(run.bootstrapEnvelope)
726
- : serializeRunBootstrapEnvelope(run.bootstrapEnvelope);
727
- const guided = [
728
- `Yui Session Manifest: ${sessionManifestPath}`,
729
- "Read the manifest and its matching Role Skill before using the yui CLI for this Task.",
730
- body
731
- ].join("\n");
732
- return title === undefined
733
- ? guided
734
- : prefixYuiTitleInput(guided, title);
735
- }
736
669
  export function nativeAgentWorkspace(workspace) {
737
670
  return workspace.entries.length === 1
738
671
  ? workspace.entries[0].path
@@ -962,7 +895,7 @@ function readySession(agentId, adapterId, nativeSessionId, effective) {
962
895
  agentId,
963
896
  adapterId,
964
897
  nativeSessionId: requireText(nativeSessionId, "Native session id"),
965
- status: "ready",
898
+ status: "active",
966
899
  effective
967
900
  };
968
901
  }
@@ -22,7 +22,7 @@ export function createPendingTurnCompletion(input) {
22
22
  roleName: requireSafeIdentity(input.roleName, "Role name"),
23
23
  agentId: requireSafeIdentity(input.agentId, "Agent id"),
24
24
  nativeSessionId: requireText(input.nativeSessionId, "Native session id"),
25
- turnId: requireSafeIdentity(input.turnId, "Turn id"),
25
+ turnId: requireText(input.turnId, "Turn id"),
26
26
  runId: requireSafeIdentity(input.runId, "Run id"),
27
27
  summary: requireText(input.summary, "Turn summary"),
28
28
  observedAt,
@@ -44,7 +44,7 @@ export function validatePendingTurnCompletion(value) {
44
44
  roleName: requireSafeIdentityValue(input.roleName, "Role name"),
45
45
  agentId: requireSafeIdentityValue(input.agentId, "Agent id"),
46
46
  nativeSessionId: requireTextValue(input.nativeSessionId, "Native session id"),
47
- turnId: requireSafeIdentityValue(input.turnId, "Turn id"),
47
+ turnId: requireTextValue(input.turnId, "Turn id"),
48
48
  runId: requireSafeIdentityValue(input.runId, "Run id"),
49
49
  summary: requireTextValue(input.summary, "Turn summary"),
50
50
  observedAt,
@@ -58,7 +58,7 @@ export function validateRecentTurnIds(value, limit = DEFAULT_RECENT_TURN_ID_LIMI
58
58
  if (value.length > normalizedLimit) {
59
59
  throw new Error(`Recent Turn ids must not contain more than ${normalizedLimit} entries.`);
60
60
  }
61
- const result = value.map((turnId) => requireSafeIdentityValue(turnId, "Recent Turn id"));
61
+ const result = value.map((turnId) => requireTextValue(turnId, "Recent Turn id"));
62
62
  if (new Set(result).size !== result.length) {
63
63
  throw new Error("Recent Turn ids must not contain duplicates.");
64
64
  }
@@ -67,7 +67,7 @@ export function validateRecentTurnIds(value, limit = DEFAULT_RECENT_TURN_ID_LIMI
67
67
  export function rememberRecentTurnId(recentTurnIds, turnId, limit = DEFAULT_RECENT_TURN_ID_LIMIT) {
68
68
  const normalizedLimit = requireLimit(limit);
69
69
  const existing = validateRecentTurnIds(recentTurnIds, normalizedLimit);
70
- const normalizedTurnId = requireSafeIdentity(turnId, "Recent Turn id");
70
+ const normalizedTurnId = requireText(turnId, "Recent Turn id");
71
71
  return [
72
72
  ...existing.filter((candidate) => candidate !== normalizedTurnId),
73
73
  normalizedTurnId
@@ -75,7 +75,7 @@ export function rememberRecentTurnId(recentTurnIds, turnId, limit = DEFAULT_RECE
75
75
  }
76
76
  export function hasRecentTurnId(recentTurnIds, turnId) {
77
77
  const existing = validateRecentTurnIds(recentTurnIds);
78
- return existing.includes(requireSafeIdentity(turnId, "Recent Turn id"));
78
+ return existing.includes(requireText(turnId, "Recent Turn id"));
79
79
  }
80
80
  function requireOrderedTimestamps(observedAt, dueAt) {
81
81
  if (Date.parse(dueAt) < Date.parse(observedAt)) {
@@ -2,7 +2,6 @@ import { isDeepStrictEqual } from "node:util";
2
2
  import { completeProcessing } from "../coordination/workMailbox.js";
3
3
  import { settleExactWorkExecution } from "../coordination/workMailboxQueue.js";
4
4
  import { terminalizeTaskRoleRunSession } from "../executor/agentExecutor.js";
5
- import { updateRoleStatus } from "../role/role.js";
6
5
  import { finishReviewRound, updateReviewExecutionGroup } from "../review/reviewRound.js";
7
6
  import { reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
8
7
  import { agentRunDeliveryReceiptId, failAgentRun, withYieldReceipt, yieldAgentRun } from "../run/agentRun.js";
@@ -265,12 +264,12 @@ export function retireExactActiveAgentRun(store, input, now) {
265
264
  const session = sessions?.sessions[input.agentId];
266
265
  const providerBinding = sessions?.providerBinding;
267
266
  const providerTurn = providerBinding?.turn;
268
- const providerSettled = providerBinding?.runId === current.id
267
+ const providerSettled = providerTurn?.runId === current.id
269
268
  && (providerTurn?.status === "completed"
270
269
  || providerTurn?.status === "failed"
271
270
  || providerTurn?.status === "cancelled"
272
271
  || providerTurn?.status === "rejected");
273
- if (session?.status !== "stopped" && session?.status !== "broken" && !providerSettled) {
272
+ if (session?.status === "active" && !providerSettled) {
274
273
  return {
275
274
  disposition: "blocked",
276
275
  run: current,
@@ -450,7 +449,6 @@ export function terminalizeExactTaskRun(store, input, now) {
450
449
  else {
451
450
  store.clearActiveAgentRun(input.taskId, input.roleName);
452
451
  }
453
- store.saveRole(input.taskId, updateRoleStatus(role, "idle", now));
454
452
  if (sessions !== null) {
455
453
  store.saveTaskRoleSessionSet(terminalizeTaskRoleRunSession(sessions, {
456
454
  agentId: input.agentId,
@@ -86,26 +86,6 @@ function roleBucket(roleName) {
86
86
  }
87
87
  return "other";
88
88
  }
89
- /** Maps a provider-retry lifecycle event to its last recorded decision. */
90
- function providerRetryDecision(event) {
91
- if (event.type === "runtime.provider-retry-waiting")
92
- return "waiting";
93
- if (event.type === "runtime.provider-retry-resume")
94
- return "resume";
95
- if (event.type === "runtime.provider-retry-session-dead")
96
- return "session-dead";
97
- if (event.type === "runtime.provider-retry-budget-exhausted")
98
- return "budget-exhausted";
99
- if (event.payload.note === "native-turn-completion-durable")
100
- return "suppressed:completion-durable";
101
- if (event.payload.note === "retry-budget-exhausted")
102
- return "budget-exhausted";
103
- if (event.payload.wouldRetry === "true")
104
- return "retrying";
105
- if (event.payload.shadow === "true")
106
- return "shadow";
107
- return "not-retried";
108
- }
109
89
  function durationMs(run) {
110
90
  if (run.endedAt === undefined)
111
91
  return 0;
@@ -205,7 +185,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
205
185
  integrations: section,
206
186
  publications: section,
207
187
  events: section,
208
- providerRetries: section,
188
+ agentErrors: section,
209
189
  workItems: section,
210
190
  orchestration: section,
211
191
  storage: section,
@@ -378,13 +358,13 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
378
358
  const history = Array.isArray(set.history) ? set.history : [];
379
359
  for (const session of [...history, ...Object.values(set.sessions)]) {
380
360
  generations += 1;
381
- if (session.status === "broken")
361
+ if (session.status === "ended" && session.endReason === "failed")
382
362
  broken += 1;
383
- else if (session.status === "stopped")
363
+ else if (session.status === "ended")
384
364
  stopped += 1;
385
365
  else
386
366
  other += 1;
387
- if (session.status === "broken" || session.status === "stopped") {
367
+ if (session.status === "ended") {
388
368
  classifyTerminalSessionRunRelation(runs, set.owner.roleName, session, terminalByRunRelation);
389
369
  }
390
370
  }
@@ -608,51 +588,38 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
608
588
  return failed(error);
609
589
  }
610
590
  })();
611
- const providerRetries = (() => {
591
+ const agentErrors = (() => {
612
592
  try {
613
- const entries = new Map();
593
+ const entries = [];
594
+ const categories = new Map();
614
595
  for (const taskId of taskIds) {
615
596
  for (const event of store.listEvents(taskId)) {
616
- if (!inWindow(event.createdAt, options))
617
- continue;
618
- if (!event.type.startsWith("runtime.provider-retry-"))
619
- continue;
620
- const runId = event.payload.runId;
621
- if (typeof runId !== "string")
597
+ if (!inWindow(event.createdAt, options) || event.type !== "runtime.agent-error") {
622
598
  continue;
623
- const key = `${taskId}/${runId}`;
624
- const previous = entries.get(key);
625
- const attempt = Number(event.payload.attempt ?? "0");
626
- const decision = providerRetryDecision(event);
627
- const terminal = decision === "session-dead" || decision === "budget-exhausted";
628
- const next = {
599
+ }
600
+ const category = event.payload.category ?? "unknown";
601
+ categories.set(category, (categories.get(category) ?? 0) + 1);
602
+ entries.push({
629
603
  taskId,
630
- runId,
631
- roleName: typeof event.payload.roleName === "string"
632
- ? event.payload.roleName
633
- : previous?.roleName ?? "",
634
- attempts: Number.isFinite(attempt) ? Math.max(previous?.attempts ?? 0, attempt) : (previous?.attempts ?? 0),
635
- errorClass: typeof event.payload.errorClass === "string"
636
- ? event.payload.errorClass
637
- : previous?.errorClass ?? "",
638
- firstFailureAt: previous?.firstFailureAt ?? event.createdAt,
639
- lastFailureAt: event.createdAt,
640
- ...(typeof event.payload.nextAttemptAt === "string"
641
- ? { lastBackoffAt: event.payload.nextAttemptAt }
642
- : previous?.lastBackoffAt !== undefined
643
- ? { lastBackoffAt: previous.lastBackoffAt }
644
- : {}),
645
- decision,
646
- terminal: (previous?.terminal ?? false) || terminal
647
- };
648
- entries.set(key, next);
604
+ eventId: event.id,
605
+ runId: event.payload.runId ?? "",
606
+ roleName: event.payload.roleName ?? "",
607
+ source: event.payload.source ?? "unknown",
608
+ phase: event.payload.phase ?? "unknown",
609
+ category,
610
+ code: event.payload.code ?? "unknown",
611
+ message: event.payload.message ?? "",
612
+ raw: event.payload.raw ?? "",
613
+ inputDisposition: event.payload.inputDisposition ?? "unknown",
614
+ sessionDisposition: event.payload.sessionDisposition ?? "unknown",
615
+ createdAt: event.createdAt
616
+ });
649
617
  }
650
618
  }
651
- const list = [...entries.values()];
652
619
  return ok({
653
- total: list.length,
654
- terminal: list.filter((entry) => entry.terminal).length,
655
- entries: list.map(({ terminal: _terminal, ...entry }) => entry)
620
+ total: entries.length,
621
+ byCategory: Object.fromEntries(categories),
622
+ entries
656
623
  });
657
624
  }
658
625
  catch (error) {
@@ -748,16 +715,10 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
748
715
  try {
749
716
  const protocolVersions = new Map();
750
717
  const manifestDigests = new Set();
751
- const retryStates = new Map();
718
+ const agentErrorCategories = new Map();
752
719
  const exitClassifications = new Map();
753
720
  const usageSemantics = new Map();
754
- let activeRetryEpisodes = 0;
755
- let activeConsecutiveFailures = 0;
756
- let activeDispatchedRetries = 0;
757
- let retryClassifiedEvents = 0;
758
- let retryDispatchedEvents = 0;
759
- let retryRecoveredEvents = 0;
760
- let retryExhaustedEvents = 0;
721
+ let agentErrors = 0;
761
722
  let contextCapacityFailures = 0;
762
723
  let processExitObservations = 0;
763
724
  let compactionEvents = 0;
@@ -770,26 +731,17 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
770
731
  if (run.effective.sessionManifestCompatibilityDigest !== undefined) {
771
732
  manifestDigests.add(run.effective.sessionManifestCompatibilityDigest);
772
733
  }
773
- if (run.providerRetry !== undefined) {
774
- activeRetryEpisodes += 1;
775
- retryStates.set(run.providerRetry.state, (retryStates.get(run.providerRetry.state) ?? 0) + 1);
776
- activeConsecutiveFailures += run.providerRetry.consecutiveFailures;
777
- activeDispatchedRetries += run.providerRetry.dispatchedRetries;
778
- }
779
734
  }
780
735
  for (const event of store.listEvents(taskId)) {
781
736
  if (!inWindow(event.createdAt, options))
782
737
  continue;
783
- if (event.type === "runtime.provider-retry-classified")
784
- retryClassifiedEvents += 1;
785
- else if (event.type === "runtime.provider-retry-dispatched")
786
- retryDispatchedEvents += 1;
787
- else if (event.type === "runtime.provider-retry-recovered")
788
- retryRecoveredEvents += 1;
789
- else if (event.type === "runtime.provider-retry-exhausted")
790
- retryExhaustedEvents += 1;
791
- else if (event.type === "runtime.context-capacity-failure")
792
- contextCapacityFailures += 1;
738
+ if (event.type === "runtime.agent-error") {
739
+ agentErrors += 1;
740
+ const category = event.payload.category ?? "unknown";
741
+ agentErrorCategories.set(category, (agentErrorCategories.get(category) ?? 0) + 1);
742
+ if (category === "context")
743
+ contextCapacityFailures += 1;
744
+ }
793
745
  else if (event.type === "runtime.process-exit-observed") {
794
746
  processExitObservations += 1;
795
747
  const classification = event.payload.classification ?? "unknown";
@@ -808,14 +760,8 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
808
760
  return ok({
809
761
  contextProtocolVersions: Object.fromEntries(protocolVersions),
810
762
  manifestCompatibilityDigests: manifestDigests.size,
811
- activeRetryEpisodes,
812
- activeRetryStates: Object.fromEntries(retryStates),
813
- activeConsecutiveFailures,
814
- activeDispatchedRetries,
815
- retryClassifiedEvents,
816
- retryDispatchedEvents,
817
- retryRecoveredEvents,
818
- retryExhaustedEvents,
763
+ agentErrors,
764
+ agentErrorCategories: Object.fromEntries(agentErrorCategories),
819
765
  contextCapacityFailures,
820
766
  processExitObservations,
821
767
  processExitClassifications: Object.fromEntries(exitClassifications),
@@ -870,7 +816,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
870
816
  integrations,
871
817
  publications,
872
818
  events,
873
- providerRetries,
819
+ agentErrors,
874
820
  workItems,
875
821
  orchestration,
876
822
  storage,
@@ -25,7 +25,7 @@ export function projectOperatorStatus(sessions, activeAgentId, activeAdapterId)
25
25
  }
26
26
  return Object.freeze({
27
27
  writer: {
28
- state: matchingActive.status === "stopped" || matchingActive.status === "broken" ? "inactive" : "active",
28
+ state: matchingActive.status === "ended" ? "inactive" : "active",
29
29
  agentId: matchingActive.agentId,
30
30
  adapterId: matchingActive.adapterId,
31
31
  sessionRef: activeRef,
@@ -42,7 +42,7 @@ export function listOperatorSessions(sessions) {
42
42
  if (sessions === null)
43
43
  return [];
44
44
  validateRoleSessionSet(sessions);
45
- const current = Object.values(sessions.sessions).map((session) => (listItem(session, session.status === "stopped" || session.status === "broken"
45
+ const current = Object.values(sessions.sessions).map((session) => (listItem(session, session.status === "ended"
46
46
  ? "current"
47
47
  : "running")));
48
48
  const history = Object.values(sessions.history ?? {}).map((session) => (listItem(session, "history")));
@@ -92,7 +92,8 @@ export function prepareOperatorResumeSession(sessions, ref, now) {
92
92
  delete history[normalizedRef];
93
93
  nextSessions[selected.agentId] = {
94
94
  ...selected,
95
- status: "stopped",
95
+ status: "ended",
96
+ endReason: "stopped",
96
97
  updatedAt: requireDate(now)
97
98
  };
98
99
  return validateRoleSessionSet({
@@ -104,12 +105,13 @@ export function prepareOperatorResumeSession(sessions, ref, now) {
104
105
  });
105
106
  }
106
107
  function archiveCurrent(history, session) {
107
- if (session.status !== "stopped" && session.status !== "broken") {
108
+ if (session.status !== "ended") {
108
109
  throw new Error(`Cannot replace Operator session while its native process is ${session.status}.`);
109
110
  }
110
111
  const archived = {
111
112
  ...session,
112
- status: "stopped"
113
+ status: "ended",
114
+ endReason: session.endReason ?? "stopped"
113
115
  };
114
116
  return {
115
117
  ...history,
@@ -23,7 +23,6 @@ export function renderRoleDetails(title, role, input) {
23
23
  const overview = [
24
24
  ` Kind ${input.kind}`,
25
25
  ` Active Agent ${role.activeAgentId}`,
26
- ...("status" in role ? [` Status ${role.status}`] : []),
27
26
  ` Workspace ${role.workspace}`,
28
27
  ` Desired launch r${role.launchRevision}; Profile intent=${role.defaultAccess}`,
29
28
  ` Effective launch ${effective === undefined
@@ -2764,7 +2764,6 @@ function retireWorkspaceBoundSession(store, taskId, roleName, now) {
2764
2764
  }
2765
2765
  function canCorrectActiveWorkItemRoleWorkspaceHint(store, taskId, role, item, workspace) {
2766
2766
  if (role.taskId !== taskId
2767
- || role.status !== "running"
2768
2767
  || item.taskId !== taskId
2769
2768
  || item.assignee !== role.name
2770
2769
  || ["completed", "failed", "retired"].includes(item.status)
package/dist/role/role.js CHANGED
@@ -19,19 +19,22 @@ export function createRole(taskId, name, bindings, activeAgentId, workspace, now
19
19
  const owner = createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess);
20
20
  return validateTaskRole({
21
21
  ...owner,
22
- taskId: requireSafeIdentity(taskId, "Task id"),
23
- status: "idle"
22
+ schemaVersion: 4,
23
+ taskId: requireSafeIdentity(taskId, "Task id")
24
24
  });
25
25
  }
26
26
  export function createGlobalRole(name, bindings, activeAgentId, workspace, now, profile = {}, defaultAccess = "write") {
27
- return createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess);
27
+ return validateGlobalRole({
28
+ ...createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess),
29
+ schemaVersion: 3
30
+ });
28
31
  }
29
32
  export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalRole.name) {
30
33
  validateGlobalRole(globalRole);
31
34
  const timestamp = now.toISOString();
32
35
  return validateTaskRole({
33
36
  ...cloneProfile(globalRole),
34
- schemaVersion: 3,
37
+ schemaVersion: 4,
35
38
  launchRevision: 1,
36
39
  defaultAccess: globalRole.defaultAccess,
37
40
  taskId: requireSafeIdentity(taskId, "Task id"),
@@ -39,7 +42,6 @@ export function copyGlobalRoleToTaskRole(globalRole, taskId, now, name = globalR
39
42
  activeAgentId: globalRole.activeAgentId,
40
43
  agentBindings: cloneBindings(globalRole.agentBindings),
41
44
  workspace: globalRole.workspace,
42
- status: "idle",
43
45
  createdAt: timestamp,
44
46
  updatedAt: timestamp
45
47
  });
@@ -76,12 +78,6 @@ export function updateRole(role, patch, now) {
76
78
  : role.launchRevision + 1;
77
79
  return validateTaskRole(updated);
78
80
  }
79
- export function updateRoleStatus(role, status, now) {
80
- validateTaskRole(role);
81
- if (!isRoleStatus(status))
82
- throw new Error(`Role status is invalid: ${status}.`);
83
- return { ...role, status, updatedAt: now.toISOString() };
84
- }
85
81
  export function switchActiveRoleAgent(role, sessions, targetAgentId, runtime, now) {
86
82
  validateRoleOwner(role);
87
83
  const normalizedTarget = requireSafeIdentity(targetAgentId, "Target Agent id");
@@ -134,7 +130,7 @@ export function unbindRoleAgent(role, sessions, agentId, now) {
134
130
  throw new Error(`Cannot unbind Role Agent with an active Run: ${normalizedAgentId}.`);
135
131
  }
136
132
  const targetSession = sessions.sessions[normalizedAgentId];
137
- if (targetSession !== undefined && targetSession.status !== "stopped") {
133
+ if (targetSession !== undefined && targetSession.status === "active") {
138
134
  throw new Error(`Cannot unbind Role Agent while its native session is ${targetSession.status}: `
139
135
  + `${normalizedAgentId}.`);
140
136
  }
@@ -175,8 +171,6 @@ export function validateGlobalRole(role) {
175
171
  }
176
172
  export function validateTaskRole(role) {
177
173
  requireSafeIdentity(role.taskId, "Task id");
178
- if (!isRoleStatus(role.status))
179
- throw new Error(`Role status is invalid: ${role.status}.`);
180
174
  return validateRoleOwner(role);
181
175
  }
182
176
  function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile, defaultAccess) {
@@ -189,9 +183,8 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
189
183
  mappedBindings[binding.agentId] = binding;
190
184
  }
191
185
  const timestamp = now.toISOString();
192
- return validateGlobalRole({
186
+ return {
193
187
  ...cloneProfile(profile),
194
- schemaVersion: 3,
195
188
  launchRevision: 1,
196
189
  defaultAccess,
197
190
  name: requireSafeIdentity(name, "Role name"),
@@ -200,11 +193,13 @@ function createRoleOwner(name, bindings, activeAgentId, workspace, now, profile,
200
193
  workspace: requireText(workspace, "Role workspace"),
201
194
  createdAt: timestamp,
202
195
  updatedAt: timestamp
203
- });
196
+ };
204
197
  }
205
198
  function validateRoleOwner(role) {
206
- if (role.schemaVersion !== 3)
199
+ const expectedSchemaVersion = "taskId" in role ? 4 : 3;
200
+ if (role.schemaVersion !== expectedSchemaVersion) {
207
201
  throw new Error("Role schema version is invalid.");
202
+ }
208
203
  if (!Number.isSafeInteger(role.launchRevision) || role.launchRevision < 1) {
209
204
  throw new Error("Role launch revision must be a positive integer.");
210
205
  }
@@ -333,6 +328,3 @@ function requireText(value, label) {
333
328
  throw new Error(`${label} is required.`);
334
329
  return normalized;
335
330
  }
336
- function isRoleStatus(value) {
337
- return ["idle", "running", "detached", "exited", "failed"].includes(value);
338
- }