@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
@@ -0,0 +1,472 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync, realpathSync } from "node:fs";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { callController as defaultCallController } from "../core/controllerClient.js";
5
+ import { inspectStorageSchema } from "../storage/storageSchema.js";
6
+ import { openCompatibleFileTaskStore } from "../storage/compatibleTaskStore.js";
7
+ import { yuiVersionIdentity } from "../version.js";
8
+ import { hasRuntimeCleanupObligation, isRuntimeLaunchReservation, runtimeLifecycleTarget } from "./lifecycleReservation.js";
9
+ import { nativeSessionIdForLaunch } from "./preallocatedNativeSession.js";
10
+ import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
11
+ import { writeTextFileAtomically } from "../storage/durableFile.js";
12
+ export const EXACT_CONTROL_ARGUMENT = "--yui-control";
13
+ export const YUI_CONTROL_PLANE_DESCRIPTOR = "YUI_CONTROL_PLANE_DESCRIPTOR";
14
+ export const YUI_TASK_RUNTIME_DESCRIPTOR = "YUI_TASK_RUNTIME_DESCRIPTOR";
15
+ export function createExactControlPlaneDescriptor(input) {
16
+ const identity = validateVersionIdentity(input.identity ?? yuiVersionIdentity());
17
+ return Object.freeze({
18
+ schemaVersion: 1,
19
+ kind: "yui-control-plane",
20
+ executable: canonicalPath(input.executable),
21
+ cliEntry: canonicalPath(input.cliEntry),
22
+ yuiHome: canonicalPath(input.yuiHome),
23
+ identity: Object.freeze({ ...identity })
24
+ });
25
+ }
26
+ export function createExactTaskRuntimeDescriptor(input) {
27
+ return Object.freeze({
28
+ schemaVersion: 1,
29
+ kind: "yui-task-runtime",
30
+ controlPlaneDigest: requireDigest(input.controlPlaneDigest),
31
+ taskId: requireIdentity(input.taskId, "Task id"),
32
+ roleName: requireIdentity(input.roleName, "Role name"),
33
+ agentId: requireIdentity(input.agentId, "Agent id"),
34
+ adapterId: requireAdapter(input.adapterId),
35
+ workspace: canonicalPath(input.workspace),
36
+ ...(input.runId === undefined ? {} : { runId: requireIdentity(input.runId, "Run id") }),
37
+ ...(input.launchId === undefined
38
+ ? {}
39
+ : { launchId: requireIdentity(input.launchId, "Launch id") }),
40
+ ...(input.nativeSessionId === undefined
41
+ ? {}
42
+ : { nativeSessionId: requireIdentity(input.nativeSessionId, "Native session id") })
43
+ });
44
+ }
45
+ export function serializeExactDescriptor(descriptor) {
46
+ return JSON.stringify(descriptor);
47
+ }
48
+ export function parseExactControlPlaneDescriptor(value) {
49
+ const record = parseDescriptorRecord(value);
50
+ assertDescriptorKind(record, "yui-control-plane");
51
+ if (record.schemaVersion !== 1) {
52
+ throw new Error("Exact control-plane descriptor schema version is invalid.");
53
+ }
54
+ return createExactControlPlaneDescriptor({
55
+ executable: requireText(record.executable, "Control-plane executable"),
56
+ cliEntry: requireText(record.cliEntry, "Control-plane CLI entry"),
57
+ yuiHome: requireText(record.yuiHome, "Control-plane YUI_HOME"),
58
+ identity: validateVersionIdentity(record.identity)
59
+ });
60
+ }
61
+ export function parseExactTaskRuntimeDescriptor(value) {
62
+ const record = parseDescriptorRecord(value);
63
+ assertDescriptorKind(record, "yui-task-runtime");
64
+ if (record.schemaVersion !== 1) {
65
+ throw new Error("Exact Task runtime descriptor schema version is invalid.");
66
+ }
67
+ return createExactTaskRuntimeDescriptor({
68
+ controlPlaneDigest: requireText(record.controlPlaneDigest, "Control-plane digest"),
69
+ taskId: requireText(record.taskId, "Task id"),
70
+ roleName: requireText(record.roleName, "Role name"),
71
+ agentId: requireText(record.agentId, "Agent id"),
72
+ adapterId: requireAdapter(record.adapterId),
73
+ workspace: requireText(record.workspace, "Task runtime workspace"),
74
+ ...(record.runId === undefined ? {} : { runId: requireText(record.runId, "Run id") }),
75
+ ...(record.launchId === undefined
76
+ ? {}
77
+ : { launchId: requireText(record.launchId, "Launch id") }),
78
+ ...(record.nativeSessionId === undefined
79
+ ? {}
80
+ : { nativeSessionId: requireText(record.nativeSessionId, "Native session id") })
81
+ });
82
+ }
83
+ export function exactControlPlaneDigest(descriptor) {
84
+ return createHash("sha256").update(serializeExactDescriptor(descriptor)).digest("hex");
85
+ }
86
+ /** Stable per Task/Role/Agent location; volatile Run/launch/native fields are content. */
87
+ export function exactTaskRuntimeDescriptorPath(home, descriptor) {
88
+ const validated = createExactTaskRuntimeDescriptor(descriptor);
89
+ const digest = createHash("sha256").update(JSON.stringify([
90
+ validated.controlPlaneDigest,
91
+ validated.taskId,
92
+ validated.roleName,
93
+ validated.agentId,
94
+ validated.adapterId,
95
+ validated.workspace
96
+ ])).digest("hex");
97
+ return join(canonicalPath(home), "runtime", "exact-task-runtime", `${digest}.json`);
98
+ }
99
+ export function readExactTaskRuntimeDescriptorSource(source, home) {
100
+ return readTaskRuntimeSource(source, home).descriptor;
101
+ }
102
+ export function exactControlPlaneCommandPrefix(descriptor) {
103
+ return [
104
+ descriptor.executable,
105
+ descriptor.cliEntry,
106
+ EXACT_CONTROL_ARGUMENT,
107
+ exactControlPlaneDigest(descriptor)
108
+ ].map(shellQuote).join(" ");
109
+ }
110
+ export function extractExactControlArgument(args) {
111
+ const later = args.indexOf(EXACT_CONTROL_ARGUMENT);
112
+ if (later < 0)
113
+ return { args: [...args] };
114
+ if (later !== 0) {
115
+ return {
116
+ args: [...args],
117
+ error: `${EXACT_CONTROL_ARGUMENT} must be the first CLI argument.`
118
+ };
119
+ }
120
+ const digest = args[1];
121
+ if (digest === undefined || !/^[a-f0-9]{64}$/u.test(digest)) {
122
+ return {
123
+ args: args.slice(2),
124
+ error: "Exact control-plane digest is invalid."
125
+ };
126
+ }
127
+ return { digest, args: args.slice(2) };
128
+ }
129
+ /**
130
+ * One read-only gate shared by every managed Task control command. It verifies
131
+ * the frozen executable/CLI/Home/digest, protocol and scalar-storage identity,
132
+ * on-disk schema, and any live Controller before command routing may construct
133
+ * a writable store. Package version alone may advance at the same managed path
134
+ * so an existing Session can cross an explicitly compatible in-place update.
135
+ */
136
+ export async function assertExactControlPlanePreflight(input, options = {}) {
137
+ const descriptor = parseExactControlPlaneDescriptor(input.serializedDescriptor);
138
+ if (exactControlPlaneDigest(descriptor) !== requireDigest(input.digest)) {
139
+ throw new Error("Exact control-plane digest does not match its frozen descriptor.");
140
+ }
141
+ assertSamePath(descriptor.executable, input.actualExecutable, "Control-plane executable");
142
+ assertSamePath(descriptor.cliEntry, input.actualCliEntry, "Control-plane CLI entry");
143
+ assertSamePath(descriptor.yuiHome, input.actualHome, "Control-plane YUI_HOME");
144
+ const localIdentity = validateVersionIdentity(options.identity ?? yuiVersionIdentity());
145
+ assertContinuityIdentity("Local CLI", descriptor.identity, localIdentity);
146
+ const storage = (options.inspectStorage ?? inspectStorageSchema)(descriptor.yuiHome);
147
+ if (storage.status !== "current") {
148
+ const exactRecordOnlyOlder = storage.status === "unsupported"
149
+ && storage.incompatibleComponent === "record"
150
+ && storage.direction === "older"
151
+ && storage.currentLayoutVersion === descriptor.identity.storageLayoutVersion
152
+ && storage.currentAggregateSchemaVersion === descriptor.identity.aggregateSchemaVersion;
153
+ if (!exactRecordOnlyOlder) {
154
+ throw new Error(`Exact control-plane storage is not current: ${storage.status}.`);
155
+ }
156
+ (options.openCompatibleStore ?? openCompatibleFileTaskStore)(descriptor.yuiHome).getConfig();
157
+ }
158
+ if (storage.currentLayoutVersion !== descriptor.identity.storageLayoutVersion) {
159
+ throw new Error("Exact control-plane storage layout does not match its frozen descriptor "
160
+ + `(expected ${descriptor.identity.storageLayoutVersion}, found `
161
+ + `${storage.currentLayoutVersion ?? "unknown"}).`);
162
+ }
163
+ if (storage.currentAggregateSchemaVersion !== descriptor.identity.aggregateSchemaVersion) {
164
+ throw new Error("Exact control-plane aggregate schema does not match its frozen descriptor "
165
+ + `(expected ${descriptor.identity.aggregateSchemaVersion}, found `
166
+ + `${storage.currentAggregateSchemaVersion ?? "unknown"}).`);
167
+ }
168
+ if (options.checkController !== false) {
169
+ const call = options.callController ?? defaultCallController;
170
+ try {
171
+ const status = await call(descriptor.yuiHome, "controller.status", {});
172
+ assertControllerContinuityIdentity(status, descriptor.identity);
173
+ }
174
+ catch (error) {
175
+ if (!isDefinitelyNotRunning(error))
176
+ throw error;
177
+ }
178
+ }
179
+ return descriptor;
180
+ }
181
+ export function assertControllerStatusIdentity(status, expected = yuiVersionIdentity()) {
182
+ if (!isRecord(status) || status.running !== true) {
183
+ throw new Error("Controller status does not describe a running Controller.");
184
+ }
185
+ assertControllerField(status.protocolVersion, expected.controllerProtocolVersion, "protocol");
186
+ assertControllerField(status.version, expected.version, "version");
187
+ assertControllerField(status.storageLayoutVersion, expected.storageLayoutVersion, "storage layout");
188
+ assertControllerField(status.aggregateSchemaVersion, expected.aggregateSchemaVersion, "aggregate schema");
189
+ }
190
+ export function assertExactTaskRuntimeEnvironment(runtimeSource, environment, expectedControlDigest, expectedHome = requireText(environment.YUI_HOME, "YUI_HOME")) {
191
+ const resolved = readTaskRuntimeSource(runtimeSource, expectedHome);
192
+ const runtime = resolved.descriptor;
193
+ if (runtime.controlPlaneDigest !== requireDigest(expectedControlDigest)) {
194
+ throw new Error("Task runtime descriptor belongs to another exact control plane.");
195
+ }
196
+ assertEnvironment(runtime.taskId, environment.YUI_TASK_ID, "Task id");
197
+ assertEnvironment(runtime.roleName, environment.YUI_ROLE, "Role name");
198
+ assertEnvironment(runtime.agentId, environment.YUI_AGENT_ID, "Agent id");
199
+ assertEnvironment(runtime.adapterId, environment.YUI_ADAPTER_ID, "Agent adapter id");
200
+ assertSamePath(runtime.workspace, requireText(environment.YUI_WORKSPACE, "YUI_WORKSPACE"), "Task runtime workspace");
201
+ // A reused native pane retains its original process environment. Volatile
202
+ // identity therefore comes only from the atomically published descriptor
203
+ // plus durable state, never from stale ambient Run/launch/native variables.
204
+ if (!resolved.fromFile) {
205
+ assertOptionalEnvironment(runtime.runId, environment.YUI_RUN_ID, "Run id");
206
+ assertOptionalEnvironment(runtime.launchId, environment.YUI_LAUNCH_ID, "Launch id");
207
+ assertOptionalEnvironment(runtime.nativeSessionId, environment.YUI_NATIVE_SESSION_ID, "Native session id");
208
+ }
209
+ return runtime;
210
+ }
211
+ /** Fences a descriptor to the one currently active durable Task runtime. */
212
+ export function assertExactTaskRuntimeState(runtime, store, options = {}) {
213
+ const task = store.getTask(runtime.taskId);
214
+ if (task === null || task.status !== "active") {
215
+ throw new Error("Exact Task runtime Task is not current and active.");
216
+ }
217
+ const role = store.getRole(runtime.taskId, runtime.roleName);
218
+ if (role === null || role.activeAgentId !== runtime.agentId) {
219
+ throw new Error("Exact Task runtime Role or Agent is not current.");
220
+ }
221
+ const run = store.getActiveAgentRun(runtime.taskId, runtime.roleName);
222
+ if (runtime.runId === undefined) {
223
+ if (run !== null)
224
+ throw new Error("Exact Task runtime Run is not current.");
225
+ }
226
+ else if (run === null
227
+ || run.id !== runtime.runId
228
+ || run.status !== "active"
229
+ || run.effective.agentId !== runtime.agentId
230
+ || run.effective.adapterId !== runtime.adapterId
231
+ || canonicalPath(run.effective.workspace.root) !== runtime.workspace) {
232
+ throw new Error("Exact Task runtime Run is not current.");
233
+ }
234
+ const sessions = store.getTaskRoleSessionSet(runtime.taskId, runtime.roleName);
235
+ const session = sessions?.sessions[runtime.agentId];
236
+ if (sessions !== null && sessions.activeAgentId !== runtime.agentId) {
237
+ throw new Error("Exact Task runtime Session Agent is not current.");
238
+ }
239
+ const lifecycleMailbox = store.getWorkMailbox(runtimeLifecycleTarget({
240
+ scope: "task",
241
+ taskId: runtime.taskId,
242
+ roleName: runtime.roleName
243
+ }));
244
+ const reservation = runtime.launchId !== undefined
245
+ && isRuntimeLaunchReservation(lifecycleMailbox?.processing, runtime.launchId);
246
+ const sessionLaunch = runtime.launchId !== undefined
247
+ && session?.launchId === runtime.launchId;
248
+ const executionRef = lifecycleMailbox?.processing?.executionRef;
249
+ const preallocated = options.preallocatedNativeSessionReservation;
250
+ const exactPreallocatedReservation = preallocated !== undefined
251
+ && runtime.adapterId === "claude"
252
+ && runtime.runId !== undefined
253
+ && runtime.launchId !== undefined
254
+ && runtime.nativeSessionId !== undefined
255
+ && session === undefined
256
+ && reservation
257
+ && !hasRuntimeCleanupObligation(lifecycleMailbox)
258
+ && executionRef?.type === "run"
259
+ && executionRef.taskId === runtime.taskId
260
+ && executionRef.id === runtime.runId
261
+ && runtime.nativeSessionId === nativeSessionIdForLaunch(preallocated.yuiHome, runtime.launchId, runtime.agentId, runtime.adapterId);
262
+ const preallocatedBeforeInFlightProjection = exactPreallocatedReservation
263
+ && (sessions?.inFlight === null || sessions?.inFlight === undefined);
264
+ if (runtime.runId !== undefined && (sessions?.inFlight?.agentId !== runtime.agentId
265
+ || sessions.inFlight.runId !== runtime.runId
266
+ || sessions.inFlight.receiptId !== formatAgentRunReceiptId(runtime.taskId, runtime.runId)) && !preallocatedBeforeInFlightProjection) {
267
+ throw new Error("Exact Task runtime in-flight Run fence is not current.");
268
+ }
269
+ if (runtime.launchId === undefined || (!reservation && !sessionLaunch)) {
270
+ throw new Error("Exact Task runtime launch fence is not current.");
271
+ }
272
+ if (runtime.nativeSessionId === undefined) {
273
+ if (session !== undefined) {
274
+ throw new Error("Exact Task runtime native Session fence is missing.");
275
+ }
276
+ }
277
+ else if (session === undefined) {
278
+ if (!exactPreallocatedReservation) {
279
+ throw new Error("Exact Task runtime native Session fence is not current.");
280
+ }
281
+ }
282
+ else if (session.agentId !== runtime.agentId
283
+ || session.adapterId !== runtime.adapterId
284
+ || session.nativeSessionId !== runtime.nativeSessionId
285
+ || session.launchId !== runtime.launchId
286
+ || session.status === "stopped"
287
+ || session.status === "broken"
288
+ || session.effective.agentId !== runtime.agentId
289
+ || session.effective.adapterId !== runtime.adapterId
290
+ || canonicalPath(session.effective.workspace.root) !== runtime.workspace) {
291
+ throw new Error("Exact Task runtime native Session fence is not current.");
292
+ }
293
+ }
294
+ /** Publishes provider-discovered native identity without changing the stable source path. */
295
+ export function refreshExactTaskRuntimeDescriptorSource(source, home, store) {
296
+ const resolved = readTaskRuntimeSource(source, home);
297
+ if (!resolved.fromFile) {
298
+ throw new Error("A managed Task runtime descriptor must use its stable file source.");
299
+ }
300
+ const current = resolved.descriptor;
301
+ const session = store.getTaskRoleSessionSet(current.taskId, current.roleName)
302
+ ?.sessions[current.agentId];
303
+ if (session === undefined) {
304
+ throw new Error("Exact Task runtime native Session is not available for refresh.");
305
+ }
306
+ const refreshed = createExactTaskRuntimeDescriptor({
307
+ ...current,
308
+ nativeSessionId: session.nativeSessionId
309
+ });
310
+ if (exactTaskRuntimeDescriptorPath(home, refreshed) !== resolve(source)) {
311
+ throw new Error("Refreshed Task runtime descriptor changed its stable identity.");
312
+ }
313
+ assertExactTaskRuntimeState(refreshed, store);
314
+ writeTextFileAtomically(resolve(source), `${serializeExactDescriptor(refreshed)}\n`);
315
+ return refreshed;
316
+ }
317
+ function readTaskRuntimeSource(source, home) {
318
+ const value = requireText(source, "Exact Task runtime descriptor source");
319
+ if (value.trimStart().startsWith("{")) {
320
+ return { descriptor: parseExactTaskRuntimeDescriptor(value), fromFile: false };
321
+ }
322
+ const requested = resolve(value);
323
+ const root = resolve(canonicalPath(home), "runtime", "exact-task-runtime");
324
+ if (dirname(requested) !== root) {
325
+ throw new Error("Exact Task runtime descriptor path is outside its control plane.");
326
+ }
327
+ let actual;
328
+ try {
329
+ actual = realpathSync(requested);
330
+ }
331
+ catch (error) {
332
+ throw new Error("Exact Task runtime descriptor file is unavailable.", { cause: error });
333
+ }
334
+ if (actual !== requested) {
335
+ throw new Error("Exact Task runtime descriptor file must not be a symbolic link.");
336
+ }
337
+ const descriptor = parseExactTaskRuntimeDescriptor(readFileSync(actual, "utf8"));
338
+ if (exactTaskRuntimeDescriptorPath(home, descriptor) !== requested) {
339
+ throw new Error("Exact Task runtime descriptor path does not match its stable identity.");
340
+ }
341
+ return { descriptor, fromFile: true };
342
+ }
343
+ function validateVersionIdentity(value) {
344
+ if (!isRecord(value))
345
+ throw new Error("Yui version identity is invalid.");
346
+ const version = requireText(value.version, "Yui version");
347
+ const controllerProtocolVersion = requireVersion(value.controllerProtocolVersion, "Controller protocol version");
348
+ const storageLayoutVersion = requireVersion(value.storageLayoutVersion, "Storage layout version");
349
+ const aggregateSchemaVersion = requireVersion(value.aggregateSchemaVersion, "Aggregate schema version");
350
+ return {
351
+ version,
352
+ controllerProtocolVersion,
353
+ storageLayoutVersion,
354
+ aggregateSchemaVersion
355
+ };
356
+ }
357
+ /** Managed continuity is a protocol/storage contract, not a package pin. */
358
+ function assertContinuityIdentity(label, expected, actual) {
359
+ for (const field of [
360
+ "controllerProtocolVersion",
361
+ "storageLayoutVersion",
362
+ "aggregateSchemaVersion"
363
+ ]) {
364
+ if (expected[field] !== actual[field]) {
365
+ throw new Error(`${label} ${field} does not match the frozen control plane `
366
+ + `(expected ${expected[field]}, found ${actual[field]}).`);
367
+ }
368
+ }
369
+ }
370
+ function assertControllerContinuityIdentity(status, expected) {
371
+ if (!isRecord(status) || status.running !== true) {
372
+ throw new Error("Controller status does not describe a running Controller.");
373
+ }
374
+ if (typeof status.version !== "string" || status.version.trim().length === 0) {
375
+ throw new Error("Controller version is invalid at the managed continuity gate.");
376
+ }
377
+ assertControllerField(status.protocolVersion, expected.controllerProtocolVersion, "protocol");
378
+ assertControllerField(status.storageLayoutVersion, expected.storageLayoutVersion, "storage layout");
379
+ assertControllerField(status.aggregateSchemaVersion, expected.aggregateSchemaVersion, "aggregate schema");
380
+ }
381
+ function assertControllerField(actual, expected, label) {
382
+ if (actual !== expected) {
383
+ throw new Error(`Controller ${label} is incompatible with the exact control plane `
384
+ + `(expected ${expected}, found ${typeof actual === "string" || typeof actual === "number" ? actual : "unknown"}). `
385
+ + "Run controller restart through the matching exact control-plane invocation "
386
+ + "before writing new Task records.");
387
+ }
388
+ }
389
+ function assertSamePath(expected, actual, label) {
390
+ const normalized = canonicalPath(actual);
391
+ if (expected !== normalized) {
392
+ throw new Error(`${label} does not match the frozen control plane (expected ${expected}, found ${normalized}).`);
393
+ }
394
+ }
395
+ function assertEnvironment(expected, actual, label) {
396
+ if (actual !== expected) {
397
+ throw new Error(`${label} does not match the exact Task runtime descriptor.`);
398
+ }
399
+ }
400
+ function assertOptionalEnvironment(expected, actual, label) {
401
+ if (actual !== expected) {
402
+ throw new Error(`${label} does not match the exact Task runtime descriptor.`);
403
+ }
404
+ }
405
+ function parseDescriptorRecord(value) {
406
+ let parsed;
407
+ try {
408
+ parsed = JSON.parse(requireText(value, "Exact descriptor"));
409
+ }
410
+ catch (error) {
411
+ throw new Error("Exact descriptor is not valid JSON.", { cause: error });
412
+ }
413
+ if (!isRecord(parsed))
414
+ throw new Error("Exact descriptor must be an object.");
415
+ return parsed;
416
+ }
417
+ function assertDescriptorKind(value, expected) {
418
+ if (value.kind !== expected) {
419
+ throw new Error(`Exact descriptor kind is invalid: expected ${expected}, found `
420
+ + `${typeof value.kind === "string" ? value.kind : "unknown"}.`);
421
+ }
422
+ }
423
+ function requireAdapter(value) {
424
+ if (value !== "codex" && value !== "claude") {
425
+ throw new Error("Agent adapter id is invalid.");
426
+ }
427
+ return value;
428
+ }
429
+ function requireIdentity(value, label) {
430
+ const text = requireText(value, label).trim();
431
+ if (text.length === 0 || text.length > 1_024 || text.includes("\0")) {
432
+ throw new Error(`${label} is invalid.`);
433
+ }
434
+ return text;
435
+ }
436
+ function requireText(value, label) {
437
+ if (typeof value !== "string" || value.length === 0 || value.includes("\0")) {
438
+ throw new Error(`${label} is invalid.`);
439
+ }
440
+ return value;
441
+ }
442
+ function requireDigest(value) {
443
+ const digest = requireText(value, "Control-plane digest");
444
+ if (!/^[a-f0-9]{64}$/u.test(digest)) {
445
+ throw new Error("Control-plane digest is invalid.");
446
+ }
447
+ return digest;
448
+ }
449
+ function requireVersion(value, label) {
450
+ if (!Number.isSafeInteger(value) || value < 1) {
451
+ throw new Error(`${label} is invalid.`);
452
+ }
453
+ return value;
454
+ }
455
+ function canonicalPath(value) {
456
+ const absolute = resolve(requireText(value, "Path"));
457
+ try {
458
+ return realpathSync(absolute);
459
+ }
460
+ catch {
461
+ return absolute;
462
+ }
463
+ }
464
+ function shellQuote(value) {
465
+ return `'${value.replaceAll("'", "'\"'\"'")}'`;
466
+ }
467
+ function isRecord(value) {
468
+ return typeof value === "object" && value !== null && !Array.isArray(value);
469
+ }
470
+ function isDefinitelyNotRunning(error) {
471
+ return isRecord(error) && error.code === "CONTROLLER_NOT_RUNNING";
472
+ }
@@ -0,0 +1,8 @@
1
+ export { createPromptEnvelope } from "./promptEnvelope.js";
2
+ export { createRuntimeBinding } from "./runtimeBinding.js";
3
+ export { normalizeRuntimeOwner } from "./runtimeOwner.js";
4
+ export { createSessionLaunchRequest } from "./sessionLaunchRequest.js";
5
+ export { createPendingTurnCompletion, DEFAULT_RECENT_TURN_ID_LIMIT, hasRecentTurnId, rememberRecentTurnId, validatePendingTurnCompletion, validateRecentTurnIds } from "./turnCompletion.js";
6
+ export { RuntimeLaunchError } from "./ports.js";
7
+ export { TmuxPromptPushAdapter, TmuxSessionHost } from "./tmuxAdapters.js";
8
+ export { FileTaskRuntimeIsolation, YUI_TASK_RUNTIME_ISOLATION_DESCRIPTOR, YUI_TASK_RUNTIME_SERVICE_NAMESPACE, assertTaskRuntimeIsolationPreflight, createTaskRuntimeIsolationDescriptor, parseTaskRuntimeIsolationDescriptor, planTaskRuntimeCleanup, taskRuntimeIsolationEnvironment, taskRuntimeIsolationFingerprint } from "./taskRuntimeIsolation.js";
@@ -0,0 +1,38 @@
1
+ export const RUNTIME_LIFECYCLE_OWNER = "runtime-lifecycle";
2
+ export const RUNTIME_LAUNCH_RESERVED_REASON = "runtime-launch-reserved";
3
+ export const RUNTIME_CLEANUP_REQUIRED_REASON = "runtime-cleanup-required";
4
+ export function runtimeLifecycleTarget(owner) {
5
+ return owner.scope === "task"
6
+ ? {
7
+ kind: "role-runtime",
8
+ taskId: owner.taskId,
9
+ roleName: owner.roleName
10
+ }
11
+ : {
12
+ kind: "global-role-runtime",
13
+ roleName: owner.roleName
14
+ };
15
+ }
16
+ export function runtimeLifecycleSignalKey(owner) {
17
+ return owner.scope === "task"
18
+ ? `role:${encodeURIComponent(owner.taskId)}/${encodeURIComponent(owner.roleName)}`
19
+ : `global-role:${encodeURIComponent(owner.roleName)}`;
20
+ }
21
+ export function isRuntimeLaunchReservation(processing, launchId) {
22
+ return processing?.owner === RUNTIME_LIFECYCLE_OWNER
23
+ && processing.batch.reasons.length === 1
24
+ && processing.batch.reasons[0] === RUNTIME_LAUNCH_RESERVED_REASON
25
+ && (launchId === undefined || processing.batchId === launchId);
26
+ }
27
+ export function hasRuntimeLaunchReservation(mailbox) {
28
+ return isRuntimeLaunchReservation(mailbox?.processing);
29
+ }
30
+ export function hasRuntimeCleanupObligation(mailbox) {
31
+ return mailbox?.pending?.reasons.includes(RUNTIME_CLEANUP_REQUIRED_REASON) === true
32
+ || (!isRuntimeLaunchReservation(mailbox?.processing)
33
+ && mailbox?.processing?.batch.reasons.includes(RUNTIME_CLEANUP_REQUIRED_REASON) === true);
34
+ }
35
+ export function hasRuntimeLifecycleWork(mailbox) {
36
+ return mailbox !== null
37
+ && (mailbox.processing !== null || mailbox.pending !== null);
38
+ }
@@ -0,0 +1,11 @@
1
+ /** A persisted launch is either temporarily unavailable or permanently lost. */
2
+ export class RuntimeLaunchError extends Error {
3
+ retryable;
4
+ launchId;
5
+ name = "RuntimeLaunchError";
6
+ constructor(retryable, launchId, message) {
7
+ super(message);
8
+ this.retryable = retryable;
9
+ this.launchId = launchId;
10
+ }
11
+ }
@@ -0,0 +1,13 @@
1
+ import { createHash } from "node:crypto";
2
+ import { resolve } from "node:path";
3
+ import { requireText } from "./validation.js";
4
+ /** Deterministic native identity preallocated for one exact provider launch. */
5
+ export function nativeSessionIdForLaunch(home, launchId, agentId, adapterId) {
6
+ const hex = createHash("sha256").update(JSON.stringify([
7
+ resolve(requireText(home, "YUI_HOME")),
8
+ requireText(launchId, "Launch id"),
9
+ requireText(agentId, "Agent id"),
10
+ requireText(adapterId, "Agent adapter id")
11
+ ])).digest("hex");
12
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-a${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
13
+ }
@@ -0,0 +1,30 @@
1
+ import { requireText, requireTimestamp } from "./validation.js";
2
+ import { formatAgentRunReceiptId, formatInputRequestReceiptId, validateTaskRecordReference } from "../task/taskRecordReference.js";
3
+ export function createPromptEnvelope(input) {
4
+ if (input.source.kind !== "agent-run" && input.source.kind !== "input-request") {
5
+ throw new Error("Prompt source kind is invalid.");
6
+ }
7
+ const source = validateTaskRecordReference({
8
+ taskId: input.source.taskId,
9
+ localId: input.source.localId
10
+ }, input.source.kind === "agent-run" ? "agentRun" : "inputRequest");
11
+ const id = requireQualifiedReceiptId(input.id, input.source.kind, source.taskId, source.localId);
12
+ return {
13
+ id,
14
+ source: {
15
+ kind: input.source.kind,
16
+ taskId: source.taskId,
17
+ localId: source.localId
18
+ },
19
+ text: requireText(input.text, "Prompt text"),
20
+ createdAt: requireTimestamp(input.createdAt, "Prompt createdAt")
21
+ };
22
+ }
23
+ function requireQualifiedReceiptId(value, kind, taskId, localId) {
24
+ const expected = kind === "agent-run"
25
+ ? formatAgentRunReceiptId(taskId, localId)
26
+ : formatInputRequestReceiptId(taskId, localId);
27
+ if (value !== expected)
28
+ throw new Error("Prompt envelope id does not match its source.");
29
+ return expected;
30
+ }
@@ -0,0 +1,31 @@
1
+ import { normalizeRuntimeOwner } from "./runtimeOwner.js";
2
+ import { requireSafeIdentity, requireText } from "./validation.js";
3
+ export function createRuntimeBinding(input) {
4
+ const hostCreated = input.hostCreated === undefined
5
+ ? undefined
6
+ : requireBoolean(input.hostCreated, "Runtime host-created flag");
7
+ const initialPromptRunId = input.initialPromptRunId === undefined
8
+ ? undefined
9
+ : requireSafeIdentity(input.initialPromptRunId, "Initial prompt Run id");
10
+ if (initialPromptRunId !== undefined && hostCreated !== true) {
11
+ throw new TypeError("An initial prompt Run id requires a newly-created runtime host.");
12
+ }
13
+ return {
14
+ id: requireSafeIdentity(input.id, "Runtime binding id"),
15
+ launchId: requireSafeIdentity(input.launchId, "Launch id"),
16
+ owner: normalizeRuntimeOwner(input.owner),
17
+ agentId: requireSafeIdentity(input.agentId, "Agent id"),
18
+ adapterId: requireSafeIdentity(input.adapterId, "Agent adapter id"),
19
+ hostRef: requireText(input.hostRef, "Session host reference"),
20
+ ...(hostCreated === undefined ? {} : { hostCreated }),
21
+ ...(initialPromptRunId === undefined ? {} : { initialPromptRunId }),
22
+ ...(input.nativeSessionId === undefined
23
+ ? {}
24
+ : { nativeSessionId: requireText(input.nativeSessionId, "Native session id") })
25
+ };
26
+ }
27
+ function requireBoolean(value, label) {
28
+ if (typeof value !== "boolean")
29
+ throw new TypeError(`${label} must be boolean.`);
30
+ return value;
31
+ }
@@ -0,0 +1,14 @@
1
+ import { requireSafeIdentity } from "./validation.js";
2
+ export function normalizeRuntimeOwner(owner) {
3
+ const roleName = requireSafeIdentity(owner.roleName, "Role name");
4
+ if (owner.scope === "global")
5
+ return { scope: "global", roleName };
6
+ if (owner.scope === "task") {
7
+ return {
8
+ scope: "task",
9
+ taskId: requireSafeIdentity(owner.taskId, "Task id"),
10
+ roleName
11
+ };
12
+ }
13
+ throw new Error("Runtime owner scope is invalid.");
14
+ }