reeval 0.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 (213) hide show
  1. package/LICENSE +202 -0
  2. package/THIRD_PARTY_NOTICES.md +2160 -0
  3. package/assets/package.json +9 -0
  4. package/assets/pnpm-lock.yaml +1336 -0
  5. package/dist/agent-DVHkx8aT.js +532 -0
  6. package/dist/agent-DVHkx8aT.js.map +1 -0
  7. package/dist/analysis-CxSCt1YY.js +16 -0
  8. package/dist/analysis-CxSCt1YY.js.map +1 -0
  9. package/dist/assemble-B-nmz8oH.js +695 -0
  10. package/dist/assemble-B-nmz8oH.js.map +1 -0
  11. package/dist/assemble-CmRVDZ2Q.js +2 -0
  12. package/dist/attempt-BdRQ9jKX.js +176 -0
  13. package/dist/attempt-BdRQ9jKX.js.map +1 -0
  14. package/dist/bridge/index.mjs +1911 -0
  15. package/dist/bridge/package.json +13 -0
  16. package/dist/bridge/pnpm-lock.yaml +1041 -0
  17. package/dist/bridge/pnpm-workspace.yaml +2 -0
  18. package/dist/budget-DBUrH4jX.js +8 -0
  19. package/dist/budget-DBUrH4jX.js.map +1 -0
  20. package/dist/cli--CQ3Iy9q.js +1865 -0
  21. package/dist/cli--CQ3Iy9q.js.map +1 -0
  22. package/dist/cli-spec-DKAu09Gi.js +478 -0
  23. package/dist/cli-spec-DKAu09Gi.js.map +1 -0
  24. package/dist/cli.d.ts +1 -0
  25. package/dist/cli.js +8 -0
  26. package/dist/cli.js.map +1 -0
  27. package/dist/colors-QV9dFVhN.js +34 -0
  28. package/dist/colors-QV9dFVhN.js.map +1 -0
  29. package/dist/command-BSJzHMtP.js +280 -0
  30. package/dist/command-BSJzHMtP.js.map +1 -0
  31. package/dist/command-CEm8PZ1N.js +1043 -0
  32. package/dist/command-CEm8PZ1N.js.map +1 -0
  33. package/dist/command-D-jnQWVG.js +673 -0
  34. package/dist/command-D-jnQWVG.js.map +1 -0
  35. package/dist/command-DQAvbG7d.js +432 -0
  36. package/dist/command-DQAvbG7d.js.map +1 -0
  37. package/dist/commands-A4udfyKZ.js +2 -0
  38. package/dist/commands-CF8mOfiu.js +207 -0
  39. package/dist/commands-CF8mOfiu.js.map +1 -0
  40. package/dist/config-Cuj0kVYj.js +415 -0
  41. package/dist/config-Cuj0kVYj.js.map +1 -0
  42. package/dist/context-loi9OCZv.js +106 -0
  43. package/dist/context-loi9OCZv.js.map +1 -0
  44. package/dist/core-tLN-z1Be.js +96 -0
  45. package/dist/core-tLN-z1Be.js.map +1 -0
  46. package/dist/db-Cjj2ZsfQ.js +2 -0
  47. package/dist/db-DxDzMhtB.js +827 -0
  48. package/dist/db-DxDzMhtB.js.map +1 -0
  49. package/dist/debug-D24NOQH1.js +89 -0
  50. package/dist/debug-D24NOQH1.js.map +1 -0
  51. package/dist/debug-SEmW4w8f.js +2 -0
  52. package/dist/dist-BZXYHN2X.js +1673 -0
  53. package/dist/dist-BZXYHN2X.js.map +1 -0
  54. package/dist/docker-DQDZWXF9.js +20 -0
  55. package/dist/docker-DQDZWXF9.js.map +1 -0
  56. package/dist/docker-DnWAVdEm.js +2 -0
  57. package/dist/docker-oSbCExHL.js +153 -0
  58. package/dist/docker-oSbCExHL.js.map +1 -0
  59. package/dist/doctor-CxlH1pDM.js +525 -0
  60. package/dist/doctor-CxlH1pDM.js.map +1 -0
  61. package/dist/egress-73SQfKdx.js +619 -0
  62. package/dist/egress-73SQfKdx.js.map +1 -0
  63. package/dist/env-BQqj93gu.js +21 -0
  64. package/dist/env-BQqj93gu.js.map +1 -0
  65. package/dist/environment-Mz7wvrtD.js +552 -0
  66. package/dist/environment-Mz7wvrtD.js.map +1 -0
  67. package/dist/errors-C9w_ud7g.js +160 -0
  68. package/dist/errors-C9w_ud7g.js.map +1 -0
  69. package/dist/errors-CSxfJ4qu.js +11 -0
  70. package/dist/errors-CSxfJ4qu.js.map +1 -0
  71. package/dist/files--6CEUXm7.js +2 -0
  72. package/dist/files-BLlrhU4-.js +62 -0
  73. package/dist/files-BLlrhU4-.js.map +1 -0
  74. package/dist/files-Bd25OAQH.js +2 -0
  75. package/dist/files-BvHCFWgD.js +2 -0
  76. package/dist/files-D2-qn_Dl.js +275 -0
  77. package/dist/files-D2-qn_Dl.js.map +1 -0
  78. package/dist/files-bF2fPuT9.js +99 -0
  79. package/dist/files-bF2fPuT9.js.map +1 -0
  80. package/dist/git-D6hMeieS.js +44 -0
  81. package/dist/git-D6hMeieS.js.map +1 -0
  82. package/dist/harness-ls5aX1UO.js +131 -0
  83. package/dist/harness-ls5aX1UO.js.map +1 -0
  84. package/dist/harness.d.ts +74 -0
  85. package/dist/harness.js +2 -0
  86. package/dist/header-DzFDoMCI.js +78 -0
  87. package/dist/header-DzFDoMCI.js.map +1 -0
  88. package/dist/ids-Bp6ut7ZD.js +49 -0
  89. package/dist/ids-Bp6ut7ZD.js.map +1 -0
  90. package/dist/images-Cc1-coFc.js +286 -0
  91. package/dist/images-Cc1-coFc.js.map +1 -0
  92. package/dist/index.d.ts +97 -0
  93. package/dist/index.js +1959 -0
  94. package/dist/index.js.map +1 -0
  95. package/dist/init-CFljS8BJ.js +344 -0
  96. package/dist/init-CFljS8BJ.js.map +1 -0
  97. package/dist/init-z2ZPn9Tj.js +2 -0
  98. package/dist/invocations-rXktgSAn.js +157 -0
  99. package/dist/invocations-rXktgSAn.js.map +1 -0
  100. package/dist/jiti-BOLXpKyG.js +46 -0
  101. package/dist/jiti-BOLXpKyG.js.map +1 -0
  102. package/dist/judge-D8Uf4dA9.js +146 -0
  103. package/dist/judge-D8Uf4dA9.js.map +1 -0
  104. package/dist/judge.d.ts +21 -0
  105. package/dist/judge.js +19 -0
  106. package/dist/judge.js.map +1 -0
  107. package/dist/live-Bs3LReMV.js +98 -0
  108. package/dist/live-Bs3LReMV.js.map +1 -0
  109. package/dist/matchers-DyLvm7EG.js +374 -0
  110. package/dist/matchers-DyLvm7EG.js.map +1 -0
  111. package/dist/model-BdO79gWz.js +44 -0
  112. package/dist/model-BdO79gWz.js.map +1 -0
  113. package/dist/model-CDL98xue.js +725 -0
  114. package/dist/model-CDL98xue.js.map +1 -0
  115. package/dist/model-v-0_Ik7S.js +2 -0
  116. package/dist/node-kG64XxdE.js +506 -0
  117. package/dist/node-kG64XxdE.js.map +1 -0
  118. package/dist/opencode-providers-DZuD_oYt.js +3149 -0
  119. package/dist/opencode-providers-DZuD_oYt.js.map +1 -0
  120. package/dist/options-CAQhu--C.js +452 -0
  121. package/dist/options-CAQhu--C.js.map +1 -0
  122. package/dist/patch-BkKje4cp.js +69 -0
  123. package/dist/patch-BkKje4cp.js.map +1 -0
  124. package/dist/paths-CBvm6pap.js +72 -0
  125. package/dist/paths-CBvm6pap.js.map +1 -0
  126. package/dist/plan-gwk6dJ8c.js +237 -0
  127. package/dist/plan-gwk6dJ8c.js.map +1 -0
  128. package/dist/preflight-BtubXKQ8.js +2 -0
  129. package/dist/preflight-BypZMNBG.js +185 -0
  130. package/dist/preflight-BypZMNBG.js.map +1 -0
  131. package/dist/prepare-Cwv0bHs_.js +3 -0
  132. package/dist/prepare-D6Uzcubb.js +213 -0
  133. package/dist/prepare-D6Uzcubb.js.map +1 -0
  134. package/dist/provenance-Dfb8YY5Y.js +129 -0
  135. package/dist/provenance-Dfb8YY5Y.js.map +1 -0
  136. package/dist/provider-BDbNOQhQ.js +2 -0
  137. package/dist/provider-CmAYTQQs.js +14 -0
  138. package/dist/provider-CmAYTQQs.js.map +1 -0
  139. package/dist/providers-2p3ylMHI.js +107 -0
  140. package/dist/providers-2p3ylMHI.js.map +1 -0
  141. package/dist/providers-BUOxitcT.js +2 -0
  142. package/dist/query-CFATHZmV.js +91 -0
  143. package/dist/query-CFATHZmV.js.map +1 -0
  144. package/dist/query-DR4m6CXf.js +2 -0
  145. package/dist/record-DQ8wzyco.js +99 -0
  146. package/dist/record-DQ8wzyco.js.map +1 -0
  147. package/dist/redact-C4fL_bZP.js +167 -0
  148. package/dist/redact-C4fL_bZP.js.map +1 -0
  149. package/dist/results-CeXb8Wde.js +37 -0
  150. package/dist/results-CeXb8Wde.js.map +1 -0
  151. package/dist/root-CFDjpWuc.js +110 -0
  152. package/dist/root-CFDjpWuc.js.map +1 -0
  153. package/dist/sandbox-By2gkdkC.js +133 -0
  154. package/dist/sandbox-By2gkdkC.js.map +1 -0
  155. package/dist/sandbox.d.ts +41 -0
  156. package/dist/sandbox.js +2 -0
  157. package/dist/server-Bi61uiY0.js +350 -0
  158. package/dist/server-Bi61uiY0.js.map +1 -0
  159. package/dist/server-CsSBL8NC.js +2 -0
  160. package/dist/source-BXzrmof5.js +2 -0
  161. package/dist/source-Bsfa3LZH.js +40 -0
  162. package/dist/source-Bsfa3LZH.js.map +1 -0
  163. package/dist/src-55j1zNHH.js +3 -0
  164. package/dist/src-AEwxd1UP.js +602 -0
  165. package/dist/src-AEwxd1UP.js.map +1 -0
  166. package/dist/src-BDwSuyIo.js +3 -0
  167. package/dist/src-BQyMiWri.js +490 -0
  168. package/dist/src-BQyMiWri.js.map +1 -0
  169. package/dist/src-C-LHyTSV.js +3 -0
  170. package/dist/src-Cmp-7YrD.js +1136 -0
  171. package/dist/src-Cmp-7YrD.js.map +1 -0
  172. package/dist/src-D0RdqpNb.js +252 -0
  173. package/dist/src-D0RdqpNb.js.map +1 -0
  174. package/dist/src-D8KYzxBX.js +793 -0
  175. package/dist/src-D8KYzxBX.js.map +1 -0
  176. package/dist/src-DEgff9Z7.js +4 -0
  177. package/dist/src-DXx_29pq.js +567 -0
  178. package/dist/src-DXx_29pq.js.map +1 -0
  179. package/dist/src-DwwiVLqv.js +2937 -0
  180. package/dist/src-DwwiVLqv.js.map +1 -0
  181. package/dist/src-N7E_mfcZ.js +2 -0
  182. package/dist/src-W9g-y5X5.js +3 -0
  183. package/dist/src-daS0wKH2.js +3 -0
  184. package/dist/state-Cyw3QTsN.js +2 -0
  185. package/dist/state-DZ7eqb1F.js +167 -0
  186. package/dist/state-DZ7eqb1F.js.map +1 -0
  187. package/dist/stats-BNAHlNMD.js +114 -0
  188. package/dist/stats-BNAHlNMD.js.map +1 -0
  189. package/dist/studio-app/assets/index-D6XyfFSH.js +58 -0
  190. package/dist/studio-app/assets/index-xwzWdcE_.css +2 -0
  191. package/dist/studio-app/favicon.svg +1 -0
  192. package/dist/studio-app/index.html +15 -0
  193. package/dist/summary-D7Ttky0z.js +166 -0
  194. package/dist/summary-D7Ttky0z.js.map +1 -0
  195. package/dist/text-gEcKjAyp.js +29 -0
  196. package/dist/text-gEcKjAyp.js.map +1 -0
  197. package/dist/types-C5vI3g1C.js +9 -0
  198. package/dist/types-C5vI3g1C.js.map +1 -0
  199. package/dist/types-DLec-6Cx.d.ts +1535 -0
  200. package/dist/usage-DsYUqIUM.js +24 -0
  201. package/dist/usage-DsYUqIUM.js.map +1 -0
  202. package/dist/view/index.d.ts +645 -0
  203. package/dist/view/index.js +8 -0
  204. package/dist/views-BrHOv6wg.js +220 -0
  205. package/dist/views-BrHOv6wg.js.map +1 -0
  206. package/dist/views-CwCWLWqo.js +3 -0
  207. package/dist/worker-entry-DnYkANPG.js +506 -0
  208. package/dist/worker-entry-DnYkANPG.js.map +1 -0
  209. package/dist/workspace-C2S0Z6gl.js +327 -0
  210. package/dist/workspace-C2S0Z6gl.js.map +1 -0
  211. package/dist/workspace-CyoXG6CY.js +2 -0
  212. package/docker/Dockerfile +19 -0
  213. package/package.json +79 -0
