@abelxiaoxing/cadence 1.8.4 → 2.0.1

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 (202) hide show
  1. package/README.md +324 -117
  2. package/THIRD_PARTY_NOTICES.md +15 -0
  3. package/agents/design-explorer.md +14 -18
  4. package/agents/diagnosis-worker.md +14 -14
  5. package/agents/implementation-worker.md +16 -17
  6. package/config/workflow-task.example.json +7 -0
  7. package/docs/remote-access.md +60 -0
  8. package/docs/reuse-and-distribution.md +44 -0
  9. package/docs/service-management.md +108 -0
  10. package/licenses/bundled-web.txt +6245 -0
  11. package/licenses/pi-web-ui-MIT.txt +21 -0
  12. package/licenses/pi-web-ui-reuse.md +22 -0
  13. package/package.json +92 -20
  14. package/src/index.ts +21 -1802
  15. package/src/native-build.ts +6 -0
  16. package/src/native-client.ts +103 -0
  17. package/src/native-export.ts +58 -0
  18. package/src/native-history.ts +359 -0
  19. package/src/operator-cli.mjs +453 -1937
  20. package/src/plan-proposal.ts +87 -0
  21. package/src/sdk-worker.mjs +549 -0
  22. package/src/service/contracts/active.ts +82 -0
  23. package/src/service/contracts/budget.ts +148 -0
  24. package/src/service/contracts/build.ts +67 -0
  25. package/src/service/contracts/capture.ts +48 -0
  26. package/src/service/contracts/core.ts +33 -0
  27. package/src/service/contracts/current.ts +261 -0
  28. package/src/service/contracts/domain.ts +68 -0
  29. package/src/service/contracts/history.ts +2245 -0
  30. package/src/service/contracts/models.ts +139 -0
  31. package/src/service/coordination.ts +862 -0
  32. package/src/service/private-files.ts +204 -0
  33. package/src/service-cli.mjs +5592 -0
  34. package/src/service-client.ts +747 -0
  35. package/src/service-contract.ts +245 -0
  36. package/src/service-host.mjs +22382 -0
  37. package/src/service-http-client.ts +425 -0
  38. package/src/service-wire.ts +372 -0
  39. package/src/task-shell.ts +1045 -0
  40. package/src/task-store.ts +479 -0
  41. package/src/task-types.ts +184 -0
  42. package/src/verification-diagnostics.ts +29 -72
  43. package/src/worker-capture.mjs +1197 -0
  44. package/src/worker-public.mjs +1753 -0
  45. package/src/worker-redaction.mjs +608 -0
  46. package/src/worker-tools.mjs +202 -0
  47. package/web/dist/assets/KaTeX_AMS-Regular.ttf +0 -0
  48. package/web/dist/assets/KaTeX_AMS-Regular.woff +0 -0
  49. package/web/dist/assets/KaTeX_AMS-Regular.woff2 +0 -0
  50. package/web/dist/assets/KaTeX_Caligraphic-Bold.ttf +0 -0
  51. package/web/dist/assets/KaTeX_Caligraphic-Bold.woff +0 -0
  52. package/web/dist/assets/KaTeX_Caligraphic-Bold.woff2 +0 -0
  53. package/web/dist/assets/KaTeX_Caligraphic-Regular.ttf +0 -0
  54. package/web/dist/assets/KaTeX_Caligraphic-Regular.woff +0 -0
  55. package/web/dist/assets/KaTeX_Caligraphic-Regular.woff2 +0 -0
  56. package/web/dist/assets/KaTeX_Fraktur-Bold.ttf +0 -0
  57. package/web/dist/assets/KaTeX_Fraktur-Bold.woff +0 -0
  58. package/web/dist/assets/KaTeX_Fraktur-Bold.woff2 +0 -0
  59. package/web/dist/assets/KaTeX_Fraktur-Regular.ttf +0 -0
  60. package/web/dist/assets/KaTeX_Fraktur-Regular.woff +0 -0
  61. package/web/dist/assets/KaTeX_Fraktur-Regular.woff2 +0 -0
  62. package/web/dist/assets/KaTeX_Main-Bold.ttf +0 -0
  63. package/web/dist/assets/KaTeX_Main-Bold.woff +0 -0
  64. package/web/dist/assets/KaTeX_Main-Bold.woff2 +0 -0
  65. package/web/dist/assets/KaTeX_Main-BoldItalic.ttf +0 -0
  66. package/web/dist/assets/KaTeX_Main-BoldItalic.woff +0 -0
  67. package/web/dist/assets/KaTeX_Main-BoldItalic.woff2 +0 -0
  68. package/web/dist/assets/KaTeX_Main-Italic.ttf +0 -0
  69. package/web/dist/assets/KaTeX_Main-Italic.woff +0 -0
  70. package/web/dist/assets/KaTeX_Main-Italic.woff2 +0 -0
  71. package/web/dist/assets/KaTeX_Main-Regular.ttf +0 -0
  72. package/web/dist/assets/KaTeX_Main-Regular.woff +0 -0
  73. package/web/dist/assets/KaTeX_Main-Regular.woff2 +0 -0
  74. package/web/dist/assets/KaTeX_Math-BoldItalic.ttf +0 -0
  75. package/web/dist/assets/KaTeX_Math-BoldItalic.woff +0 -0
  76. package/web/dist/assets/KaTeX_Math-BoldItalic.woff2 +0 -0
  77. package/web/dist/assets/KaTeX_Math-Italic.ttf +0 -0
  78. package/web/dist/assets/KaTeX_Math-Italic.woff +0 -0
  79. package/web/dist/assets/KaTeX_Math-Italic.woff2 +0 -0
  80. package/web/dist/assets/KaTeX_SansSerif-Bold.ttf +0 -0
  81. package/web/dist/assets/KaTeX_SansSerif-Bold.woff +0 -0
  82. package/web/dist/assets/KaTeX_SansSerif-Bold.woff2 +0 -0
  83. package/web/dist/assets/KaTeX_SansSerif-Italic.ttf +0 -0
  84. package/web/dist/assets/KaTeX_SansSerif-Italic.woff +0 -0
  85. package/web/dist/assets/KaTeX_SansSerif-Italic.woff2 +0 -0
  86. package/web/dist/assets/KaTeX_SansSerif-Regular.ttf +0 -0
  87. package/web/dist/assets/KaTeX_SansSerif-Regular.woff +0 -0
  88. package/web/dist/assets/KaTeX_SansSerif-Regular.woff2 +0 -0
  89. package/web/dist/assets/KaTeX_Script-Regular.ttf +0 -0
  90. package/web/dist/assets/KaTeX_Script-Regular.woff +0 -0
  91. package/web/dist/assets/KaTeX_Script-Regular.woff2 +0 -0
  92. package/web/dist/assets/KaTeX_Size1-Regular.ttf +0 -0
  93. package/web/dist/assets/KaTeX_Size1-Regular.woff +0 -0
  94. package/web/dist/assets/KaTeX_Size1-Regular.woff2 +0 -0
  95. package/web/dist/assets/KaTeX_Size2-Regular.ttf +0 -0
  96. package/web/dist/assets/KaTeX_Size2-Regular.woff +0 -0
  97. package/web/dist/assets/KaTeX_Size2-Regular.woff2 +0 -0
  98. package/web/dist/assets/KaTeX_Size3-Regular.ttf +0 -0
  99. package/web/dist/assets/KaTeX_Size3-Regular.woff +0 -0
  100. package/web/dist/assets/KaTeX_Size3-Regular.woff2 +0 -0
  101. package/web/dist/assets/KaTeX_Size4-Regular.ttf +0 -0
  102. package/web/dist/assets/KaTeX_Size4-Regular.woff +0 -0
  103. package/web/dist/assets/KaTeX_Size4-Regular.woff2 +0 -0
  104. package/web/dist/assets/KaTeX_Typewriter-Regular.ttf +0 -0
  105. package/web/dist/assets/KaTeX_Typewriter-Regular.woff +0 -0
  106. package/web/dist/assets/KaTeX_Typewriter-Regular.woff2 +0 -0
  107. package/web/dist/assets/cadence.css +2 -0
  108. package/web/dist/assets/cadence.js +7 -0
  109. package/web/dist/assets/markdown.js +296 -0
  110. package/web/dist/assets/react.js +9 -0
  111. package/web/dist/assets/rolldown-runtime.js +1 -0
  112. package/web/dist/assets/timeline-client.js +41 -0
  113. package/web/dist/assets/vis-timeline.css +1274 -0
  114. package/web/dist/assets/vis-timeline.js +32 -0
  115. package/web/dist/index.html +18 -0
  116. package/web/dist/manifest.json +505 -0
  117. package/config/plan-draft.example.json +0 -125
  118. package/config/plan-draft.multiple-tasks.example.json +0 -375
  119. package/config/plan-draft.quick.example.json +0 -78
  120. package/prompts/abel-design.md +0 -382
  121. package/prompts/abel-diagnose.md +0 -71
  122. package/prompts/abel-implement.md +0 -284
  123. package/prompts/abel-init.md +0 -52
  124. package/src/activation.ts +0 -44
  125. package/src/activity-contracts.ts +0 -82
  126. package/src/agent-registry.ts +0 -53
  127. package/src/apply-transaction.ts +0 -1437
  128. package/src/artifact-store.ts +0 -213
  129. package/src/build-windows-job.mjs +0 -95
  130. package/src/candidate-artifact.ts +0 -143
  131. package/src/candidate-context.ts +0 -289
  132. package/src/candidate-patch.ts +0 -271
  133. package/src/candidate-workspace.ts +0 -80
  134. package/src/canonical.ts +0 -16
  135. package/src/change-contract-schema.ts +0 -191
  136. package/src/change-contract.ts +0 -324
  137. package/src/change-verification.ts +0 -1617
  138. package/src/contracts.ts +0 -2466
  139. package/src/control-contracts.ts +0 -382
  140. package/src/delivery-compiler.ts +0 -1805
  141. package/src/delivery-revision.ts +0 -42
  142. package/src/design-control.ts +0 -1299
  143. package/src/design-diagnostics.ts +0 -181
  144. package/src/design-journal.ts +0 -1773
  145. package/src/directory-sync.ts +0 -36
  146. package/src/durable-contracts.ts +0 -227
  147. package/src/durable-workflow.ts +0 -822
  148. package/src/evidence-draft.ts +0 -122
  149. package/src/execution-profile.ts +0 -64
  150. package/src/execution-retention.ts +0 -77
  151. package/src/file-snapshot.ts +0 -163
  152. package/src/implement-continuation.ts +0 -559
  153. package/src/implement-graph.ts +0 -690
  154. package/src/implement-plan.ts +0 -72
  155. package/src/install-config.d.mts +0 -6
  156. package/src/isolation-backend.ts +0 -353
  157. package/src/model-source.ts +0 -22
  158. package/src/openspec-cli.ts +0 -494
  159. package/src/operator-cli-entry.mjs +0 -24
  160. package/src/operator-tools.ts +0 -309
  161. package/src/package-candidate-process.ts +0 -206
  162. package/src/package-candidate.ts +0 -24
  163. package/src/package-delivery.ts +0 -486
  164. package/src/package-state.ts +0 -320
  165. package/src/package-verification.ts +0 -635
  166. package/src/package-workflow.ts +0 -305
  167. package/src/packet-runtime.ts +0 -459
  168. package/src/phase-execution.ts +0 -2163
  169. package/src/pi-adapter.ts +0 -19
  170. package/src/plan-draft-summary.ts +0 -527
  171. package/src/plan-draft.ts +0 -502
  172. package/src/remove-path.ts +0 -32
  173. package/src/run-state.ts +0 -420
  174. package/src/run-store.ts +0 -1179
  175. package/src/safe-path.ts +0 -77
  176. package/src/scoped-grep-worker.mjs +0 -23
  177. package/src/scoped-tools.ts +0 -775
  178. package/src/single-task-draft.ts +0 -142
  179. package/src/sqlite-schema.ts +0 -171
  180. package/src/state-root.ts +0 -215
  181. package/src/storage-schema.ts +0 -360
  182. package/src/subagent-activity.ts +0 -1417
  183. package/src/subagent-process.ts +0 -476
  184. package/src/task-ledger.ts +0 -1338
  185. package/src/transport-budget.ts +0 -50
  186. package/src/verification-capability.ts +0 -1191
  187. package/src/verification-environment-io.ts +0 -389
  188. package/src/verification-environment.ts +0 -162
  189. package/src/verification-identity.ts +0 -77
  190. package/src/verification-scan.ts +0 -64
  191. package/src/windows-job-backend.ts +0 -419
  192. package/src/windows-job.c +0 -149
  193. package/src/workflow-engine.ts +0 -28
  194. package/src/workflow-policy.ts +0 -1670
  195. package/src/workflow-recovery-policy.ts +0 -97
  196. package/src/workflow-scheduling.ts +0 -105
  197. package/src/workflow-state-machine.ts +0 -4341
  198. package/src/workflow-status.ts +0 -576
  199. package/src/workspace-io-entry.mjs +0 -76
  200. package/src/workspace-io-worker.mjs +0 -1312
  201. package/src/workspace-io.ts +0 -89
  202. package/src/workspace-store.ts +0 -634
