@ai-sdk/harness-deepagents 0.0.0-6b196531-20260710185421

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,1030 @@
1
+ // src/bridge/index.ts
2
+ import { randomUUID } from "crypto";
3
+ import { argv, env as procEnv2 } from "process";
4
+
5
+ // ../harness/dist/bridge/index.js
6
+ import { appendFile, mkdir, writeFile } from "fs/promises";
7
+ import { existsSync, readFileSync } from "fs";
8
+ import { env as procEnv, pid, stdout } from "process";
9
+ import { WebSocketServer } from "ws";
10
+ var DEBUG_LEVEL_WEIGHT = {
11
+ error: 0,
12
+ warn: 1,
13
+ info: 2,
14
+ debug: 3,
15
+ trace: 4
16
+ };
17
+ function subsystemMatches(filters, subsystem) {
18
+ if (!filters || filters.length === 0) return true;
19
+ return filters.some(
20
+ (filter) => subsystem === filter || subsystem.startsWith(`${filter}.`)
21
+ );
22
+ }
23
+ function formatBridgeError(err) {
24
+ if (err instanceof Error) {
25
+ return { name: err.name, message: err.message, stack: err.stack };
26
+ }
27
+ if (typeof err === "string") {
28
+ return { message: err };
29
+ }
30
+ if (err !== null && typeof err === "object") {
31
+ try {
32
+ return { message: JSON.stringify(err) };
33
+ } catch {
34
+ }
35
+ }
36
+ return { message: String(err) };
37
+ }
38
+ function parseEnvList(value) {
39
+ if (!value) return void 0;
40
+ const items = value.split(",").map((item) => item.trim()).filter(Boolean);
41
+ return items.length > 0 ? items : void 0;
42
+ }
43
+ var ENV_TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
44
+ var WS_OPEN = 1;
45
+ async function runBridge(options) {
46
+ const { bridgeType, bridgeStateDir: bridgeStateDir2, onStart, onDetach } = options;
47
+ const expectedToken = options.token ?? procEnv.BRIDGE_CHANNEL_TOKEN ?? "";
48
+ const bridgeWsPort = options.port ?? parseInt(procEnv.BRIDGE_WS_PORT ?? "0", 10);
49
+ const bridgeMetaPath = `${bridgeStateDir2}/bridge-meta.json`;
50
+ const startConfigPath = `${bridgeStateDir2}/start-config.json`;
51
+ const rerunStartConfigPath = `${bridgeStateDir2}/rerun-start-config.json`;
52
+ const eventLogPath = `${bridgeStateDir2}/event-log.ndjson`;
53
+ try {
54
+ await mkdir(bridgeStateDir2, { recursive: true });
55
+ } catch {
56
+ }
57
+ let currentBoundPort = 0;
58
+ let currentTurnState = "init";
59
+ let activeSocket;
60
+ let isFirstTurn = true;
61
+ let turnAbort;
62
+ let currentUserMessages;
63
+ let currentInterruptHandler;
64
+ let debugConfig;
65
+ let consoleCaptureInstalled = false;
66
+ const envDebugEnabled = ENV_TRUTHY.has(
67
+ (procEnv.HARNESS_DEBUG ?? "").toLowerCase()
68
+ );
69
+ let seqCounter = 0;
70
+ let eventLog = [];
71
+ let diskBuffer = "";
72
+ let flushPromise = null;
73
+ const flushEventsToDisk = async () => {
74
+ while (diskBuffer.length > 0) {
75
+ const buf = diskBuffer;
76
+ diskBuffer = "";
77
+ await appendFile(eventLogPath, buf).catch(() => {
78
+ });
79
+ }
80
+ };
81
+ const scheduleEventFlush = () => {
82
+ if (flushPromise) return;
83
+ flushPromise = new Promise((resolve) => {
84
+ setImmediate(() => {
85
+ void flushEventsToDisk().finally(resolve);
86
+ });
87
+ }).finally(() => {
88
+ flushPromise = null;
89
+ if (diskBuffer.length > 0) {
90
+ scheduleEventFlush();
91
+ }
92
+ });
93
+ };
94
+ const flushPendingEventsToDisk = async () => {
95
+ if (diskBuffer.length > 0 && !flushPromise) {
96
+ scheduleEventFlush();
97
+ }
98
+ let inFlight = flushPromise;
99
+ while (inFlight) {
100
+ await inFlight;
101
+ inFlight = flushPromise;
102
+ }
103
+ };
104
+ const replayFromDisk = procEnv.BRIDGE_REPLAY_FROM_DISK === "1";
105
+ if (replayFromDisk && existsSync(eventLogPath)) {
106
+ try {
107
+ const lines = readFileSync(eventLogPath, "utf8").split("\n").map((line) => line.trim()).filter(Boolean);
108
+ eventLog = lines.map((line) => ({
109
+ seq: JSON.parse(line).seq,
110
+ line
111
+ }));
112
+ seqCounter = eventLog.at(-1)?.seq ?? 0;
113
+ } catch {
114
+ eventLog = [];
115
+ seqCounter = 0;
116
+ }
117
+ }
118
+ const pendingToolResults = /* @__PURE__ */ new Map();
119
+ const pendingToolApprovals = /* @__PURE__ */ new Map();
120
+ const writeBridgeMeta = async (state) => {
121
+ try {
122
+ await writeFile(
123
+ bridgeMetaPath,
124
+ JSON.stringify({
125
+ type: bridgeType,
126
+ port: currentBoundPort,
127
+ state,
128
+ pid
129
+ })
130
+ );
131
+ } catch {
132
+ }
133
+ };
134
+ const writeStartConfig = async (start) => {
135
+ try {
136
+ const serialized = JSON.stringify(start);
137
+ await writeFile(startConfigPath, serialized);
138
+ if (!existsSync(rerunStartConfigPath)) {
139
+ await writeFile(rerunStartConfigPath, serialized);
140
+ }
141
+ } catch {
142
+ }
143
+ };
144
+ const sendControl = (msg) => {
145
+ if (activeSocket?.readyState === WS_OPEN) {
146
+ try {
147
+ activeSocket.send(JSON.stringify(msg));
148
+ } catch {
149
+ }
150
+ }
151
+ };
152
+ const emit = (event) => {
153
+ const seq = ++seqCounter;
154
+ const line = JSON.stringify({ ...event, seq });
155
+ eventLog.push({ seq, line });
156
+ diskBuffer += `${line}
157
+ `;
158
+ scheduleEventFlush();
159
+ if (activeSocket?.readyState === WS_OPEN) {
160
+ try {
161
+ activeSocket.send(line);
162
+ } catch {
163
+ }
164
+ }
165
+ };
166
+ const replay = (ws, afterSeq) => {
167
+ for (const entry of eventLog) {
168
+ if (entry.seq > afterSeq && ws.readyState === WS_OPEN) {
169
+ ws.send(entry.line);
170
+ }
171
+ }
172
+ };
173
+ const shouldEmitDebugEvent = (level, subsystem) => {
174
+ if (!debugConfig?.enabled) return false;
175
+ const threshold = debugConfig.level ?? "debug";
176
+ if (DEBUG_LEVEL_WEIGHT[level] > DEBUG_LEVEL_WEIGHT[threshold]) return false;
177
+ return subsystemMatches(debugConfig.subsystems, subsystem);
178
+ };
179
+ const rawStdoutWrite = process.stdout.write.bind(process.stdout);
180
+ const rawStderrWrite = process.stderr.write.bind(process.stderr);
181
+ const writeErrorToStderr = (input) => {
182
+ try {
183
+ const formatted = formatBridgeError(input.error);
184
+ rawStderrWrite(
185
+ `[harness:${bridgeType}:error] ${input.message}: ${formatted.message}
186
+ `
187
+ );
188
+ if (formatted.stack) {
189
+ rawStderrWrite(`${formatted.stack}
190
+ `);
191
+ }
192
+ } catch {
193
+ }
194
+ };
195
+ const emitWarning = (input) => {
196
+ try {
197
+ for (const line of input.message.split("\n")) {
198
+ if (line.trim().length > 0) {
199
+ rawStderrWrite(`[harness:${bridgeType}:warn] ${line}
200
+ `);
201
+ }
202
+ }
203
+ } catch {
204
+ }
205
+ };
206
+ const emitError = (input) => {
207
+ writeErrorToStderr({
208
+ message: input.message ?? "bridge error",
209
+ error: input.error
210
+ });
211
+ emit({ type: "error", error: serialiseError(input.error) });
212
+ };
213
+ const installConsoleCapture = () => {
214
+ if (consoleCaptureInstalled) return;
215
+ consoleCaptureInstalled = true;
216
+ const buffers = {
217
+ stdout: "",
218
+ stderr: ""
219
+ };
220
+ const patch = (stream, raw) => (chunk, encoding, cb) => {
221
+ if (debugConfig?.enabled) {
222
+ try {
223
+ const enc = typeof encoding === "string" ? encoding : "utf8";
224
+ const text = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString(
225
+ enc
226
+ );
227
+ const combined = buffers[stream] + text.replace(/\r\n/g, "\n");
228
+ const parts = combined.split("\n");
229
+ buffers[stream] = parts.pop() ?? "";
230
+ for (const line of parts) {
231
+ const trimmed = line.replace(/\s+$/, "");
232
+ if (trimmed) {
233
+ emit({
234
+ type: "sandbox-log",
235
+ source: bridgeType,
236
+ stream,
237
+ line: trimmed
238
+ });
239
+ }
240
+ }
241
+ } catch {
242
+ }
243
+ }
244
+ return raw(
245
+ chunk,
246
+ encoding,
247
+ cb
248
+ );
249
+ };
250
+ process.stdout.write = patch(
251
+ "stdout",
252
+ rawStdoutWrite
253
+ );
254
+ process.stderr.write = patch(
255
+ "stderr",
256
+ rawStderrWrite
257
+ );
258
+ };
259
+ const handleInbound = async (msg, ws) => {
260
+ switch (msg.type) {
261
+ case "start": {
262
+ const firstTurn = isFirstTurn;
263
+ isFirstTurn = false;
264
+ eventLog = [];
265
+ diskBuffer = "";
266
+ void writeFile(eventLogPath, "").catch(() => {
267
+ });
268
+ turnAbort = new AbortController();
269
+ currentTurnState = "running";
270
+ currentInterruptHandler = void 0;
271
+ void writeStartConfig(msg);
272
+ void writeBridgeMeta("running");
273
+ const startDebug = msg.debug;
274
+ debugConfig = {
275
+ enabled: startDebug?.enabled ?? envDebugEnabled,
276
+ level: startDebug?.level ?? procEnv.HARNESS_DEBUG_LEVEL,
277
+ subsystems: startDebug?.subsystems ?? parseEnvList(procEnv.HARNESS_DEBUG_SUBSYSTEMS)
278
+ };
279
+ if (debugConfig.enabled) {
280
+ installConsoleCapture();
281
+ }
282
+ const turn = {
283
+ emit,
284
+ requestToolResult: (toolCallId) => new Promise((resolve) => {
285
+ pendingToolResults.set(toolCallId, resolve);
286
+ }),
287
+ requestToolApproval: (approvalId) => new Promise((resolve) => {
288
+ pendingToolApprovals.set(approvalId, resolve);
289
+ }),
290
+ pendingUserMessages: [],
291
+ abortSignal: turnAbort.signal,
292
+ onInterrupt: (handler) => {
293
+ currentInterruptHandler = handler;
294
+ },
295
+ firstTurn,
296
+ bridgeLog: (input) => {
297
+ const level = input.level ?? "debug";
298
+ if (!shouldEmitDebugEvent(level, input.subsystem)) return;
299
+ emit({
300
+ type: "debug-event",
301
+ level,
302
+ subsystem: input.subsystem,
303
+ message: input.message,
304
+ ...input.attrs ? { attrs: input.attrs } : {},
305
+ ...input.error !== void 0 ? { error: formatBridgeError(input.error) } : {}
306
+ });
307
+ },
308
+ emitWarning,
309
+ emitError
310
+ };
311
+ currentUserMessages = turn.pendingUserMessages;
312
+ try {
313
+ await onStart(msg, turn);
314
+ } catch (err) {
315
+ emitError({ error: err, message: "bridge turn failed" });
316
+ } finally {
317
+ currentInterruptHandler = void 0;
318
+ currentTurnState = "waiting";
319
+ void writeBridgeMeta("waiting");
320
+ }
321
+ return;
322
+ }
323
+ case "tool-result": {
324
+ const resolver = pendingToolResults.get(msg.toolCallId);
325
+ if (resolver) {
326
+ pendingToolResults.delete(msg.toolCallId);
327
+ resolver({ output: msg.output, isError: msg.isError });
328
+ }
329
+ return;
330
+ }
331
+ case "tool-approval-response": {
332
+ const resolver = pendingToolApprovals.get(msg.approvalId);
333
+ if (resolver) {
334
+ pendingToolApprovals.delete(msg.approvalId);
335
+ resolver({ approved: msg.approved, reason: msg.reason });
336
+ }
337
+ return;
338
+ }
339
+ case "user-message":
340
+ currentUserMessages?.push(msg.text);
341
+ return;
342
+ case "abort":
343
+ turnAbort?.abort();
344
+ return;
345
+ case "interrupt":
346
+ try {
347
+ if (pendingToolResults.size === 0 && pendingToolApprovals.size === 0) {
348
+ if (currentInterruptHandler) {
349
+ await currentInterruptHandler();
350
+ } else {
351
+ turnAbort?.abort();
352
+ }
353
+ }
354
+ sendControl({ type: "bridge-interrupted", ok: true });
355
+ } catch (err) {
356
+ sendControl({
357
+ type: "bridge-interrupted",
358
+ ok: false,
359
+ error: serialiseError(err)
360
+ });
361
+ }
362
+ return;
363
+ case "resume":
364
+ replay(ws, msg.lastSeenEventId);
365
+ return;
366
+ case "shutdown":
367
+ currentTurnState = "done";
368
+ void writeBridgeMeta("done");
369
+ drainThenExit(ws, 1e3, "shutdown");
370
+ return;
371
+ case "detach": {
372
+ currentTurnState = "done";
373
+ void writeBridgeMeta("done");
374
+ const data = await onDetach?.() ?? {};
375
+ sendControl({ type: "bridge-detach", data });
376
+ drainThenExit(ws, 1e3, "detach");
377
+ return;
378
+ }
379
+ }
380
+ };
381
+ void writeBridgeMeta("init");
382
+ const wss = new WebSocketServer({ port: bridgeWsPort, host: "0.0.0.0" });
383
+ const exit = () => {
384
+ if (options.onExit) {
385
+ options.onExit();
386
+ return;
387
+ }
388
+ wss.close(() => process.exit(0));
389
+ setTimeout(() => process.exit(0), 1e3).unref();
390
+ };
391
+ const drainThenExit = (ws, code, reason) => {
392
+ const start = Date.now();
393
+ const tick = () => {
394
+ const drained = ws.bufferedAmount === 0 || ws.readyState !== WS_OPEN;
395
+ if (drained || Date.now() - start >= 5e3) {
396
+ void flushPendingEventsToDisk().finally(() => {
397
+ try {
398
+ ws.close(code, reason);
399
+ } finally {
400
+ exit();
401
+ }
402
+ });
403
+ return;
404
+ }
405
+ setTimeout(tick, 10).unref();
406
+ };
407
+ tick();
408
+ };
409
+ wss.on("listening", () => {
410
+ const addr = wss.address();
411
+ currentBoundPort = typeof addr === "object" && addr ? addr.port : 0;
412
+ currentTurnState = "waiting";
413
+ void writeBridgeMeta("waiting");
414
+ stdout.write(
415
+ JSON.stringify({
416
+ type: "bridge-ready",
417
+ port: currentBoundPort
418
+ }) + "\n"
419
+ );
420
+ options.onListening?.(currentBoundPort);
421
+ });
422
+ wss.on("connection", (ws, req) => {
423
+ const url = new URL(req.url ?? "/", "http://localhost");
424
+ if (url.searchParams.get("agent_bridge_token") !== expectedToken) {
425
+ ws.close(1008, "unauthorized");
426
+ return;
427
+ }
428
+ activeSocket = ws;
429
+ sendControl({
430
+ type: "bridge-hello",
431
+ state: currentTurnState,
432
+ lastSeq: seqCounter
433
+ });
434
+ ws.on("message", (raw) => {
435
+ let parsed;
436
+ try {
437
+ const text = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
438
+ parsed = JSON.parse(text);
439
+ } catch (err) {
440
+ sendControl({
441
+ type: "error",
442
+ error: `protocol parse error: ${err.message}`
443
+ });
444
+ return;
445
+ }
446
+ void handleInbound(parsed, ws);
447
+ });
448
+ ws.on("close", () => {
449
+ if (activeSocket === ws) {
450
+ activeSocket = void 0;
451
+ }
452
+ });
453
+ ws.on("error", () => {
454
+ });
455
+ });
456
+ process.on("uncaughtException", (err) => {
457
+ emitError({ error: err, message: "uncaught exception" });
458
+ });
459
+ process.on("unhandledRejection", (err) => {
460
+ emitError({ error: err, message: "unhandled rejection" });
461
+ });
462
+ await new Promise((resolve, reject) => {
463
+ if (wss.address() != null) {
464
+ resolve();
465
+ return;
466
+ }
467
+ wss.once("listening", resolve);
468
+ wss.once("error", reject);
469
+ });
470
+ return {
471
+ port: currentBoundPort,
472
+ close: () => new Promise((resolve) => {
473
+ wss.close(() => resolve());
474
+ })
475
+ };
476
+ }
477
+ function serialiseError(err) {
478
+ if (err instanceof Error) {
479
+ return { name: err.name, message: err.message, stack: err.stack };
480
+ }
481
+ return err;
482
+ }
483
+
484
+ // src/bridge/index.ts
485
+ import { ChatAnthropic } from "@langchain/anthropic";
486
+ import { tool } from "@langchain/core/tools";
487
+ import { Command, MemorySaver } from "@langchain/langgraph";
488
+ import { createDeepAgent } from "deepagents";
489
+
490
+ // src/bridge/approvals.ts
491
+ var NATIVE_TOOL_KIND = {
492
+ read_file: "readonly",
493
+ write_file: "edit",
494
+ edit_file: "edit",
495
+ execute: "bash",
496
+ grep: "readonly",
497
+ glob: "readonly",
498
+ ls: "readonly",
499
+ task: "edit",
500
+ write_todos: "edit"
501
+ };
502
+ var NATIVE_TO_COMMON = {
503
+ read_file: "read",
504
+ write_file: "write",
505
+ edit_file: "edit",
506
+ execute: "bash"
507
+ };
508
+ function toCommonName(nativeName) {
509
+ return NATIVE_TO_COMMON[nativeName] ?? nativeName;
510
+ }
511
+ function isBuiltinToolIncluded(input) {
512
+ if (input.toolFiltering == null) return true;
513
+ const toolName = toCommonName(input.nativeName);
514
+ return input.toolFiltering.mode === "allow" ? input.toolFiltering.toolNames.includes(toolName) : !input.toolFiltering.toolNames.includes(toolName);
515
+ }
516
+ function builtinToolRequiresApproval(kind, permissionMode) {
517
+ if (permissionMode === "allow-all") return false;
518
+ if (permissionMode === "allow-edits") return kind === "bash";
519
+ return kind === "edit" || kind === "bash";
520
+ }
521
+ function buildInterruptOn(permissionMode, builtinToolFiltering) {
522
+ const config = {};
523
+ for (const [nativeName, kind] of Object.entries(NATIVE_TOOL_KIND)) {
524
+ if (permissionMode != null && isBuiltinToolIncluded({
525
+ nativeName,
526
+ toolFiltering: builtinToolFiltering
527
+ }) && builtinToolRequiresApproval(kind, permissionMode)) {
528
+ config[nativeName] = { allowedDecisions: ["approve", "reject"] };
529
+ }
530
+ }
531
+ return Object.keys(config).length > 0 ? config : void 0;
532
+ }
533
+ function collectActionRequests(interrupts) {
534
+ const out = [];
535
+ for (const interrupt of interrupts) {
536
+ const value = interrupt.value;
537
+ for (const action of value?.actionRequests ?? []) {
538
+ out.push({ name: action.name, args: action.args ?? {} });
539
+ }
540
+ }
541
+ return out;
542
+ }
543
+
544
+ // src/bridge/json-schema-to-zod.ts
545
+ import { z } from "zod/v4";
546
+ function jsonSchemaToZodObject(input) {
547
+ const schema = input && typeof input === "object" ? input : {};
548
+ return z.object(toZodShape(schema));
549
+ }
550
+ function toZodShape(schema) {
551
+ if (!schema.properties) return {};
552
+ const required = new Set(schema.required ?? []);
553
+ const shape = {};
554
+ for (const [key, propSchema] of Object.entries(schema.properties)) {
555
+ const propType = toZodType(propSchema);
556
+ shape[key] = required.has(key) ? propType : propType.optional();
557
+ }
558
+ return shape;
559
+ }
560
+ function toZodType(schema) {
561
+ if (!schema) return z.any();
562
+ const types = Array.isArray(schema.type) ? schema.type.filter((t) => t !== "null") : [schema.type].filter(Boolean);
563
+ let zType;
564
+ switch (types[0]) {
565
+ case "string":
566
+ zType = z.string();
567
+ break;
568
+ case "number":
569
+ zType = z.number();
570
+ break;
571
+ case "integer":
572
+ zType = z.number().int();
573
+ break;
574
+ case "boolean":
575
+ zType = z.boolean();
576
+ break;
577
+ case "array":
578
+ zType = z.array(toZodType(schema.items));
579
+ break;
580
+ case "object":
581
+ zType = z.object(toZodShape(schema));
582
+ break;
583
+ case "null":
584
+ zType = z.null();
585
+ break;
586
+ default:
587
+ zType = z.any();
588
+ }
589
+ if (schema.description) zType = zType.describe(schema.description);
590
+ if (schema.nullable) zType = zType.nullable();
591
+ return zType;
592
+ }
593
+
594
+ // src/bridge/local-shell-backend.ts
595
+ import { LocalShellBackend } from "deepagents";
596
+ var SANDBOX_PATH_FALLBACK = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
597
+ function createLocalShellBackend({
598
+ rootDir,
599
+ env = process.env
600
+ }) {
601
+ return new LocalShellBackend({
602
+ rootDir,
603
+ env: {
604
+ PATH: env.PATH ?? SANDBOX_PATH_FALLBACK
605
+ }
606
+ });
607
+ }
608
+
609
+ // src/bridge/tool-filtering.ts
610
+ import {
611
+ AIMessage,
612
+ ToolMessage
613
+ } from "@langchain/core/messages";
614
+ var MIDDLEWARE_BRAND = /* @__PURE__ */ Symbol.for("AgentMiddleware");
615
+ function getBuiltinToolFilteringDenialReason(input) {
616
+ return `Tool '${input.toolName}' is inactive due to the HarnessAgent tool filtering policy.`;
617
+ }
618
+ function stringifyToolInput(input) {
619
+ return JSON.stringify(input ?? {});
620
+ }
621
+ function isNativeBuiltinToolCall(input) {
622
+ return Object.keys(NATIVE_TOOL_KIND).includes(input.toolCall.name);
623
+ }
624
+ function isInactiveBuiltinToolCall(input) {
625
+ return isNativeBuiltinToolCall({ toolCall: input.toolCall }) && !isBuiltinToolIncluded({
626
+ nativeName: input.toolCall.name,
627
+ toolFiltering: input.builtinToolFiltering
628
+ });
629
+ }
630
+ function createBuiltinToolFilteringMiddleware(input) {
631
+ if (input.builtinToolFiltering == null) return void 0;
632
+ return {
633
+ [MIDDLEWARE_BRAND]: true,
634
+ name: "HarnessBuiltinToolFilteringMiddleware",
635
+ afterModel: {
636
+ canJumpTo: ["model"],
637
+ hook: (state) => {
638
+ const lastMessage = [...state.messages].reverse().find((message) => AIMessage.isInstance(message));
639
+ if (!lastMessage?.tool_calls?.length) return void 0;
640
+ let hasActiveToolCalls = false;
641
+ const deniedToolMessages = [];
642
+ for (const toolCall of lastMessage.tool_calls) {
643
+ if (!isInactiveBuiltinToolCall({
644
+ toolCall,
645
+ builtinToolFiltering: input.builtinToolFiltering
646
+ })) {
647
+ hasActiveToolCalls = true;
648
+ continue;
649
+ }
650
+ const nativeName = toolCall.name;
651
+ toolCall.id ??= `${nativeName}-filtered-${deniedToolMessages.length}`;
652
+ const toolName = toCommonName(nativeName);
653
+ const reason = getBuiltinToolFilteringDenialReason({ toolName });
654
+ input.emit({
655
+ type: "tool-call",
656
+ toolCallId: toolCall.id,
657
+ toolName,
658
+ input: stringifyToolInput(toolCall.args),
659
+ providerExecuted: true,
660
+ nativeName
661
+ });
662
+ input.emit({
663
+ type: "tool-result",
664
+ toolCallId: toolCall.id,
665
+ toolName,
666
+ result: reason
667
+ });
668
+ deniedToolMessages.push(
669
+ new ToolMessage({
670
+ content: reason,
671
+ name: nativeName,
672
+ tool_call_id: toolCall.id,
673
+ status: "error"
674
+ })
675
+ );
676
+ }
677
+ if (deniedToolMessages.length === 0) return void 0;
678
+ return {
679
+ messages: [lastMessage, ...deniedToolMessages],
680
+ ...hasActiveToolCalls ? {} : { jumpTo: "model" }
681
+ };
682
+ }
683
+ }
684
+ };
685
+ }
686
+
687
+ // src/bridge/index.ts
688
+ var NATIVE_TO_COMMON2 = {
689
+ read_file: "read",
690
+ write_file: "write",
691
+ edit_file: "edit",
692
+ execute: "bash"
693
+ };
694
+ var HARNESS_CLIENT_APP = procEnv2.AI_SDK_HARNESS_CLIENT_APP;
695
+ function toCommonName2(nativeName) {
696
+ return NATIVE_TO_COMMON2[nativeName] ?? nativeName;
697
+ }
698
+ function parseArgs(rawArgs) {
699
+ const out = {};
700
+ for (let i = 0; i < rawArgs.length; i++) {
701
+ const arg = rawArgs[i];
702
+ if (arg.startsWith("--")) {
703
+ const key = arg.slice(2).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
704
+ out[key] = rawArgs[i + 1];
705
+ i++;
706
+ }
707
+ }
708
+ return out;
709
+ }
710
+ function buildModel(rawModel) {
711
+ if (!rawModel) return void 0;
712
+ const baseUrl = procEnv2.ANTHROPIC_BASE_URL;
713
+ const model = baseUrl ? rawModel : rawModel.replace(/^anthropic[/:]/, "");
714
+ return new ChatAnthropic({
715
+ model,
716
+ ...procEnv2.ANTHROPIC_API_KEY ? { apiKey: procEnv2.ANTHROPIC_API_KEY } : {},
717
+ ...baseUrl ? { anthropicApiUrl: baseUrl } : {},
718
+ ...procEnv2.AI_GATEWAY_API_KEY && HARNESS_CLIENT_APP ? {
719
+ clientOptions: {
720
+ defaultHeaders: {
721
+ "User-Agent": HARNESS_CLIENT_APP,
722
+ "x-client-app": HARNESS_CLIENT_APP
723
+ }
724
+ }
725
+ } : {}
726
+ });
727
+ }
728
+ function toToolCallInput(raw) {
729
+ if (raw && typeof raw === "object" && !Array.isArray(raw) && Object.keys(raw).length === 1 && typeof raw.input === "string") {
730
+ const inner = raw.input;
731
+ if (/^\s*[[{]/.test(inner)) return inner;
732
+ }
733
+ return JSON.stringify(raw ?? {});
734
+ }
735
+ var args = parseArgs(argv.slice(2));
736
+ var workdir = args.workdir;
737
+ var bridgeStateDir = args.bridgeStateDir;
738
+ if (!workdir || !bridgeStateDir) {
739
+ console.error("deepagents bridge: missing --workdir / --bridge-state-dir");
740
+ process.exit(1);
741
+ }
742
+ var agent;
743
+ var currentTurn;
744
+ function buildHostTools(toolSchemas) {
745
+ return (toolSchemas ?? []).map(
746
+ (schema) => tool(
747
+ async (input) => {
748
+ const turn = currentTurn;
749
+ if (!turn) throw new Error("no active turn");
750
+ const toolCallId = `${schema.name}-${randomUUID()}`;
751
+ turn.emit({
752
+ type: "tool-call",
753
+ toolCallId,
754
+ toolName: schema.name,
755
+ input: JSON.stringify(input),
756
+ providerExecuted: false
757
+ });
758
+ const { output } = await turn.requestToolResult(toolCallId);
759
+ return typeof output === "string" ? output : JSON.stringify(output);
760
+ },
761
+ {
762
+ name: schema.name,
763
+ description: schema.description ?? "",
764
+ schema: jsonSchemaToZodObject(schema.inputSchema)
765
+ }
766
+ )
767
+ );
768
+ }
769
+ async function runTurn(start, turn) {
770
+ currentTurn = turn;
771
+ const emit = (event) => turn.emit(event);
772
+ const interruptOn = buildInterruptOn(
773
+ start.permissionMode,
774
+ start.builtinToolFiltering
775
+ );
776
+ if (!agent) {
777
+ const model = buildModel(start.model);
778
+ const builtinToolFilteringMiddleware = createBuiltinToolFilteringMiddleware(
779
+ {
780
+ builtinToolFiltering: start.builtinToolFiltering,
781
+ emit: (event) => {
782
+ const turn2 = currentTurn;
783
+ if (!turn2) throw new Error("no active turn");
784
+ turn2.emit(event);
785
+ }
786
+ }
787
+ );
788
+ agent = createDeepAgent({
789
+ // Defer to Deep Agents's own default when the host configured no model.
790
+ ...model ? { model } : {},
791
+ tools: buildHostTools(start.tools),
792
+ backend: createLocalShellBackend({ rootDir: workdir }),
793
+ systemPrompt: start.instructions || void 0,
794
+ // Native skills loaded from the source dirs ($HOME-materialized + <workDir> for repo-provided skills).
795
+ ...start.skillsPaths?.length ? { skills: start.skillsPaths } : {},
796
+ ...builtinToolFilteringMiddleware ? { middleware: [builtinToolFilteringMiddleware] } : {},
797
+ // Gate built-in tools behind HITL approval when the permission mode requires it.
798
+ ...interruptOn ? { interruptOn } : {},
799
+ // Real instance (LangGraph rejects `true` for root graphs); gives multi-turn memory.
800
+ checkpointer: new MemorySaver()
801
+ });
802
+ }
803
+ emit({
804
+ type: "stream-start",
805
+ ...start.model ? { modelId: start.model } : {}
806
+ });
807
+ const hostToolNames = new Set((start.tools ?? []).map((t) => t.name));
808
+ let textBlockId;
809
+ let reasoningBlockId;
810
+ let inputTokens = 0;
811
+ let outputTokens = 0;
812
+ let streamedStepInput = 0;
813
+ let streamedStepOutput = 0;
814
+ let pendingStep;
815
+ const approvedToolQueue = /* @__PURE__ */ new Map();
816
+ const approvedRunIds = /* @__PURE__ */ new Map();
817
+ const ensureTextBlock = () => {
818
+ if (!textBlockId) {
819
+ textBlockId = `text-${randomUUID()}`;
820
+ emit({ type: "text-start", id: textBlockId });
821
+ }
822
+ return textBlockId;
823
+ };
824
+ const endTextBlock = () => {
825
+ if (textBlockId) {
826
+ emit({ type: "text-end", id: textBlockId });
827
+ textBlockId = void 0;
828
+ }
829
+ };
830
+ const endReasoningBlock = () => {
831
+ if (reasoningBlockId) {
832
+ emit({ type: "reasoning-end", id: reasoningBlockId });
833
+ reasoningBlockId = void 0;
834
+ }
835
+ };
836
+ const emitText = (delta) => {
837
+ endReasoningBlock();
838
+ emit({ type: "text-delta", id: ensureTextBlock(), delta });
839
+ };
840
+ const emitReasoning = (delta) => {
841
+ endTextBlock();
842
+ if (!reasoningBlockId) {
843
+ reasoningBlockId = `reasoning-${randomUUID()}`;
844
+ emit({ type: "reasoning-start", id: reasoningBlockId });
845
+ }
846
+ emit({ type: "reasoning-delta", id: reasoningBlockId, delta });
847
+ };
848
+ const flushStep = () => {
849
+ if (!pendingStep) return;
850
+ emit({
851
+ type: "finish-step",
852
+ finishReason: { unified: "stop" },
853
+ usage: {
854
+ inputTokens: { total: pendingStep.input },
855
+ outputTokens: { total: pendingStep.output }
856
+ }
857
+ });
858
+ pendingStep = void 0;
859
+ };
860
+ const config = {
861
+ version: "v2",
862
+ configurable: { thread_id: "bridge-session" },
863
+ recursionLimit: start.recursionLimit ?? 100,
864
+ signal: turn.abortSignal
865
+ };
866
+ const readPendingApprovals = async () => {
867
+ try {
868
+ const state = await agent.getState({
869
+ configurable: { thread_id: "bridge-session" }
870
+ });
871
+ return collectActionRequests(
872
+ (state.tasks ?? []).flatMap((t) => t.interrupts ?? [])
873
+ );
874
+ } catch {
875
+ return [];
876
+ }
877
+ };
878
+ let resumeInput = {
879
+ messages: [{ role: "user", content: start.prompt }]
880
+ };
881
+ while (true) {
882
+ const stream = await agent.streamEvents(resumeInput, config);
883
+ for await (const event of stream) {
884
+ const kind = event.event;
885
+ const data = event.data ?? {};
886
+ const ns = event.metadata?.langgraph_checkpoint_ns ?? "";
887
+ const nested = ns.includes("|");
888
+ if (kind === "on_chat_model_start") {
889
+ if (!nested) flushStep();
890
+ } else if (kind === "on_chat_model_stream") {
891
+ if (nested) continue;
892
+ const chunk = data.chunk;
893
+ if (!chunk) continue;
894
+ const content = chunk.content;
895
+ if (typeof content === "string" && content) {
896
+ emitText(content);
897
+ } else if (Array.isArray(content)) {
898
+ for (const block of content) {
899
+ if (block && typeof block === "object") {
900
+ const b = block;
901
+ if (b.type === "text" && b.text) emitText(b.text);
902
+ else if (b.type === "thinking" && b.thinking)
903
+ emitReasoning(b.thinking);
904
+ }
905
+ }
906
+ }
907
+ const usage = chunk.usage_metadata;
908
+ if (usage) {
909
+ streamedStepInput = Math.max(
910
+ streamedStepInput,
911
+ usage.input_tokens ?? 0
912
+ );
913
+ streamedStepOutput = Math.max(
914
+ streamedStepOutput,
915
+ usage.output_tokens ?? 0
916
+ );
917
+ }
918
+ } else if (kind === "on_chat_model_end") {
919
+ const output = data.output;
920
+ const usage = output?.usage_metadata;
921
+ const stepInput = usage?.input_tokens ?? streamedStepInput;
922
+ const stepOutput = usage?.output_tokens ?? streamedStepOutput;
923
+ inputTokens += stepInput;
924
+ outputTokens += stepOutput;
925
+ streamedStepInput = 0;
926
+ streamedStepOutput = 0;
927
+ if (!nested) {
928
+ endTextBlock();
929
+ endReasoningBlock();
930
+ pendingStep = { input: stepInput, output: stepOutput };
931
+ }
932
+ } else if (kind === "on_tool_start") {
933
+ const toolName = event.name ?? "unknown";
934
+ const runId = event.run_id ?? "";
935
+ if (!nested && !hostToolNames.has(toolName)) {
936
+ const queued = approvedToolQueue.get(toolName);
937
+ if (queued && queued.length > 0) {
938
+ const approvalId = queued.shift();
939
+ if (runId) approvedRunIds.set(runId, approvalId);
940
+ } else {
941
+ endTextBlock();
942
+ endReasoningBlock();
943
+ emit({
944
+ type: "tool-call",
945
+ toolCallId: runId,
946
+ toolName: toCommonName2(toolName),
947
+ input: toToolCallInput(data.input),
948
+ providerExecuted: true,
949
+ nativeName: toolName
950
+ });
951
+ }
952
+ }
953
+ } else if (kind === "on_tool_end") {
954
+ const toolName = event.name ?? "unknown";
955
+ const runId = event.run_id ?? "";
956
+ if (!nested && !hostToolNames.has(toolName)) {
957
+ let output = data.output ?? "";
958
+ if (output && typeof output === "object" && "content" in output) {
959
+ output = output.content;
960
+ }
961
+ emit({
962
+ type: "tool-result",
963
+ toolCallId: approvedRunIds.get(runId) ?? runId,
964
+ toolName: toCommonName2(toolName),
965
+ result: output ?? null
966
+ });
967
+ approvedRunIds.delete(runId);
968
+ }
969
+ }
970
+ }
971
+ const actionRequests = await readPendingApprovals();
972
+ if (actionRequests.length === 0) break;
973
+ const decisions = [];
974
+ for (const action of actionRequests) {
975
+ const approvalId = `approval-${randomUUID()}`;
976
+ endTextBlock();
977
+ endReasoningBlock();
978
+ emit({
979
+ type: "tool-call",
980
+ toolCallId: approvalId,
981
+ toolName: toCommonName2(action.name),
982
+ input: JSON.stringify(action.args ?? {}),
983
+ providerExecuted: true,
984
+ nativeName: action.name
985
+ });
986
+ emit({
987
+ type: "tool-approval-request",
988
+ approvalId,
989
+ toolCallId: approvalId
990
+ });
991
+ flushStep();
992
+ const decision = await turn.requestToolApproval(approvalId);
993
+ if (decision.approved) {
994
+ const queue = approvedToolQueue.get(action.name) ?? [];
995
+ queue.push(approvalId);
996
+ approvedToolQueue.set(action.name, queue);
997
+ decisions.push({ type: "approve" });
998
+ } else {
999
+ emit({
1000
+ type: "tool-result",
1001
+ toolCallId: approvalId,
1002
+ toolName: toCommonName2(action.name),
1003
+ result: decision.reason ?? "Rejected by user."
1004
+ });
1005
+ decisions.push({
1006
+ type: "reject",
1007
+ ...decision.reason ? { message: decision.reason } : {}
1008
+ });
1009
+ }
1010
+ }
1011
+ resumeInput = new Command({ resume: { decisions } });
1012
+ }
1013
+ endTextBlock();
1014
+ endReasoningBlock();
1015
+ flushStep();
1016
+ emit({
1017
+ type: "finish",
1018
+ finishReason: { unified: "stop" },
1019
+ totalUsage: {
1020
+ inputTokens: { total: inputTokens },
1021
+ outputTokens: { total: outputTokens }
1022
+ }
1023
+ });
1024
+ }
1025
+ await runBridge({
1026
+ bridgeType: "deepagents",
1027
+ bridgeStateDir,
1028
+ onStart: runTurn
1029
+ });
1030
+ //# sourceMappingURL=index.mjs.map