@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,564 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { closeSync, constants, existsSync, fchmodSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { withUpgradeCoordinationLock } from "../storage/upgradeCoordination.js";
5
+ export const MAX_RUNTIME_TURN_SUMMARY_BYTES = 32 * 1024;
6
+ export const MAX_CLAUDE_HOOK_TEXT_BYTES = 4 * 1024 * 1024;
7
+ export const MAX_RUNTIME_EVENT_FILE_BYTES = 16 * 1024 * 1024;
8
+ const RUNTIME_EVENT_DIRECTORY = join("runtime", "inbox");
9
+ const INVALID_RUNTIME_EVENT_DIRECTORY = join("runtime", "inbox-invalid");
10
+ const EVENT_ID_PATTERN = /^turn-[a-f0-9]{64}$/;
11
+ /**
12
+ * Durable ingress for facts emitted by native Agent hooks. It deliberately
13
+ * owns no FileTaskStore lock: immutable files are acknowledged only after the
14
+ * Controller commits their authoritative aggregate effect.
15
+ */
16
+ export class FileRuntimeEventInbox {
17
+ home;
18
+ now;
19
+ hooks;
20
+ directory;
21
+ constructor(home, now = () => new Date(), hooks = {}) {
22
+ this.home = home;
23
+ this.now = now;
24
+ this.hooks = hooks;
25
+ this.directory = join(home, RUNTIME_EVENT_DIRECTORY);
26
+ }
27
+ enqueueTurnCompleted(input) {
28
+ const normalized = normalizeCodexInput(input);
29
+ return this.publish(Object.freeze({
30
+ schemaVersion: 1,
31
+ id: runtimeEventId("native-turn-completed", normalized),
32
+ type: "native-turn-completed",
33
+ receivedAt: this.now().toISOString(),
34
+ ...normalized
35
+ }));
36
+ }
37
+ enqueueClaudeStopFailure(input) {
38
+ const normalized = normalizeClaudeStopFailureInput(input);
39
+ return this.publish(Object.freeze({
40
+ schemaVersion: 1,
41
+ id: runtimeEventId("claude-stop-failure", normalized),
42
+ type: "claude-stop-failure",
43
+ receivedAt: this.now().toISOString(),
44
+ ...normalized
45
+ }));
46
+ }
47
+ enqueueSessionLifecycle(input) {
48
+ const normalized = normalizeSessionLifecycleInput(input);
49
+ return this.publish(Object.freeze({
50
+ schemaVersion: 1,
51
+ id: runtimeEventId("native-session-lifecycle", normalized),
52
+ type: "native-session-lifecycle",
53
+ receivedAt: this.now().toISOString(),
54
+ ...normalized
55
+ }));
56
+ }
57
+ enqueuePromptAccepted(input) {
58
+ const normalized = normalizePromptAcceptedInput(input);
59
+ return this.publish(Object.freeze({
60
+ schemaVersion: 1,
61
+ id: runtimeEventId("native-prompt-accepted", normalized),
62
+ type: "native-prompt-accepted",
63
+ receivedAt: this.now().toISOString(),
64
+ ...normalized
65
+ }));
66
+ }
67
+ enqueueProviderProgress(input) {
68
+ const normalized = normalizeProviderProgressInput(input);
69
+ return this.publish(Object.freeze({
70
+ schemaVersion: 1,
71
+ id: runtimeEventId("native-turn-progress", normalized),
72
+ type: "native-turn-progress",
73
+ receivedAt: this.now().toISOString(),
74
+ ...normalized
75
+ }));
76
+ }
77
+ list() {
78
+ if (!existsSync(this.directory))
79
+ return [];
80
+ const events = [];
81
+ for (const name of readdirSync(this.directory).filter((entry) => entry.endsWith(".json"))) {
82
+ try {
83
+ const id = name.slice(0, -".json".length);
84
+ assertEventId(id);
85
+ const event = this.read(id);
86
+ if (event !== null)
87
+ events.push(event);
88
+ }
89
+ catch (error) {
90
+ if (!(error instanceof RuntimeEventInboxError)
91
+ || error.code !== "RUNTIME_EVENT_INVALID") {
92
+ throw error;
93
+ }
94
+ try {
95
+ this.quarantine(name);
96
+ }
97
+ catch {
98
+ // One unreadable entry must not block independent durable events.
99
+ }
100
+ }
101
+ }
102
+ return events.sort((left, right) => (left.receivedAt.localeCompare(right.receivedAt)
103
+ || left.id.localeCompare(right.id)));
104
+ }
105
+ read(id) {
106
+ assertEventId(id);
107
+ const path = this.eventPath(id);
108
+ let metadata;
109
+ try {
110
+ metadata = lstatSync(path);
111
+ }
112
+ catch (error) {
113
+ if (isNodeError(error, "ENOENT"))
114
+ return null;
115
+ throw error;
116
+ }
117
+ if (!metadata.isFile()
118
+ || (metadata.mode & 0o777) !== 0o600
119
+ || metadata.size > MAX_RUNTIME_EVENT_FILE_BYTES) {
120
+ throw invalidEvent(`Runtime event file is invalid: ${id}`);
121
+ }
122
+ let value;
123
+ try {
124
+ value = JSON.parse(readFileSync(path, "utf8"));
125
+ }
126
+ catch {
127
+ throw invalidEvent(`Runtime event JSON is invalid: ${id}`);
128
+ }
129
+ const event = parseRuntimeEvent(value);
130
+ if (event.id !== id || runtimeEventId(event.type, event) !== id) {
131
+ throw invalidEvent(`Runtime event identity is invalid: ${id}`);
132
+ }
133
+ return event;
134
+ }
135
+ acknowledge(id) {
136
+ assertEventId(id);
137
+ try {
138
+ unlinkSync(this.eventPath(id));
139
+ fsyncDirectory(this.directory);
140
+ return true;
141
+ }
142
+ catch (error) {
143
+ if (isNodeError(error, "ENOENT"))
144
+ return false;
145
+ throw error;
146
+ }
147
+ }
148
+ publish(event) {
149
+ // The fence check and the complete durable write share one sibling
150
+ // coordination lock with upgrade's final inbox scan/copy/two-step switch.
151
+ // A hook that passed admission before the fence was placed either finishes
152
+ // under this lock (so its event is copied) or waits and receives an
153
+ // UpgradeFenceError after the cutover holder releases the lock.
154
+ return withUpgradeCoordinationLock(this.home, () => {
155
+ this.hooks.afterAdmission?.();
156
+ const content = `${JSON.stringify(event)}\n`;
157
+ if (Buffer.byteLength(content, "utf8") > MAX_RUNTIME_EVENT_FILE_BYTES) {
158
+ throw new RuntimeEventInboxError("RUNTIME_EVENT_TOO_LARGE", "Runtime event exceeds the durable inbox limit.");
159
+ }
160
+ ensureInboxDirectory(this.directory);
161
+ const target = this.eventPath(event.id);
162
+ const temporary = join(this.directory, `.${event.id}.tmp-${process.pid}-${randomUUID()}`);
163
+ let descriptor = null;
164
+ try {
165
+ descriptor = openSync(temporary, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 0o600);
166
+ writeFileSync(descriptor, content, "utf8");
167
+ fchmodSync(descriptor, 0o600);
168
+ fsyncSync(descriptor);
169
+ closeSync(descriptor);
170
+ descriptor = null;
171
+ try {
172
+ linkSync(temporary, target);
173
+ unlinkSync(temporary);
174
+ fsyncDirectory(this.directory);
175
+ return { event, created: true };
176
+ }
177
+ catch (error) {
178
+ if (!isNodeError(error, "EEXIST"))
179
+ throw error;
180
+ const existing = this.read(event.id);
181
+ if (existing === null)
182
+ return { event, created: false };
183
+ if (!hasSameIdentity(existing, event)) {
184
+ throw new RuntimeEventInboxError("RUNTIME_EVENT_CONFLICT", `Runtime event id conflicts with an existing file: ${event.id}`);
185
+ }
186
+ return { event: existing, created: false };
187
+ }
188
+ }
189
+ finally {
190
+ if (descriptor !== null)
191
+ closeSync(descriptor);
192
+ rmSync(temporary, { force: true });
193
+ }
194
+ });
195
+ }
196
+ eventPath(id) {
197
+ return join(this.directory, `${id}.json`);
198
+ }
199
+ quarantine(name) {
200
+ const invalidDirectory = join(this.home, INVALID_RUNTIME_EVENT_DIRECTORY);
201
+ ensureInboxDirectory(invalidDirectory);
202
+ const source = join(this.directory, name);
203
+ const target = join(invalidDirectory, `${name}.${randomUUID()}`);
204
+ try {
205
+ renameSync(source, target);
206
+ fsyncDirectory(this.directory);
207
+ fsyncDirectory(invalidDirectory);
208
+ }
209
+ catch (error) {
210
+ if (!isNodeError(error, "ENOENT"))
211
+ throw error;
212
+ }
213
+ }
214
+ }
215
+ export class RuntimeEventInboxError extends Error {
216
+ code;
217
+ constructor(code, message) {
218
+ super(message);
219
+ this.code = code;
220
+ this.name = "RuntimeEventInboxError";
221
+ }
222
+ }
223
+ function runtimeEventId(type, input) {
224
+ const common = [
225
+ 1,
226
+ type,
227
+ input.scope,
228
+ input.taskId ?? null,
229
+ input.roleName,
230
+ input.agentId,
231
+ input.adapterId,
232
+ input.launchId ?? null,
233
+ input.nativeSessionId,
234
+ "turnId" in input ? input.turnId : null,
235
+ input.runId ?? null,
236
+ "progressId" in input ? input.progressId : null,
237
+ "sequence" in input ? input.sequence ?? null : null,
238
+ "sessionSource" in input ? input.sessionSource ?? null : null,
239
+ "receiptId" in input ? input.receiptId ?? null : null
240
+ ];
241
+ return `turn-${createHash("sha256").update(JSON.stringify(common)).digest("hex")}`;
242
+ }
243
+ function normalizeCodexInput(input) {
244
+ const scope = input.scope;
245
+ if (scope !== "task" && scope !== "global")
246
+ throw invalidEvent();
247
+ if (typeof input.summary !== "string" || input.summary.includes("\0"))
248
+ throw invalidEvent();
249
+ const common = {
250
+ scope,
251
+ roleName: requireIdentityText(input.roleName, "Role name"),
252
+ agentId: requireIdentityText(input.agentId, "Agent id"),
253
+ adapterId: input.adapterId,
254
+ ...(input.launchId === undefined
255
+ ? {}
256
+ : { launchId: requireIdentityText(input.launchId, "Launch id") }),
257
+ nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
258
+ turnId: requireIdentityText(input.turnId, "Turn id"),
259
+ ...(input.runId === undefined
260
+ ? {}
261
+ : { runId: requireIdentityText(input.runId, "Run id") }),
262
+ ...(input.title === undefined
263
+ ? {}
264
+ : { title: requireIdentityText(input.title, "Session title") }),
265
+ summary: truncateUtf8(input.summary.trim(), MAX_RUNTIME_TURN_SUMMARY_BYTES)
266
+ };
267
+ if (common.adapterId !== "codex" || common.summary.length === 0)
268
+ throw invalidEvent();
269
+ return scope === "task"
270
+ ? { ...common, taskId: requireIdentityText(input.taskId, "Task id") }
271
+ : common;
272
+ }
273
+ function normalizeClaudeEnvelope(input) {
274
+ if (input.scope !== "task" || input.adapterId !== "claude")
275
+ throw invalidEvent();
276
+ return {
277
+ scope: "task",
278
+ taskId: requireIdentityText(input.taskId, "Task id"),
279
+ roleName: requireIdentityText(input.roleName, "Role name"),
280
+ agentId: requireIdentityText(input.agentId, "Agent id"),
281
+ adapterId: "claude",
282
+ launchId: requireIdentityText(input.launchId, "Launch id"),
283
+ nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
284
+ runId: requireIdentityText(input.runId, "Run id")
285
+ };
286
+ }
287
+ function normalizeClaudeStopFailureInput(input) {
288
+ return {
289
+ ...normalizeClaudeEnvelope(input),
290
+ error: requireLongText(input.error, "Claude failure error"),
291
+ ...(input.errorDetails === undefined
292
+ ? {}
293
+ : { errorDetails: requireLongText(input.errorDetails, "Claude failure details") }),
294
+ ...(input.lastAssistantMessage === undefined
295
+ ? {}
296
+ : {
297
+ lastAssistantMessage: requireLongText(input.lastAssistantMessage, "Claude failure last assistant message")
298
+ })
299
+ };
300
+ }
301
+ function normalizeSessionLifecycleInput(input) {
302
+ if (input.scope !== "task")
303
+ throw invalidEvent();
304
+ if (input.adapterId !== "codex" && input.adapterId !== "claude")
305
+ throw invalidEvent();
306
+ return {
307
+ scope: "task",
308
+ taskId: requireIdentityText(input.taskId, "Task id"),
309
+ roleName: requireIdentityText(input.roleName, "Role name"),
310
+ agentId: requireIdentityText(input.agentId, "Agent id"),
311
+ adapterId: input.adapterId,
312
+ launchId: requireIdentityText(input.launchId, "Launch id"),
313
+ nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
314
+ ...(input.runId === undefined
315
+ ? {}
316
+ : { runId: requireIdentityText(input.runId, "Run id") }),
317
+ ...(input.sessionSource === undefined
318
+ ? {}
319
+ : { sessionSource: requireIdentityText(input.sessionSource, "Session source") })
320
+ };
321
+ }
322
+ function normalizePromptAcceptedInput(input) {
323
+ if (input.scope !== "task")
324
+ throw invalidEvent();
325
+ if (input.adapterId !== "codex" && input.adapterId !== "claude")
326
+ throw invalidEvent();
327
+ return {
328
+ scope: "task",
329
+ taskId: requireIdentityText(input.taskId, "Task id"),
330
+ roleName: requireIdentityText(input.roleName, "Role name"),
331
+ agentId: requireIdentityText(input.agentId, "Agent id"),
332
+ adapterId: input.adapterId,
333
+ launchId: requireIdentityText(input.launchId, "Launch id"),
334
+ nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
335
+ runId: requireIdentityText(input.runId, "Run id"),
336
+ receiptId: requireIdentityText(input.receiptId, "Receipt id")
337
+ };
338
+ }
339
+ function normalizeProviderProgressInput(input) {
340
+ if (input.scope !== "task")
341
+ throw invalidEvent();
342
+ if (input.adapterId !== "codex" && input.adapterId !== "claude")
343
+ throw invalidEvent();
344
+ if (input.sequence !== undefined && !Number.isSafeInteger(input.sequence)) {
345
+ throw invalidEvent();
346
+ }
347
+ return {
348
+ scope: "task",
349
+ taskId: requireIdentityText(input.taskId, "Task id"),
350
+ roleName: requireIdentityText(input.roleName, "Role name"),
351
+ agentId: requireIdentityText(input.agentId, "Agent id"),
352
+ adapterId: input.adapterId,
353
+ launchId: requireIdentityText(input.launchId, "Launch id"),
354
+ nativeSessionId: requireIdentityText(input.nativeSessionId, "Native session id"),
355
+ runId: requireIdentityText(input.runId, "Run id"),
356
+ progressId: requireIdentityText(input.progressId, "Provider progress id"),
357
+ ...(input.sequence === undefined ? {} : { sequence: input.sequence })
358
+ };
359
+ }
360
+ function parseRuntimeEvent(value) {
361
+ if (!isObject(value))
362
+ throw invalidEvent();
363
+ switch (value.type) {
364
+ case "native-turn-completed": return parseCodexEvent(value);
365
+ case "claude-stop-failure": return parseClaudeStopFailureEvent(value);
366
+ case "native-session-lifecycle": return parseSessionLifecycleEvent(value);
367
+ case "native-prompt-accepted": return parsePromptAcceptedEvent(value);
368
+ case "native-turn-progress": return parseProviderProgressEvent(value);
369
+ default: throw invalidEvent();
370
+ }
371
+ }
372
+ function parseSessionLifecycleEvent(value) {
373
+ const expected = [
374
+ "schemaVersion", "id", "type", "receivedAt", "scope", "taskId", "roleName",
375
+ "agentId", "adapterId", "launchId", "nativeSessionId",
376
+ ...(value.runId === undefined ? [] : ["runId"]),
377
+ ...(value.sessionSource === undefined ? [] : ["sessionSource"])
378
+ ];
379
+ if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
380
+ throw invalidEvent();
381
+ const normalized = normalizeSessionLifecycleInput(value);
382
+ return Object.freeze({
383
+ schemaVersion: 1,
384
+ id: requireIdentityText(value.id, "Event id"),
385
+ type: "native-session-lifecycle",
386
+ receivedAt: requireTimestamp(value.receivedAt),
387
+ ...normalized
388
+ });
389
+ }
390
+ function parsePromptAcceptedEvent(value) {
391
+ const expected = [
392
+ "schemaVersion", "id", "type", "receivedAt", "scope", "taskId", "roleName",
393
+ "agentId", "adapterId", "launchId", "nativeSessionId", "runId", "receiptId"
394
+ ];
395
+ if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
396
+ throw invalidEvent();
397
+ const normalized = normalizePromptAcceptedInput(value);
398
+ return Object.freeze({
399
+ schemaVersion: 1,
400
+ id: requireIdentityText(value.id, "Event id"),
401
+ type: "native-prompt-accepted",
402
+ receivedAt: requireTimestamp(value.receivedAt),
403
+ ...normalized
404
+ });
405
+ }
406
+ function parseProviderProgressEvent(value) {
407
+ const expected = [
408
+ "schemaVersion", "id", "type", "receivedAt", "scope", "taskId", "roleName",
409
+ "agentId", "adapterId", "launchId", "nativeSessionId", "runId", "progressId",
410
+ ...(value.sequence === undefined ? [] : ["sequence"])
411
+ ];
412
+ if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
413
+ throw invalidEvent();
414
+ const normalized = normalizeProviderProgressInput(value);
415
+ return Object.freeze({
416
+ schemaVersion: 1,
417
+ id: requireIdentityText(value.id, "Event id"),
418
+ type: "native-turn-progress",
419
+ receivedAt: requireTimestamp(value.receivedAt),
420
+ ...normalized
421
+ });
422
+ }
423
+ function parseCodexEvent(value) {
424
+ const scope = value.scope;
425
+ const expected = scope === "task"
426
+ ? [
427
+ "schemaVersion", "id", "type", "receivedAt", "scope", "taskId",
428
+ "roleName", "agentId", "adapterId", "nativeSessionId", "turnId", "summary",
429
+ ...(value.launchId === undefined ? [] : ["launchId"]),
430
+ ...(value.runId === undefined ? [] : ["runId"]),
431
+ ...(value.title === undefined ? [] : ["title"])
432
+ ]
433
+ : [
434
+ "schemaVersion", "id", "type", "receivedAt", "scope",
435
+ "roleName", "agentId", "adapterId", "nativeSessionId", "turnId", "summary",
436
+ ...(value.launchId === undefined ? [] : ["launchId"]),
437
+ ...(value.runId === undefined ? [] : ["runId"]),
438
+ ...(value.title === undefined ? [] : ["title"])
439
+ ];
440
+ if ((scope !== "task" && scope !== "global")
441
+ || value.schemaVersion !== 1
442
+ || !hasExactKeys(value, expected))
443
+ throw invalidEvent();
444
+ const receivedAt = requireTimestamp(value.receivedAt);
445
+ const normalized = normalizeCodexInput({
446
+ scope,
447
+ ...(scope === "task" ? { taskId: value.taskId } : {}),
448
+ roleName: value.roleName,
449
+ agentId: value.agentId,
450
+ adapterId: value.adapterId,
451
+ ...(value.launchId === undefined ? {} : { launchId: value.launchId }),
452
+ nativeSessionId: value.nativeSessionId,
453
+ turnId: value.turnId,
454
+ ...(value.runId === undefined ? {} : { runId: value.runId }),
455
+ ...(value.title === undefined ? {} : { title: value.title }),
456
+ summary: value.summary
457
+ });
458
+ return Object.freeze({
459
+ schemaVersion: 1,
460
+ id: requireIdentityText(value.id, "Event id"),
461
+ type: "native-turn-completed",
462
+ receivedAt,
463
+ ...normalized
464
+ });
465
+ }
466
+ function parseClaudeStopFailureEvent(value) {
467
+ const expected = [
468
+ "schemaVersion", "id", "type", "receivedAt", "scope", "taskId", "roleName",
469
+ "agentId", "adapterId", "launchId", "nativeSessionId", "runId", "error",
470
+ ...(value.errorDetails === undefined ? [] : ["errorDetails"]),
471
+ ...(value.lastAssistantMessage === undefined ? [] : ["lastAssistantMessage"])
472
+ ];
473
+ if (value.schemaVersion !== 1 || !hasExactKeys(value, expected))
474
+ throw invalidEvent();
475
+ const normalized = normalizeClaudeStopFailureInput(value);
476
+ return Object.freeze({
477
+ schemaVersion: 1,
478
+ id: requireIdentityText(value.id, "Event id"),
479
+ type: "claude-stop-failure",
480
+ receivedAt: requireTimestamp(value.receivedAt),
481
+ ...normalized
482
+ });
483
+ }
484
+ function requireIdentityText(value, label) {
485
+ if (typeof value !== "string" || value.includes("\0"))
486
+ throw invalidEvent();
487
+ const text = value.trim();
488
+ if (text.length === 0 || text.length > 1_024) {
489
+ throw invalidEvent(`${label} is invalid.`);
490
+ }
491
+ return text;
492
+ }
493
+ function requireLongText(value, label) {
494
+ if (typeof value !== "string" || value.includes("\0") || value.trim().length === 0) {
495
+ throw invalidEvent(`${label} is required.`);
496
+ }
497
+ if (Buffer.byteLength(value, "utf8") > MAX_CLAUDE_HOOK_TEXT_BYTES) {
498
+ throw new RuntimeEventInboxError("RUNTIME_EVENT_TOO_LARGE", `${label} exceeds the durable inbox limit.`);
499
+ }
500
+ return value;
501
+ }
502
+ function requireTimestamp(value) {
503
+ const timestamp = requireIdentityText(value, "Received at");
504
+ if (!Number.isFinite(Date.parse(timestamp)))
505
+ throw invalidEvent();
506
+ return timestamp;
507
+ }
508
+ function truncateUtf8(value, maximumBytes) {
509
+ const encoded = Buffer.from(value, "utf8");
510
+ if (encoded.length <= maximumBytes)
511
+ return value;
512
+ let end = maximumBytes;
513
+ while (end > 0 && (encoded[end] & 0b1100_0000) === 0b1000_0000)
514
+ end -= 1;
515
+ return encoded.subarray(0, end).toString("utf8");
516
+ }
517
+ function ensureInboxDirectory(directory) {
518
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
519
+ }
520
+ function fsyncDirectory(directory) {
521
+ const descriptor = openSync(directory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0));
522
+ try {
523
+ fsyncSync(descriptor);
524
+ }
525
+ finally {
526
+ closeSync(descriptor);
527
+ }
528
+ }
529
+ function assertEventId(id) {
530
+ if (!EVENT_ID_PATTERN.test(id))
531
+ throw invalidEvent("Runtime event id is invalid.");
532
+ }
533
+ function hasSameIdentity(left, right) {
534
+ return left.id === right.id
535
+ && left.type === right.type
536
+ && left.scope === right.scope
537
+ && left.taskId === right.taskId
538
+ && left.roleName === right.roleName
539
+ && left.agentId === right.agentId
540
+ && left.adapterId === right.adapterId
541
+ && left.launchId === right.launchId
542
+ && left.nativeSessionId === right.nativeSessionId
543
+ && left.runId === right.runId
544
+ && (!("turnId" in left) || !("turnId" in right) || left.turnId === right.turnId)
545
+ && (!("progressId" in left)
546
+ || !("progressId" in right)
547
+ || left.progressId === right.progressId)
548
+ && (!("sequence" in left) || !("sequence" in right) || left.sequence === right.sequence);
549
+ }
550
+ function hasExactKeys(value, expected) {
551
+ const actual = Object.keys(value).sort();
552
+ const sortedExpected = [...expected].sort();
553
+ return actual.length === sortedExpected.length
554
+ && actual.every((key, index) => key === sortedExpected[index]);
555
+ }
556
+ function isObject(value) {
557
+ return typeof value === "object" && value !== null && !Array.isArray(value);
558
+ }
559
+ function isNodeError(error, code) {
560
+ return error instanceof Error && "code" in error && error.code === code;
561
+ }
562
+ function invalidEvent(message = "Runtime event is invalid.") {
563
+ return new RuntimeEventInboxError("RUNTIME_EVENT_INVALID", message);
564
+ }