@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
@@ -1,12 +1,12 @@
1
1
  import { createAgentRun } from "../run/agentRun.js";
2
- import { createRunAssignment } from "../context/runContextContract.js";
2
+ import { createRunAssignment, serializeRunBootstrapEnvelope } from "../context/runContextContract.js";
3
3
  import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
4
4
  import { effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleForTaskSession } from "../executor/effectiveLaunch.js";
5
5
  import { roleAgentSessionResumeMode } from "../executor/agentExecutor.js";
6
6
  import { RuntimeLifecycleBusyError } from "../runtime/lifecycleReservation.js";
7
- import { recordLeaderFailure } from "./leaderFailure.js";
8
7
  import { isSchedulerTaskWorkspaceReady } from "./ports.js";
9
8
  import { RuntimeLaunchError } from "../runtime/ports.js";
9
+ import { serializeAgentErrorRaw } from "../runtime/agentError.js";
10
10
  export async function processLeaderWakeups(store, delivery, now, selection) {
11
11
  const results = [];
12
12
  const wakeups = selection === undefined || selection.full
@@ -42,8 +42,10 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
42
42
  results.push({ taskId: task.id, status: "skipped", reason: "busy" });
43
43
  continue;
44
44
  }
45
- if (typeof store.hasInFlightTurn === "function"
46
- && store.hasInFlightTurn(task.id, role.name)) {
45
+ // A durable wake is already the complete next intent. Do not materialize
46
+ // another Run while the Provider still owns the previous Turn: doing so
47
+ // would make its late terminal compete with a newer Run generation.
48
+ if (providerTurnIsUnsettled(store, task.id, role.name)) {
47
49
  results.push({ taskId: task.id, status: "skipped", reason: "busy" });
48
50
  continue;
49
51
  }
@@ -55,6 +57,7 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
55
57
  let run = null;
56
58
  let prepared;
57
59
  let preStartFencePersisted = false;
60
+ let mode = "new";
58
61
  try {
59
62
  const compatibleSession = existingSession !== null
60
63
  && (reopening
@@ -65,16 +68,15 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
65
68
  && !compatibleSession;
66
69
  if (hasNativeSession(existingSession) && !compatibleSession
67
70
  && !reopenIdentityDrift
68
- && existingSession.status !== "stopped" && existingSession.status !== "broken") {
71
+ && existingSession.status === "active") {
69
72
  throw new Error(`Leader Session is incompatible with desired effective launch: ${task.id}/${role.name}.`);
70
73
  }
71
74
  const resumableSession = hasNativeSession(existingSession)
72
- && existingSession.status !== "stopped"
73
- && existingSession.status !== "broken";
75
+ && existingSession.status === "active";
74
76
  // Reuse a healthy Session. A terminal Session is disposable and the next
75
77
  // Run starts fresh without consulting historical recovery records.
76
78
  const sessionSet = store.getTaskRoleSessionSet?.(task.id, role.name) ?? null;
77
- const mode = reopenIdentityDrift
79
+ mode = reopenIdentityDrift
78
80
  ? "new"
79
81
  : sessionSet === null
80
82
  ? resumableSession && compatibleSession ? "resume" : "new"
@@ -135,25 +137,17 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
135
137
  mode,
136
138
  runId: run.id,
137
139
  ...(mode === "resume" ? { nativeSessionId: existingSession.nativeSessionId } : {}),
140
+ ...(mode !== "resume" || existingSession?.launchId === undefined
141
+ ? {}
142
+ : { hostActivationId: existingSession.launchId }),
138
143
  beforeHostStart: (preflight) => {
139
144
  persistPreStartFence(store, task, role, run, existingSession, mode, preflight, now);
140
145
  effectiveSession = preflightSession(role, run.effective, existingSession, mode, preflight);
141
146
  preStartFencePersisted = true;
142
147
  }
143
148
  });
144
- // A managed Host may have submitted the exact Run Turn after its
145
- // two-phase launch handshake. Once preparation returns that transport
146
- // fact, any
147
- // later readiness or aggregate-write failure is delivery uncertainty,
148
- // not a launch failure: preserve the Run and its reservation for the
149
- // matching provider Hook instead of terminalizing it.
150
- deliveryAttempted = prepared.turnAcceptedDuringLaunch === true
151
- || prepared.turnDeliveryUnknownDuringLaunch === true;
152
- // Persist the exact preparation fence before waiting on provider
153
- // readiness. A pre-input lifecycle Hook may fire during that wait; it
154
- // must be able to resolve this Run/Session/launch generation from durable
155
- // state rather than an in-memory delivery object. Fresh providers that
156
- // discover their native Session later intentionally persist `null`.
149
+ // Persist the Session identity before submitting the Leader Turn.
150
+ // Starting/restoring a Session is independent from sending Run input.
157
151
  if (!preStartFencePersisted) {
158
152
  const preparedFenceSession = prepared.session === undefined
159
153
  ? existingSession
@@ -169,9 +163,6 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
169
163
  });
