@zq-silk/yui 0.6.15 → 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 +279 -1
  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
@@ -121,6 +121,7 @@ const roleChildren = [
121
121
  },
122
122
  { name: "list", summary: "List global Roles." },
123
123
  { name: "show", summary: "Show one global Role.", usage: "yui role show <name>" },
124
+ { name: "context", summary: "Load the exact authorized global Role context.", usage: "yui role context <name>" },
124
125
  {
125
126
  name: "update",
126
127
  summary: "Update a global Role.",
@@ -1227,7 +1228,7 @@ export const ROOT_COMMAND = buildNode({
1227
1228
  name: "role",
1228
1229
  summary: "Manage reusable global Roles and their native sessions.",
1229
1230
  sections: [
1230
- { id: "inspect", title: "Inspect", entries: ["list", "show"] },
1231
+ { id: "inspect", title: "Inspect", entries: ["list", "show", "context"] },
1231
1232
  { id: "manage", title: "Manage", entries: ["add", "update", "remove", "bind", "unbind"] },
1232
1233
  { id: "sessions", title: "Sessions", entries: ["enter", "session"] }
1233
1234
  ],
@@ -1278,7 +1279,7 @@ export const ROOT_COMMAND = buildNode({
1278
1279
  name: "internal",
1279
1280
  summary: "Internal Yui callbacks.",
1280
1281
  hidden: true,
1281
- sections: [{ id: "callbacks", title: "Callbacks", entries: ["session-notify", "runtime-hook", "managed-claude-run"] }],
1282
+ sections: [{ id: "callbacks", title: "Callbacks", entries: ["session-notify", "runtime-hook"] }],
1282
1283
  children: [
1283
1284
  {
1284
1285
  name: "session-notify",
@@ -1289,11 +1290,6 @@ export const ROOT_COMMAND = buildNode({
1289
1290
  name: "runtime-hook",
1290
1291
  summary: "Record a managed Agent Driver observation from stdin.",
1291
1292
  usage: "yui internal runtime-hook"
1292
- },
1293
- {
1294
- name: "managed-claude-run",
1295
- summary: "Run one managed Claude turn over the stream-json protocol.",
1296
- usage: "yui internal managed-claude-run -- <command> [args...]"
1297
1293
  }
1298
1294
  ]
1299
1295
  }
package/dist/cli.js CHANGED
@@ -60,7 +60,7 @@ import { runRuntimeObservationHookCommand } from "./controller/runtimeObservatio
60
60
  import { buildDoctorReport, renderDoctor, runDoctorCommand } from "./doctor/doctor.js";
61
61
  import { agentNotFound, CliError, usageError } from "./errors/cliError.js";
62
62
  import { FileRoleLaunchPlanner } from "./executor/fileRoleLaunchPlanner.js";
63
- import { runManagedClaudeProcess } from "./executor/managedClaudeRunner.js";
63
+ import { runAgentHost } from "./runtime/agentHost.js";
64
64
  import { TaskWorkspaceCoordinator, WorkspaceCleanupBlockedError } from "./repository/taskWorkspaceCoordinator.js";
65
65
  import { FileTaskWorkspacePreparer, ReviewRoundWorkspaceEvidenceError } from "./repository/taskWorkspacePreparer.js";
66
66
  import { inspectStorageSchema } from "./storage/storageSchema.js";
@@ -243,6 +243,14 @@ export async function main() {
243
243
  return;
244
244
  }
245
245
  if (args[0] === "internal") {
246
+ if (args[1] === "agent-host" && args.length === 4) {
247
+ process.exitCode = await runAgentHost({
248
+ home,
249
+ launchId: args[2],
250
+ ticket: args[3]
251
+ });
252
+ return;
253
+ }
246
254
  if (args[1] === "session-notify" && args.length === 3) {
247
255
  await runSessionNotifyCommand(args[2], process.env);
248
256
  return;
@@ -251,36 +259,6 @@ export async function main() {
251
259
  await runRuntimeObservationHookCommand(readFileSync(0, "utf8"), process.env);
252
260
  return;
253
261
  }
254
- if (args[1] === "managed-claude-run"
255
- && args[2] === "--"
256
- && args.length >= 4
257
- && verifiedStore !== undefined) {
258
- const taskId = process.env.YUI_TASK_ID;
259
- const runId = process.env.YUI_RUN_ID;
260
- const agentId = process.env.YUI_AGENT_ID;
261
- if (taskId === undefined || runId === undefined || agentId === undefined) {
262
- throw new Error("Managed Claude run identity is incomplete.");
263
- }
264
- const run = verifiedStore.getActiveAgentRun(taskId, process.env.YUI_ROLE ?? "");
265
- if (run === null || run.id !== runId || run.effective.agentId !== agentId
266
- || run.effective.adapterId !== "claude") {
267
- throw new Error("Managed Claude run is not the exact active generation.");
268
- }
269
- const configured = verifiedStore.getConfiguredAgent(agentId);
270
- if (configured === null || configured.adapterId !== "claude" || args[3] !== configured.command) {
271
- throw new Error("Managed Claude command does not match the active Agent.");
272
- }
273
- if (!configured.baseArgs.every((value, index) => args[index + 4] === value)) {
274
- throw new Error("Managed Claude arguments do not start with the configured base arguments.");
275
- }
276
- process.exitCode = await runManagedClaudeProcess({
277
- command: configured.command,
278
- args: args.slice(4),
279
- prompt: run.input,
280
- environment: process.env
281
- });
282
- return;
283
- }
284
262
  throw usageError("Internal lifecycle callback usage is invalid.");
285
263
  }
286
264
  if (args[0] === "session") {
@@ -620,7 +598,8 @@ export async function main() {
620
598
  if (resolved[0] === "role") {
621
599
  const roleOptions = {
622
600
  yuiHome: home,
623
- env: process.env
601
+ env: process.env,
602
+ jsonOutput
624
603
  };
625
604
  const result = runGlobalRoleCommand(resolved.slice(1), store, roleOptions);
626
605
  if (typeof result === "string") {
@@ -1240,7 +1219,7 @@ async function preflightManagedTaskControlPlane() {
1240
1219
  });
1241
1220
  const runtime = assertExactTaskRuntimeEnvironment(serializedRuntime, process.env, digest, control.yuiHome);
1242
1221
  const runtimeDriverCallback = args[0] === "internal"
1243
- && (args[1] === "runtime-hook" || args[1] === "managed-claude-run")
1222
+ && args[1] === "runtime-hook"
1244
1223
  && process.env.YUI_DRIVER_ID !== undefined
1245
1224
  ? builtinAgentDriverRegistry().require(process.env.YUI_DRIVER_ID)
1246
1225
  : undefined;
@@ -221,6 +221,25 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
221
221
  else {
222
222
  lines.push("", ...sectionError("storage", report));
223
223
  }
224
+ if (report.runtimeProtocol.status === "ok" && report.runtimeProtocol.data !== undefined) {
225
+ const runtime = report.runtimeProtocol.data;
226
+ const versions = Object.entries(runtime.contextProtocolVersions)
227
+ .map(([version, count]) => `${version}:${count}`)
228
+ .join(", ") || "none";
229
+ const retryStates = Object.entries(runtime.activeRetryStates)
230
+ .map(([state, count]) => `${state}:${count}`)
231
+ .join(", ") || "none";
232
+ const exits = Object.entries(runtime.processExitClassifications)
233
+ .map(([classification, count]) => `${classification}:${count}`)
234
+ .join(", ") || "none";
235
+ const usage = Object.entries(runtime.usageSemantics)
236
+ .map(([semantics, count]) => `${semantics}:${count}`)
237
+ .join(", ") || "none";
238
+ lines.push("", `Runtime protocol: context versions ${versions} · manifest compatibility identities ${runtime.manifestCompatibilityDigests}`, `Active retry: ${runtime.activeRetryEpisodes} episode(s) [${retryStates}] · failures ${runtime.activeConsecutiveFailures} · dispatched ${runtime.activeDispatchedRetries}`, `Retry audit: ${runtime.retryClassifiedEvents} classified · ${runtime.retryDispatchedEvents} dispatched · ${runtime.retryRecoveredEvents} recovered · ${runtime.retryExhaustedEvents} exhausted`, `Process exits: ${runtime.processExitObservations} [${exits}] · capacity failures ${runtime.contextCapacityFailures}`, `Context telemetry: usage [${usage}] · native compaction events ${runtime.compactionEvents}`);
239
+ }
240
+ else {
241
+ lines.push("", ...sectionError("runtimeProtocol", report));
242
+ }
224
243
  if (report.topLongRunning.status === "ok" && report.topLongRunning.data !== undefined) {
225
244
  const entries = report.topLongRunning.data;
226
245
  if (entries.length > 0) {
@@ -14,6 +14,7 @@ export function runGlobalRoleCommand(args, store, options = {}) {
14
14
  case "add": return addRole(rest, store, options);
15
15
  case "list": return listRoles(rest, store);
16
16
  case "show": return showRole(rest, store);
17
+ case "context": return roleContext(rest, store, options);
17
18
  case "update": return updateRole(rest, store, options);
18
19
  case "remove": return removeRole(rest, store);
19
20
  case "bind": return bindRole(rest, store);
@@ -26,6 +27,75 @@ export function runGlobalRoleCommand(args, store, options = {}) {
26
27
  : `Unknown command: role ${command}`);
27
28
  }
28
29
  }
30
+ function roleContext(args, store, options) {
31
+ const [rawName, ...rest] = args;
32
+ const name = roleName(rawName);
33
+ assertNoArguments(rest, "Role context usage: yui role context <role>");
34
+ const environment = options.env ?? process.env;
35
+ if (environment.YUI_SESSION_SCOPE !== undefined) {
36
+ if (environment.YUI_SESSION_SCOPE !== "global" || environment.YUI_ROLE !== name) {
37
+ throw usageError("Managed GlobalRole context is outside the exact Session authority.");
38
+ }
39
+ if (environment.YUI_SESSION_MANIFEST === undefined
40
+ || environment.YUI_SESSION_CLI === undefined) {
41
+ throw usageError("Managed GlobalRole context requires the exact Session Manifest and CLI.");
42
+ }
43
+ }
44
+ const role = requireRole(name, store);
45
+ const binding = activeRoleAgentBinding(role);
46
+ const effective = resolveEffectiveLaunch({ role, purpose: "execution" });
47
+ const sessions = store.getGlobalRoleSessionSet(name);
48
+ const session = sessions?.sessions[role.activeAgentId];
49
+ const context = Object.freeze({
50
+ schemaVersion: 1,
51
+ protocol: "yui-managed-context/v1",
52
+ identity: {
53
+ scope: "global",
54
+ roleName: role.name,
55
+ roleKind: role.name === "operator" ? "operator" : "global",
56
+ agentId: binding.agentId,
57
+ adapterId: binding.adapterId,
58
+ workspace: role.workspace,
59
+ effectiveRevision: role.launchRevision,
60
+ ...(session?.nativeSessionId === undefined
61
+ ? {}
62
+ : { nativeSessionId: session.nativeSessionId })
63
+ },
64
+ profile: {
65
+ description: role.description,
66
+ responsibilities: role.responsibilities ?? [],
67
+ constraints: role.constraints ?? [],
68
+ expectedOutput: role.expectedOutput,
69
+ skillIds: [
70
+ "yui-runtime",
71
+ ...(role.name === "operator" ? ["yui-operator"] : []),
72
+ ...(role.skills ?? [])
73
+ ]
74
+ },
75
+ authority: {
76
+ view: role.name === "operator" ? "operator" : "global",
77
+ taskImplementation: false,
78
+ writableProjectIds: effective.writeProjectIds,
79
+ allowedActions: role.name === "operator"
80
+ ? ["route-request", "inspect-catalog", "answer-user-boundary"]
81
+ : ["perform-global-role-request"]
82
+ },
83
+ sessionManifestPath: environment.YUI_SESSION_MANIFEST,
84
+ sessionCliPath: environment.YUI_SESSION_CLI
85
+ });
86
+ if (options.jsonOutput === true)
87
+ return `${JSON.stringify(context)}\n`;
88
+ return [
89
+ `Role Context: ${role.name}`,
90
+ `Scope: global (${context.identity.roleKind})`,
91
+ `Agent: ${binding.agentId}/${binding.adapterId}`,
92
+ `Workspace: ${role.workspace}`,
93
+ `Effective revision: ${role.launchRevision}`,
94
+ `Skills: ${context.profile.skillIds.join(", ") || "none"}`,
95
+ `Authority: ${context.authority.view}; Task implementation: no`,
96
+ ""
97
+ ].join("\n");
98
+ }
29
99
  function addRole(args, store, options) {
30
100
  const [rawName, ...tail] = args;
31
101
  const name = roleName(rawName);
@@ -1,6 +1,7 @@
1
1
  import { usageError } from "../errors/cliError.js";
2
2
  import { activeRoleAgentBinding } from "../role/role.js";
3
3
  import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
4
+ import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
4
5
  const LEADER_ROLE = "leader";
5
6
  const LEADER_ACTION_RUN_ENV = "YUI_LEADER_ACTION_RUN_ID";
6
7
  const LEADER_ACTION_RECEIPT_ENV = "YUI_LEADER_ACTION_RECEIPT_ID";
@@ -152,7 +153,7 @@ function activeLeaderRunId(store, taskId) {
152
153
  return undefined;
153
154
  }
154
155
  const sessions = store.getTaskRoleSessionSet(taskId, LEADER_ROLE);
155
- const expectedReceipt = formatAgentRunReceiptId(taskId, run.id);
156
+ const expectedReceipt = agentRunDeliveryReceiptId(run);
156
157
  if (sessions === null
157
158
  || sessions.inFlight === null
158
159
  || sessions.inFlight.runId !== run.id
@@ -223,7 +224,7 @@ export function taskLeaderActionRunId(store, taskId, environment, yuiHome) {
223
224
  || sessions.inFlight === null
224
225
  || sessions.inFlight.runId !== runId
225
226
  || sessions.inFlight.agentId !== agentId
226
- || sessions.inFlight.receiptId !== formatAgentRunReceiptId(taskId, runId))
227
+ || sessions.inFlight.receiptId !== agentRunDeliveryReceiptId(run))
227
228
  return undefined;
228
229
  if (explicitAssertion !== undefined
229
230
  && explicitAssertion.receiptId !== formatAgentRunReceiptId(taskId, runId))
@@ -1,5 +1,8 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { isDeepStrictEqual } from "node:util";
2
- import { compileDispatchInput, ensureWorkerRunCompletionRequirement } from "../context/dispatchContext.js";
3
+ import { createRunAssignment } from "../context/runContextContract.js";
4
+ import { buildRunContextPack, buildRunContextDelta, contextSnapshotDeltaRefIds, expandRunContextRef, freezeRunContextSnapshot } from "../context/runContextPack.js";
5
+ import { contextSnapshotRef } from "../context/contextSnapshot.js";
3
6
  import { CliError, dataError, roleNotFound, runtimeError, taskNotFound, usageError } from "../errors/cliError.js";
4
7
  import { createTaskEvent } from "../event/taskEvent.js";
5
8
  import { clearMatchingLeaderStallAttention, isRoleRunStalled, RUN_PROGRESS_EVENT, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
@@ -14,7 +17,7 @@ import { cancelInputRequest } from "../input/inputRequest.js";
14
17
  import { recoverExactAgentRun, terminalizeExactTaskRun, validateExactRunReviewRound } from "../lifecycle/exactRunTerminalization.js";
15
18
  import { resetTaskRoleSessionGeneration } from "../lifecycle/taskRoleSessionReset.js";
16
19
  import { activeRoleAgentBinding, copyGlobalRoleToTaskRole, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole, updateRoleStatus } from "../role/role.js";
17
- import { createAgentRun } from "../run/agentRun.js";
20
+ import { agentRunDeliveryReceiptId, createAgentRun, withAgentRunContextSnapshot } from "../run/agentRun.js";
18
21
  import { projectRunRecovery, readRunRecoveryFacts } from "../run/recoveryProjection.js";
19
22
  import { matchYieldReceipt } from "../run/yieldReceipt.js";
20
23
  import { providerRetryConfig } from "../run/providerRetryConfig.js";
@@ -22,8 +25,6 @@ import { createReviewRound, createTaskReviewRound, createTaskDeltaReviewRound, a
22
25
  import { buildDeltaRecheckDispatchContext, verifyDeltaRecheckDiff } from "../review/deltaRecheck.js";
23
26
  import { buildTaskFinalReviewFindingContext, dispositionReviewFinding, planRepairGroups, reconcileReviewFindings, reconcileReviewFindingsAfterReview } from "../review/reviewFindingLedger.js";
24
27
  import { LEADER_FINDING_DISPOSITIONS } from "../review/reviewFinding.js";
25
- import { markYuiRunInput, retagYuiRunInput } from "../run/runIdentity.js";
26
- import { taskRoleSessionTitle } from "../runtime/sessionTitle.js";
27
28
  import { createTaskBrief, updateTaskBrief } from "../brief/taskBrief.js";
28
29
  import { createDecision, supersedeDecision } from "../decision/decision.js";
29
30
  import { createMilestone } from "../milestone/milestone.js";
@@ -34,7 +35,7 @@ import { RUNTIME_CLEANUP_REQUIRED_REASON, runtimeLifecycleTarget } from "../runt
34
35
  import { blockingProviderContinuations, projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
35
36
  import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
36
37
  import { activateTask, addTaskProjectBinding, archiveTask, completeTask, createTask, retireTask, reopenTask, updateTaskMetadata } from "../task/task.js";
37
- import { formatAgentRunReceiptId, resolveTaskRecordReference } from "../task/taskRecordReference.js";
38
+ import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
38
39
  import { projectCompletionReadiness } from "../task/completionReadiness.js";
39
40
  import { resolveProject } from "../repository/project.js";
40
41
  import { acquireProjectMaintenanceLocks } from "../repository/projectMaintenanceLock.js";
@@ -683,7 +684,7 @@ function completeTaskCommand(args, store, options) {
683
684
  roleName: LEADER_ROLE,
684
685
  agentId: leaderEntry.run.effective.agentId,
685
686
  runId: leaderEntry.run.id,
686
- receiptId: formatAgentRunReceiptId(task.id, leaderEntry.run.id),
687
+ receiptId: agentRunDeliveryReceiptId(leaderEntry.run),
687
688
  outcome: { status: "yielded", summary }
688
689
  }, now);
689
690
  if (terminal.disposition !== "applied") {
@@ -923,7 +924,7 @@ function retireTaskCommand(args, store, options) {
923
924
  roleName: run.roleName,
924
925
  agentId: run.effective.agentId,
925
926
  runId: run.id,
926
- receiptId: formatAgentRunReceiptId(task.id, run.id),
927
+ receiptId: agentRunDeliveryReceiptId(run),
927
928
  mailboxDisposition: "discard",
928
929
  outcome: {
929
930
  status: "failed",
@@ -1964,15 +1965,21 @@ function dispatchWork(args, store, options) {
1964
1965
  workspace: laneWorkspace
1965
1966
  }, now);
1966
1967
  runningGroup = runningLane;
1967
- const input = markYuiRunInput(compileDispatchInput({}, task.id, plan.role, `${rawInput}\nExecution Group ${group.id}; Lane ${lane.id}; frozen target ${group.target.fingerprint}.`, {
1968
- workItem: item,
1969
- projectPolicy: task.projectBindings.map(({ projectId, directory }) => ({
1970
- projectId,
1971
- directory
1972
- })),
1973
- ...(laneManagedWorkspace === undefined ? {} : { workspace: laneManagedWorkspace })
1974
- }), plan.runId, taskRoleSessionTitle(task, plan.role.name));
1975
- createdRuns.push(createAgentRun(plan.runId, task.id, plan.role.name, dispatchMode, input, now, {
1968
+ const assignment = createRunAssignment({
1969
+ runId: plan.runId,
1970
+ roleName: plan.role.name,
1971
+ purpose: "execution",
1972
+ action: item.status === "failed" ? "repair-work-item" : "execute-work-item",
1973
+ subject: {
1974
+ taskId: task.id,
1975
+ workItemId: item.id,
1976
+ executionGroupId: runningGroup.id,
1977
+ executionLaneId: lane.id
1978
+ },
1979
+ directive: `${rawInput}\nFrozen target: ${group.target.fingerprint}.`,
1980
+ deltaRefIds: []
1981
+ });
1982
+ createdRuns.push(createAgentRun(plan.runId, task.id, plan.role.name, dispatchMode, assignment, now, {
1976
1983
  workItemId: item.id,
1977
1984
  executionGroupId: runningGroup.id,
1978
1985
  executionLaneId: lane.id,
@@ -2028,7 +2035,15 @@ function dispatchWork(args, store, options) {
2028
2035
  }
2029
2036
  }
2030
2037
  for (let index = 0; index < createdRuns.length; index += 1) {
2031
- const runWithLineage = createdRuns[index];
2038
+ const unboundRun = createdRuns[index];
2039
+ const snapshot = freezeRunContextSnapshot(tx, {
2040
+ taskId: task.id,
2041
+ roleName: unboundRun.roleName,
2042
+ purpose: "execution",
2043
+ workItemId: item.id
2044
+ }, now);
2045
+ const runWithLineage = withAgentRunContextSnapshot(unboundRun, contextSnapshotRef(snapshot), contextSnapshotDeltaRefIds(tx, snapshot));
2046
+ createdRuns[index] = runWithLineage;
2032
2047
  const role = plans[index].role;
2033
2048
  tx.saveAgentRun(runWithLineage);
2034
2049
  tx.saveActiveAgentRun(runWithLineage);
@@ -2404,7 +2419,7 @@ function retireWork(args, store, options) {
2404
2419
  roleName: run.roleName,
2405
2420
  agentId: run.effective.agentId,
2406
2421
  runId: run.id,
2407
- receiptId: formatAgentRunReceiptId(task.id, run.id),
2422
+ receiptId: agentRunDeliveryReceiptId(run),
2408
2423
  outcome: {
2409
2424
  status: "failed",
2410
2425
  summary: `Work Item retired: ${summary}`
@@ -3217,6 +3232,8 @@ function taskRunCommand(args, store, options) {
3217
3232
  return output(listRuns(rest, store, options));
3218
3233
  if (command === "show")
3219
3234
  return showRun(rest, store, options);
3235
+ if (command === "context")
3236
+ return runContextCommand(rest, store, options);
3220
3237
  if (command === "retry")
3221
3238
  return retryRun(rest, store, options);
3222
3239
  if (command === "settle")
@@ -3233,6 +3250,70 @@ function taskRunCommand(args, store, options) {
3233
3250
  ? "Task run command is required."
3234
3251
  : `Unknown command: task run ${command}`);
3235
3252
  }
3253
+ function runContextCommand(args, store, options) {
3254
+ const [first, ...rest] = args;
3255
+ if (first === "expand") {
3256
+ if (rest.length < 2 || rest.length > 4 || (rest.length > 2
3257
+ && !(rest.length === 4 && rest[2] === "--mode" && rest[3] === "full"))) {
3258
+ throw usageError("Task run context expand usage: yui task run context expand <task>/<run> <ref-id> [--mode full].");
3259
+ }
3260
+ const { taskId, runId } = parseRunContextReference(rest[0]);
3261
+ authorizeRunContext(store, taskId, runId, options.environment);
3262
+ const expanded = store.transaction((tx) => expandRunContextRef(tx, taskId, runId, rest[1]));
3263
+ return output(`${JSON.stringify(expanded, null, 2)}\n`, { context: expanded });
3264
+ }
3265
+ if (first === "delta") {
3266
+ if (rest.length !== 3 || rest[1] !== "--after") {
3267
+ throw usageError("Task run context delta usage: yui task run context delta <task>/<run> --after <cursor>.");
3268
+ }
3269
+ const { taskId, runId } = parseRunContextReference(rest[0]);
3270
+ authorizeRunContext(store, taskId, runId, options.environment);
3271
+ const delta = store.transaction((tx) => (buildRunContextDelta(tx, taskId, runId, rest[2])));
3272
+ return output(`${JSON.stringify(delta, null, 2)}\n`, { contextDelta: delta });
3273
+ }
3274
+ if (first === undefined || rest.length !== 0) {
3275
+ throw usageError("Task run context usage: yui task run context <task>/<run>.");
3276
+ }
3277
+ const { taskId, runId } = parseRunContextReference(first);
3278
+ authorizeRunContext(store, taskId, runId, options.environment);
3279
+ const pack = store.transaction((tx) => buildRunContextPack(tx, taskId, runId));
3280
+ return output(`${JSON.stringify(pack, null, 2)}\n`, { context: pack });
3281
+ }
3282
+ function parseRunContextReference(value) {
3283
+ const [taskId, runId, extra] = value.split("/");
3284
+ if (taskId === undefined || taskId.length === 0 || runId === undefined || runId.length === 0
3285
+ || extra !== undefined) {
3286
+ throw usageError(`Run context reference is invalid: ${value}.`);
3287
+ }
3288
+ return { taskId, runId };
3289
+ }
3290
+ function authorizeRunContext(store, taskId, runId, environment) {
3291
+ const managed = environment?.YUI_SESSION_SCOPE !== undefined
3292
+ || environment?.YUI_TASK_ID !== undefined
3293
+ || environment?.YUI_ROLE !== undefined
3294
+ || environment?.YUI_RUN_ID !== undefined;
3295
+ if (!managed)
3296
+ return;
3297
+ const run = store.getAgentRun(taskId, runId);
3298
+ const active = run === null ? null : store.getActiveAgentRun(taskId, run.roleName);
3299
+ if (environment?.YUI_SESSION_SCOPE !== "task"
3300
+ || environment.YUI_TASK_ID !== taskId
3301
+ || run === null
3302
+ || environment.YUI_ROLE !== run.roleName
3303
+ || environment.YUI_AGENT_ID !== run.effective.agentId
3304
+ || environment.YUI_ADAPTER_ID !== run.effective.adapterId
3305
+ || run.status !== "active"
3306
+ || active?.id !== runId) {
3307
+ throw usageError(`Run Context access is not authorized: ${taskId}/${runId}.`);
3308
+ }
3309
+ const callerKey = environment.YUI_JOB_CALLER_KEY;
3310
+ const expectedCallerKeyHash = store.getJobCallerKeyHash(taskId, run.roleName, run.effective.agentId);
3311
+ if (callerKey === undefined
3312
+ || expectedCallerKeyHash === null
3313
+ || createHash("sha256").update(callerKey).digest("hex") !== expectedCallerKeyHash) {
3314
+ throw usageError(`Run Context caller key is not authorized: ${taskId}/${runId}.`);
3315
+ }
3316
+ }
3236
3317
  function listRuns(args, store, options) {
3237
3318
  exactPositionals(args, 1, "Task run list usage: yui task run list <task>/<work>.");
3238
3319
  const item = requireWorkItem(store, args[0], options);
@@ -3505,18 +3586,6 @@ function retryRun(args, store, options) {
3505
3586
  : laneRestartedItem.status === "failed"
3506
3587
  ? retryFailedWorkItem(laneRestartedItem, now)
3507
3588
  : laneRestartedItem;
3508
- const retaggedInput = retagYuiRunInput(previous.input, runId, taskRoleSessionTitle(task, role.name));
3509
- const created = createAgentRun(runId, task.id, role.name, roleAgentSessionResumeMode(sessions, effective.agentId, effective), previous.workItemId === undefined
3510
- ? retaggedInput
3511
- : ensureWorkerRunCompletionRequirement(retaggedInput), now, {
3512
- ...(previous.workItemId === undefined ? {} : { workItemId: previous.workItemId }),
3513
- ...(runningGroup === undefined ? {} : {
3514
- executionGroupId: runningGroup.id,
3515
- executionLaneId: retryLane.id
3516
- }),
3517
- ...(retryManagedWorkspace === undefined ? {} : { workspace: retryManagedWorkspace }),
3518
- effective
3519
- });
3520
3589
  if (retriedItemWithGroup !== null) {
3521
3590
  if (laneRestartedItem !== null && laneRestartedItem !== retryItem) {
3522
3591
  tx.saveWorkItem(task.id, laneRestartedItem);
@@ -3525,6 +3594,40 @@ function retryRun(args, store, options) {
3525
3594
  tx.saveWorkItem(task.id, retriedItemWithGroup);
3526
3595
  }
3527
3596
  }
3597
+ const retrySnapshot = freezeRunContextSnapshot(tx, {
3598
+ taskId: task.id,
3599
+ roleName: role.name,
3600
+ purpose: "execution",
3601
+ ...(previous.workItemId === undefined ? {} : { workItemId: previous.workItemId })
3602
+ }, now);
3603
+ const assignment = createRunAssignment({
3604
+ runId,
3605
+ roleName: role.name,
3606
+ purpose: previous.assignment.purpose,
3607
+ action: previous.workItemId === undefined ? previous.assignment.action : "repair-work-item",
3608
+ subject: {
3609
+ taskId: task.id,
3610
+ ...(previous.workItemId === undefined ? {} : { workItemId: previous.workItemId }),
3611
+ ...(runningGroup === undefined ? {} : {
3612
+ executionGroupId: runningGroup.id,
3613
+ executionLaneId: retryLane.id
3614
+ })
3615
+ },
3616
+ ...(previous.assignment.directive === undefined
3617
+ ? {}
3618
+ : { directive: previous.assignment.directive }),
3619
+ contextSnapshotRef: contextSnapshotRef(retrySnapshot),
3620
+ deltaRefIds: contextSnapshotDeltaRefIds(tx, retrySnapshot)
3621
+ });
3622
+ const created = createAgentRun(runId, task.id, role.name, roleAgentSessionResumeMode(sessions, effective.agentId, effective), assignment, now, {
3623
+ ...(previous.workItemId === undefined ? {} : { workItemId: previous.workItemId }),
3624
+ ...(runningGroup === undefined ? {} : {
3625
+ executionGroupId: runningGroup.id,
3626
+ executionLaneId: retryLane.id
3627
+ }),
3628
+ ...(retryManagedWorkspace === undefined ? {} : { workspace: retryManagedWorkspace }),
3629
+ effective
3630
+ });
3528
3631
  tx.saveAgentRun(created);
3529
3632
  tx.saveActiveAgentRun(created);
3530
3633
  tx.saveRole(task.id, updateRoleStatus(role, "running", now));
@@ -4448,7 +4551,7 @@ function yieldRun(args, store, options) {
4448
4551
  roleName: role.name,
4449
4552
  agentId: active.effective.agentId,
4450
4553
  runId: active.id,
4451
- receiptId: formatAgentRunReceiptId(task.id, active.id),
4554
+ receiptId: agentRunDeliveryReceiptId(active),
4452
4555
  ...(active.purpose !== "review" || options.environment?.YUI_NATIVE_SESSION_ID === undefined
4453
4556
  ? {}
4454
4557
  : { nativeSessionId: options.environment.YUI_NATIVE_SESSION_ID }),
@@ -5035,14 +5138,21 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5035
5138
  .filter(({ access }) => access === "write")
5036
5139
  .map(({ projectId }) => projectId)
5037
5140
  };
5038
- const input = markYuiRunInput(compileDispatchInput({}, taskId, laneReviewer, `${rawInput}\nReviewer Lane ${lane.id}; frozen target ${runningGroup.target.fingerprint}.`, {
5039
- workItem: item,
5040
- projectPolicy: task.projectBindings.map(({ projectId, directory }) => ({
5041
- projectId,
5042
- directory
5043
- })),
5044
- workspace: laneManagedWorkspace ?? round.workspace
5045
- }), runId, taskRoleSessionTitle(task, laneReviewer.name));
5141
+ const assignment = createRunAssignment({
5142
+ runId,
5143
+ roleName: laneReviewer.name,
5144
+ purpose: "review",
5145
+ action: "review-round",
5146
+ subject: {
5147
+ taskId,
5148
+ workItemId: item.id,
5149
+ reviewRoundId: round.id,
5150
+ executionGroupId: runningGroup.id,
5151
+ executionLaneId: lane.id
5152
+ },
5153
+ directive: `${rawInput}\nFrozen target: ${runningGroup.target.fingerprint}.`,
5154
+ deltaRefIds: []
5155
+ });
5046
5156
  runningGroup = updateExecutionLane(runningGroup, lane.id, {
5047
5157
  status: "running",
5048
5158
  runId,
@@ -5050,7 +5160,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5050
5160
  effective,
5051
5161
  workspace: laneWorkspace
5052
5162
  }, now);
5053
- createdRuns.push(createAgentRun(runId, taskId, laneReviewer.name, roleAgentSessionResumeMode(sessions, effective.agentId, effective), input, now, {
5163
+ createdRuns.push(createAgentRun(runId, taskId, laneReviewer.name, roleAgentSessionResumeMode(sessions, effective.agentId, effective), assignment, now, {
5054
5164
  workItemId: item.id,
5055
5165
  purpose: "review",
5056
5166
  reviewRoundId: round.id,
@@ -5083,7 +5193,16 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5083
5193
  }
5084
5194
  }
5085
5195
  for (let index = 0; index < createdRuns.length; index += 1) {
5086
- const created = createdRuns[index];
5196
+ const unboundRun = createdRuns[index];
5197
+ const snapshot = freezeRunContextSnapshot(tx, {
5198
+ taskId,
5199
+ roleName: unboundRun.roleName,
5200
+ purpose: "review",
5201
+ workItemId: item.id,
5202
+ reviewRoundId: round.id
5203
+ }, now);
5204
+ const created = withAgentRunContextSnapshot(unboundRun, contextSnapshotRef(snapshot), contextSnapshotDeltaRefIds(tx, snapshot));
5205
+ createdRuns[index] = created;
5087
5206
  const laneReviewer = reviewers[index];
5088
5207
  tx.saveAgentRun(created);
5089
5208
  tx.saveActiveAgentRun(created);
@@ -247,7 +247,7 @@ export function runTaskContextCommand(args, store) {
247
247
  ? [" AgentRuns: none."]
248
248
  : [
249
249
  ` AgentRuns: ${itemRuns.length}; latest ${latestRun.id} [${latestRun.status}] ${latestRun.effective.agentId}/${latestRun.effective.adapterId} · effective r${latestRun.effective.sourceDesiredRevision}/${latestRun.effective.profileAccess}/${latestRun.effective.permission.strategy}`,
250
- ` Input: ${compactText(latestRun.input)}`,
250
+ ` Assignment: ${latestRun.assignment.action}${latestRun.assignment.directive === undefined ? "" : ` · ${compactText(latestRun.assignment.directive)}`}`,
251
251
  ...(latestRun.summary === undefined
252
252
  ? []
253
253
  : [` Summary: ${compactText(latestRun.summary)}`])
@@ -4,10 +4,11 @@ import { activeLiveRoleAgentSession } from "../executor/agentExecutor.js";
4
4
  import { answerInputRequest, cancelInputRequest, createInputRequest } from "../input/inputRequest.js";
5
5
  import { defaultTableWidth, renderTable } from "../output/table.js";
6
6
  import { formatTimestamp } from "../output/timePresentation.js";
7
+ import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
7
8
  import { enqueueWork } from "../coordination/workMailboxQueue.js";
8
9
  import { clearMatchingLeaderStallAttention, isRoleRunStalled, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
9
10
  import { terminalizeExactTaskRun } from "../lifecycle/exactRunTerminalization.js";
10
- import { formatAgentRunReceiptId, resolveTaskRecordReference } from "../task/taskRecordReference.js";
11
+ import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
11
12
  const LEADER_ROLE = "leader";
12
13
  export function runTaskInputCommand(args, store, options) {
13
14
  const [command, ...rest] = args;
@@ -71,7 +72,7 @@ function createRequest(args, store, options) {
71
72
  roleName: LEADER_ROLE,
72
73
  agentId: origin.run.effective.agentId,
73
74
  runId: origin.run.id,
74
- receiptId: formatAgentRunReceiptId(task.id, origin.run.id),
75
+ receiptId: agentRunDeliveryReceiptId(origin.run),
75
76
  ...(origin.requester.nativeSessionId === undefined
76
77
  ? {}
77
78
  : { nativeSessionId: origin.requester.nativeSessionId }),
@@ -1,11 +1,11 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
+ import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
2
3
  import { hasRuntimeCleanupObligation, hasRuntimeLifecycleWork, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
3
4
  import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunStall.js";
4
5
  import { createRuntimeObservation, runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
5
6
  import { classifyRuntimeHealth, projectRuntimeMailbox, projectRuntimeObservation, projectRuntimeTaskEvents, runtimeDisplayStatus } from "../runtime/runtimeProjection.js";
6
7
  import { latestRunDurableProgressAt } from "../scheduler/roleRunStall.js";
7
8
  import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
8
- import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
9
9
  export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now = new Date()) {
10
10
  const taskOpenInputRequestCount = store.listInputRequests(taskId)
11
11
  .filter((request) => request.status === "open").length;
@@ -372,9 +372,9 @@ function projectTaskRoleRuntime(run, session, tmux, events, mailbox, store, task
372
372
  driverId,
373
373
  launchId: session.launchId,
374
374
  nativeSessionId: session.nativeSessionId,
375
- receiptId: formatAgentRunReceiptId(run.taskId, run.id)
375
+ receiptId: agentRunDeliveryReceiptId(run)
376
376
  }) ?? run.id,
377
- receiptId: formatAgentRunReceiptId(run.taskId, run.id)
377
+ receiptId: agentRunDeliveryReceiptId(run)
378
378
  };
379
379
  let projection = projectRuntimeTaskEvents(fence, run.createdAt, events);
380
380
  projection = projectRuntimeMailbox(projection, mailbox);