@zq-silk/yui 0.6.16 → 0.7.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.
Files changed (69) hide show
  1. package/dist/cli/commandCatalog.js +3 -7
  2. package/dist/cli.js +12 -33
  3. package/dist/commands/executionAuditCommands.js +19 -0
  4. package/dist/commands/globalRoleCommands.js +70 -0
  5. package/dist/commands/taskActor.js +3 -2
  6. package/dist/commands/taskCommands.js +160 -41
  7. package/dist/commands/taskContextCommand.js +1 -1
  8. package/dist/commands/taskInputCommands.js +3 -2
  9. package/dist/commands/taskRoleRuntimeStatus.js +3 -3
  10. package/dist/context/contextSnapshot.js +228 -0
  11. package/dist/context/roleSessionContext.js +3 -1
  12. package/dist/context/runContextContract.js +162 -0
  13. package/dist/context/runContextPack.js +322 -0
  14. package/dist/context/sessionBootstrapManifest.js +81 -0
  15. package/dist/context/sessionProtocolIdentity.js +23 -0
  16. package/dist/controller/agentRuntimeObserver.js +6 -1
  17. package/dist/controller/controller.js +4 -3
  18. package/dist/controller/fileSchedulerStoreAdapter.js +446 -145
  19. package/dist/controller/jobControl.js +2 -1
  20. package/dist/controller/runtime.js +83 -0
  21. package/dist/controller/runtimeHookRunFence.js +6 -2
  22. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  23. package/dist/executor/agentAdapter.js +7 -2
  24. package/dist/executor/agentExecutor.js +23 -0
  25. package/dist/executor/effectiveLaunch.js +24 -0
  26. package/dist/executor/executorRegistry.js +7 -1
  27. package/dist/executor/fileRoleLaunchPlanner.js +73 -27
  28. package/dist/lifecycle/exactRunTerminalization.js +2 -3
  29. package/dist/lifecycle/providerErrorClass.js +8 -3
  30. package/dist/observability/executionAudit.js +87 -2
  31. package/dist/repository/taskWorkspacePreparer.js +2 -2
  32. package/dist/run/agentRun.js +101 -16
  33. package/dist/run/providerRetry.js +167 -56
  34. package/dist/run/providerRetryConfig.js +5 -1
  35. package/dist/run/runControlRequest.js +50 -0
  36. package/dist/runtime/agentDriver.js +47 -0
  37. package/dist/runtime/agentHost.js +327 -0
  38. package/dist/runtime/builtinAgentDrivers.js +23 -1
  39. package/dist/runtime/builtinTranscriptObserver.js +4 -0
  40. package/dist/runtime/builtinTranscriptUsage.js +2 -0
  41. package/dist/runtime/exactControlPlane.js +2 -2
  42. package/dist/runtime/globalProcessExitStore.js +38 -0
  43. package/dist/runtime/launchBroker.js +95 -0
  44. package/dist/runtime/processExitObservation.js +60 -0
  45. package/dist/runtime/runtimeBinding.js +6 -0
  46. package/dist/runtime/runtimeObservation.js +27 -6
  47. package/dist/runtime/runtimeProjection.js +6 -3
  48. package/dist/runtime/runtimeStopReceipt.js +42 -0
  49. package/dist/runtime/sessionTerminationGuard.js +13 -0
  50. package/dist/runtime/tmuxAdapters.js +203 -220
  51. package/dist/scheduler/activeRoleRunDelivery.js +24 -3
  52. package/dist/scheduler/leaderWakeupProcessor.js +18 -60
  53. package/dist/scheduler/roleRunLiveness.js +61 -27
  54. package/dist/storage/migration/productionRegistry.js +264 -0
  55. package/dist/storage/sqliteSchema.js +23 -2
  56. package/dist/storage/sqliteStore.js +39 -2
  57. package/dist/storage/taskStore.js +54 -5
  58. package/dist/storage/upgrade/recordVersions.js +3 -1
  59. package/dist/storage/upgrade/sqliteStateMigration.js +10 -0
  60. package/dist/task/taskRecordReference.js +1 -0
  61. package/dist/tmux/tmuxManager.js +15 -4
  62. package/dist/web/assets/client/components.js +1 -1
  63. package/package.json +1 -1
  64. package/skills/yui-leader/SKILL.md +10 -5
  65. package/skills/yui-operator/SKILL.md +4 -0
  66. package/skills/yui-reviewer/SKILL.md +4 -0
  67. package/skills/yui-runtime/SKILL.md +61 -0
  68. package/skills/yui-worker/SKILL.md +82 -218
  69. package/dist/executor/managedClaudeRunner.js +0 -121
@@ -12,6 +12,7 @@ import { createHash } from "node:crypto";
12
12
  import { resolve, sep } from "node:path";
13
13
  import { acknowledgeUnknownDurableJob, createDurableJob, durableJobIdempotencyKey, isDurableJobTerminal, requestDurableJobCancel, retryDurableJobIdempotencyKey } from "../job/durableJob.js";
14
14
  import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