170
164
  }
171
165
  const ready = await delivery.waitUntilReady(prepared);
172
- deliveryAttempted = deliveryAttempted
173
- || ready.prepared.turnAcceptedDuringLaunch === true
174
- || ready.prepared.turnDeliveryUnknownDuringLaunch === true;
175
166
  const latestTask = store.getTask(task.id);
176
167
  if (latestTask === null
177
168
  || latestTask.status !== "active"
@@ -198,10 +189,13 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
198
189
  : { launchId: ready.prepared.launchId }),
199
190
  now
200
191
  });
201
- if (ready.prepared.turnBusyDuringLaunch === true) {
202
- // A direct Desktop/user Turn is ordinary thread activity. Keep the
203
- // claimed Run and wake batch intact; active-Run delivery retries after
204
- // that Turn reaches its native terminal boundary.
192
+ deliveryAttempted = true;
193
+ const outcome = await delivery.sendOnce({
194
+ delivery: ready,
195
+ receiptId: formatAgentRunReceiptId(task.id, run.id),
196
+ text: serializeRunBootstrapEnvelope(run.bootstrapEnvelope)
197
+ });
198
+ if (outcome === "busy" || outcome === "unavailable") {
205
199
  delivery.forgetPrepared?.({
206
200
  taskId: task.id,
207
201
  roleName: role.name,
@@ -214,12 +208,12 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
214
208
  taskId: task.id,
215
209
  runId: run.id,
216
210
  status: "skipped",
217
- reason: "busy"
211
+ reason: outcome === "busy" ? "busy" : "not-ready"
218
212
  });
219
213
  continue;
220
214
  }
221
- if (ready.prepared.turnRejectedDuringLaunch === true) {
222
- const persisted = store.saveRoleRunDeliveryFailure({
215
+ if (outcome === "rejected") {
216
+ const terminal = store.saveRoleRunDeliveryFailure({
223
217
  taskId: task.id,
224
218
  roleName: role.name,
225
219
  agentId: run.effective.agentId,
@@ -232,43 +226,10 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
232
226
  ...(ready.prepared.launchId === undefined
233
227
  ? {}
234
228
  : { launchId: ready.prepared.launchId }),
235
- summary: `Provider conclusively rejected the exact initial Leader Run input: ${run.id}.`,
229
+ summary: "Provider rejected the Leader Turn before acceptance; Operator recovery is required.",
236
230
  cleanupRequired: false,
237
231
  now
238
232
  });
239
- if (persisted === "failed") {
240
- delivery.forgetPrepared?.({
241
- taskId: task.id,
242
- roleName: role.name,
243
- runId: run.id,
244
- ...(ready.prepared.launchId === undefined
245
- ? {}
246
- : { launchId: ready.prepared.launchId })
247
- });
248
- }
249
- results.push({
250
- taskId: task.id,
251
- runId: run.id,
252
- status: persisted === "failed" ? "failed" : "skipped",
253
- reason: "provider-rejected",
254
- terminalized: persisted === "failed",
255
- ...(persisted === "failed"
256
- ? {}
257
- : { error: "Run state changed while recording exact Provider rejection." })
258
- });
259
- continue;
260
- }
261
- if (ready.prepared.turnDeliveryUnknownDuringLaunch === true) {
262
- store.saveRoleRunDelivery({
263
- task,
264
- role,
265
- run,
266
- session: effectiveSession,
267
- ...(ready.prepared.launchId === undefined
268
- ? {}
269
- : { launchId: ready.prepared.launchId }),
270
- now
271
- });
272
233
  delivery.forgetPrepared?.({
273
234
  taskId: task.id,
274
235
  roleName: role.name,
@@ -280,15 +241,13 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
280
241
  results.push({
281
242
  taskId: task.id,
282
243
  runId: run.id,
283
- status: "skipped",
284
- reason: "delivery-uncertain"
244
+ status: terminal === "failed" ? "failed" : "skipped",
245
+ reason: "provider-rejected",
246
+ ...(terminal === "failed" ? { terminalized: true } : {})
285
247
  });
286
248
  continue;
287
249
  }
288
- if (ready.prepared.turnAcceptedDuringLaunch === true) {
289
- // The Agent Host submitted the exact first Turn through structured
290
- // Provider control. Persist the transport fence without a second write;
291
- // the matching structured acknowledgement owns Run acceptance.
250
+ if (outcome === "delivery-unknown") {
292
251
  store.saveRoleRunDelivery({
293
252
  task,
294
253
  role,
@@ -307,10 +266,34 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
307
266
  ? {}
308
267
  : { launchId: ready.prepared.launchId })
309
268
  });
310
- results.push({ taskId: task.id, runId: run.id, status: "dispatched" });
269
+ results.push({
270
+ taskId: task.id,
271
+ runId: run.id,
272
+ status: "skipped",
273
+ reason: "delivery-uncertain"
274
+ });
311
275
  continue;
312
276
  }
313
- throw new Error("Managed Provider launch completed without an initial Turn outcome.");
277
+ store.saveRoleRunDelivery({
278
+ task,
279
+ role,
280
+ run,
281
+ session: effectiveSession,
282
+ ...(ready.prepared.launchId === undefined
283
+ ? {}
284
+ : { launchId: ready.prepared.launchId }),
285
+ now
286
+ });
287
+ delivery.forgetPrepared?.({
288
+ taskId: task.id,
289
+ roleName: role.name,
290
+ runId: run.id,
291
+ ...(ready.prepared.launchId === undefined
292
+ ? {}
293
+ : { launchId: ready.prepared.launchId })
294
+ });
295
+ results.push({ taskId: task.id, runId: run.id, status: "dispatched" });
296
+ continue;
314
297
  }
315
298
  catch (error) {
316
299
  const detail = error instanceof Error ? error.message : String(error);
@@ -336,6 +319,57 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
336
319
  });
337
320
  continue;
338
321
  }
322
+ store.recordAgentError?.({
323
+ taskId: task.id,
324
+ roleName: role.name,
325
+ runId: run.id,
326
+ source: error instanceof RuntimeLaunchError ? "host" : "yui",
327
+ phase: deliveryAttempted
328
+ ? "turn-submit"
329
+ : mode === "new" ? "session-start" : "session-restore",
330
+ message: detail,
331
+ raw: serializeAgentErrorRaw(error),
332
+ inputDisposition: deliveryAttempted ? "unknown" : "not-accepted"
333
+ }, now);
334
+ if (!deliveryAttempted) {
335
+ const failedSession = store.getRoleSession(task.id, role.name, run.effective.agentId);
336
+ const terminal = store.saveRoleRunDeliveryFailure({
337
+ taskId: task.id,
338
+ roleName: role.name,
339
+ agentId: run.effective.agentId,
340
+ adapterId: run.effective.adapterId,
341
+ runId: run.id,
342
+ mailboxBatchId: formatAgentRunReceiptId(task.id, run.id),
343
+ ...(failedSession?.nativeSessionId === undefined
344
+ ? {}
345
+ : { nativeSessionId: failedSession.nativeSessionId }),
346
+ ...(prepared?.launchId === undefined && failedSession?.launchId === undefined
347
+ ? {}
348
+ : { launchId: prepared?.launchId ?? failedSession.launchId }),
349
+ summary: `Leader Session preparation failed before Turn acceptance: ${message}`,
350
+ cleanupRequired: false,
351
+ now
352
+ });
353
+ if (terminal === "failed") {
354
+ delivery.forgetPrepared?.({
355
+ taskId: task.id,
356
+ roleName: role.name,
357
+ runId: run.id,
358
+ ...(prepared?.launchId === undefined
359
+ ? {}
360
+ : { launchId: prepared.launchId })
361
+ });
362
+ results.push({
363
+ taskId: task.id,
364
+ runId: run.id,
365
+ status: "failed",
366
+ reason: "unavailable",
367
+ error: message,
368
+ terminalized: true
369
+ });
370
+ continue;
371
+ }
372
+ }
339
373
  // A managed Provider lifecycle operation may still be settling. Keep
340
374
  // this newly-claimed Run as the sole mailbox owner; active-Run delivery
341
375
  // retries after the lifecycle lane clears.
@@ -372,20 +406,13 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
372
406
  ? {}
373
407
  : { launchId: prepared.launchId })
374
408
  });
