@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,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { createPromptEnvelope, createSessionLaunchRequest } from "../runtime/index.js";
2
3
  /**
3
4
  * Scheduler-to-tmux adapter. It retains only in-process prepared launch data;
4
5
  * durable session identity remains owned by FileTaskStore.
@@ -7,55 +8,264 @@ export class ExecutorRegistry {
7
8
  planner;
8
9
  tmux;
9
10
  readiness;
11
+ runtimePorts;
10
12
  #prepared = new Map();
11
- constructor(planner, tmux, readiness = agentComposerReadinessProbe) {
13
+ constructor(planner, tmux, readiness = agentProcessReadinessProbe, runtimePorts) {
12
14
  this.planner = planner;
13
15
  this.tmux = tmux;
14
16
  this.readiness = readiness;
17
+ this.runtimePorts = runtimePorts;
15
18
  }
16
19
  async prepareRoleSession(input) {
17
20
  if (input.mode === "resume" && !hasText(input.nativeSessionId)) {
18
21
  throw new Error("Role session resume requires a native session id.");
19
22
  }
20
- const planned = this.planner.plan(input);
21
- this.tmux.ensureRoleWindow(input.taskId, planned.role, planned.launch);
22
- const delivery = {
23
+ let sessionStarted = false;
24
+ const deliveryBase = {
23
25
  deliveryId: preparedDeliveryId(input),
24
26
  taskId: input.taskId,
25
27
  roleName: input.roleName,
26
28
  agentId: input.agentId,
27
29
  adapterId: input.adapterId,
28
- mode: input.mode
30
+ mode: input.mode,
31
+ ...(input.runId === undefined ? {} : { runId: input.runId })
32
+ };
33
+ const cached = this.#prepared.get(deliveryBase.deliveryId);
34
+ if (cached !== undefined)
35
+ return cached.delivery;
36
+ let binding;
37
+ let planned;
38
+ let session;
39
+ if (this.runtimePorts === undefined) {
40
+ planned = this.planner.plan(input);
41
+ sessionStarted = this.tmux.ensureRoleWindow(input.taskId, planned.role, planned.launch);
42
+ session = planned.session;
43
+ }
44
+ else {
45
+ const common = {
46
+ owner: { scope: "task", taskId: input.taskId, roleName: input.roleName },
47
+ agentId: input.agentId,
48
+ adapterId: input.adapterId,
49
+ effective: input.effective,
50
+ workspace: input.workspace,
51
+ ...(input.managedWorkspace === undefined
52
+ ? {}
53
+ : { managedWorkspace: input.managedWorkspace }),
54
+ ...(input.runtimePolicy === undefined
55
+ ? {}
56
+ : { runtimePolicy: input.runtimePolicy }),
57
+ ...(input.runId === undefined ? {} : { runId: input.runId })
58
+ };
59
+ if (this.runtimePorts.launchCoordinator !== undefined) {
60
+ binding = await this.runtimePorts.launchCoordinator.prepare(input.mode === "new"
61
+ ? { ...common, mode: "new" }
62
+ : {
63
+ ...common,
64
+ mode: "resume",
65
+ nativeSessionId: input.nativeSessionId
66
+ }, "deferred", undefined, input.beforeHostStart);
67
+ }
68
+ else {
69
+ const request = input.mode === "new"
70
+ ? createSessionLaunchRequest({
71
+ ...common,
72
+ launchId: deliveryBase.deliveryId,
73
+ mode: "new"
74
+ })
75
+ : createSessionLaunchRequest({
76
+ ...common,
77
+ launchId: deliveryBase.deliveryId,
78
+ mode: "resume",
79
+ nativeSessionId: input.nativeSessionId
80
+ });
81
+ binding = request.mode === "new"
82
+ ? await this.runtimePorts.sessionHost.start(request, input.beforeHostStart)
83
+ : await this.runtimePorts.sessionHost.resume(request, input.beforeHostStart);
84
+ }
85
+ sessionStarted = binding.hostCreated === true;
86
+ session = binding.nativeSessionId === undefined
87
+ ? null
88
+ : {
89
+ agentId: binding.agentId,
90
+ adapterId: binding.adapterId,
91
+ nativeSessionId: binding.nativeSessionId,
92
+ status: "ready",
93
+ effective: input.effective
94
+ };
95
+ }
96
+ const delivery = {
97
+ ...deliveryBase,
98
+ ...(binding === undefined ? {} : { launchId: binding.launchId }),
99
+ sessionStarted,
100
+ session,
101
+ ...(input.runId !== undefined
102
+ && ((sessionStarted
103
+ && (planned?.initialPromptRunId ?? binding?.initialPromptRunId) === input.runId)
104
+ || binding?.launchPromptUncertainRunId === input.runId)
105
+ ? { inputSubmittedAtLaunch: true }
106
+ : {})
29
107
  };
30
- this.#prepared.set(delivery.deliveryId, planned);
108
+ this.#prepared.set(delivery.deliveryId, {
109
+ delivery,
110
+ session,
111
+ ...(planned === undefined ? {} : { planned }),
112
+ ...(binding === undefined ? {} : { binding })
113
+ });
31
114
  return delivery;
32
115
  }
33
116
  async waitUntilReady(delivery) {
34
- const planned = this.requirePrepared(delivery);
35
- this.tmux.waitUntilReady(delivery.taskId, delivery.roleName, this.readiness(delivery.adapterId));
36
- return { prepared: delivery, session: planned.session };
117
+ const prepared = this.requirePrepared(delivery);
118
+ if (prepared.binding === undefined) {
119
+ this.tmux.waitUntilReady(delivery.taskId, delivery.roleName, this.readiness(delivery.adapterId));
120
+ }
121
+ return { prepared: delivery, session: prepared.session };
37
122
  }
38
123
  async sendOnce(input) {
39
- this.requirePrepared(input.delivery.prepared);
40
- return this.tmux.sendRoleInputOnce(input.delivery.prepared.taskId, input.delivery.prepared.roleName, input.receiptId, input.text, this.readiness(input.delivery.prepared.adapterId));
124
+ const prepared = this.requirePrepared(input.delivery.prepared);
125
+ if (prepared.binding !== undefined && this.runtimePorts !== undefined) {
126
+ const runId = input.delivery.prepared.runId;
127
+ if (runId === undefined) {
128
+ throw new Error("Runtime prompt delivery requires a Task-local Run id.");
129
+ }
130
+ const outcome = await this.runtimePorts.promptPush.tryPush({
131
+ binding: prepared.binding,
132
+ envelope: createPromptEnvelope({
133
+ id: input.receiptId,
134
+ source: {
135
+ kind: "agent-run",
136
+ taskId: input.delivery.prepared.taskId,
137
+ localId: runId
138
+ },
139
+ text: input.text,
140
+ createdAt: new Date()
141
+ })
142
+ });
143
+ if (outcome === "delivered") {
144
+ this.#prepared.delete(input.delivery.prepared.deliveryId);
145
+ }
146
+ return outcome === "delivered" ? "sent" : outcome;
147
+ }
148
+ const outcome = this.tmux.sendRoleInputOnce(input.delivery.prepared.taskId, input.delivery.prepared.roleName, input.receiptId, input.text, this.readiness(input.delivery.prepared.adapterId));
149
+ if (outcome === "sent" || outcome === "already-sent") {
150
+ this.#prepared.delete(input.delivery.prepared.deliveryId);
151
+ }
152
+ return outcome;
41
153
  }
42
154
  async notifyOperatorInputOnce(input) {
43
- if (this.tmux.probeRoleStatus("operator", input.roleName) !== "running") {
44
- return "unavailable";
155
+ const probe = this.readiness(input.adapterId, "operator");
156
+ return this.tmux.sendRoleInputOnceIfReadyAsync === undefined
157
+ ? this.tmux.sendRoleInputOnceIfReady("operator", input.roleName, input.receiptId, input.text, probe)
158
+ : this.tmux.sendRoleInputOnceIfReadyAsync("operator", input.roleName, input.receiptId, input.text, probe);
159
+ }
160
+ forgetPrepared(input) {
161
+ for (const [deliveryId, prepared] of this.#prepared) {
162
+ const delivery = prepared.delivery;
163
+ if (delivery.taskId !== input.taskId
164
+ || delivery.roleName !== input.roleName
165
+ || (input.runId !== undefined
166
+ && delivery.runId !== input.runId)
167
+ || (input.launchId !== undefined
168
+ && delivery.launchId !== input.launchId)) {
169
+ continue;
170
+ }
171
+ this.#prepared.delete(deliveryId);
45
172
  }
46
- return this.tmux.sendRoleInputOnceIfReady("operator", input.roleName, input.receiptId, input.text, this.readiness(input.adapterId));
47
173
  }
48
174
  async inspectRole(input) {
49
- return this.tmux.probeRoleStatus(input.taskId, input.roleName) === "running"
175
+ const status = this.tmux.probeRoleStatusAsync === undefined
176
+ ? this.tmux.probeRoleStatus(input.taskId, input.roleName)
177
+ : await this.tmux.probeRoleStatusAsync(input.taskId, input.roleName);
178
+ return status === "running"
50
179
  ? "present"
51
180
  : "absent";
52
181
  }
53
- async findExistingReceipt(input) {
54
- const planned = this.requirePrepared(input.delivery);
55
- return this.tmux.hasDeliveryReceipt(input.delivery.taskId, input.delivery.roleName, input.receiptId) ? { prepared: input.delivery, session: planned.session } : null;
182
+ async inspectRoleReadiness(input) {
183
+ const status = await this.inspectRole(input);
184
+ if (status === "absent")
185
+ return "absent";
186
+ try {
187
+ const pane = this.tmux.inspectPaneAsync === undefined
188
+ ? this.tmux.inspectPane?.(input.taskId, input.roleName)
189
+ : await this.tmux.inspectPaneAsync(input.taskId, input.roleName);
190
+ if (pane === undefined)
191
+ return "busy";
192
+ return this.readiness(input.adapterId)(pane) ? "ready" : "busy";
193
+ }
194
+ catch {
195
+ // A pane can disappear between inventory and the targeted process snapshot; the
196
+ // ordinary liveness pass will classify it on the next reconciliation.
197
+ return "busy";
198
+ }
199
+ }
200
+ async inspectRoles(inputs, resourceInputs) {
201
+ if (this.tmux.inspectRolePaneInventory === undefined
202
+ && this.tmux.inspectRolePaneInventoryAsync === undefined) {
203
+ return Promise.all(inputs.map(async (input) => ({
204
+ taskId: input.taskId,
205
+ roleName: input.roleName,
206
+ status: await this.inspectRole(input)
207
+ })));
208
+ }
209
+ const inventory = this.tmux.inspectRolePaneInventoryAsync === undefined
210
+ ? this.tmux.inspectRolePaneInventory()
211
+ : await this.tmux.inspectRolePaneInventoryAsync();
212
+ let resources = new Map();
213
+ const requested = resourceInputs ?? inputs.map((input) => ({
214
+ taskId: input.taskId,
215
+ roleName: input.roleName,
216
+ ...(input.runId === undefined ? {} : { runId: input.runId }),
217
+ agentId: input.agentId,
218
+ adapterId: input.adapterId,
219
+ ...(input.nativeSessionId === undefined
220
+ ? {}
221
+ : { nativeSessionId: input.nativeSessionId }),
222
+ ...(input.launchId === undefined ? {} : { launchId: input.launchId }),
223
+ ...(input.progressAt === undefined ? {} : { progressAt: input.progressAt })
224
+ }));
225
+ if (this.runtimePorts?.roleResourceInventory !== undefined
226
+ && requested.length > 0) {
227
+ try {
228
+ for (const entry of await this.runtimePorts.roleResourceInventory(inventory, requested)) {
229
+ const key = `${entry.taskId}\0${entry.roleName}`;
230
+ if (!resources.has(key))
231
+ resources.set(key, entry.resource);
232
+ }
233
+ }
234
+ catch {
235
+ // Resource evidence is advisory. A failed process snapshot must not
236
+ // turn the authoritative pane inventory into an absent observation.
237
+ }
238
+ }
239
+ const present = new Set(inventory
240
+ .filter((pane) => !pane.dead)
241
+ .map((pane) => `${pane.taskId}\0${pane.roleName}`));
242
+ return inputs.map((input) => {
243
+ const key = `${input.taskId}\0${input.roleName}`;
244
+ const resource = resources.get(key);
245
+ return {
246
+ taskId: input.taskId,
247
+ roleName: input.roleName,
248
+ status: present.has(key) ? "present" : "absent",
249
+ ...(resource === undefined ? {} : { resource })
250
+ };
251
+ });
56
252
  }
57
- async stopTask(taskId) {
58
- return this.tmux.stopTask(taskId);
253
+ async stopRole(taskId, roleName) {
254
+ const status = this.tmux.probeRoleStatusAsync === undefined
255
+ ? this.tmux.probeRoleStatus(taskId, roleName)
256
+ : await this.tmux.probeRoleStatusAsync(taskId, roleName);
257
+ if (status !== "running") {
258
+ this.forgetPrepared({ taskId, roleName });
259
+ return false;
260
+ }
261
+ if (this.tmux.killRoleAsync === undefined) {
262
+ this.tmux.killRole(taskId, roleName);
263
+ }
264
+ else {
265
+ await this.tmux.killRoleAsync(taskId, roleName);
266
+ }
267
+ this.forgetPrepared({ taskId, roleName });
268
+ return true;
59
269
  }
60
270
  requirePrepared(delivery) {
61
271
  const planned = this.#prepared.get(delivery.deliveryId);
@@ -65,23 +275,13 @@ export class ExecutorRegistry {
65
275
  return planned;
66
276
  }
67
277
  }
68
- export function agentComposerReadinessProbe(adapterId) {
69
- switch (adapterId) {
70
- case "codex":
71
- return (pane) => {
72
- const content = pane.content.replace(/\r/g, "");
73
- return livePane(pane)
74
- && !/Press enter to continue|select.*update|update selector/i.test(content)
75
- && content.includes("OpenAI Codex")
76
- && content.includes("/model to change")
77
- && /(?:^|\n)\s*›\s*(?:$|\S)/u.test(content);
78
- };
79
- case "claude":
80
- return (pane) => livePane(pane)
81
- && /(?:^|\n)\s*❯\s*(?:$|\S)/u.test(pane.content.replace(/\r/g, ""));
82
- default:
83
- throw new Error(`No tmux readiness probe is registered for Agent adapter: ${adapterId}.`);
278
+ export function agentProcessReadinessProbe(adapterId, _surface = "role") {
279
+ if (adapterId !== "codex" && adapterId !== "claude") {
280
+ throw new Error(`No tmux readiness probe is registered for Agent adapter: ${adapterId}.`);
84
281
  }
282
+ // Run state and receipt fences decide whether delivery is allowed. Provider
283
+ // terminal contents are display-only evidence and never lifecycle input.
284
+ return livePane;
85
285
  }
86
286
  function livePane(pane) {
87
287
  return !pane.dead && pane.pid !== undefined && pane.currentCommand.trim().length > 0;
@@ -92,7 +292,9 @@ function preparedDeliveryId(input) {
92
292
  input.roleName,
93
293
  input.agentId,
94
294
  input.adapterId,
295
+ input.effective,
95
296
  input.mode,
297
+ input.runId ?? null,
96
298
  input.nativeSessionId ?? null
97
299
  ])).digest("hex");
98
300
  }