15
+ import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
15
16
  export function createDurableJobControl(store) {
16
17
  return {
17
18
  startJob(params, now) {
@@ -350,7 +351,7 @@ function assertLeaderActionRun(store, taskId, assertion) {
350
351
  if (sessions === null
351
352
  || sessions.inFlight === null
352
353
  || sessions.inFlight.runId !== run.id
353
- || sessions.inFlight.receiptId !== expectedReceipt) {
354
+ || sessions.inFlight.receiptId !== agentRunDeliveryReceiptId(run)) {
354
355
  throw jobControlError("UNAUTHORIZED", "job.acknowledge Leader Run is not in flight.");
355
356
  }
356
357
  }
@@ -34,6 +34,12 @@ import { ResourceInventoryClient } from "./resourceInventoryRpc.js";
34
34
  import { createResourceAutoGc } from "../resources/autoResourceGc.js";
35
35
  import { createRuntimeResourceActivityTracker } from "./resourceInventory.js";
36
36
  import { SessionOwnerReconciliation } from "./sessionOwnerReconciliation.js";
37
+ import { launchBrokerForHome } from "../runtime/launchBroker.js";
38
+ import { classifyRuntimeProcessExit, validateRuntimeProcessExitObservation } from "../runtime/processExitObservation.js";
39
+ import { appendGlobalProcessExitObservation } from "../runtime/globalProcessExitStore.js";
40
+ import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
41
+ import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
42
+ import { createTaskEvent } from "../event/taskEvent.js";
37
43
  /** Refreshes only the exact Task runtime generation folded by the event transaction. */
38
44
  export function refreshAppliedTaskRuntimeDescriptor(store, planner, input) {
39
45
  if (input.launchId === undefined)
@@ -521,6 +527,83 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
521
527
  });
522
528
  const lifecycleTails = new Map();
523
529
  return async (method, params) => {
530
+ if (method === "runtime.process-exit-observe") {
531
+ const observation = validateRuntimeProcessExitObservation(params);
532
+ const run = observation.taskId === undefined || observation.runId === undefined
533
+ ? null
534
+ : store.getAgentRun(observation.taskId, observation.runId);
535
+ const globalRole = observation.taskId === undefined
536
+ ? store.getGlobalRole(observation.roleName)
537
+ : null;
538
+ const adapterId = run?.effective.adapterId
539
+ ?? globalRole?.agentBindings[globalRole.activeAgentId]?.adapterId;
540
+ const driver = adapterId === undefined
541
+ ? null
542
+ : builtinAgentDriverRegistry().findByAdapterId(adapterId);
543
+ const turnTerminalObserved = observation.taskId !== undefined
544
+ && observation.runId !== undefined
545
+ && store.listEvents(observation.taskId).some((event) => {
546
+ const runtime = runtimeObservationFromTaskEvent(event);
547
+ return runtime !== null
548
+ && runtime.fence.runId === observation.runId
549
+ && ["turn.completed", "turn.failed", "turn.cancelled"].includes(runtime.kind)
550
+ && Date.parse(runtime.receivedAt) <= Date.parse(observation.observedAt);
551
+ });
552
+ const turnFailureObserved = observation.taskId !== undefined
553
+ && observation.runId !== undefined
554
+ && store.listEvents(observation.taskId).some((event) => {
555
+ const runtime = runtimeObservationFromTaskEvent(event);
556
+ return runtime !== null
557
+ && runtime.fence.runId === observation.runId
558
+ && runtime.fence.launchId === observation.launchId
559
+ && runtime.kind === "turn.failed"
560
+ && Date.parse(runtime.receivedAt) <= Date.parse(observation.observedAt);
561
+ });
562
+ const classification = classifyRuntimeProcessExit(observation, {
563
+ ...(driver === null
564
+ ? {}
565
+ : { childLifecycle: driver.capabilities.lifecycle.providerProcess }),
566
+ turnTerminalObserved,
567
+ turnFailureObserved
568
+ });
569
+ if (observation.taskId === undefined) {
570
+ const recorded = appendGlobalProcessExitObservation(store.rootDirectory(), observation, classification);
571
+ return { recorded, scope: "global", classification };
572
+ }
573
+ const recorded = store.transaction((tx) => {
574
+ if (tx.getTask(observation.taskId) === null) {
575
+ throw applicationError("INVALID_PARAMS", `Task not found: ${observation.taskId}.`);
576
+ }
577
+ const duplicate = tx.listEvents(observation.taskId).some((event) => (event.type === "runtime.process-exit-observed"
578
+ && event.payload.observationId === observation.observationId));
579
+ if (duplicate)
580
+ return false;
581
+ tx.saveEvent(observation.taskId, createTaskEvent(tx.nextEventId(observation.taskId), observation.taskId, "runtime.process-exit-observed", {
582
+ observationId: observation.observationId,
583
+ processKind: observation.processKind,
584
+ roleName: observation.roleName,
585
+ launchId: observation.launchId,
586
+ observedAt: observation.observedAt,
587
+ classification,
588
+ observation: JSON.stringify(observation)
589
+ }, new Date(observation.observedAt)));
590
+ return true;
591
+ });
592
+ return { recorded, classification };
593
+ }
594
+ if (method === "runtime.launch-redeem") {
595
+ if (params === null || typeof params !== "object" || Array.isArray(params)) {
596
+ throw applicationError("INVALID_PARAMS", "Launch redemption params are invalid.");
597
+ }
598
+ const launchId = params.launchId;
599
+ const ticket = params.ticket;
600
+ const hostPid = params.hostPid;
601
+ if (typeof launchId !== "string" || typeof ticket !== "string"
602
+ || !Number.isSafeInteger(hostPid) || hostPid <= 0) {
603
+ throw applicationError("INVALID_PARAMS", "Launch redemption identity is invalid.");
604
+ }
605
+ return launchBrokerForHome(store.rootDirectory()).redeem(launchId, ticket);
606
+ }
524
607
  if (method === "runtime.replace-agent-environment") {
525
608
  if (environmentRefresher === undefined) {
526
609
  throw applicationError("METHOD_NOT_FOUND", "Controller method was not found.");
@@ -3,7 +3,7 @@ import { hasRuntimeCleanupObligation, isRuntimeLaunchReservation, runtimeLifecyc
3
3
  import { nativeSessionIdForLaunch } from "../runtime/preallocatedNativeSession.js";
4
4
  import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
5
5
  import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertExactTaskRuntimeEnvironment, exactControlPlaneDigest, parseExactControlPlaneDescriptor, refreshReusedTaskRuntimeDescriptorSource } from "../runtime/exactControlPlane.js";
6
- import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
6
+ import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
7
7
  /**
8
8
  * Resolves turn identity from the current durable in-flight fence. The one
9
9
  * exception is a Driver-declared startup Session Hook, which can arrive before
@@ -98,6 +98,9 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
98
98
  && executionRef?.type === "run"
99
99
  && executionRef.taskId === taskId
100
100
  && executionRef.id === startupRunId;
101
+ const startupRun = startupRunId === undefined
102
+ ? null
103
+ : store.getAgentRun(taskId, startupRunId);
101
104
  const preallocatedStartup = options.startupSession === "preallocated"
102
105
  && expectedNativeSessionId !== undefined
103
106
  && session === undefined
@@ -137,7 +140,8 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
137
140
  && inFlight !== null
138
141
  && inFlight !== undefined
139
142
  && (inFlight.runId !== startupRunId
140
- || inFlight.receiptId !== formatAgentRunReceiptId(taskId, startupRunId))) {
143
+ || startupRun === null
144
+ || inFlight.receiptId !== agentRunDeliveryReceiptId(startupRun))) {
141
145
  throw new Error("Runtime observation Hook has no matching durable in-flight Run.");
142
146
  }
143
147
  const runId = acceptedBinding?.fence.runId
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { createTaskEvent } from "../event/taskEvent.js";
4
4
  import { createSessionOwnerIdentity, discoverProviderRootByLaunchEnv, isLinuxProcessLive, listLaunchFencedProcesses, listOwnedProcessTree, readLinuxProcessIdentity, reconcileSessionOwners, terminateSessionOwners } from "../runtime/index.js";
5
+ import { removeRuntimeStopReceipt, writeRuntimeStopReceipt } from "../runtime/runtimeStopReceipt.js";
5
6
  import { tmuxSocketDirectory } from "../tmux/tmuxSocketEndpoint.js";
6
7
  import { yuiTmuxServerName, yuiTmuxSessionName, yuiTmuxTarget } from "../tmux/tmuxManager.js";
7
8
  /**
@@ -148,12 +149,16 @@ export class SessionOwnerReconciliation {
148
149
  if (result.outcome === "stop-confirmed") {
149
150
  for (const record of result.confirmed) {
150
151
  this.#store.removeSessionOwner(record.launchId);
152
+ removeRuntimeStopReceipt(this.#home, record.launchId);
151
153
  }
152
154
  }
153
155
  return result;
154
156
  }
155
157
  #recordTerminationEvent(event) {
156
158
  const owner = event.owner;
159
+ if (event.stage === "stop-requested" && event.launchId !== undefined) {
160
+ writeRuntimeStopReceipt(this.#home, event.launchId, event.at);
161
+ }
157
162
  if (owner.scope !== "task")
158
163
  return;
159
164
  try {
@@ -126,7 +126,7 @@ class CodexAdapter extends BaseAdapter {
126
126
  "--config",
127
127
  `projects={${JSON.stringify(resolve(input.workspace))}={trust_level="trusted"}}`
128
128
  ];
129
- const instructions = [
129
+ const instructions = input.sessionManifestPath === undefined ? [
130
130
  input.developerInstructions,
131
131
  ...(input.skills === undefined || input.skills.length === 0
132
132
  ? []
@@ -134,7 +134,9 @@ class CodexAdapter extends BaseAdapter {
134
134
  "Yui Role Skills are available at the paths below. Before performing work governed by one, read and follow its SKILL.md on demand; do not treat this list as a user message.",
135
135
  ...input.skills.map((skill) => `- ${skill.id}: ${skill.path}/SKILL.md`)
136
136
  ])
137
- ].filter((value) => value !== undefined && value.trim().length > 0);
137
+ ].filter((value) => value !== undefined && value.trim().length > 0) : [
138
+ `Yui managed Session. Read and follow the Session Manifest at ${input.sessionManifestPath} (digest ${input.sessionManifestDigest ?? "unknown"}). Load each Skill and Role Profile by its manifest path before acting; Task content is available only through the manifest's exact Context API.`
139
+ ];
138
140
  if (instructions.length === 0)
139
141
  return workspaceTrust;
140
142
  const nativeInstructions = input.codexDeveloperInstructions
@@ -236,6 +238,9 @@ class ClaudeAdapter extends BaseAdapter {
236
238
  };
237
239
  }
238
240
  launchContextArgs(input) {
241
+ if (input.sessionManifestPath !== undefined) {
242
+ return ["--append-system-prompt-file", input.sessionManifestPath];
243
+ }
239
244
  const sections = [
240
245
  input.developerInstructions,
241
246
  ...(input.skills ?? []).map((skill) => [
@@ -412,6 +412,29 @@ export function recordObservedTaskRoleCompletion(set, completion) {
412
412
  turnId: observed.turnId
413
413
  }, new Date(observed.observedAt));
414
414
  }
415
+ /**
416
+ * Rebinds the same active Run to a bounded control-input receipt while keeping
417
+ * its observed native Turn completion unsettled. No new Run fence is created.
418
+ */
419
+ export function rebindTaskRoleRunControlReceipt(set, input, now) {
420
+ validateRoleSessionSet(set);
421
+ assertTaskRoleSessionSet(set);
422
+ const inFlight = set.inFlight;
423
+ if (inFlight === null
424
+ || inFlight.agentId !== input.agentId
425
+ || inFlight.runId !== input.runId) {
426
+ throw new Error("Workflow outcome control request does not match the unsettled Run.");
427
+ }
428
+ const timestamp = requireDate(now, "Workflow outcome control receipt timestamp");
429
+ return validateRoleSessionSet({
430
+ ...set,
431
+ inFlight: {
432
+ ...inFlight,
433
+ receiptId: requireSafeIdentity(input.receiptId, "Workflow outcome control receipt id")
434
+ },
435
+ updatedAt: timestamp
436
+ });
437
+ }
415
438
  export function clearTaskRoleRun(set, fence, clearedAt) {
416
439
  validateRoleSessionSet(set);
417
440
  assertTaskRoleSessionSet(set);
@@ -1,6 +1,8 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
2
  import { validateManagedWorkspace } from "../worktree/managedWorkspace.js";
3
3
  import { resolveAgentAdapter } from "./agentAdapter.js";
4
+ import { roleSessionKind } from "../context/roleSessionContext.js";
5
+ import { SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION, sessionManifestCompatibilityDigest } from "../context/sessionProtocolIdentity.js";
4
6
  export function resolveEffectiveLaunch(input) {
5
7
  validateDesiredRole(input.role);
6
8
  const binding = input.role.agentBindings[input.role.activeAgentId];
@@ -15,6 +17,10 @@ export function resolveEffectiveLaunch(input) {
15
17
  writeProjectIds,
16
18
  workspace,
17
19
  context: snapshotContext(input.role),
20
+ contextProtocolVersion: SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION,
21
+ sessionManifestCompatibilityDigest: sessionManifestCompatibilityDigest(input.role.name, roleSessionKind(input.role, "taskId" in input.role
22
+ ? { scope: "task", taskId: input.role.taskId }
23
+ : { scope: "global" }, input.purpose), input.role),
18
24
  ...(input.purpose === "review"
19
25
  ? {
20
26
  reviewRoundId: identity(input.reviewRoundId ?? "", "ReviewRound id"),
@@ -152,6 +158,18 @@ export function validateEffectiveLaunchSnapshot(snapshot) {
152
158
  identity(snapshot.reviewRoundId, "Effective ReviewRound id");
153
159
  commit(snapshot.reviewBaseCommit, "Effective review base commit");
154
160
  }
161
+ if (snapshot.contextProtocolVersion !== undefined
162
+ && snapshot.contextProtocolVersion !== SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION) {
163
+ throw new Error("Effective launch context protocol version is unsupported.");
164
+ }
165
+ if (snapshot.sessionManifestCompatibilityDigest !== undefined
166
+ && !/^[a-f0-9]{64}$/u.test(snapshot.sessionManifestCompatibilityDigest)) {
167
+ throw new Error("Effective launch Session Manifest compatibility digest is invalid.");
168
+ }
169
+ if ((snapshot.contextProtocolVersion === undefined)
170
+ !== (snapshot.sessionManifestCompatibilityDigest === undefined)) {
171
+ throw new Error("Effective launch Context protocol compatibility identity is incomplete.");
172
+ }
155
173
  validateWorkspace(snapshot.workspace);
156
174
  cloneContext(snapshot.context);
157
175
  const config = effectiveLaunchConfigUnchecked(snapshot);
@@ -204,6 +222,12 @@ function snapshotFromConfig(input) {
204
222
  writeProjectIds: [...input.writeProjectIds],
205
223
  workspace: cloneWorkspace(input.workspace),
206
224
  context: cloneContext(input.context),
225
+ ...(input.contextProtocolVersion === undefined
226
+ ? {}
227
+ : { contextProtocolVersion: input.contextProtocolVersion }),
228
+ ...(input.sessionManifestCompatibilityDigest === undefined
229
+ ? {}
230
+ : { sessionManifestCompatibilityDigest: input.sessionManifestCompatibilityDigest }),
207
231
  ...review
208
232
  };
209
233
  const snapshot = config.adapterId === "codex"
@@ -337,11 +337,17 @@ export class ExecutorRegistry {
337
337
  return inputs.map((input) => {
338
338
  const key = `${input.taskId}\0${input.roleName}`;
339
339
  const resource = resources.get(key);
340
+ const deadPane = inventory.find((pane) => (pane.taskId === input.taskId && pane.roleName === input.roleName && pane.dead));
340
341
  return {
341
342
  taskId: input.taskId,
342
343
  roleName: input.roleName,
343
344
  status: present.has(key) ? "present" : "absent",
344
- ...(resource === undefined ? {} : { resource })
345
+ ...(resource === undefined ? {} : { resource }),
346
+ ...(deadPane === undefined
347
+ ? {}
348
+ : { hostExit: {
349
+ ...(deadPane.deadStatus === undefined ? {} : { deadStatus: deadPane.deadStatus })
350
+ } })
345
351
  };
346
352
  });
347
353
  }
@@ -7,7 +7,10 @@ import { configuredAgentToDefinition, resolveAgentEnvironment } from "../agent/a
7
7
  import { NATIVE_AGENT_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, operationalAgentEnvironment, selectEnvironment } from "../agent/launchEnvironment.js";
8
8
  import { activeRoleAgentBinding } from "../role/role.js";
9
9
  import { writeTextFileAtomically } from "../storage/durableFile.js";
10
- import { compileRoleSessionContext } from "../context/roleSessionContext.js";
10
+ import { compileRoleSessionContext, roleSessionKind } from "../context/roleSessionContext.js";
11
+ import { materializeSessionBootstrap } from "../context/sessionBootstrapManifest.js";
12
+ import { serializeRunBootstrapEnvelope, serializeRunHostRecoveryEnvelope } from "../context/runContextContract.js";
13
+ import { serializeProviderRetryEnvelope } from "../run/providerRetry.js";
11
14
  import { resolveAgentAdapter } from "./agentAdapter.js";
12
15
  import { inspectCodexLaunchConfig } from "./codexConfigConflict.js";
13
16
  import { taskRoleSessionTitle } from "../runtime/sessionTitle.js";
@@ -285,15 +288,27 @@ export class FileRoleLaunchPlanner {
285
288
  ? {}
286
289
  : taskRuntimeIsolationEnvironment(runtimeIsolation));
287
290
  const baseSessionContext = compileRoleSessionContext(this.home, launchRole, owner, sessionPolicy);
288
- const sessionContext = owner.scope === "task"
289
- ? {
290
- ...baseSessionContext,
291
- developerInstructions: [
292
- baseSessionContext.developerInstructions,
293
- renderExactControlPlaneInstructions(this.#controlPlane)
294
- ].join("\n")
295
- }
296
- : baseSessionContext;
291
+ const bootstrap = materializeSessionBootstrap({
292
+ yuiHome: this.home,
293
+ role: launchRole,
294
+ owner,
295
+ roleKind: roleSessionKind(launchRole, owner, sessionPolicy.purpose),
296
+ skills: baseSessionContext.skills,
297
+ controlPlane: this.#controlPlane
298
+ });
299
+ if (effective.contextProtocolVersion !== bootstrap.manifest.schemaVersion
300
+ || effective.sessionManifestCompatibilityDigest
301
+ !== bootstrap.manifest.compatibilityDigest) {
302
+ throw new Error("Effective launch Context protocol identity does not match the materialized Session Manifest.");
303
+ }
304
+ const sessionContext = {
305
+ ...baseSessionContext,
306
+ developerInstructions: `Read and follow the exact Yui Session Manifest at ${bootstrap.manifestPath}.`,
307
+ managedContextFile: bootstrap.manifestPath,
308
+ sessionManifestPath: bootstrap.manifestPath,
309
+ sessionManifestDigest: bootstrap.manifest.digest,
310
+ sessionCliPath: bootstrap.sessionCliPath
311
+ };
297
312
  const codexConfig = binding.config.adapterId === "codex"
298
313
  ? inspectCodexLaunchConfig({
299
314
  environment: {
@@ -327,7 +342,7 @@ export class FileRoleLaunchPlanner {
327
342
  const roleConfig = binding.config.adapterId === "claude"
328
343
  && owner.scope === "task"
329
344
  && input.runId !== undefined
330
- ? managedClaudeControlPlaneConfig(binding.config, owner.taskId, managedRun?.workItemId, input.runId, this.#controlPlane)
345
+ ? managedClaudeControlPlaneConfig(binding.config, owner.taskId, managedRun?.workItemId, input.runId, this.#controlPlane, sessionContext.sessionCliPath)
331
346
  : binding.config;
332
347
  const effectiveConfig = withNativeProjectDirectories(roleConfig, nativeAdditionalDirectories(effective.workspace, agentWorkspace));
333
348
  const compileInput = {
@@ -360,8 +375,17 @@ export class FileRoleLaunchPlanner {
360
375
  nativeSessionId: resumeNativeSessionId
361
376
  })
362
377
  : adapter.compileNew(compileInput);
363
- if (owner.scope === "task" && sessionContext.managedContextFile !== undefined) {
364
- this.#resourceRegistrar().registerSessionContext(sessionContext.managedContextFile, { home: resolve(this.home), taskId: owner.taskId, basis: "descriptor" });
378
+ for (const path of [
379
+ bootstrap.manifestPath,
380
+ bootstrap.sessionCliPath,
381
+ bootstrap.roleProfilePath,
382
+ bootstrap.descriptorPath
383
+ ]) {
384
+ this.#resourceRegistrar().registerSessionContext(path, {
385
+ home: resolve(this.home),
386
+ ...(owner.scope === "task" ? { taskId: owner.taskId } : {}),
387
+ basis: "descriptor"
388
+ });
365
389
  }
366
390
  let args = [...compiled.argv];
367
391
  let command = configured.command;
@@ -384,8 +408,9 @@ export class FileRoleLaunchPlanner {
384
408
  args = addCodexLifecycleHooks(args, launchMode, this.#cliPath);
385
409
  // End option parsing before the opaque prompt so a wakeup beginning
386
410
  // with '-' can never be reinterpreted as a Codex CLI flag.
387
- if (managedRun.pushedAt === undefined)
388
- args.push("--", managedRun.input);
411
+ if (managedRun.pushedAt === undefined) {
412
+ args.push("--", managedRunLaunchEnvelope(managedRun, input.mode));
413
+ }
389
414
  }
390
415
  session = launchMode === "resume"
391
416
  ? readySession(input.agentId, binding.adapterId, resumeNativeSessionId, effective)
@@ -412,16 +437,8 @@ export class FileRoleLaunchPlanner {
412
437
  const managedClaudeRun = binding.adapterId === "claude"
413
438
  && owner.scope === "task"
414
439
  && input.runId !== undefined;
415
- if (managedClaudeRun) {
416
- command = process.execPath;
417
- args = [
418
- this.#cliPath,
419
- "internal",
420
- "managed-claude-run",
421
- "--",
422
- configured.command,
423
- ...args
424
- ];
440
+ if (managedClaudeRun && (managedRun === null || managedRun.status !== "active")) {
441
+ throw new Error(`Managed Claude Run is no longer active: ${input.runId}.`);
425
442
  }
426
443
  const runtimeDescriptor = owner.scope === "task"
427
444
  ? createExactTaskRuntimeDescriptor({
@@ -455,6 +472,18 @@ export class FileRoleLaunchPlanner {
455
472
  const launch = {
456
473
  command,
457
474
  args,
475
+ ...(managedRun?.providerRetry !== undefined
476
+ && managedRun.providerRetry.state !== "dispatching"
477
+ ? { deferProviderStart: true }
478
+ : {}),
479
+ ...(managedClaudeRun
480
+ ? {
481
+ providerInput: {
482
+ kind: "stdin-json-user-message",
483
+ boundedText: managedRunLaunchEnvelope(managedRun, input.mode)
484
+ }
485
+ }
486
+ : {}),
458
487
  env: {
459
488
  ...launchEnvironment,
460
489
  YUI_HOME: resolve(this.home),
@@ -465,6 +494,8 @@ export class FileRoleLaunchPlanner {
465
494
  YUI_ADAPTER_ID: configured.adapterId,
466
495
  YUI_DRIVER_ID: driver.id,
467
496
  YUI_WORKSPACE: effectiveWorkspace,
497
+ YUI_SESSION_MANIFEST: sessionContext.sessionManifestPath,
498
+ YUI_SESSION_CLI: sessionContext.sessionCliPath,
468
499
  ...(jobCallerKey === undefined ? {} : { YUI_JOB_CALLER_KEY: jobCallerKey }),
469
500
  ...(runtimeDescriptor === undefined
470
501
  ? {}
@@ -490,7 +521,8 @@ export class FileRoleLaunchPlanner {
490
521
  ...(session === null
491
522
  ? {}
492
523
  : { YUI_NATIVE_SESSION_ID: session.nativeSessionId })
493
- }
524
+ },
525
+ childLifecycle: driver.capabilities.lifecycle.providerProcess
494
526
  };
495
527
  const scopedLaunch = owner.scope === "task"
496
528
  ? this.#applyWorkspaceScope(owner.taskId, role, launch, workspaceOverride)
@@ -547,6 +579,19 @@ export class FileRoleLaunchPlanner {
547
579
  return selectEnvironment(source, names);
548
580
  }
549
581
  }
582
+ function managedRunLaunchEnvelope(run, mode) {
583
+ if (run.providerRetry?.state === "dispatching") {
584
+ return serializeProviderRetryEnvelope({
585
+ taskId: run.taskId,
586
+ runId: run.id,
587
+ roleName: run.roleName,
588
+ retry: run.providerRetry
589
+ });
590
+ }
591
+ return mode === "resume" && run.pushedAt !== undefined
592
+ ? serializeRunHostRecoveryEnvelope(run.bootstrapEnvelope)
593
+ : serializeRunBootstrapEnvelope(run.bootstrapEnvelope);
594
+ }
550
595
  export function nativeAgentWorkspace(workspace) {
551
596
  return workspace.entries.length === 1
552
597
  ? workspace.entries[0].path
@@ -629,11 +674,12 @@ function ensureManagedClaudeLifecyclePlugin(home, cliPath) {
629
674
  }, null, 2)}\n`);
630
675
  return root;
631
676
  }
632
- function managedClaudeControlPlaneConfig(config, taskId, workItemId, runId, controlPlane) {
677
+ function managedClaudeControlPlaneConfig(config, taskId, workItemId, runId, controlPlane, sessionCliPath) {
633
678
  if (config.permission.strategy !== "configured")
634
679
  return config;
635
680
  const exact = exactControlPlaneCommandPrefix(controlPlane);
636
681
  const managed = [
682
+ `Bash(${sessionCliPath} task run context ${taskId}/${runId}:*)`,
637
683
  `Bash(${exact} --json task context ${taskId})`,
638
684
  `Bash(${exact} --json task work list ${taskId})`,
639
685
  ...(workItemId === undefined
@@ -7,10 +7,9 @@ import { updateRoleStatus } from "../role/role.js";
7
7
  import { createTaskEvent } from "../event/taskEvent.js";
8
8
  import { finishReviewRound, updateReviewExecutionGroup } from "../review/reviewRound.js";
9
9
  import { reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
10
- import { failAgentRun, withYieldReceipt, yieldAgentRun } from "../run/agentRun.js";
10
+ import { agentRunDeliveryReceiptId, failAgentRun, withYieldReceipt, yieldAgentRun } from "../run/agentRun.js";
11
11
  import { createYieldReceipt } from "../run/yieldReceipt.js";
12
12
  import { recordExecutionLaneResult } from "../execution/executionGroup.js";
13
- import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
14
13
  import { isRuntimeLaunchReservation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
15
14
  import { clearMatchingLeaderStallAttention, latestRunDurableProgressAt, RUN_RECOVERY_APPLIED_EVENT, RUN_RECOVERY_REQUESTED_EVENT } from "../scheduler/roleRunStall.js";
16
15
  import { markTaskWakeConsumed } from "../scheduler/taskWake.js";
@@ -466,7 +465,7 @@ function recoverExactAgentRunInTransaction(store, input) {
466
465
  roleName: input.roleName,
467
466
  agentId: input.agentId,
468
467
  runId: input.runId,
469
- receiptId: formatAgentRunReceiptId(input.taskId, input.runId),
468
+ receiptId: agentRunDeliveryReceiptId(current),
470
469
  ...(input.nativeSessionId === undefined ? {} : { nativeSessionId: input.nativeSessionId }),
471
470
  ...(input.launchId === undefined ? {} : { launchId: input.launchId }),
472
471
  outcome: { status: "failed", summary: input.reason }
@@ -51,9 +51,13 @@ const SESSION_DEAD_PATTERNS = [
51
51
  { pattern: /session (has )?ended/iu, label: "session-ended" },
52
52
  { pattern: /session (is )?dead/iu, label: "session-dead" },
53
53
  { pattern: /session terminated/iu, label: "session-terminated" },
54
- { pattern: /process exited/iu, label: "process-exited" },
55
- { pattern: /pane (is )?dead/iu, label: "pane-dead" },
56
- { pattern: /target session/iu, label: "target-session" }
54
+ ];
55
+ const CONTEXT_CAPACITY_PATTERNS = [
56
+ { pattern: /maximum context length/iu, label: "maximum-context-length" },
57
+ { pattern: /context length exceeded/iu, label: "context-length-exceeded" },
58
+ { pattern: /context window (is )?(full|exceeded)/iu, label: "context-window-exceeded" },
59
+ { pattern: /prompt (is )?too long/iu, label: "prompt-too-long" },
60
+ { pattern: /too many tokens/iu, label: "too-many-tokens" }
57
61
  ];
58
62
  const POLICY_DENIED_PATTERNS = [
59
63
  { pattern: /cyber[_-]?policy/iu, label: "cyber-policy" },
@@ -112,6 +116,7 @@ const TRANSPORT_UNCERTAIN_PATTERNS = [
112
116
  const CLASS_TABLE = [
113
117
  { errorClass: "session-dead", patterns: SESSION_DEAD_PATTERNS },
114
118
  { errorClass: "policy-denied", patterns: POLICY_DENIED_PATTERNS },
119
+ { errorClass: "context-capacity", patterns: CONTEXT_CAPACITY_PATTERNS },
115
120
  { errorClass: "invalid-request", patterns: INVALID_REQUEST_PATTERNS },
116
121
  { errorClass: "transient-provider", patterns: TRANSIENT_PROVIDER_PATTERNS },
117
122
  { errorClass: "transport-uncertain", patterns: TRANSPORT_UNCERTAIN_PATTERNS }
@@ -63,7 +63,7 @@ function directorySizeBytes(path) {
63
63
  }
64
64
  return total;
65
65
  }
66
- const WAKE_REASON_PATTERN = /Yui wakeup reasons: ([^\n.]+)/u;
66
+ const WAKE_REASON_PATTERN = /Wake reasons: ([^\n.]+)/u;
67
67
  function inWindow(createdAt, options) {
68
68
  const time = Date.parse(createdAt);
69
69
  if (!Number.isFinite(time))
@@ -207,6 +207,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
207
207
  providerRetries: section,
208
208
  workItems: section,
209
209
  storage: section,
210
+ runtimeProtocol: section,
210
211
  topLongRunning: section
211
212
  };
212
213
  }
@@ -314,7 +315,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
314
315
  if (!inWindow(run.createdAt, options))
315
316
  continue;
316
317
  leaderRuns += 1;
317
- const match = WAKE_REASON_PATTERN.exec(run.input);
318
+ const match = WAKE_REASON_PATTERN.exec(run.assignment.directive ?? "");
318
319
  if (match === null)
319
320
  continue;
320
321
  withWakeReasons += 1;
@@ -705,6 +706,89 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
705
706
  return failed(error);
706
707
  }
707
708
  })();
709
+ const runtimeProtocol = (() => {
710
+ try {
711
+ const protocolVersions = new Map();
712
+ const manifestDigests = new Set();
713
+ const retryStates = new Map();
714
+ const exitClassifications = new Map();
715
+ const usageSemantics = new Map();
716
+ let activeRetryEpisodes = 0;
717
+ let activeConsecutiveFailures = 0;
718
+ let activeDispatchedRetries = 0;
719
+ let retryClassifiedEvents = 0;
720
+ let retryDispatchedEvents = 0;
721
+ let retryRecoveredEvents = 0;
722
+ let retryExhaustedEvents = 0;
723
+ let contextCapacityFailures = 0;
724
+ let processExitObservations = 0;
725
+ let compactionEvents = 0;
726
+ for (const taskId of taskIds) {
727
+ for (const run of store.listAgentRuns(taskId)) {
728
+ if (!inWindow(run.createdAt, options))
729
+ continue;
730
+ const version = String(run.effective.contextProtocolVersion ?? "legacy");
731
+ protocolVersions.set(version, (protocolVersions.get(version) ?? 0) + 1);
732
+ if (run.effective.sessionManifestCompatibilityDigest !== undefined) {
733
+ manifestDigests.add(run.effective.sessionManifestCompatibilityDigest);
734
+ }
735
+ if (run.providerRetry !== undefined) {
736
+ activeRetryEpisodes += 1;
737
+ retryStates.set(run.providerRetry.state, (retryStates.get(run.providerRetry.state) ?? 0) + 1);
738
+ activeConsecutiveFailures += run.providerRetry.consecutiveFailures;
739
+ activeDispatchedRetries += run.providerRetry.dispatchedRetries;
740
+ }
741
+ }
742
+ for (const event of store.listEvents(taskId)) {
743
+ if (!inWindow(event.createdAt, options))
744
+ continue;
745
+ if (event.type === "runtime.provider-retry-classified")
746
+ retryClassifiedEvents += 1;
747
+ else if (event.type === "runtime.provider-retry-dispatched")
748
+ retryDispatchedEvents += 1;
749
+ else if (event.type === "runtime.provider-retry-recovered")
750
+ retryRecoveredEvents += 1;
751
+ else if (event.type === "runtime.provider-retry-exhausted")
752
+ retryExhaustedEvents += 1;
753
+ else if (event.type === "runtime.context-capacity-failure")
754
+ contextCapacityFailures += 1;
755
+ else if (event.type === "runtime.process-exit-observed") {
756
+ processExitObservations += 1;
757
+ const classification = event.payload.classification ?? "unknown";
758
+ exitClassifications.set(classification, (exitClassifications.get(classification) ?? 0) + 1);
759
+ }
760
+ const observation = runtimeObservationFromTaskEvent(event);
761
+ const semantics = observation?.payload.usage?.semantics;
762
+ if (semantics !== undefined) {
763
+ usageSemantics.set(semantics, (usageSemantics.get(semantics) ?? 0) + 1);
764
+ }
765
+ if (event.type === "runtime.compaction-started"
766
+ || event.type === "runtime.compaction-completed")
767
+ compactionEvents += 1;
768
+ }
769
+ }
770
+ return ok({
771
+ contextProtocolVersions: Object.fromEntries(protocolVersions),
772
+ manifestCompatibilityDigests: manifestDigests.size,
773
+ activeRetryEpisodes,
774
+ activeRetryStates: Object.fromEntries(retryStates),
775
+ activeConsecutiveFailures,
776
+ activeDispatchedRetries,
777
+ retryClassifiedEvents,
778
+ retryDispatchedEvents,
779
+ retryRecoveredEvents,
780
+ retryExhaustedEvents,
781
+ contextCapacityFailures,
782
+ processExitObservations,
783
+ processExitClassifications: Object.fromEntries(exitClassifications),
784
+ usageSemantics: Object.fromEntries(usageSemantics),
785
+ compactionEvents
786
+ });
787
+ }
788
+ catch (error) {
789
+ return failed(error);
790
+ }
791
+ })();
708
792
  const topLongRunning = (() => {
709
793
  try {
710
794
  const entries = [];
@@ -751,6 +835,7 @@ export function runExecutionAudit(home, options = {}, ports = createProductionEx
751
835
  providerRetries,
752
836
  workItems,
753
837
  storage,
838
+ runtimeProtocol,
754
839
  topLongRunning
755
840
  };
756
841
  }