@zq-silk/yui 0.2.0 → 0.4.2

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 (208) hide show
  1. package/ARCHITECTURE.md +603 -133
  2. package/README.md +806 -31
  3. package/dist/agent/agent.js +2 -1
  4. package/dist/agent/argumentPolicy.js +3 -1
  5. package/dist/agent/launchEnvironment.js +106 -0
  6. package/dist/agent/managedRuntimeEnvironment.js +34 -0
  7. package/dist/brief/taskBrief.js +11 -1
  8. package/dist/cli/agentConfigurationPicker.js +287 -0
  9. package/dist/cli/commandCatalog.js +488 -60
  10. package/dist/cli/completion.js +146 -22
  11. package/dist/cli/helpRenderer.js +3 -1
  12. package/dist/cli/interactionCandidates.js +53 -15
  13. package/dist/cli/interactionPolicy.js +267 -30
  14. package/dist/cli/interactiveSelection.js +6 -2
  15. package/dist/cli/invocationRouter.js +5 -1
  16. package/dist/cli/operatorWizard.js +87 -0
  17. package/dist/cli/roleOptionCatalog.js +1 -0
  18. package/dist/cli/roleWizard.js +185 -21
  19. package/dist/cli/updateCommand.js +62 -19
  20. package/dist/cli/updateOrchestrator.js +539 -0
  21. package/dist/cli/updatePorts.js +1119 -0
  22. package/dist/cli/upgradeCommand.js +112 -0
  23. package/dist/cli.js +1420 -86
  24. package/dist/commands/agentCommands.js +146 -3
  25. package/dist/commands/configCommands.js +126 -0
  26. package/dist/commands/controllerCommands.js +365 -0
  27. package/dist/commands/globalRoleCommands.js +168 -126
  28. package/dist/commands/jobCommands.js +18 -8
  29. package/dist/commands/operatorCommands.js +159 -9
  30. package/dist/commands/profileCommands.js +203 -0
  31. package/dist/commands/projectCommands.js +650 -0
  32. package/dist/commands/roleConfiguration.js +85 -24
  33. package/dist/commands/roleRuntimeGuard.js +12 -0
  34. package/dist/commands/roleSkillValidation.js +47 -0
  35. package/dist/commands/taskActor.js +127 -0
  36. package/dist/commands/taskCommands.js +4201 -313
  37. package/dist/commands/taskCompletionGate.js +131 -0
  38. package/dist/commands/taskContextCommand.js +244 -30
  39. package/dist/commands/taskInputCommands.js +177 -59
  40. package/dist/commands/taskIntegrationCommands.js +303 -0
  41. package/dist/commands/taskOverviewCommand.js +363 -0
  42. package/dist/commands/taskRoleRuntimeStatus.js +125 -19
  43. package/dist/commands/textInput.js +15 -0
  44. package/dist/completion/completionInstaller.js +26 -22
  45. package/dist/config/yuiConfig.js +4 -3
  46. package/dist/context/dispatchContext.js +90 -38
  47. package/dist/context/roleSessionContext.js +119 -0
  48. package/dist/controller/claudeLifecycleHook.js +203 -0
  49. package/dist/controller/clientRuntime.js +408 -56
  50. package/dist/controller/codexLifecycleHook.js +108 -0
  51. package/dist/controller/controller.js +1089 -32
  52. package/dist/controller/domainIdentity.js +505 -0
  53. package/dist/controller/ephemeralResourceReaper.js +131 -0
  54. package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
  55. package/dist/controller/providerHookRunFence.js +127 -0
  56. package/dist/controller/resourceCleanupLinux.js +286 -0
  57. package/dist/controller/resourceInventory.js +531 -0
  58. package/dist/controller/resourceInventoryLinux.js +610 -0
  59. package/dist/controller/runtime.js +629 -10
  60. package/dist/controller/runtimeEventInbox.js +564 -0
  61. package/dist/controller/runtimeEventProcessor.js +248 -0
  62. package/dist/controller/runtimeLaunchCoordinator.js +477 -0
  63. package/dist/controller/sessionNotify.js +121 -78
  64. package/dist/coordination/deadlineScheduler.js +15 -0
  65. package/dist/coordination/mailboxScheduler.js +108 -0
  66. package/dist/coordination/workMailbox.js +329 -0
  67. package/dist/coordination/workMailboxQueue.js +86 -0
  68. package/dist/core/controllerClient.js +19 -5
  69. package/dist/core/controllerEndpoint.js +37 -0
  70. package/dist/core/controllerServer.js +218 -10
  71. package/dist/core/protocol.js +6 -2
  72. package/dist/decision/decision.js +2 -1
  73. package/dist/doctor/doctor.js +681 -32
  74. package/dist/domain/validation.js +53 -0
  75. package/dist/errors/cliError.js +5 -3
  76. package/dist/event/taskEvent.js +7 -3
  77. package/dist/execution/codexThreadNaming.js +160 -0
  78. package/dist/execution/executionGroup.js +579 -0
  79. package/dist/executor/agentAdapter.js +255 -40
  80. package/dist/executor/agentConfigurationCatalog.js +326 -0
  81. package/dist/executor/agentConfigurationProbe.js +506 -0
  82. package/dist/executor/agentExecutor.js +625 -10
  83. package/dist/executor/codexConfigConflict.js +290 -0
  84. package/dist/executor/effectiveLaunch.js +340 -0
  85. package/dist/executor/executorRegistry.js +238 -36
  86. package/dist/executor/fileRoleLaunchPlanner.js +550 -40
  87. package/dist/executor/turnCompletion.js +126 -0
  88. package/dist/input/inputRequest.js +30 -9
  89. package/dist/integration/changeSet.js +36 -0
  90. package/dist/integration/checkResult.js +24 -0
  91. package/dist/integration/gitIntegrationService.js +695 -0
  92. package/dist/integration/integrationAttempt.js +142 -0
  93. package/dist/interaction/operatorPresentation.js +96 -0
  94. package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
  95. package/dist/lifecycle/exactRunTerminalization.js +572 -0
  96. package/dist/lifecycle/providerLifecycleMapping.js +190 -0
  97. package/dist/lifecycle/taskRoleSessionReset.js +124 -0
  98. package/dist/message/message.js +23 -7
  99. package/dist/milestone/milestone.js +2 -1
  100. package/dist/operator/operatorSessionHistory.js +124 -0
  101. package/dist/output/agentConfigurationPresentation.js +43 -0
  102. package/dist/output/rolePresentation.js +34 -10
  103. package/dist/output/terminal.js +8 -0
  104. package/dist/output/timePresentation.js +55 -0
  105. package/dist/profile/agentProfile.js +128 -0
  106. package/dist/repository/gitWorkspace.js +578 -24
  107. package/dist/repository/project.js +213 -0
  108. package/dist/repository/taskWorkspaceCoordinator.js +392 -0
  109. package/dist/repository/taskWorkspacePreparer.js +1688 -191
  110. package/dist/review/reviewConfig.js +11 -0
  111. package/dist/review/reviewRound.js +399 -0
  112. package/dist/review/taskFinalReviewContract.js +90 -0
  113. package/dist/role/role.js +124 -23
  114. package/dist/run/agentRun.js +155 -12
  115. package/dist/run/runIdentity.js +82 -0
  116. package/dist/runtime/exactControlPlane.js +472 -0
  117. package/dist/runtime/index.js +8 -0
  118. package/dist/runtime/lifecycleReservation.js +38 -0
  119. package/dist/runtime/ports.js +11 -0
  120. package/dist/runtime/preallocatedNativeSession.js +13 -0
  121. package/dist/runtime/promptEnvelope.js +30 -0
  122. package/dist/runtime/runtimeBinding.js +31 -0
  123. package/dist/runtime/runtimeOwner.js +14 -0
  124. package/dist/runtime/sessionLaunchRequest.js +62 -0
  125. package/dist/runtime/sessionTitle.js +54 -0
  126. package/dist/runtime/taskRuntimeIsolation.js +643 -0
  127. package/dist/runtime/tmuxAdapters.js +315 -0
  128. package/dist/runtime/turnCompletion.js +3 -0
  129. package/dist/runtime/validation.js +23 -0
  130. package/dist/scheduler/activeRoleRunDelivery.js +342 -32
  131. package/dist/scheduler/activeTaskProgress.js +63 -0
  132. package/dist/scheduler/leaderFailure.js +2 -1
  133. package/dist/scheduler/leaderWakeupProcessor.js +307 -66
  134. package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
  135. package/dist/scheduler/operatorNotification.js +44 -2
  136. package/dist/scheduler/ports.js +28 -1
  137. package/dist/scheduler/roleRunLiveness.js +131 -25
  138. package/dist/scheduler/roleRunStall.js +951 -0
  139. package/dist/scheduler/taskExecutionProjection.js +544 -0
  140. package/dist/scheduler/wakeupQueue.js +3 -0
  141. package/dist/setup/setupCommand.js +302 -52
  142. package/dist/storage/compatibleTaskStore.js +102 -0
  143. package/dist/storage/migration/baseline.js +78 -0
  144. package/dist/storage/migration/classifier.js +51 -0
  145. package/dist/storage/migration/compatibleCodec.js +53 -0
  146. package/dist/storage/migration/engine.js +147 -0
  147. package/dist/storage/migration/index.js +33 -0
  148. package/dist/storage/migration/planner.js +154 -0
  149. package/dist/storage/migration/productionRegistry.js +486 -0
  150. package/dist/storage/migration/registry.js +169 -0
  151. package/dist/storage/migration/report.js +54 -0
  152. package/dist/storage/migration/types.js +31 -0
  153. package/dist/storage/storageSchema.js +147 -123
  154. package/dist/storage/storageVersions.js +11 -0
  155. package/dist/storage/taskStore.js +1793 -197
  156. package/dist/storage/upgrade/homeClassification.js +156 -0
  157. package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
  158. package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
  159. package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
  160. package/dist/storage/upgrade/recordVersionScan.js +176 -0
  161. package/dist/storage/upgrade/recordVersions.js +159 -0
  162. package/dist/storage/upgrade/switchProgress.js +80 -0
  163. package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
  164. package/dist/storage/upgrade/upgradeReceipt.js +161 -0
  165. package/dist/storage/upgradeCoordination.js +186 -0
  166. package/dist/storage/upgradeFence.js +366 -0
  167. package/dist/task/task.js +132 -26
  168. package/dist/task/taskRecordReference.js +66 -0
  169. package/dist/tmux/commandExecutor.js +75 -2
  170. package/dist/tmux/tmuxManager.js +747 -49
  171. package/dist/version.js +23 -0
  172. package/dist/web/assets/assetManifest.js +62 -0
  173. package/dist/web/assets/client/app.js +631 -0
  174. package/dist/web/assets/client/components.js +605 -0
  175. package/dist/web/assets/client/dom.js +14 -0
  176. package/dist/web/assets/client/format.js +28 -0
  177. package/dist/web/assets/client/i18n.js +494 -0
  178. package/dist/web/assets/client/markdown.js +114 -0
  179. package/dist/web/assets/client/theme.js +32 -0
  180. package/dist/web/assets/client/view.js +458 -0
  181. package/dist/web/assets/fontData.js +12 -0
  182. package/dist/web/assets/fonts.js +12 -0
  183. package/dist/web/assets/shell.js +114 -0
  184. package/dist/web/assets/styles/cards.js +135 -0
  185. package/dist/web/assets/styles/layout.js +47 -0
  186. package/dist/web/assets/styles/markdown.js +29 -0
  187. package/dist/web/assets/styles/responsive.js +39 -0
  188. package/dist/web/assets/styles/tokens.js +101 -0
  189. package/dist/web/assets/styles/widgets.js +147 -0
  190. package/dist/web/tmuxWebTerminal.js +158 -0
  191. package/dist/web/webServer.js +463 -0
  192. package/dist/web/webSnapshot.js +148 -0
  193. package/dist/workItem/workItem.js +642 -23
  194. package/dist/workspace/gitChangeSetCapture.js +86 -0
  195. package/dist/workspace/workItemChangeSetManager.js +445 -0
  196. package/dist/worktree/managedWorkspace.js +202 -0
  197. package/docs/task-local-identity.md +62 -0
  198. package/i18n/README.zh-CN.md +406 -31
  199. package/package.json +10 -2
  200. package/skills/yui-leader/SKILL.md +601 -39
  201. package/skills/yui-operator/SKILL.md +255 -34
  202. package/skills/yui-reviewer/SKILL.md +57 -0
  203. package/skills/yui-worker/SKILL.md +214 -17
  204. package/dist/commands/repositoryCommands.js +0 -86
  205. package/dist/operator/operatorContext.js +0 -66
  206. package/dist/repository/repository.js +0 -55
  207. package/dist/scheduler/archivedTaskRuntime.js +0 -12
  208. package/dist/worktree/roleWorkspace.js +0 -62