@@ -0,0 +1,1911 @@
1
+ // Modified by Invertase with patches/@ai-sdk__harness-claude-code@1.0.128.patch; see THIRD_PARTY_NOTICES.md.
2
+ // ../harness/dist/bridge/index.js
3
+ import { appendFile, mkdir, writeFile } from "fs/promises";
4
+ import { existsSync, readFileSync } from "fs";
5
+ import { randomUUID } from "crypto";
6
+ import { env as procEnv, pid, stdout } from "process";
7
+ import { WebSocketServer } from "ws";
8
+ var DEBUG_LEVEL_WEIGHT = {
9
+ error: 0,
10
+ warn: 1,
11
+ info: 2,
12
+ debug: 3,
13
+ trace: 4
14
+ };
15
+ function subsystemMatches(filters, subsystem) {
16
+ if (!filters || filters.length === 0) return true;
17
+ return filters.some(
18
+ (filter) => subsystem === filter || subsystem.startsWith(`${filter}.`)
19
+ );
20
+ }
21
+ function formatBridgeError(err) {
22
+ if (err instanceof Error) {
23
+ return { name: err.name, message: err.message, stack: err.stack };
24
+ }
25
+ if (typeof err === "string") {
26
+ return { message: err };
27
+ }
28
+ if (err !== null && typeof err === "object") {
29
+ try {
30
+ return { message: JSON.stringify(err) };
31
+ } catch {
32
+ }
33
+ }
34
+ return { message: String(err) };
35
+ }
36
+ function createBridgeUserMessageQueue(options) {
37
+ const messages = [];
38
+ const waiters = [];
39
+ const entries = /* @__PURE__ */ new Map();
40
+ let closed = false;
41
+ let pendingCount = 0;
42
+ const enqueue = (input) => {
43
+ const existing = entries.get(input.messageId);
44
+ if (existing != null) {
45
+ if (existing.response != null) {
46
+ options.respond(existing.response);
47
+ }
48
+ return;
49
+ }
50
+ let settled = false;
51
+ const settle = (response) => {
52
+ if (settled) return;
53
+ settled = true;
54
+ pendingCount--;
55
+ const entry = entries.get(input.messageId);
56
+ if (entry != null) entry.response = response;
57
+ options.respond(response);
58
+ };
59
+ const message = {
60
+ messageId: input.messageId,
61
+ text: input.text,
62
+ accept: () => {
63
+ settle({
64
+ type: "user-message-response",
65
+ messageId: input.messageId,
66
+ accepted: true
67
+ });
68
+ },
69
+ reject: (error) => {
70
+ settle({
71
+ type: "user-message-response",
72
+ messageId: input.messageId,
73
+ accepted: false,
74
+ error: { message: formatBridgeError(error).message }
75
+ });
76
+ }
77
+ };
78
+ entries.set(input.messageId, {
79
+ reject: message.reject
80
+ });
81
+ pendingCount++;
82
+ if (closed) {
83
+ message.reject(
84
+ new Error("The bridge turn is no longer accepting user messages.")
85
+ );
86
+ return;
87
+ }
88
+ const waiter = waiters.shift();
89
+ if (waiter != null) {
90
+ waiter({ done: false, value: message });
91
+ } else {
92
+ messages.push(message);
93
+ }
94
+ };
95
+ const close = (error) => {
96
+ if (closed) return;
97
+ closed = true;
98
+ const reason = error ?? new Error("The bridge turn ended before accepting the user message.");
99
+ for (const entry of entries.values()) {
100
+ if (entry.response == null) entry.reject(reason);
101
+ }
102
+ messages.length = 0;
103
+ while (waiters.length > 0) {
104
+ waiters.shift()({ done: true, value: void 0 });
105
+ }
106
+ };
107
+ return {
108
+ get pendingCount() {
109
+ return pendingCount;
110
+ },
111
+ enqueue,
112
+ close,
113
+ [Symbol.asyncIterator]() {
114
+ return {
115
+ next: () => {
116
+ const message = messages.shift();
117
+ if (message != null) {
118
+ return Promise.resolve({ done: false, value: message });
119
+ }
120
+ if (closed) {
121
+ return Promise.resolve({
122
+ done: true,
123
+ value: void 0
124
+ });
125
+ }
126
+ return new Promise(
127
+ (resolve) => {
128
+ waiters.push(resolve);
129
+ }
130
+ );
131
+ }
132
+ };
133
+ }
134
+ };
135
+ }
136
+ function parseEnvList(value) {
137
+ if (!value) return void 0;
138
+ const items = value.split(",").map((item) => item.trim()).filter(Boolean);
139
+ return items.length > 0 ? items : void 0;
140
+ }
141
+ var ENV_TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
142
+ var WS_OPEN = 1;
143
+ async function runBridge(options) {
144
+ const { bridgeType, bridgeStateDir: bridgeStateDir2, onStart, onStop, onDestroy } = options;
145
+ const teardownGraceMs = options.turnTeardownGraceMs ?? 1e4;
146
+ const expectedToken = options.token ?? procEnv.BRIDGE_CHANNEL_TOKEN ?? "";
147
+ const bridgeWsPort = options.port ?? parseInt(procEnv.BRIDGE_WS_PORT ?? "0", 10);
148
+ const bridgeMetaPath = `${bridgeStateDir2}/bridge-meta.json`;
149
+ const startConfigPath = `${bridgeStateDir2}/start-config.json`;
150
+ const rerunStartConfigPath = `${bridgeStateDir2}/rerun-start-config.json`;
151
+ const eventLogPath = `${bridgeStateDir2}/event-log.ndjson`;
152
+ try {
153
+ await mkdir(bridgeStateDir2, { recursive: true });
154
+ } catch {
155
+ }
156
+ let currentBoundPort = 0;
157
+ let currentTurnState = "init";
158
+ let activeSocket;
159
+ let isFirstTurn = true;
160
+ let turnAbort;
161
+ let currentUserMessages;
162
+ let activeTurn;
163
+ let debugConfig;
164
+ let consoleCaptureInstalled = false;
165
+ const envDebugEnabled = ENV_TRUTHY.has(
166
+ (procEnv.HARNESS_DEBUG ?? "").toLowerCase()
167
+ );
168
+ let seqCounter = 0;
169
+ let eventLog = [];
170
+ let diskBuffer = "";
171
+ let flushPromise = null;
172
+ const flushEventsToDisk = async () => {
173
+ while (diskBuffer.length > 0) {
174
+ const buf = diskBuffer;
175
+ diskBuffer = "";
176
+ await appendFile(eventLogPath, buf).catch(() => {
177
+ });
178
+ }
179
+ };
180
+ const scheduleEventFlush = () => {
181
+ if (flushPromise) return;
182
+ flushPromise = new Promise((resolve) => {
183
+ setImmediate(() => {
184
+ void flushEventsToDisk().finally(resolve);
185
+ });
186
+ }).finally(() => {
187
+ flushPromise = null;
188
+ if (diskBuffer.length > 0) {
189
+ scheduleEventFlush();
190
+ }
191
+ });
192
+ };
193
+ const flushPendingEventsToDisk = async () => {
194
+ if (diskBuffer.length > 0 && !flushPromise) {
195
+ scheduleEventFlush();
196
+ }
197
+ let inFlight = flushPromise;
198
+ while (inFlight) {
199
+ await inFlight;
200
+ inFlight = flushPromise;
201
+ }
202
+ };
203
+ const replayFromDisk = procEnv.BRIDGE_REPLAY_FROM_DISK === "1";
204
+ if (replayFromDisk && existsSync(eventLogPath)) {
205
+ try {
206
+ const lines = readFileSync(eventLogPath, "utf8").split("\n").map((line) => line.trim()).filter(Boolean);
207
+ eventLog = lines.map((line) => ({
208
+ seq: JSON.parse(line).seq,
209
+ line
210
+ }));
211
+ seqCounter = eventLog.at(-1)?.seq ?? 0;
212
+ } catch {
213
+ eventLog = [];
214
+ seqCounter = 0;
215
+ }
216
+ }
217
+ const pendingToolResults = /* @__PURE__ */ new Map();
218
+ const bufferedToolResults = [];
219
+ const pendingToolApprovals = /* @__PURE__ */ new Map();
220
+ const writeBridgeMeta = async (state) => {
221
+ try {
222
+ await writeFile(
223
+ bridgeMetaPath,
224
+ JSON.stringify({
225
+ type: bridgeType,
226
+ port: currentBoundPort,
227
+ state,
228
+ pid
229
+ })
230
+ );
231
+ } catch {
232
+ }
233
+ };
234
+ const writeStartConfig = async (start) => {
235
+ try {
236
+ const serialized = JSON.stringify(start);
237
+ await writeFile(startConfigPath, serialized);
238
+ if (!existsSync(rerunStartConfigPath)) {
239
+ await writeFile(rerunStartConfigPath, serialized);
240
+ }
241
+ } catch {
242
+ }
243
+ };
244
+ const emit = (event) => {
245
+ const seq = ++seqCounter;
246
+ const line = JSON.stringify({ ...event, seq });
247
+ eventLog.push({ seq, line });
248
+ diskBuffer += `${line}
249
+ `;
250
+ scheduleEventFlush();
251
+ if (activeSocket?.readyState === WS_OPEN) {
252
+ try {
253
+ activeSocket.send(line);
254
+ } catch {
255
+ }
256
+ }
257
+ };
258
+ const replay = (ws, afterSeq) => {
259
+ for (const entry of eventLog) {
260
+ if (entry.seq > afterSeq && ws.readyState === WS_OPEN) {
261
+ ws.send(entry.line);
262
+ }
263
+ }
264
+ };
265
+ const shouldEmitDebugEvent = (level, subsystem) => {
266
+ if (!debugConfig?.enabled) return false;
267
+ const threshold = debugConfig.level ?? "debug";
268
+ if (DEBUG_LEVEL_WEIGHT[level] > DEBUG_LEVEL_WEIGHT[threshold]) return false;
269
+ return subsystemMatches(debugConfig.subsystems, subsystem);
270
+ };
271
+ const rawStdoutWrite = process.stdout.write.bind(process.stdout);
272
+ const rawStderrWrite = process.stderr.write.bind(process.stderr);
273
+ const writeErrorToStderr = (input) => {
274
+ try {
275
+ const formatted = formatBridgeError(input.error);
276
+ rawStderrWrite(
277
+ `[harness:${bridgeType}:error] ${input.message}: ${formatted.message}
278
+ `
279
+ );
280
+ if (formatted.stack) {
281
+ rawStderrWrite(`${formatted.stack}
282
+ `);
283
+ }
284
+ } catch {
285
+ }
286
+ };
287
+ const emitWarning = (input) => {
288
+ try {
289
+ for (const line of input.message.split("\n")) {
290
+ if (line.trim().length > 0) {
291
+ rawStderrWrite(`[harness:${bridgeType}:warn] ${line}
292
+ `);
293
+ }
294
+ }
295
+ } catch {
296
+ }
297
+ };
298
+ const emitError = (input) => {
299
+ writeErrorToStderr({
300
+ message: input.message ?? "bridge error",
301
+ error: input.error
302
+ });
303
+ emit({ type: "error", error: serialiseError(input.error) });
304
+ };
305
+ const installConsoleCapture = () => {
306
+ if (consoleCaptureInstalled) return;
307
+ consoleCaptureInstalled = true;
308
+ const buffers = {
309
+ stdout: "",
310
+ stderr: ""
311
+ };
312
+ const patch = (stream, raw) => (chunk, encoding, cb) => {
313
+ if (debugConfig?.enabled) {
314
+ try {
315
+ const enc = typeof encoding === "string" ? encoding : "utf8";
316
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(
317
+ enc
318
+ );
319
+ const combined = buffers[stream] + text.replace(/\r\n/g, "\n");
320
+ const parts = combined.split("\n");
321
+ buffers[stream] = parts.pop() ?? "";
322
+ for (const line of parts) {
323
+ const trimmed = line.replace(/\s+$/, "");
324
+ if (trimmed) {
325
+ emit({
326
+ type: "sandbox-log",
327
+ source: bridgeType,
328
+ stream,
329
+ line: trimmed
330
+ });
331
+ }
332
+ }
333
+ } catch {
334
+ }
335
+ }
336
+ return raw(
337
+ chunk,
338
+ encoding,
339
+ cb
340
+ );
341
+ };
342
+ process.stdout.write = patch(
343
+ "stdout",
344
+ rawStdoutWrite
345
+ );
346
+ process.stderr.write = patch(
347
+ "stderr",
348
+ rawStderrWrite
349
+ );
350
+ };
351
+ const handleInbound = async (msg, ws) => {
352
+ switch (msg.type) {
353
+ case "start": {
354
+ for (; ; ) {
355
+ const pendingTurn = activeTurn;
356
+ if (pendingTurn == null) break;
357
+ turnAbort?.abort();
358
+ currentUserMessages?.close(
359
+ new Error("A new bridge turn replaced the active turn.")
360
+ );
361
+ let graceTimer;
362
+ const settled = await Promise.race([
363
+ pendingTurn.then(() => true),
364
+ new Promise((resolve) => {
365
+ graceTimer = setTimeout(() => resolve(false), teardownGraceMs);
366
+ graceTimer.unref?.();
367
+ })
368
+ ]);
369
+ clearTimeout(graceTimer);
370
+ if (!settled) break;
371
+ }
372
+ let turnFinished;
373
+ const thisTurn = new Promise((resolve) => turnFinished = resolve);
374
+ activeTurn = thisTurn;
375
+ activeSocket = ws;
376
+ const firstTurn = isFirstTurn;
377
+ isFirstTurn = false;
378
+ eventLog = [];
379
+ diskBuffer = "";
380
+ void writeFile(eventLogPath, "").catch(() => {
381
+ });
382
+ turnAbort = new AbortController();
383
+ currentTurnState = "running";
384
+ void writeStartConfig(msg);
385
+ void writeBridgeMeta("running");
386
+ const startDebug = msg.debug;
387
+ debugConfig = {
388
+ enabled: startDebug?.enabled ?? envDebugEnabled,
389
+ level: startDebug?.level ?? procEnv.HARNESS_DEBUG_LEVEL,
390
+ subsystems: startDebug?.subsystems ?? parseEnvList(procEnv.HARNESS_DEBUG_SUBSYSTEMS)
391
+ };
392
+ if (debugConfig.enabled) {
393
+ installConsoleCapture();
394
+ }
395
+ const userMessages = createBridgeUserMessageQueue({ respond: emit });
396
+ const turn = {
397
+ emit,
398
+ requestToolResult: (requestInput) => {
399
+ const request = typeof requestInput === "string" ? { toolCallId: requestInput } : requestInput;
400
+ const bufferedIndex = bufferedToolResults.findIndex(
401
+ (buffered) => buffered.toolCallId === request.toolCallId || request.matches?.(buffered.result) === true
402
+ );
403
+ if (bufferedIndex >= 0) {
404
+ return Promise.resolve(
405
+ bufferedToolResults.splice(bufferedIndex, 1)[0].result
406
+ );
407
+ }
408
+ return new Promise((resolve) => {
409
+ pendingToolResults.set(request.toolCallId, {
410
+ resolve,
411
+ matches: request.matches
412
+ });
413
+ });
414
+ },
415
+ requestToolApproval: (approvalId) => new Promise((resolve) => {
416
+ pendingToolApprovals.set(approvalId, resolve);
417
+ }),
418
+ experimental_userMessages: userMessages,
419
+ abortSignal: turnAbort.signal,
420
+ firstTurn,
421
+ bridgeLog: (input) => {
422
+ const level = input.level ?? "debug";
423
+ if (!shouldEmitDebugEvent(level, input.subsystem)) return;
424
+ emit({
425
+ type: "debug-event",
426
+ level,
427
+ subsystem: input.subsystem,
428
+ message: input.message,
429
+ ...input.attrs ? { attrs: input.attrs } : {},
430
+ ...input.error !== void 0 ? { error: formatBridgeError(input.error) } : {}
431
+ });
432
+ },
433
+ emitWarning,
434
+ emitError
435
+ };
436
+ currentUserMessages = userMessages;
437
+ try {
438
+ await onStart(msg, turn);
439
+ } catch (err) {
440
+ emitError({ error: err, message: "bridge turn failed" });
441
+ } finally {
442
+ userMessages.close();
443
+ if (currentUserMessages === userMessages) {
444
+ currentUserMessages = void 0;
445
+ }
446
+ if (activeTurn === thisTurn) {
447
+ activeTurn = void 0;
448
+ currentTurnState = "waiting";
449
+ void writeBridgeMeta("waiting");
450
+ }
451
+ turnFinished();
452
+ }
453
+ return;
454
+ }
455
+ case "tool-result": {
456
+ const result = {
457
+ output: msg.output,
458
+ isError: msg.isError,
459
+ toolResult: msg.toolResult
460
+ };
461
+ const exactPending = pendingToolResults.get(msg.toolCallId);
462
+ const matchingPending = exactPending == null ? Array.from(pendingToolResults.entries()).find(
463
+ ([, pending2]) => pending2.matches?.(result) === true
464
+ ) : void 0;
465
+ const pending = exactPending ?? matchingPending?.[1];
466
+ const pendingId = exactPending != null ? msg.toolCallId : matchingPending?.[0];
467
+ if (pending != null && pendingId != null) {
468
+ pendingToolResults.delete(pendingId);
469
+ pending.resolve(result);
470
+ } else {
471
+ bufferedToolResults.push({
472
+ toolCallId: msg.toolCallId,
473
+ result
474
+ });
475
+ }
476
+ return;
477
+ }
478
+ case "tool-approval-response": {
479
+ const resolver = pendingToolApprovals.get(msg.approvalId);
480
+ if (resolver) {
481
+ pendingToolApprovals.delete(msg.approvalId);
482
+ resolver({ approved: msg.approved, reason: msg.reason });
483
+ }
484
+ return;
485
+ }
486
+ case "user-message": {
487
+ const messageId = msg.messageId ?? randomUUID();
488
+ if (currentUserMessages == null) {
489
+ sendControl(ws, {
490
+ type: "user-message-response",
491
+ messageId,
492
+ accepted: false,
493
+ error: { message: "The bridge has no active turn to steer." }
494
+ });
495
+ return;
496
+ }
497
+ if (ws !== activeSocket) {
498
+ sendControl(ws, {
499
+ type: "user-message-response",
500
+ messageId,
501
+ accepted: false,
502
+ error: {
503
+ message: "The connection does not own the active bridge turn."
504
+ }
505
+ });
506
+ return;
507
+ }
508
+ currentUserMessages.enqueue({
509
+ messageId,
510
+ text: msg.text
511
+ });
512
+ return;
513
+ }
514
+ case "abort":
515
+ turnAbort?.abort();
516
+ return;
517
+ case "resume":
518
+ activeSocket = ws;
519
+ replay(ws, msg.lastSeenEventId);
520
+ return;
521
+ case "destroy":
522
+ currentTurnState = "done";
523
+ void writeBridgeMeta("done");
524
+ await onDestroy?.();
525
+ drainThenExit(ws, 1e3, "destroy");
526
+ return;
527
+ case "stop": {
528
+ currentTurnState = "done";
529
+ void writeBridgeMeta("done");
530
+ const data = await onStop?.() ?? {};
531
+ sendControl(ws, { type: "bridge-stop", data });
532
+ drainThenExit(ws, 1e3, "stop");
533
+ }
534
+ }
535
+ };
536
+ void writeBridgeMeta("init");
537
+ const wss = new WebSocketServer({ port: bridgeWsPort, host: "0.0.0.0" });
538
+ const exit = () => {
539
+ if (options.onExit) {
540
+ options.onExit();
541
+ return;
542
+ }
543
+ wss.close(() => process.exit(0));
544
+ setTimeout(() => process.exit(0), 1e3).unref();
545
+ };
546
+ const drainThenExit = (ws, code, reason) => {
547
+ const start = Date.now();
548
+ const tick = () => {
549
+ const drained = ws.bufferedAmount === 0 || ws.readyState !== WS_OPEN;
550
+ if (drained || Date.now() - start >= 5e3) {
551
+ void flushPendingEventsToDisk().finally(() => {
552
+ try {
553
+ ws.close(code, reason);
554
+ } finally {
555
+ exit();
556
+ }
557
+ });
558
+ return;
559
+ }
560
+ setTimeout(tick, 10).unref();
561
+ };
562
+ tick();
563
+ };
564
+ wss.on("listening", () => {
565
+ const addr = wss.address();
566
+ currentBoundPort = typeof addr === "object" && addr ? addr.port : 0;
567
+ currentTurnState = "waiting";
568
+ void writeBridgeMeta("waiting");
569
+ stdout.write(
570
+ JSON.stringify({
571
+ type: "bridge-ready",
572
+ port: currentBoundPort
573
+ }) + "\n"
574
+ );
575
+ options.onListening?.(currentBoundPort);
576
+ });
577
+ wss.on("connection", (ws, req) => {
578
+ const url = new URL(req.url ?? "/", "http://localhost");
579
+ if (url.searchParams.get("agent_bridge_token") !== expectedToken) {
580
+ ws.close(1008, "unauthorized");
581
+ return;
582
+ }
583
+ sendControl(ws, {
584
+ type: "bridge-hello",
585
+ state: currentTurnState,
586
+ lastSeq: seqCounter,
587
+ capabilities: { experimental_userMessageResponses: true }
588
+ });
589
+ ws.on("message", (raw) => {
590
+ let parsed;
591
+ try {
592
+ const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
593
+ parsed = JSON.parse(text);
594
+ } catch (err) {
595
+ sendControl(ws, {
596
+ type: "error",
597
+ error: `protocol parse error: ${err.message}`
598
+ });
599
+ return;
600
+ }
601
+ void handleInbound(parsed, ws);
602
+ });
603
+ ws.on("close", () => {
604
+ if (activeSocket === ws) {
605
+ activeSocket = void 0;
606
+ }
607
+ });
608
+ ws.on("error", () => {
609
+ });
610
+ });
611
+ process.on("uncaughtException", (err) => {
612
+ emitError({ error: err, message: "uncaught exception" });
613
+ });
614
+ process.on("unhandledRejection", (err) => {
615
+ emitError({ error: err, message: "unhandled rejection" });
616
+ });
617
+ await new Promise((resolve, reject) => {
618
+ if (wss.address() != null) {
619
+ resolve();
620
+ return;
621
+ }
622
+ wss.once("listening", resolve);
623
+ wss.once("error", reject);
624
+ });
625
+ return {
626
+ port: currentBoundPort,
627
+ close: () => new Promise((resolve) => {
628
+ wss.close(() => resolve());
629
+ })
630
+ };
631
+ }
632
+ function sendControl(socket, message) {
633
+ if (socket?.readyState === WS_OPEN) {
634
+ try {
635
+ socket.send(JSON.stringify(message));
636
+ } catch {
637
+ }
638
+ }
639
+ }
640
+ function serialiseError(err) {
641
+ if (err instanceof Error) {
642
+ return { name: err.name, message: err.message, stack: err.stack };
643
+ }
644
+ return err;
645
+ }
646
+
647
+ // src/bridge/compaction-latch.ts
648
+ function createCompactionLatch(emit) {
649
+ let boundary;
650
+ let summary;
651
+ const tryEmit = () => {
652
+ if (!boundary || summary === void 0) return;
653
+ emit({
654
+ type: "compaction",
655
+ trigger: boundary.trigger,
656
+ summary,
657
+ ...boundary.tokensBefore !== void 0 ? { tokensBefore: boundary.tokensBefore } : {},
658
+ ...boundary.tokensAfter !== void 0 ? { tokensAfter: boundary.tokensAfter } : {}
659
+ });
660
+ boundary = void 0;
661
+ summary = void 0;
662
+ };
663
+ return {
664
+ onBoundary(next) {
665
+ boundary = next;
666
+ tryEmit();
667
+ },
668
+ onSummary(next) {
669
+ summary = next;
670
+ tryEmit();
671
+ }
672
+ };
673
+ }
674
+
675
+ // src/bridge/index.ts
676
+ import { randomUUID as randomUUID3 } from "crypto";
677
+ import { argv, env as procEnv2, stdout as stdout2 } from "process";
678
+ import * as claudeAgentSdk from "@anthropic-ai/claude-agent-sdk";
679
+ import * as mcpServerModule from "@modelcontextprotocol/sdk/server/mcp.js";
680
+
681
+ // src/bridge/claude-code-system-prompt.ts
682
+ function createClaudeCodeSystemPrompt(instructions) {
683
+ return {
684
+ type: "preset",
685
+ preset: "claude_code",
686
+ ...instructions ? { append: instructions } : {}
687
+ };
688
+ }
689
+
690
+ // src/bridge/claude-skills-option.ts
691
+ function toClaudeSkillsOption(skills) {
692
+ return skills && skills.length > 0 ? "all" : void 0;
693
+ }
694
+
695
+ // src/bridge/create-emit-stream-event.ts
696
+ import { randomUUID as randomUUID2 } from "crypto";
697
+ function createClaudeStreamEventState() {
698
+ return {
699
+ nativeToolCallNames: /* @__PURE__ */ new Map(),
700
+ approvalRequestedToolUseIds: /* @__PURE__ */ new Set(),
701
+ partialBlocks: /* @__PURE__ */ new Map(),
702
+ stepUsage: void 0,
703
+ pendingStepToolUseIds: /* @__PURE__ */ new Set(),
704
+ pendingStepAssistantUsage: void 0,
705
+ pendingStepDeltaUsage: void 0,
706
+ pendingStepUsage: void 0,
707
+ stepOpen: false,
708
+ mcpToolUseIds: /* @__PURE__ */ new Set(),
709
+ externalMcpToolUseIds: /* @__PURE__ */ new Set(),
710
+ structuredOutputToolUseIds: /* @__PURE__ */ new Set(),
711
+ observedTerminalError: void 0
712
+ };
713
+ }
714
+ var UNRECOVERABLE_API_RETRY_STATUSES = /* @__PURE__ */ new Set([401, 403, 404]);
715
+ var HOST_TOOL_PREFIX = "mcp__harness-tools__";
716
+ function isExternalMcpTool(nativeName) {
717
+ return nativeName.startsWith("mcp__") && !nativeName.startsWith(HOST_TOOL_PREFIX);
718
+ }
719
+ function createEmitStreamEvent({
720
+ state,
721
+ emit,
722
+ emitWarning,
723
+ emitTerminalError,
724
+ onCompactionBoundary,
725
+ toCommonName: toCommonName2
726
+ }) {
727
+ let streamStarted = false;
728
+ return (msg) => {
729
+ const type = msg.type;
730
+ if (!streamStarted && type !== "command_lifecycle") {
731
+ const initModel = type === "system" && msg.subtype === "init" && typeof msg.model === "string" ? msg.model : void 0;
732
+ emit({
733
+ type: "stream-start",
734
+ ...initModel ? { modelId: initModel } : {}
735
+ });
736
+ streamStarted = true;
737
+ }
738
+ if (type === "system" && msg.subtype === "api_retry") {
739
+ if (typeof msg.error_status === "number" && UNRECOVERABLE_API_RETRY_STATUSES.has(msg.error_status)) {
740
+ emitTerminalError(
741
+ `HTTP ${msg.error_status}: ${msg.error ?? "provider request failed"}`
742
+ );
743
+ return;
744
+ }
745
+ emitWarning({ message: formatApiRetryWarning(msg) });
746
+ return;
747
+ }
748
+ if (typeof msg.error === "string" && msg.error.trim()) {
749
+ state.observedTerminalError = msg.error.trim();
750
+ }
751
+ if (type === "auth_status" && typeof msg.error === "string" && msg.error.trim()) {
752
+ emitTerminalError(msg.error);
753
+ return;
754
+ }
755
+ if (type === "system" && msg.subtype === "task_updated" && msg.patch?.status === "failed" && typeof msg.patch.error === "string") {
756
+ emitTerminalError(msg.patch.error);
757
+ return;
758
+ }
759
+ if (type === "system" && msg.subtype === "compact_boundary") {
760
+ const meta = msg.compact_metadata;
761
+ if (meta) {
762
+ onCompactionBoundary({
763
+ trigger: meta.trigger,
764
+ ...typeof meta.pre_tokens === "number" ? { tokensBefore: meta.pre_tokens } : {},
765
+ ...typeof meta.post_tokens === "number" ? { tokensAfter: meta.post_tokens } : {}
766
+ });
767
+ }
768
+ return;
769
+ }
770
+ if (msg.parent_tool_use_id != null) {
771
+ return;
772
+ }
773
+ if (type === "stream_event") {
774
+ handleStreamEvent({
775
+ event: msg.event,
776
+ state,
777
+ send: emit,
778
+ toCommonName: toCommonName2
779
+ });
780
+ return;
781
+ }
782
+ const messageContent = msg.message?.content;
783
+ if (type === "assistant" && Array.isArray(messageContent)) {
784
+ const usage = toUsageRecord(msg.message?.usage);
785
+ const toolUseIds = [];
786
+ let opensStep = false;
787
+ for (const block of messageContent) {
788
+ if (block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") {
789
+ toolUseIds.push(block.id);
790
+ if (block.name === "StructuredOutput") {
791
+ state.structuredOutputToolUseIds.add(block.id);
792
+ continue;
793
+ }
794
+ if (block.name.startsWith(HOST_TOOL_PREFIX)) {
795
+ state.pendingStepToolUseIds.add(block.id);
796
+ state.mcpToolUseIds.add(block.id);
797
+ opensStep = true;
798
+ continue;
799
+ }
800
+ state.nativeToolCallNames.set(block.id, block.name);
801
+ if (block.name === "AskUserQuestion") {
802
+ state.pendingStepToolUseIds.add(block.id);
803
+ opensStep = true;
804
+ continue;
805
+ }
806
+ const dynamic = isExternalMcpTool(block.name);
807
+ if (dynamic) state.externalMcpToolUseIds.add(block.id);
808
+ if (state.approvalRequestedToolUseIds.has(block.id)) {
809
+ continue;
810
+ }
811
+ state.pendingStepToolUseIds.add(block.id);
812
+ opensStep = true;
813
+ emit({
814
+ type: "tool-call",
815
+ toolCallId: block.id,
816
+ toolName: toCommonName2(block.name),
817
+ nativeName: block.name,
818
+ input: JSON.stringify(block.input ?? {}),
819
+ providerExecuted: true,
820
+ ...dynamic ? { dynamic: true } : {}
821
+ });
822
+ }
823
+ }
824
+ if (opensStep || toolUseIds.length === 0) {
825
+ state.stepOpen = true;
826
+ if (usage) {
827
+ state.pendingStepAssistantUsage = usage;
828
+ updatePendingStepUsage(state);
829
+ }
830
+ }
831
+ return;
832
+ }
833
+ if (type === "user" && Array.isArray(messageContent)) {
834
+ const toolResultBlocks = messageContent.filter(
835
+ (block) => block.type === "tool_result"
836
+ );
837
+ const toolUseResult = toolResultBlocks.length === 1 ? msg.tool_use_result : void 0;
838
+ for (const block of messageContent) {
839
+ if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
840
+ if (state.structuredOutputToolUseIds.delete(block.tool_use_id)) {
841
+ continue;
842
+ }
843
+ if (state.mcpToolUseIds.has(block.tool_use_id)) {
844
+ state.mcpToolUseIds.delete(block.tool_use_id);
845
+ state.pendingStepToolUseIds.delete(block.tool_use_id);
846
+ continue;
847
+ }
848
+ state.approvalRequestedToolUseIds.delete(block.tool_use_id);
849
+ const nativeName = state.nativeToolCallNames.get(block.tool_use_id) ?? "unknown";
850
+ state.nativeToolCallNames.delete(block.tool_use_id);
851
+ const toolName = toCommonName2(nativeName);
852
+ const dynamic = state.externalMcpToolUseIds.delete(block.tool_use_id);
853
+ const isError = !!block.is_error;
854
+ const result = toolUseResult !== void 0 ? toolUseResult : resolveToolResult({
855
+ toolName,
856
+ dynamic,
857
+ isError,
858
+ rawContent: block.content
859
+ });
860
+ emit({
861
+ type: "tool-result",
862
+ toolCallId: block.tool_use_id,
863
+ toolName,
864
+ result,
865
+ isError,
866
+ ...dynamic ? { dynamic: true } : {}
867
+ });
868
+ state.pendingStepToolUseIds.delete(block.tool_use_id);
869
+ }
870
+ }
871
+ closeStepIfReady({ state, emit });
872
+ }
873
+ };
874
+ }
875
+ function finishApprovalStep({
876
+ state,
877
+ emit,
878
+ approvalId
879
+ }) {
880
+ state.stepOpen = true;
881
+ state.pendingStepToolUseIds.delete(approvalId);
882
+ closeStepIfReady({ state, emit });
883
+ }
884
+ function emitFinishStep({
885
+ state,
886
+ emit,
887
+ usage
888
+ }) {
889
+ emit({
890
+ type: "finish-step",
891
+ finishReason: { unified: "stop", raw: "stop" },
892
+ usage: usage ?? defaultUsage()
893
+ });
894
+ state.stepUsage = usage ?? state.stepUsage;
895
+ state.pendingStepAssistantUsage = void 0;
896
+ state.pendingStepDeltaUsage = void 0;
897
+ state.pendingStepUsage = void 0;
898
+ state.pendingStepToolUseIds = /* @__PURE__ */ new Set();
899
+ state.stepOpen = false;
900
+ }
901
+ function closeStepIfReady({
902
+ state,
903
+ emit
904
+ }) {
905
+ if (!state.stepOpen || state.pendingStepToolUseIds.size > 0 || state.partialBlocks.size > 0) {
906
+ return;
907
+ }
908
+ emitFinishStep({ state, emit, usage: state.pendingStepUsage });
909
+ }
910
+ function formatApiRetryWarning(msg) {
911
+ const details = [];
912
+ if (typeof msg.attempt === "number") {
913
+ const maxRetries = typeof msg.max_retries === "number" ? `/${msg.max_retries}` : "";
914
+ details.push(`attempt ${msg.attempt}${maxRetries}`);
915
+ }
916
+ if (typeof msg.error_status === "number") {
917
+ details.push(`HTTP ${msg.error_status}`);
918
+ }
919
+ if (typeof msg.retry_delay_ms === "number") {
920
+ details.push(`retrying in ${msg.retry_delay_ms}ms`);
921
+ }
922
+ if (msg.error) details.push(msg.error);
923
+ return details.length > 0 ? `Claude Code API retry: ${details.join("; ")}` : "Claude Code API retry";
924
+ }
925
+ function handleStreamEvent({
926
+ event,
927
+ state,
928
+ send,
929
+ toCommonName: toCommonName2
930
+ }) {
931
+ if (!event) return;
932
+ if (event.type === "message_delta") {
933
+ const usage = toUsageRecord(event.usage);
934
+ if (usage) {
935
+ state.pendingStepDeltaUsage = mergeNonNullUsage(
936
+ state.pendingStepDeltaUsage,
937
+ usage
938
+ );
939
+ updatePendingStepUsage(state);
940
+ }
941
+ return;
942
+ }
943
+ if (typeof event.index !== "number") return;
944
+ const index = event.index;
945
+ const partialBlocks = state.partialBlocks;
946
+ if (event.type === "content_block_start") {
947
+ const blockType = event.content_block?.type;
948
+ if (blockType === "text") {
949
+ const id = randomUUID2();
950
+ partialBlocks.set(index, { id, kind: "text" });
951
+ send({ type: "text-start", id });
952
+ } else if (blockType === "thinking") {
953
+ const id = randomUUID2();
954
+ partialBlocks.set(index, { id, kind: "thinking" });
955
+ send({ type: "reasoning-start", id });
956
+ } else if (blockType === "tool_use" && typeof event.content_block?.id === "string" && typeof event.content_block.name === "string") {
957
+ const id = event.content_block.id;
958
+ const nativeName = event.content_block.name;
959
+ if (nativeName === "StructuredOutput") {
960
+ return;
961
+ }
962
+ if (nativeName === "AskUserQuestion") {
963
+ return;
964
+ }
965
+ const hostToolName = nativeName.startsWith(HOST_TOOL_PREFIX) ? nativeName.slice(HOST_TOOL_PREFIX.length) : void 0;
966
+ const dynamic = isExternalMcpTool(nativeName);
967
+ partialBlocks.set(index, { id, kind: "tool-input" });
968
+ send({
969
+ type: "tool-input-start",
970
+ id,
971
+ toolName: hostToolName ?? toCommonName2(nativeName),
972
+ providerExecuted: hostToolName === void 0,
973
+ ...dynamic ? { dynamic: true } : {}
974
+ });
975
+ }
976
+ return;
977
+ }
978
+ if (event.type === "content_block_delta") {
979
+ const block = partialBlocks.get(index);
980
+ if (!block) return;
981
+ if (block.kind === "text" && event.delta?.type === "text_delta" && typeof event.delta.text === "string") {
982
+ send({ type: "text-delta", id: block.id, delta: event.delta.text });
983
+ } else if (block.kind === "thinking" && event.delta?.type === "thinking_delta" && typeof event.delta.thinking === "string") {
984
+ send({
985
+ type: "reasoning-delta",
986
+ id: block.id,
987
+ delta: event.delta.thinking
988
+ });
989
+ } else if (block.kind === "tool-input" && event.delta?.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
990
+ send({
991
+ type: "tool-input-delta",
992
+ id: block.id,
993
+ delta: event.delta.partial_json
994
+ });
995
+ }
996
+ return;
997
+ }
998
+ if (event.type === "content_block_stop") {
999
+ const block = partialBlocks.get(index);
1000
+ if (!block) return;
1001
+ partialBlocks.delete(index);
1002
+ if (block.kind === "text") {
1003
+ send({ type: "text-end", id: block.id });
1004
+ } else if (block.kind === "thinking") {
1005
+ send({ type: "reasoning-end", id: block.id });
1006
+ } else {
1007
+ send({ type: "tool-input-end", id: block.id });
1008
+ }
1009
+ }
1010
+ }
1011
+ function toUsageRecord(usage) {
1012
+ return usage != null && typeof usage === "object" ? usage : void 0;
1013
+ }
1014
+ function mergeNonNullUsage(current, update) {
1015
+ const merged = { ...current };
1016
+ for (const [key, value] of Object.entries(update)) {
1017
+ if (value != null) {
1018
+ merged[key] = value;
1019
+ }
1020
+ }
1021
+ return merged;
1022
+ }
1023
+ function updatePendingStepUsage(state) {
1024
+ const assistantUsage = state.pendingStepAssistantUsage;
1025
+ const deltaUsage = state.pendingStepDeltaUsage;
1026
+ state.pendingStepUsage = mapUsage(
1027
+ assistantUsage || deltaUsage ? { ...assistantUsage, ...deltaUsage } : void 0
1028
+ );
1029
+ }
1030
+ function isTextEntry(entry) {
1031
+ return entry != null && typeof entry === "object" && "text" in entry;
1032
+ }
1033
+ function stringifyContent(content) {
1034
+ if (typeof content === "string") return content;
1035
+ if (Array.isArray(content)) {
1036
+ return content.map(
1037
+ (entry) => isTextEntry(entry) ? String(entry.text ?? "") : JSON.stringify(entry)
1038
+ ).join("");
1039
+ }
1040
+ return JSON.stringify(content);
1041
+ }
1042
+ function hasNonTextContent(content) {
1043
+ return Array.isArray(content) && content.some((entry) => !isTextEntry(entry));
1044
+ }
1045
+ function resolveToolResult({
1046
+ toolName,
1047
+ dynamic,
1048
+ isError,
1049
+ rawContent
1050
+ }) {
1051
+ if (toolName === "bash") {
1052
+ return { exitCode: isError ? 1 : 0, stdout: stringifyContent(rawContent) };
1053
+ }
1054
+ if (hasNonTextContent(rawContent)) return rawContent;
1055
+ const content = stringifyContent(rawContent);
1056
+ return dynamic ? parseMcpToolResult(content) : content;
1057
+ }
1058
+ function parseMcpToolResult(content) {
1059
+ try {
1060
+ const parsed = JSON.parse(content);
1061
+ return parsed !== null && typeof parsed === "object" ? parsed : content;
1062
+ } catch {
1063
+ return content;
1064
+ }
1065
+ }
1066
+ function mapUsage(usage) {
1067
+ if (!usage || typeof usage !== "object") return void 0;
1068
+ const u = usage;
1069
+ return {
1070
+ inputTokens: {
1071
+ total: (u.input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0) + (u.cache_read_input_tokens ?? 0),
1072
+ noCache: u.input_tokens ?? 0,
1073
+ cacheRead: u.cache_read_input_tokens ?? 0,
1074
+ cacheWrite: u.cache_creation_input_tokens ?? 0
1075
+ },
1076
+ outputTokens: {
1077
+ total: u.output_tokens ?? 0,
1078
+ text: u.output_tokens ?? 0
1079
+ }
1080
+ };
1081
+ }
1082
+ function defaultUsage() {
1083
+ return {
1084
+ inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
1085
+ outputTokens: { total: 0, text: 0 }
1086
+ };
1087
+ }
1088
+
1089
+ // src/bridge/json-schema-to-zod.ts
1090
+ import { z } from "zod/v4";
1091
+ function jsonSchemaToZodObject(input) {
1092
+ const schema = isJsonSchemaObject(input) ? input : {};
1093
+ return toZodObject(schema);
1094
+ }
1095
+ function toZodObject(schema) {
1096
+ const object = z.object(toZodShape(schema));
1097
+ if (schema.additionalProperties === false) return object.strict();
1098
+ return object.catchall(
1099
+ isJsonSchemaObject(schema.additionalProperties) ? toZodType(schema.additionalProperties) : z.unknown()
1100
+ );
1101
+ }
1102
+ function toZodShape(schema) {
1103
+ if (!schema?.properties) return {};
1104
+ const required = new Set(schema.required);
1105
+ const shape = {};
1106
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
1107
+ const propType = toZodType(propSchema);
1108
+ let propertyType = required.has(key) ? propType : propType.optional();
1109
+ if (propSchema.description) {
1110
+ propertyType = propertyType.describe(propSchema.description);
1111
+ }
1112
+ shape[key] = propertyType;
1113
+ }
1114
+ return shape;
1115
+ }
1116
+ function toZodType(schema) {
1117
+ if (!schema) return z.any();
1118
+ let zType = zodForConst(schema) ?? zodForEnum(schema) ?? zodForUnion(schema) ?? zodForType(schema);
1119
+ if (isNullable(schema)) zType = zType.nullable();
1120
+ if (schema.description) zType = zType.describe(schema.description);
1121
+ return zType;
1122
+ }
1123
+ function zodForType(schema) {
1124
+ const types = getNonNullTypes(schema);
1125
+ if (types.length > 1) return z.any();
1126
+ if (Array.isArray(schema.type) && types.length === 0) return z.null();
1127
+ const type = types[0] ?? (schema.properties ? "object" : void 0);
1128
+ switch (type) {
1129
+ case "string":
1130
+ return z.string();
1131
+ case "number":
1132
+ return z.number();
1133
+ case "integer":
1134
+ return z.number().int();
1135
+ case "boolean":
1136
+ return z.boolean();
1137
+ case "array":
1138
+ return z.array(
1139
+ Array.isArray(schema.items) ? z.any() : toZodType(schema.items)
1140
+ );
1141
+ case "object":
1142
+ return toZodObject(schema);
1143
+ case "null":
1144
+ return z.null();
1145
+ default:
1146
+ return z.any();
1147
+ }
1148
+ }
1149
+ function zodForConst(schema) {
1150
+ if (!("const" in schema)) return void 0;
1151
+ return isJsonLiteral(schema.const) ? z.literal(schema.const) : z.any();
1152
+ }
1153
+ function zodForEnum(schema) {
1154
+ if (!Array.isArray(schema.enum)) return void 0;
1155
+ if (!schema.enum.every(isJsonLiteral)) return z.any();
1156
+ return zodForLiterals(schema.enum);
1157
+ }
1158
+ function zodForUnion(schema) {
1159
+ const unionSchemas = schema.anyOf ?? schema.oneOf;
1160
+ if (!unionSchemas || unionSchemas.length < 2) return void 0;
1161
+ const options = unionSchemas.map((item) => toZodType(item));
1162
+ return z.union(options);
1163
+ }
1164
+ function zodForLiterals(values) {
1165
+ if (values.length === 0) return z.any();
1166
+ if (values.length === 1) return z.literal(values[0]);
1167
+ const literals = values.map((value) => z.literal(value));
1168
+ return z.union(literals);
1169
+ }
1170
+ function getNonNullTypes(schema) {
1171
+ return Array.isArray(schema.type) ? schema.type.filter((type) => type !== "null") : [schema.type].filter(Boolean);
1172
+ }
1173
+ function isNullable(schema) {
1174
+ return schema.nullable === true || Array.isArray(schema.type) && schema.type.includes("null");
1175
+ }
1176
+ function isJsonSchemaObject(input) {
1177
+ return input != null && typeof input === "object" && !Array.isArray(input);
1178
+ }
1179
+ function isJsonLiteral(value) {
1180
+ return value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
1181
+ }
1182
+
1183
+ // src/bridge/tool-filtering.ts
1184
+ var PUBLIC_TO_NATIVE = {
1185
+ read: "Read",
1186
+ write: "Write",
1187
+ edit: "Edit",
1188
+ bash: "Bash",
1189
+ glob: "Glob",
1190
+ grep: "Grep",
1191
+ webSearch: "WebSearch",
1192
+ WebFetch: "WebFetch",
1193
+ NotebookEdit: "NotebookEdit",
1194
+ TodoWrite: "TodoWrite",
1195
+ Agent: "Agent",
1196
+ TaskCreate: "TaskCreate",
1197
+ TaskGet: "TaskGet",
1198
+ TaskUpdate: "TaskUpdate",
1199
+ TaskList: "TaskList",
1200
+ TaskStop: "TaskStop",
1201
+ TaskOutput: "TaskOutput",
1202
+ Monitor: "Monitor",
1203
+ ListMcpResources: "ListMcpResources",
1204
+ ReadMcpResource: "ReadMcpResource",
1205
+ ExitPlanMode: "ExitPlanMode",
1206
+ EnterWorktree: "EnterWorktree",
1207
+ ExitWorktree: "ExitWorktree",
1208
+ askUserQuestions: "AskUserQuestion",
1209
+ Skill: "Skill",
1210
+ ToolSearch: "ToolSearch"
1211
+ };
1212
+ var PUBLIC_TOOL_NAMES = Object.keys(PUBLIC_TO_NATIVE);
1213
+ function toNativeName(toolName) {
1214
+ return PUBLIC_TO_NATIVE[toolName] ?? toolName;
1215
+ }
1216
+ function resolveNativeTools(toolFiltering) {
1217
+ if (toolFiltering == null || toolFiltering.mode === "deny") return void 0;
1218
+ return toolFiltering.toolNames.map((name) => toNativeName(name));
1219
+ }
1220
+ function resolveInactiveNativeTools(toolFiltering) {
1221
+ if (toolFiltering == null) return [];
1222
+ const inactiveToolNames = toolFiltering.mode === "allow" ? PUBLIC_TOOL_NAMES.filter(
1223
+ (name) => !toolFiltering.toolNames.includes(name)
1224
+ ) : toolFiltering.toolNames;
1225
+ return inactiveToolNames.map((name) => toNativeName(name));
1226
+ }
1227
+
1228
+ // src/bridge/question-tool.ts
1229
+ function claudeCodeQuestionKey(nativeInput) {
1230
+ return JSON.stringify(nativeInput.questions);
1231
+ }
1232
+ function toHarnessQuestionsInput(nativeInput) {
1233
+ return {
1234
+ allowPartialAnswers: true,
1235
+ questions: nativeInput.questions.map((question, questionIndex) => ({
1236
+ id: `question-${questionIndex + 1}`,
1237
+ question: question.question,
1238
+ header: question.header,
1239
+ options: question.options.map((option, optionIndex) => ({
1240
+ id: `option-${optionIndex + 1}`,
1241
+ label: option.label,
1242
+ description: option.description,
1243
+ ...option.preview !== void 0 ? { preview: option.preview } : {}
1244
+ })),
1245
+ allowMultiple: question.multiSelect,
1246
+ allowFreeForm: true
1247
+ }))
1248
+ };
1249
+ }
1250
+ function toClaudeCodeQuestionResult(input) {
1251
+ const output = input.output;
1252
+ if (output.action === "declined" || output.action === "cancelled") {
1253
+ return {
1254
+ behavior: "deny",
1255
+ message: `The user ${output.action} the questions.`
1256
+ };
1257
+ }
1258
+ const canonicalInput = toHarnessQuestionsInput(input.nativeInput);
1259
+ const answers = Object.fromEntries(
1260
+ canonicalInput.questions.flatMap((question, questionIndex) => {
1261
+ const answer = output.answers[question.id];
1262
+ if (answer == null) return [];
1263
+ const nativeQuestion = input.nativeInput.questions[questionIndex];
1264
+ return [
1265
+ [
1266
+ nativeQuestion.question,
1267
+ toClaudeCodeAnswer({
1268
+ nativeQuestion,
1269
+ optionIds: answer.optionIds,
1270
+ freeform: answer.freeform
1271
+ })
1272
+ ]
1273
+ ];
1274
+ })
1275
+ );
1276
+ return {
1277
+ behavior: "allow",
1278
+ updatedInput: { ...input.nativeInput, answers }
1279
+ };
1280
+ }
1281
+ function toClaudeCodeAnswer(input) {
1282
+ if (input.freeform !== void 0) return input.freeform;
1283
+ const labels = input.optionIds.flatMap((optionId) => {
1284
+ const index = positionalIdIndex({ id: optionId, prefix: "option-" });
1285
+ const option = index == null ? void 0 : input.nativeQuestion.options[index];
1286
+ return option == null ? [] : [option.label];
1287
+ });
1288
+ return input.nativeQuestion.multiSelect ? labels.join(", ") : labels[0] ?? "";
1289
+ }
1290
+ function positionalIdIndex(input) {
1291
+ if (!input.id.startsWith(input.prefix)) return void 0;
1292
+ const oneBasedIndex = Number(input.id.slice(input.prefix.length));
1293
+ return Number.isInteger(oneBasedIndex) && oneBasedIndex > 0 ? oneBasedIndex - 1 : void 0;
1294
+ }
1295
+
1296
+ // src/bridge/index.ts
1297
+ var NATIVE_TO_COMMON = {
1298
+ Read: "read",
1299
+ Write: "write",
1300
+ Edit: "edit",
1301
+ Bash: "bash",
1302
+ Glob: "glob",
1303
+ Grep: "grep",
1304
+ WebSearch: "webSearch",
1305
+ AskUserQuestion: "askUserQuestions"
1306
+ };
1307
+ var NATIVE_TOOL_KINDS = {
1308
+ Read: "readonly",
1309
+ Glob: "readonly",
1310
+ Grep: "readonly",
1311
+ WebSearch: "readonly",
1312
+ WebFetch: "readonly",
1313
+ TaskGet: "readonly",
1314
+ TaskList: "readonly",
1315
+ TaskOutput: "readonly",
1316
+ ListMcpResources: "readonly",
1317
+ ReadMcpResource: "readonly",
1318
+ Write: "edit",
1319
+ Edit: "edit",
1320
+ NotebookEdit: "edit",
1321
+ TodoWrite: "edit",
1322
+ TaskCreate: "edit",
1323
+ TaskUpdate: "edit",
1324
+ TaskStop: "edit",
1325
+ EnterWorktree: "edit",
1326
+ ExitWorktree: "edit",
1327
+ ExitPlanMode: "edit",
1328
+ Skill: "readonly",
1329
+ AskUserQuestion: "readonly",
1330
+ ToolSearch: "readonly",
1331
+ Bash: "bash",
1332
+ Monitor: "bash"
1333
+ };
1334
+ function toCommonName(nativeName) {
1335
+ return NATIVE_TO_COMMON[nativeName] ?? nativeName;
1336
+ }
1337
+ var args = parseArgs(argv.slice(2));
1338
+ var workdir = args.workdir;
1339
+ var bridgeStateDir = args.bridgeStateDir;
1340
+ if (!workdir) {
1341
+ emitFatal("Missing --workdir argument.");
1342
+ }
1343
+ if (!bridgeStateDir) {
1344
+ emitFatal("Missing --bridge-state-dir argument.");
1345
+ }
1346
+ var claudeSdk = claudeAgentSdk;
1347
+ var mcpModule = mcpServerModule;
1348
+ var lastClaudeSessionId;
1349
+ await runBridge({
1350
+ bridgeType: "claude-code",
1351
+ bridgeStateDir,
1352
+ onStart: runTurn,
1353
+ // Claude Code's conversation state lives in the runtime's own store, keyed
1354
+ // by working directory. The resume payload names the exact conversation so a
1355
+ // later resume does not have to fall back to "most recent in this workdir".
1356
+ onStop: () => lastClaudeSessionId == null ? {} : { claudeSessionId: lastClaudeSessionId }
1357
+ });
1358
+ function createPermissionOptions(input) {
1359
+ const permissionMode = input.start.permissionMode ?? "allow-all";
1360
+ const inactiveNativeTools = new Set(input.inactiveNativeTools);
1361
+ const permissionSettings = createPermissionSettings({
1362
+ permissionMode,
1363
+ inactiveNativeTools
1364
+ });
1365
+ const baseOptions = {
1366
+ permissionMode: permissionMode === "allow-all" ? "bypassPermissions" : permissionMode === "allow-edits" ? "acceptEdits" : "default",
1367
+ allowDangerouslySkipPermissions: permissionMode === "allow-all",
1368
+ ...permissionSettings ? { settings: permissionSettings } : {}
1369
+ };
1370
+ if (permissionMode === "allow-all") {
1371
+ return {
1372
+ ...baseOptions,
1373
+ /*
1374
+ * Claude Code exposes AskUserQuestion in headless SDK sessions only
1375
+ * when a permission prompt tool is configured. The stdio prompt tool
1376
+ * preserves that tool surface without supplying the canUseTool callback
1377
+ * that bypassPermissions guarantees it will never invoke.
1378
+ */
1379
+ permissionPromptToolName: "stdio"
1380
+ };
1381
+ }
1382
+ return {
1383
+ ...baseOptions,
1384
+ canUseTool: async (toolName, toolInput, options) => {
1385
+ if (toolName.startsWith("mcp__harness-tools__")) {
1386
+ return { behavior: "allow", updatedInput: toolInput };
1387
+ }
1388
+ if (!inactiveNativeTools.has(toolName) && !nativeToolRequiresApproval({
1389
+ nativeName: toolName,
1390
+ permissionMode
1391
+ })) {
1392
+ return { behavior: "allow", updatedInput: toolInput };
1393
+ }
1394
+ const approvalId = options.toolUseID;
1395
+ input.approvalRequestedToolUseIds.add(approvalId);
1396
+ input.nativeToolCallNames.set(approvalId, toolName);
1397
+ input.emit({
1398
+ type: "tool-call",
1399
+ toolCallId: approvalId,
1400
+ toolName: toCommonName(toolName),
1401
+ nativeName: toolName,
1402
+ input: JSON.stringify(toolInput ?? {}),
1403
+ providerExecuted: true,
1404
+ ...isExternalMcpTool(toolName) ? { dynamic: true } : {}
1405
+ });
1406
+ input.emit({
1407
+ type: "tool-approval-request",
1408
+ approvalId,
1409
+ toolCallId: approvalId
1410
+ });
1411
+ input.finishApprovalStep(approvalId);
1412
+ const decision = await input.turn.requestToolApproval(approvalId);
1413
+ return decision.approved ? { behavior: "allow", updatedInput: toolInput, toolUseID: approvalId } : {
1414
+ behavior: "deny",
1415
+ message: decision.reason ?? "Denied",
1416
+ toolUseID: approvalId
1417
+ };
1418
+ }
1419
+ };
1420
+ }
1421
+ function createQuestionPreToolUseHook(input) {
1422
+ return async (hookInput, toolUseID) => {
1423
+ if (hookInput.hook_event_name !== "PreToolUse" || hookInput.tool_name !== "AskUserQuestion") {
1424
+ return {};
1425
+ }
1426
+ const nativeInput = hookInput.tool_input;
1427
+ const canonicalInput = toHarnessQuestionsInput(nativeInput);
1428
+ const toolCallId = toolUseID ?? hookInput.tool_use_id;
1429
+ input.nativeToolCallNames.set(toolCallId, hookInput.tool_name);
1430
+ input.emit({
1431
+ type: "tool-call",
1432
+ toolCallId,
1433
+ toolName: "askUserQuestions",
1434
+ nativeName: hookInput.tool_name,
1435
+ input: JSON.stringify(canonicalInput),
1436
+ providerExecuted: false,
1437
+ providerMetadata: {
1438
+ "claude-code": {
1439
+ nativeRequest: nativeInput
1440
+ }
1441
+ }
1442
+ });
1443
+ const questionKey = claudeCodeQuestionKey(nativeInput);
1444
+ const result = await input.turn.requestToolResult({
1445
+ toolCallId,
1446
+ matches: (candidate) => {
1447
+ const nativeRequest = candidate.toolResult?.providerOptions?.["claude-code"]?.nativeRequest;
1448
+ return nativeRequest != null && claudeCodeQuestionKey(nativeRequest) === questionKey;
1449
+ }
1450
+ });
1451
+ const nativeResult = toClaudeCodeQuestionResult({
1452
+ nativeInput,
1453
+ output: result.output
1454
+ });
1455
+ return {
1456
+ hookSpecificOutput: {
1457
+ hookEventName: "PreToolUse",
1458
+ ...nativeResult.behavior === "allow" ? {
1459
+ permissionDecision: "allow",
1460
+ updatedInput: {
1461
+ ...nativeResult.updatedInput
1462
+ }
1463
+ } : {
1464
+ permissionDecision: "deny",
1465
+ permissionDecisionReason: nativeResult.message
1466
+ }
1467
+ }
1468
+ };
1469
+ };
1470
+ }
1471
+ function createPermissionSettings(input) {
1472
+ const askRules = /* @__PURE__ */ new Set();
1473
+ for (const [nativeName, kind] of Object.entries(NATIVE_TOOL_KINDS)) {
1474
+ if (input.inactiveNativeTools.has(nativeName) || (input.permissionMode === "allow-reads" ? kind === "edit" || kind === "bash" : input.permissionMode === "allow-edits" ? kind === "bash" : false)) {
1475
+ askRules.add(`${nativeName}(*)`);
1476
+ }
1477
+ }
1478
+ if (askRules.size === 0) return void 0;
1479
+ return {
1480
+ permissions: { ask: [...askRules] },
1481
+ sandbox: { autoAllowBashIfSandboxed: false }
1482
+ };
1483
+ }
1484
+ function nativeToolRequiresApproval(input) {
1485
+ if (input.permissionMode === "allow-all") return false;
1486
+ const kind = NATIVE_TOOL_KINDS[input.nativeName] ?? "edit";
1487
+ if (input.permissionMode === "allow-edits") return kind === "bash";
1488
+ return kind === "edit" || kind === "bash";
1489
+ }
1490
+ async function runTurn(start, turn) {
1491
+ const emit = (msg) => turn.emit(msg);
1492
+ const abortCtl = new AbortController();
1493
+ let gracefulAbort;
1494
+ let hardAbortTimer;
1495
+ const onHostAbort = () => {
1496
+ if (gracefulAbort) {
1497
+ gracefulAbort();
1498
+ } else {
1499
+ abortCtl.abort();
1500
+ }
1501
+ };
1502
+ if (turn.abortSignal.aborted) {
1503
+ abortCtl.abort();
1504
+ } else {
1505
+ turn.abortSignal.addEventListener("abort", onHostAbort, { once: true });
1506
+ }
1507
+ const streamEventState = createClaudeStreamEventState();
1508
+ const mcpServers = { ...start.mcpServers };
1509
+ if (start.tools && start.tools.length > 0) {
1510
+ const server = new mcpModule.McpServer({
1511
+ name: "harness-tools",
1512
+ version: "1.0.0"
1513
+ });
1514
+ for (const tool of start.tools) {
1515
+ server.registerTool(
1516
+ tool.name,
1517
+ {
1518
+ description: tool.description ?? "",
1519
+ inputSchema: jsonSchemaToZodObject(tool.inputSchema)
1520
+ },
1521
+ async (...handlerArgs) => {
1522
+ const [input, extra] = handlerArgs;
1523
+ const metadataToolCallId = extra._meta?.["claudecode/toolUseId"];
1524
+ const toolCallId = typeof metadataToolCallId === "string" ? metadataToolCallId : randomUUID3();
1525
+ emit({
1526
+ type: "tool-call",
1527
+ toolCallId,
1528
+ toolName: tool.name,
1529
+ input: JSON.stringify(input),
1530
+ providerExecuted: false
1531
+ });
1532
+ const { output, isError } = await turn.requestToolResult(toolCallId);
1533
+ emit({
1534
+ type: "tool-result",
1535
+ toolCallId,
1536
+ toolName: tool.name,
1537
+ result: output ?? null,
1538
+ isError: !!isError
1539
+ });
1540
+ return {
1541
+ content: [{ type: "text", text: JSON.stringify(output ?? null) }],
1542
+ isError
1543
+ };
1544
+ }
1545
+ );
1546
+ }
1547
+ mcpServers["harness-tools"] = {
1548
+ type: "sdk",
1549
+ name: "harness-tools",
1550
+ instance: server
1551
+ };
1552
+ }
1553
+ const compaction = createCompactionLatch((event) => emit(event));
1554
+ const queryInput = createQueryInput({
1555
+ initialUserMessage: start.prompt,
1556
+ userMessages: turn.experimental_userMessages,
1557
+ abortSignal: abortCtl.signal
1558
+ });
1559
+ const skillsOption = toClaudeSkillsOption(start.skills);
1560
+ const nativeTools = resolveNativeTools(start.builtinToolFiltering);
1561
+ const inactiveNativeTools = resolveInactiveNativeTools(
1562
+ start.builtinToolFiltering
1563
+ );
1564
+ const permissionOptions = createPermissionOptions({
1565
+ start,
1566
+ inactiveNativeTools,
1567
+ turn,
1568
+ emit,
1569
+ finishApprovalStep: (approvalId) => {
1570
+ finishApprovalStep({ state: streamEventState, emit, approvalId });
1571
+ },
1572
+ nativeToolCallNames: streamEventState.nativeToolCallNames,
1573
+ approvalRequestedToolUseIds: streamEventState.approvalRequestedToolUseIds
1574
+ });
1575
+ const questionPreToolUseHook = createQuestionPreToolUseHook({
1576
+ turn,
1577
+ emit,
1578
+ nativeToolCallNames: streamEventState.nativeToolCallNames
1579
+ });
1580
+ const q = claudeSdk.query({
1581
+ prompt: queryInput.input,
1582
+ options: {
1583
+ ...start.model ? { model: start.model } : {},
1584
+ ...start.maxTurns !== void 0 ? { maxTurns: start.maxTurns } : {},
1585
+ ...start.env !== void 0 ? { env: { ...procEnv2, ...start.env } } : {},
1586
+ ...skillsOption ? { skills: skillsOption } : {},
1587
+ ...nativeTools !== void 0 ? { tools: nativeTools } : {},
1588
+ ...inactiveNativeTools.length > 0 ? { disallowedTools: inactiveNativeTools } : {},
1589
+ systemPrompt: createClaudeCodeSystemPrompt(start.instructions),
1590
+ thinking: start.thinking,
1591
+ ...start.effort !== void 0 ? { effort: start.effort } : {},
1592
+ ...start.responseFormat?.type === "json" && start.responseFormat.schema != null ? {
1593
+ outputFormat: {
1594
+ type: "json_schema",
1595
+ schema: start.responseFormat.schema
1596
+ }
1597
+ } : {},
1598
+ includePartialMessages: true,
1599
+ // The `PostCompact` hook carries the compaction summary, which the
1600
+ // `compact_boundary` system message does not. Latch it for the unified
1601
+ // `compaction` event; return an empty output so compaction proceeds.
1602
+ hooks: {
1603
+ PreToolUse: [
1604
+ {
1605
+ matcher: "AskUserQuestion",
1606
+ hooks: [questionPreToolUseHook]
1607
+ }
1608
+ ],
1609
+ PostCompact: [
1610
+ {
1611
+ hooks: [
1612
+ async (input) => {
1613
+ if (typeof input?.compact_summary === "string") {
1614
+ compaction.onSummary(input.compact_summary);
1615
+ }
1616
+ return {};
1617
+ }
1618
+ ]
1619
+ }
1620
+ ]
1621
+ },
1622
+ // Continuation rule, most specific first.
1623
+ //
1624
+ // `resumeSessionId` names the exact conversation and is what a
1625
+ // cross-process resume should use: `continue` means "most recent thread
1626
+ // in this workdir", which silently picks the wrong one once anything
1627
+ // else has run there. The bridge also retains the id observed during its
1628
+ // previous query, so every later query stays pinned to that conversation
1629
+ // even when the host detached and reattached between turns. `resume` and
1630
+ // `continue` are mutually exclusive in the SDK.
1631
+ //
1632
+ // Otherwise the host can force-continue by setting `start.continue`,
1633
+ // and turns after the first fall back to the legacy cwd-based behavior
1634
+ // when no exact id was observed.
1635
+ ...start.resumeSessionId ?? lastClaudeSessionId ? { resume: start.resumeSessionId ?? lastClaudeSessionId } : start.continue === true || !turn.firstTurn ? { continue: true } : {},
1636
+ ...permissionOptions,
1637
+ mcpServers,
1638
+ cwd: workdir,
1639
+ abortSignal: abortCtl.signal
1640
+ }
1641
+ });
1642
+ gracefulAbort = () => {
1643
+ hardAbortTimer = setTimeout(() => abortCtl.abort(), 5e3);
1644
+ hardAbortTimer.unref?.();
1645
+ void Promise.resolve().then(() => q.interrupt()).catch(() => abortCtl.abort());
1646
+ };
1647
+ let turnUsage;
1648
+ let totalCostUsd;
1649
+ let emittedTerminalError = false;
1650
+ let emittedTerminalFinish = false;
1651
+ const emitTerminalError = (message) => {
1652
+ const normalized = message?.trim();
1653
+ if (!normalized || emittedTerminalError || emittedTerminalFinish) return;
1654
+ streamEventState.observedTerminalError = normalized;
1655
+ emittedTerminalError = true;
1656
+ if (!turn.abortSignal.aborted) {
1657
+ turn.emitError({
1658
+ error: normalized,
1659
+ message: "claude-code terminal error"
1660
+ });
1661
+ }
1662
+ queryInput.close();
1663
+ abortCtl.abort();
1664
+ };
1665
+ const emitStreamEvent = createEmitStreamEvent({
1666
+ state: streamEventState,
1667
+ emit,
1668
+ emitWarning: turn.emitWarning,
1669
+ emitTerminalError,
1670
+ onCompactionBoundary: (boundary) => compaction.onBoundary(boundary),
1671
+ toCommonName
1672
+ });
1673
+ try {
1674
+ for await (const msg of q) {
1675
+ if (abortCtl.signal.aborted) break;
1676
+ const type = msg.type;
1677
+ if (type === "command_lifecycle") {
1678
+ queryInput.handleLifecycle(msg);
1679
+ }
1680
+ const sessionId = msg.session_id;
1681
+ if (typeof sessionId === "string" && sessionId.length > 0) {
1682
+ lastClaudeSessionId = sessionId;
1683
+ }
1684
+ emitStreamEvent(msg);
1685
+ if (type === "result") {
1686
+ if (msg.subtype === "success") {
1687
+ if (msg.is_error) {
1688
+ emitTerminalError(
1689
+ msg.result?.trim() || streamEventState.observedTerminalError || (typeof msg.api_error_status === "number" ? `Claude Code reported an API error (HTTP ${msg.api_error_status})` : "Claude Code reported a failed result")
1690
+ );
1691
+ continue;
1692
+ }
1693
+ const emptyResult = !msg.result?.trim?.();
1694
+ if (emptyResult && streamEventState.observedTerminalError) {
1695
+ emitTerminalError(streamEventState.observedTerminalError);
1696
+ continue;
1697
+ }
1698
+ const usage = msg.usage ?? msg.message?.usage;
1699
+ const harnessUsage = mapUsage(usage);
1700
+ if (harnessUsage) turnUsage = addUsage(turnUsage, harnessUsage);
1701
+ if (typeof msg.total_cost_usd === "number") {
1702
+ totalCostUsd = (totalCostUsd ?? 0) + msg.total_cost_usd;
1703
+ }
1704
+ if (start.responseFormat?.type === "json" && msg.structured_output !== void 0) {
1705
+ const id = randomUUID3();
1706
+ emit({ type: "text-start", id });
1707
+ emit({
1708
+ type: "text-delta",
1709
+ id,
1710
+ delta: JSON.stringify(msg.structured_output)
1711
+ });
1712
+ emit({ type: "text-end", id });
1713
+ streamEventState.stepOpen = true;
1714
+ }
1715
+ if (streamEventState.stepOpen) {
1716
+ emitFinishStep({
1717
+ state: streamEventState,
1718
+ emit,
1719
+ usage: streamEventState.pendingStepUsage ?? harnessUsage
1720
+ });
1721
+ }
1722
+ queryInput.observeResult();
1723
+ if (!queryInput.hasActiveUserMessages()) {
1724
+ queryInput.close();
1725
+ break;
1726
+ }
1727
+ } else {
1728
+ emitTerminalError(
1729
+ (Array.isArray(msg.errors) ? msg.errors.join("\n") : void 0) || streamEventState.observedTerminalError || msg.result || "Unknown error"
1730
+ );
1731
+ }
1732
+ continue;
1733
+ }
1734
+ if (queryInput.hasObservedResult && !queryInput.hasActiveUserMessages()) {
1735
+ queryInput.close();
1736
+ break;
1737
+ }
1738
+ }
1739
+ } catch (err) {
1740
+ if (!turn.abortSignal.aborted && !(abortCtl.signal.aborted && emittedTerminalError)) {
1741
+ turn.emitError({ error: err, message: "claude-code turn failed" });
1742
+ }
1743
+ return;
1744
+ } finally {
1745
+ gracefulAbort = void 0;
1746
+ if (hardAbortTimer != null) clearTimeout(hardAbortTimer);
1747
+ turn.abortSignal.removeEventListener("abort", onHostAbort);
1748
+ queryInput.close();
1749
+ try {
1750
+ await q.return?.(
1751
+ void 0
1752
+ );
1753
+ } catch {
1754
+ }
1755
+ }
1756
+ if (emittedTerminalError) return;
1757
+ emittedTerminalFinish = true;
1758
+ void emittedTerminalFinish;
1759
+ emit({
1760
+ type: "finish",
1761
+ finishReason: { unified: "stop", raw: "stop" },
1762
+ totalUsage: turnUsage ?? streamEventState.stepUsage ?? defaultUsage(),
1763
+ ...totalCostUsd !== void 0 || lastClaudeSessionId !== void 0 ? {
1764
+ harnessMetadata: {
1765
+ "claude-code": {
1766
+ ...totalCostUsd !== void 0 ? { costUsd: totalCostUsd } : {},
1767
+ // The conversation this turn belongs to, resumable outside the
1768
+ // SDK with `claude --resume <sessionId>` and captured by the
1769
+ // adapter for exact cross-process resume.
1770
+ ...lastClaudeSessionId !== void 0 ? { sessionId: lastClaudeSessionId } : {}
1771
+ }
1772
+ }
1773
+ } : {}
1774
+ });
1775
+ }
1776
+ function createQueryInput({
1777
+ initialUserMessage,
1778
+ userMessages,
1779
+ abortSignal
1780
+ }) {
1781
+ let closed = false;
1782
+ let observedResult = false;
1783
+ const submittedMessages = /* @__PURE__ */ new Map();
1784
+ const close = (error) => {
1785
+ if (closed) return;
1786
+ closed = true;
1787
+ userMessages.close(error);
1788
+ };
1789
+ if (abortSignal.aborted) {
1790
+ close(abortSignal.reason);
1791
+ } else {
1792
+ abortSignal.addEventListener("abort", () => close(abortSignal.reason), {
1793
+ once: true
1794
+ });
1795
+ }
1796
+ const toUserMessage = (options) => ({
1797
+ type: "user",
1798
+ message: {
1799
+ role: "user",
1800
+ content: [{ type: "text", text: options.text }]
1801
+ },
1802
+ parent_tool_use_id: null,
1803
+ uuid: options.messageId,
1804
+ ...options.priority == null ? {} : { priority: options.priority }
1805
+ });
1806
+ const messageIterator = userMessages[Symbol.asyncIterator]();
1807
+ return {
1808
+ close,
1809
+ handleLifecycle: (message) => {
1810
+ const lifecycle = message;
1811
+ if (lifecycle.command_uuid == null || lifecycle.state == null) return;
1812
+ const submitted = submittedMessages.get(lifecycle.command_uuid);
1813
+ if (submitted == null) return;
1814
+ if (lifecycle.state === "queued" || lifecycle.state === "started") {
1815
+ submitted.accept();
1816
+ return;
1817
+ }
1818
+ if (lifecycle.state === "cancelled" || lifecycle.state === "discarded") {
1819
+ submitted.reject(
1820
+ new Error(`Claude Code ${lifecycle.state} the user message.`)
1821
+ );
1822
+ }
1823
+ submittedMessages.delete(lifecycle.command_uuid);
1824
+ },
1825
+ hasActiveUserMessages: () => submittedMessages.size > 0 || userMessages.pendingCount > 0,
1826
+ observeResult: () => {
1827
+ observedResult = true;
1828
+ },
1829
+ get hasObservedResult() {
1830
+ return observedResult;
1831
+ },
1832
+ input: {
1833
+ [Symbol.asyncIterator]() {
1834
+ let sentInitial = false;
1835
+ return {
1836
+ async next() {
1837
+ if (closed || abortSignal.aborted) {
1838
+ return {
1839
+ value: void 0,
1840
+ done: true
1841
+ };
1842
+ }
1843
+ if (!sentInitial) {
1844
+ sentInitial = true;
1845
+ return {
1846
+ value: toUserMessage({
1847
+ text: initialUserMessage,
1848
+ messageId: randomUUID3()
1849
+ }),
1850
+ done: false
1851
+ };
1852
+ }
1853
+ const nextMessage = await messageIterator.next();
1854
+ if (nextMessage.done) {
1855
+ return {
1856
+ value: void 0,
1857
+ done: true
1858
+ };
1859
+ }
1860
+ submittedMessages.set(
1861
+ nextMessage.value.messageId,
1862
+ nextMessage.value
1863
+ );
1864
+ return {
1865
+ value: toUserMessage({
1866
+ text: nextMessage.value.text,
1867
+ messageId: nextMessage.value.messageId,
1868
+ priority: "next"
1869
+ }),
1870
+ done: false
1871
+ };
1872
+ }
1873
+ };
1874
+ }
1875
+ }
1876
+ };
1877
+ }
1878
+ function addUsage(total, usage) {
1879
+ if (total == null) return usage;
1880
+ const result = { ...total };
1881
+ for (const [key, value] of Object.entries(usage)) {
1882
+ const previous = result[key];
1883
+ if (typeof value === "number" && typeof previous === "number") {
1884
+ result[key] = previous + value;
1885
+ } else if (value != null && previous != null && typeof value === "object" && typeof previous === "object" && !Array.isArray(value) && !Array.isArray(previous)) {
1886
+ result[key] = addUsage(
1887
+ previous,
1888
+ value
1889
+ );
1890
+ } else {
1891
+ result[key] = value;
1892
+ }
1893
+ }
1894
+ return result;
1895
+ }
1896
+ function parseArgs(args2) {
1897
+ const out = {};
1898
+ for (let i = 0; i < args2.length; i++) {
1899
+ if (args2[i] === "--workdir" && i + 1 < args2.length) {
1900
+ out.workdir = args2[++i];
1901
+ } else if (args2[i] === "--bridge-state-dir" && i + 1 < args2.length) {
1902
+ out.bridgeStateDir = args2[++i];
1903
+ }
1904
+ }
1905
+ return out;
1906
+ }
1907
+ function emitFatal(message) {
1908
+ stdout2.write(JSON.stringify({ type: "bridge-fatal", message }) + "\n");
1909
+ process.exit(1);
1910
+ }
1911
+ //# sourceMappingURL=index.mjs.map