375
- const failureResult = store.saveLeaderDispatchFailure({
376
- task,
377
- role,
378
- session: effectiveSession,
379
- claimed: { run, wakeup },
380
- failure: recordLeaderFailure(task.id, effectiveSession?.nativeSessionId ?? "(unregistered)", message, now, store.getLeaderFailure(task.id)),
381
- now
409
+ results.push({
410
+ taskId: task.id,
411
+ runId: run.id,
412
+ status: "skipped",
413
+ reason: "not-ready",
414
+ error: message
382
415
  });
383
- if (failureResult === "state-changed") {
384
- results.push({ taskId: task.id, status: "skipped", reason: "state-changed" });
385
- }
386
- else {
387
- results.push({ taskId: task.id, runId: run.id, status: "failed", error: message });
388
- }
389
416
  continue;
390
417
  }
391
418
  results.push({
@@ -399,6 +426,14 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
399
426
  }
400
427
  return results;
401
428
  }
429
+ function providerTurnIsUnsettled(store, taskId, roleName) {
430
+ if (store.getRoleSession(taskId, roleName)?.status !== "active")
431
+ return false;
432
+ const status = store.getTaskRoleSessionSet?.(taskId, roleName)
433
+ ?.providerBinding?.turn?.status;
434
+ return status !== undefined
435
+ && ["submitting", "accepted", "running", "delivery-unknown"].includes(status);
436
+ }
402
437
  function validateReadySession(role, effective, existing, mode, session) {
403
438
  if (mode === "new" && session === null)
404
439
  return null;
@@ -421,7 +456,7 @@ function validateReadySession(role, effective, existing, mode, session) {
421
456
  }
422
457
  return {
423
458
  ...session,
424
- status: "running",
459
+ status: "active",
425
460
  ...(mode === "resume" && existing !== null
426
461
  ? { effective: existing.effective }
427
462
  : {})
@@ -454,7 +489,7 @@ function preflightSession(role, effective, existing, mode, preflight) {
454
489
  nativeSessionId: preflight.nativeSessionId,
455
490
  launchId: preflight.launchId,
456
491
  ...(preflight.sessionTitle === undefined ? {} : { title: preflight.sessionTitle }),
457
- status: "ready",
492
+ status: "active",
458
493
  effective: preflight.effective
459
494
  };
460
495
  return validateReadySession(role, effective, existing, mode, session);
@@ -125,7 +125,10 @@ export async function reconcileExitedRoleRuns(store, delivery, now, selection, e
125
125
  ...(run.workspace === undefined ? {} : { managedWorkspace: run.workspace }),
126
126
  mode: "resume",
127
127
  runId: run.id,
128
- nativeSessionId: session.nativeSessionId
128
+ nativeSessionId: session.nativeSessionId,
129
+ ...(session.launchId === undefined
130
+ ? {}
131
+ : { hostActivationId: session.launchId })
129
132
  });
130
133
  store.saveRoleRunPrepared({
131
134
  task,
@@ -56,16 +56,12 @@ export function projectRoleRunHealth(input) {
56
56
  const session = input.nativeSession;
57
57
  const nativeSession = session === null || session === undefined
58
58
  ? "missing"
59
- : session.status === "stopped"
60
- ? "stopped"
61
- : session.status === "broken"
62
- ? "broken"
63
- : session.status !== "ready" && session.status !== "running"
64
- ? "unknown"
65
- : !hasResourceIdentityText(session.nativeSessionId)
66
- && !hasResourceIdentityText(session.launchId)
67
- ? "unknown"
68
- : "matching";
59
+ : session.status === "ended"
60
+ ? session.endReason === "failed" ? "broken" : "stopped"
61
+ : !hasResourceIdentityText(session.nativeSessionId)
62
+ && !hasResourceIdentityText(session.launchId)
63
+ ? "unknown"
64
+ : "matching";
69
65
  const resourceActivity = hostLiveness === "present"
70
66
  && nativeSession === "matching"
71
67
  && resourceEvidenceIsFresh(input.resource, input.now, windowMs, diagnosticAfterMs)
@@ -689,7 +685,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
689
685
  || (candidate.session.agentId === runAgentId
690
686
  && candidate.session.adapterId === runAdapterId);
691
687
  const sessionUsable = candidate.session === null
692
- || (candidate.session.status !== "stopped" && candidate.session.status !== "broken");
688
+ || candidate.session.status === "active";
693
689
  const expectedResourceIdentity = candidate.session === null
694
690
  ? undefined
695
691
  : {
@@ -786,7 +782,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
786
782
  continue;
787
783
  const evidenceKey = [
788
784
  kind,
789
- stallEvidenceKey(candidate.role.status, candidate.session?.status),
785
+ stallEvidenceKey(candidate.session?.status),
790
786
  classification,
791
787
  ...(candidate.role.name === "leader"
792
788
  ? [leaderStallEvidence(store, candidate.task.id, observed, now, windowMs)]
@@ -1001,8 +997,8 @@ function leaderStallEvidence(store, taskId, observed, now, windowMs) {
1001
997
  : "recent";
1002
998
  return `downstream=active:${active},healthy:${healthy},stalled:${stalled}:leader-mailbox=${pendingAge},leader-processing=${processingAge}`;
1003
999
  }
1004
- function stallEvidenceKey(roleStatus, sessionStatus) {
1005
- return `live-pane-no-progress:role=${roleStatus}:session=${sessionStatus ?? "unknown"}`;
1000
+ function stallEvidenceKey(sessionStatus) {
1001
+ return `live-pane-no-progress:session=${sessionStatus ?? "unknown"}`;
1006
1002
  }
1007
1003
  function exactLiveStatuses(statuses, candidates) {
1008
1004
  const expected = new Set(candidates.map(({ task, role }) => `${task.id}\0${role.name}`));
@@ -17,8 +17,12 @@ export const WAKE_REASON_KINDS = Object.freeze([
17
17
  "task-orphaned",
18
18
  "input-timeout",
19
19
  "role-run-stalled",
20
+ "agent-error",
21
+ "agent-session-recovery",
20
22
  "review-failed",
21
23
  "task-reopened",
24
+ "agent-error",
25
+ "agent-session-recovery",
22
26
  "leader-run-failed",
23
27
  "role-run-failed",
24
28
  "job-finished",