@@ -1,18 +1,20 @@
1
1
  import { dataError, roleNotFound, taskNotFound, usageError } from "../errors/cliError.js";
2
2
  import { createTaskEvent } from "../event/taskEvent.js";
3
- import { updateRoleAgentSessionStatus } from "../executor/agentExecutor.js";
3
+ 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
- import { updateRoleStatus } from "../role/role.js";
7
- import { yieldAgentRun } from "../run/agentRun.js";
8
- import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
6
+ import { formatTimestamp } from "../output/timePresentation.js";
7
+ import { enqueueWork } from "../coordination/workMailboxQueue.js";
8
+ import { clearMatchingLeaderStallAttention, isRoleRunStalled, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
9
+ import { terminalizeExactTaskRun } from "../lifecycle/exactRunTerminalization.js";
10
+ import { formatAgentRunReceiptId, resolveTaskRecordReference } from "../task/taskRecordReference.js";
9
11
  const LEADER_ROLE = "leader";
10
12
  export function runTaskInputCommand(args, store, options) {
11
13
  const [command, ...rest] = args;
12
14
  switch (command) {
13
15
  case "request": return createRequest(rest, store, options);
14
16
  case "list": return listRequests(rest, store);
15
- case "show": return showRequest(rest, store);
17
+ case "show": return showRequest(rest, store, options);
16
18
  case "answer": return answerRequest(rest, store, options);
17
19
  case "cancel": return cancelRequest(rest, store, options);
18
20
  default:
@@ -36,7 +38,7 @@ function createRequest(args, store, options) {
36
38
  exactPositionals(parsed.positionals, 1, usage);
37
39
  const question = requiredOption(parsed.options, "--question");
38
40
  const choices = (parsed.multiOptions.get("--choice") ?? []).map(parseInputChoice);
39
- const blockedRefs = (parsed.multiOptions.get("--blocks") ?? []).map(parseInputBlockedRef);
41
+ const blockedValues = parsed.multiOptions.get("--blocks") ?? [];
40
42
  const recommendedChoiceKey = optionalNonEmptyOption(parsed.options, "--recommend");
41
43
  const timeoutSeconds = optionalNonEmptyOption(parsed.options, "--timeout-seconds");
42
44
  if ((recommendedChoiceKey === undefined) !== (timeoutSeconds === undefined)) {
@@ -54,16 +56,42 @@ function createRequest(args, store, options) {
54
56
  const task = requireTask(tx, parsed.positionals[0]);
55
57
  if (task.status !== "active")
56
58
  throw usageError(inactiveTaskMessage(task, "requesting input"));
59
+ const blockedRefs = blockedValues.map((value) => parseInputBlockedRef(value, task.id));
57
60
  validateBlockedInputOwnership(tx, task.id, blockedRefs);
58
61
  const origin = requireLeaderInputOrigin(tx, task.id, options.environment);
59
62
  const created = createInputRequest(tx.nextInputRequestId(task.id), task.id, origin.requester, { question, choices, blockedRefs, policy }, now);
63
+ const wasStalled = isRoleRunStalled(tx.listEvents(task.id), origin.run.id);
60
64
  tx.saveInputRequest(task.id, created);
61
- tx.saveAgentRun(yieldAgentRun(origin.run, `Waiting for input ${created.id}: ${created.question}`, now));
62
- tx.clearActiveAgentRun(task.id, LEADER_ROLE);
63
- tx.saveRole(task.id, updateRoleStatus(origin.role, "idle", now));
64
- if (origin.sessions?.sessions[origin.role.activeAgentId]?.status === "running") {
65
- tx.saveTaskRoleSessionSet(updateRoleAgentSessionStatus(origin.sessions, origin.role.activeAgentId, "ready", now));
65
+ enqueueWork(tx, { kind: "operator" }, "input-requested", now, [
66
+ { type: "input", taskId: task.id, id: created.id },
67
+ { type: "run", taskId: task.id, id: origin.run.id }
68
+ ]);
69
+ const terminal = terminalizeExactTaskRun(tx, {
70
+ taskId: task.id,
71
+ roleName: LEADER_ROLE,
72
+ agentId: origin.run.effective.agentId,
73
+ runId: origin.run.id,
74
+ receiptId: formatAgentRunReceiptId(task.id, origin.run.id),
75
+ ...(origin.requester.nativeSessionId === undefined
76
+ ? {}
77
+ : { nativeSessionId: origin.requester.nativeSessionId }),
78
+ outcome: {
79
+ status: "yielded",
80
+ summary: `Waiting for input ${created.id}: ${created.question}`
81
+ }
82
+ }, now);
83
+ if (terminal.disposition !== "applied") {
84
+ throw usageError(`Task Leader Run changed while requesting input: ${origin.run.id}/${terminal.reason}.`);
85
+ }
86
+ if (wasStalled) {
87
+ recordTaskEvent(tx, task.id, RUN_RECOVERED_EVENT, {
88
+ runId: origin.run.id,
89
+ roleName: LEADER_ROLE,
90
+ progressAt: now.toISOString(),
91
+ kind: "input-request"
92
+ }, now);
66
93
  }
94
+ clearMatchingLeaderStallAttention(tx, task.id, origin.run.id);
67
95
  recordTaskEvent(tx, task.id, "input.requested", {
68
96
  requestId: created.id,
69
97
  requesterRunId: created.requester.runId,
@@ -71,7 +99,7 @@ function createRequest(args, store, options) {
71
99
  }, now);
72
100
  return created;
73
101
  });
74
- options.runtime?.notifyStateChanged(request.taskId);
102
+ notifyMailbox(options, { kind: "operator" }, request.taskId);
75
103
  return output(`Created input request ${request.id} for ${request.taskId}\n`, { request });
76
104
  }
77
105
  function listRequests(args, store) {
@@ -88,9 +116,10 @@ function listRequests(args, store) {
88
116
  const requests = parsed.options.has("--all")
89
117
  ? all
90
118
  : all.filter((request) => request.status === "open");
91
- const rendered = requests.length === 0
92
- ? "No input requests found.\n"
93
- : `${renderTable(taskId === undefined ? "Input inbox" : `Input requests: ${taskId}`, [
119
+ let rendered = "No input requests found.\n";
120
+ if (requests.length > 0) {
121
+ const timeZone = store.getConfig().timeZone;
122
+ rendered = `${renderTable(taskId === undefined ? "Input inbox" : `Input requests: ${taskId}`, [
94
123
  { header: "Input", minWidth: 6, maxWidth: 18 },
95
124
  { header: "Task", minWidth: 6, maxWidth: 18 },
96
125
  { header: "Status", minWidth: 6, maxWidth: 10 },
@@ -98,29 +127,29 @@ function listRequests(args, store) {
98
127
  { header: "Question", minWidth: 8, maxWidth: 72 },
99
128
  { header: "Created", minWidth: 10, maxWidth: 28 }
100
129
  ], requests.map((request) => [
101
- request.id,
130
+ taskId === undefined ? `${request.taskId}/${request.id}` : request.id,
102
131
  request.taskId,
103
132
  request.status,
104
133
  request.policy.kind,
105
134
  request.question,
106
- request.createdAt
135
+ formatTimestamp(request.createdAt, timeZone)
107
136
  ]), defaultTableWidth())}\n`;
137
+ }
108
138
  return output(rendered, { requests });
109
139
  }
110
- function showRequest(args, store) {
111
- const usage = "Task input show usage: yui task input show <input> [--task <task>].";
140
+ function showRequest(args, store, options) {
141
+ const usage = "Task input show usage: yui task input show (<task>/<input> | <input> --task <task>).";
112
142
  const parsed = parseTail(args, new Set(["--task"]), usage);
113
143
  exactPositionals(parsed.positionals, 1, usage);
114
- const taskId = optionalNonEmptyOption(parsed.options, "--task");
115
- const request = taskId === undefined
116
- ? store.findInputRequest(parsed.positionals[0])
117
- : store.getInputRequest(requireTask(store, taskId).id, parsed.positionals[0]);
118
- if (request === null)
119
- throw dataError(`Input request not found: ${parsed.positionals[0]}.`);
120
- return output(renderInputRequest(request), { request });
144
+ const reference = inputRequestReference(store, parsed.positionals[0], optionalNonEmptyOption(parsed.options, "--task"), options.environment);
145
+ const request = store.getInputRequest(reference.taskId, reference.localId);
146
+ if (request === null) {
147
+ throw dataError(`Input request not found: ${reference.taskId}/${reference.localId}.`);
148
+ }
149
+ return output(renderInputRequest(request, store.getConfig().timeZone), { request });
121
150
  }
122
151
  function answerRequest(args, store, options) {
123
- const usage = "Task input answer usage: yui task input answer <input> [--task <task>] (--choice <key> | --text <text>).";
152
+ const usage = "Task input answer usage: yui task input answer (<task>/<input> | <input> --task <task>) (--choice <key> | --text <text>).";
124
153
  const parsed = parseTail(args, new Set(["--task", "--choice", "--text"]), usage);
125
154
  exactPositionals(parsed.positionals, 1, usage);
126
155
  const choice = optionalNonEmptyOption(parsed.options, "--choice");
@@ -128,12 +157,11 @@ function answerRequest(args, store, options) {
128
157
  if ((choice === undefined) === (text === undefined)) {
129
158
  throw usageError("Exactly one of --choice or --text is required.", usage);
130
159
  }
131
- const taskHint = optionalNonEmptyOption(parsed.options, "--task");
132
- const located = taskHint === undefined
133
- ? store.findInputRequest(parsed.positionals[0])
134
- : store.getInputRequest(requireTask(store, taskHint).id, parsed.positionals[0]);
135
- if (located === null)
136
- throw dataError(`Input request not found: ${parsed.positionals[0]}.`);
160
+ const reference = inputRequestReference(store, parsed.positionals[0], optionalNonEmptyOption(parsed.options, "--task"), options.environment);
161
+ const located = store.getInputRequest(reference.taskId, reference.localId);
162
+ if (located === null) {
163
+ throw dataError(`Input request not found: ${reference.taskId}/${reference.localId}.`);
164
+ }
137
165
  const answer = choice === undefined ? { text: text } : { choiceKey: choice };
138
166
  const now = clock(options);
139
167
  const request = store.transaction((tx) => {
@@ -149,10 +177,10 @@ function answerRequest(args, store, options) {
149
177
  requestId: answered.id,
150
178
  answeredBy: answered.resolution.answeredBy
151
179
  }, now);
152
- queueLeaderWakeup(tx, task.id, `input-answered:${answered.id}`, now);
180
+ enqueueWork(tx, { kind: "role", taskId: task.id, roleName: LEADER_ROLE }, `input-answered:${answered.id}`, now, [{ type: "input", taskId: task.id, id: answered.id }]);
153
181
  return answered;
154
182
  });
155
- options.runtime?.notifyStateChanged(request.taskId);
183
+ notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE }, request.taskId);
156
184
  return output(`Answered input request ${request.id} for ${request.taskId}\n`, { request });
157
185
  }
158
186
  function cancelRequest(args, store, options) {
@@ -168,13 +196,21 @@ function cancelRequest(args, store, options) {
168
196
  const current = tx.getInputRequest(task.id, parsed.positionals[1]);
169
197
  if (current === null)
170
198
  throw dataError(`Input request not found: ${parsed.positionals[1]}.`);
171
- assertInputCancelOrigin(current, options.environment);
199
+ const cancelledBy = assertInputCancelOrigin(tx, current, options.environment);
172
200
  const cancelled = cancelInputRequest(current, reason, now);
201
+ if (cancelled.status !== "cancelled") {
202
+ throw new Error(`Input request ${cancelled.id} did not enter the cancelled state.`);
203
+ }
173
204
  tx.saveInputRequest(task.id, cancelled);
174
- recordTaskEvent(tx, task.id, "input.cancelled", { requestId: cancelled.id }, now);
205
+ recordTaskEvent(tx, task.id, "input.cancelled", {
206
+ requestId: cancelled.id,
207
+ cancelledBy,
208
+ reason: cancelled.cancellation.reason
209
+ }, now);
210
+ enqueueWork(tx, { kind: "role", taskId: task.id, roleName: LEADER_ROLE }, `input-cancelled:${cancelled.id}`, now, [{ type: "input", taskId: task.id, id: cancelled.id }]);
175
211
  return cancelled;
176
212
  });
177
- options.runtime?.notifyStateChanged(request.taskId);
213
+ notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE }, request.taskId);
178
214
  return output(`Cancelled input request ${request.id} for ${request.taskId}\n`, { request });
179
215
  }
180
216
  function requireLeaderInputOrigin(store, taskId, environment) {
@@ -184,24 +220,24 @@ function requireLeaderInputOrigin(store, taskId, environment) {
184
220
  if (env.YUI_SESSION_SCOPE !== "task"
185
221
  || env.YUI_TASK_ID !== taskId
186
222
  || env.YUI_ROLE !== LEADER_ROLE
187
- || env.YUI_AGENT_ID !== role.activeAgentId
188
223
  || run === null
189
- || env.YUI_RUN_ID !== run.id
224
+ || env.YUI_AGENT_ID !== run.effective.agentId
190
225
  || run.status !== "active"
191
- || run.deliveredAt === undefined
226
+ || run.pushedAt === undefined
192
227
  || run.workItemId !== undefined) {
193
228
  throw usageError("Task input request requires the active Leader Run environment.");
194
229
  }
195
230
  const sessions = store.getTaskRoleSessionSet(taskId, LEADER_ROLE);
196
231
  const nativeSessionId = trimmed(env.YUI_NATIVE_SESSION_ID);
197
232
  if (nativeSessionId !== undefined
198
- && sessions?.sessions[role.activeAgentId]?.nativeSessionId !== nativeSessionId) {
233
+ && sessions?.sessions[run.effective.agentId]?.nativeSessionId !== nativeSessionId) {
199
234
  throw usageError("Task input request native session does not match the active Leader session.");
200
235
  }
201
236
  return {
202
237
  requester: {
238
+ taskId,
203
239
  roleName: "leader",
204
- agentId: role.activeAgentId,
240
+ agentId: run.effective.agentId,
205
241
  runId: run.id,
206
242
  ...(nativeSessionId === undefined ? {} : { nativeSessionId })
207
243
  },
@@ -210,17 +246,62 @@ function requireLeaderInputOrigin(store, taskId, environment) {
210
246
  sessions
211
247
  };
212
248
  }
213
- function assertInputCancelOrigin(request, environment) {
249
+ function assertInputCancelOrigin(store, request, environment) {
214
250
  const env = environment ?? {};
251
+ if (isCurrentGlobalOperator(store, env)) {
252
+ return "operator";
253
+ }
215
254
  if (env.YUI_SESSION_SCOPE !== "task"
216
255
  || env.YUI_TASK_ID !== request.taskId
217
256
  || env.YUI_ROLE !== request.requester.roleName
218
257
  || env.YUI_AGENT_ID !== request.requester.agentId
219
- || env.YUI_RUN_ID !== request.requester.runId
220
258
  || (request.requester.nativeSessionId !== undefined
221
259
  && env.YUI_NATIVE_SESSION_ID !== request.requester.nativeSessionId)) {
222
260
  throw usageError("Only the originating Leader may cancel this input request.");
223
261
  }
262
+ return "leader";
263
+ }
264
+ function isCurrentGlobalOperator(store, environment) {
265
+ if (environment.YUI_SESSION_SCOPE !== "global"
266
+ || environment.YUI_ROLE !== "operator"
267
+ || environment.YUI_TASK_ID !== undefined)
268
+ return false;
269
+ const role = store.getGlobalRole("operator");
270
+ if (role === null)
271
+ return false;
272
+ const sessions = store.getGlobalRoleSessionSet(role.name);
273
+ const session = activeLiveRoleAgentSession(sessions);
274
+ if (sessions === null || session === null || sessions.activeAgentId !== role.activeAgentId) {
275
+ return false;
276
+ }
277
+ const agentId = exactIdentity(environment.YUI_AGENT_ID);
278
+ const adapterId = exactIdentity(environment.YUI_ADAPTER_ID);
279
+ const launchId = exactIdentity(environment.YUI_LAUNCH_ID);
280
+ const nativeSessionId = exactIdentity(environment.YUI_NATIVE_SESSION_ID);
281
+ const binding = role.agentBindings[role.activeAgentId];
282
+ // A fresh Codex launch discovers its native Session asynchronously. Its
283
+ // launch envelope therefore cannot carry YUI_NATIVE_SESSION_ID, but the
284
+ // durable Session still binds that provider identity to the exact launch
285
+ // generation. Accept that one transport shape only when the current
286
+ // launch, Agent, and adapter all match; Claude and stale/unknown launches
287
+ // remain fail-closed on the native identity fence.
288
+ const nativeSessionMatches = binding !== undefined && (nativeSessionId === session.nativeSessionId
289
+ || (nativeSessionId === undefined
290
+ && binding.adapterId === "codex"
291
+ && session.adapterId === "codex"
292
+ && session.launchId !== undefined
293
+ && session.launchId === launchId));
294
+ return agentId !== undefined
295
+ && adapterId !== undefined
296
+ && launchId !== undefined
297
+ && binding !== undefined
298
+ && binding.agentId === session.agentId
299
+ && binding.adapterId === session.adapterId
300
+ && session.agentId === agentId
301
+ && session.adapterId === adapterId
302
+ && session.launchId !== undefined
303
+ && session.launchId === launchId
304
+ && nativeSessionMatches;
224
305
  }
225
306
  function inputAnswerer(environment) {
226
307
  const env = environment ?? {};
@@ -237,28 +318,49 @@ function parseInputChoice(value) {
237
318
  }
238
319
  return { key: value.slice(0, separator).trim(), label: value.slice(separator + 1).trim() };
239
320
  }
240
- function parseInputBlockedRef(value) {
321
+ function parseInputBlockedRef(value, taskId) {
241
322
  const separator = value.indexOf(":");
242
323
  const type = value.slice(0, separator);
243
324
  const id = value.slice(separator + 1).trim();
244
325
  if ((type !== "work-item" && type !== "run") || separator <= 0 || id.length === 0) {
245
326
  throw usageError("--blocks must use work-item:<id> or run:<id>.");
246
327
  }
247
- return { type, id };
328
+ return { type, taskId, id };
248
329
  }
249
330
  function validateBlockedInputOwnership(store, taskId, references) {
250
331
  for (const reference of references) {
251
332
  const record = reference.type === "work-item"
252
- ? store.findWorkItem(reference.id)
253
- : store.findAgentRun(reference.id);
333
+ ? store.getWorkItem(taskId, reference.id)
334
+ : store.getAgentRun(taskId, reference.id);
254
335
  if (record === null)
255
336
  throw dataError(`Blocked ${reference.type} not found: ${reference.id}.`);
256
- if (record.taskId !== taskId) {
257
- throw usageError(`Blocked ${reference.type} belongs to another Task: ${reference.id}.`);
258
- }
259
337
  }
260
338
  }
261
- function renderInputRequest(request) {
339
+ function inputRequestReference(store, value, taskHint, environment) {
340
+ const explicitTaskId = taskHint === undefined
341
+ ? undefined
342
+ : requireTask(store, taskHint).id;
343
+ let reference;
344
+ try {
345
+ reference = resolveTaskRecordReference(value, {
346
+ kind: "inputRequest",
347
+ label: "Input request reference",
348
+ ...(explicitTaskId !== undefined
349
+ ? { contextTaskId: explicitTaskId }
350
+ : environment?.YUI_TASK_ID === undefined
351
+ ? {}
352
+ : { contextTaskId: environment.YUI_TASK_ID })
353
+ });
354
+ }
355
+ catch (error) {
356
+ throw usageError(error instanceof Error ? error.message : String(error));
357
+ }
358
+ if (explicitTaskId !== undefined && reference.taskId !== explicitTaskId) {
359
+ throw usageError(`Input request belongs to another Task: ${reference.taskId}/${reference.localId}.`);
360
+ }
361
+ return reference;
362
+ }
363
+ function renderInputRequest(request, timeZone) {
262
364
  return [
263
365
  `Input: ${request.id}`,
264
366
  `Task: ${request.taskId}`,
@@ -275,22 +377,22 @@ function renderInputRequest(request) {
275
377
  ? ["Policy: user response required"]
276
378
  : [
277
379
  `Policy: use recommended choice ${request.policy.recommendedChoiceKey} after timeout`,
278
- `Timeout: ${request.policy.timeoutAt}`
380
+ `Timeout: ${formatTimestamp(request.policy.timeoutAt, timeZone)}`
279
381
  ]),
280
382
  ...(request.status === "answered"
281
383
  ? [
282
384
  `Answered by: ${request.resolution.answeredBy}`,
283
385
  `Answer: ${request.resolution.answer.text}`,
284
- `Answered: ${request.resolution.answeredAt}`
386
+ `Answered: ${formatTimestamp(request.resolution.answeredAt, timeZone)}`
285
387
  ]
286
388
  : request.status === "cancelled"
287
389
  ? [
288
390
  `Cancellation: ${request.cancellation.reason}`,
289
- `Cancelled: ${request.cancellation.cancelledAt}`
391
+ `Cancelled: ${formatTimestamp(request.cancellation.cancelledAt, timeZone)}`
290
392
  ]
291
393
  : []),
292
- `Created: ${request.createdAt}`,
293
- `Updated: ${request.updatedAt}`
394
+ `Created: ${formatTimestamp(request.createdAt, timeZone)}`,
395
+ `Updated: ${formatTimestamp(request.updatedAt, timeZone)}`
294
396
  ].join("\n").concat("\n");
295
397
  }
296
398
  function requireTask(store, taskId) {
@@ -313,6 +415,8 @@ function inactiveTaskMessage(task, action) {
313
415
  if (task.status === "completed") {
314
416
  return `Task ${task.id} is completed; reopen it before ${action}.`;
315
417
  }
418
+ if (task.status === "retired")
419
+ return `Task ${task.id} is retired; it cannot resume ${action}.`;
316
420
  return `Task is archived: ${task.id}.`;
317
421
  }
318
422
  function requiredOption(options, name) {
@@ -332,7 +436,7 @@ function clock(options) {
332
436
  return options.now?.() ?? new Date();
333
437
  }
334
438
  function recordTaskEvent(store, taskId, type, payload, now) {
335
- store.saveEvent(taskId, createTaskEvent(store.nextEventId(taskId), type, payload, now));
439
+ store.saveEvent(taskId, createTaskEvent(store.nextEventId(taskId), taskId, type, payload, now));
336
440
  }
337
441
  function requiredText(value, label) {
338
442
  const normalized = value?.trim();
@@ -344,6 +448,12 @@ function trimmed(value) {
344
448
  const normalized = value?.trim();
345
449
  return normalized === undefined || normalized.length === 0 ? undefined : normalized;
346
450
  }
451
+ function exactIdentity(value) {
452
+ if (value === undefined || value.includes("\0"))
453
+ return undefined;
454
+ const normalized = value.trim();
455
+ return normalized.length === 0 || normalized !== value ? undefined : normalized;
456
+ }
347
457
  function timeoutAfter(now, value) {
348
458
  if (!/^[1-9][0-9]*$/.test(value)) {
349
459
  throw usageError("--timeout-seconds must be a positive integer.");
@@ -421,3 +531,11 @@ function parseMultiValueTail(args, valueOptions, repeatOptions, usage) {
421
531
  }
422
532
  return { positionals, options, multiOptions };
423
533
  }
534
+ function notifyMailbox(options, target, compatibilityTaskId) {
535
+ if (options.runtime?.notifyMailboxChanged !== undefined) {
536
+ options.runtime.notifyMailboxChanged(target);
537
+ }
538
+ else {
539
+ options.runtime?.notifyStateChanged(compatibilityTaskId);
540
+ }
541
+ }