@ai-sdk/harness-deepagents 0.0.0 → 1.0.1

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