@ai-sdk/harness-acp 1.0.15 → 1.0.17
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/host-tool-mcp.mjs +1 -1
- package/dist/bridge/index.mjs +140 -5
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +82 -20
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/acp-harness.ts +7 -1
- package/src/v1/acp-v1-harness.ts +97 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @ai-sdk/harness-acp
|
|
2
2
|
|
|
3
|
+
## 1.0.17
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @ai-sdk/harness@1.0.79
|
|
8
|
+
|
|
9
|
+
## 1.0.16
|
|
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.15
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ListToolsRequestSchema,
|
|
13
13
|
McpError
|
|
14
14
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
15
|
-
var VERSION = true ? "1.0.
|
|
15
|
+
var VERSION = true ? "1.0.17" : "0.0.0-test";
|
|
16
16
|
function createHostToolMCPServer({
|
|
17
17
|
tools: tools2,
|
|
18
18
|
revision = 1,
|
package/dist/bridge/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// ../harness/dist/bridge/index.js
|
|
2
2
|
import { appendFile, mkdir, writeFile } from "fs/promises";
|
|
3
3
|
import { existsSync, readFileSync } from "fs";
|
|
4
|
+
import { randomUUID } from "crypto";
|
|
4
5
|
import { env as procEnv, pid, stdout } from "process";
|
|
5
6
|
import { WebSocketServer } from "ws";
|
|
6
7
|
var name = "AI_HarnessBridgeCapabilityUnsupportedError";
|
|
@@ -47,6 +48,106 @@ function formatBridgeError(err) {
|
|
|
47
48
|
}
|
|
48
49
|
return { message: String(err) };
|
|
49
50
|
}
|
|
51
|
+
function createBridgeUserMessageQueue(options) {
|
|
52
|
+
const messages = [];
|
|
53
|
+
const waiters = [];
|
|
54
|
+
const entries = /* @__PURE__ */ new Map();
|
|
55
|
+
let closed = false;
|
|
56
|
+
let pendingCount = 0;
|
|
57
|
+
const enqueue = (input) => {
|
|
58
|
+
const existing = entries.get(input.messageId);
|
|
59
|
+
if (existing != null) {
|
|
60
|
+
if (existing.response != null) {
|
|
61
|
+
options.respond(existing.response);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
let settled = false;
|
|
66
|
+
const settle = (response) => {
|
|
67
|
+
if (settled) return;
|
|
68
|
+
settled = true;
|
|
69
|
+
pendingCount--;
|
|
70
|
+
const entry = entries.get(input.messageId);
|
|
71
|
+
if (entry != null) entry.response = response;
|
|
72
|
+
options.respond(response);
|
|
73
|
+
};
|
|
74
|
+
const message = {
|
|
75
|
+
messageId: input.messageId,
|
|
76
|
+
text: input.text,
|
|
77
|
+
accept: () => {
|
|
78
|
+
settle({
|
|
79
|
+
type: "user-message-response",
|
|
80
|
+
messageId: input.messageId,
|
|
81
|
+
accepted: true
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
reject: (error) => {
|
|
85
|
+
settle({
|
|
86
|
+
type: "user-message-response",
|
|
87
|
+
messageId: input.messageId,
|
|
88
|
+
accepted: false,
|
|
89
|
+
error: { message: formatBridgeError(error).message }
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
entries.set(input.messageId, {
|
|
94
|
+
reject: message.reject
|
|
95
|
+
});
|
|
96
|
+
pendingCount++;
|
|
97
|
+
if (closed) {
|
|
98
|
+
message.reject(
|
|
99
|
+
new Error("The bridge turn is no longer accepting user messages.")
|
|
100
|
+
);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const waiter = waiters.shift();
|
|
104
|
+
if (waiter != null) {
|
|
105
|
+
waiter({ done: false, value: message });
|
|
106
|
+
} else {
|
|
107
|
+
messages.push(message);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const close = (error) => {
|
|
111
|
+
if (closed) return;
|
|
112
|
+
closed = true;
|
|
113
|
+
const reason = error ?? new Error("The bridge turn ended before accepting the user message.");
|
|
114
|
+
for (const entry of entries.values()) {
|
|
115
|
+
if (entry.response == null) entry.reject(reason);
|
|
116
|
+
}
|
|
117
|
+
messages.length = 0;
|
|
118
|
+
while (waiters.length > 0) {
|
|
119
|
+
waiters.shift()({ done: true, value: void 0 });
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
get pendingCount() {
|
|
124
|
+
return pendingCount;
|
|
125
|
+
},
|
|
126
|
+
enqueue,
|
|
127
|
+
close,
|
|
128
|
+
[Symbol.asyncIterator]() {
|
|
129
|
+
return {
|
|
130
|
+
next: () => {
|
|
131
|
+
const message = messages.shift();
|
|
132
|
+
if (message != null) {
|
|
133
|
+
return Promise.resolve({ done: false, value: message });
|
|
134
|
+
}
|
|
135
|
+
if (closed) {
|
|
136
|
+
return Promise.resolve({
|
|
137
|
+
done: true,
|
|
138
|
+
value: void 0
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return new Promise(
|
|
142
|
+
(resolve) => {
|
|
143
|
+
waiters.push(resolve);
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
50
151
|
function parseEnvList(value) {
|
|
51
152
|
if (!value) return void 0;
|
|
52
153
|
const items = value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
@@ -263,6 +364,9 @@ async function runBridge(options) {
|
|
|
263
364
|
switch (msg.type) {
|
|
264
365
|
case "start": {
|
|
265
366
|
activeSocket = ws;
|
|
367
|
+
currentUserMessages?.close(
|
|
368
|
+
new Error("A new bridge turn replaced the active turn.")
|
|
369
|
+
);
|
|
266
370
|
const firstTurn = isFirstTurn;
|
|
267
371
|
isFirstTurn = false;
|
|
268
372
|
eventLog = [];
|
|
@@ -282,6 +386,7 @@ async function runBridge(options) {
|
|
|
282
386
|
if (debugConfig.enabled) {
|
|
283
387
|
installConsoleCapture();
|
|
284
388
|
}
|
|
389
|
+
const userMessages = createBridgeUserMessageQueue({ respond: emit });
|
|
285
390
|
const turn = {
|
|
286
391
|
emit,
|
|
287
392
|
requestToolResult: (toolCallId) => new Promise((resolve) => {
|
|
@@ -290,7 +395,7 @@ async function runBridge(options) {
|
|
|
290
395
|
requestToolApproval: (approvalId) => new Promise((resolve) => {
|
|
291
396
|
pendingToolApprovals.set(approvalId, resolve);
|
|
292
397
|
}),
|
|
293
|
-
|
|
398
|
+
experimental_userMessages: userMessages,
|
|
294
399
|
abortSignal: turnAbort.signal,
|
|
295
400
|
firstTurn,
|
|
296
401
|
bridgeLog: (input) => {
|
|
@@ -308,12 +413,16 @@ async function runBridge(options) {
|
|
|
308
413
|
emitWarning,
|
|
309
414
|
emitError
|
|
310
415
|
};
|
|
311
|
-
currentUserMessages =
|
|
416
|
+
currentUserMessages = userMessages;
|
|
312
417
|
try {
|
|
313
418
|
await onStart(msg, turn);
|
|
314
419
|
} catch (err) {
|
|
315
420
|
emitError({ error: err, message: "bridge turn failed" });
|
|
316
421
|
} finally {
|
|
422
|
+
userMessages.close();
|
|
423
|
+
if (currentUserMessages === userMessages) {
|
|
424
|
+
currentUserMessages = void 0;
|
|
425
|
+
}
|
|
317
426
|
currentTurnState = "waiting";
|
|
318
427
|
void writeBridgeMeta("waiting");
|
|
319
428
|
}
|
|
@@ -335,9 +444,34 @@ async function runBridge(options) {
|
|
|
335
444
|
}
|
|
336
445
|
return;
|
|
337
446
|
}
|
|
338
|
-
case "user-message":
|
|
339
|
-
|
|
447
|
+
case "user-message": {
|
|
448
|
+
const messageId = msg.messageId ?? randomUUID();
|
|
449
|
+
if (currentUserMessages == null) {
|
|
450
|
+
sendControl(ws, {
|
|
451
|
+
type: "user-message-response",
|
|
452
|
+
messageId,
|
|
453
|
+
accepted: false,
|
|
454
|
+
error: { message: "The bridge has no active turn to steer." }
|
|
455
|
+
});
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if (ws !== activeSocket) {
|
|
459
|
+
sendControl(ws, {
|
|
460
|
+
type: "user-message-response",
|
|
461
|
+
messageId,
|
|
462
|
+
accepted: false,
|
|
463
|
+
error: {
|
|
464
|
+
message: "The connection does not own the active bridge turn."
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
currentUserMessages.enqueue({
|
|
470
|
+
messageId,
|
|
471
|
+
text: msg.text
|
|
472
|
+
});
|
|
340
473
|
return;
|
|
474
|
+
}
|
|
341
475
|
case "abort":
|
|
342
476
|
turnAbort?.abort();
|
|
343
477
|
return;
|
|
@@ -411,7 +545,8 @@ async function runBridge(options) {
|
|
|
411
545
|
sendControl(ws, {
|
|
412
546
|
type: "bridge-hello",
|
|
413
547
|
state: currentTurnState,
|
|
414
|
-
lastSeq: seqCounter
|
|
548
|
+
lastSeq: seqCounter,
|
|
549
|
+
capabilities: { experimental_userMessageResponses: true }
|
|
415
550
|
});
|
|
416
551
|
ws.on("message", (raw) => {
|
|
417
552
|
let parsed;
|