@abelxiaoxing/cadence 1.8.3 → 2.0.0

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/README.md +324 -111
  2. package/THIRD_PARTY_NOTICES.md +15 -0
  3. package/agents/design-explorer.md +14 -26
  4. package/agents/diagnosis-worker.md +14 -25
  5. package/agents/implementation-worker.md +16 -35
  6. package/config/.env.example +2 -5
  7. package/config/workflow-task.example.json +7 -0
  8. package/docs/remote-access.md +60 -0
  9. package/docs/reuse-and-distribution.md +44 -0
  10. package/docs/service-management.md +108 -0
  11. package/licenses/bundled-web.txt +6245 -0
  12. package/licenses/pi-web-ui-MIT.txt +21 -0
  13. package/licenses/pi-web-ui-reuse.md +22 -0
  14. package/package.json +94 -21
  15. package/src/index.ts +22 -1807
  16. package/src/native-build.ts +6 -0
  17. package/src/native-client.ts +103 -0
  18. package/src/native-export.ts +58 -0
  19. package/src/native-history.ts +359 -0
  20. package/src/operator-cli.mjs +453 -1937
  21. package/src/plan-proposal.ts +87 -0
  22. package/src/sdk-worker.mjs +549 -0
  23. package/src/service/contracts/active.ts +82 -0
  24. package/src/service/contracts/budget.ts +148 -0
  25. package/src/service/contracts/build.ts +67 -0
  26. package/src/service/contracts/capture.ts +48 -0
  27. package/src/service/contracts/core.ts +33 -0
  28. package/src/service/contracts/current.ts +261 -0
  29. package/src/service/contracts/domain.ts +68 -0
  30. package/src/service/contracts/history.ts +2245 -0
  31. package/src/service/contracts/models.ts +139 -0
  32. package/src/service/coordination.ts +862 -0
  33. package/src/service/private-files.ts +204 -0
  34. package/src/service-cli.mjs +5592 -0
  35. package/src/service-client.ts +747 -0
  36. package/src/service-contract.ts +245 -0
  37. package/src/service-host.mjs +22382 -0
  38. package/src/service-http-client.ts +425 -0
  39. package/src/service-wire.ts +372 -0
  40. package/src/task-shell.ts +1045 -0
  41. package/src/task-store.ts +479 -0
  42. package/src/task-types.ts +184 -0
  43. package/src/verification-diagnostics.ts +29 -72
  44. package/src/worker-capture.mjs +1197 -0
  45. package/src/worker-public.mjs +1753 -0
  46. package/src/worker-redaction.mjs +608 -0
  47. package/src/worker-tools.mjs +202 -0
  48. package/web/dist/assets/KaTeX_AMS-Regular.ttf +0 -0
  49. package/web/dist/assets/KaTeX_AMS-Regular.woff +0 -0
  50. package/web/dist/assets/KaTeX_AMS-Regular.woff2 +0 -0
  51. package/web/dist/assets/KaTeX_Caligraphic-Bold.ttf +0 -0
  52. package/web/dist/assets/KaTeX_Caligraphic-Bold.woff +0 -0
  53. package/web/dist/assets/KaTeX_Caligraphic-Bold.woff2 +0 -0
  54. package/web/dist/assets/KaTeX_Caligraphic-Regular.ttf +0 -0
  55. package/web/dist/assets/KaTeX_Caligraphic-Regular.woff +0 -0
  56. package/web/dist/assets/KaTeX_Caligraphic-Regular.woff2 +0 -0
  57. package/web/dist/assets/KaTeX_Fraktur-Bold.ttf +0 -0
  58. package/web/dist/assets/KaTeX_Fraktur-Bold.woff +0 -0
  59. package/web/dist/assets/KaTeX_Fraktur-Bold.woff2 +0 -0
  60. package/web/dist/assets/KaTeX_Fraktur-Regular.ttf +0 -0
  61. package/web/dist/assets/KaTeX_Fraktur-Regular.woff +0 -0
  62. package/web/dist/assets/KaTeX_Fraktur-Regular.woff2 +0 -0
  63. package/web/dist/assets/KaTeX_Main-Bold.ttf +0 -0
  64. package/web/dist/assets/KaTeX_Main-Bold.woff +0 -0
  65. package/web/dist/assets/KaTeX_Main-Bold.woff2 +0 -0
  66. package/web/dist/assets/KaTeX_Main-BoldItalic.ttf +0 -0
  67. package/web/dist/assets/KaTeX_Main-BoldItalic.woff +0 -0
  68. package/web/dist/assets/KaTeX_Main-BoldItalic.woff2 +0 -0
  69. package/web/dist/assets/KaTeX_Main-Italic.ttf +0 -0
  70. package/web/dist/assets/KaTeX_Main-Italic.woff +0 -0
  71. package/web/dist/assets/KaTeX_Main-Italic.woff2 +0 -0
  72. package/web/dist/assets/KaTeX_Main-Regular.ttf +0 -0
  73. package/web/dist/assets/KaTeX_Main-Regular.woff +0 -0
  74. package/web/dist/assets/KaTeX_Main-Regular.woff2 +0 -0
  75. package/web/dist/assets/KaTeX_Math-BoldItalic.ttf +0 -0
  76. package/web/dist/assets/KaTeX_Math-BoldItalic.woff +0 -0
  77. package/web/dist/assets/KaTeX_Math-BoldItalic.woff2 +0 -0
  78. package/web/dist/assets/KaTeX_Math-Italic.ttf +0 -0
  79. package/web/dist/assets/KaTeX_Math-Italic.woff +0 -0
  80. package/web/dist/assets/KaTeX_Math-Italic.woff2 +0 -0
  81. package/web/dist/assets/KaTeX_SansSerif-Bold.ttf +0 -0
  82. package/web/dist/assets/KaTeX_SansSerif-Bold.woff +0 -0
  83. package/web/dist/assets/KaTeX_SansSerif-Bold.woff2 +0 -0
  84. package/web/dist/assets/KaTeX_SansSerif-Italic.ttf +0 -0
  85. package/web/dist/assets/KaTeX_SansSerif-Italic.woff +0 -0
  86. package/web/dist/assets/KaTeX_SansSerif-Italic.woff2 +0 -0
  87. package/web/dist/assets/KaTeX_SansSerif-Regular.ttf +0 -0
  88. package/web/dist/assets/KaTeX_SansSerif-Regular.woff +0 -0
  89. package/web/dist/assets/KaTeX_SansSerif-Regular.woff2 +0 -0
  90. package/web/dist/assets/KaTeX_Script-Regular.ttf +0 -0
  91. package/web/dist/assets/KaTeX_Script-Regular.woff +0 -0
  92. package/web/dist/assets/KaTeX_Script-Regular.woff2 +0 -0
  93. package/web/dist/assets/KaTeX_Size1-Regular.ttf +0 -0
  94. package/web/dist/assets/KaTeX_Size1-Regular.woff +0 -0
  95. package/web/dist/assets/KaTeX_Size1-Regular.woff2 +0 -0
  96. package/web/dist/assets/KaTeX_Size2-Regular.ttf +0 -0
  97. package/web/dist/assets/KaTeX_Size2-Regular.woff +0 -0
  98. package/web/dist/assets/KaTeX_Size2-Regular.woff2 +0 -0
  99. package/web/dist/assets/KaTeX_Size3-Regular.ttf +0 -0
  100. package/web/dist/assets/KaTeX_Size3-Regular.woff +0 -0
  101. package/web/dist/assets/KaTeX_Size3-Regular.woff2 +0 -0
  102. package/web/dist/assets/KaTeX_Size4-Regular.ttf +0 -0
  103. package/web/dist/assets/KaTeX_Size4-Regular.woff +0 -0
  104. package/web/dist/assets/KaTeX_Size4-Regular.woff2 +0 -0
  105. package/web/dist/assets/KaTeX_Typewriter-Regular.ttf +0 -0
  106. package/web/dist/assets/KaTeX_Typewriter-Regular.woff +0 -0
  107. package/web/dist/assets/KaTeX_Typewriter-Regular.woff2 +0 -0
  108. package/web/dist/assets/cadence.css +2 -0
  109. package/web/dist/assets/cadence.js +7 -0
  110. package/web/dist/assets/markdown.js +296 -0
  111. package/web/dist/assets/react.js +9 -0
  112. package/web/dist/assets/rolldown-runtime.js +1 -0
  113. package/web/dist/assets/timeline-client.js +41 -0
  114. package/web/dist/assets/vis-timeline.css +1274 -0
  115. package/web/dist/assets/vis-timeline.js +32 -0
  116. package/web/dist/index.html +18 -0
  117. package/web/dist/manifest.json +505 -0
  118. package/config/plan-draft.example.json +0 -125
  119. package/config/plan-draft.multiple-tasks.example.json +0 -375
  120. package/config/plan-draft.quick.example.json +0 -78
  121. package/config/routes.example.json +0 -39
  122. package/prompts/abel-design.md +0 -382
  123. package/prompts/abel-diagnose.md +0 -71
  124. package/prompts/abel-implement.md +0 -284
  125. package/prompts/abel-init.md +0 -52
  126. package/src/activation.ts +0 -44
  127. package/src/activity-contracts.ts +0 -82
  128. package/src/agent-registry.ts +0 -53
  129. package/src/apply-transaction.ts +0 -1437
  130. package/src/artifact-store.ts +0 -213
  131. package/src/build-windows-job.mjs +0 -95
  132. package/src/candidate-artifact.ts +0 -143
  133. package/src/candidate-patch.ts +0 -271
  134. package/src/candidate-workspace.ts +0 -80
  135. package/src/canonical.ts +0 -16
  136. package/src/change-contract-schema.ts +0 -191
  137. package/src/change-contract.ts +0 -324
  138. package/src/change-verification.ts +0 -1617
  139. package/src/child-budget.ts +0 -58
  140. package/src/child-model.ts +0 -142
  141. package/src/child-session.ts +0 -640
  142. package/src/contracts.ts +0 -2476
  143. package/src/control-contracts.ts +0 -382
  144. package/src/delivery-compiler.ts +0 -1805
  145. package/src/delivery-revision.ts +0 -42
  146. package/src/design-control.ts +0 -1299
  147. package/src/design-diagnostics.ts +0 -181
  148. package/src/design-journal.ts +0 -1773
  149. package/src/directory-sync.ts +0 -36
  150. package/src/durable-contracts.ts +0 -231
  151. package/src/durable-workflow.ts +0 -978
  152. package/src/evidence-draft.ts +0 -122
  153. package/src/execution-profile.ts +0 -64
  154. package/src/execution-retention.ts +0 -77
  155. package/src/file-snapshot.ts +0 -163
  156. package/src/implement-continuation.ts +0 -559
  157. package/src/implement-graph.ts +0 -690
  158. package/src/implement-plan.ts +0 -72
  159. package/src/install-config.d.mts +0 -6
  160. package/src/isolation-backend.ts +0 -353
  161. package/src/model-source.ts +0 -22
  162. package/src/openspec-cli.ts +0 -494
  163. package/src/operator-cli-entry.mjs +0 -24
  164. package/src/operator-tools.ts +0 -309
  165. package/src/package-candidate.ts +0 -227
  166. package/src/package-delivery.ts +0 -486
  167. package/src/package-state.ts +0 -320
  168. package/src/package-verification.ts +0 -635
  169. package/src/package-workflow.ts +0 -342
  170. package/src/packet-runtime.ts +0 -570
  171. package/src/parent-provider.ts +0 -375
  172. package/src/phase-execution.ts +0 -2259
  173. package/src/pi-adapter.ts +0 -19
  174. package/src/plan-draft-summary.ts +0 -527
  175. package/src/plan-draft.ts +0 -502
  176. package/src/remove-path.ts +0 -32
  177. package/src/route-policy.ts +0 -545
  178. package/src/run-state.ts +0 -420
  179. package/src/run-store.ts +0 -1179
  180. package/src/safe-path.ts +0 -77
  181. package/src/scoped-grep-worker.mjs +0 -23
  182. package/src/scoped-tools.ts +0 -775
  183. package/src/single-task-draft.ts +0 -142
  184. package/src/sqlite-schema.ts +0 -171
  185. package/src/state-root.ts +0 -215
  186. package/src/storage-schema.ts +0 -360
  187. package/src/subagent-activity.ts +0 -1417
  188. package/src/submit-tool.ts +0 -950
  189. package/src/task-ledger.ts +0 -1338
  190. package/src/transport-budget.ts +0 -52
  191. package/src/verification-capability.ts +0 -1191
  192. package/src/verification-environment-io.ts +0 -389
  193. package/src/verification-environment.ts +0 -162
  194. package/src/verification-identity.ts +0 -77
  195. package/src/verification-scan.ts +0 -64
  196. package/src/windows-job-backend.ts +0 -419
  197. package/src/windows-job.c +0 -149
  198. package/src/worker-broker.ts +0 -806
  199. package/src/workflow-engine.ts +0 -28
  200. package/src/workflow-policy.ts +0 -1679
  201. package/src/workflow-recovery-policy.ts +0 -97
  202. package/src/workflow-scheduling.ts +0 -105
  203. package/src/workflow-state-machine.ts +0 -4355
  204. package/src/workflow-status.ts +0 -576
  205. package/src/workspace-io-entry.mjs +0 -76
  206. package/src/workspace-io-worker.mjs +0 -1312
  207. package/src/workspace-io.ts +0 -89
  208. 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
+ }