@ai-sdk/harness-claude-code 0.0.0-07500555-20260730161446

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.
@@ -0,0 +1,1260 @@
1
+ // ../harness/dist/bridge/index.js
2
+ import { appendFile, mkdir, writeFile } from "fs/promises";
3
+ import { existsSync, readFileSync } from "fs";
4
+ import { env as procEnv, pid, stdout } from "process";
5
+ import { WebSocketServer } from "ws";
6
+ var DEBUG_LEVEL_WEIGHT = {
7
+ error: 0,
8
+ warn: 1,
9
+ info: 2,
10
+ debug: 3,
11
+ trace: 4
12
+ };
13
+ function subsystemMatches(filters, subsystem) {
14
+ if (!filters || filters.length === 0) return true;
15
+ return filters.some(
16
+ (filter) => subsystem === filter || subsystem.startsWith(`${filter}.`)
17
+ );
18
+ }
19
+ function formatBridgeError(err) {
20
+ if (err instanceof Error) {
21
+ return { name: err.name, message: err.message, stack: err.stack };
22
+ }
23
+ if (typeof err === "string") {
24
+ return { message: err };
25
+ }
26
+ if (err !== null && typeof err === "object") {
27
+ try {
28
+ return { message: JSON.stringify(err) };
29
+ } catch {
30
+ }
31
+ }
32
+ return { message: String(err) };
33
+ }
34
+ function parseEnvList(value) {
35
+ if (!value) return void 0;
36
+ const items = value.split(",").map((item) => item.trim()).filter(Boolean);
37
+ return items.length > 0 ? items : void 0;
38
+ }
39
+ var ENV_TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
40
+ var WS_OPEN = 1;
41
+ async function runBridge(options) {
42
+ const { bridgeType, bridgeStateDir: bridgeStateDir2, onStart, onDetach } = options;
43
+ const expectedToken = options.token ?? procEnv.BRIDGE_CHANNEL_TOKEN ?? "";
44
+ const bridgeWsPort = options.port ?? parseInt(procEnv.BRIDGE_WS_PORT ?? "0", 10);
45
+ const bridgeMetaPath = `${bridgeStateDir2}/bridge-meta.json`;
46
+ const startConfigPath = `${bridgeStateDir2}/start-config.json`;
47
+ const rerunStartConfigPath = `${bridgeStateDir2}/rerun-start-config.json`;
48
+ const eventLogPath = `${bridgeStateDir2}/event-log.ndjson`;
49
+ try {
50
+ await mkdir(bridgeStateDir2, { recursive: true });
51
+ } catch {
52
+ }
53
+ let currentBoundPort = 0;
54
+ let currentTurnState = "init";
55
+ let activeSocket;
56
+ let activeSocketReadyForLiveEvents = false;
57
+ let isFirstTurn = true;
58
+ let turnAbort;
59
+ let currentUserMessages;
60
+ let currentInterruptHandler;
61
+ let debugConfig;
62
+ let consoleCaptureInstalled = false;
63
+ const envDebugEnabled = ENV_TRUTHY.has(
64
+ (procEnv.HARNESS_DEBUG ?? "").toLowerCase()
65
+ );
66
+ let seqCounter = 0;
67
+ let eventLog = [];
68
+ let diskBuffer = "";
69
+ let flushPromise = null;
70
+ const flushEventsToDisk = async () => {
71
+ while (diskBuffer.length > 0) {
72
+ const buf = diskBuffer;
73
+ diskBuffer = "";
74
+ await appendFile(eventLogPath, buf).catch(() => {
75
+ });
76
+ }
77
+ };
78
+ const scheduleEventFlush = () => {
79
+ if (flushPromise) return;
80
+ flushPromise = new Promise((resolve) => {
81
+ setImmediate(() => {
82
+ void flushEventsToDisk().finally(resolve);
83
+ });
84
+ }).finally(() => {
85
+ flushPromise = null;
86
+ if (diskBuffer.length > 0) {
87
+ scheduleEventFlush();
88
+ }
89
+ });
90
+ };
91
+ const flushPendingEventsToDisk = async () => {
92
+ if (diskBuffer.length > 0 && !flushPromise) {
93
+ scheduleEventFlush();
94
+ }
95
+ let inFlight = flushPromise;
96
+ while (inFlight) {
97
+ await inFlight;
98
+ inFlight = flushPromise;
99
+ }
100
+ };
101
+ const replayFromDisk = procEnv.BRIDGE_REPLAY_FROM_DISK === "1";
102
+ if (replayFromDisk && existsSync(eventLogPath)) {
103
+ try {
104
+ const lines = readFileSync(eventLogPath, "utf8").split("\n").map((line) => line.trim()).filter(Boolean);
105
+ eventLog = lines.map((line) => ({
106
+ seq: JSON.parse(line).seq,
107
+ line
108
+ }));
109
+ seqCounter = eventLog.at(-1)?.seq ?? 0;
110
+ } catch {
111
+ eventLog = [];
112
+ seqCounter = 0;
113
+ }
114
+ }
115
+ const pendingToolResults = /* @__PURE__ */ new Map();
116
+ const pendingToolApprovals = /* @__PURE__ */ new Map();
117
+ const writeBridgeMeta = async (state) => {
118
+ try {
119
+ await writeFile(
120
+ bridgeMetaPath,
121
+ JSON.stringify({
122
+ type: bridgeType,
123
+ port: currentBoundPort,
124
+ state,
125
+ pid
126
+ })
127
+ );
128
+ } catch {
129
+ }
130
+ };
131
+ const writeStartConfig = async (start) => {
132
+ try {
133
+ const serialized = JSON.stringify(start);
134
+ await writeFile(startConfigPath, serialized);
135
+ if (!existsSync(rerunStartConfigPath)) {
136
+ await writeFile(rerunStartConfigPath, serialized);
137
+ }
138
+ } catch {
139
+ }
140
+ };
141
+ const sendControl = (msg) => {
142
+ if (activeSocket?.readyState === WS_OPEN) {
143
+ try {
144
+ activeSocket.send(JSON.stringify(msg));
145
+ } catch {
146
+ }
147
+ }
148
+ };
149
+ const emit = (event) => {
150
+ const seq = ++seqCounter;
151
+ const line = JSON.stringify({ ...event, seq });
152
+ eventLog.push({ seq, line });
153
+ diskBuffer += `${line}
154
+ `;
155
+ scheduleEventFlush();
156
+ if (activeSocketReadyForLiveEvents && activeSocket?.readyState === WS_OPEN) {
157
+ try {
158
+ activeSocket.send(line);
159
+ } catch {
160
+ }
161
+ }
162
+ };
163
+ const replay = (ws, afterSeq) => {
164
+ for (const entry of eventLog) {
165
+ if (entry.seq > afterSeq && ws.readyState === WS_OPEN) {
166
+ ws.send(entry.line);
167
+ }
168
+ }
169
+ };
170
+ const shouldEmitDebugEvent = (level, subsystem) => {
171
+ if (!debugConfig?.enabled) return false;
172
+ const threshold = debugConfig.level ?? "debug";
173
+ if (DEBUG_LEVEL_WEIGHT[level] > DEBUG_LEVEL_WEIGHT[threshold]) return false;
174
+ return subsystemMatches(debugConfig.subsystems, subsystem);
175
+ };
176
+ const rawStdoutWrite = process.stdout.write.bind(process.stdout);
177
+ const rawStderrWrite = process.stderr.write.bind(process.stderr);
178
+ const writeErrorToStderr = (input) => {
179
+ try {
180
+ const formatted = formatBridgeError(input.error);
181
+ rawStderrWrite(
182
+ `[harness:${bridgeType}:error] ${input.message}: ${formatted.message}
183
+ `
184
+ );
185
+ if (formatted.stack) {
186
+ rawStderrWrite(`${formatted.stack}
187
+ `);
188
+ }
189
+ } catch {
190
+ }
191
+ };
192
+ const emitWarning = (input) => {
193
+ try {
194
+ for (const line of input.message.split("\n")) {
195
+ if (line.trim().length > 0) {
196
+ rawStderrWrite(`[harness:${bridgeType}:warn] ${line}
197
+ `);
198
+ }
199
+ }
200
+ } catch {
201
+ }
202
+ };
203
+ const emitError = (input) => {
204
+ writeErrorToStderr({
205
+ message: input.message ?? "bridge error",
206
+ error: input.error
207
+ });
208
+ emit({ type: "error", error: serialiseError(input.error) });
209
+ };
210
+ const installConsoleCapture = () => {
211
+ if (consoleCaptureInstalled) return;
212
+ consoleCaptureInstalled = true;
213
+ const buffers = {
214
+ stdout: "",
215
+ stderr: ""
216
+ };
217
+ const patch = (stream, raw) => (chunk, encoding, cb) => {
218
+ if (debugConfig?.enabled) {
219
+ try {
220
+ const enc = typeof encoding === "string" ? encoding : "utf8";
221
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(
222
+ enc
223
+ );
224
+ const combined = buffers[stream] + text.replace(/\r\n/g, "\n");
225
+ const parts = combined.split("\n");
226
+ buffers[stream] = parts.pop() ?? "";
227
+ for (const line of parts) {
228
+ const trimmed = line.replace(/\s+$/, "");
229
+ if (trimmed) {
230
+ emit({
231
+ type: "sandbox-log",
232
+ source: bridgeType,
233
+ stream,
234
+ line: trimmed
235
+ });
236
+ }
237
+ }
238
+ } catch {
239
+ }
240
+ }
241
+ return raw(
242
+ chunk,
243
+ encoding,
244
+ cb
245
+ );
246
+ };
247
+ process.stdout.write = patch(
248
+ "stdout",
249
+ rawStdoutWrite
250
+ );
251
+ process.stderr.write = patch(
252
+ "stderr",
253
+ rawStderrWrite
254
+ );
255
+ };
256
+ const handleInbound = async (msg, ws) => {
257
+ switch (msg.type) {
258
+ case "start": {
259
+ if (activeSocket !== ws) return;
260
+ activeSocketReadyForLiveEvents = true;
261
+ const firstTurn = isFirstTurn;
262
+ isFirstTurn = false;
263
+ eventLog = [];
264
+ diskBuffer = "";
265
+ void writeFile(eventLogPath, "").catch(() => {
266
+ });
267
+ turnAbort = new AbortController();
268
+ currentTurnState = "running";
269
+ currentInterruptHandler = void 0;
270
+ void writeStartConfig(msg);
271
+ void writeBridgeMeta("running");
272
+ const startDebug = msg.debug;
273
+ debugConfig = {
274
+ enabled: startDebug?.enabled ?? envDebugEnabled,
275
+ level: startDebug?.level ?? procEnv.HARNESS_DEBUG_LEVEL,
276
+ subsystems: startDebug?.subsystems ?? parseEnvList(procEnv.HARNESS_DEBUG_SUBSYSTEMS)
277
+ };
278
+ if (debugConfig.enabled) {
279
+ installConsoleCapture();
280
+ }
281
+ const turn = {
282
+ emit,
283
+ requestToolResult: (toolCallId) => new Promise((resolve) => {
284
+ pendingToolResults.set(toolCallId, resolve);
285
+ }),
286
+ requestToolApproval: (approvalId) => new Promise((resolve) => {
287
+ pendingToolApprovals.set(approvalId, resolve);
288
+ }),
289
+ pendingUserMessages: [],
290
+ abortSignal: turnAbort.signal,
291
+ onInterrupt: (handler) => {
292
+ currentInterruptHandler = handler;
293
+ },
294
+ firstTurn,
295
+ bridgeLog: (input) => {
296
+ const level = input.level ?? "debug";
297
+ if (!shouldEmitDebugEvent(level, input.subsystem)) return;
298
+ emit({
299
+ type: "debug-event",
300
+ level,
301
+ subsystem: input.subsystem,
302
+ message: input.message,
303
+ ...input.attrs ? { attrs: input.attrs } : {},
304
+ ...input.error !== void 0 ? { error: formatBridgeError(input.error) } : {}
305
+ });
306
+ },
307
+ emitWarning,
308
+ emitError
309
+ };
310
+ currentUserMessages = turn.pendingUserMessages;
311
+ try {
312
+ await onStart(msg, turn);
313
+ } catch (err) {
314
+ emitError({ error: err, message: "bridge turn failed" });
315
+ } finally {
316
+ currentInterruptHandler = void 0;
317
+ currentTurnState = "waiting";
318
+ void writeBridgeMeta("waiting");
319
+ }
320
+ return;
321
+ }
322
+ case "tool-result": {
323
+ const resolver = pendingToolResults.get(msg.toolCallId);
324
+ if (resolver) {
325
+ pendingToolResults.delete(msg.toolCallId);
326
+ resolver({ output: msg.output, isError: msg.isError });
327
+ }
328
+ return;
329
+ }
330
+ case "tool-approval-response": {
331
+ const resolver = pendingToolApprovals.get(msg.approvalId);
332
+ if (resolver) {
333
+ pendingToolApprovals.delete(msg.approvalId);
334
+ resolver({ approved: msg.approved, reason: msg.reason });
335
+ }
336
+ return;
337
+ }
338
+ case "user-message":
339
+ currentUserMessages?.push(msg.text);
340
+ return;
341
+ case "abort":
342
+ turnAbort?.abort();
343
+ return;
344
+ case "interrupt":
345
+ try {
346
+ if (pendingToolResults.size === 0 && pendingToolApprovals.size === 0) {
347
+ if (currentInterruptHandler) {
348
+ await currentInterruptHandler();
349
+ } else {
350
+ turnAbort?.abort();
351
+ }
352
+ }
353
+ sendControl({ type: "bridge-interrupted", ok: true });
354
+ } catch (err) {
355
+ sendControl({
356
+ type: "bridge-interrupted",
357
+ ok: false,
358
+ error: serialiseError(err)
359
+ });
360
+ }
361
+ return;
362
+ case "resume":
363
+ if (activeSocket !== ws) return;
364
+ replay(ws, msg.lastSeenEventId);
365
+ activeSocketReadyForLiveEvents = true;
366
+ return;
367
+ case "shutdown":
368
+ currentTurnState = "done";
369
+ void writeBridgeMeta("done");
370
+ drainThenExit(ws, 1e3, "shutdown");
371
+ return;
372
+ case "detach": {
373
+ currentTurnState = "done";
374
+ void writeBridgeMeta("done");
375
+ const data = await onDetach?.() ?? {};
376
+ sendControl({ type: "bridge-detach", data });
377
+ drainThenExit(ws, 1e3, "detach");
378
+ return;
379
+ }
380
+ }
381
+ };
382
+ void writeBridgeMeta("init");
383
+ const wss = new WebSocketServer({ port: bridgeWsPort, host: "0.0.0.0" });
384
+ const exit = () => {
385
+ if (options.onExit) {
386
+ options.onExit();
387
+ return;
388
+ }
389
+ wss.close(() => process.exit(0));
390
+ setTimeout(() => process.exit(0), 1e3).unref();
391
+ };
392
+ const drainThenExit = (ws, code, reason) => {
393
+ const start = Date.now();
394
+ const tick = () => {
395
+ const drained = ws.bufferedAmount === 0 || ws.readyState !== WS_OPEN;
396
+ if (drained || Date.now() - start >= 5e3) {
397
+ void flushPendingEventsToDisk().finally(() => {
398
+ try {
399
+ ws.close(code, reason);
400
+ } finally {
401
+ exit();
402
+ }
403
+ });
404
+ return;
405
+ }
406
+ setTimeout(tick, 10).unref();
407
+ };
408
+ tick();
409
+ };
410
+ wss.on("listening", () => {
411
+ const addr = wss.address();
412
+ currentBoundPort = typeof addr === "object" && addr ? addr.port : 0;
413
+ currentTurnState = "waiting";
414
+ void writeBridgeMeta("waiting");
415
+ stdout.write(
416
+ JSON.stringify({
417
+ type: "bridge-ready",
418
+ port: currentBoundPort
419
+ }) + "\n"
420
+ );
421
+ options.onListening?.(currentBoundPort);
422
+ });
423
+ wss.on("connection", (ws, req) => {
424
+ const url = new URL(req.url ?? "/", "http://localhost");
425
+ if (url.searchParams.get("agent_bridge_token") !== expectedToken) {
426
+ ws.close(1008, "unauthorized");
427
+ return;
428
+ }
429
+ activeSocket = ws;
430
+ activeSocketReadyForLiveEvents = false;
431
+ sendControl({
432
+ type: "bridge-hello",
433
+ state: currentTurnState,
434
+ lastSeq: seqCounter
435
+ });
436
+ ws.on("message", (raw) => {
437
+ let parsed;
438
+ try {
439
+ const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
440
+ parsed = JSON.parse(text);
441
+ } catch (err) {
442
+ sendControl({
443
+ type: "error",
444
+ error: `protocol parse error: ${err.message}`
445
+ });
446
+ return;
447
+ }
448
+ void handleInbound(parsed, ws);
449
+ });
450
+ ws.on("close", () => {
451
+ if (activeSocket === ws) {
452
+ activeSocket = void 0;
453
+ activeSocketReadyForLiveEvents = false;
454
+ }
455
+ });
456
+ ws.on("error", () => {
457
+ });
458
+ });
459
+ process.on("uncaughtException", (err) => {
460
+ emitError({ error: err, message: "uncaught exception" });
461
+ });
462
+ process.on("unhandledRejection", (err) => {
463
+ emitError({ error: err, message: "unhandled rejection" });
464
+ });
465
+ await new Promise((resolve, reject) => {
466
+ if (wss.address() != null) {
467
+ resolve();
468
+ return;
469
+ }
470
+ wss.once("listening", resolve);
471
+ wss.once("error", reject);
472
+ });
473
+ return {
474
+ port: currentBoundPort,
475
+ close: () => new Promise((resolve) => {
476
+ wss.close(() => resolve());
477
+ })
478
+ };
479
+ }
480
+ function serialiseError(err) {
481
+ if (err instanceof Error) {
482
+ return { name: err.name, message: err.message, stack: err.stack };
483
+ }
484
+ return err;
485
+ }
486
+
487
+ // src/bridge/compaction-latch.ts
488
+ function createCompactionLatch(emit) {
489
+ let boundary;
490
+ let summary;
491
+ const tryEmit = () => {
492
+ if (!boundary || summary === void 0) return;
493
+ emit({
494
+ type: "compaction",
495
+ trigger: boundary.trigger,
496
+ summary,
497
+ ...boundary.tokensBefore !== void 0 ? { tokensBefore: boundary.tokensBefore } : {},
498
+ ...boundary.tokensAfter !== void 0 ? { tokensAfter: boundary.tokensAfter } : {}
499
+ });
500
+ boundary = void 0;
501
+ summary = void 0;
502
+ };
503
+ return {
504
+ onBoundary(next) {
505
+ boundary = next;
506
+ tryEmit();
507
+ },
508
+ onSummary(next) {
509
+ summary = next;
510
+ tryEmit();
511
+ }
512
+ };
513
+ }
514
+
515
+ // src/bridge/index.ts
516
+ import { randomUUID } from "crypto";
517
+ import { argv, stdout as stdout2 } from "process";
518
+ import * as claudeAgentSdk from "@anthropic-ai/claude-agent-sdk";
519
+ import * as mcpServerModule from "@modelcontextprotocol/sdk/server/mcp.js";
520
+
521
+ // src/bridge/claude-skills-option.ts
522
+ function toClaudeSkillsOption(skills) {
523
+ return skills && skills.length > 0 ? "all" : void 0;
524
+ }
525
+
526
+ // src/bridge/json-schema-to-zod.ts
527
+ import { z } from "zod/v4";
528
+ function jsonSchemaToZodShape(input) {
529
+ const schema = isJsonSchemaObject(input) ? input : {};
530
+ return toZodShape(schema);
531
+ }
532
+ function toZodShape(schema) {
533
+ if (!schema?.properties) return {};
534
+ const required = new Set(schema.required ?? []);
535
+ const shape = {};
536
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
537
+ const propType = toZodType(propSchema);
538
+ let propertyType = required.has(key) ? propType : propType.optional();
539
+ if (propSchema.description) {
540
+ propertyType = propertyType.describe(propSchema.description);
541
+ }
542
+ shape[key] = propertyType;
543
+ }
544
+ return shape;
545
+ }
546
+ function toZodType(schema) {
547
+ if (!schema) return z.any();
548
+ let zType = zodForConst(schema) ?? zodForEnum(schema) ?? zodForNullableUnion(schema) ?? zodForType(schema);
549
+ if (isNullable(schema)) zType = zType.nullable();
550
+ if (schema.description) zType = zType.describe(schema.description);
551
+ return zType;
552
+ }
553
+ function zodForType(schema) {
554
+ const types = getNonNullTypes(schema);
555
+ if (types.length > 1) return z.any();
556
+ if (Array.isArray(schema.type) && types.length === 0) return z.null();
557
+ const type = types[0] ?? (schema.properties ? "object" : void 0);
558
+ switch (type) {
559
+ case "string":
560
+ return z.string();
561
+ case "number":
562
+ return z.number();
563
+ case "integer":
564
+ return z.number().int();
565
+ case "boolean":
566
+ return z.boolean();
567
+ case "array":
568
+ return z.array(
569
+ Array.isArray(schema.items) ? z.any() : toZodType(schema.items)
570
+ );
571
+ case "object":
572
+ return z.object(toZodShape(schema));
573
+ case "null":
574
+ return z.null();
575
+ default:
576
+ return z.any();
577
+ }
578
+ }
579
+ function zodForConst(schema) {
580
+ if (!("const" in schema)) return void 0;
581
+ return isJsonLiteral(schema.const) ? z.literal(schema.const) : z.any();
582
+ }
583
+ function zodForEnum(schema) {
584
+ if (!Array.isArray(schema.enum)) return void 0;
585
+ if (!schema.enum.every(isJsonLiteral)) return z.any();
586
+ return zodForLiterals(schema.enum);
587
+ }
588
+ function zodForNullableUnion(schema) {
589
+ const unionSchemas = schema.anyOf ?? schema.oneOf;
590
+ if (!unionSchemas || unionSchemas.length < 2) return void 0;
591
+ const nonNullSchemas = unionSchemas.filter((item) => !isNullOnlySchema(item));
592
+ if (nonNullSchemas.length !== 1) return void 0;
593
+ return toZodType(nonNullSchemas[0]).nullable();
594
+ }
595
+ function zodForLiterals(values) {
596
+ if (values.length === 0) return z.any();
597
+ if (values.length === 1) return z.literal(values[0]);
598
+ const literals = values.map((value) => z.literal(value));
599
+ return z.union(literals);
600
+ }
601
+ function getNonNullTypes(schema) {
602
+ return Array.isArray(schema.type) ? schema.type.filter((type) => type !== "null") : [schema.type].filter(Boolean);
603
+ }
604
+ function isNullable(schema) {
605
+ return schema.nullable === true || Array.isArray(schema.type) && schema.type.includes("null");
606
+ }
607
+ function isNullOnlySchema(schema) {
608
+ return schema.type === "null" || Array.isArray(schema.type) && schema.type.length === 1 && schema.type[0] === "null" || schema.const === null || Array.isArray(schema.enum) && schema.enum.length === 1 && schema.enum[0] === null;
609
+ }
610
+ function isJsonSchemaObject(input) {
611
+ return input != null && typeof input === "object" && !Array.isArray(input);
612
+ }
613
+ function isJsonLiteral(value) {
614
+ return value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value);
615
+ }
616
+
617
+ // src/bridge/index.ts
618
+ var NATIVE_TO_COMMON = {
619
+ Read: "read",
620
+ Write: "write",
621
+ Edit: "edit",
622
+ Bash: "bash",
623
+ Glob: "glob",
624
+ Grep: "grep",
625
+ WebSearch: "webSearch"
626
+ };
627
+ var PUBLIC_TO_NATIVE = {
628
+ read: "Read",
629
+ write: "Write",
630
+ edit: "Edit",
631
+ bash: "Bash",
632
+ glob: "Glob",
633
+ grep: "Grep",
634
+ webSearch: "WebSearch",
635
+ WebFetch: "WebFetch",
636
+ NotebookEdit: "NotebookEdit",
637
+ TodoWrite: "TodoWrite",
638
+ Agent: "Agent",
639
+ TaskCreate: "TaskCreate",
640
+ TaskGet: "TaskGet",
641
+ TaskUpdate: "TaskUpdate",
642
+ TaskList: "TaskList",
643
+ TaskStop: "TaskStop",
644
+ TaskOutput: "TaskOutput",
645
+ Monitor: "Monitor",
646
+ ListMcpResources: "ListMcpResources",
647
+ ReadMcpResource: "ReadMcpResource",
648
+ ExitPlanMode: "ExitPlanMode",
649
+ EnterWorktree: "EnterWorktree",
650
+ ExitWorktree: "ExitWorktree",
651
+ AskUserQuestion: "AskUserQuestion",
652
+ Skill: "Skill",
653
+ ToolSearch: "ToolSearch"
654
+ };
655
+ var PUBLIC_TOOL_NAMES = Object.keys(PUBLIC_TO_NATIVE);
656
+ var UNRECOVERABLE_API_RETRY_STATUSES = /* @__PURE__ */ new Set([401, 403, 404]);
657
+ var NATIVE_TOOL_KINDS = {
658
+ Read: "readonly",
659
+ Glob: "readonly",
660
+ Grep: "readonly",
661
+ WebSearch: "readonly",
662
+ WebFetch: "readonly",
663
+ TaskGet: "readonly",
664
+ TaskList: "readonly",
665
+ TaskOutput: "readonly",
666
+ ListMcpResources: "readonly",
667
+ ReadMcpResource: "readonly",
668
+ Write: "edit",
669
+ Edit: "edit",
670
+ NotebookEdit: "edit",
671
+ TodoWrite: "edit",
672
+ TaskCreate: "edit",
673
+ TaskUpdate: "edit",
674
+ TaskStop: "edit",
675
+ EnterWorktree: "edit",
676
+ ExitWorktree: "edit",
677
+ ExitPlanMode: "edit",
678
+ Skill: "readonly",
679
+ AskUserQuestion: "readonly",
680
+ ToolSearch: "readonly",
681
+ Bash: "bash",
682
+ Monitor: "bash"
683
+ };
684
+ function toCommonName(nativeName) {
685
+ return NATIVE_TO_COMMON[nativeName] ?? nativeName;
686
+ }
687
+ function toNativeName(toolName) {
688
+ return PUBLIC_TO_NATIVE[toolName] ?? toolName;
689
+ }
690
+ function resolveNativeTools(start) {
691
+ const toolFiltering = start.builtinToolFiltering;
692
+ if (toolFiltering == null) return void 0;
693
+ const activeToolNames = toolFiltering.mode === "allow" ? toolFiltering.toolNames : PUBLIC_TOOL_NAMES.filter(
694
+ (name) => !toolFiltering.toolNames.includes(name)
695
+ );
696
+ return activeToolNames.map((name) => toNativeName(name));
697
+ }
698
+ function resolveInactiveNativeTools(start) {
699
+ const toolFiltering = start.builtinToolFiltering;
700
+ if (toolFiltering == null) return [];
701
+ const inactiveToolNames = toolFiltering.mode === "allow" ? PUBLIC_TOOL_NAMES.filter(
702
+ (name) => !toolFiltering.toolNames.includes(name)
703
+ ) : toolFiltering.toolNames;
704
+ return inactiveToolNames.map((name) => toNativeName(name));
705
+ }
706
+ var args = parseArgs(argv.slice(2));
707
+ var workdir = args.workdir;
708
+ var bridgeStateDir = args.bridgeStateDir;
709
+ if (!workdir) {
710
+ emitFatal("Missing --workdir argument.");
711
+ }
712
+ if (!bridgeStateDir) {
713
+ emitFatal("Missing --bridge-state-dir argument.");
714
+ }
715
+ var claudeSdk = claudeAgentSdk;
716
+ var mcpModule = mcpServerModule;
717
+ await runBridge({
718
+ bridgeType: "claude-code",
719
+ bridgeStateDir,
720
+ onStart: runTurn,
721
+ // Claude Code's session state lives in the workdir on the sandbox filesystem
722
+ // (captured by the sandbox snapshot on stop); the resume payload is empty.
723
+ onDetach: () => ({})
724
+ });
725
+ function createPermissionOptions(input) {
726
+ const permissionMode = input.start.permissionMode ?? "allow-all";
727
+ const inactiveNativeTools = new Set(input.inactiveNativeTools);
728
+ const permissionSettings = createPermissionSettings({
729
+ permissionMode,
730
+ inactiveNativeTools
731
+ });
732
+ if (permissionMode === "allow-all" && inactiveNativeTools.size === 0) {
733
+ return {
734
+ permissionMode: "bypassPermissions",
735
+ allowDangerouslySkipPermissions: true
736
+ };
737
+ }
738
+ return {
739
+ permissionMode: permissionMode === "allow-edits" ? "acceptEdits" : "default",
740
+ allowDangerouslySkipPermissions: false,
741
+ ...permissionSettings ? { settings: permissionSettings } : {},
742
+ canUseTool: async (toolName, toolInput, options) => {
743
+ if (toolName.startsWith("mcp__harness-tools__")) {
744
+ return { behavior: "allow", updatedInput: toolInput };
745
+ }
746
+ if (!inactiveNativeTools.has(toolName) && !nativeToolRequiresApproval({
747
+ nativeName: toolName,
748
+ permissionMode
749
+ })) {
750
+ return { behavior: "allow", updatedInput: toolInput };
751
+ }
752
+ const approvalId = options.toolUseID;
753
+ input.approvalRequestedToolUseIds.add(approvalId);
754
+ input.nativeToolCallNames.set(approvalId, toolName);
755
+ input.emit({
756
+ type: "tool-call",
757
+ toolCallId: approvalId,
758
+ toolName: toCommonName(toolName),
759
+ nativeName: toolName,
760
+ input: JSON.stringify(toolInput ?? {}),
761
+ providerExecuted: true
762
+ });
763
+ input.emit({
764
+ type: "tool-approval-request",
765
+ approvalId,
766
+ toolCallId: approvalId
767
+ });
768
+ input.finishApprovalStep(approvalId);
769
+ const decision = await input.turn.requestToolApproval(approvalId);
770
+ return decision.approved ? { behavior: "allow", updatedInput: toolInput, toolUseID: approvalId } : {
771
+ behavior: "deny",
772
+ message: decision.reason ?? "Denied",
773
+ toolUseID: approvalId
774
+ };
775
+ }
776
+ };
777
+ }
778
+ function createPermissionSettings(input) {
779
+ const askRules = /* @__PURE__ */ new Set();
780
+ for (const [nativeName, kind] of Object.entries(NATIVE_TOOL_KINDS)) {
781
+ if (input.inactiveNativeTools.has(nativeName) || (input.permissionMode === "allow-reads" ? kind === "edit" || kind === "bash" : input.permissionMode === "allow-edits" ? kind === "bash" : false)) {
782
+ askRules.add(`${nativeName}(*)`);
783
+ }
784
+ }
785
+ if (askRules.size === 0) return void 0;
786
+ return {
787
+ permissions: { ask: [...askRules] },
788
+ sandbox: { autoAllowBashIfSandboxed: false }
789
+ };
790
+ }
791
+ function nativeToolRequiresApproval(input) {
792
+ if (input.permissionMode === "allow-all") return false;
793
+ const kind = NATIVE_TOOL_KINDS[input.nativeName] ?? "edit";
794
+ if (input.permissionMode === "allow-edits") return kind === "bash";
795
+ return kind === "edit" || kind === "bash";
796
+ }
797
+ async function runTurn(start, turn) {
798
+ const emit = (msg) => turn.emit(msg);
799
+ const abortCtl = new AbortController();
800
+ if (turn.abortSignal.aborted) {
801
+ abortCtl.abort();
802
+ } else {
803
+ turn.abortSignal.addEventListener("abort", () => abortCtl.abort(), {
804
+ once: true
805
+ });
806
+ }
807
+ const nativeToolCallNames = /* @__PURE__ */ new Map();
808
+ const approvalRequestedToolUseIds = /* @__PURE__ */ new Set();
809
+ const partialBlocks = /* @__PURE__ */ new Map();
810
+ let stepUsage;
811
+ let pendingStepToolUseIds = /* @__PURE__ */ new Set();
812
+ let pendingStepUsage;
813
+ let stepOpen = false;
814
+ const emitFinishStep = (usage) => {
815
+ emit({
816
+ type: "finish-step",
817
+ finishReason: { unified: "stop", raw: "stop" },
818
+ usage: usage ?? defaultUsage()
819
+ });
820
+ stepUsage = usage ?? stepUsage;
821
+ pendingStepUsage = void 0;
822
+ pendingStepToolUseIds = /* @__PURE__ */ new Set();
823
+ stepOpen = false;
824
+ };
825
+ const closeStepIfReady = () => {
826
+ if (!stepOpen || pendingStepToolUseIds.size > 0 || partialBlocks.size > 0) {
827
+ return;
828
+ }
829
+ emitFinishStep(pendingStepUsage);
830
+ };
831
+ const mcpToolUseIds = /* @__PURE__ */ new Set();
832
+ const mcpServers = {};
833
+ if (start.tools && start.tools.length > 0) {
834
+ const server = new mcpModule.McpServer({
835
+ name: "harness-tools",
836
+ version: "1.0.0"
837
+ });
838
+ for (const tool of start.tools) {
839
+ const shape = jsonSchemaToZodShape(tool.inputSchema);
840
+ server.tool(
841
+ tool.name,
842
+ tool.description ?? "",
843
+ shape,
844
+ async (input) => {
845
+ const toolCallId = randomUUID();
846
+ emit({
847
+ type: "tool-call",
848
+ toolCallId,
849
+ toolName: tool.name,
850
+ input: JSON.stringify(input),
851
+ providerExecuted: false
852
+ });
853
+ const { output, isError } = await turn.requestToolResult(toolCallId);
854
+ emit({
855
+ type: "tool-result",
856
+ toolCallId,
857
+ toolName: tool.name,
858
+ result: output ?? null,
859
+ isError: !!isError
860
+ });
861
+ return {
862
+ content: [{ type: "text", text: JSON.stringify(output ?? null) }],
863
+ isError
864
+ };
865
+ }
866
+ );
867
+ }
868
+ mcpServers["harness-tools"] = {
869
+ type: "sdk",
870
+ name: "harness-tools",
871
+ instance: server
872
+ };
873
+ }
874
+ const compaction = createCompactionLatch((event) => emit(event));
875
+ const queryInput = createQueryInput({
876
+ initialUserMessage: start.prompt,
877
+ pendingUserMessages: turn.pendingUserMessages,
878
+ abortSignal: abortCtl.signal
879
+ });
880
+ const skillsOption = toClaudeSkillsOption(start.skills);
881
+ const nativeTools = resolveNativeTools(start);
882
+ const inactiveNativeTools = resolveInactiveNativeTools(start);
883
+ const permissionOptions = createPermissionOptions({
884
+ start,
885
+ inactiveNativeTools,
886
+ turn,
887
+ emit,
888
+ finishApprovalStep: (approvalId) => {
889
+ stepOpen = true;
890
+ pendingStepToolUseIds.delete(approvalId);
891
+ closeStepIfReady();
892
+ },
893
+ nativeToolCallNames,
894
+ approvalRequestedToolUseIds
895
+ });
896
+ const q = claudeSdk.query({
897
+ prompt: queryInput.input,
898
+ options: {
899
+ ...start.model ? { model: start.model } : {},
900
+ ...start.maxTurns !== void 0 ? { maxTurns: start.maxTurns } : {},
901
+ ...skillsOption ? { skills: skillsOption } : {},
902
+ ...nativeTools !== void 0 ? { tools: nativeTools } : {},
903
+ ...inactiveNativeTools.length > 0 ? { disallowedTools: inactiveNativeTools } : {},
904
+ thinking: start.thinking,
905
+ includePartialMessages: true,
906
+ // The `PostCompact` hook carries the compaction summary, which the
907
+ // `compact_boundary` system message does not. Latch it for the unified
908
+ // `compaction` event; return an empty output so compaction proceeds.
909
+ hooks: {
910
+ PostCompact: [
911
+ {
912
+ hooks: [
913
+ async (input) => {
914
+ if (typeof input?.compact_summary === "string") {
915
+ compaction.onSummary(input.compact_summary);
916
+ }
917
+ return {};
918
+ }
919
+ ]
920
+ }
921
+ ]
922
+ },
923
+ // Continuation rule: the host can force-continue (resume after a
924
+ // cross-process detach) by setting `start.continue: true`; otherwise
925
+ // we continue every subsequent turn after the first one in this
926
+ // bridge process.
927
+ ...start.continue === true || !turn.firstTurn ? { continue: true } : {},
928
+ ...permissionOptions,
929
+ mcpServers,
930
+ cwd: workdir,
931
+ abortSignal: abortCtl.signal
932
+ }
933
+ });
934
+ turn.onInterrupt(() => q.interrupt());
935
+ let turnUsage;
936
+ let totalCostUsd;
937
+ let observedTerminalError;
938
+ let emittedTerminalError = false;
939
+ let emittedTerminalFinish = false;
940
+ let streamStarted = false;
941
+ const emitTerminalError = (message) => {
942
+ const normalized = message?.trim();
943
+ if (!normalized || emittedTerminalError || emittedTerminalFinish) return;
944
+ observedTerminalError = normalized;
945
+ emittedTerminalError = true;
946
+ turn.emitError({
947
+ error: normalized,
948
+ message: "claude-code terminal error"
949
+ });
950
+ queryInput.close();
951
+ abortCtl.abort();
952
+ };
953
+ try {
954
+ for await (const msg of q) {
955
+ if (abortCtl.signal.aborted) break;
956
+ const type = msg.type;
957
+ if (!streamStarted) {
958
+ const initModel = type === "system" && msg.subtype === "init" && typeof msg.model === "string" ? msg.model : void 0;
959
+ emit({
960
+ type: "stream-start",
961
+ ...initModel ? { modelId: initModel } : {}
962
+ });
963
+ streamStarted = true;
964
+ }
965
+ if (type === "system" && msg.subtype === "api_retry") {
966
+ if (typeof msg.error_status === "number" && UNRECOVERABLE_API_RETRY_STATUSES.has(msg.error_status)) {
967
+ emitTerminalError(
968
+ `HTTP ${msg.error_status}: ${msg.error ?? "provider request failed"}`
969
+ );
970
+ continue;
971
+ }
972
+ turn.emitWarning({ message: formatApiRetryWarning(msg) });
973
+ continue;
974
+ }
975
+ if (typeof msg.error === "string" && msg.error.trim()) {
976
+ observedTerminalError = msg.error.trim();
977
+ }
978
+ if (type === "auth_status" && typeof msg.error === "string" && msg.error.trim()) {
979
+ emitTerminalError(msg.error);
980
+ continue;
981
+ }
982
+ if (type === "system" && msg.subtype === "task_updated" && msg.patch?.status === "failed" && typeof msg.patch.error === "string") {
983
+ emitTerminalError(msg.patch.error);
984
+ continue;
985
+ }
986
+ if (type === "system" && msg.subtype === "compact_boundary") {
987
+ const meta = msg.compact_metadata;
988
+ if (meta) {
989
+ compaction.onBoundary({
990
+ trigger: meta.trigger,
991
+ ...typeof meta.pre_tokens === "number" ? { tokensBefore: meta.pre_tokens } : {},
992
+ ...typeof meta.post_tokens === "number" ? { tokensAfter: meta.post_tokens } : {}
993
+ });
994
+ }
995
+ continue;
996
+ }
997
+ if (type === "stream_event") {
998
+ handleStreamEvent(msg.event, partialBlocks, emit);
999
+ continue;
1000
+ }
1001
+ if (type === "assistant" && msg.message?.content) {
1002
+ const usage = mapUsage(msg.message.usage);
1003
+ const toolUseIds = [];
1004
+ let opensStep = false;
1005
+ for (const block of msg.message.content) {
1006
+ if (block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") {
1007
+ toolUseIds.push(block.id);
1008
+ const mcpPrefix = "mcp__harness-tools__";
1009
+ if (block.name.startsWith(mcpPrefix)) {
1010
+ pendingStepToolUseIds.add(block.id);
1011
+ mcpToolUseIds.add(block.id);
1012
+ opensStep = true;
1013
+ continue;
1014
+ }
1015
+ nativeToolCallNames.set(block.id, block.name);
1016
+ if (approvalRequestedToolUseIds.has(block.id)) {
1017
+ continue;
1018
+ }
1019
+ pendingStepToolUseIds.add(block.id);
1020
+ opensStep = true;
1021
+ emit({
1022
+ type: "tool-call",
1023
+ toolCallId: block.id,
1024
+ toolName: toCommonName(block.name),
1025
+ nativeName: block.name,
1026
+ input: JSON.stringify(block.input ?? {}),
1027
+ providerExecuted: true
1028
+ });
1029
+ }
1030
+ }
1031
+ if (opensStep || toolUseIds.length === 0) {
1032
+ stepOpen = true;
1033
+ if (usage) pendingStepUsage = usage;
1034
+ }
1035
+ continue;
1036
+ }
1037
+ if (type === "user" && msg.message?.content) {
1038
+ for (const block of msg.message.content) {
1039
+ if (block.type === "tool_result" && typeof block.tool_use_id === "string") {
1040
+ if (mcpToolUseIds.has(block.tool_use_id)) {
1041
+ mcpToolUseIds.delete(block.tool_use_id);
1042
+ pendingStepToolUseIds.delete(block.tool_use_id);
1043
+ continue;
1044
+ }
1045
+ approvalRequestedToolUseIds.delete(block.tool_use_id);
1046
+ const nativeName = nativeToolCallNames.get(block.tool_use_id) ?? "unknown";
1047
+ nativeToolCallNames.delete(block.tool_use_id);
1048
+ const toolName = toCommonName(nativeName);
1049
+ const isError = !!block.is_error;
1050
+ const content = stringifyContent(block.content);
1051
+ const result = toolName === "bash" ? { exitCode: isError ? 1 : 0, stdout: content } : content;
1052
+ emit({
1053
+ type: "tool-result",
1054
+ toolCallId: block.tool_use_id,
1055
+ toolName,
1056
+ result,
1057
+ isError
1058
+ });
1059
+ pendingStepToolUseIds.delete(block.tool_use_id);
1060
+ }
1061
+ }
1062
+ closeStepIfReady();
1063
+ continue;
1064
+ }
1065
+ if (type === "result") {
1066
+ if (msg.subtype === "success") {
1067
+ const emptyResult = !msg.result?.trim?.();
1068
+ if (emptyResult && observedTerminalError) {
1069
+ emitTerminalError(observedTerminalError);
1070
+ continue;
1071
+ }
1072
+ const usage = msg.usage ?? msg.message?.usage;
1073
+ const harnessUsage = mapUsage(usage);
1074
+ if (harnessUsage) turnUsage = harnessUsage;
1075
+ if (typeof msg.total_cost_usd === "number") {
1076
+ totalCostUsd = (totalCostUsd ?? 0) + msg.total_cost_usd;
1077
+ }
1078
+ if (stepOpen) emitFinishStep(harnessUsage ?? pendingStepUsage);
1079
+ queryInput.close();
1080
+ break;
1081
+ } else {
1082
+ emitTerminalError(
1083
+ (Array.isArray(msg.errors) ? msg.errors.join("\n") : void 0) || observedTerminalError || msg.result || "Unknown error"
1084
+ );
1085
+ }
1086
+ continue;
1087
+ }
1088
+ }
1089
+ } catch (err) {
1090
+ if (!(abortCtl.signal.aborted && emittedTerminalError)) {
1091
+ turn.emitError({ error: err, message: "claude-code turn failed" });
1092
+ }
1093
+ return;
1094
+ } finally {
1095
+ queryInput.close();
1096
+ }
1097
+ if (emittedTerminalError) return;
1098
+ emittedTerminalFinish = true;
1099
+ void emittedTerminalFinish;
1100
+ emit({
1101
+ type: "finish",
1102
+ finishReason: { unified: "stop", raw: "stop" },
1103
+ totalUsage: turnUsage ?? stepUsage ?? defaultUsage(),
1104
+ ...totalCostUsd !== void 0 ? { harnessMetadata: { "claude-code": { costUsd: totalCostUsd } } } : {}
1105
+ });
1106
+ }
1107
+ function formatApiRetryWarning(msg) {
1108
+ const details = [];
1109
+ if (typeof msg.attempt === "number") {
1110
+ const maxRetries = typeof msg.max_retries === "number" ? `/${msg.max_retries}` : "";
1111
+ details.push(`attempt ${msg.attempt}${maxRetries}`);
1112
+ }
1113
+ if (typeof msg.error_status === "number") {
1114
+ details.push(`HTTP ${msg.error_status}`);
1115
+ }
1116
+ if (typeof msg.retry_delay_ms === "number") {
1117
+ details.push(`retrying in ${msg.retry_delay_ms}ms`);
1118
+ }
1119
+ if (msg.error) details.push(msg.error);
1120
+ return details.length > 0 ? `Claude Code API retry: ${details.join("; ")}` : "Claude Code API retry";
1121
+ }
1122
+ function handleStreamEvent(event, partialBlocks, send) {
1123
+ if (!event || typeof event.index !== "number") return;
1124
+ const index = event.index;
1125
+ if (event.type === "content_block_start") {
1126
+ const blockType = event.content_block?.type;
1127
+ if (blockType === "text") {
1128
+ const id = randomUUID();
1129
+ partialBlocks.set(index, { id, kind: "text" });
1130
+ send({ type: "text-start", id });
1131
+ } else if (blockType === "thinking") {
1132
+ const id = randomUUID();
1133
+ partialBlocks.set(index, { id, kind: "thinking" });
1134
+ send({ type: "reasoning-start", id });
1135
+ }
1136
+ return;
1137
+ }
1138
+ if (event.type === "content_block_delta") {
1139
+ const block = partialBlocks.get(index);
1140
+ if (!block) return;
1141
+ if (block.kind === "text" && event.delta?.type === "text_delta" && typeof event.delta.text === "string") {
1142
+ send({ type: "text-delta", id: block.id, delta: event.delta.text });
1143
+ } else if (block.kind === "thinking" && event.delta?.type === "thinking_delta" && typeof event.delta.thinking === "string") {
1144
+ send({
1145
+ type: "reasoning-delta",
1146
+ id: block.id,
1147
+ delta: event.delta.thinking
1148
+ });
1149
+ }
1150
+ return;
1151
+ }
1152
+ if (event.type === "content_block_stop") {
1153
+ const block = partialBlocks.get(index);
1154
+ if (!block) return;
1155
+ partialBlocks.delete(index);
1156
+ if (block.kind === "text") {
1157
+ send({ type: "text-end", id: block.id });
1158
+ } else {
1159
+ send({ type: "reasoning-end", id: block.id });
1160
+ }
1161
+ }
1162
+ }
1163
+ function createQueryInput({
1164
+ initialUserMessage,
1165
+ pendingUserMessages,
1166
+ abortSignal
1167
+ }) {
1168
+ let closed = false;
1169
+ const close = () => {
1170
+ closed = true;
1171
+ };
1172
+ if (abortSignal.aborted) {
1173
+ close();
1174
+ } else {
1175
+ abortSignal.addEventListener("abort", close, { once: true });
1176
+ }
1177
+ const toUserMessage = (text) => ({
1178
+ type: "user",
1179
+ message: {
1180
+ role: "user",
1181
+ content: [{ type: "text", text }]
1182
+ }
1183
+ });
1184
+ return {
1185
+ close,
1186
+ input: {
1187
+ [Symbol.asyncIterator]() {
1188
+ let sentInitial = false;
1189
+ return {
1190
+ async next() {
1191
+ while (!closed && !abortSignal.aborted) {
1192
+ if (!sentInitial) {
1193
+ sentInitial = true;
1194
+ return {
1195
+ value: toUserMessage(initialUserMessage),
1196
+ done: false
1197
+ };
1198
+ }
1199
+ if (pendingUserMessages.length > 0) {
1200
+ return {
1201
+ value: toUserMessage(pendingUserMessages.shift()),
1202
+ done: false
1203
+ };
1204
+ }
1205
+ await new Promise((resolve) => setTimeout(resolve, 50));
1206
+ }
1207
+ return { value: void 0, done: true };
1208
+ }
1209
+ };
1210
+ }
1211
+ }
1212
+ };
1213
+ }
1214
+ function stringifyContent(content) {
1215
+ if (typeof content === "string") return content;
1216
+ if (Array.isArray(content)) {
1217
+ return content.map(
1218
+ (entry) => entry && typeof entry === "object" && "text" in entry ? String(entry.text ?? "") : JSON.stringify(entry)
1219
+ ).join("");
1220
+ }
1221
+ return JSON.stringify(content);
1222
+ }
1223
+ function mapUsage(usage) {
1224
+ if (!usage || typeof usage !== "object") return void 0;
1225
+ const u = usage;
1226
+ return {
1227
+ inputTokens: {
1228
+ total: (u.input_tokens ?? 0) + (u.cache_creation_input_tokens ?? 0) + (u.cache_read_input_tokens ?? 0),
1229
+ noCache: u.input_tokens ?? 0,
1230
+ cacheRead: u.cache_read_input_tokens ?? 0,
1231
+ cacheWrite: u.cache_creation_input_tokens ?? 0
1232
+ },
1233
+ outputTokens: {
1234
+ total: u.output_tokens ?? 0,
1235
+ text: u.output_tokens ?? 0
1236
+ }
1237
+ };
1238
+ }
1239
+ function defaultUsage() {
1240
+ return {
1241
+ inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
1242
+ outputTokens: { total: 0, text: 0 }
1243
+ };
1244
+ }
1245
+ function parseArgs(args2) {
1246
+ const out = {};
1247
+ for (let i = 0; i < args2.length; i++) {
1248
+ if (args2[i] === "--workdir" && i + 1 < args2.length) {
1249
+ out.workdir = args2[++i];
1250
+ } else if (args2[i] === "--bridge-state-dir" && i + 1 < args2.length) {
1251
+ out.bridgeStateDir = args2[++i];
1252
+ }
1253
+ }
1254
+ return out;
1255
+ }
1256
+ function emitFatal(message) {
1257
+ stdout2.write(JSON.stringify({ type: "bridge-fatal", message }) + "\n");
1258
+ process.exit(1);
1259
+ }
1260
+ //# sourceMappingURL=index.mjs.map