@ai-sdk/harness-deepagents 1.0.77 → 1.0.79
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/bridge/index.mjs +147 -12
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +86 -27
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/deepagents-harness.ts +115 -30
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @ai-sdk/harness-deepagents
|
|
2
2
|
|
|
3
|
+
## 1.0.79
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @ai-sdk/harness@1.0.79
|
|
8
|
+
|
|
9
|
+
## 1.0.78
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- eace6fb: feat(harness): add experimental support for steering agent conversations mid-turn
|
|
14
|
+
- c0595b4: feat(harness): support passing a filesystem and process restricted sandbox session to `HarnessAgentSession`, using fallbacks in favor of the preferred network sandbox session methods
|
|
15
|
+
- Updated dependencies [eace6fb]
|
|
16
|
+
- Updated dependencies [c0595b4]
|
|
17
|
+
- @ai-sdk/harness@1.0.78
|
|
18
|
+
|
|
3
19
|
## 1.0.77
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// src/bridge/index.ts
|
|
2
|
-
import { randomUUID as
|
|
2
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
3
3
|
import { argv, env as procEnv2 } from "process";
|
|
4
4
|
|
|
5
5
|
// ../harness/dist/bridge/index.js
|
|
6
6
|
import { appendFile, mkdir, writeFile } from "fs/promises";
|
|
7
7
|
import { existsSync, readFileSync } from "fs";
|
|
8
|
+
import { randomUUID } from "crypto";
|
|
8
9
|
import { env as procEnv, pid, stdout } from "process";
|
|
9
10
|
import { WebSocketServer } from "ws";
|
|
10
11
|
var DEBUG_LEVEL_WEIGHT = {
|
|
@@ -35,6 +36,106 @@ function formatBridgeError(err) {
|
|
|
35
36
|
}
|
|
36
37
|
return { message: String(err) };
|
|
37
38
|
}
|
|
39
|
+
function createBridgeUserMessageQueue(options) {
|
|
40
|
+
const messages = [];
|
|
41
|
+
const waiters = [];
|
|
42
|
+
const entries = /* @__PURE__ */ new Map();
|
|
43
|
+
let closed = false;
|
|
44
|
+
let pendingCount = 0;
|
|
45
|
+
const enqueue = (input) => {
|
|
46
|
+
const existing = entries.get(input.messageId);
|
|
47
|
+
if (existing != null) {
|
|
48
|
+
if (existing.response != null) {
|
|
49
|
+
options.respond(existing.response);
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
let settled = false;
|
|
54
|
+
const settle = (response) => {
|
|
55
|
+
if (settled) return;
|
|
56
|
+
settled = true;
|
|
57
|
+
pendingCount--;
|
|
58
|
+
const entry = entries.get(input.messageId);
|
|
59
|
+
if (entry != null) entry.response = response;
|
|
60
|
+
options.respond(response);
|
|
61
|
+
};
|
|
62
|
+
const message = {
|
|
63
|
+
messageId: input.messageId,
|
|
64
|
+
text: input.text,
|
|
65
|
+
accept: () => {
|
|
66
|
+
settle({
|
|
67
|
+
type: "user-message-response",
|
|
68
|
+
messageId: input.messageId,
|
|
69
|
+
accepted: true
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
reject: (error) => {
|
|
73
|
+
settle({
|
|
74
|
+
type: "user-message-response",
|
|
75
|
+
messageId: input.messageId,
|
|
76
|
+
accepted: false,
|
|
77
|
+
error: { message: formatBridgeError(error).message }
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
entries.set(input.messageId, {
|
|
82
|
+
reject: message.reject
|
|
83
|
+
});
|
|
84
|
+
pendingCount++;
|
|
85
|
+
if (closed) {
|
|
86
|
+
message.reject(
|
|
87
|
+
new Error("The bridge turn is no longer accepting user messages.")
|
|
88
|
+
);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const waiter = waiters.shift();
|
|
92
|
+
if (waiter != null) {
|
|
93
|
+
waiter({ done: false, value: message });
|
|
94
|
+
} else {
|
|
95
|
+
messages.push(message);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const close = (error) => {
|
|
99
|
+
if (closed) return;
|
|
100
|
+
closed = true;
|
|
101
|
+
const reason = error ?? new Error("The bridge turn ended before accepting the user message.");
|
|
102
|
+
for (const entry of entries.values()) {
|
|
103
|
+
if (entry.response == null) entry.reject(reason);
|
|
104
|
+
}
|
|
105
|
+
messages.length = 0;
|
|
106
|
+
while (waiters.length > 0) {
|
|
107
|
+
waiters.shift()({ done: true, value: void 0 });
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
return {
|
|
111
|
+
get pendingCount() {
|
|
112
|
+
return pendingCount;
|
|
113
|
+
},
|
|
114
|
+
enqueue,
|
|
115
|
+
close,
|
|
116
|
+
[Symbol.asyncIterator]() {
|
|
117
|
+
return {
|
|
118
|
+
next: () => {
|
|
119
|
+
const message = messages.shift();
|
|
120
|
+
if (message != null) {
|
|
121
|
+
return Promise.resolve({ done: false, value: message });
|
|
122
|
+
}
|
|
123
|
+
if (closed) {
|
|
124
|
+
return Promise.resolve({
|
|
125
|
+
done: true,
|
|
126
|
+
value: void 0
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return new Promise(
|
|
130
|
+
(resolve) => {
|
|
131
|
+
waiters.push(resolve);
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
38
139
|
function parseEnvList(value) {
|
|
39
140
|
if (!value) return void 0;
|
|
40
141
|
const items = value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
@@ -251,6 +352,9 @@ async function runBridge(options) {
|
|
|
251
352
|
switch (msg.type) {
|
|
252
353
|
case "start": {
|
|
253
354
|
activeSocket = ws;
|
|
355
|
+
currentUserMessages?.close(
|
|
356
|
+
new Error("A new bridge turn replaced the active turn.")
|
|
357
|
+
);
|
|
254
358
|
const firstTurn = isFirstTurn;
|
|
255
359
|
isFirstTurn = false;
|
|
256
360
|
eventLog = [];
|
|
@@ -270,6 +374,7 @@ async function runBridge(options) {
|
|
|
270
374
|
if (debugConfig.enabled) {
|
|
271
375
|
installConsoleCapture();
|
|
272
376
|
}
|
|
377
|
+
const userMessages = createBridgeUserMessageQueue({ respond: emit });
|
|
273
378
|
const turn = {
|
|
274
379
|
emit,
|
|
275
380
|
requestToolResult: (toolCallId) => new Promise((resolve) => {
|
|
@@ -278,7 +383,7 @@ async function runBridge(options) {
|
|
|
278
383
|
requestToolApproval: (approvalId) => new Promise((resolve) => {
|
|
279
384
|
pendingToolApprovals.set(approvalId, resolve);
|
|
280
385
|
}),
|
|
281
|
-
|
|
386
|
+
experimental_userMessages: userMessages,
|
|
282
387
|
abortSignal: turnAbort.signal,
|
|
283
388
|
firstTurn,
|
|
284
389
|
bridgeLog: (input) => {
|
|
@@ -296,12 +401,16 @@ async function runBridge(options) {
|
|
|
296
401
|
emitWarning,
|
|
297
402
|
emitError
|
|
298
403
|
};
|
|
299
|
-
currentUserMessages =
|
|
404
|
+
currentUserMessages = userMessages;
|
|
300
405
|
try {
|
|
301
406
|
await onStart(msg, turn);
|
|
302
407
|
} catch (err) {
|
|
303
408
|
emitError({ error: err, message: "bridge turn failed" });
|
|
304
409
|
} finally {
|
|
410
|
+
userMessages.close();
|
|
411
|
+
if (currentUserMessages === userMessages) {
|
|
412
|
+
currentUserMessages = void 0;
|
|
413
|
+
}
|
|
305
414
|
currentTurnState = "waiting";
|
|
306
415
|
void writeBridgeMeta("waiting");
|
|
307
416
|
}
|
|
@@ -323,9 +432,34 @@ async function runBridge(options) {
|
|
|
323
432
|
}
|
|
324
433
|
return;
|
|
325
434
|
}
|
|
326
|
-
case "user-message":
|
|
327
|
-
|
|
435
|
+
case "user-message": {
|
|
436
|
+
const messageId = msg.messageId ?? randomUUID();
|
|
437
|
+
if (currentUserMessages == null) {
|
|
438
|
+
sendControl(ws, {
|
|
439
|
+
type: "user-message-response",
|
|
440
|
+
messageId,
|
|
441
|
+
accepted: false,
|
|
442
|
+
error: { message: "The bridge has no active turn to steer." }
|
|
443
|
+
});
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (ws !== activeSocket) {
|
|
447
|
+
sendControl(ws, {
|
|
448
|
+
type: "user-message-response",
|
|
449
|
+
messageId,
|
|
450
|
+
accepted: false,
|
|
451
|
+
error: {
|
|
452
|
+
message: "The connection does not own the active bridge turn."
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
currentUserMessages.enqueue({
|
|
458
|
+
messageId,
|
|
459
|
+
text: msg.text
|
|
460
|
+
});
|
|
328
461
|
return;
|
|
462
|
+
}
|
|
329
463
|
case "abort":
|
|
330
464
|
turnAbort?.abort();
|
|
331
465
|
return;
|
|
@@ -399,7 +533,8 @@ async function runBridge(options) {
|
|
|
399
533
|
sendControl(ws, {
|
|
400
534
|
type: "bridge-hello",
|
|
401
535
|
state: currentTurnState,
|
|
402
|
-
lastSeq: seqCounter
|
|
536
|
+
lastSeq: seqCounter,
|
|
537
|
+
capabilities: { experimental_userMessageResponses: true }
|
|
403
538
|
});
|
|
404
539
|
ws.on("message", (raw) => {
|
|
405
540
|
let parsed;
|
|
@@ -1423,7 +1558,7 @@ function collectActionRequests(interrupts) {
|
|
|
1423
1558
|
}
|
|
1424
1559
|
|
|
1425
1560
|
// src/bridge/create-emit-stream-event.ts
|
|
1426
|
-
import { randomUUID } from "crypto";
|
|
1561
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
1427
1562
|
function createDeepAgentsStreamEventState() {
|
|
1428
1563
|
return {
|
|
1429
1564
|
streamStarted: false,
|
|
@@ -1607,7 +1742,7 @@ function ensureTextBlock({
|
|
|
1607
1742
|
emit
|
|
1608
1743
|
}) {
|
|
1609
1744
|
if (!state.textBlockId) {
|
|
1610
|
-
state.textBlockId = `text-${
|
|
1745
|
+
state.textBlockId = `text-${randomUUID2()}`;
|
|
1611
1746
|
emit({ type: "text-start", id: state.textBlockId });
|
|
1612
1747
|
}
|
|
1613
1748
|
return state.textBlockId;
|
|
@@ -1627,7 +1762,7 @@ function emitReasoning({
|
|
|
1627
1762
|
}) {
|
|
1628
1763
|
endTextBlock({ state, emit });
|
|
1629
1764
|
if (!state.reasoningBlockId) {
|
|
1630
|
-
state.reasoningBlockId = `reasoning-${
|
|
1765
|
+
state.reasoningBlockId = `reasoning-${randomUUID2()}`;
|
|
1631
1766
|
emit({ type: "reasoning-start", id: state.reasoningBlockId });
|
|
1632
1767
|
}
|
|
1633
1768
|
emit({ type: "reasoning-delta", id: state.reasoningBlockId, delta });
|
|
@@ -1883,7 +2018,7 @@ function buildHostTools(toolSchemas) {
|
|
|
1883
2018
|
async (input) => {
|
|
1884
2019
|
const turn = currentTurn;
|
|
1885
2020
|
if (!turn) throw new Error("no active turn");
|
|
1886
|
-
const toolCallId = `${schema.name}-${
|
|
2021
|
+
const toolCallId = `${schema.name}-${randomUUID3()}`;
|
|
1887
2022
|
turn.emit({
|
|
1888
2023
|
type: "tool-call",
|
|
1889
2024
|
toolCallId,
|
|
@@ -2001,7 +2136,7 @@ async function runTurn(start, turn) {
|
|
|
2001
2136
|
const namespace = streamEvent.metadata?.langgraph_checkpoint_ns ?? "";
|
|
2002
2137
|
const output = streamEvent.data?.output;
|
|
2003
2138
|
if (!emittedStructuredOutput && streamEvent.event === "on_chain_end" && !namespace.includes("|") && output?.structuredResponse !== void 0) {
|
|
2004
|
-
const id = `structured-output-${
|
|
2139
|
+
const id = `structured-output-${randomUUID3()}`;
|
|
2005
2140
|
emit({ type: "text-start", id });
|
|
2006
2141
|
emit({
|
|
2007
2142
|
type: "text-delta",
|
|
@@ -2016,7 +2151,7 @@ async function runTurn(start, turn) {
|
|
|
2016
2151
|
if (actionRequests.length === 0) break;
|
|
2017
2152
|
const decisions = [];
|
|
2018
2153
|
for (const action of actionRequests) {
|
|
2019
|
-
const approvalId = `approval-${
|
|
2154
|
+
const approvalId = `approval-${randomUUID3()}`;
|
|
2020
2155
|
endTextBlock({ state: streamEventState, emit });
|
|
2021
2156
|
endReasoningBlock({ state: streamEventState, emit });
|
|
2022
2157
|
emit({
|