@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 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" : "0.0.0-test";
15
+ var VERSION = true ? "1.0.17" : "0.0.0-test";
16
16
  function createHostToolMCPServer({
17
17
  tools: tools2,
18
18
  revision = 1,
@@ -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
- pendingUserMessages: [],
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 = turn.pendingUserMessages;
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
- currentUserMessages?.push(msg.text);
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;