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