@@ -0,0 +1,87 @@
1
+ /** Portable local proposal only. UUID/hash are prospective, not stored membership. */
2
+
3
+ import { text } from "./service/contracts/core.ts";
4
+ import {
5
+ type Action,
6
+ type Assignment,
7
+ ContractError,
8
+ type CurrentView,
9
+ canonicalJson,
10
+ decodeAction,
11
+ decodeCurrentView,
12
+ roleForPurpose,
13
+ utf8Bytes,
14
+ } from "./service-contract.ts";
15
+
16
+ export interface ProposalCrypto {
17
+ uuid(): string;
18
+ sha256(bytes: Uint8Array): string | Promise<string>;
19
+ }
20
+ export async function proposePlan(
21
+ input: CurrentView,
22
+ readablePlan: string,
23
+ assignments: readonly Assignment[],
24
+ crypto: ProposalCrypto,
25
+ ): Promise<Extract<Action, { kind: "save-plan" }>> {
26
+ // Decode/copy BEFORE the digest await. Caller mutation cannot retarget a draft.
27
+ const current = decodeCurrentView(input);
28
+ if (
29
+ current.context.status !== "available" ||
30
+ !current.context.version.task ||
31
+ !current.readablePlan?.editable ||
32
+ current.savedGraph.status === "unavailable"
33
+ )
34
+ throw new ContractError("unavailable");
35
+ const context = current.context.version;
36
+ const task = context.task;
37
+ if (!task) throw new ContractError("stale");
38
+ const plan = text(readablePlan, 32768);
39
+ // The shared action/graph decoder checks every role/capability/dependency/UTF8
40
+ // and aggregate bound before invoking asynchronous caller-supplied crypto.
41
+ const action = decodeAction({
42
+ kind: "save-plan",
43
+ taskId: task.id,
44
+ plan,
45
+ graph: {
46
+ schema: "cadence-graph/2",
47
+ taskId: task.id,
48
+ taskKind: task.kind,
49
+ taskRevision: task.revision + 1,
50
+ revision: (task.graphRevision ?? 0) + 1,
51
+ planRevision:
52
+ current.savedGraph.status === "saved"
53
+ ? current.savedGraph.planRevision + 1
54
+ : 1,
55
+ plan: {
56
+ recordId: crypto.uuid(),
57
+ version:
58
+ current.savedGraph.status === "saved"
59
+ ? current.savedGraph.planRevision + 1
60
+ : 1,
61
+ field: "plan",
62
+ bytes: utf8Bytes(plan),
63
+ // Structural placeholder only, replaced by the exact digest below.
64
+ sha256: "0".repeat(64),
65
+ },
66
+ assignments,
67
+ },
68
+ });
69
+ if (action.kind !== "save-plan") throw new ContractError("invalid");
70
+ for (const assignment of action.graph.assignments) {
71
+ const role = current.roles.find(
72
+ (r) => r.role === roleForPurpose(assignment.purpose),
73
+ );
74
+ if (
75
+ !role ||
76
+ canonicalJson(role) !== canonicalJson(assignment.role) ||
77
+ canonicalJson(context.model) !== canonicalJson(assignment.model)
78
+ )
79
+ throw new ContractError("stale");
80
+ }
81
+ action.graph.plan.sha256 = await crypto.sha256(
82
+ new TextEncoder().encode(plan),
83
+ );
84
+ const proposal = decodeAction(action);
85
+ if (proposal.kind !== "save-plan") throw new ContractError("invalid");
86
+ return proposal;
87
+ }
@@ -0,0 +1,549 @@
1
+ // Ordinary SDK child, not Pi CLI startup. No discovery, credential files or fallback executor.
2
+ import {
3
+ createProvider,
4
+ InMemoryCredentialStore,
5
+ InMemoryModelsStore,
6
+ } from "@earendil-works/pi-ai";
7
+ import {
8
+ createAgentSession,
9
+ createExtensionRuntime,
10
+ createLocalBashOperations,
11
+ ModelRuntime,
12
+ SessionManager,
13
+ SettingsManager,
14
+ } from "@earendil-works/pi-coding-agent";
15
+ import { CaptureEmitter, SdkCapture } from "./worker-capture.mjs";
16
+ import {
17
+ closed,
18
+ decodeWorkerPublicRequest,
19
+ LIMITS,
20
+ parseWireJson,
21
+ } from "./worker-public.mjs";
22
+ import { own } from "./worker-redaction.mjs";
23
+ import { captureBash, ToolIngress } from "./worker-tools.mjs";
24
+
25
+ const adapters = {
26
+ "openai-completions": () =>
27
+ import("@earendil-works/pi-ai/api/openai-completions"),
28
+ "openai-responses": () =>
29
+ import("@earendil-works/pi-ai/api/openai-responses"),
30
+ "openai-codex-responses": () =>
31
+ import("@earendil-works/pi-ai/api/openai-codex-responses"),
32
+ "anthropic-messages": () =>
33
+ import("@earendil-works/pi-ai/api/anthropic-messages"),
34
+ "google-generative-ai": () =>
35
+ import("@earendil-works/pi-ai/api/google-generative-ai"),
36
+ "mistral-conversations": () =>
37
+ import("@earendil-works/pi-ai/api/mistral-conversations"),
38
+ "azure-openai-responses": () =>
39
+ import("@earendil-works/pi-ai/api/azure-openai-responses"),
40
+ };
41
+ let session;
42
+ let ownedAgent;
43
+ let unsubscribe;
44
+ let emitter;
45
+ let capture;
46
+ let ingress;
47
+ let stdoutFailureListener;
48
+ let bytes = 0;
49
+ let inputBytes = 0;
50
+ let sdkEvents = 0;
51
+ let modelOutputBytes = 0;
52
+ let stage = "guards";
53
+ let failure;
54
+ let terminalStopReason = "UNKNOWN";
55
+ let requestPrepared = false;
56
+ let actualResponseStatus = "UNKNOWN";
57
+ let preflight = "UNKNOWN";
58
+ let cleanupFailures = 0; // Bit flags: unsubscribe=1, abort=2, dispose=4, owned close=8.
59
+ let pendingAbort;
60
+ let cleanupSettled = false;
61
+ const fail = (where, category) => {
62
+ const first = failure === undefined;
63
+ failure ??= { stage: where, class: category };
64
+ process.exitCode = 1;
65
+ if (first) {
66
+ if (
67
+ emitter?.state.position.workerSeq &&
68
+ !emitter.state.position.final &&
69
+ !emitter.broken
70
+ ) {
71
+ try {
72
+ void emitter
73
+ .emit({
74
+ kind: "worker-failure",
75
+ code:
76
+ where === "cleanup"
77
+ ? "cleanup"
78
+ : where === "network"
79
+ ? "network"
80
+ : "capture",
81
+ })
82
+ .catch(() => {});
83
+ } catch {
84
+ /* A failed capture cannot fabricate a failure receipt. */
85
+ }
86
+ }
87
+ // A cleanup promise can remain unsettled after all native handles close.
88
+ // Preserve the observed failure before awaiting it, without claiming cleanup.
89
+ try {
90
+ emitFailure();
91
+ } catch {
92
+ // Diagnostic I/O must not expose an exception or repair failed work.
93
+ }
94
+ }
95
+ };
96
+ const refuse = (where, category) => {
97
+ fail(where, category);
98
+ throw new Error("Worker refused");
99
+ };
100
+ const abortForFailure = () => {
101
+ // Attach a rejection handler immediately, including synchronous SDK throws.
102
+ // Do not let fire-and-forget abort print a secret-bearing unhandled rejection.
103
+ pendingAbort ??= (async () => {
104
+ try {
105
+ await session.abort();
106
+ } catch {
107
+ cleanupFailures |= 2;
108
+ fail("cleanup", "cleanup-failure");
109
+ }
110
+ })();
111
+ };
112
+ const emitFailure = () => {
113
+ // Construct from fixed labels/primitives only; never inspect exception text,
114
+ // provider diagnostics, response headers, payloads, usage or tool contents.
115
+ const line = `${JSON.stringify({
116
+ ...failure,
117
+ outcome: "failed",
118
+ sdkEvents: Math.min(sdkEvents, LIMITS.sdkEvents + 1),
119
+ inputBytes: Math.min(inputBytes, 128 * 1024 + 1),
120
+ modelOutputBytes: Math.min(modelOutputBytes, LIMITS.childProtocolBytes + 1),
121
+ protocolBytes: Math.min(
122
+ bytes + (emitter?.state.bytes ?? 0),
123
+ LIMITS.childProtocolBytes + 1,
124
+ ),
125
+ terminalStopReason,
126
+ preflight,
127
+ requestPrepared,
128
+ actualResponseStatus,
129
+ cleanupFailures,
130
+ cleanupSettled,
131
+ })}\n`;
132
+ // Diagnostic output shares the existing aggregate cap, not a new allowance.
133
+ const write = (text) => {
134
+ const size = Buffer.byteLength(text);
135
+ if (
136
+ bytes + (emitter?.state.bytes ?? 0) + size <=
137
+ LIMITS.childProtocolBytes
138
+ ) {
139
+ bytes += size;
140
+ process.stderr.write(text);
141
+ }
142
+ };
143
+ if (Buffer.byteLength(line) <= 512) write(line);
144
+ if (cleanupSettled)
145
+ write("SDK worker failed or returned an incomplete result.\n");
146
+ };
147
+ try {
148
+ if (
149
+ process.env.CADENCE_SUBAGENT_CHILD !== "1" ||
150
+ process.env.PI_OFFLINE !== "1"
151
+ )
152
+ refuse("guards", "guards-required");
153
+ // Only the parent's validated uppercase projection is input here. Capture in
154
+ // RAM before asynchronous setup; literal auth.env is never process routing.
155
+ let proxy;
156
+ try {
157
+ const httpProxy = process.env.HTTP_PROXY;
158
+ const httpsProxy = process.env.HTTPS_PROXY;
159
+ const optIn = process.env.NODE_USE_ENV_PROXY;
160
+ if (
161
+ httpProxy !== undefined ||
162
+ httpsProxy !== undefined ||
163
+ optIn !== undefined
164
+ ) {
165
+ if (
166
+ typeof httpProxy !== "string" ||
167
+ !httpProxy ||
168
+ httpProxy !== httpsProxy ||
169
+ optIn !== "1"
170
+ )
171
+ refuse("network", "unsupported-network");
172
+ proxy = { httpProxy, httpsProxy, noProxy: "" };
173
+ }
174
+ } catch {
175
+ refuse("network", "unsupported-network");
176
+ }
177
+ stage = "parse";
178
+ const chunks = [];
179
+ for await (const chunk of process.stdin) {
180
+ inputBytes += chunk.length;
181
+ if (inputBytes > 128 * 1024) refuse("parse", "input-bound");
182
+ chunks.push(chunk);
183
+ }
184
+ const envelope = closed(
185
+ parseWireJson(
186
+ new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)),
187
+ LIMITS.workerRequestBytes,
188
+ ),
189
+ ["request", "cwd", "model", "auth"],
190
+ );
191
+ const publicRequest = decodeWorkerPublicRequest(envelope.request);
192
+ const request = {
193
+ ...envelope,
194
+ kind:
195
+ publicRequest.purpose === "edit"
196
+ ? "code"
197
+ : publicRequest.purpose === "diagnosis"
198
+ ? "diagnosis"
199
+ : "research",
200
+ prompt: publicRequest.prompt,
201
+ instructions: publicRequest.instructions,
202
+ thinkingLevel: publicRequest.model.thinking,
203
+ };
204
+ stage = "auth-model";
205
+ const { cwd, auth, kind, prompt, instructions, thinkingLevel } = request;
206
+ if (
207
+ !auth?.ok ||
208
+ typeof cwd !== "string" ||
209
+ typeof prompt !== "string" ||
210
+ typeof instructions !== "string" ||
211
+ !["code", "research", "diagnosis"].includes(kind)
212
+ )
213
+ refuse("auth-model", "invalid-request");
214
+ if (
215
+ Object.keys(auth.env ?? {}).some((name) =>
216
+ /(?:_FILE|_PATH|_CREDENTIALS)$/.test(name),
217
+ )
218
+ )
219
+ refuse("auth-model", "unsupported-auth");
220
+ const model = {
221
+ ...request.model,
222
+ baseUrl: auth.baseUrl ?? request.model.baseUrl,
223
+ };
224
+ delete model.headers; // Parent auth already resolved these. Never config-interpolate literal credentials.
225
+ if (
226
+ !Object.hasOwn(adapters, model.api) ||
227
+ typeof model.provider !== "string" ||
228
+ typeof model.id !== "string" ||
229
+ model.id !== publicRequest.model.model ||
230
+ model.provider !== publicRequest.model.provider ||
231
+ model.api !== publicRequest.model.api
232
+ )
233
+ refuse("auth-model", "unsupported-model");
234
+ stage = "setup";
235
+ const api = await adapters[model.api]();
236
+ if (proxy) {
237
+ stage = "network";
238
+ try {
239
+ // SDK/adapter imports can reset native startup routing. Configure only
240
+ // this disposable child, using our declared public peer after imports.
241
+ const { EnvHttpProxyAgent, setGlobalDispatcher, getGlobalDispatcher } =
242
+ await import("undici");
243
+ if (
244
+ typeof EnvHttpProxyAgent !== "function" ||
245
+ typeof setGlobalDispatcher !== "function" ||
246
+ typeof getGlobalDispatcher !== "function"
247
+ )
248
+ refuse("network", "unsupported-network");
249
+ ownedAgent = new EnvHttpProxyAgent(proxy);
250
+ // Retain ownership even when installation or its identity check fails.
251
+ setGlobalDispatcher(ownedAgent);
252
+ if (getGlobalDispatcher() !== ownedAgent)
253
+ refuse("network", "unsupported-network");
254
+ } catch {
255
+ refuse("network", "unsupported-network");
256
+ }
257
+ }
258
+ stage = "setup";
259
+ const runtime = await ModelRuntime.create({
260
+ credentials: new InMemoryCredentialStore(),
261
+ modelsStore: new InMemoryModelsStore(),
262
+ modelsPath: null,
263
+ allowModelNetwork: false,
264
+ refreshOnCreate: false,
265
+ });
266
+ runtime.registerNativeProvider(
267
+ createProvider({
268
+ id: model.provider,
269
+ models: [model],
270
+ api,
271
+ auth: {
272
+ apiKey: {
273
+ name: "Resolved parent request",
274
+ resolve: async () => ({
275
+ auth: {
276
+ apiKey: auth.apiKey,
277
+ headers: auth.headers,
278
+ baseUrl: auth.baseUrl,
279
+ },
280
+ env: auth.env,
281
+ }),
282
+ },
283
+ },
284
+ }),
285
+ );
286
+ const extensionRuntime = createExtensionRuntime();
287
+ const resourceLoader = {
288
+ getExtensions: () => ({
289
+ extensions: [],
290
+ errors: [],
291
+ runtime: extensionRuntime,
292
+ }),
293
+ getSkills: () => ({ skills: [], diagnostics: [] }),
294
+ getPrompts: () => ({ prompts: [], diagnostics: [] }),
295
+ getThemes: () => ({ themes: [], diagnostics: [] }),
296
+ getAgentsFiles: () => ({ agentsFiles: [] }),
297
+ getSystemPrompt: () => instructions,
298
+ getSystemPromptSource: () => undefined,
299
+ getAppendSystemPrompt: () => [],
300
+ getAppendSystemPromptSources: () => [],
301
+ extendResources: () => {},
302
+ reload: async () => {
303
+ throw new Error("Discovery disabled");
304
+ },
305
+ };
306
+ const secrets = [
307
+ auth.apiKey ?? "",
308
+ ...Object.values(auth.headers ?? {}).filter((v) => typeof v === "string"),
309
+ ...Object.values(auth.env ?? {}),
310
+ ...(proxy ? [proxy.httpProxy] : []),
311
+ ];
312
+ const captureFailure = () => {
313
+ fail("events", "event-projection-failed");
314
+ abortForFailure();
315
+ };
316
+ emitter = new CaptureEmitter(
317
+ publicRequest.childRunId,
318
+ process.stdout,
319
+ captureFailure,
320
+ publicRequest,
321
+ );
322
+ capture = new SdkCapture(emitter, secrets);
323
+ ingress = new ToolIngress(secrets, captureFailure, emitter.queue);
324
+ stdoutFailureListener = captureFailure;
325
+ process.stdout.on("error", stdoutFailureListener);
326
+ stage = "session";
327
+ const requestedThinkingLevel = thinkingLevel ?? "off";
328
+ const created = await createAgentSession({
329
+ cwd,
330
+ agentDir: cwd,
331
+ model,
332
+ modelRuntime: runtime,
333
+ thinkingLevel: requestedThinkingLevel,
334
+ tools:
335
+ kind === "code"
336
+ ? ["read", "grep", "find", "ls", "write", "edit", "bash"]
337
+ : ["read", "grep", "find", "ls"],
338
+ customTools:
339
+ kind === "code"
340
+ ? [
341
+ captureBash(
342
+ cwd,
343
+ secrets,
344
+ captureFailure,
345
+ createLocalBashOperations,
346
+ emitter.queue,
347
+ ),
348
+ ]
349
+ : [],
350
+ resourceLoader,
351
+ sessionManager: SessionManager.inMemory(cwd),
352
+ settingsManager: SettingsManager.inMemory({
353
+ compaction: { enabled: false },
354
+ retry: { enabled: false },
355
+ cacheWarming: "off",
356
+ }),
357
+ });
358
+ session = created.session;
359
+ let effectiveThinkingLevel;
360
+ try {
361
+ effectiveThinkingLevel = session.thinkingLevel;
362
+ } catch {
363
+ refuse("session", "model-substitution");
364
+ }
365
+ if (
366
+ created.modelFallbackMessage ||
367
+ session.model?.id !== model.id ||
368
+ session.model?.provider !== model.provider ||
369
+ session.model?.api !== model.api ||
370
+ effectiveThinkingLevel !== requestedThinkingLevel
371
+ )
372
+ refuse("session", "model-substitution");
373
+ // Adapter retry defaults are not workflow retries; explicitly disable both.
374
+ const stream = session.agent.streamFunction;
375
+ session.agent.streamFunction = (selected, context, options) => {
376
+ const snapshot = { ...options, maxRetries: 0 };
377
+ const { onPayload, onResponse } = snapshot;
378
+ snapshot.onPayload = function (...args) {
379
+ // Preparation is not proof of sending, an HTTP response or inference.
380
+ requestPrepared = true;
381
+ return onPayload == null
382
+ ? undefined
383
+ : Reflect.apply(onPayload, this, args);
384
+ };
385
+ snapshot.onResponse = function (...args) {
386
+ // Observation must not bypass the original callback, even for a bad peer.
387
+ // Public HTTP hook only; WS transports may never report a status here.
388
+ actualResponseStatus = "UNKNOWN";
389
+ try {
390
+ const status = args[0]?.status;
391
+ if (Number.isInteger(status) && status >= 100 && status <= 599)
392
+ actualResponseStatus = status;
393
+ } catch {
394
+ // Do not inspect/coerce the response or exception.
395
+ }
396
+ return onResponse == null
397
+ ? undefined
398
+ : Reflect.apply(onResponse, this, args);
399
+ };
400
+ return stream(selected, context, snapshot);
401
+ };
402
+ // Public awaited core subscription, not synchronous AgentSession forwarding.
403
+ if (
404
+ typeof session.agent.subscribe !== "function" ||
405
+ !["parallel", "sequential"].includes(session.agent.toolExecution)
406
+ )
407
+ refuse("session", "capture-unsupported");
408
+ session.agent.toolExecution = "sequential";
409
+ if (session.agent.toolExecution !== "sequential")
410
+ refuse("session", "capture-unsupported");
411
+ session.agent.state.tools = ingress.wrap(session.agent.state.tools);
412
+ unsubscribe = session.agent.subscribe(async (event) => {
413
+ try {
414
+ sdkEvents++;
415
+ if (sdkEvents > LIMITS.sdkEvents) refuse("outputbounds", "event-bound");
416
+ if (own(event, "type") === "message_update") {
417
+ const update = own(event, "assistantMessageEvent");
418
+ const type = own(update, "type");
419
+ if (type === "text_delta" || type === "thinking_delta") {
420
+ const delta = own(update, "delta");
421
+ if (typeof delta !== "string") throw new Error("Malformed SDK event");
422
+ modelOutputBytes += Buffer.byteLength(delta);
423
+ if (modelOutputBytes > LIMITS.childProtocolBytes)
424
+ refuse("outputbounds", "model-output-bound");
425
+ }
426
+ }
427
+ if (own(event, "type") === "message_end") {
428
+ const message = own(event, "message");
429
+ if (own(message, "role") === "assistant") {
430
+ const reason = own(message, "stopReason");
431
+ terminalStopReason = [
432
+ "pending",
433
+ "stop",
434
+ "length",
435
+ "toolUse",
436
+ "error",
437
+ "aborted",
438
+ "deferred",
439
+ ].includes(reason)
440
+ ? reason
441
+ : "UNKNOWN";
442
+ }
443
+ }
444
+ if (failure) return;
445
+ await capture.event(event);
446
+ } catch {
447
+ fail("events", "event-projection-failed");
448
+ abortForFailure();
449
+ } finally {
450
+ try {
451
+ ingress.release(event);
452
+ } catch {
453
+ fail("events", "event-projection-failed");
454
+ abortForFailure();
455
+ }
456
+ }
457
+ });
458
+ stage = "ready";
459
+ await emitter.emit({ kind: "ready", requestProtocol: 2 });
460
+ await capture.context(publicRequest, session.systemPrompt);
461
+ stage = "prompt";
462
+ const suppliedPrompt = [
463
+ publicRequest.context,
464
+ ...publicRequest.handoff.inputs.map(
465
+ (input) =>
466
+ `Dependency ${input.assignmentId} (recorded input):\n${input.text}`,
467
+ ),
468
+ prompt,
469
+ ]
470
+ .filter(Boolean)
471
+ .join("\n\n");
472
+ await session.prompt(suppliedPrompt, {
473
+ expandPromptTemplates: false,
474
+ preflightResult: (accepted) => {
475
+ preflight = accepted ? "accepted" : "rejected";
476
+ if (!accepted) fail("preflight", "preflight-rejected");
477
+ },
478
+ });
479
+ stage = "assistant-final";
480
+ if (failure || terminalStopReason !== "stop" || ingress.frames !== 0)
481
+ refuse("assistant-final", "terminal-incomplete");
482
+ await capture.final();
483
+ await emitter.drain();
484
+ } catch {
485
+ // The stage is a location, not a complete diagnosis of an SDK/provider cause.
486
+ fail(stage, stage === "parse" ? "parse-failed" : "sdk-failure");
487
+ } finally {
488
+ try {
489
+ capture?.dispose();
490
+ } catch {
491
+ fail("cleanup", "cleanup-failure");
492
+ }
493
+ // Start and rejection-handle owned close BEFORE any potentially pending SDK
494
+ // cleanup. Never restore or close the previous, unowned global dispatcher.
495
+ const closing = (async () => {
496
+ if (!ownedAgent) return;
497
+ try {
498
+ await ownedAgent.close();
499
+ } catch {
500
+ cleanupFailures |= 8;
501
+ fail("cleanup", "cleanup-failure");
502
+ }
503
+ })();
504
+ const unsubscribing = (async () => {
505
+ try {
506
+ await unsubscribe?.();
507
+ } catch {
508
+ cleanupFailures |= 1;
509
+ fail("cleanup", "cleanup-failure");
510
+ }
511
+ })();
512
+ const aborting = (async () => {
513
+ if (!session) return;
514
+ try {
515
+ await session.abort();
516
+ } catch {
517
+ cleanupFailures |= 2;
518
+ fail("cleanup", "cleanup-failure");
519
+ }
520
+ })();
521
+ const disposing = (async () => {
522
+ if (!session) return;
523
+ try {
524
+ await session.dispose();
525
+ } catch {
526
+ cleanupFailures |= 4;
527
+ fail("cleanup", "cleanup-failure");
528
+ }
529
+ })();
530
+ await Promise.all([
531
+ closing,
532
+ unsubscribing,
533
+ pendingAbort,
534
+ aborting,
535
+ disposing,
536
+ emitter?.drain(),
537
+ ]);
538
+ // A final snapshot exists only after the actual cleanup attempts settle.
539
+ if (stdoutFailureListener)
540
+ process.stdout.removeListener("error", stdoutFailureListener);
541
+ cleanupSettled = true;
542
+ // Cleanup failures cannot become success, nor replace the first run failure.
543
+ try {
544
+ if (failure) emitFailure();
545
+ } catch {
546
+ // A failed diagnostic write cannot print an arbitrary exception or repair failure.
547
+ process.exitCode = 1;
548
+ }
549
+ }