@band-ai/sdk 0.3.4 → 0.4.0
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/README.md +54 -1
- package/dist/{ClaudeSDKAdapter-CXud2DBE.d.ts → CopilotACPAdapter-BCpbHFB0.d.ts} +240 -19
- package/dist/{ClaudeSDKAdapter-Cx6zhSaG.d.cts → CopilotACPAdapter-D4VWg8lJ.d.cts} +240 -19
- package/dist/{opencode-2YdscIc1.d.cts → acp-client-D-I_5lK-.d.cts} +23 -4
- package/dist/{opencode-Bld62x5t.d.ts → acp-client-DUXyczlF.d.ts} +23 -4
- package/dist/acp-server-BlcYMxKt.d.ts +17 -0
- package/dist/acp-server-CJhclo6P.d.cts +17 -0
- package/dist/adapters.cjs +2205 -969
- package/dist/adapters.d.cts +21 -132
- package/dist/adapters.d.ts +21 -132
- package/dist/adapters.js +26 -1922
- package/dist/{backends-D-dBdDm-.d.ts → backends-CgsPQa9B.d.ts} +2 -2
- package/dist/{backends-ZLxmQrTZ.d.cts → backends-Dh2WXcHQ.d.cts} +2 -2
- package/dist/{chunk-JY75VSQK.js → chunk-4OVNFDD7.js} +14 -1
- package/dist/chunk-6BE2EG5P.js +128 -0
- package/dist/{chunk-U6AFCLYZ.js → chunk-6TGXV5TE.js} +1824 -514
- package/dist/{chunk-OH6VVVKT.js → chunk-CBCHTTUY.js} +10 -0
- package/dist/{chunk-K5KAEYPF.js → chunk-HNWZZXAE.js} +288 -120
- package/dist/chunk-I34SXMTJ.js +6 -0
- package/dist/{chunk-XWHWJP4I.js → chunk-JDW5WSGF.js} +2 -2
- package/dist/{chunk-KXJ74J73.js → chunk-JIZE4EC6.js} +4 -10
- package/dist/chunk-OGOXY43L.js +119 -0
- package/dist/{chunk-W5IAI6Z4.js → chunk-OVXVZUPO.js} +5 -5
- package/dist/{chunk-YJ42AZO4.js → chunk-RJNJUISR.js} +5 -2
- package/dist/{chunk-MQY74R7D.js → chunk-SHZBLLDQ.js} +21 -9
- package/dist/{chunk-JADCAMZY.js → chunk-TGDDFBYS.js} +45 -0
- package/dist/{chunk-US226AS4.js → chunk-UENIC5FJ.js} +14 -11
- package/dist/{chunk-WA7CHXUS.js → chunk-UL3Y5C4J.js} +2 -2
- package/dist/{chunk-WL2HNJZ6.js → chunk-VOMJRQOM.js} +3 -3
- package/dist/{chunk-MQBBWYRP.js → chunk-YF7K4UB3.js} +3352 -572
- package/dist/{chunk-OD2G5LFQ.js → chunk-ZYOXI4JL.js} +41 -2
- package/dist/config.cjs +2 -12
- package/dist/config.js +2 -2
- package/dist/converters.cjs +36 -11
- package/dist/converters.d.cts +7 -5
- package/dist/converters.d.ts +7 -5
- package/dist/converters.js +11 -13
- package/dist/core.cjs +239 -5
- package/dist/core.d.cts +71 -6
- package/dist/core.d.ts +71 -6
- package/dist/core.js +31 -6
- package/dist/{customTools-BzF0IISO.d.cts → customTools-Bfecd0mJ.d.cts} +1 -1
- package/dist/{customTools-BzF0IISO.d.ts → customTools-Bfecd0mJ.d.ts} +1 -1
- package/dist/{disconnectReason-IuADl6TH.d.ts → disconnectReason-Cctmg1SN.d.cts} +1 -1
- package/dist/{disconnectReason-IuADl6TH.d.cts → disconnectReason-Cctmg1SN.d.ts} +1 -1
- package/dist/{dtos-B8KU_q4d.d.ts → dtos-CjQsN1_h.d.cts} +5 -12
- package/dist/{dtos-B8KU_q4d.d.cts → dtos-CjQsN1_h.d.ts} +5 -12
- package/dist/index.cjs +5237 -1474
- package/dist/index.d.cts +61 -22
- package/dist/index.d.ts +61 -22
- package/dist/index.js +151 -43
- package/dist/linear.cjs +45 -7
- package/dist/linear.d.cts +4 -3
- package/dist/linear.d.ts +4 -3
- package/dist/linear.js +10 -10
- package/dist/mcp-claude.cjs +11 -6
- package/dist/mcp-claude.d.cts +4 -3
- package/dist/mcp-claude.d.ts +4 -3
- package/dist/mcp-claude.js +4 -5
- package/dist/mcp.cjs +13 -7
- package/dist/mcp.d.cts +6 -5
- package/dist/mcp.d.ts +6 -5
- package/dist/mcp.js +5 -6
- package/dist/{pagination-DqdkQArT.d.cts → pagination-BZDhB_nO.d.cts} +2 -2
- package/dist/{pagination-BgEx-HeL.d.ts → pagination-CPz0J_vQ.d.ts} +2 -2
- package/dist/{protocols-Bz1rxcUg.d.cts → protocols-CabgO52c.d.cts} +38 -3
- package/dist/{protocols-Bwwcql0h.d.ts → protocols-Dpci6uae.d.ts} +38 -3
- package/dist/rest.cjs +43 -1
- package/dist/rest.d.cts +6 -5
- package/dist/rest.d.ts +6 -5
- package/dist/rest.js +6 -5
- package/dist/runtime.cjs +2113 -597
- package/dist/runtime.d.cts +44 -25
- package/dist/runtime.d.ts +44 -25
- package/dist/runtime.js +11 -11
- package/dist/{schemas-BgJhlqqO.d.cts → schemas-BKnh4fKR.d.cts} +209 -24
- package/dist/{schemas-C4mK1WSs.d.ts → schemas-CDa3V_I5.d.ts} +209 -24
- package/dist/{sdk-BTciSLRQ.d.cts → sdk-CXNqzoY1.d.cts} +1 -1
- package/dist/{sdk-qKJtMma_.d.ts → sdk-Cin80BTC.d.ts} +1 -1
- package/dist/{simpleAdapter-BpT4XbZC.d.cts → simpleAdapter-D75rcz9n.d.cts} +3 -1
- package/dist/{simpleAdapter--wznuoOw.d.ts → simpleAdapter-eLvsAQNo.d.ts} +3 -1
- package/dist/testing.cjs +25 -7
- package/dist/testing.d.cts +5 -3
- package/dist/testing.d.ts +5 -3
- package/dist/testing.js +9 -6
- package/dist/{types-BivGO7I9.d.ts → types-42-HWeX8.d.cts} +10 -4
- package/dist/types-CKU1N0SK.d.ts +137 -0
- package/dist/{types-BgYFd_Yw.d.cts → types-CjxZekMH.d.ts} +10 -4
- package/dist/types-DtcOLALn.d.cts +137 -0
- package/package.json +25 -26
- package/dist/acp-server-CiUqN3G3.d.ts +0 -24
- package/dist/acp-server-Dlj7D563.d.cts +0 -24
- package/dist/chunk-AVVDPXP4.js +0 -39
- package/dist/chunk-F63TPOI3.js +0 -29
- package/dist/chunk-V5TSWS7P.js +0 -62
- package/dist/types-BNNtOXu3.d.cts +0 -446
- package/dist/types-DFUtpxah.d.ts +0 -446
package/dist/adapters.cjs
CHANGED
|
@@ -31,7 +31,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
33
|
// src/core/errors.ts
|
|
34
|
-
|
|
34
|
+
function rethrowIfRecoverableTurnFailure(error) {
|
|
35
|
+
if (error instanceof RecoverableTurnError) {
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
var BandSdkError, UnsupportedFeatureError, ValidationError, RuntimeStateError, RecoverableTurnError;
|
|
35
40
|
var init_errors = __esm({
|
|
36
41
|
"src/core/errors.ts"() {
|
|
37
42
|
"use strict";
|
|
@@ -59,6 +64,65 @@ var init_errors = __esm({
|
|
|
59
64
|
this.name = "RuntimeStateError";
|
|
60
65
|
}
|
|
61
66
|
};
|
|
67
|
+
RecoverableTurnError = class extends BandSdkError {
|
|
68
|
+
constructor(message, cause) {
|
|
69
|
+
super(message, cause);
|
|
70
|
+
this.name = "RecoverableTurnError";
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// src/contracts/chatEvents.ts
|
|
77
|
+
function isBlankEventContent(content) {
|
|
78
|
+
return !VISIBLE_CONTENT_PATTERN.test(content);
|
|
79
|
+
}
|
|
80
|
+
var CHAT_EVENT_TYPES, CHAT_MESSAGE_TYPES, VISIBLE_CONTENT_PATTERN;
|
|
81
|
+
var init_chatEvents = __esm({
|
|
82
|
+
"src/contracts/chatEvents.ts"() {
|
|
83
|
+
"use strict";
|
|
84
|
+
init_errors();
|
|
85
|
+
CHAT_EVENT_TYPES = ["tool_call", "tool_result", "thought", "error", "task"];
|
|
86
|
+
CHAT_MESSAGE_TYPES = ["text", ...CHAT_EVENT_TYPES];
|
|
87
|
+
VISIBLE_CONTENT_PATTERN = /[\p{L}\p{N}\p{P}\p{S}]/u;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// src/contracts/protocols.ts
|
|
92
|
+
function isToolExecutorError(value) {
|
|
93
|
+
if (!value || typeof value !== "object") {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const candidate = value;
|
|
97
|
+
return candidate.ok === false && typeof candidate.errorType === "string" && TOOL_EXECUTOR_ERROR_TYPES.includes(candidate.errorType) && typeof candidate.toolName === "string" && typeof candidate.message === "string" && typeof candidate.legacyMessage === "string";
|
|
98
|
+
}
|
|
99
|
+
function isStructuredToolFailure(value) {
|
|
100
|
+
if (!value || typeof value !== "object") {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const payload = value;
|
|
104
|
+
return payload.ok === false && typeof payload.message === "string";
|
|
105
|
+
}
|
|
106
|
+
function toLegacyToolExecutorErrorMessage(value) {
|
|
107
|
+
if (typeof value === "string") {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
if (!isToolExecutorError(value)) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
return value.legacyMessage;
|
|
114
|
+
}
|
|
115
|
+
var FAILURE_METADATA_KEY, TOOL_EXECUTOR_ERROR_TYPES;
|
|
116
|
+
var init_protocols = __esm({
|
|
117
|
+
"src/contracts/protocols.ts"() {
|
|
118
|
+
"use strict";
|
|
119
|
+
init_chatEvents();
|
|
120
|
+
FAILURE_METADATA_KEY = "failure";
|
|
121
|
+
TOOL_EXECUTOR_ERROR_TYPES = [
|
|
122
|
+
"ToolArgumentsValidationError",
|
|
123
|
+
"ToolNotFoundError",
|
|
124
|
+
"ToolExecutionError"
|
|
125
|
+
];
|
|
62
126
|
}
|
|
63
127
|
});
|
|
64
128
|
|
|
@@ -66,10 +130,11 @@ var init_errors = __esm({
|
|
|
66
130
|
function namedValues(values) {
|
|
67
131
|
return Object.fromEntries(values.map((value) => [value, value]));
|
|
68
132
|
}
|
|
69
|
-
var SENSORY_MEMORY_TYPES, COGNITIVE_MEMORY_TYPES, MEMORY_SYSTEM_TYPES, MEMORY_SYSTEMS, MEMORY_TYPES, MEMORY_SEGMENTS, MEMORY_STORE_SCOPES, MEMORY_LIST_SCOPES, MEMORY_STATUSES, MEMORY_SYSTEM, MEMORY_TYPE, MEMORY_SEGMENT, MEMORY_STORE_SCOPE;
|
|
133
|
+
var import_band_sdk_core2, SENSORY_MEMORY_TYPES, COGNITIVE_MEMORY_TYPES, MEMORY_SYSTEM_TYPES, MEMORY_SYSTEMS, MEMORY_TYPES, MEMORY_SEGMENTS, MEMORY_STORE_SCOPES, MEMORY_LIST_SCOPES, MEMORY_STATUSES, MEMORY_SYSTEM, MEMORY_TYPE, MEMORY_SEGMENT, MEMORY_STORE_SCOPE;
|
|
70
134
|
var init_memory = __esm({
|
|
71
135
|
"src/contracts/memory.ts"() {
|
|
72
136
|
"use strict";
|
|
137
|
+
import_band_sdk_core2 = require("@band-ai/band-sdk-core");
|
|
73
138
|
SENSORY_MEMORY_TYPES = [
|
|
74
139
|
"iconic",
|
|
75
140
|
// Visual input
|
|
@@ -110,18 +175,22 @@ var init_memory = __esm({
|
|
|
110
175
|
// Behavioral rules or policies
|
|
111
176
|
];
|
|
112
177
|
MEMORY_STORE_SCOPES = [
|
|
178
|
+
"agent",
|
|
179
|
+
// Private to the storing agent; omit subject_id
|
|
113
180
|
"subject",
|
|
114
181
|
// About one person/agent; requires subject_id
|
|
115
182
|
"organization"
|
|
116
183
|
// Shared org-wide
|
|
117
184
|
];
|
|
118
185
|
MEMORY_LIST_SCOPES = [
|
|
186
|
+
"agent",
|
|
187
|
+
// Caller's private agent-scoped memories only
|
|
119
188
|
"subject",
|
|
120
189
|
// Subject-scoped memories only
|
|
121
190
|
"organization",
|
|
122
191
|
// Organization-scoped memories only
|
|
123
192
|
"all"
|
|
124
|
-
//
|
|
193
|
+
// All scopes (no scope filter)
|
|
125
194
|
];
|
|
126
195
|
MEMORY_STATUSES = [
|
|
127
196
|
"active",
|
|
@@ -140,21 +209,6 @@ var init_memory = __esm({
|
|
|
140
209
|
}
|
|
141
210
|
});
|
|
142
211
|
|
|
143
|
-
// src/contracts/chatEvents.ts
|
|
144
|
-
function isBlankEventContent(content) {
|
|
145
|
-
return !VISIBLE_CONTENT_PATTERN.test(content);
|
|
146
|
-
}
|
|
147
|
-
var CHAT_EVENT_TYPES, CHAT_MESSAGE_TYPES, VISIBLE_CONTENT_PATTERN;
|
|
148
|
-
var init_chatEvents = __esm({
|
|
149
|
-
"src/contracts/chatEvents.ts"() {
|
|
150
|
-
"use strict";
|
|
151
|
-
init_errors();
|
|
152
|
-
CHAT_EVENT_TYPES = ["tool_call", "tool_result", "thought", "error", "task"];
|
|
153
|
-
CHAT_MESSAGE_TYPES = ["text", ...CHAT_EVENT_TYPES];
|
|
154
|
-
VISIBLE_CONTENT_PATTERN = /[\p{L}\p{N}\p{P}\p{S}]/u;
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
212
|
// src/runtime/tools/schemas.ts
|
|
159
213
|
function mcpToolNames(names) {
|
|
160
214
|
return [...names].sort((a, b) => a.localeCompare(b)).map((name) => `${MCP_TOOL_PREFIX}${name}`);
|
|
@@ -347,7 +401,7 @@ var init_schemas = __esm({
|
|
|
347
401
|
required: ["action"]
|
|
348
402
|
},
|
|
349
403
|
band_list_memories: {
|
|
350
|
-
description:
|
|
404
|
+
description: `List memories accessible to the agent. Use scope="agent" for the caller's private memories, scope="subject" for memories about a specific person or agent, and scope="organization" for org-wide shared memories. Omit scope or use scope="all" to search across scopes.`,
|
|
351
405
|
properties: {
|
|
352
406
|
subject_id: {
|
|
353
407
|
type: "string",
|
|
@@ -356,7 +410,7 @@ var init_schemas = __esm({
|
|
|
356
410
|
scope: {
|
|
357
411
|
type: "string",
|
|
358
412
|
enum: [...MEMORY_LIST_SCOPES],
|
|
359
|
-
description:
|
|
413
|
+
description: `Filter by scope. "agent" returns the caller's private memories; "subject" and "organization" filter to those audiences; "all" omits the filter.`
|
|
360
414
|
},
|
|
361
415
|
system: {
|
|
362
416
|
type: "string",
|
|
@@ -392,7 +446,7 @@ var init_schemas = __esm({
|
|
|
392
446
|
required: []
|
|
393
447
|
},
|
|
394
448
|
band_store_memory: {
|
|
395
|
-
description:
|
|
449
|
+
description: 'Store a new memory entry. The memory will be associated with the authenticated agent as the source. Use scope="agent" for information private to this agent (no subject_id). Use scope="subject" with a subject_id for memories about a specific person or agent. Use scope="organization" for knowledge genuinely shared across the organization.',
|
|
396
450
|
properties: {
|
|
397
451
|
content: {
|
|
398
452
|
type: "string",
|
|
@@ -420,11 +474,11 @@ var init_schemas = __esm({
|
|
|
420
474
|
scope: {
|
|
421
475
|
type: "string",
|
|
422
476
|
enum: [...MEMORY_STORE_SCOPES],
|
|
423
|
-
description:
|
|
477
|
+
description: 'Visibility scope. "agent" is private to this agent; "subject" requires subject_id; "organization" is shared org-wide.'
|
|
424
478
|
},
|
|
425
479
|
subject_id: {
|
|
426
480
|
type: "string",
|
|
427
|
-
description: "UUID of the subject this memory is about (required for subject scope)."
|
|
481
|
+
description: "UUID of the subject this memory is about (required for subject scope; omit for agent scope)."
|
|
428
482
|
},
|
|
429
483
|
metadata: {
|
|
430
484
|
type: "object",
|
|
@@ -492,47 +546,11 @@ var init_schemas = __esm({
|
|
|
492
546
|
}
|
|
493
547
|
});
|
|
494
548
|
|
|
495
|
-
// src/contracts/protocols.ts
|
|
496
|
-
function isToolExecutorError(value) {
|
|
497
|
-
if (!value || typeof value !== "object") {
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
const candidate = value;
|
|
501
|
-
return candidate.ok === false && typeof candidate.errorType === "string" && TOOL_EXECUTOR_ERROR_TYPES.includes(candidate.errorType) && typeof candidate.toolName === "string" && typeof candidate.message === "string" && typeof candidate.legacyMessage === "string";
|
|
502
|
-
}
|
|
503
|
-
function isStructuredToolFailure(value) {
|
|
504
|
-
if (!value || typeof value !== "object") {
|
|
505
|
-
return false;
|
|
506
|
-
}
|
|
507
|
-
const payload = value;
|
|
508
|
-
return payload.ok === false && typeof payload.message === "string";
|
|
509
|
-
}
|
|
510
|
-
function toLegacyToolExecutorErrorMessage(value) {
|
|
511
|
-
if (typeof value === "string") {
|
|
512
|
-
return value;
|
|
513
|
-
}
|
|
514
|
-
if (!isToolExecutorError(value)) {
|
|
515
|
-
return null;
|
|
516
|
-
}
|
|
517
|
-
return value.legacyMessage;
|
|
518
|
-
}
|
|
519
|
-
var TOOL_EXECUTOR_ERROR_TYPES;
|
|
520
|
-
var init_protocols = __esm({
|
|
521
|
-
"src/contracts/protocols.ts"() {
|
|
522
|
-
"use strict";
|
|
523
|
-
TOOL_EXECUTOR_ERROR_TYPES = [
|
|
524
|
-
"ToolArgumentsValidationError",
|
|
525
|
-
"ToolNotFoundError",
|
|
526
|
-
"ToolExecutionError"
|
|
527
|
-
];
|
|
528
|
-
}
|
|
529
|
-
});
|
|
530
|
-
|
|
531
549
|
// src/mcp/registrations.ts
|
|
532
550
|
function buildRoomScopedRegistrations(resolver, options = {}) {
|
|
533
551
|
const toolNames = resolveToolNames(options);
|
|
534
552
|
const registrations = buildRegistrations(toolNames, async (toolName, args) => {
|
|
535
|
-
const roomId =
|
|
553
|
+
const roomId = asNonEmptyString3(args.room_id);
|
|
536
554
|
if (!roomId) {
|
|
537
555
|
return errorResult("Missing required room_id");
|
|
538
556
|
}
|
|
@@ -611,7 +629,7 @@ async function executeToolCall(tools, toolName, args) {
|
|
|
611
629
|
return errorResult(error instanceof Error ? error.message : String(error));
|
|
612
630
|
}
|
|
613
631
|
}
|
|
614
|
-
function
|
|
632
|
+
function asNonEmptyString3(value) {
|
|
615
633
|
if (typeof value !== "string") {
|
|
616
634
|
return null;
|
|
617
635
|
}
|
|
@@ -970,6 +988,9 @@ __export(adapters_exports, {
|
|
|
970
988
|
CodexAdapter: () => CodexAdapter,
|
|
971
989
|
CodexAppServerStdioClient: () => CodexAppServerStdioClient,
|
|
972
990
|
CodexJsonRpcError: () => CodexJsonRpcError,
|
|
991
|
+
CopilotACPAdapter: () => CopilotACPAdapter,
|
|
992
|
+
DEFAULT_COPILOT_ACP_COMMAND: () => DEFAULT_COPILOT_ACP_COMMAND,
|
|
993
|
+
DEFAULT_OMP_ACP_COMMAND: () => DEFAULT_OMP_ACP_COMMAND,
|
|
973
994
|
GatewayHistoryConverter: () => GatewayHistoryConverter,
|
|
974
995
|
GatewayServer: () => GatewayServer,
|
|
975
996
|
GeminiAdapter: () => GeminiAdapter,
|
|
@@ -981,6 +1002,7 @@ __export(adapters_exports, {
|
|
|
981
1002
|
LangGraphAdapter: () => LangGraphAdapter,
|
|
982
1003
|
LettaAdapter: () => LettaAdapter,
|
|
983
1004
|
LettaHistoryConverter: () => LettaHistoryConverter,
|
|
1005
|
+
OmpACPAdapter: () => OmpACPAdapter,
|
|
984
1006
|
OpenAIAdapter: () => OpenAIAdapter,
|
|
985
1007
|
OpenAIToolCallingModel: () => OpenAIToolCallingModel,
|
|
986
1008
|
OpencodeAdapter: () => OpencodeAdapter,
|
|
@@ -1032,29 +1054,320 @@ var SimpleAdapter = class {
|
|
|
1032
1054
|
};
|
|
1033
1055
|
|
|
1034
1056
|
// src/adapters/GenericAdapter.ts
|
|
1057
|
+
init_errors();
|
|
1058
|
+
|
|
1059
|
+
// src/adapters/shared/coercion.ts
|
|
1060
|
+
init_protocols();
|
|
1061
|
+
|
|
1062
|
+
// src/core/coercion.ts
|
|
1063
|
+
var MAX_ERROR_CAUSE_DEPTH = 5;
|
|
1064
|
+
var MAX_ERROR_DETAIL_LENGTH = 500;
|
|
1065
|
+
var LENGTH_TRUNCATED_MARKER = "... (truncated)";
|
|
1066
|
+
var DEPTH_TRUNCATED_MARKER = "... (cause chain truncated)";
|
|
1067
|
+
function asOptionalRecord(value) {
|
|
1068
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1069
|
+
return void 0;
|
|
1070
|
+
}
|
|
1071
|
+
return value;
|
|
1072
|
+
}
|
|
1073
|
+
function asNonEmptyString(value) {
|
|
1074
|
+
if (typeof value !== "string") {
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
1077
|
+
const trimmed = value.trim();
|
|
1078
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1079
|
+
}
|
|
1080
|
+
function toDisplayText(value) {
|
|
1081
|
+
if (typeof value === "string") {
|
|
1082
|
+
return value;
|
|
1083
|
+
}
|
|
1084
|
+
if (value == null) {
|
|
1085
|
+
return "";
|
|
1086
|
+
}
|
|
1087
|
+
try {
|
|
1088
|
+
const json = JSON.stringify(value);
|
|
1089
|
+
return typeof json === "string" ? json : String(value);
|
|
1090
|
+
} catch {
|
|
1091
|
+
return String(value);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
function asErrorMessage(error) {
|
|
1095
|
+
return formatCaughtError(error, 0);
|
|
1096
|
+
}
|
|
1097
|
+
function formatCaughtError(error, depth) {
|
|
1098
|
+
if (typeof error !== "object" || error === null) {
|
|
1099
|
+
return truncate(String(error));
|
|
1100
|
+
}
|
|
1101
|
+
const message = truncate(asNestedMessage(error) ?? String(error));
|
|
1102
|
+
const detail = selectDetail(asOptionalRecord(error));
|
|
1103
|
+
if (detail === void 0) {
|
|
1104
|
+
return message;
|
|
1105
|
+
}
|
|
1106
|
+
if (depth >= MAX_ERROR_CAUSE_DEPTH) {
|
|
1107
|
+
return `${message} (${DEPTH_TRUNCATED_MARKER})`;
|
|
1108
|
+
}
|
|
1109
|
+
return `${message} (${formatErrorDetail(detail, depth + 1)})`;
|
|
1110
|
+
}
|
|
1111
|
+
function selectDetail(record) {
|
|
1112
|
+
if (isPresentDetail(record?.data)) {
|
|
1113
|
+
return record?.data;
|
|
1114
|
+
}
|
|
1115
|
+
if (isPresentDetail(record?.cause)) {
|
|
1116
|
+
return record?.cause;
|
|
1117
|
+
}
|
|
1118
|
+
return void 0;
|
|
1119
|
+
}
|
|
1120
|
+
function isPresentDetail(value) {
|
|
1121
|
+
if (value === void 0 || value === null) {
|
|
1122
|
+
return false;
|
|
1123
|
+
}
|
|
1124
|
+
if (typeof value === "string") {
|
|
1125
|
+
return asNonEmptyString(value) !== null;
|
|
1126
|
+
}
|
|
1127
|
+
if (value instanceof Error) {
|
|
1128
|
+
return true;
|
|
1129
|
+
}
|
|
1130
|
+
if (Array.isArray(value)) {
|
|
1131
|
+
return value.length > 0;
|
|
1132
|
+
}
|
|
1133
|
+
if (typeof value === "object") {
|
|
1134
|
+
return isPlainObject(value) ? Object.keys(value).length > 0 : true;
|
|
1135
|
+
}
|
|
1136
|
+
return true;
|
|
1137
|
+
}
|
|
1138
|
+
function isPlainObject(value) {
|
|
1139
|
+
const proto = Object.getPrototypeOf(value);
|
|
1140
|
+
return proto === Object.prototype || proto === null;
|
|
1141
|
+
}
|
|
1142
|
+
function formatErrorDetail(detail, depth) {
|
|
1143
|
+
if (detail instanceof Error || selectDetail(asOptionalRecord(detail)) !== void 0) {
|
|
1144
|
+
return formatCaughtError(detail, depth);
|
|
1145
|
+
}
|
|
1146
|
+
return truncate(formatLeafDetail(detail));
|
|
1147
|
+
}
|
|
1148
|
+
function formatLeafDetail(detail) {
|
|
1149
|
+
if (typeof detail === "string") {
|
|
1150
|
+
return detail;
|
|
1151
|
+
}
|
|
1152
|
+
if (typeof detail === "number" && !Number.isFinite(detail)) {
|
|
1153
|
+
return String(detail);
|
|
1154
|
+
}
|
|
1155
|
+
const builtin = formatNonEnumerableDetail(detail);
|
|
1156
|
+
if (builtin !== null) {
|
|
1157
|
+
return builtin;
|
|
1158
|
+
}
|
|
1159
|
+
const nested = asNestedMessage(detail);
|
|
1160
|
+
if (nested !== null) {
|
|
1161
|
+
return nested;
|
|
1162
|
+
}
|
|
1163
|
+
return toDisplayText(detail);
|
|
1164
|
+
}
|
|
1165
|
+
function formatNonEnumerableDetail(detail) {
|
|
1166
|
+
if (detail instanceof RegExp) {
|
|
1167
|
+
return detail.toString();
|
|
1168
|
+
}
|
|
1169
|
+
if (detail instanceof Map) {
|
|
1170
|
+
return `Map ${toDisplayText([...detail])}`;
|
|
1171
|
+
}
|
|
1172
|
+
if (detail instanceof Set) {
|
|
1173
|
+
return `Set ${toDisplayText([...detail])}`;
|
|
1174
|
+
}
|
|
1175
|
+
return null;
|
|
1176
|
+
}
|
|
1177
|
+
function truncate(text) {
|
|
1178
|
+
return text.length > MAX_ERROR_DETAIL_LENGTH ? `${text.slice(0, MAX_ERROR_DETAIL_LENGTH)}${LENGTH_TRUNCATED_MARKER}` : text;
|
|
1179
|
+
}
|
|
1180
|
+
function asNestedMessage(value) {
|
|
1181
|
+
const record = asOptionalRecord(value);
|
|
1182
|
+
return asNonEmptyString(record?.message);
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
// src/adapters/shared/coercion.ts
|
|
1186
|
+
function asOptionalRecord2(value) {
|
|
1187
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1188
|
+
return void 0;
|
|
1189
|
+
}
|
|
1190
|
+
return value;
|
|
1191
|
+
}
|
|
1192
|
+
function asRecord(value, context = "value") {
|
|
1193
|
+
const record = asOptionalRecord2(value);
|
|
1194
|
+
if (!record) {
|
|
1195
|
+
throw new TypeError(`Expected ${context} to be an object record.`);
|
|
1196
|
+
}
|
|
1197
|
+
return record;
|
|
1198
|
+
}
|
|
1199
|
+
function asNonEmptyString2(value) {
|
|
1200
|
+
if (typeof value !== "string") {
|
|
1201
|
+
return null;
|
|
1202
|
+
}
|
|
1203
|
+
const trimmed = value.trim();
|
|
1204
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
1205
|
+
}
|
|
1206
|
+
function asString(value) {
|
|
1207
|
+
return typeof value === "string" ? value : void 0;
|
|
1208
|
+
}
|
|
1209
|
+
function toDisplayText2(value) {
|
|
1210
|
+
if (typeof value === "string") {
|
|
1211
|
+
return value;
|
|
1212
|
+
}
|
|
1213
|
+
if (value == null) {
|
|
1214
|
+
return "";
|
|
1215
|
+
}
|
|
1216
|
+
try {
|
|
1217
|
+
const json = JSON.stringify(value);
|
|
1218
|
+
return typeof json === "string" ? json : String(value);
|
|
1219
|
+
} catch {
|
|
1220
|
+
return String(value);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
function toWireString(value) {
|
|
1224
|
+
const legacyErrorMessage = toLegacyToolExecutorErrorMessage(value);
|
|
1225
|
+
if (legacyErrorMessage !== null) {
|
|
1226
|
+
return legacyErrorMessage;
|
|
1227
|
+
}
|
|
1228
|
+
if (value === void 0 || value === null) {
|
|
1229
|
+
return "";
|
|
1230
|
+
}
|
|
1231
|
+
try {
|
|
1232
|
+
const json = JSON.stringify(value);
|
|
1233
|
+
return typeof json === "string" ? json : String(value);
|
|
1234
|
+
} catch {
|
|
1235
|
+
return String(value);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/core/deliveryFailedError.ts
|
|
1240
|
+
init_errors();
|
|
1241
|
+
var DeliveryFailedError = class extends RecoverableTurnError {
|
|
1242
|
+
constructor(cause) {
|
|
1243
|
+
super(asErrorMessage(cause), cause);
|
|
1244
|
+
this.cause = cause;
|
|
1245
|
+
this.name = "DeliveryFailedError";
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
async function deliverReply(tools, content, mentions = []) {
|
|
1249
|
+
try {
|
|
1250
|
+
return await tools.sendMessage(content, mentions);
|
|
1251
|
+
} catch (error) {
|
|
1252
|
+
throw new DeliveryFailedError(error);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// src/core/providerFailure.ts
|
|
1257
|
+
var import_band_sdk_core = require("@band-ai/band-sdk-core");
|
|
1258
|
+
init_errors();
|
|
1259
|
+
var FAILURE_CODE_TIMEOUT = "timeout";
|
|
1260
|
+
var ProviderTurnFailedError = class extends RecoverableTurnError {
|
|
1261
|
+
constructor(failure) {
|
|
1262
|
+
super(failure.message);
|
|
1263
|
+
this.failure = failure;
|
|
1264
|
+
this.name = "ProviderTurnFailedError";
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
async function trySendFailure(tools, failure, logger, logContext) {
|
|
1268
|
+
logger?.warn("provider_failure.reported", {
|
|
1269
|
+
provider: failure.provider,
|
|
1270
|
+
code: failure.code,
|
|
1271
|
+
...logContext
|
|
1272
|
+
});
|
|
1273
|
+
try {
|
|
1274
|
+
await tools.sendFailure(failure);
|
|
1275
|
+
} catch (error) {
|
|
1276
|
+
logger?.warn("provider_failure.report_failed", {
|
|
1277
|
+
provider: failure.provider,
|
|
1278
|
+
code: failure.code,
|
|
1279
|
+
...logContext,
|
|
1280
|
+
error
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
async function reportTurnFailure(tools, failure, logger, logContext) {
|
|
1285
|
+
await trySendFailure(tools, failure, logger, logContext);
|
|
1286
|
+
throw new ProviderTurnFailedError(failure);
|
|
1287
|
+
}
|
|
1288
|
+
async function reportProviderTurnFailure(tools, logger, provider, logLabel, error, logContext) {
|
|
1289
|
+
logger.error(logLabel, { ...logContext, error });
|
|
1290
|
+
return reportTurnFailure(tools, agentFailure(provider, asErrorMessage(error)), logger, logContext);
|
|
1291
|
+
}
|
|
1292
|
+
async function safeSendFailure(tools, failure, logger, logContext) {
|
|
1293
|
+
await trySendFailure(tools, failure, logger, logContext);
|
|
1294
|
+
}
|
|
1295
|
+
function agentFailure(provider, message, code, detail) {
|
|
1296
|
+
if (detail === void 0) {
|
|
1297
|
+
return new import_band_sdk_core.AgentFailure(provider, message, code);
|
|
1298
|
+
}
|
|
1299
|
+
try {
|
|
1300
|
+
return new import_band_sdk_core.AgentFailure(provider, message, code, detail);
|
|
1301
|
+
} catch {
|
|
1302
|
+
return new import_band_sdk_core.AgentFailure(provider, message, code);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// src/adapters/GenericAdapter.ts
|
|
1307
|
+
function toolsWithDeliverySafeSendMessage(tools) {
|
|
1308
|
+
return new Proxy({}, {
|
|
1309
|
+
has(_target, key) {
|
|
1310
|
+
return Reflect.has(tools, key);
|
|
1311
|
+
},
|
|
1312
|
+
get(_target, key) {
|
|
1313
|
+
if (key === "sendMessage") {
|
|
1314
|
+
return (content, mentions) => deliverReply(tools, content, mentions);
|
|
1315
|
+
}
|
|
1316
|
+
const value = Reflect.get(tools, key, tools);
|
|
1317
|
+
return typeof value === "function" ? value.bind(tools) : value;
|
|
1318
|
+
},
|
|
1319
|
+
// Without this, a write against the empty target would silently succeed
|
|
1320
|
+
// and vanish — invisible even to the handler that made it, since `get`
|
|
1321
|
+
// never consults the target. `tools` is frozen, so forwarding here
|
|
1322
|
+
// throws the same `TypeError` a write against the real, unwrapped
|
|
1323
|
+
// `tools` always has.
|
|
1324
|
+
set(_target, key, value) {
|
|
1325
|
+
return Reflect.set(tools, key, value, tools);
|
|
1326
|
+
},
|
|
1327
|
+
// Without these two traps, `Object.keys`/spread/`Object.assign` fall back
|
|
1328
|
+
// to the empty target's own keys — reporting no properties at all, even
|
|
1329
|
+
// though `has`/`get` resolve every one of them. `configurable: true` is
|
|
1330
|
+
// required, not a choice: the target has no own properties of its own, so
|
|
1331
|
+
// the Proxy invariants forbid reporting any key as non-configurable.
|
|
1332
|
+
ownKeys() {
|
|
1333
|
+
return Reflect.ownKeys(tools);
|
|
1334
|
+
},
|
|
1335
|
+
getOwnPropertyDescriptor(_target, key) {
|
|
1336
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(tools, key);
|
|
1337
|
+
return descriptor && { ...descriptor, configurable: true };
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1035
1341
|
var GenericAdapter = class extends SimpleAdapter {
|
|
1342
|
+
provider = "generic";
|
|
1036
1343
|
handler;
|
|
1037
1344
|
constructor(handler) {
|
|
1038
1345
|
super();
|
|
1039
1346
|
this.handler = handler;
|
|
1040
1347
|
}
|
|
1041
1348
|
async onMessage(message, tools, history, participantsMessage, contactsMessage, context) {
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1349
|
+
try {
|
|
1350
|
+
await this.handler({
|
|
1351
|
+
message,
|
|
1352
|
+
tools: toolsWithDeliverySafeSendMessage(tools),
|
|
1353
|
+
history,
|
|
1354
|
+
participantsMessage,
|
|
1355
|
+
contactsMessage,
|
|
1356
|
+
isSessionBootstrap: context.isSessionBootstrap,
|
|
1357
|
+
roomId: context.roomId,
|
|
1358
|
+
agentName: this.agentName,
|
|
1359
|
+
agentDescription: this.agentDescription
|
|
1360
|
+
});
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
1363
|
+
await reportTurnFailure(tools, agentFailure(this.provider, asErrorMessage(error)));
|
|
1364
|
+
}
|
|
1053
1365
|
}
|
|
1054
1366
|
};
|
|
1055
1367
|
|
|
1056
1368
|
// src/adapters/acp/ACPClientAdapter.ts
|
|
1057
1369
|
var import_node_child_process = require("child_process");
|
|
1370
|
+
var import_node_net = require("net");
|
|
1058
1371
|
var import_node_stream = require("stream");
|
|
1059
1372
|
|
|
1060
1373
|
// src/converters/acp-client.ts
|
|
@@ -1080,14 +1393,52 @@ var ACPClientHistoryConverter = class {
|
|
|
1080
1393
|
}
|
|
1081
1394
|
};
|
|
1082
1395
|
|
|
1396
|
+
// src/core/sensitiveTerms.ts
|
|
1397
|
+
var SENSITIVE_KEY_TERMS = "authorization|api[-_ ]?key|token|secret|password|cookie";
|
|
1398
|
+
var GATEWAY_FREE_TEXT_SENSITIVE_KEY_TERMS = `${SENSITIVE_KEY_TERMS}|session`;
|
|
1399
|
+
|
|
1083
1400
|
// src/core/logger.ts
|
|
1084
1401
|
var noop = () => void 0;
|
|
1402
|
+
var SENSITIVE_KEY_PATTERN = new RegExp(`(${SENSITIVE_KEY_TERMS})`, "i");
|
|
1085
1403
|
var NoopLogger = class {
|
|
1086
1404
|
debug = noop;
|
|
1087
1405
|
info = noop;
|
|
1088
1406
|
warn = noop;
|
|
1089
1407
|
error = noop;
|
|
1090
1408
|
};
|
|
1409
|
+
function resolveLogger(logger) {
|
|
1410
|
+
if (!logger) {
|
|
1411
|
+
return new NoopLogger();
|
|
1412
|
+
}
|
|
1413
|
+
return logger instanceof GuardedLogger ? logger : new GuardedLogger(logger);
|
|
1414
|
+
}
|
|
1415
|
+
var GuardedLogger = class {
|
|
1416
|
+
constructor(inner) {
|
|
1417
|
+
this.inner = inner;
|
|
1418
|
+
}
|
|
1419
|
+
debug(message, context) {
|
|
1420
|
+
return this.emit("debug", message, context);
|
|
1421
|
+
}
|
|
1422
|
+
info(message, context) {
|
|
1423
|
+
return this.emit("info", message, context);
|
|
1424
|
+
}
|
|
1425
|
+
warn(message, context) {
|
|
1426
|
+
return this.emit("warn", message, context);
|
|
1427
|
+
}
|
|
1428
|
+
error(message, context) {
|
|
1429
|
+
return this.emit("error", message, context);
|
|
1430
|
+
}
|
|
1431
|
+
emit(level, message, context) {
|
|
1432
|
+
try {
|
|
1433
|
+
const result = this.inner[level](message, context);
|
|
1434
|
+
if (result instanceof Promise) {
|
|
1435
|
+
result.catch(noop);
|
|
1436
|
+
}
|
|
1437
|
+
return result;
|
|
1438
|
+
} catch {
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1091
1442
|
|
|
1092
1443
|
// src/adapters/acp/ACPClientAdapter.ts
|
|
1093
1444
|
init_errors();
|
|
@@ -1192,6 +1543,7 @@ function longTermPattern(label, type, segment, scope) {
|
|
|
1192
1543
|
};
|
|
1193
1544
|
}
|
|
1194
1545
|
var COMMON_MEMORY_PATTERNS = [
|
|
1546
|
+
longTermPattern("Private notes for this agent only", MEMORY_TYPE.semantic, MEMORY_SEGMENT.guideline, MEMORY_STORE_SCOPE.agent),
|
|
1195
1547
|
longTermPattern("Facts learned about a specific agent/entity", MEMORY_TYPE.semantic, MEMORY_SEGMENT.agent, MEMORY_STORE_SCOPE.subject),
|
|
1196
1548
|
longTermPattern("Events involving a specific person/agent", MEMORY_TYPE.episodic, MEMORY_SEGMENT.agent, MEMORY_STORE_SCOPE.subject),
|
|
1197
1549
|
longTermPattern("A user's preferences or profile info", MEMORY_TYPE.semantic, MEMORY_SEGMENT.user, MEMORY_STORE_SCOPE.subject),
|
|
@@ -1204,7 +1556,9 @@ var MEMORY_COMMON_PATTERNS = [
|
|
|
1204
1556
|
"Common patterns:",
|
|
1205
1557
|
...COMMON_MEMORY_PATTERNS.map(renderMemoryPattern)
|
|
1206
1558
|
].join("\n");
|
|
1207
|
-
var MEMORY_SCOPE_GUIDANCE = `
|
|
1559
|
+
var MEMORY_SCOPE_GUIDANCE = `Use \`scope="${MEMORY_STORE_SCOPE.agent}"\` for information private to this agent that is not about an
|
|
1560
|
+
identified subject. Omitting \`scope\` and \`subject_id\` on store defaults to agent scope on the platform.
|
|
1561
|
+
Prefer \`scope="${MEMORY_STORE_SCOPE.subject}"\` whenever the memory is about a specific person or agent, so it
|
|
1208
1562
|
stays attached to that subject rather than leaking org-wide. Storing with \`scope="${MEMORY_STORE_SCOPE.subject}"\` requires a
|
|
1209
1563
|
real \`subject_id\` UUID, so resolve it first via \`band_lookup_peers\` or the participant list.
|
|
1210
1564
|
Reserve \`scope="${MEMORY_STORE_SCOPE.organization}"\` for knowledge that is genuinely shared across the whole organization and
|
|
@@ -1354,13 +1708,16 @@ function formatHistoryLine(entry) {
|
|
|
1354
1708
|
}
|
|
1355
1709
|
|
|
1356
1710
|
// src/adapters/shared/withTimeout.ts
|
|
1357
|
-
async function withTimeout(promise, timeoutMs,
|
|
1711
|
+
async function withTimeout(promise, timeoutMs, onTimeout) {
|
|
1712
|
+
if (!Number.isFinite(timeoutMs)) {
|
|
1713
|
+
return promise;
|
|
1714
|
+
}
|
|
1358
1715
|
let timer;
|
|
1359
1716
|
try {
|
|
1360
1717
|
return await Promise.race([
|
|
1361
1718
|
promise,
|
|
1362
1719
|
new Promise((_, reject) => {
|
|
1363
|
-
timer = setTimeout(() => reject(new Error(
|
|
1720
|
+
timer = setTimeout(() => reject(typeof onTimeout === "function" ? onTimeout() : new Error(onTimeout)), timeoutMs);
|
|
1364
1721
|
})
|
|
1365
1722
|
]);
|
|
1366
1723
|
} finally {
|
|
@@ -1368,6 +1725,11 @@ async function withTimeout(promise, timeoutMs, message) {
|
|
|
1368
1725
|
}
|
|
1369
1726
|
}
|
|
1370
1727
|
|
|
1728
|
+
// src/adapters/shared/abandon.ts
|
|
1729
|
+
function abandon(operation, onError = () => void 0) {
|
|
1730
|
+
void Promise.resolve().then(operation).catch(onError);
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1371
1733
|
// src/adapters/acp/ACPClientAdapter.ts
|
|
1372
1734
|
init_chatEvents();
|
|
1373
1735
|
init_schemas();
|
|
@@ -1898,14 +2260,17 @@ var BandACPClient = class {
|
|
|
1898
2260
|
constructor(permissionHandler) {
|
|
1899
2261
|
this.permissionHandler = permissionHandler;
|
|
1900
2262
|
}
|
|
2263
|
+
beginSession(sessionId) {
|
|
2264
|
+
this.sessionChunks.set(sessionId, []);
|
|
2265
|
+
}
|
|
1901
2266
|
async sessionUpdate(params) {
|
|
1902
|
-
|
|
1903
|
-
if (!chunk) {
|
|
2267
|
+
if (!this.sessionChunks.has(params.sessionId)) {
|
|
1904
2268
|
return;
|
|
1905
2269
|
}
|
|
1906
|
-
const
|
|
1907
|
-
|
|
1908
|
-
|
|
2270
|
+
const chunk = toCollectedChunk(params.update);
|
|
2271
|
+
if (chunk) {
|
|
2272
|
+
this.appendChunk(params.sessionId, chunk);
|
|
2273
|
+
}
|
|
1909
2274
|
}
|
|
1910
2275
|
async requestPermission(params) {
|
|
1911
2276
|
return this.permissionHandler(params);
|
|
@@ -1924,6 +2289,19 @@ var BandACPClient = class {
|
|
|
1924
2289
|
}
|
|
1925
2290
|
return [...this.sessionChunks.values()].flatMap((chunks) => coalesceChunks(chunks));
|
|
1926
2291
|
}
|
|
2292
|
+
// Unlike `getCollectedChunks`, this clears the buffer as it reads it — so a
|
|
2293
|
+
// caller that flushes twice for the same turn (success path, then a catch
|
|
2294
|
+
// block) can never redeliver the same chunks. Keeps the session's map entry
|
|
2295
|
+
// (an empty array, not a deleted one) so a late `sessionUpdate` still has
|
|
2296
|
+
// somewhere to collect into.
|
|
2297
|
+
takeCollectedChunks(sessionId) {
|
|
2298
|
+
if (!this.sessionChunks.has(sessionId)) {
|
|
2299
|
+
return [];
|
|
2300
|
+
}
|
|
2301
|
+
const chunks = coalesceChunks(this.sessionChunks.get(sessionId) ?? []);
|
|
2302
|
+
this.sessionChunks.set(sessionId, []);
|
|
2303
|
+
return chunks;
|
|
2304
|
+
}
|
|
1927
2305
|
async extMethod(method, params) {
|
|
1928
2306
|
if (method === "cursor/ask_question") {
|
|
1929
2307
|
const options = Array.isArray(params.options) ? params.options : [];
|
|
@@ -1984,9 +2362,7 @@ var BandACPClient = class {
|
|
|
1984
2362
|
}
|
|
1985
2363
|
}
|
|
1986
2364
|
appendChunk(sessionId, chunk) {
|
|
1987
|
-
|
|
1988
|
-
existing.push(chunk);
|
|
1989
|
-
this.sessionChunks.set(sessionId, existing);
|
|
2365
|
+
this.sessionChunks.get(sessionId)?.push(chunk);
|
|
1990
2366
|
}
|
|
1991
2367
|
};
|
|
1992
2368
|
function coalesceChunks(chunks) {
|
|
@@ -2171,8 +2547,20 @@ var acpModule = new LazyAsyncValue({
|
|
|
2171
2547
|
|
|
2172
2548
|
// src/adapters/acp/ACPClientAdapter.ts
|
|
2173
2549
|
var DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 6e4;
|
|
2174
|
-
var
|
|
2550
|
+
var DEFAULT_TURN_TIMEOUT_MS = 60 * 6e4;
|
|
2551
|
+
var SET_SESSION_CONFIG_TIMEOUT_MS = 1e4;
|
|
2552
|
+
var MAX_SETTIMEOUT_DELAY_MS = 2147483647;
|
|
2553
|
+
var MIN_TCP_PORT = 1;
|
|
2554
|
+
var MAX_TCP_PORT = 65535;
|
|
2555
|
+
var CONNECTION_ATTEMPT_SUPERSEDED_ERROR = "ACP connection attempt superseded by stop()";
|
|
2556
|
+
var TCP_CONNECTION_ATTEMPT_ABORTED_ERROR = "ACP TCP connection attempt aborted";
|
|
2557
|
+
function assertWithinSetTimeoutBound(message, value) {
|
|
2558
|
+
if (value > MAX_SETTIMEOUT_DELAY_MS) {
|
|
2559
|
+
throw new ValidationError(message);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2175
2562
|
var ACPClientAdapter = class extends SimpleAdapter {
|
|
2563
|
+
provider = "acp";
|
|
2176
2564
|
command;
|
|
2177
2565
|
cwd;
|
|
2178
2566
|
env;
|
|
@@ -2183,11 +2571,23 @@ var ACPClientAdapter = class extends SimpleAdapter {
|
|
|
2183
2571
|
additionalMcpTools;
|
|
2184
2572
|
clientCapabilities;
|
|
2185
2573
|
connectionFactory;
|
|
2574
|
+
tcpEndpoint;
|
|
2575
|
+
// The value's `generation` is the connection generation the session was
|
|
2576
|
+
// last established/restored against. `client` is the exact BandACPClient
|
|
2577
|
+
// instance that session was established against — never reread from
|
|
2578
|
+
// `this.client` at cleanup time, because a reconnect can already have
|
|
2579
|
+
// replaced it. `null` only for a room rehydrated from persisted history.
|
|
2186
2580
|
roomToSession = /* @__PURE__ */ new Map();
|
|
2187
2581
|
sessionToRoom = /* @__PURE__ */ new Map();
|
|
2188
2582
|
roomTools = /* @__PURE__ */ new Map();
|
|
2583
|
+
// Keyed by `sessionKey(generation, sessionId)`, not bare sessionId: an ACP
|
|
2584
|
+
// agent can reissue the identical session id across a reconnect.
|
|
2189
2585
|
activeSessions = /* @__PURE__ */ new Set();
|
|
2190
2586
|
bootstrappedSessions = /* @__PURE__ */ new Set();
|
|
2587
|
+
// A timed-out turn's generation-qualified session, once `cancel()`'d, can
|
|
2588
|
+
// never be trusted for restore on that same connection. A later connection
|
|
2589
|
+
// generation with the same raw session id is a different owner.
|
|
2590
|
+
abandonedSessions = /* @__PURE__ */ new Set();
|
|
2191
2591
|
pendingPermissions = /* @__PURE__ */ new Map();
|
|
2192
2592
|
sessionsInFlight = /* @__PURE__ */ new Map();
|
|
2193
2593
|
roomTurnLocks = /* @__PURE__ */ new Map();
|
|
@@ -2198,25 +2598,31 @@ var ACPClientAdapter = class extends SimpleAdapter {
|
|
|
2198
2598
|
roomGeneration = /* @__PURE__ */ new Map();
|
|
2199
2599
|
resolvePermission;
|
|
2200
2600
|
resolveSessionMode;
|
|
2601
|
+
resolveSessionModel;
|
|
2201
2602
|
permissionTimeoutMs;
|
|
2603
|
+
turnTimeoutMs;
|
|
2202
2604
|
logger;
|
|
2203
2605
|
backend = null;
|
|
2204
2606
|
backendPromise = null;
|
|
2205
2607
|
client = null;
|
|
2206
2608
|
connectionHandle = null;
|
|
2609
|
+
pendingConnectionStop = null;
|
|
2207
2610
|
connection = null;
|
|
2208
2611
|
connectionState = null;
|
|
2209
2612
|
started = false;
|
|
2210
2613
|
systemPrompt = "";
|
|
2211
2614
|
spawnPromise = null;
|
|
2615
|
+
// Bumped by `stop()` and on every successful spawn install. Cleanup/timeout
|
|
2616
|
+
// and permission maps key by this plus session id so a stale generation
|
|
2617
|
+
// cannot alias a same-id session on a newer connection.
|
|
2618
|
+
connectionGeneration = 0;
|
|
2212
2619
|
constructor(options) {
|
|
2213
2620
|
super({
|
|
2214
2621
|
historyConverter: new ACPClientHistoryConverter()
|
|
2215
2622
|
});
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
}
|
|
2623
|
+
const command = options.command === void 0 ? [] : Array.isArray(options.command) ? [...options.command] : [options.command];
|
|
2624
|
+
const tcpEndpoint = validateTransport(command, options.host, options.port);
|
|
2625
|
+
this.command = command;
|
|
2220
2626
|
this.cwd = options.cwd ?? process.cwd();
|
|
2221
2627
|
this.env = options.env;
|
|
2222
2628
|
this.mcpServers = [...options.mcpServers ?? []];
|
|
@@ -2225,17 +2631,39 @@ var ACPClientAdapter = class extends SimpleAdapter {
|
|
|
2225
2631
|
this.enableMcpTools = options.enableMcpTools ?? true;
|
|
2226
2632
|
this.additionalMcpTools = [...options.additionalMcpTools ?? []];
|
|
2227
2633
|
this.clientCapabilities = options.clientCapabilities;
|
|
2228
|
-
this.connectionFactory = options.connectionFactory
|
|
2634
|
+
this.connectionFactory = options.connectionFactory;
|
|
2635
|
+
this.tcpEndpoint = tcpEndpoint;
|
|
2229
2636
|
this.resolvePermission = options.resolvePermission;
|
|
2230
2637
|
this.resolveSessionMode = options.resolveSessionMode;
|
|
2231
|
-
this.
|
|
2638
|
+
this.resolveSessionModel = options.resolveSessionModel;
|
|
2639
|
+
this.logger = resolveLogger(options.logger);
|
|
2232
2640
|
this.permissionTimeoutMs = options.permissionTimeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
|
|
2233
|
-
if ((this.resolvePermission || this.resolveSessionMode) && (!Number.isFinite(this.permissionTimeoutMs) || this.permissionTimeoutMs <= 0)) {
|
|
2641
|
+
if ((this.resolvePermission || this.resolveSessionMode || this.resolveSessionModel) && (!Number.isFinite(this.permissionTimeoutMs) || this.permissionTimeoutMs <= 0)) {
|
|
2234
2642
|
throw new ValidationError(`permissionTimeoutMs must be a positive finite number, got ${options.permissionTimeoutMs}`);
|
|
2235
2643
|
}
|
|
2644
|
+
if (this.resolvePermission || this.resolveSessionMode || this.resolveSessionModel) {
|
|
2645
|
+
assertWithinSetTimeoutBound(
|
|
2646
|
+
`permissionTimeoutMs must be at most ${MAX_SETTIMEOUT_DELAY_MS}, got ${options.permissionTimeoutMs}`,
|
|
2647
|
+
this.permissionTimeoutMs
|
|
2648
|
+
);
|
|
2649
|
+
}
|
|
2650
|
+
this.turnTimeoutMs = options.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS;
|
|
2651
|
+
if (typeof this.turnTimeoutMs !== "number" || Number.isNaN(this.turnTimeoutMs) || this.turnTimeoutMs <= 0) {
|
|
2652
|
+
throw new ValidationError(`turnTimeoutMs must be a positive number or Infinity, got ${options.turnTimeoutMs}`);
|
|
2653
|
+
}
|
|
2654
|
+
if (Number.isFinite(this.turnTimeoutMs)) {
|
|
2655
|
+
assertWithinSetTimeoutBound(
|
|
2656
|
+
`turnTimeoutMs must be Infinity or at most ${MAX_SETTIMEOUT_DELAY_MS}, got ${options.turnTimeoutMs}`,
|
|
2657
|
+
this.turnTimeoutMs
|
|
2658
|
+
);
|
|
2659
|
+
}
|
|
2236
2660
|
}
|
|
2237
2661
|
async onStarted(agentName, agentDescription) {
|
|
2662
|
+
const generation = this.connectionGeneration;
|
|
2238
2663
|
await super.onStarted(agentName, agentDescription);
|
|
2664
|
+
if (generation !== this.connectionGeneration) {
|
|
2665
|
+
throw new Error("ACP adapter start superseded by stop()");
|
|
2666
|
+
}
|
|
2239
2667
|
this.started = true;
|
|
2240
2668
|
this.systemPrompt = renderSystemPrompt({
|
|
2241
2669
|
agentName,
|
|
@@ -2252,44 +2680,109 @@ var ACPClientAdapter = class extends SimpleAdapter {
|
|
|
2252
2680
|
await this.withRoomTurnLock(context.roomId, () => this.runTurn(message, tools, participantsMessage, contactsMessage, context));
|
|
2253
2681
|
}
|
|
2254
2682
|
async runTurn(message, tools, participantsMessage, contactsMessage, context) {
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2683
|
+
let connection;
|
|
2684
|
+
let client = null;
|
|
2685
|
+
let sessionId;
|
|
2686
|
+
let generation = 0;
|
|
2687
|
+
try {
|
|
2688
|
+
const ensured = await this.ensureConnection();
|
|
2689
|
+
connection = ensured.connection;
|
|
2690
|
+
generation = ensured.generation;
|
|
2691
|
+
client = this.client;
|
|
2692
|
+
if (!client) {
|
|
2693
|
+
throw new Error("ACP client was not initialized");
|
|
2694
|
+
}
|
|
2695
|
+
sessionId = await this.getOrCreateSession(context.roomId, connection, generation, client);
|
|
2696
|
+
const sessionKey = this.sessionKey(generation, sessionId);
|
|
2697
|
+
client.beginSession(sessionId);
|
|
2698
|
+
const content = replaceUuidMentions(message.content, mentionSubjectsFromMetadata(message.metadata));
|
|
2699
|
+
const messageWithContext = [...systemUpdateParts(participantsMessage, contactsMessage), content].join("\n\n");
|
|
2700
|
+
const promptText = this.bootstrappedSessions.has(sessionKey) ? messageWithContext : `${this.buildSystemContext(context.roomId, message)}
|
|
2265
2701
|
|
|
2266
2702
|
${messageWithContext}`;
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
await connection.prompt({
|
|
2703
|
+
this.bootstrappedSessions.add(sessionKey);
|
|
2704
|
+
const response = await withTimeout(connection.prompt({
|
|
2270
2705
|
sessionId,
|
|
2271
2706
|
prompt: [{
|
|
2272
2707
|
type: "text",
|
|
2273
2708
|
text: promptText
|
|
2274
2709
|
}]
|
|
2710
|
+
}), this.turnTimeoutMs, () => new AcpTurnTimeoutError());
|
|
2711
|
+
await this.flushChunks({
|
|
2712
|
+
client,
|
|
2713
|
+
tools,
|
|
2714
|
+
sessionId,
|
|
2715
|
+
senderId: message.senderId,
|
|
2716
|
+
senderHandle: message.senderName ?? message.senderType
|
|
2275
2717
|
});
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
acp_error: toErrorMessage(error)
|
|
2718
|
+
await tools.sendEvent("ACP client session", "task", {
|
|
2719
|
+
acp_client_session_id: sessionId,
|
|
2720
|
+
acp_client_room_id: context.roomId
|
|
2280
2721
|
});
|
|
2281
|
-
|
|
2722
|
+
if (response.stopReason !== "end_turn") {
|
|
2723
|
+
await reportTurnFailure(
|
|
2724
|
+
tools,
|
|
2725
|
+
agentFailure(this.provider, `ACP turn ended with stop reason: ${response.stopReason ?? "unknown"}.`, response.stopReason),
|
|
2726
|
+
this.logger,
|
|
2727
|
+
{ roomId: context.roomId, sessionId }
|
|
2728
|
+
);
|
|
2729
|
+
}
|
|
2730
|
+
} catch (error) {
|
|
2731
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
2732
|
+
const isTimeout = error instanceof AcpTurnTimeoutError;
|
|
2733
|
+
if (isTimeout && connection && sessionId) {
|
|
2734
|
+
await this.abandonTimedOutTurn(connection, sessionId, generation);
|
|
2735
|
+
}
|
|
2736
|
+
if (client && sessionId) {
|
|
2737
|
+
try {
|
|
2738
|
+
await this.flushChunks({
|
|
2739
|
+
client,
|
|
2740
|
+
tools,
|
|
2741
|
+
sessionId,
|
|
2742
|
+
senderId: message.senderId,
|
|
2743
|
+
senderHandle: message.senderName ?? message.senderType
|
|
2744
|
+
});
|
|
2745
|
+
} finally {
|
|
2746
|
+
if (isTimeout) {
|
|
2747
|
+
client.resetChunks(sessionId);
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
const acpError = asAcpJsonRpcError(error);
|
|
2752
|
+
await reportTurnFailure(
|
|
2753
|
+
tools,
|
|
2754
|
+
isTimeout ? agentFailure(this.provider, "ACP turn timed out.", FAILURE_CODE_TIMEOUT) : acpError ? agentFailure(this.provider, acpError.message, String(acpError.code), acpError.data) : agentFailure(this.provider, asErrorMessage(error)),
|
|
2755
|
+
this.logger,
|
|
2756
|
+
{ roomId: context.roomId, sessionId }
|
|
2757
|
+
);
|
|
2282
2758
|
}
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2759
|
+
}
|
|
2760
|
+
// Best-effort: tells the agent to stop working on a turn Band has already
|
|
2761
|
+
// given up waiting for (the ACP client has no way to force it), evicts the
|
|
2762
|
+
// session so the room's next turn re-establishes rather than reuses it
|
|
2763
|
+
// (`establishSession` also refuses to restore it — see `abandonedSessions`),
|
|
2764
|
+
// and resets the chunk buffer so a stray notification the agent sends
|
|
2765
|
+
// before the next turn starts (the tail it may still send after `cancel`,
|
|
2766
|
+
// per the ACP spec) has nowhere to land (`sessionUpdate` only collects
|
|
2767
|
+
// into a session it still has a buffer for).
|
|
2768
|
+
//
|
|
2769
|
+
// `connection.cancel()` is fired via the shared `abandon()` helper, not
|
|
2770
|
+
// awaited: the turn only timed out because the underlying agent process
|
|
2771
|
+
// stopped responding, and `cancel` rides the same transport — awaiting it
|
|
2772
|
+
// would risk blocking this room's turn lock forever on the very process
|
|
2773
|
+
// that just proved it can hang.
|
|
2774
|
+
async abandonTimedOutTurn(connection, sessionId, generation) {
|
|
2775
|
+
const key = this.sessionKey(generation, sessionId);
|
|
2776
|
+
this.activeSessions.delete(key);
|
|
2777
|
+
this.abandonedSessions.add(key);
|
|
2778
|
+
const owner = [...this.roomToSession.entries()].find(([, value]) => value.sessionId === sessionId && value.generation === generation);
|
|
2779
|
+
if (owner) {
|
|
2780
|
+
this.unlinkOwner(owner[0], owner[1]);
|
|
2781
|
+
}
|
|
2782
|
+
abandon(
|
|
2783
|
+
() => connection.cancel({ sessionId }),
|
|
2784
|
+
(error) => this.safeWarn("acp_client.cancel_failed", { sessionId, error: asErrorMessage(error) })
|
|
2785
|
+
);
|
|
2293
2786
|
}
|
|
2294
2787
|
// A per-room async mutex: `fn` for a given `roomId` never overlaps another
|
|
2295
2788
|
// call for that same room, while different rooms stay fully concurrent.
|
|
@@ -2304,25 +2797,31 @@ ${messageWithContext}`;
|
|
|
2304
2797
|
return run;
|
|
2305
2798
|
}
|
|
2306
2799
|
async onCleanup(roomId) {
|
|
2307
|
-
const
|
|
2800
|
+
const owner = this.unlinkRoom(roomId);
|
|
2308
2801
|
this.roomTools.delete(roomId);
|
|
2309
2802
|
this.sessionsInFlight.delete(roomId);
|
|
2310
2803
|
this.roomTurnLocks.delete(roomId);
|
|
2311
2804
|
this.nextRoomGeneration(roomId);
|
|
2312
|
-
if (
|
|
2313
|
-
this.
|
|
2314
|
-
|
|
2315
|
-
this.
|
|
2805
|
+
if (owner) {
|
|
2806
|
+
const key = this.sessionKey(owner.generation, owner.sessionId);
|
|
2807
|
+
owner.client?.resetChunks(owner.sessionId);
|
|
2808
|
+
this.activeSessions.delete(key);
|
|
2809
|
+
this.bootstrappedSessions.delete(key);
|
|
2810
|
+
this.abandonedSessions.delete(key);
|
|
2811
|
+
this.cancelPendingPermissions(key, "room-closed");
|
|
2316
2812
|
}
|
|
2317
2813
|
}
|
|
2318
2814
|
async onRuntimeStop() {
|
|
2319
2815
|
await this.stop();
|
|
2320
2816
|
}
|
|
2321
2817
|
async stop() {
|
|
2818
|
+
this.connectionGeneration++;
|
|
2819
|
+
this.started = false;
|
|
2322
2820
|
this.spawnPromise = null;
|
|
2323
2821
|
this.connectionState = null;
|
|
2324
2822
|
this.activeSessions.clear();
|
|
2325
2823
|
this.bootstrappedSessions.clear();
|
|
2824
|
+
this.abandonedSessions.clear();
|
|
2326
2825
|
this.roomToSession.clear();
|
|
2327
2826
|
this.sessionToRoom.clear();
|
|
2328
2827
|
this.roomTools.clear();
|
|
@@ -2334,6 +2833,11 @@ ${messageWithContext}`;
|
|
|
2334
2833
|
this.cancelAllPendingPermissions("adapter-stopped");
|
|
2335
2834
|
this.client = null;
|
|
2336
2835
|
this.connection = null;
|
|
2836
|
+
if (this.pendingConnectionStop) {
|
|
2837
|
+
const stopPending = this.pendingConnectionStop;
|
|
2838
|
+
this.pendingConnectionStop = null;
|
|
2839
|
+
await stopPending();
|
|
2840
|
+
}
|
|
2337
2841
|
if (this.backend) {
|
|
2338
2842
|
const backend = this.backend;
|
|
2339
2843
|
this.backend = null;
|
|
@@ -2348,7 +2852,7 @@ ${messageWithContext}`;
|
|
|
2348
2852
|
rehydrate(history) {
|
|
2349
2853
|
for (const [roomId, sessionId] of Object.entries(history.roomToSession)) {
|
|
2350
2854
|
if (!this.roomToSession.has(roomId)) {
|
|
2351
|
-
this.linkSession(roomId, sessionId);
|
|
2855
|
+
this.linkSession(roomId, sessionId, this.connectionGeneration, null);
|
|
2352
2856
|
}
|
|
2353
2857
|
}
|
|
2354
2858
|
}
|
|
@@ -2360,8 +2864,9 @@ ${messageWithContext}`;
|
|
|
2360
2864
|
// on to activate/configure/prompt a session regardless of a `false` here
|
|
2361
2865
|
// would use a session this room was refused, not just fail to route its
|
|
2362
2866
|
// permissions.
|
|
2363
|
-
linkSession(roomId, sessionId) {
|
|
2364
|
-
const
|
|
2867
|
+
linkSession(roomId, sessionId, generation, client) {
|
|
2868
|
+
const key = this.sessionKey(generation, sessionId);
|
|
2869
|
+
const routedRoomId = this.sessionToRoom.get(key);
|
|
2365
2870
|
if (routedRoomId !== void 0 && routedRoomId !== roomId) {
|
|
2366
2871
|
this.safeWarn("refusing to route one ACP session to a second room", {
|
|
2367
2872
|
sessionId,
|
|
@@ -2370,12 +2875,15 @@ ${messageWithContext}`;
|
|
|
2370
2875
|
});
|
|
2371
2876
|
return false;
|
|
2372
2877
|
}
|
|
2373
|
-
const
|
|
2374
|
-
if (
|
|
2375
|
-
|
|
2878
|
+
const replaced = this.roomToSession.get(roomId);
|
|
2879
|
+
if (replaced && replaced.generation > generation) {
|
|
2880
|
+
return false;
|
|
2881
|
+
}
|
|
2882
|
+
if (replaced !== void 0 && (replaced.sessionId !== sessionId || replaced.generation !== generation)) {
|
|
2883
|
+
this.sessionToRoom.delete(this.sessionKey(replaced.generation, replaced.sessionId));
|
|
2376
2884
|
}
|
|
2377
|
-
this.roomToSession.set(roomId, sessionId);
|
|
2378
|
-
this.sessionToRoom.set(
|
|
2885
|
+
this.roomToSession.set(roomId, { sessionId, generation, client });
|
|
2886
|
+
this.sessionToRoom.set(key, roomId);
|
|
2379
2887
|
return true;
|
|
2380
2888
|
}
|
|
2381
2889
|
nextRoomGeneration(roomId) {
|
|
@@ -2401,16 +2909,26 @@ ${messageWithContext}`;
|
|
|
2401
2909
|
return Promise.race([operation, closedRejection]);
|
|
2402
2910
|
}
|
|
2403
2911
|
unlinkRoom(roomId) {
|
|
2404
|
-
const
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
this.sessionToRoom.delete(sessionId);
|
|
2912
|
+
const owner = this.roomToSession.get(roomId);
|
|
2913
|
+
if (owner) {
|
|
2914
|
+
this.unlinkOwner(roomId, owner);
|
|
2408
2915
|
}
|
|
2409
|
-
return
|
|
2916
|
+
return owner;
|
|
2917
|
+
}
|
|
2918
|
+
unlinkOwner(roomId, owner) {
|
|
2919
|
+
const current = this.roomToSession.get(roomId);
|
|
2920
|
+
if (!current || current.sessionId !== owner.sessionId || current.generation !== owner.generation) {
|
|
2921
|
+
return;
|
|
2922
|
+
}
|
|
2923
|
+
this.roomToSession.delete(roomId);
|
|
2924
|
+
this.sessionToRoom.delete(this.sessionKey(owner.generation, owner.sessionId));
|
|
2925
|
+
}
|
|
2926
|
+
sessionKey(generation, sessionId) {
|
|
2927
|
+
return `${generation}:${sessionId}`;
|
|
2410
2928
|
}
|
|
2411
2929
|
async ensureConnection() {
|
|
2412
2930
|
if (this.connection && !this.connection.signal.aborted) {
|
|
2413
|
-
return this.connection;
|
|
2931
|
+
return { connection: this.connection, generation: this.connectionGeneration };
|
|
2414
2932
|
}
|
|
2415
2933
|
if (!this.started) {
|
|
2416
2934
|
throw new Error("ACPClientAdapter was not started");
|
|
@@ -2419,56 +2937,103 @@ ${messageWithContext}`;
|
|
|
2419
2937
|
if (isCreator) {
|
|
2420
2938
|
this.spawnPromise = this.spawnConnection();
|
|
2421
2939
|
}
|
|
2940
|
+
const spawnPromise = this.spawnPromise;
|
|
2422
2941
|
try {
|
|
2423
|
-
|
|
2942
|
+
const connection = await spawnPromise;
|
|
2943
|
+
return { connection, generation: this.connectionGeneration };
|
|
2424
2944
|
} finally {
|
|
2425
|
-
if (isCreator
|
|
2945
|
+
if (isCreator && this.spawnPromise === spawnPromise) {
|
|
2946
|
+
this.spawnPromise = null;
|
|
2947
|
+
}
|
|
2426
2948
|
}
|
|
2427
2949
|
}
|
|
2428
2950
|
async spawnConnection() {
|
|
2429
|
-
const
|
|
2430
|
-
const
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
}
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2951
|
+
const generation = this.connectionGeneration;
|
|
2952
|
+
const attempt = new AbortController();
|
|
2953
|
+
let handle = null;
|
|
2954
|
+
const stopAttempt = async () => {
|
|
2955
|
+
attempt.abort();
|
|
2956
|
+
await handle?.stop();
|
|
2957
|
+
};
|
|
2958
|
+
this.pendingConnectionStop = stopAttempt;
|
|
2959
|
+
try {
|
|
2960
|
+
const acp = await acpModule.get();
|
|
2961
|
+
if (attempt.signal.aborted) {
|
|
2962
|
+
throw new Error(CONNECTION_ATTEMPT_SUPERSEDED_ERROR);
|
|
2963
|
+
}
|
|
2964
|
+
const owner = { generation: -1 };
|
|
2965
|
+
const client = new BandACPClient((params) => this.routePermissionRequest(params, owner.generation));
|
|
2966
|
+
handle = await (this.connectionFactory ? this.connectionFactory(client, {
|
|
2967
|
+
command: this.command,
|
|
2968
|
+
cwd: this.cwd,
|
|
2969
|
+
env: this.env
|
|
2970
|
+
}) : this.tcpEndpoint ? createTcpConnection(client, this.tcpEndpoint, attempt.signal) : createSubprocessConnection(client, {
|
|
2971
|
+
command: this.command,
|
|
2972
|
+
cwd: this.cwd,
|
|
2973
|
+
env: this.env
|
|
2974
|
+
}));
|
|
2975
|
+
const connection2 = handle.connection;
|
|
2976
|
+
if (attempt.signal.aborted) {
|
|
2977
|
+
await handle.stop();
|
|
2978
|
+
throw new Error(CONNECTION_ATTEMPT_SUPERSEDED_ERROR);
|
|
2979
|
+
}
|
|
2980
|
+
const initializeResult = await this.raceAgainstConnectionClose(connection2, connection2.initialize({
|
|
2981
|
+
protocolVersion: acp.PROTOCOL_VERSION,
|
|
2982
|
+
clientCapabilities: this.clientCapabilities ?? {}
|
|
2983
|
+
}));
|
|
2984
|
+
if (this.authMethod) {
|
|
2985
|
+
await this.raceAgainstConnectionClose(connection2, connection2.authenticate({
|
|
2986
|
+
methodId: this.authMethod
|
|
2987
|
+
}));
|
|
2988
|
+
}
|
|
2989
|
+
if (generation !== this.connectionGeneration) {
|
|
2990
|
+
await handle.stop();
|
|
2991
|
+
throw new Error(CONNECTION_ATTEMPT_SUPERSEDED_ERROR);
|
|
2992
|
+
}
|
|
2993
|
+
this.connectionGeneration++;
|
|
2994
|
+
owner.generation = this.connectionGeneration;
|
|
2995
|
+
this.client = client;
|
|
2996
|
+
this.connection = connection2;
|
|
2997
|
+
this.connectionHandle = handle;
|
|
2998
|
+
this.connectionState = initializeResult;
|
|
2999
|
+
} catch (error) {
|
|
3000
|
+
if (handle && this.connection !== handle.connection) {
|
|
3001
|
+
try {
|
|
3002
|
+
await handle.stop();
|
|
3003
|
+
} catch (stopError) {
|
|
3004
|
+
this.safeWarn("acp_client.handle_stop_after_handshake_failure", { error: asErrorMessage(stopError) });
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
throw error;
|
|
3008
|
+
} finally {
|
|
3009
|
+
if (this.pendingConnectionStop === stopAttempt) {
|
|
3010
|
+
this.pendingConnectionStop = null;
|
|
3011
|
+
}
|
|
2445
3012
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
this.connectionHandle = handle;
|
|
2449
|
-
this.connectionState = initializeResult;
|
|
3013
|
+
const connection = handle.connection;
|
|
3014
|
+
const installedGeneration = this.connectionGeneration;
|
|
2450
3015
|
void connection.closed.finally(() => {
|
|
3016
|
+
this.pruneConnectionGeneration(installedGeneration);
|
|
2451
3017
|
if (this.connection === connection) {
|
|
2452
3018
|
this.connection = null;
|
|
2453
3019
|
this.connectionHandle = null;
|
|
2454
3020
|
this.connectionState = null;
|
|
2455
|
-
this.activeSessions.clear();
|
|
2456
|
-
this.cancelAllPendingPermissions("connection-lost");
|
|
2457
3021
|
}
|
|
2458
3022
|
});
|
|
2459
3023
|
return connection;
|
|
2460
3024
|
}
|
|
2461
|
-
async getOrCreateSession(roomId, connection) {
|
|
2462
|
-
const
|
|
2463
|
-
|
|
2464
|
-
|
|
3025
|
+
async getOrCreateSession(roomId, connection, connectionGeneration, client) {
|
|
3026
|
+
const owner = this.roomToSession.get(roomId);
|
|
3027
|
+
const existingSessionId = owner?.sessionId;
|
|
3028
|
+
if (owner && owner.generation === connectionGeneration && this.activeSessions.has(this.sessionKey(connectionGeneration, owner.sessionId))) {
|
|
3029
|
+
return owner.sessionId;
|
|
2465
3030
|
}
|
|
2466
3031
|
const inFlight = this.sessionsInFlight.get(roomId);
|
|
2467
3032
|
if (inFlight) {
|
|
2468
3033
|
return inFlight;
|
|
2469
3034
|
}
|
|
2470
3035
|
const generation = this.nextRoomGeneration(roomId);
|
|
2471
|
-
const establishing = this.establishSession(roomId, existingSessionId, connection, generation);
|
|
3036
|
+
const establishing = this.establishSession(roomId, existingSessionId, connection, generation, connectionGeneration, client);
|
|
2472
3037
|
establishing.finally(() => {
|
|
2473
3038
|
if (this.sessionsInFlight.get(roomId) === establishing) {
|
|
2474
3039
|
this.sessionsInFlight.delete(roomId);
|
|
@@ -2477,15 +3042,17 @@ ${messageWithContext}`;
|
|
|
2477
3042
|
this.sessionsInFlight.set(roomId, establishing);
|
|
2478
3043
|
return establishing;
|
|
2479
3044
|
}
|
|
2480
|
-
async establishSession(roomId, existingSessionId, connection, generation) {
|
|
3045
|
+
async establishSession(roomId, existingSessionId, connection, generation, connectionGeneration, client) {
|
|
2481
3046
|
const mcpServers = await this.buildSessionMcpServers();
|
|
2482
|
-
if (existingSessionId) {
|
|
3047
|
+
if (existingSessionId && !this.abandonedSessions.has(this.sessionKey(connectionGeneration, existingSessionId))) {
|
|
2483
3048
|
const restored = await this.tryRestoreSession(connection, existingSessionId, mcpServers);
|
|
2484
3049
|
if (restored.ok) {
|
|
2485
|
-
this.linkOrAbandon(roomId, existingSessionId, generation);
|
|
2486
|
-
this.
|
|
2487
|
-
this.
|
|
3050
|
+
this.linkOrAbandon(roomId, existingSessionId, generation, connectionGeneration, client);
|
|
3051
|
+
const restoredKey = this.sessionKey(connectionGeneration, existingSessionId);
|
|
3052
|
+
this.activeSessions.add(restoredKey);
|
|
3053
|
+
this.bootstrappedSessions.add(restoredKey);
|
|
2488
3054
|
await this.configureSessionMode(roomId, existingSessionId, restored.modes, connection);
|
|
3055
|
+
await this.configureSessionModel(roomId, existingSessionId, restored.configOptions, connection);
|
|
2489
3056
|
return existingSessionId;
|
|
2490
3057
|
}
|
|
2491
3058
|
}
|
|
@@ -2493,9 +3060,10 @@ ${messageWithContext}`;
|
|
|
2493
3060
|
cwd: this.cwd,
|
|
2494
3061
|
mcpServers
|
|
2495
3062
|
}));
|
|
2496
|
-
this.linkOrAbandon(roomId, created.sessionId, generation);
|
|
2497
|
-
this.activeSessions.add(created.sessionId);
|
|
3063
|
+
this.linkOrAbandon(roomId, created.sessionId, generation, connectionGeneration, client);
|
|
3064
|
+
this.activeSessions.add(this.sessionKey(connectionGeneration, created.sessionId));
|
|
2498
3065
|
await this.configureSessionMode(roomId, created.sessionId, created.modes, connection);
|
|
3066
|
+
await this.configureSessionModel(roomId, created.sessionId, created.configOptions, connection);
|
|
2499
3067
|
return created.sessionId;
|
|
2500
3068
|
}
|
|
2501
3069
|
// The single gate an establishment must pass before it's allowed to claim
|
|
@@ -2505,11 +3073,11 @@ ${messageWithContext}`;
|
|
|
2505
3073
|
// room. Either failure throws — this establishment cannot silently
|
|
2506
3074
|
// continue to activate, configure, and prompt a session it has no right
|
|
2507
3075
|
// to use for this room.
|
|
2508
|
-
linkOrAbandon(roomId, sessionId, generation) {
|
|
3076
|
+
linkOrAbandon(roomId, sessionId, generation, connectionGeneration, client) {
|
|
2509
3077
|
if (!this.isCurrentGeneration(roomId, generation)) {
|
|
2510
3078
|
throw new Error(`ACP session establishment for room "${roomId}" was superseded before it could be linked`);
|
|
2511
3079
|
}
|
|
2512
|
-
if (!this.linkSession(roomId, sessionId)) {
|
|
3080
|
+
if (!this.linkSession(roomId, sessionId, connectionGeneration, client)) {
|
|
2513
3081
|
throw new Error(`ACP session "${sessionId}" could not be linked to room "${roomId}": already routed elsewhere`);
|
|
2514
3082
|
}
|
|
2515
3083
|
}
|
|
@@ -2523,7 +3091,8 @@ ${messageWithContext}`;
|
|
|
2523
3091
|
if (availableModes.length === 0) {
|
|
2524
3092
|
return;
|
|
2525
3093
|
}
|
|
2526
|
-
const selectedModeId = await this.
|
|
3094
|
+
const selectedModeId = await this.resolveManualSelection(
|
|
3095
|
+
"resolveSessionMode",
|
|
2527
3096
|
(signal) => this.resolveSessionMode({
|
|
2528
3097
|
roomId,
|
|
2529
3098
|
sessionId,
|
|
@@ -2546,8 +3115,8 @@ ${messageWithContext}`;
|
|
|
2546
3115
|
try {
|
|
2547
3116
|
await withTimeout(
|
|
2548
3117
|
connection.setSessionMode({ sessionId, modeId: selectedModeId }),
|
|
2549
|
-
|
|
2550
|
-
`setSessionMode did not respond within ${
|
|
3118
|
+
SET_SESSION_CONFIG_TIMEOUT_MS,
|
|
3119
|
+
`setSessionMode did not respond within ${SET_SESSION_CONFIG_TIMEOUT_MS}ms`
|
|
2551
3120
|
);
|
|
2552
3121
|
} catch (error) {
|
|
2553
3122
|
this.safeWarn("failed to switch session into the selected mode", {
|
|
@@ -2557,7 +3126,61 @@ ${messageWithContext}`;
|
|
|
2557
3126
|
});
|
|
2558
3127
|
}
|
|
2559
3128
|
}
|
|
2560
|
-
|
|
3129
|
+
// Best-effort, same contract as `configureSessionMode`: never throws, so a
|
|
3130
|
+
// model switch going wrong can't take a session establishment down with
|
|
3131
|
+
// it. Reads the model catalog off `configOptions` (the stable, generic
|
|
3132
|
+
// mechanism every ACP protocol version from this repo's actual pin through
|
|
3133
|
+
// npm's current latest carries), not the separate `unstable_`-prefixed
|
|
3134
|
+
// `SessionModelState` field, which real agents don't populate — see
|
|
3135
|
+
// `ACPModelRequest`'s doc comment for why.
|
|
3136
|
+
async configureSessionModel(roomId, sessionId, configOptions, connection) {
|
|
3137
|
+
if (!this.resolveSessionModel || !Array.isArray(configOptions)) {
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3140
|
+
const modelOption = configOptions.find(isModelConfigOption) ?? configOptions.find(isModelConfigOptionById);
|
|
3141
|
+
if (!modelOption) {
|
|
3142
|
+
return;
|
|
3143
|
+
}
|
|
3144
|
+
const availableModels = flattenConfigSelectOptions(modelOption.options);
|
|
3145
|
+
if (availableModels.length === 0) {
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3148
|
+
const selectedModelId = await this.resolveManualSelection(
|
|
3149
|
+
"resolveSessionModel",
|
|
3150
|
+
(signal) => this.resolveSessionModel({
|
|
3151
|
+
roomId,
|
|
3152
|
+
sessionId,
|
|
3153
|
+
currentModelId: modelOption.currentValue,
|
|
3154
|
+
models: availableModels
|
|
3155
|
+
}, signal),
|
|
3156
|
+
connection.signal
|
|
3157
|
+
);
|
|
3158
|
+
if (!selectedModelId || selectedModelId === modelOption.currentValue) {
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
if (!availableModels.some((model) => model?.value === selectedModelId)) {
|
|
3162
|
+
this.safeWarn("resolveSessionModel selected a model id this session does not advertise", {
|
|
3163
|
+
sessionId,
|
|
3164
|
+
selectedModelId,
|
|
3165
|
+
availableModelIds: availableModels.map((model) => model?.value)
|
|
3166
|
+
});
|
|
3167
|
+
return;
|
|
3168
|
+
}
|
|
3169
|
+
try {
|
|
3170
|
+
await withTimeout(
|
|
3171
|
+
connection.setSessionConfigOption({ sessionId, configId: modelOption.id, value: selectedModelId }),
|
|
3172
|
+
SET_SESSION_CONFIG_TIMEOUT_MS,
|
|
3173
|
+
`setSessionConfigOption did not respond within ${SET_SESSION_CONFIG_TIMEOUT_MS}ms`
|
|
3174
|
+
);
|
|
3175
|
+
} catch (error) {
|
|
3176
|
+
this.safeWarn("failed to switch session into the selected model", {
|
|
3177
|
+
sessionId,
|
|
3178
|
+
selectedModelId,
|
|
3179
|
+
error: String(error)
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
async resolveManualSelection(hookName, resolver, signal) {
|
|
2561
3184
|
const controller = new AbortController();
|
|
2562
3185
|
const abort = () => controller.abort();
|
|
2563
3186
|
signal.addEventListener("abort", abort, { once: true });
|
|
@@ -2566,17 +3189,28 @@ ${messageWithContext}`;
|
|
|
2566
3189
|
const cancelled = new Promise((resolve) => {
|
|
2567
3190
|
controller.signal.addEventListener("abort", () => resolve(void 0), { once: true });
|
|
2568
3191
|
});
|
|
3192
|
+
if (signal.aborted) {
|
|
3193
|
+
abort();
|
|
3194
|
+
}
|
|
2569
3195
|
const timeout = new Promise((resolve) => {
|
|
2570
3196
|
timer = setTimeout(() => resolve(void 0), this.permissionTimeoutMs);
|
|
2571
3197
|
});
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
3198
|
+
let settled = false;
|
|
3199
|
+
const resolved = await Promise.race([
|
|
3200
|
+
Promise.resolve().then(() => resolver(controller.signal)).then((chosenId) => {
|
|
3201
|
+
if (settled && chosenId !== void 0) {
|
|
3202
|
+
this.safeWarn(`${hookName} answered after the request was abandoned; discarding`, { chosenId });
|
|
3203
|
+
}
|
|
3204
|
+
return chosenId;
|
|
3205
|
+
}).catch((error) => {
|
|
3206
|
+
this.safeWarn(`${hookName} threw; preserving the harness default`, { error: String(error) });
|
|
2575
3207
|
return void 0;
|
|
2576
3208
|
}),
|
|
2577
3209
|
timeout,
|
|
2578
3210
|
cancelled
|
|
2579
3211
|
]);
|
|
3212
|
+
settled = true;
|
|
3213
|
+
return resolved;
|
|
2580
3214
|
} finally {
|
|
2581
3215
|
clearTimeout(timer);
|
|
2582
3216
|
signal.removeEventListener("abort", abort);
|
|
@@ -2586,13 +3220,13 @@ ${messageWithContext}`;
|
|
|
2586
3220
|
async tryRestoreSession(connection, sessionId, mcpServers) {
|
|
2587
3221
|
const capabilities = this.connectionState?.agentCapabilities;
|
|
2588
3222
|
const params = { cwd: this.cwd, mcpServers, sessionId };
|
|
2589
|
-
const restore = capabilities?.loadSession ? () => connection.loadSession(params) : capabilities?.sessionCapabilities?.resume ? () => connection.
|
|
3223
|
+
const restore = capabilities?.loadSession ? () => connection.loadSession(params) : capabilities?.sessionCapabilities?.resume ? () => connection.resumeSession(params) : null;
|
|
2590
3224
|
if (!restore) {
|
|
2591
3225
|
return { ok: false };
|
|
2592
3226
|
}
|
|
2593
3227
|
try {
|
|
2594
3228
|
const restored = await this.raceAgainstConnectionClose(connection, restore());
|
|
2595
|
-
return { ok: true, modes: restored?.modes };
|
|
3229
|
+
return { ok: true, modes: restored?.modes, configOptions: restored?.configOptions };
|
|
2596
3230
|
} catch {
|
|
2597
3231
|
return { ok: false };
|
|
2598
3232
|
}
|
|
@@ -2710,9 +3344,13 @@ ${messageWithContext}`;
|
|
|
2710
3344
|
// silently declined. `activeSessions` is the gate that keeps a dead
|
|
2711
3345
|
// session from raising a live prompt — `roomToSession` deliberately
|
|
2712
3346
|
// outlives a dropped connection so the session can be restored later.
|
|
2713
|
-
async routePermissionRequest(params) {
|
|
2714
|
-
|
|
2715
|
-
|
|
3347
|
+
async routePermissionRequest(params, connectionGeneration) {
|
|
3348
|
+
if (connectionGeneration < 0) {
|
|
3349
|
+
return { outcome: { outcome: "cancelled" } };
|
|
3350
|
+
}
|
|
3351
|
+
const key = this.sessionKey(connectionGeneration, params.sessionId);
|
|
3352
|
+
const isActive = this.activeSessions.has(key);
|
|
3353
|
+
const roomId = isActive ? this.sessionToRoom.get(key) : void 0;
|
|
2716
3354
|
const tools = roomId === void 0 ? void 0 : this.roomTools.get(roomId);
|
|
2717
3355
|
if (roomId === void 0 || !tools) {
|
|
2718
3356
|
this.safeWarn("cancelling a permission request that maps to no live room", {
|
|
@@ -2724,7 +3362,7 @@ ${messageWithContext}`;
|
|
|
2724
3362
|
return { outcome: { outcome: "cancelled" } };
|
|
2725
3363
|
}
|
|
2726
3364
|
try {
|
|
2727
|
-
return await this.handlePermissionRequest(tools, roomId, params);
|
|
3365
|
+
return await this.handlePermissionRequest(tools, roomId, params, connectionGeneration);
|
|
2728
3366
|
} catch (error) {
|
|
2729
3367
|
this.safeWarn("permission handling failed; cancelling the request", {
|
|
2730
3368
|
sessionId: params.sessionId,
|
|
@@ -2734,12 +3372,12 @@ ${messageWithContext}`;
|
|
|
2734
3372
|
return { outcome: { outcome: "cancelled" } };
|
|
2735
3373
|
}
|
|
2736
3374
|
}
|
|
2737
|
-
async handlePermissionRequest(tools, roomId, params) {
|
|
3375
|
+
async handlePermissionRequest(tools, roomId, params, connectionGeneration) {
|
|
2738
3376
|
const toolName = params.toolCall.title ?? "unknown";
|
|
2739
3377
|
const autoSelection = this.resolvePermission ? void 0 : choosePermissionOption(params.options);
|
|
2740
3378
|
const controller = this.resolvePermission ? new AbortController() : void 0;
|
|
2741
3379
|
if (controller) {
|
|
2742
|
-
this.trackPending(params.sessionId, controller);
|
|
3380
|
+
this.trackPending(this.sessionKey(connectionGeneration, params.sessionId), controller);
|
|
2743
3381
|
}
|
|
2744
3382
|
let requestEventFailed = false;
|
|
2745
3383
|
const [, resolvedChosenId] = await Promise.all([
|
|
@@ -2762,7 +3400,7 @@ ${messageWithContext}`;
|
|
|
2762
3400
|
this.abandon(controller, "no-answer");
|
|
2763
3401
|
}
|
|
2764
3402
|
}),
|
|
2765
|
-
controller ? this.resolveManually(roomId, params, controller) : Promise.resolve(autoSelection?.optionId)
|
|
3403
|
+
controller ? this.resolveManually(roomId, params, controller, connectionGeneration) : Promise.resolve(autoSelection?.optionId)
|
|
2766
3404
|
]);
|
|
2767
3405
|
const chosenId = requestEventFailed ? void 0 : resolvedChosenId;
|
|
2768
3406
|
const response = this.toResponse(chosenId, params.options, { roomId, sessionId: params.sessionId });
|
|
@@ -2807,7 +3445,7 @@ ${messageWithContext}`;
|
|
|
2807
3445
|
// supplies. `controller` is the same object tracked in `pendingPermissions`,
|
|
2808
3446
|
// so there is exactly one cancellation channel here, not a second
|
|
2809
3447
|
// hand-rolled one alongside it.
|
|
2810
|
-
async resolveManually(roomId, params, controller) {
|
|
3448
|
+
async resolveManually(roomId, params, controller, connectionGeneration) {
|
|
2811
3449
|
const abandoned = new Promise((resolve) => {
|
|
2812
3450
|
controller.signal.addEventListener("abort", () => resolve(void 0));
|
|
2813
3451
|
});
|
|
@@ -2827,7 +3465,7 @@ ${messageWithContext}`;
|
|
|
2827
3465
|
]);
|
|
2828
3466
|
} finally {
|
|
2829
3467
|
clearTimeout(timer);
|
|
2830
|
-
this.untrackPending(params.sessionId, controller);
|
|
3468
|
+
this.untrackPending(this.sessionKey(connectionGeneration, params.sessionId), controller);
|
|
2831
3469
|
}
|
|
2832
3470
|
}
|
|
2833
3471
|
// An answer that lands after the request was given up on can no longer be
|
|
@@ -2872,17 +3510,36 @@ ${messageWithContext}`;
|
|
|
2872
3510
|
this.cancelPendingPermissions(sessionId, reason);
|
|
2873
3511
|
}
|
|
2874
3512
|
}
|
|
2875
|
-
|
|
2876
|
-
const
|
|
2877
|
-
|
|
2878
|
-
|
|
3513
|
+
pruneConnectionGeneration(generation) {
|
|
3514
|
+
const prefix = `${generation}:`;
|
|
3515
|
+
for (const key of [...this.abandonedSessions]) {
|
|
3516
|
+
if (key.startsWith(prefix)) {
|
|
3517
|
+
this.abandonedSessions.delete(key);
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
for (const key of [...this.activeSessions]) {
|
|
3521
|
+
if (key.startsWith(prefix)) {
|
|
3522
|
+
this.activeSessions.delete(key);
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
for (const key of [...this.bootstrappedSessions]) {
|
|
3526
|
+
if (key.startsWith(prefix)) {
|
|
3527
|
+
this.bootstrappedSessions.delete(key);
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
for (const key of [...this.pendingPermissions.keys()]) {
|
|
3531
|
+
if (key.startsWith(prefix)) {
|
|
3532
|
+
this.cancelPendingPermissions(key, "connection-lost");
|
|
3533
|
+
}
|
|
2879
3534
|
}
|
|
2880
|
-
|
|
3535
|
+
}
|
|
3536
|
+
async flushChunks(input) {
|
|
3537
|
+
for (const chunk of input.client.takeCollectedChunks(input.sessionId)) {
|
|
2881
3538
|
if (isBlankEventContent(chunk.content)) {
|
|
2882
3539
|
continue;
|
|
2883
3540
|
}
|
|
2884
3541
|
if (chunk.chunkType === "text") {
|
|
2885
|
-
await input.tools
|
|
3542
|
+
await deliverReply(input.tools, chunk.content, [{
|
|
2886
3543
|
id: input.senderId,
|
|
2887
3544
|
handle: input.senderHandle
|
|
2888
3545
|
}]);
|
|
@@ -2897,6 +3554,33 @@ ${messageWithContext}`;
|
|
|
2897
3554
|
}
|
|
2898
3555
|
}
|
|
2899
3556
|
};
|
|
3557
|
+
var AcpTurnTimeoutError = class extends Error {
|
|
3558
|
+
};
|
|
3559
|
+
function validateTransport(command, host, port) {
|
|
3560
|
+
const hasHost = host !== void 0;
|
|
3561
|
+
const hasPort = port !== void 0;
|
|
3562
|
+
if (hasHost !== hasPort) {
|
|
3563
|
+
throw new ValidationError("ACPClientAdapter requires both host and port for a TCP connection");
|
|
3564
|
+
}
|
|
3565
|
+
if (hasHost && hasPort) {
|
|
3566
|
+
if (command.length > 0) {
|
|
3567
|
+
throw new ValidationError("ACPClientAdapter cannot use command with a TCP connection");
|
|
3568
|
+
}
|
|
3569
|
+
if (typeof host !== "string" || host.trim().length === 0) {
|
|
3570
|
+
throw new ValidationError("ACPClientAdapter TCP host must be a non-empty string");
|
|
3571
|
+
}
|
|
3572
|
+
if (!Number.isInteger(port) || port < MIN_TCP_PORT || port > MAX_TCP_PORT) {
|
|
3573
|
+
throw new ValidationError(
|
|
3574
|
+
`ACPClientAdapter TCP port must be an integer between ${MIN_TCP_PORT} and ${MAX_TCP_PORT}`
|
|
3575
|
+
);
|
|
3576
|
+
}
|
|
3577
|
+
return { host, port };
|
|
3578
|
+
}
|
|
3579
|
+
if (command.length === 0 || typeof command[0] !== "string" || command[0].trim().length === 0) {
|
|
3580
|
+
throw new ValidationError("ACPClientAdapter requires a command or TCP host and port");
|
|
3581
|
+
}
|
|
3582
|
+
return null;
|
|
3583
|
+
}
|
|
2900
3584
|
async function createSubprocessConnection(client, options) {
|
|
2901
3585
|
const acp = await acpModule.get();
|
|
2902
3586
|
const child = (0, import_node_child_process.spawn)(options.command[0], options.command.slice(1), {
|
|
@@ -2945,11 +3629,86 @@ async function createSubprocessConnection(client, options) {
|
|
|
2945
3629
|
}
|
|
2946
3630
|
};
|
|
2947
3631
|
}
|
|
2948
|
-
function
|
|
2949
|
-
|
|
2950
|
-
|
|
3632
|
+
async function createTcpConnection(client, endpoint, signal) {
|
|
3633
|
+
const acp = await acpModule.get();
|
|
3634
|
+
if (signal?.aborted) {
|
|
3635
|
+
throw new Error(TCP_CONNECTION_ATTEMPT_ABORTED_ERROR);
|
|
3636
|
+
}
|
|
3637
|
+
const socket = await new Promise((resolve, reject) => {
|
|
3638
|
+
const candidate = (0, import_node_net.createConnection)(endpoint);
|
|
3639
|
+
const cleanup = () => {
|
|
3640
|
+
candidate.off("error", fail);
|
|
3641
|
+
candidate.off("connect", connect);
|
|
3642
|
+
signal?.removeEventListener("abort", abort);
|
|
3643
|
+
};
|
|
3644
|
+
const fail = (error) => {
|
|
3645
|
+
cleanup();
|
|
3646
|
+
reject(error);
|
|
3647
|
+
};
|
|
3648
|
+
const connect = () => {
|
|
3649
|
+
cleanup();
|
|
3650
|
+
resolve(candidate);
|
|
3651
|
+
};
|
|
3652
|
+
const abort = () => {
|
|
3653
|
+
candidate.destroy();
|
|
3654
|
+
fail(new Error(TCP_CONNECTION_ATTEMPT_ABORTED_ERROR));
|
|
3655
|
+
};
|
|
3656
|
+
candidate.once("error", fail);
|
|
3657
|
+
candidate.once("connect", connect);
|
|
3658
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
3659
|
+
});
|
|
3660
|
+
const webSocket = import_node_stream.Duplex.toWeb(socket);
|
|
3661
|
+
const stream = acp.ndJsonStream(
|
|
3662
|
+
webSocket.writable,
|
|
3663
|
+
webSocket.readable
|
|
3664
|
+
);
|
|
3665
|
+
const connection = new acp.ClientSideConnection(() => client, stream);
|
|
3666
|
+
let stopped = false;
|
|
3667
|
+
return {
|
|
3668
|
+
connection,
|
|
3669
|
+
stop: async () => {
|
|
3670
|
+
if (stopped) {
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3673
|
+
stopped = true;
|
|
3674
|
+
socket.destroy();
|
|
3675
|
+
await connection.closed;
|
|
3676
|
+
}
|
|
3677
|
+
};
|
|
3678
|
+
}
|
|
3679
|
+
var MODEL_CONFIG_OPTION_KEY = "model";
|
|
3680
|
+
function isSessionConfigSelect(option) {
|
|
3681
|
+
return option?.type === "select";
|
|
3682
|
+
}
|
|
3683
|
+
function isModelConfigOption(option) {
|
|
3684
|
+
return isSessionConfigSelect(option) && option.category === MODEL_CONFIG_OPTION_KEY;
|
|
3685
|
+
}
|
|
3686
|
+
function isModelConfigOptionById(option) {
|
|
3687
|
+
return isSessionConfigSelect(option) && option.id === MODEL_CONFIG_OPTION_KEY;
|
|
3688
|
+
}
|
|
3689
|
+
function flattenConfigSelectOptions(options) {
|
|
3690
|
+
if (!Array.isArray(options)) {
|
|
3691
|
+
return [];
|
|
3692
|
+
}
|
|
3693
|
+
return options.flatMap((entry) => {
|
|
3694
|
+
if (!asOptionalRecord2(entry)) {
|
|
3695
|
+
return [];
|
|
3696
|
+
}
|
|
3697
|
+
if ("group" in entry) {
|
|
3698
|
+
return Array.isArray(entry.options) ? entry.options : [];
|
|
3699
|
+
}
|
|
3700
|
+
return [entry];
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
function isAcpErrorResponse(error) {
|
|
3704
|
+
return typeof error === "object" && error !== null && typeof error.code === "number" && typeof error.message === "string";
|
|
3705
|
+
}
|
|
3706
|
+
function asAcpJsonRpcError(error) {
|
|
3707
|
+
if (isAcpErrorResponse(error)) {
|
|
3708
|
+
return error;
|
|
2951
3709
|
}
|
|
2952
|
-
|
|
3710
|
+
const nested = asOptionalRecord2(error)?.error;
|
|
3711
|
+
return isAcpErrorResponse(nested) ? nested : void 0;
|
|
2953
3712
|
}
|
|
2954
3713
|
|
|
2955
3714
|
// src/adapters/acp/ACPServer.ts
|
|
@@ -3204,6 +3963,7 @@ var DEFAULT_MAX_SESSIONS = 100;
|
|
|
3204
3963
|
var DEFAULT_PROMPT_TIMEOUT_MS = 3e5;
|
|
3205
3964
|
var DEFAULT_PROMPT_COMPLETION_GRACE_MS = 250;
|
|
3206
3965
|
var BandACPServerAdapter = class extends SimpleAdapter {
|
|
3966
|
+
provider = "acp-server";
|
|
3207
3967
|
bandRest;
|
|
3208
3968
|
maxSessions;
|
|
3209
3969
|
responseTimeoutMs;
|
|
@@ -3712,7 +4472,7 @@ var ACPServer = class {
|
|
|
3712
4472
|
sessionId
|
|
3713
4473
|
};
|
|
3714
4474
|
}
|
|
3715
|
-
async
|
|
4475
|
+
async resumeSession(params) {
|
|
3716
4476
|
if (!this.adapter.hasSession(params.sessionId)) {
|
|
3717
4477
|
const acp = await acpModule.get();
|
|
3718
4478
|
throw acp.RequestError.resourceNotFound(params.sessionId);
|
|
@@ -3725,7 +4485,7 @@ var ACPServer = class {
|
|
|
3725
4485
|
modes: this.adapter.getModeState(params.sessionId)
|
|
3726
4486
|
};
|
|
3727
4487
|
}
|
|
3728
|
-
async
|
|
4488
|
+
async closeSession(params) {
|
|
3729
4489
|
await this.adapter.closeSession(params.sessionId);
|
|
3730
4490
|
return {};
|
|
3731
4491
|
}
|
|
@@ -3737,14 +4497,12 @@ var ACPServer = class {
|
|
|
3737
4497
|
this.adapter.setSessionMode(params.sessionId, params.modeId);
|
|
3738
4498
|
return {};
|
|
3739
4499
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
return {};
|
|
3747
|
-
}
|
|
4500
|
+
// `unstable_setSessionModel`/`SetSessionModelRequest` from ACP <1.0 were
|
|
4501
|
+
// removed outright in v1 rather than renamed: model selection now goes
|
|
4502
|
+
// through the generic `setSessionConfigOption` RPC (a `SessionConfigOption`
|
|
4503
|
+
// with category `"model"`). This server doesn't advertise any config
|
|
4504
|
+
// options yet (see the stub below), so there is nothing for a client to
|
|
4505
|
+
// select and no equivalent call to make here.
|
|
3748
4506
|
async setSessionConfigOption(_params) {
|
|
3749
4507
|
return {
|
|
3750
4508
|
configOptions: []
|
|
@@ -3828,12 +4586,48 @@ function extractPromptText(prompt) {
|
|
|
3828
4586
|
return parts.join("\n").trim();
|
|
3829
4587
|
}
|
|
3830
4588
|
|
|
4589
|
+
// src/adapters/omp-acp/OmpACPAdapter.ts
|
|
4590
|
+
var DEFAULT_OMP_ACP_COMMAND = ["omp", "acp"];
|
|
4591
|
+
var OmpACPAdapter = class extends ACPClientAdapter {
|
|
4592
|
+
provider = "omp-acp";
|
|
4593
|
+
constructor(options = {}) {
|
|
4594
|
+
const { command, ...rest } = options;
|
|
4595
|
+
super({
|
|
4596
|
+
...rest,
|
|
4597
|
+
command: command ?? [...DEFAULT_OMP_ACP_COMMAND]
|
|
4598
|
+
});
|
|
4599
|
+
}
|
|
4600
|
+
};
|
|
4601
|
+
|
|
4602
|
+
// src/adapters/copilot-acp/CopilotACPAdapter.ts
|
|
4603
|
+
var DEFAULT_COPILOT_ACP_COMMAND = ["copilot", "--acp", "--stdio"];
|
|
4604
|
+
function isTcpOptions(options) {
|
|
4605
|
+
return "host" in options || "port" in options;
|
|
4606
|
+
}
|
|
4607
|
+
var CopilotACPAdapter = class extends ACPClientAdapter {
|
|
4608
|
+
provider = "copilot-acp";
|
|
4609
|
+
constructor(options = {}) {
|
|
4610
|
+
if (isTcpOptions(options)) {
|
|
4611
|
+
if (options.env) {
|
|
4612
|
+
resolveLogger(options.logger).warn(
|
|
4613
|
+
"CopilotACPAdapter ignores env for a TCP connection because the remote server owns its environment"
|
|
4614
|
+
);
|
|
4615
|
+
}
|
|
4616
|
+
super({ ...options, env: void 0 });
|
|
4617
|
+
return;
|
|
4618
|
+
}
|
|
4619
|
+
super({
|
|
4620
|
+
...options,
|
|
4621
|
+
command: options.command ?? [...DEFAULT_COPILOT_ACP_COMMAND]
|
|
4622
|
+
});
|
|
4623
|
+
}
|
|
4624
|
+
};
|
|
4625
|
+
|
|
3831
4626
|
// src/adapters/tool-calling/ToolCallingAdapter.ts
|
|
3832
4627
|
init_protocols();
|
|
3833
4628
|
|
|
3834
4629
|
// src/runtime/tools/customTools.ts
|
|
3835
4630
|
var import_zod3 = require("zod");
|
|
3836
|
-
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
3837
4631
|
var CustomToolDefinitionError = class extends Error {
|
|
3838
4632
|
constructor(message) {
|
|
3839
4633
|
super(message);
|
|
@@ -3892,7 +4686,11 @@ function customToolToAnthropicSchema(def) {
|
|
|
3892
4686
|
};
|
|
3893
4687
|
}
|
|
3894
4688
|
function toCleanJsonSchema(schema) {
|
|
3895
|
-
const jsonSchema =
|
|
4689
|
+
const jsonSchema = import_zod3.z.toJSONSchema(schema, {
|
|
4690
|
+
target: "draft-7",
|
|
4691
|
+
io: "input",
|
|
4692
|
+
unrepresentable: "any"
|
|
4693
|
+
});
|
|
3896
4694
|
delete jsonSchema["$schema"];
|
|
3897
4695
|
delete jsonSchema["additionalProperties"];
|
|
3898
4696
|
return jsonSchema;
|
|
@@ -3936,14 +4734,16 @@ async function executeCustomTool(def, arguments_) {
|
|
|
3936
4734
|
}
|
|
3937
4735
|
}
|
|
3938
4736
|
function formatZodIssue(issue) {
|
|
3939
|
-
const path = issue.path.length > 0 ? issue.path.join(".") : "value";
|
|
4737
|
+
const path = issue.path.length > 0 ? issue.path.map(String).join(".") : "value";
|
|
3940
4738
|
return `${path}: ${issue.message}`;
|
|
3941
4739
|
}
|
|
3942
4740
|
|
|
3943
4741
|
// src/adapters/tool-calling/ToolCallingAdapter.ts
|
|
4742
|
+
var DEFAULT_PROVIDER = "tool-calling";
|
|
3944
4743
|
var ToolCallingAdapter = class extends SimpleAdapter {
|
|
3945
4744
|
model;
|
|
3946
4745
|
toolFormat;
|
|
4746
|
+
provider;
|
|
3947
4747
|
systemPrompt;
|
|
3948
4748
|
includeMemoryTools;
|
|
3949
4749
|
maxToolRounds;
|
|
@@ -3955,123 +4755,125 @@ var ToolCallingAdapter = class extends SimpleAdapter {
|
|
|
3955
4755
|
super();
|
|
3956
4756
|
this.model = options.model;
|
|
3957
4757
|
this.toolFormat = options.toolFormat;
|
|
4758
|
+
this.provider = options.provider ?? DEFAULT_PROVIDER;
|
|
3958
4759
|
this.systemPrompt = options.systemPrompt;
|
|
3959
4760
|
this.includeMemoryTools = options.includeMemoryTools ?? false;
|
|
3960
4761
|
this.maxToolRounds = options.maxToolRounds ?? 8;
|
|
3961
4762
|
this.enableExecutionReporting = options.enableExecutionReporting ?? false;
|
|
3962
4763
|
this.customTools = options.customTools ?? [];
|
|
3963
4764
|
this.customToolIndex = buildCustomToolIndex(this.customTools);
|
|
3964
|
-
this.logger = options.logger
|
|
4765
|
+
this.logger = resolveLogger(options.logger);
|
|
3965
4766
|
}
|
|
3966
4767
|
async onMessage(message, tools, history, participantsMessage, contactsMessage, _context) {
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
roundCount
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
maxRoundsError.message,
|
|
3988
|
-
"error"
|
|
3989
|
-
);
|
|
3990
|
-
throw maxRoundsError;
|
|
3991
|
-
}
|
|
3992
|
-
const roundToolCalls = response.toolCalls ?? [];
|
|
3993
|
-
const roundToolResults = [];
|
|
3994
|
-
for (const call of roundToolCalls) {
|
|
3995
|
-
if (this.enableExecutionReporting) {
|
|
3996
|
-
await this.reportExecutionEvent(
|
|
3997
|
-
tools,
|
|
3998
|
-
{
|
|
3999
|
-
name: call.name,
|
|
4000
|
-
args: call.input,
|
|
4001
|
-
tool_call_id: call.id
|
|
4002
|
-
},
|
|
4003
|
-
"tool_call"
|
|
4768
|
+
let text;
|
|
4769
|
+
try {
|
|
4770
|
+
const platformSchemas = tools.getToolSchemas(this.toolFormat, {
|
|
4771
|
+
includeMemory: this.includeMemoryTools
|
|
4772
|
+
});
|
|
4773
|
+
const customSchemas = customToolsToSchemas(this.customTools, this.toolFormat);
|
|
4774
|
+
const schemas = [...platformSchemas, ...customSchemas];
|
|
4775
|
+
const messages = this.buildMessages(history, message, participantsMessage, contactsMessage);
|
|
4776
|
+
const toolRounds = [];
|
|
4777
|
+
let response = await this.model.complete({
|
|
4778
|
+
systemPrompt: this.systemPrompt,
|
|
4779
|
+
messages,
|
|
4780
|
+
tools: schemas
|
|
4781
|
+
});
|
|
4782
|
+
let roundCount = 0;
|
|
4783
|
+
while ((response.toolCalls?.length ?? 0) > 0) {
|
|
4784
|
+
roundCount += 1;
|
|
4785
|
+
if (roundCount > this.maxToolRounds) {
|
|
4786
|
+
throw new Error(
|
|
4787
|
+
`Stopped tool loop after ${this.maxToolRounds} rounds to prevent infinite recursion.`
|
|
4004
4788
|
);
|
|
4005
4789
|
}
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4790
|
+
const roundToolCalls = response.toolCalls ?? [];
|
|
4791
|
+
const roundToolResults = [];
|
|
4792
|
+
for (const call of roundToolCalls) {
|
|
4793
|
+
if (this.enableExecutionReporting) {
|
|
4794
|
+
await this.reportExecutionEvent(
|
|
4795
|
+
tools,
|
|
4796
|
+
{
|
|
4797
|
+
name: call.name,
|
|
4798
|
+
args: call.input,
|
|
4799
|
+
tool_call_id: call.id
|
|
4800
|
+
},
|
|
4801
|
+
"tool_call"
|
|
4802
|
+
);
|
|
4803
|
+
}
|
|
4804
|
+
let output;
|
|
4805
|
+
if (call.inputParseError) {
|
|
4806
|
+
output = {
|
|
4807
|
+
ok: false,
|
|
4808
|
+
errorType: "ToolCallArgumentsParseError",
|
|
4809
|
+
message: call.inputParseError,
|
|
4810
|
+
toolName: call.name,
|
|
4811
|
+
toolCallId: call.id
|
|
4812
|
+
};
|
|
4813
|
+
} else {
|
|
4814
|
+
const customTool = findCustomToolInIndex(this.customToolIndex, call.name);
|
|
4815
|
+
if (customTool) {
|
|
4816
|
+
try {
|
|
4817
|
+
output = await executeCustomTool(customTool, call.input);
|
|
4818
|
+
} catch (error) {
|
|
4819
|
+
if (error instanceof CustomToolValidationError || error instanceof CustomToolExecutionError) {
|
|
4820
|
+
output = {
|
|
4821
|
+
ok: false,
|
|
4822
|
+
errorType: error.name,
|
|
4823
|
+
message: error.message,
|
|
4824
|
+
toolName: error.toolName
|
|
4825
|
+
};
|
|
4826
|
+
} else {
|
|
4827
|
+
output = {
|
|
4828
|
+
ok: false,
|
|
4829
|
+
errorType: "CustomToolUnknownError",
|
|
4830
|
+
message: asErrorMessage(error),
|
|
4831
|
+
toolName: call.name
|
|
4832
|
+
};
|
|
4833
|
+
}
|
|
4035
4834
|
}
|
|
4835
|
+
} else {
|
|
4836
|
+
output = await tools.executeToolCall(call.name, call.input);
|
|
4036
4837
|
}
|
|
4037
|
-
}
|
|
4038
|
-
|
|
4838
|
+
}
|
|
4839
|
+
const isError = isToolOutputError(output);
|
|
4840
|
+
roundToolResults.push({
|
|
4841
|
+
toolCallId: call.id,
|
|
4842
|
+
name: call.name,
|
|
4843
|
+
output,
|
|
4844
|
+
isError
|
|
4845
|
+
});
|
|
4846
|
+
if (this.enableExecutionReporting) {
|
|
4847
|
+
await this.reportExecutionEvent(
|
|
4848
|
+
tools,
|
|
4849
|
+
{
|
|
4850
|
+
name: call.name,
|
|
4851
|
+
output,
|
|
4852
|
+
tool_call_id: call.id
|
|
4853
|
+
},
|
|
4854
|
+
"tool_result"
|
|
4855
|
+
);
|
|
4039
4856
|
}
|
|
4040
4857
|
}
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4858
|
+
toolRounds.push({ toolCalls: roundToolCalls, toolResults: roundToolResults });
|
|
4859
|
+
response = await this.model.complete({
|
|
4860
|
+
systemPrompt: this.systemPrompt,
|
|
4861
|
+
messages,
|
|
4862
|
+
tools: schemas,
|
|
4863
|
+
toolRounds
|
|
4047
4864
|
});
|
|
4048
|
-
if (this.enableExecutionReporting) {
|
|
4049
|
-
await this.reportExecutionEvent(
|
|
4050
|
-
tools,
|
|
4051
|
-
{
|
|
4052
|
-
name: call.name,
|
|
4053
|
-
output,
|
|
4054
|
-
tool_call_id: call.id
|
|
4055
|
-
},
|
|
4056
|
-
"tool_result"
|
|
4057
|
-
);
|
|
4058
|
-
}
|
|
4059
4865
|
}
|
|
4060
|
-
|
|
4061
|
-
response
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4866
|
+
text = response.text?.trim();
|
|
4867
|
+
if (!text && (response.toolCalls?.length ?? 0) === 0) {
|
|
4868
|
+
this.logger.warn("Model returned empty response with no tool calls", {
|
|
4869
|
+
messageId: message.id
|
|
4870
|
+
});
|
|
4871
|
+
}
|
|
4872
|
+
} catch (error) {
|
|
4873
|
+
await reportProviderTurnFailure(tools, this.logger, this.provider, "Tool-calling adapter request failed", error, { messageId: message.id });
|
|
4067
4874
|
}
|
|
4068
|
-
const text = response.text?.trim();
|
|
4069
4875
|
if (text) {
|
|
4070
|
-
await tools
|
|
4071
|
-
} else if ((response.toolCalls?.length ?? 0) === 0) {
|
|
4072
|
-
this.logger.warn("Model returned empty response with no tool calls", {
|
|
4073
|
-
messageId: message.id
|
|
4074
|
-
});
|
|
4876
|
+
await deliverReply(tools, text, [{ id: message.senderId, handle: message.senderName ?? message.senderType }]);
|
|
4075
4877
|
}
|
|
4076
4878
|
}
|
|
4077
4879
|
buildMessages(history, message, participantsMessage, contactsMessage) {
|
|
@@ -4130,63 +4932,6 @@ function runSingleToolRound(model, request) {
|
|
|
4130
4932
|
// src/adapters/vercel-ai-sdk/model.ts
|
|
4131
4933
|
init_errors();
|
|
4132
4934
|
|
|
4133
|
-
// src/adapters/shared/coercion.ts
|
|
4134
|
-
init_protocols();
|
|
4135
|
-
function asOptionalRecord(value) {
|
|
4136
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4137
|
-
return void 0;
|
|
4138
|
-
}
|
|
4139
|
-
return value;
|
|
4140
|
-
}
|
|
4141
|
-
function asRecord(value, context = "value") {
|
|
4142
|
-
const record = asOptionalRecord(value);
|
|
4143
|
-
if (!record) {
|
|
4144
|
-
throw new TypeError(`Expected ${context} to be an object record.`);
|
|
4145
|
-
}
|
|
4146
|
-
return record;
|
|
4147
|
-
}
|
|
4148
|
-
function asNonEmptyString2(value) {
|
|
4149
|
-
if (typeof value !== "string") {
|
|
4150
|
-
return null;
|
|
4151
|
-
}
|
|
4152
|
-
const trimmed = value.trim();
|
|
4153
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
4154
|
-
}
|
|
4155
|
-
function toDisplayText(value) {
|
|
4156
|
-
if (typeof value === "string") {
|
|
4157
|
-
return value;
|
|
4158
|
-
}
|
|
4159
|
-
if (value == null) {
|
|
4160
|
-
return "";
|
|
4161
|
-
}
|
|
4162
|
-
try {
|
|
4163
|
-
return JSON.stringify(value);
|
|
4164
|
-
} catch {
|
|
4165
|
-
return String(value);
|
|
4166
|
-
}
|
|
4167
|
-
}
|
|
4168
|
-
function toWireString(value) {
|
|
4169
|
-
const legacyErrorMessage = toLegacyToolExecutorErrorMessage(value);
|
|
4170
|
-
if (legacyErrorMessage !== null) {
|
|
4171
|
-
return legacyErrorMessage;
|
|
4172
|
-
}
|
|
4173
|
-
if (value === void 0 || value === null) {
|
|
4174
|
-
return "";
|
|
4175
|
-
}
|
|
4176
|
-
try {
|
|
4177
|
-
const json = JSON.stringify(value);
|
|
4178
|
-
return typeof json === "string" ? json : String(value);
|
|
4179
|
-
} catch {
|
|
4180
|
-
return String(value);
|
|
4181
|
-
}
|
|
4182
|
-
}
|
|
4183
|
-
function asErrorMessage(error) {
|
|
4184
|
-
if (error instanceof Error) {
|
|
4185
|
-
return error.message;
|
|
4186
|
-
}
|
|
4187
|
-
return String(error);
|
|
4188
|
-
}
|
|
4189
|
-
|
|
4190
4935
|
// src/adapters/tool-calling/valueUtils.ts
|
|
4191
4936
|
function normalizeConversationRole(value) {
|
|
4192
4937
|
if (value === "system" || value === "user" || value === "assistant") {
|
|
@@ -4284,7 +5029,7 @@ function toVercelAISDKMessage(entry) {
|
|
|
4284
5029
|
}
|
|
4285
5030
|
return {
|
|
4286
5031
|
role,
|
|
4287
|
-
content:
|
|
5032
|
+
content: toDisplayText2(entry.content)
|
|
4288
5033
|
};
|
|
4289
5034
|
}
|
|
4290
5035
|
function toVercelAISDKTools(schemas, toolFactory) {
|
|
@@ -4376,7 +5121,8 @@ var VercelAISDKAdapter = class extends ToolCallingAdapter {
|
|
|
4376
5121
|
generateText,
|
|
4377
5122
|
toolFactory
|
|
4378
5123
|
}),
|
|
4379
|
-
toolFormat: "openai"
|
|
5124
|
+
toolFormat: "openai",
|
|
5125
|
+
provider: "vercel-ai-sdk"
|
|
4380
5126
|
});
|
|
4381
5127
|
}
|
|
4382
5128
|
};
|
|
@@ -4459,7 +5205,7 @@ function toBaseOpenAIMessage(entry) {
|
|
|
4459
5205
|
}
|
|
4460
5206
|
return {
|
|
4461
5207
|
role,
|
|
4462
|
-
content:
|
|
5208
|
+
content: toDisplayText2(entry.content)
|
|
4463
5209
|
};
|
|
4464
5210
|
}
|
|
4465
5211
|
function parseOpenAIResponse(response) {
|
|
@@ -4585,7 +5331,8 @@ var OpenAIAdapter = class extends ToolCallingAdapter {
|
|
|
4585
5331
|
super({
|
|
4586
5332
|
...adapterOptions,
|
|
4587
5333
|
model: resolvedModel,
|
|
4588
|
-
toolFormat: "openai"
|
|
5334
|
+
toolFormat: "openai",
|
|
5335
|
+
provider: "openai"
|
|
4589
5336
|
});
|
|
4590
5337
|
}
|
|
4591
5338
|
};
|
|
@@ -4664,12 +5411,12 @@ function toAnthropicMessageWithSystemAsUser(entry) {
|
|
|
4664
5411
|
if (role === "system") {
|
|
4665
5412
|
return {
|
|
4666
5413
|
role: "user",
|
|
4667
|
-
content: `[System]: ${
|
|
5414
|
+
content: `[System]: ${toDisplayText2(entry.content)}`
|
|
4668
5415
|
};
|
|
4669
5416
|
}
|
|
4670
5417
|
return {
|
|
4671
5418
|
role,
|
|
4672
|
-
content:
|
|
5419
|
+
content: toDisplayText2(entry.content)
|
|
4673
5420
|
};
|
|
4674
5421
|
}
|
|
4675
5422
|
function parseAnthropicResponse(response) {
|
|
@@ -4742,7 +5489,8 @@ var AnthropicAdapter = class extends ToolCallingAdapter {
|
|
|
4742
5489
|
super({
|
|
4743
5490
|
...adapterOptions,
|
|
4744
5491
|
model: resolvedModel,
|
|
4745
|
-
toolFormat: "anthropic"
|
|
5492
|
+
toolFormat: "anthropic",
|
|
5493
|
+
provider: "anthropic"
|
|
4746
5494
|
});
|
|
4747
5495
|
}
|
|
4748
5496
|
};
|
|
@@ -4862,7 +5610,7 @@ var GeminiToolCallingModel = class {
|
|
|
4862
5610
|
if (!role) {
|
|
4863
5611
|
return null;
|
|
4864
5612
|
}
|
|
4865
|
-
const content =
|
|
5613
|
+
const content = toDisplayText2(entry.content);
|
|
4866
5614
|
if (role === "assistant") {
|
|
4867
5615
|
return { role: "model", parts: [{ text: content }] };
|
|
4868
5616
|
}
|
|
@@ -4963,7 +5711,8 @@ var GeminiAdapter = class extends ToolCallingAdapter {
|
|
|
4963
5711
|
...adapterOptions,
|
|
4964
5712
|
model: resolvedModel,
|
|
4965
5713
|
// Gemini's OpenAI-compatible endpoint accepts OpenAI-format tool schemas.
|
|
4966
|
-
toolFormat: "openai"
|
|
5714
|
+
toolFormat: "openai",
|
|
5715
|
+
provider: "gemini"
|
|
4967
5716
|
});
|
|
4968
5717
|
}
|
|
4969
5718
|
};
|
|
@@ -4979,12 +5728,82 @@ function findLatestTaskMetadata(raw, predicate) {
|
|
|
4979
5728
|
if (messageType !== "task") {
|
|
4980
5729
|
continue;
|
|
4981
5730
|
}
|
|
4982
|
-
const metadata =
|
|
4983
|
-
if (predicate(metadata)) {
|
|
4984
|
-
return metadata;
|
|
5731
|
+
const metadata = asOptionalRecord2(message.metadata) ?? {};
|
|
5732
|
+
if (predicate(metadata)) {
|
|
5733
|
+
return metadata;
|
|
5734
|
+
}
|
|
5735
|
+
}
|
|
5736
|
+
return null;
|
|
5737
|
+
}
|
|
5738
|
+
var UNKNOWN_SENDER = "Unknown";
|
|
5739
|
+
function mergeConsecutiveSameRole2(turns) {
|
|
5740
|
+
const merged = [];
|
|
5741
|
+
let attributedTo = "";
|
|
5742
|
+
for (const turn of turns) {
|
|
5743
|
+
if (!turn.content) {
|
|
5744
|
+
continue;
|
|
5745
|
+
}
|
|
5746
|
+
const previous = merged[merged.length - 1];
|
|
5747
|
+
if (!previous || previous.role !== turn.role) {
|
|
5748
|
+
merged.push({ ...turn });
|
|
5749
|
+
attributedTo = turn.sender ?? "";
|
|
5750
|
+
continue;
|
|
5751
|
+
}
|
|
5752
|
+
const speaker = turn.sender ?? "";
|
|
5753
|
+
previous.content += speaker === attributedTo ? `
|
|
5754
|
+
${turn.content}` : `
|
|
5755
|
+
${nameContribution(turn.content, speaker)}`;
|
|
5756
|
+
attributedTo = speaker;
|
|
5757
|
+
}
|
|
5758
|
+
return merged;
|
|
5759
|
+
}
|
|
5760
|
+
function nameContribution(content, speaker) {
|
|
5761
|
+
if (speaker !== "" && content.startsWith(`[${speaker}]: `)) {
|
|
5762
|
+
return content;
|
|
5763
|
+
}
|
|
5764
|
+
return `[${speaker || UNKNOWN_SENDER}]: ${content}`;
|
|
5765
|
+
}
|
|
5766
|
+
function pairUserAssistantTurns(turns) {
|
|
5767
|
+
const paired = [];
|
|
5768
|
+
let index = 0;
|
|
5769
|
+
while (index < turns.length) {
|
|
5770
|
+
const current = turns[index];
|
|
5771
|
+
if (current.role !== "user" || !current.content) {
|
|
5772
|
+
index += 1;
|
|
5773
|
+
continue;
|
|
5774
|
+
}
|
|
5775
|
+
const next = turns[index + 1];
|
|
5776
|
+
if (next && next.role === "assistant" && next.content) {
|
|
5777
|
+
paired.push(current, next);
|
|
5778
|
+
index += 2;
|
|
5779
|
+
continue;
|
|
5780
|
+
}
|
|
5781
|
+
if (index === turns.length - 1) {
|
|
5782
|
+
paired.push(current);
|
|
4985
5783
|
}
|
|
5784
|
+
index += 1;
|
|
4986
5785
|
}
|
|
4987
|
-
return
|
|
5786
|
+
return paired;
|
|
5787
|
+
}
|
|
5788
|
+
function takeRecentTurns(turns, limit) {
|
|
5789
|
+
const cap = Number.isNaN(limit) ? 0 : Math.floor(limit);
|
|
5790
|
+
if (cap <= 0) {
|
|
5791
|
+
return [];
|
|
5792
|
+
}
|
|
5793
|
+
if (turns.length <= cap) {
|
|
5794
|
+
return [...turns];
|
|
5795
|
+
}
|
|
5796
|
+
const truncated = turns.slice(-cap);
|
|
5797
|
+
if (truncated[0]?.role === "assistant") {
|
|
5798
|
+
truncated.shift();
|
|
5799
|
+
}
|
|
5800
|
+
return truncated;
|
|
5801
|
+
}
|
|
5802
|
+
function selectCompleteExchanges(history, limit) {
|
|
5803
|
+
return takeRecentTurns(
|
|
5804
|
+
pairUserAssistantTurns(mergeConsecutiveSameRole2(history)),
|
|
5805
|
+
limit
|
|
5806
|
+
);
|
|
4988
5807
|
}
|
|
4989
5808
|
|
|
4990
5809
|
// src/converters/google-adk.ts
|
|
@@ -5147,7 +5966,7 @@ function stripAdditionalProperties(value) {
|
|
|
5147
5966
|
return next;
|
|
5148
5967
|
}
|
|
5149
5968
|
function asToolArgs(value) {
|
|
5150
|
-
return
|
|
5969
|
+
return asOptionalRecord2(value) ?? {};
|
|
5151
5970
|
}
|
|
5152
5971
|
function stringifyToolResult(result) {
|
|
5153
5972
|
if (typeof result === "string") {
|
|
@@ -5186,6 +6005,7 @@ async function loadGoogleAdkSdk() {
|
|
|
5186
6005
|
};
|
|
5187
6006
|
}
|
|
5188
6007
|
var GoogleADKAdapter = class extends SimpleAdapter {
|
|
6008
|
+
provider = "google-adk";
|
|
5189
6009
|
model;
|
|
5190
6010
|
systemPromptOverride;
|
|
5191
6011
|
customSection;
|
|
@@ -5211,7 +6031,7 @@ var GoogleADKAdapter = class extends SimpleAdapter {
|
|
|
5211
6031
|
this.customTools = [...options.additionalTools ?? []];
|
|
5212
6032
|
this.maxHistoryMessages = options.maxHistoryMessages ?? DEFAULT_MAX_HISTORY_MESSAGES;
|
|
5213
6033
|
this.maxTranscriptChars = options.maxTranscriptChars ?? DEFAULT_MAX_TRANSCRIPT_CHARS;
|
|
5214
|
-
this.logger = options.logger
|
|
6034
|
+
this.logger = resolveLogger(options.logger);
|
|
5215
6035
|
this.historyConverterInstance = historyConverter;
|
|
5216
6036
|
this.sdkLoader = new LazyAsyncValue({
|
|
5217
6037
|
load: async () => options.sdkFactory ? options.sdkFactory() : loadGoogleAdkSdk(),
|
|
@@ -5231,23 +6051,11 @@ var GoogleADKAdapter = class extends SimpleAdapter {
|
|
|
5231
6051
|
});
|
|
5232
6052
|
}
|
|
5233
6053
|
async onMessage(message, tools, history, participantsMessage, contactsMessage, context) {
|
|
5234
|
-
const sdk = await this.sdkLoader.get();
|
|
5235
6054
|
if (context.isSessionBootstrap) {
|
|
5236
6055
|
this.roomHistory.set(context.roomId, [...history]);
|
|
5237
6056
|
} else if (!this.roomHistory.has(context.roomId)) {
|
|
5238
6057
|
this.roomHistory.set(context.roomId, []);
|
|
5239
6058
|
}
|
|
5240
|
-
const runner = sdk.createRunner({
|
|
5241
|
-
agent: this.buildAgent(sdk, tools),
|
|
5242
|
-
appName: APP_NAME
|
|
5243
|
-
});
|
|
5244
|
-
const sessionId = (0, import_node_crypto3.randomUUID)();
|
|
5245
|
-
this.roomSessions.set(context.roomId, sessionId);
|
|
5246
|
-
await runner.sessionService.createSession({
|
|
5247
|
-
appName: APP_NAME,
|
|
5248
|
-
userId: context.roomId,
|
|
5249
|
-
sessionId
|
|
5250
|
-
});
|
|
5251
6059
|
const prompt = this.buildPrompt(
|
|
5252
6060
|
message,
|
|
5253
6061
|
participantsMessage,
|
|
@@ -5256,6 +6064,18 @@ var GoogleADKAdapter = class extends SimpleAdapter {
|
|
|
5256
6064
|
);
|
|
5257
6065
|
let finalResponseText = "";
|
|
5258
6066
|
try {
|
|
6067
|
+
const sdk = await this.sdkLoader.get();
|
|
6068
|
+
const runner = sdk.createRunner({
|
|
6069
|
+
agent: this.buildAgent(sdk, tools),
|
|
6070
|
+
appName: APP_NAME
|
|
6071
|
+
});
|
|
6072
|
+
const sessionId = (0, import_node_crypto3.randomUUID)();
|
|
6073
|
+
this.roomSessions.set(context.roomId, sessionId);
|
|
6074
|
+
await runner.sessionService.createSession({
|
|
6075
|
+
appName: APP_NAME,
|
|
6076
|
+
userId: context.roomId,
|
|
6077
|
+
sessionId
|
|
6078
|
+
});
|
|
5259
6079
|
for await (const event of runner.runAsync({
|
|
5260
6080
|
userId: context.roomId,
|
|
5261
6081
|
sessionId,
|
|
@@ -5272,13 +6092,7 @@ var GoogleADKAdapter = class extends SimpleAdapter {
|
|
|
5272
6092
|
}
|
|
5273
6093
|
}
|
|
5274
6094
|
} catch (error) {
|
|
5275
|
-
|
|
5276
|
-
this.logger.error("Google ADK adapter request failed", {
|
|
5277
|
-
error,
|
|
5278
|
-
roomId: context.roomId
|
|
5279
|
-
});
|
|
5280
|
-
await tools.sendEvent(`Google ADK adapter error: ${messageText}`, "error");
|
|
5281
|
-
throw error instanceof Error ? error : new Error(messageText);
|
|
6095
|
+
await reportProviderTurnFailure(tools, this.logger, this.provider, "Google ADK adapter request failed", error, { roomId: context.roomId });
|
|
5282
6096
|
}
|
|
5283
6097
|
const nextHistory = this.roomHistory.get(context.roomId) ?? [];
|
|
5284
6098
|
nextHistory.push({
|
|
@@ -5290,7 +6104,7 @@ var GoogleADKAdapter = class extends SimpleAdapter {
|
|
|
5290
6104
|
role: "model",
|
|
5291
6105
|
content: finalResponseText
|
|
5292
6106
|
});
|
|
5293
|
-
await tools
|
|
6107
|
+
await deliverReply(tools, finalResponseText, [{ id: message.senderId }]);
|
|
5294
6108
|
}
|
|
5295
6109
|
this.roomHistory.set(context.roomId, trimRoomHistory(nextHistory, this.maxHistoryMessages));
|
|
5296
6110
|
}
|
|
@@ -5317,7 +6131,7 @@ var GoogleADKAdapter = class extends SimpleAdapter {
|
|
|
5317
6131
|
return adkTools;
|
|
5318
6132
|
}
|
|
5319
6133
|
buildPlatformTool(sdk, tools, schema) {
|
|
5320
|
-
const functionDef =
|
|
6134
|
+
const functionDef = asOptionalRecord2(schema.function) ?? {};
|
|
5321
6135
|
const name = functionDef?.name;
|
|
5322
6136
|
if (typeof name !== "string" || name.length === 0) {
|
|
5323
6137
|
return null;
|
|
@@ -5325,17 +6139,17 @@ var GoogleADKAdapter = class extends SimpleAdapter {
|
|
|
5325
6139
|
return sdk.createFunctionTool({
|
|
5326
6140
|
name,
|
|
5327
6141
|
description: typeof functionDef.description === "string" ? functionDef.description : "",
|
|
5328
|
-
parameters:
|
|
6142
|
+
parameters: asOptionalRecord2(stripAdditionalProperties(functionDef.parameters)) ?? void 0,
|
|
5329
6143
|
execute: async (input) => stringifyToolResult(await tools.executeToolCall(name, asToolArgs(input)))
|
|
5330
6144
|
});
|
|
5331
6145
|
}
|
|
5332
6146
|
buildCustomTool(sdk, customTool) {
|
|
5333
6147
|
const schema = customToolToOpenAISchema(customTool);
|
|
5334
|
-
const functionDef =
|
|
6148
|
+
const functionDef = asOptionalRecord2(schema.function) ?? {};
|
|
5335
6149
|
return sdk.createFunctionTool({
|
|
5336
6150
|
name: String(functionDef.name ?? customTool.name),
|
|
5337
6151
|
description: typeof functionDef.description === "string" ? functionDef.description : "",
|
|
5338
|
-
parameters:
|
|
6152
|
+
parameters: asOptionalRecord2(stripAdditionalProperties(functionDef.parameters)) ?? void 0,
|
|
5339
6153
|
execute: async (input) => stringifyToolResult(await executeCustomTool(customTool, asToolArgs(input)))
|
|
5340
6154
|
});
|
|
5341
6155
|
}
|
|
@@ -5387,10 +6201,7 @@ var GoogleADKAdapter = class extends SimpleAdapter {
|
|
|
5387
6201
|
}
|
|
5388
6202
|
warnOnFailedSend(result, message, meta) {
|
|
5389
6203
|
if (result.ok === false) {
|
|
5390
|
-
|
|
5391
|
-
this.logger.warn(message, meta);
|
|
5392
|
-
} catch {
|
|
5393
|
-
}
|
|
6204
|
+
this.logger.warn(message, meta);
|
|
5394
6205
|
}
|
|
5395
6206
|
}
|
|
5396
6207
|
};
|
|
@@ -5433,6 +6244,7 @@ function trimRoomHistory(history, maxHistoryMessages) {
|
|
|
5433
6244
|
// src/adapters/langgraph/LangGraphAdapter.ts
|
|
5434
6245
|
init_errors();
|
|
5435
6246
|
var LangGraphAdapter = class extends SimpleAdapter {
|
|
6247
|
+
provider = "langgraph";
|
|
5436
6248
|
llm;
|
|
5437
6249
|
checkpointer;
|
|
5438
6250
|
graph;
|
|
@@ -5464,7 +6276,7 @@ var LangGraphAdapter = class extends SimpleAdapter {
|
|
|
5464
6276
|
this.maxHistoryMessages = options.maxHistoryMessages ?? 100;
|
|
5465
6277
|
this.emitExecutionEvents = options.emitExecutionEvents ?? true;
|
|
5466
6278
|
this.includeMemoryTools = options.includeMemoryTools ?? false;
|
|
5467
|
-
this.logger = options.logger
|
|
6279
|
+
this.logger = resolveLogger(options.logger);
|
|
5468
6280
|
this.sdkLoader = new LazyAsyncValue({
|
|
5469
6281
|
load: async () => loadLangGraphSdk(),
|
|
5470
6282
|
onRejected: (error) => {
|
|
@@ -5482,53 +6294,54 @@ var LangGraphAdapter = class extends SimpleAdapter {
|
|
|
5482
6294
|
});
|
|
5483
6295
|
}
|
|
5484
6296
|
async onMessage(message, tools, history, participantsMessage, contactsMessage, context) {
|
|
5485
|
-
let
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
const usesCheckpointer = Boolean(this.checkpointer);
|
|
5501
|
-
const isFirstBootstrap = context.isSessionBootstrap && !this.bootstrappedRooms.has(context.roomId);
|
|
5502
|
-
const replayHistory = !usesCheckpointer || isFirstBootstrap;
|
|
5503
|
-
const includeSystemPrompt = this.usesCustomGraph && (!usesCheckpointer || isFirstBootstrap);
|
|
5504
|
-
if (context.isSessionBootstrap) {
|
|
5505
|
-
this.bootstrappedRooms.add(context.roomId);
|
|
5506
|
-
}
|
|
5507
|
-
const messages = this.buildMessages(history, message, participantsMessage, contactsMessage, {
|
|
5508
|
-
replayHistory,
|
|
5509
|
-
includeSystemPrompt
|
|
5510
|
-
});
|
|
5511
|
-
const input = { messages };
|
|
5512
|
-
const graphConfig = {
|
|
5513
|
-
configurable: {
|
|
5514
|
-
thread_id: context.roomId
|
|
5515
|
-
},
|
|
5516
|
-
recursion_limit: this.recursionLimit
|
|
5517
|
-
};
|
|
5518
|
-
if (this.emitExecutionEvents && graph.streamEvents) {
|
|
5519
|
-
const text2 = await this.forwardStreamEvents(graph, input, graphConfig, tools);
|
|
5520
|
-
if (text2) {
|
|
5521
|
-
await tools.sendMessage(text2, [{ id: message.senderId, handle: message.senderName ?? message.senderType }]);
|
|
6297
|
+
let text = null;
|
|
6298
|
+
try {
|
|
6299
|
+
let sdk;
|
|
6300
|
+
let langGraphTools = [...this.additionalTools];
|
|
6301
|
+
if (!this.graph || this.graphFactory) {
|
|
6302
|
+
sdk = await this.sdkLoader.get();
|
|
6303
|
+
langGraphTools = [
|
|
6304
|
+
...buildLangGraphTools({
|
|
6305
|
+
sdk,
|
|
6306
|
+
tools,
|
|
6307
|
+
includeMemoryTools: this.includeMemoryTools,
|
|
6308
|
+
logger: this.logger
|
|
6309
|
+
}),
|
|
6310
|
+
...this.additionalTools
|
|
6311
|
+
];
|
|
5522
6312
|
}
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
6313
|
+
const graph = await this.resolveGraph(sdk, langGraphTools);
|
|
6314
|
+
const usesCheckpointer = Boolean(this.checkpointer);
|
|
6315
|
+
const isFirstBootstrap = context.isSessionBootstrap && !this.bootstrappedRooms.has(context.roomId);
|
|
6316
|
+
const replayHistory = !usesCheckpointer || isFirstBootstrap;
|
|
6317
|
+
const includeSystemPrompt = this.usesCustomGraph && (!usesCheckpointer || isFirstBootstrap);
|
|
6318
|
+
if (context.isSessionBootstrap) {
|
|
6319
|
+
this.bootstrappedRooms.add(context.roomId);
|
|
6320
|
+
}
|
|
6321
|
+
const messages = this.buildMessages(history, message, participantsMessage, contactsMessage, {
|
|
6322
|
+
replayHistory,
|
|
6323
|
+
includeSystemPrompt
|
|
6324
|
+
});
|
|
6325
|
+
const input = { messages };
|
|
6326
|
+
const graphConfig = {
|
|
6327
|
+
configurable: {
|
|
6328
|
+
thread_id: context.roomId
|
|
6329
|
+
},
|
|
6330
|
+
recursion_limit: this.recursionLimit
|
|
6331
|
+
};
|
|
6332
|
+
if (this.emitExecutionEvents && graph.streamEvents) {
|
|
6333
|
+
text = await this.forwardStreamEvents(graph, input, graphConfig, tools);
|
|
6334
|
+
} else if (!graph.invoke) {
|
|
6335
|
+
return;
|
|
6336
|
+
} else {
|
|
6337
|
+
const result = await graph.invoke(input, graphConfig);
|
|
6338
|
+
text = extractAssistantText(result);
|
|
6339
|
+
}
|
|
6340
|
+
} catch (error) {
|
|
6341
|
+
await reportProviderTurnFailure(tools, this.logger, this.provider, "LangGraph adapter request failed", error, { roomId: context.roomId });
|
|
5527
6342
|
}
|
|
5528
|
-
const result = await graph.invoke(input, graphConfig);
|
|
5529
|
-
const text = extractAssistantText(result);
|
|
5530
6343
|
if (text) {
|
|
5531
|
-
await tools
|
|
6344
|
+
await deliverReply(tools, text, [{ id: message.senderId, handle: message.senderName ?? message.senderType }]);
|
|
5532
6345
|
}
|
|
5533
6346
|
}
|
|
5534
6347
|
async onCleanup(roomId) {
|
|
@@ -5589,7 +6402,7 @@ var LangGraphAdapter = class extends SimpleAdapter {
|
|
|
5589
6402
|
}
|
|
5590
6403
|
let lastAssistantText = null;
|
|
5591
6404
|
for await (const event of stream) {
|
|
5592
|
-
const data =
|
|
6405
|
+
const data = asOptionalRecord2(event) ?? {};
|
|
5593
6406
|
const eventType = String(data.event ?? "");
|
|
5594
6407
|
if (eventType === "on_tool_start") {
|
|
5595
6408
|
await tools.sendEvent(
|
|
@@ -5614,7 +6427,7 @@ var LangGraphAdapter = class extends SimpleAdapter {
|
|
|
5614
6427
|
if (!this.usesCustomGraph && chainName !== "LangGraph" && chainName !== "agent") {
|
|
5615
6428
|
continue;
|
|
5616
6429
|
}
|
|
5617
|
-
const output =
|
|
6430
|
+
const output = asOptionalRecord2(data.data) ?? {};
|
|
5618
6431
|
const text = extractAssistantText(output.output);
|
|
5619
6432
|
if (text) {
|
|
5620
6433
|
lastAssistantText = text;
|
|
@@ -5679,7 +6492,7 @@ function extractAssistantText(result) {
|
|
|
5679
6492
|
const trimmed = result.trim();
|
|
5680
6493
|
return trimmed.length > 0 && !isInternalLangGraphMarker(trimmed) ? trimmed : null;
|
|
5681
6494
|
}
|
|
5682
|
-
const record =
|
|
6495
|
+
const record = asOptionalRecord2(result) ?? {};
|
|
5683
6496
|
if (isLangChainAssistantMessage(record)) {
|
|
5684
6497
|
const text = asLangChainMessageContent(record);
|
|
5685
6498
|
if (text && !isInternalLangGraphMarker(text)) {
|
|
@@ -5706,7 +6519,7 @@ function asMessageText(value) {
|
|
|
5706
6519
|
}
|
|
5707
6520
|
return null;
|
|
5708
6521
|
}
|
|
5709
|
-
const record =
|
|
6522
|
+
const record = asOptionalRecord2(value);
|
|
5710
6523
|
if (!record) {
|
|
5711
6524
|
return null;
|
|
5712
6525
|
}
|
|
@@ -5738,7 +6551,7 @@ function isLangChainAssistantMessage(record) {
|
|
|
5738
6551
|
return false;
|
|
5739
6552
|
}
|
|
5740
6553
|
function asLangChainMessageContent(record) {
|
|
5741
|
-
const kwargs =
|
|
6554
|
+
const kwargs = asOptionalRecord2(record.kwargs);
|
|
5742
6555
|
return asMessageContent(kwargs?.content ?? record.content);
|
|
5743
6556
|
}
|
|
5744
6557
|
function isInternalLangGraphMarker(text) {
|
|
@@ -5751,7 +6564,7 @@ function asMessageContent(value) {
|
|
|
5751
6564
|
if (Array.isArray(value)) {
|
|
5752
6565
|
const chunks = [];
|
|
5753
6566
|
for (const item of value) {
|
|
5754
|
-
const record =
|
|
6567
|
+
const record = asOptionalRecord2(item);
|
|
5755
6568
|
if (!record) {
|
|
5756
6569
|
continue;
|
|
5757
6570
|
}
|
|
@@ -5851,6 +6664,7 @@ var A2AHistoryConverter = class {
|
|
|
5851
6664
|
var TERMINAL_STATES = /* @__PURE__ */ new Set(["completed", "failed", "canceled", "rejected", "auth-required"]);
|
|
5852
6665
|
var DEFAULT_MAX_STREAM_EVENTS = 1e4;
|
|
5853
6666
|
var A2AAdapter = class extends SimpleAdapter {
|
|
6667
|
+
provider = "a2a";
|
|
5854
6668
|
remoteUrl;
|
|
5855
6669
|
authHeaders;
|
|
5856
6670
|
streaming;
|
|
@@ -5870,20 +6684,20 @@ var A2AAdapter = class extends SimpleAdapter {
|
|
|
5870
6684
|
this.streaming = options.streaming ?? true;
|
|
5871
6685
|
this.clientFactory = options.clientFactory;
|
|
5872
6686
|
this.maxStreamEvents = normalizeMaxStreamEvents(options.maxStreamEvents);
|
|
5873
|
-
this.logger = options.logger
|
|
6687
|
+
this.logger = resolveLogger(options.logger);
|
|
5874
6688
|
}
|
|
5875
6689
|
async onStarted(agentName, agentDescription) {
|
|
5876
6690
|
await super.onStarted(agentName, agentDescription);
|
|
5877
6691
|
this.client = await this.createClient();
|
|
5878
6692
|
}
|
|
5879
6693
|
async onMessage(message, tools, history, _participantsMessage, _contactsMessage, context) {
|
|
5880
|
-
const client = this.client ?? await this.createClient();
|
|
5881
|
-
this.client = client;
|
|
5882
|
-
if (context.isSessionBootstrap) {
|
|
5883
|
-
await this.rehydrateFromHistory(context.roomId, history, client);
|
|
5884
|
-
}
|
|
5885
|
-
const request = this.toSendParams(message, context.roomId);
|
|
5886
6694
|
try {
|
|
6695
|
+
const client = this.client ?? await this.createClient();
|
|
6696
|
+
this.client = client;
|
|
6697
|
+
if (context.isSessionBootstrap) {
|
|
6698
|
+
await this.rehydrateFromHistory(context.roomId, history, client);
|
|
6699
|
+
}
|
|
6700
|
+
const request = this.toSendParams(message, context.roomId);
|
|
5887
6701
|
if (this.streaming) {
|
|
5888
6702
|
let streamEventCount = 0;
|
|
5889
6703
|
for await (const event of client.sendMessageStream(request)) {
|
|
@@ -5894,6 +6708,7 @@ var A2AAdapter = class extends SimpleAdapter {
|
|
|
5894
6708
|
try {
|
|
5895
6709
|
await this.handleEvent(event, tools, context.roomId, message.senderId);
|
|
5896
6710
|
} catch (handleError) {
|
|
6711
|
+
rethrowIfRecoverableTurnFailure(handleError);
|
|
5897
6712
|
this.logger.error("A2A stream event handling failed; continuing stream", {
|
|
5898
6713
|
roomId: context.roomId,
|
|
5899
6714
|
remoteUrl: this.remoteUrl,
|
|
@@ -5907,24 +6722,11 @@ var A2AAdapter = class extends SimpleAdapter {
|
|
|
5907
6722
|
const response = await client.sendMessage(request);
|
|
5908
6723
|
await this.handleEvent(response, tools, context.roomId, message.senderId);
|
|
5909
6724
|
} catch (error) {
|
|
5910
|
-
|
|
5911
|
-
this.logger.
|
|
6725
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
6726
|
+
await reportProviderTurnFailure(tools, this.logger, this.provider, "A2A adapter request failed", error, {
|
|
5912
6727
|
roomId: context.roomId,
|
|
5913
|
-
remoteUrl: this.remoteUrl
|
|
5914
|
-
error
|
|
6728
|
+
remoteUrl: this.remoteUrl
|
|
5915
6729
|
});
|
|
5916
|
-
try {
|
|
5917
|
-
await tools.sendEvent(`A2A agent error: ${errorMessage}`, "error", {
|
|
5918
|
-
a2a_error: errorMessage
|
|
5919
|
-
});
|
|
5920
|
-
} catch (eventError) {
|
|
5921
|
-
this.logger.warn("A2A adapter failed to emit error event", {
|
|
5922
|
-
roomId: context.roomId,
|
|
5923
|
-
remoteUrl: this.remoteUrl,
|
|
5924
|
-
error: eventError
|
|
5925
|
-
});
|
|
5926
|
-
}
|
|
5927
|
-
throw error instanceof Error ? error : new Error(errorMessage);
|
|
5928
6730
|
}
|
|
5929
6731
|
}
|
|
5930
6732
|
async onCleanup(roomId) {
|
|
@@ -5944,7 +6746,7 @@ var A2AAdapter = class extends SimpleAdapter {
|
|
|
5944
6746
|
if (isMessageEvent(event)) {
|
|
5945
6747
|
const text = extractMessageText(event);
|
|
5946
6748
|
if (text) {
|
|
5947
|
-
await tools
|
|
6749
|
+
await deliverReply(tools, text, [{ id: senderId }]);
|
|
5948
6750
|
}
|
|
5949
6751
|
return;
|
|
5950
6752
|
}
|
|
@@ -5959,7 +6761,7 @@ var A2AAdapter = class extends SimpleAdapter {
|
|
|
5959
6761
|
if (isArtifactUpdateEvent(event)) {
|
|
5960
6762
|
const text = extractArtifactText(event.artifact);
|
|
5961
6763
|
if (text) {
|
|
5962
|
-
await tools
|
|
6764
|
+
await deliverReply(tools, text, [{ id: senderId }]);
|
|
5963
6765
|
}
|
|
5964
6766
|
}
|
|
5965
6767
|
}
|
|
@@ -6030,23 +6832,29 @@ var A2AAdapter = class extends SimpleAdapter {
|
|
|
6030
6832
|
}
|
|
6031
6833
|
if (input.state === "input-required") {
|
|
6032
6834
|
const text = extractMessageText(input.statusMessage) ?? "Please provide more information.";
|
|
6033
|
-
await input.tools
|
|
6835
|
+
await deliverReply(input.tools, text, [input.sender]);
|
|
6034
6836
|
await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
|
|
6035
6837
|
return;
|
|
6036
6838
|
}
|
|
6037
6839
|
if (input.state === "completed") {
|
|
6038
|
-
|
|
6039
|
-
|
|
6840
|
+
try {
|
|
6841
|
+
if (input.completedMessage) {
|
|
6842
|
+
await deliverReply(input.tools, input.completedMessage, [input.sender]);
|
|
6843
|
+
}
|
|
6844
|
+
await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
|
|
6845
|
+
} finally {
|
|
6846
|
+
this.clearTaskTracking(input.key, input.roomId);
|
|
6040
6847
|
}
|
|
6041
|
-
await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
|
|
6042
|
-
this.clearTaskTracking(input.key, input.roomId);
|
|
6043
6848
|
return;
|
|
6044
6849
|
}
|
|
6045
6850
|
if (TERMINAL_STATES.has(input.state)) {
|
|
6046
6851
|
const text = extractMessageText(input.statusMessage) ?? `A2A task ${input.state}`;
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6852
|
+
try {
|
|
6853
|
+
await this.emitTaskEvent(input.tools, input.contextId, input.taskId, input.state);
|
|
6854
|
+
} finally {
|
|
6855
|
+
this.clearTaskTracking(input.key, input.roomId);
|
|
6856
|
+
}
|
|
6857
|
+
await reportTurnFailure(input.tools, agentFailure(this.provider, text, input.state), this.logger, { roomId: input.roomId });
|
|
6050
6858
|
}
|
|
6051
6859
|
}
|
|
6052
6860
|
async emitTaskEvent(tools, contextId, taskId, state) {
|
|
@@ -6358,6 +7166,7 @@ function createAuthFetch(authHeaders) {
|
|
|
6358
7166
|
// src/adapters/a2a-gateway/A2AGatewayAdapter.ts
|
|
6359
7167
|
var import_node_crypto7 = require("crypto");
|
|
6360
7168
|
init_errors();
|
|
7169
|
+
init_protocols();
|
|
6361
7170
|
|
|
6362
7171
|
// src/adapters/a2a-gateway/history.ts
|
|
6363
7172
|
var GatewayHistoryConverter = class {
|
|
@@ -6365,7 +7174,7 @@ var GatewayHistoryConverter = class {
|
|
|
6365
7174
|
const contextToRoom = {};
|
|
6366
7175
|
const roomParticipants = /* @__PURE__ */ new Map();
|
|
6367
7176
|
for (const entry of raw) {
|
|
6368
|
-
const metadata =
|
|
7177
|
+
const metadata = asOptionalRecord2(entry.metadata) ?? {};
|
|
6369
7178
|
const contextId = asNonEmptyString2(metadata.gateway_context_id);
|
|
6370
7179
|
if (contextId) {
|
|
6371
7180
|
const roomId2 = asNonEmptyString2(metadata.gateway_room_id) ?? asNonEmptyString2(entry.room_id) ?? asNonEmptyString2(entry.roomId);
|
|
@@ -6426,15 +7235,58 @@ function buildStatusEvent(input) {
|
|
|
6426
7235
|
};
|
|
6427
7236
|
}
|
|
6428
7237
|
|
|
7238
|
+
// src/adapters/a2a-gateway/failure.ts
|
|
7239
|
+
var import_band_sdk_core3 = require("@band-ai/band-sdk-core");
|
|
7240
|
+
init_protocols();
|
|
7241
|
+
var PROVIDER = "a2a-gateway";
|
|
7242
|
+
var SENSITIVE_VALUE_PATTERN = new RegExp(
|
|
7243
|
+
`(${GATEWAY_FREE_TEXT_SENSITIVE_KEY_TERMS})"?\\s*(?:[A-Za-z]+\\s*)?[:=]\\s*"?(?:[A-Za-z][\\w-]*\\s+)?[^\\s,;"]+`,
|
|
7244
|
+
"gi"
|
|
7245
|
+
);
|
|
7246
|
+
function buildGatewayFailureMetadata(error, code, message = sanitizeGatewayErrorMessage(error)) {
|
|
7247
|
+
return {
|
|
7248
|
+
[FAILURE_METADATA_KEY]: new import_band_sdk_core3.AgentFailure(
|
|
7249
|
+
PROVIDER,
|
|
7250
|
+
message,
|
|
7251
|
+
code ?? (error instanceof Error ? error.name : "UnknownError")
|
|
7252
|
+
).toObject()
|
|
7253
|
+
};
|
|
7254
|
+
}
|
|
7255
|
+
function sanitizeGatewayErrorMessage(error) {
|
|
7256
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
7257
|
+
const trimmed = rawMessage.trim();
|
|
7258
|
+
if (!trimmed) {
|
|
7259
|
+
return "Unknown error";
|
|
7260
|
+
}
|
|
7261
|
+
const withBearerRedaction = trimmed.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [REDACTED]").replace(SENSITIVE_VALUE_PATTERN, "$1=[REDACTED]");
|
|
7262
|
+
const maxLength = 240;
|
|
7263
|
+
if (withBearerRedaction.length <= maxLength) {
|
|
7264
|
+
return withBearerRedaction;
|
|
7265
|
+
}
|
|
7266
|
+
return `${withBearerRedaction.slice(0, maxLength - 3)}...`;
|
|
7267
|
+
}
|
|
7268
|
+
function sanitizeForwardedFailure(value) {
|
|
7269
|
+
const record = asOptionalRecord2(value);
|
|
7270
|
+
if (!record) {
|
|
7271
|
+
return void 0;
|
|
7272
|
+
}
|
|
7273
|
+
const provider = asString(record.provider) ?? "unknown";
|
|
7274
|
+
const code = asString(record.code) ?? void 0;
|
|
7275
|
+
const message = sanitizeGatewayErrorMessage(asString(record.message) ?? "Unknown error");
|
|
7276
|
+
return new import_band_sdk_core3.AgentFailure(provider, message, code).toObject();
|
|
7277
|
+
}
|
|
7278
|
+
|
|
6429
7279
|
// src/adapters/a2a-gateway/server.ts
|
|
6430
7280
|
var GatewayPeerExecutor = class {
|
|
6431
7281
|
peer;
|
|
6432
7282
|
onRequest;
|
|
6433
7283
|
onCancel;
|
|
7284
|
+
logger;
|
|
6434
7285
|
constructor(options) {
|
|
6435
7286
|
this.peer = options.peer;
|
|
6436
7287
|
this.onRequest = options.onRequest;
|
|
6437
7288
|
this.onCancel = options.onCancel;
|
|
7289
|
+
this.logger = options.logger;
|
|
6438
7290
|
}
|
|
6439
7291
|
async execute(requestContext, eventBus) {
|
|
6440
7292
|
const taskId = asNonEmptyString2(requestContext.taskId) ?? (0, import_node_crypto6.randomUUID)();
|
|
@@ -6454,6 +7306,12 @@ var GatewayPeerExecutor = class {
|
|
|
6454
7306
|
}
|
|
6455
7307
|
}
|
|
6456
7308
|
} catch (error) {
|
|
7309
|
+
this.logger.warn("a2a_gateway.peer_request_failed", {
|
|
7310
|
+
peerId: this.peer.id,
|
|
7311
|
+
taskId,
|
|
7312
|
+
contextId,
|
|
7313
|
+
error
|
|
7314
|
+
});
|
|
6457
7315
|
eventBus.publish(
|
|
6458
7316
|
buildStatusEvent({
|
|
6459
7317
|
taskId,
|
|
@@ -6461,7 +7319,7 @@ var GatewayPeerExecutor = class {
|
|
|
6461
7319
|
state: "failed",
|
|
6462
7320
|
final: true,
|
|
6463
7321
|
text: "Peer request failed.",
|
|
6464
|
-
metadata:
|
|
7322
|
+
metadata: buildGatewayFailureMetadata(error)
|
|
6465
7323
|
})
|
|
6466
7324
|
);
|
|
6467
7325
|
} finally {
|
|
@@ -6489,10 +7347,12 @@ var GatewayPeerExecutor = class {
|
|
|
6489
7347
|
};
|
|
6490
7348
|
var GatewayServer = class {
|
|
6491
7349
|
options;
|
|
7350
|
+
logger;
|
|
6492
7351
|
server = null;
|
|
6493
7352
|
started = false;
|
|
6494
7353
|
constructor(options) {
|
|
6495
7354
|
this.options = options;
|
|
7355
|
+
this.logger = resolveLogger(options.logger);
|
|
6496
7356
|
}
|
|
6497
7357
|
async start() {
|
|
6498
7358
|
if (this.started) {
|
|
@@ -6562,7 +7422,8 @@ var GatewayServer = class {
|
|
|
6562
7422
|
new GatewayPeerExecutor({
|
|
6563
7423
|
peer,
|
|
6564
7424
|
onRequest: this.options.onRequest,
|
|
6565
|
-
onCancel: this.options.onCancel
|
|
7425
|
+
onCancel: this.options.onCancel,
|
|
7426
|
+
logger: this.logger
|
|
6566
7427
|
})
|
|
6567
7428
|
);
|
|
6568
7429
|
const userBuilder = createUserBuilder(
|
|
@@ -6819,25 +7680,6 @@ function verifyBearerAuthorization(headers, authToken) {
|
|
|
6819
7680
|
}
|
|
6820
7681
|
return safeHeaderEquals(authorization, `Bearer ${authToken}`);
|
|
6821
7682
|
}
|
|
6822
|
-
function buildGatewayExecutionFailureMetadata(error) {
|
|
6823
|
-
return {
|
|
6824
|
-
error_type: error instanceof Error ? error.name : "UnknownError",
|
|
6825
|
-
error_message: sanitizeGatewayErrorMessage(error)
|
|
6826
|
-
};
|
|
6827
|
-
}
|
|
6828
|
-
function sanitizeGatewayErrorMessage(error) {
|
|
6829
|
-
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
6830
|
-
const trimmed = rawMessage.trim();
|
|
6831
|
-
if (!trimmed) {
|
|
6832
|
-
return "Unknown error";
|
|
6833
|
-
}
|
|
6834
|
-
const withBearerRedaction = trimmed.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [REDACTED]").replace(/(token|authorization|api[_-]?key)\s*[:=]\s*[^\s,;]+/gi, "$1=[REDACTED]");
|
|
6835
|
-
const maxLength = 240;
|
|
6836
|
-
if (withBearerRedaction.length <= maxLength) {
|
|
6837
|
-
return withBearerRedaction;
|
|
6838
|
-
}
|
|
6839
|
-
return `${withBearerRedaction.slice(0, maxLength - 3)}...`;
|
|
6840
|
-
}
|
|
6841
7683
|
function safeHeaderEquals(left, right) {
|
|
6842
7684
|
const leftBuffer = Buffer.from(left);
|
|
6843
7685
|
const rightBuffer = Buffer.from(right);
|
|
@@ -6855,6 +7697,7 @@ var DEFAULT_RESPONSE_TIMEOUT_MS = 12e4;
|
|
|
6855
7697
|
var DEFAULT_PEER_PAGE_SIZE = 100;
|
|
6856
7698
|
var DEFAULT_MAX_PEER_PAGES = 25;
|
|
6857
7699
|
var A2AGatewayAdapter = class extends SimpleAdapter {
|
|
7700
|
+
provider = PROVIDER;
|
|
6858
7701
|
bandRest;
|
|
6859
7702
|
gatewayUrl;
|
|
6860
7703
|
host;
|
|
@@ -6865,6 +7708,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
|
|
|
6865
7708
|
peerPageSize;
|
|
6866
7709
|
maxPeerPages;
|
|
6867
7710
|
serverFactory;
|
|
7711
|
+
logger;
|
|
6868
7712
|
peersBySlug = /* @__PURE__ */ new Map();
|
|
6869
7713
|
peersById = /* @__PURE__ */ new Map();
|
|
6870
7714
|
contextToRoom = /* @__PURE__ */ new Map();
|
|
@@ -6886,6 +7730,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
|
|
|
6886
7730
|
this.peerPageSize = options.peerPageSize ?? DEFAULT_PEER_PAGE_SIZE;
|
|
6887
7731
|
this.maxPeerPages = options.maxPeerPages ?? DEFAULT_MAX_PEER_PAGES;
|
|
6888
7732
|
this.serverFactory = options.serverFactory ?? createGatewayServer;
|
|
7733
|
+
this.logger = resolveLogger(options.logger);
|
|
6889
7734
|
if (this.allowUnauthenticatedLoopback && !isLoopbackHost2(this.host)) {
|
|
6890
7735
|
throw new ValidationError(
|
|
6891
7736
|
"A2AGatewayAdapter `allowUnauthenticatedLoopback` may only be enabled for loopback hosts."
|
|
@@ -6907,7 +7752,8 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
|
|
|
6907
7752
|
onCancel: async (request) => {
|
|
6908
7753
|
const canonicalPeerId = this.resolveCanonicalPeerId(request);
|
|
6909
7754
|
this.cancelPendingTask(request.taskId, canonicalPeerId ?? request.peerId);
|
|
6910
|
-
}
|
|
7755
|
+
},
|
|
7756
|
+
logger: this.logger
|
|
6911
7757
|
});
|
|
6912
7758
|
await this.server.start();
|
|
6913
7759
|
}
|
|
@@ -6989,12 +7835,15 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
|
|
|
6989
7835
|
async *handleGatewayRequest(request) {
|
|
6990
7836
|
const peer = this.resolveGatewayPeer(request);
|
|
6991
7837
|
if (!peer) {
|
|
7838
|
+
const text = `Peer not found: ${request.peerId}`;
|
|
7839
|
+
this.logger.warn("a2a_gateway.peer_not_found", { peerId: request.peerId, taskId: request.taskId });
|
|
6992
7840
|
yield buildStatusEvent({
|
|
6993
7841
|
taskId: request.taskId,
|
|
6994
7842
|
contextId: request.contextId,
|
|
6995
7843
|
state: "failed",
|
|
6996
7844
|
final: true,
|
|
6997
|
-
text
|
|
7845
|
+
text,
|
|
7846
|
+
metadata: buildGatewayFailureMetadata(text, "peer_not_found")
|
|
6998
7847
|
});
|
|
6999
7848
|
return;
|
|
7000
7849
|
}
|
|
@@ -7045,12 +7894,20 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
|
|
|
7045
7894
|
});
|
|
7046
7895
|
} catch (error) {
|
|
7047
7896
|
this.removePending(pending);
|
|
7897
|
+
const text = sanitizeGatewayErrorMessage(error);
|
|
7898
|
+
this.logger.warn("a2a_gateway.relay_failed", {
|
|
7899
|
+
roomId,
|
|
7900
|
+
peerId: peer.id,
|
|
7901
|
+
taskId: pending.taskId,
|
|
7902
|
+
error
|
|
7903
|
+
});
|
|
7048
7904
|
yield buildStatusEvent({
|
|
7049
7905
|
taskId: pending.taskId,
|
|
7050
7906
|
contextId: pending.contextId,
|
|
7051
7907
|
state: "failed",
|
|
7052
7908
|
final: true,
|
|
7053
|
-
text
|
|
7909
|
+
text,
|
|
7910
|
+
metadata: buildGatewayFailureMetadata(error, void 0, text)
|
|
7054
7911
|
});
|
|
7055
7912
|
return;
|
|
7056
7913
|
}
|
|
@@ -7058,12 +7915,20 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
|
|
|
7058
7915
|
const next = await pending.queue.dequeue(this.responseTimeoutMs);
|
|
7059
7916
|
if (!next) {
|
|
7060
7917
|
this.removePending(pending);
|
|
7918
|
+
const text = "Timed out waiting for a Band peer response.";
|
|
7919
|
+
this.logger.warn("a2a_gateway.peer_response_timeout", {
|
|
7920
|
+
roomId,
|
|
7921
|
+
peerId: peer.id,
|
|
7922
|
+
taskId: pending.taskId,
|
|
7923
|
+
responseTimeoutMs: this.responseTimeoutMs
|
|
7924
|
+
});
|
|
7061
7925
|
yield buildStatusEvent({
|
|
7062
7926
|
taskId: pending.taskId,
|
|
7063
7927
|
contextId: pending.contextId,
|
|
7064
7928
|
state: "failed",
|
|
7065
7929
|
final: true,
|
|
7066
|
-
text
|
|
7930
|
+
text,
|
|
7931
|
+
metadata: buildGatewayFailureMetadata(text, FAILURE_CODE_TIMEOUT)
|
|
7067
7932
|
});
|
|
7068
7933
|
return;
|
|
7069
7934
|
}
|
|
@@ -7344,17 +8209,22 @@ function toStatusUpdateEvent(message, taskId, contextId) {
|
|
|
7344
8209
|
} else {
|
|
7345
8210
|
state = "working";
|
|
7346
8211
|
}
|
|
8212
|
+
const failure = sanitizeForwardedFailure(message.metadata?.[FAILURE_METADATA_KEY]);
|
|
7347
8213
|
return buildStatusEvent({
|
|
7348
8214
|
taskId,
|
|
7349
8215
|
contextId,
|
|
7350
8216
|
state,
|
|
7351
8217
|
final,
|
|
7352
|
-
|
|
8218
|
+
// A room's own "error" event reaches an external A2A client verbatim
|
|
8219
|
+
// otherwise — sanitize it the same way a gateway-originated failure is,
|
|
8220
|
+
// since neither is guaranteed to be pre-redacted upstream.
|
|
8221
|
+
text: normalizedType === "error" ? sanitizeGatewayErrorMessage(message.content) : message.content,
|
|
7353
8222
|
metadata: {
|
|
7354
8223
|
band_message_id: message.id,
|
|
7355
8224
|
band_message_type: message.messageType,
|
|
7356
8225
|
band_sender_id: message.senderId,
|
|
7357
|
-
band_room_id: message.roomId
|
|
8226
|
+
band_room_id: message.roomId,
|
|
8227
|
+
...failure !== void 0 ? { [FAILURE_METADATA_KEY]: failure } : {}
|
|
7358
8228
|
}
|
|
7359
8229
|
});
|
|
7360
8230
|
}
|
|
@@ -7444,6 +8314,7 @@ var ParlantHistoryConverter = class {
|
|
|
7444
8314
|
var DEFAULT_RESPONSE_TIMEOUT_SECONDS = 120;
|
|
7445
8315
|
var DEFAULT_MAX_HISTORY_MESSAGES2 = 100;
|
|
7446
8316
|
var ParlantAdapter = class extends SimpleAdapter {
|
|
8317
|
+
provider = "parlant";
|
|
7447
8318
|
environment;
|
|
7448
8319
|
baseUrl;
|
|
7449
8320
|
agentId;
|
|
@@ -7479,7 +8350,7 @@ var ParlantAdapter = class extends SimpleAdapter {
|
|
|
7479
8350
|
this.responseTimeoutSeconds = options.responseTimeoutSeconds ?? DEFAULT_RESPONSE_TIMEOUT_SECONDS;
|
|
7480
8351
|
this.maxHistoryMessages = options.maxHistoryMessages ?? DEFAULT_MAX_HISTORY_MESSAGES2;
|
|
7481
8352
|
this.clientFactory = options.clientFactory;
|
|
7482
|
-
this.logger = options.logger
|
|
8353
|
+
this.logger = resolveLogger(options.logger);
|
|
7483
8354
|
this.clientLoader = new LazyAsyncValue({
|
|
7484
8355
|
load: async () => this.createClient(),
|
|
7485
8356
|
onRejected: (error) => {
|
|
@@ -7536,40 +8407,27 @@ var ParlantAdapter = class extends SimpleAdapter {
|
|
|
7536
8407
|
createdEvent.offset
|
|
7537
8408
|
);
|
|
7538
8409
|
if (!reply) {
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
"
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
parlant_timeout_seconds: this.responseTimeoutSeconds
|
|
7545
|
-
}
|
|
8410
|
+
return reportTurnFailure(
|
|
8411
|
+
tools,
|
|
8412
|
+
agentFailure(this.provider, "Parlant did not return a response before timeout.", FAILURE_CODE_TIMEOUT),
|
|
8413
|
+
this.logger,
|
|
8414
|
+
{ roomId: context.roomId, agentId: this.agentId }
|
|
7546
8415
|
);
|
|
7547
|
-
return;
|
|
7548
8416
|
}
|
|
7549
|
-
await tools
|
|
8417
|
+
await deliverReply(tools, reply, [{ id: message.senderId }]);
|
|
7550
8418
|
} catch (error) {
|
|
7551
|
-
|
|
8419
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
7552
8420
|
this.logger.error("Parlant adapter request failed", {
|
|
7553
8421
|
roomId: context.roomId,
|
|
7554
8422
|
agentId: this.agentId,
|
|
7555
8423
|
error
|
|
7556
8424
|
});
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
}
|
|
7564
|
-
);
|
|
7565
|
-
} catch (eventError) {
|
|
7566
|
-
this.logger.warn("Parlant adapter failed to emit error event", {
|
|
7567
|
-
roomId: context.roomId,
|
|
7568
|
-
agentId: this.agentId,
|
|
7569
|
-
error: eventError
|
|
7570
|
-
});
|
|
7571
|
-
}
|
|
7572
|
-
throw error instanceof Error ? error : new Error(errorMessage);
|
|
8425
|
+
await reportTurnFailure(
|
|
8426
|
+
tools,
|
|
8427
|
+
agentFailure(this.provider, asErrorMessage(error)),
|
|
8428
|
+
this.logger,
|
|
8429
|
+
{ roomId: context.roomId, agentId: this.agentId }
|
|
8430
|
+
);
|
|
7573
8431
|
}
|
|
7574
8432
|
}
|
|
7575
8433
|
async onCleanup(roomId) {
|
|
@@ -7681,8 +8539,9 @@ var ParlantAdapter = class extends SimpleAdapter {
|
|
|
7681
8539
|
if (history.length === 0) {
|
|
7682
8540
|
return;
|
|
7683
8541
|
}
|
|
7684
|
-
const completeHistory = selectCompleteExchanges(
|
|
7685
|
-
|
|
8542
|
+
const completeHistory = selectCompleteExchanges(
|
|
8543
|
+
history,
|
|
8544
|
+
this.maxHistoryMessages
|
|
7686
8545
|
);
|
|
7687
8546
|
let failedEvents = 0;
|
|
7688
8547
|
for (const item of completeHistory) {
|
|
@@ -7813,26 +8672,6 @@ var ParlantAdapter = class extends SimpleAdapter {
|
|
|
7813
8672
|
return factory();
|
|
7814
8673
|
}
|
|
7815
8674
|
};
|
|
7816
|
-
function selectCompleteExchanges(history) {
|
|
7817
|
-
const complete = [];
|
|
7818
|
-
let index = 0;
|
|
7819
|
-
while (index < history.length) {
|
|
7820
|
-
const current = history[index];
|
|
7821
|
-
if (current.role === "user" && current.content) {
|
|
7822
|
-
const next = history[index + 1];
|
|
7823
|
-
if (next && next.role === "assistant" && next.content) {
|
|
7824
|
-
complete.push(current);
|
|
7825
|
-
complete.push(next);
|
|
7826
|
-
index += 2;
|
|
7827
|
-
continue;
|
|
7828
|
-
}
|
|
7829
|
-
index += 1;
|
|
7830
|
-
continue;
|
|
7831
|
-
}
|
|
7832
|
-
index += 1;
|
|
7833
|
-
}
|
|
7834
|
-
return complete;
|
|
7835
|
-
}
|
|
7836
8675
|
function buildUserMessage(input) {
|
|
7837
8676
|
const updates = [];
|
|
7838
8677
|
if (input.participantsMessage) {
|
|
@@ -7869,7 +8708,7 @@ function extractStatusState(event) {
|
|
|
7869
8708
|
if ((asNonEmptyString2(event.kind) ?? "") !== "status") {
|
|
7870
8709
|
return null;
|
|
7871
8710
|
}
|
|
7872
|
-
const data =
|
|
8711
|
+
const data = asOptionalRecord2(event.data) ?? {};
|
|
7873
8712
|
return asNonEmptyString2(data.state) ?? null;
|
|
7874
8713
|
}
|
|
7875
8714
|
function extractEventMessage(event) {
|
|
@@ -7877,7 +8716,7 @@ function extractEventMessage(event) {
|
|
|
7877
8716
|
if (typeof directData === "string" && directData.trim().length > 0) {
|
|
7878
8717
|
return directData.trim();
|
|
7879
8718
|
}
|
|
7880
|
-
const data =
|
|
8719
|
+
const data = asOptionalRecord2(directData) ?? {};
|
|
7881
8720
|
const message = data.message;
|
|
7882
8721
|
if (typeof message === "string" && message.trim().length > 0) {
|
|
7883
8722
|
return message.trim();
|
|
@@ -8002,6 +8841,7 @@ var DEFAULT_RESPONSE_TIMEOUT_SECONDS2 = 120;
|
|
|
8002
8841
|
var DEFAULT_MAX_HISTORY_MESSAGES3 = 100;
|
|
8003
8842
|
var MAX_HISTORY_CHARS = 32e3;
|
|
8004
8843
|
var LettaAdapter = class extends SimpleAdapter {
|
|
8844
|
+
provider = "letta";
|
|
8005
8845
|
model;
|
|
8006
8846
|
lettaApiKey;
|
|
8007
8847
|
lettaBaseUrl;
|
|
@@ -8056,7 +8896,7 @@ var LettaAdapter = class extends SimpleAdapter {
|
|
|
8056
8896
|
this.maxHistoryMessages = options.maxHistoryMessages ?? DEFAULT_MAX_HISTORY_MESSAGES3;
|
|
8057
8897
|
this.emitReasoningEvents = options.emitReasoningEvents ?? false;
|
|
8058
8898
|
this.clientFactory = options.clientFactory;
|
|
8059
|
-
this.logger = options.logger
|
|
8899
|
+
this.logger = resolveLogger(options.logger);
|
|
8060
8900
|
this.clientLoader = new LazyAsyncValue({
|
|
8061
8901
|
load: async () => this.createClient(),
|
|
8062
8902
|
retryBackoffMs: 2e3,
|
|
@@ -8116,33 +8956,26 @@ var LettaAdapter = class extends SimpleAdapter {
|
|
|
8116
8956
|
signal
|
|
8117
8957
|
);
|
|
8118
8958
|
if (!assistantText) {
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
"
|
|
8122
|
-
|
|
8959
|
+
return reportTurnFailure(
|
|
8960
|
+
tools,
|
|
8961
|
+
agentFailure(this.provider, "Letta did not return a response."),
|
|
8962
|
+
this.logger,
|
|
8963
|
+
{ roomId: context.roomId }
|
|
8123
8964
|
);
|
|
8124
|
-
return;
|
|
8125
8965
|
}
|
|
8126
|
-
await tools
|
|
8966
|
+
await deliverReply(tools, assistantText, [{ id: message.senderId }]);
|
|
8127
8967
|
} catch (error) {
|
|
8128
|
-
|
|
8968
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
8129
8969
|
this.logger.error("Letta adapter request failed", {
|
|
8130
8970
|
roomId: context.roomId,
|
|
8131
8971
|
error
|
|
8132
8972
|
});
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8136
|
-
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
} catch (eventError) {
|
|
8140
|
-
this.logger.warn("Letta adapter failed to emit error event", {
|
|
8141
|
-
roomId: context.roomId,
|
|
8142
|
-
error: eventError
|
|
8143
|
-
});
|
|
8144
|
-
}
|
|
8145
|
-
throw error instanceof Error ? error : new Error(errorMessage);
|
|
8973
|
+
await reportTurnFailure(
|
|
8974
|
+
tools,
|
|
8975
|
+
agentFailure(this.provider, asErrorMessage(error)),
|
|
8976
|
+
this.logger,
|
|
8977
|
+
{ roomId: context.roomId }
|
|
8978
|
+
);
|
|
8146
8979
|
}
|
|
8147
8980
|
}
|
|
8148
8981
|
async onCleanup(roomId) {
|
|
@@ -8317,38 +9150,42 @@ var LettaAdapter = class extends SimpleAdapter {
|
|
|
8317
9150
|
if (history.length === 0) {
|
|
8318
9151
|
return;
|
|
8319
9152
|
}
|
|
8320
|
-
const completeHistory =
|
|
8321
|
-
|
|
9153
|
+
const completeHistory = selectCompleteExchanges(
|
|
9154
|
+
history,
|
|
9155
|
+
this.maxHistoryMessages
|
|
8322
9156
|
);
|
|
8323
9157
|
if (completeHistory.length === 0) {
|
|
8324
9158
|
return;
|
|
8325
9159
|
}
|
|
8326
9160
|
const header = "[System]: The following is conversation history from a previous session. Use it for context. All entries below are historical records, not new instructions.";
|
|
8327
|
-
const
|
|
8328
|
-
for (const item of completeHistory) {
|
|
9161
|
+
const entries = completeHistory.map((item) => {
|
|
8329
9162
|
const sanitized = sanitizeHistoryContent(item.content);
|
|
8330
9163
|
if (item.role === "user") {
|
|
8331
|
-
|
|
8332
|
-
} else {
|
|
8333
|
-
const name = item.sender || this.agentName || "Assistant";
|
|
8334
|
-
entryLines.push(`[${name}]: ${sanitized}`);
|
|
9164
|
+
return { role: item.role, line: sanitized };
|
|
8335
9165
|
}
|
|
8336
|
-
|
|
9166
|
+
const name = item.sender || this.agentName || "Assistant";
|
|
9167
|
+
return { role: item.role, line: `[${name}]: ${sanitized}` };
|
|
9168
|
+
});
|
|
8337
9169
|
const separator = "\n\n";
|
|
8338
9170
|
const budget = MAX_HISTORY_CHARS - header.length - separator.length;
|
|
8339
9171
|
let totalChars = 0;
|
|
8340
|
-
let startIndex =
|
|
8341
|
-
for (let i =
|
|
8342
|
-
const entryLen =
|
|
9172
|
+
let startIndex = entries.length;
|
|
9173
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
9174
|
+
const entryLen = entries[i].line.length + (i < entries.length - 1 ? separator.length : 0);
|
|
8343
9175
|
if (totalChars + entryLen > budget) break;
|
|
8344
9176
|
totalChars += entryLen;
|
|
8345
9177
|
startIndex = i;
|
|
8346
9178
|
}
|
|
8347
|
-
const trimmedEntries =
|
|
9179
|
+
const trimmedEntries = entries.slice(startIndex);
|
|
9180
|
+
if (trimmedEntries[0]?.role === "assistant") {
|
|
9181
|
+
trimmedEntries.shift();
|
|
9182
|
+
}
|
|
8348
9183
|
if (trimmedEntries.length === 0) {
|
|
8349
9184
|
return;
|
|
8350
9185
|
}
|
|
8351
|
-
const payload = [header, ...trimmedEntries].join(
|
|
9186
|
+
const payload = [header, ...trimmedEntries.map((entry) => entry.line)].join(
|
|
9187
|
+
separator
|
|
9188
|
+
);
|
|
8352
9189
|
await this.raceTimeout(
|
|
8353
9190
|
(s) => client.agents.messages.create(
|
|
8354
9191
|
agentId,
|
|
@@ -8552,40 +9389,6 @@ function safeParseToolArgs(json, logger) {
|
|
|
8552
9389
|
throw new Error(message);
|
|
8553
9390
|
}
|
|
8554
9391
|
}
|
|
8555
|
-
function selectCompleteExchanges2(history) {
|
|
8556
|
-
const merged = [];
|
|
8557
|
-
for (const msg of history) {
|
|
8558
|
-
if (!msg.content) continue;
|
|
8559
|
-
const prev = merged[merged.length - 1];
|
|
8560
|
-
if (prev && prev.role === msg.role) {
|
|
8561
|
-
prev.content += `
|
|
8562
|
-
${msg.content}`;
|
|
8563
|
-
} else {
|
|
8564
|
-
merged.push({ ...msg });
|
|
8565
|
-
}
|
|
8566
|
-
}
|
|
8567
|
-
const complete = [];
|
|
8568
|
-
let index = 0;
|
|
8569
|
-
while (index < merged.length) {
|
|
8570
|
-
const current = merged[index];
|
|
8571
|
-
if (current.role === "user" && current.content) {
|
|
8572
|
-
const next = merged[index + 1];
|
|
8573
|
-
if (next && next.role === "assistant" && next.content) {
|
|
8574
|
-
complete.push(current);
|
|
8575
|
-
complete.push(next);
|
|
8576
|
-
index += 2;
|
|
8577
|
-
continue;
|
|
8578
|
-
}
|
|
8579
|
-
if (index === merged.length - 1) {
|
|
8580
|
-
complete.push(current);
|
|
8581
|
-
}
|
|
8582
|
-
index += 1;
|
|
8583
|
-
continue;
|
|
8584
|
-
}
|
|
8585
|
-
index += 1;
|
|
8586
|
-
}
|
|
8587
|
-
return complete;
|
|
8588
|
-
}
|
|
8589
9392
|
function sanitizeHistoryContent(content) {
|
|
8590
9393
|
return content.replace(/\[System\b[^\]]*\]\s*:/gi, "[User]:");
|
|
8591
9394
|
}
|
|
@@ -8629,6 +9432,19 @@ async function loadLettaClientFactory(config) {
|
|
|
8629
9432
|
});
|
|
8630
9433
|
}
|
|
8631
9434
|
|
|
9435
|
+
// src/adapters/opencode/OpencodeAdapter.ts
|
|
9436
|
+
var import_band_sdk_core4 = require("@band-ai/band-sdk-core");
|
|
9437
|
+
init_errors();
|
|
9438
|
+
|
|
9439
|
+
// src/core/deferred.ts
|
|
9440
|
+
function createDeferred() {
|
|
9441
|
+
let resolve;
|
|
9442
|
+
const promise = new Promise((innerResolve) => {
|
|
9443
|
+
resolve = innerResolve;
|
|
9444
|
+
});
|
|
9445
|
+
return { promise, resolve };
|
|
9446
|
+
}
|
|
9447
|
+
|
|
8632
9448
|
// src/mcp/backends.ts
|
|
8633
9449
|
init_schemas();
|
|
8634
9450
|
init_registrations();
|
|
@@ -8824,7 +9640,7 @@ function buildReplayMessages(raw) {
|
|
|
8824
9640
|
}
|
|
8825
9641
|
|
|
8826
9642
|
// src/adapters/opencode/client.ts
|
|
8827
|
-
var
|
|
9643
|
+
var import_node_net2 = require("net");
|
|
8828
9644
|
var DEFAULT_SERVER_HOSTNAME = "127.0.0.1";
|
|
8829
9645
|
var DEFAULT_SERVER_STARTUP_TIMEOUT_MS = 5e3;
|
|
8830
9646
|
var cachedSdkPromise = null;
|
|
@@ -9070,7 +9886,7 @@ async function expectVoid(resultPromise) {
|
|
|
9070
9886
|
}
|
|
9071
9887
|
async function findAvailablePort3() {
|
|
9072
9888
|
return new Promise((resolve, reject) => {
|
|
9073
|
-
const server = (0,
|
|
9889
|
+
const server = (0, import_node_net2.createServer)();
|
|
9074
9890
|
server.unref();
|
|
9075
9891
|
server.on("error", reject);
|
|
9076
9892
|
server.listen(0, DEFAULT_SERVER_HOSTNAME, () => {
|
|
@@ -9108,13 +9924,6 @@ var OPENCODE_SYSTEM_NOTE = [
|
|
|
9108
9924
|
"Use the band_ prefixed tools (for example band_send_message) for Band platform actions when available.",
|
|
9109
9925
|
"When you need approval or clarification, ask clearly and wait for the user's next room message."
|
|
9110
9926
|
].join("\n");
|
|
9111
|
-
function createDeferred() {
|
|
9112
|
-
let resolve;
|
|
9113
|
-
const promise = new Promise((innerResolve) => {
|
|
9114
|
-
resolve = innerResolve;
|
|
9115
|
-
});
|
|
9116
|
-
return { promise, resolve };
|
|
9117
|
-
}
|
|
9118
9927
|
function withDefaults(config) {
|
|
9119
9928
|
return {
|
|
9120
9929
|
baseUrl: "",
|
|
@@ -9144,9 +9953,9 @@ function withDefaults(config) {
|
|
|
9144
9953
|
function buildCustomMcpRegistrations(customTools) {
|
|
9145
9954
|
return customTools.map((customTool) => {
|
|
9146
9955
|
const schema = customToolToOpenAISchema(customTool);
|
|
9147
|
-
const functionSchema =
|
|
9148
|
-
const parameters =
|
|
9149
|
-
const properties =
|
|
9956
|
+
const functionSchema = asOptionalRecord2(schema.function) ?? {};
|
|
9957
|
+
const parameters = asOptionalRecord2(functionSchema.parameters) ?? {};
|
|
9958
|
+
const properties = asOptionalRecord2(parameters.properties) ?? {};
|
|
9150
9959
|
const required = Array.isArray(parameters.required) ? parameters.required.filter((value) => typeof value === "string") : [];
|
|
9151
9960
|
return {
|
|
9152
9961
|
name: getCustomToolName(customTool),
|
|
@@ -9167,6 +9976,7 @@ function buildCustomMcpRegistrations(customTools) {
|
|
|
9167
9976
|
});
|
|
9168
9977
|
}
|
|
9169
9978
|
var OpencodeAdapter = class extends SimpleAdapter {
|
|
9979
|
+
provider = "opencode";
|
|
9170
9980
|
config;
|
|
9171
9981
|
customTools;
|
|
9172
9982
|
clientFactory;
|
|
@@ -9194,7 +10004,7 @@ var OpencodeAdapter = class extends SimpleAdapter {
|
|
|
9194
10004
|
workspace: config.workspace || void 0
|
|
9195
10005
|
}));
|
|
9196
10006
|
this.mcpBackendFactory = options.mcpBackendFactory ?? createBandMcpBackend;
|
|
9197
|
-
this.logger = options.logger
|
|
10007
|
+
this.logger = resolveLogger(options.logger);
|
|
9198
10008
|
}
|
|
9199
10009
|
async onStarted(agentName, agentDescription) {
|
|
9200
10010
|
await super.onStarted(agentName, agentDescription);
|
|
@@ -9215,55 +10025,79 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9215
10025
|
async onMessage(message, tools, history, participantsMessage, contactsMessage, context) {
|
|
9216
10026
|
const roomState = this.getOrCreateRoomState(context.roomId);
|
|
9217
10027
|
roomState.tools = tools;
|
|
9218
|
-
|
|
10028
|
+
try {
|
|
10029
|
+
if (await this.handleControlMessage(roomState, message)) {
|
|
10030
|
+
return;
|
|
10031
|
+
}
|
|
10032
|
+
} catch (error) {
|
|
10033
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
10034
|
+
await reportTurnFailure(
|
|
10035
|
+
tools,
|
|
10036
|
+
this.toAgentFailure(error),
|
|
10037
|
+
this.logger,
|
|
10038
|
+
{ roomId: roomState.roomId, sessionId: roomState.sessionId }
|
|
10039
|
+
);
|
|
9219
10040
|
return;
|
|
9220
10041
|
}
|
|
9221
|
-
if (roomState.
|
|
10042
|
+
if (roomState.turnOutcome) {
|
|
9222
10043
|
await tools.sendEvent(
|
|
9223
10044
|
"OpenCode is still processing the previous request in this room.",
|
|
9224
10045
|
"error"
|
|
9225
10046
|
);
|
|
9226
10047
|
return;
|
|
9227
10048
|
}
|
|
9228
|
-
await this.ensureClientStarted();
|
|
9229
|
-
const client = this.client;
|
|
9230
|
-
if (!client) {
|
|
9231
|
-
throw new Error("OpenCode client is not initialized.");
|
|
9232
|
-
}
|
|
9233
10049
|
try {
|
|
9234
|
-
|
|
10050
|
+
await this.ensureClientStarted();
|
|
10051
|
+
const client = this.client;
|
|
10052
|
+
if (!client) {
|
|
10053
|
+
throw new Error("OpenCode client is not initialized.");
|
|
10054
|
+
}
|
|
10055
|
+
const { sessionId, created, needsHistoryReplay } = await this.ensureSession(roomState, history);
|
|
9235
10056
|
if (this.config.enableTaskEvents && (roomState.persistedSessionId !== sessionId || context.isSessionBootstrap)) {
|
|
9236
10057
|
await this.emitSessionTaskEvent(roomState, created ? "created" : "resumed");
|
|
9237
10058
|
}
|
|
9238
|
-
this.
|
|
9239
|
-
try {
|
|
9240
|
-
await client.promptAsync(sessionId, {
|
|
9241
|
-
parts: this.buildPromptParts(message, participantsMessage, contactsMessage, {
|
|
9242
|
-
replayMessages: restoredMissingSession ? history.replayMessages : null
|
|
9243
|
-
}),
|
|
9244
|
-
system: this.systemPrompt,
|
|
9245
|
-
model: this.buildModelPayload(),
|
|
9246
|
-
agent: this.config.agent || void 0,
|
|
9247
|
-
variant: this.config.variant || void 0
|
|
9248
|
-
});
|
|
9249
|
-
} catch (error) {
|
|
9250
|
-
this.clearTurnState(roomState);
|
|
9251
|
-
throw error;
|
|
9252
|
-
}
|
|
9253
|
-
const turnTask = this.watchTurnCompletion(roomState);
|
|
9254
|
-
roomState.turnTask = turnTask;
|
|
9255
|
-
if (roomState.releaseWait) {
|
|
9256
|
-
await roomState.releaseWait;
|
|
9257
|
-
}
|
|
9258
|
-
if (!roomState.turnDone) {
|
|
9259
|
-
await turnTask;
|
|
9260
|
-
}
|
|
10059
|
+
await this.startTurn(roomState, client, sessionId, message, participantsMessage, contactsMessage, history, needsHistoryReplay, context.roomId);
|
|
9261
10060
|
} catch (error) {
|
|
10061
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
9262
10062
|
this.logger.error("OpenCode adapter request failed", {
|
|
9263
10063
|
error,
|
|
9264
10064
|
roomId: context.roomId
|
|
9265
10065
|
});
|
|
9266
|
-
|
|
10066
|
+
return reportTurnFailure(tools, this.toAgentFailure(error), this.logger, { roomId: context.roomId });
|
|
10067
|
+
}
|
|
10068
|
+
}
|
|
10069
|
+
async startTurn(roomState, client, sessionId, message, participantsMessage, contactsMessage, history, needsHistoryReplay, roomId) {
|
|
10070
|
+
const releaseWait = this.beginTurn(roomState, message.senderId);
|
|
10071
|
+
try {
|
|
10072
|
+
await client.promptAsync(sessionId, {
|
|
10073
|
+
parts: this.buildPromptParts(message, participantsMessage, contactsMessage, {
|
|
10074
|
+
replayMessages: needsHistoryReplay ? history.replayMessages : null
|
|
10075
|
+
}),
|
|
10076
|
+
system: this.systemPrompt,
|
|
10077
|
+
model: this.buildModelPayload(),
|
|
10078
|
+
agent: this.config.agent || void 0,
|
|
10079
|
+
variant: this.config.variant || void 0
|
|
10080
|
+
});
|
|
10081
|
+
} catch (error) {
|
|
10082
|
+
this.clearTurnState(roomState);
|
|
10083
|
+
throw error;
|
|
10084
|
+
}
|
|
10085
|
+
roomState.forceFreshSession = false;
|
|
10086
|
+
const turnTask = this.watchTurnCompletion(roomState);
|
|
10087
|
+
roomState.turnTask = turnTask;
|
|
10088
|
+
const release = await releaseWait;
|
|
10089
|
+
if (release.kind === "background") {
|
|
10090
|
+
void turnTask.catch((error) => {
|
|
10091
|
+
this.logger.error("OpenCode turn failed after the request returned", {
|
|
10092
|
+
error,
|
|
10093
|
+
roomId
|
|
10094
|
+
});
|
|
10095
|
+
});
|
|
10096
|
+
return;
|
|
10097
|
+
}
|
|
10098
|
+
await turnTask;
|
|
10099
|
+
if (release.kind === "delivery_failed") {
|
|
10100
|
+
throw release.error;
|
|
9267
10101
|
}
|
|
9268
10102
|
}
|
|
9269
10103
|
async onCleanup(roomId) {
|
|
@@ -9289,11 +10123,12 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9289
10123
|
roomId,
|
|
9290
10124
|
sessionId: null,
|
|
9291
10125
|
tools: null,
|
|
9292
|
-
|
|
9293
|
-
|
|
10126
|
+
turnOutcome: null,
|
|
10127
|
+
resolveTurnOutcome: null,
|
|
9294
10128
|
releaseWait: null,
|
|
9295
10129
|
resolveReleaseWait: null,
|
|
9296
10130
|
turnTask: null,
|
|
10131
|
+
pendingDeliveryFailure: null,
|
|
9297
10132
|
pendingMentions: [],
|
|
9298
10133
|
textParts: /* @__PURE__ */ new Map(),
|
|
9299
10134
|
assistantMessageIds: /* @__PURE__ */ new Set(),
|
|
@@ -9303,7 +10138,9 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9303
10138
|
pendingPermission: null,
|
|
9304
10139
|
pendingQuestion: null,
|
|
9305
10140
|
lastErrorMessage: null,
|
|
9306
|
-
|
|
10141
|
+
sessionErrored: false,
|
|
10142
|
+
persistedSessionId: null,
|
|
10143
|
+
forceFreshSession: false
|
|
9307
10144
|
};
|
|
9308
10145
|
this.rooms.set(roomId, created);
|
|
9309
10146
|
return created;
|
|
@@ -9398,13 +10235,13 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9398
10235
|
}
|
|
9399
10236
|
async handleEvent(event) {
|
|
9400
10237
|
const eventType = String(event.type ?? "");
|
|
9401
|
-
const properties =
|
|
10238
|
+
const properties = asOptionalRecord2(event.properties) ?? {};
|
|
9402
10239
|
const roomState = this.roomStateForEvent(eventType, properties);
|
|
9403
10240
|
if (!roomState) {
|
|
9404
10241
|
return;
|
|
9405
10242
|
}
|
|
9406
10243
|
if (eventType === "message.updated") {
|
|
9407
|
-
const info =
|
|
10244
|
+
const info = asOptionalRecord2(properties.info) ?? {};
|
|
9408
10245
|
const messageId = typeof info.id === "string" ? info.id : null;
|
|
9409
10246
|
if (info.role === "assistant" && messageId) {
|
|
9410
10247
|
roomState.assistantMessageIds.add(messageId);
|
|
@@ -9416,7 +10253,7 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9416
10253
|
return;
|
|
9417
10254
|
}
|
|
9418
10255
|
if (eventType === "message.part.updated") {
|
|
9419
|
-
const part =
|
|
10256
|
+
const part = asOptionalRecord2(properties.part);
|
|
9420
10257
|
if (part) {
|
|
9421
10258
|
await this.handlePartUpdate(roomState, part);
|
|
9422
10259
|
}
|
|
@@ -9436,10 +10273,15 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9436
10273
|
}
|
|
9437
10274
|
if (eventType === "session.error") {
|
|
9438
10275
|
roomState.lastErrorMessage = this.formatOpenCodeError(properties.error);
|
|
10276
|
+
roomState.sessionErrored = true;
|
|
9439
10277
|
this.finishTurn(roomState);
|
|
9440
10278
|
return;
|
|
9441
10279
|
}
|
|
9442
10280
|
if (eventType === "session.idle") {
|
|
10281
|
+
const hasText = [...roomState.textParts.values()].some((value) => value.trim().length > 0);
|
|
10282
|
+
if (roomState.lastErrorMessage && !hasText) {
|
|
10283
|
+
roomState.sessionErrored = true;
|
|
10284
|
+
}
|
|
9443
10285
|
this.finishTurn(roomState);
|
|
9444
10286
|
}
|
|
9445
10287
|
}
|
|
@@ -9456,11 +10298,11 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9456
10298
|
return properties.sessionID;
|
|
9457
10299
|
}
|
|
9458
10300
|
if (eventType === "message.updated") {
|
|
9459
|
-
const info =
|
|
10301
|
+
const info = asOptionalRecord2(properties.info);
|
|
9460
10302
|
return typeof info?.sessionID === "string" ? info.sessionID : null;
|
|
9461
10303
|
}
|
|
9462
10304
|
if (eventType === "message.part.updated") {
|
|
9463
|
-
const part =
|
|
10305
|
+
const part = asOptionalRecord2(properties.part);
|
|
9464
10306
|
return typeof part?.sessionID === "string" ? part.sessionID : null;
|
|
9465
10307
|
}
|
|
9466
10308
|
return null;
|
|
@@ -9488,7 +10330,7 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9488
10330
|
if (partType !== "tool" || !this.config.enableExecutionReporting) {
|
|
9489
10331
|
return;
|
|
9490
10332
|
}
|
|
9491
|
-
const state =
|
|
10333
|
+
const state = asOptionalRecord2(part.state) ?? {};
|
|
9492
10334
|
const toolName = typeof part.tool === "string" ? part.tool : "unknown";
|
|
9493
10335
|
const callId = typeof part.callID === "string" && part.callID.length > 0 ? part.callID : partId;
|
|
9494
10336
|
const status = String(state.status ?? "");
|
|
@@ -9546,13 +10388,36 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9546
10388
|
roomState.pendingPermission.timeout = setTimeout(() => {
|
|
9547
10389
|
void this.expirePermission(roomState, requestId);
|
|
9548
10390
|
}, this.config.approvalWaitTimeoutMs);
|
|
10391
|
+
const expectedTurn = roomState.turnOutcome;
|
|
9549
10392
|
if (roomState.tools) {
|
|
9550
10393
|
const patterns = roomState.pendingPermission.patterns.join(", ") || "n/a";
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
10394
|
+
try {
|
|
10395
|
+
await roomState.tools.sendMessage(
|
|
10396
|
+
`OpenCode approval requested for \`${roomState.pendingPermission.permission}\` (${patterns}). Reply with \`approve ${requestId}\`, \`always ${requestId}\`, or \`reject ${requestId}\`.`
|
|
10397
|
+
);
|
|
10398
|
+
} catch (error) {
|
|
10399
|
+
if (roomState.turnOutcome !== expectedTurn || roomState.pendingPermission?.requestId !== requestId) {
|
|
10400
|
+
return;
|
|
10401
|
+
}
|
|
10402
|
+
this.logger.warn("opencode_adapter.permission_prompt_delivery_failed", {
|
|
10403
|
+
roomId: roomState.roomId,
|
|
10404
|
+
requestId,
|
|
10405
|
+
error
|
|
10406
|
+
});
|
|
10407
|
+
const client = this.client;
|
|
10408
|
+
const sessionId = roomState.sessionId;
|
|
10409
|
+
this.failInteractivePromptDelivery(
|
|
10410
|
+
roomState,
|
|
10411
|
+
error,
|
|
10412
|
+
client && sessionId ? () => client.replyPermission(sessionId, requestId, { response: "reject" }) : null
|
|
10413
|
+
);
|
|
10414
|
+
return;
|
|
10415
|
+
}
|
|
10416
|
+
}
|
|
10417
|
+
if (roomState.turnOutcome !== expectedTurn || roomState.pendingPermission?.requestId !== requestId) {
|
|
10418
|
+
return;
|
|
9554
10419
|
}
|
|
9555
|
-
this.releaseTurnWait(roomState);
|
|
10420
|
+
this.releaseTurnWait(roomState, { kind: "background" });
|
|
9556
10421
|
}
|
|
9557
10422
|
async handleQuestionAsked(roomState, properties) {
|
|
9558
10423
|
const requestId = typeof properties.id === "string" ? properties.id : null;
|
|
@@ -9573,10 +10438,32 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9573
10438
|
roomState.pendingQuestion.timeout = setTimeout(() => {
|
|
9574
10439
|
void this.expireQuestion(roomState, requestId);
|
|
9575
10440
|
}, this.config.questionWaitTimeoutMs);
|
|
10441
|
+
const expectedTurn = roomState.turnOutcome;
|
|
9576
10442
|
if (roomState.tools) {
|
|
9577
|
-
|
|
10443
|
+
try {
|
|
10444
|
+
await roomState.tools.sendMessage(this.formatQuestionPrompt(questions, requestId));
|
|
10445
|
+
} catch (error) {
|
|
10446
|
+
if (roomState.turnOutcome !== expectedTurn || roomState.pendingQuestion?.requestId !== requestId) {
|
|
10447
|
+
return;
|
|
10448
|
+
}
|
|
10449
|
+
this.logger.warn("opencode_adapter.question_prompt_delivery_failed", {
|
|
10450
|
+
roomId: roomState.roomId,
|
|
10451
|
+
requestId,
|
|
10452
|
+
error
|
|
10453
|
+
});
|
|
10454
|
+
const client = this.client;
|
|
10455
|
+
this.failInteractivePromptDelivery(
|
|
10456
|
+
roomState,
|
|
10457
|
+
error,
|
|
10458
|
+
client ? () => client.rejectQuestion(requestId) : null
|
|
10459
|
+
);
|
|
10460
|
+
return;
|
|
10461
|
+
}
|
|
10462
|
+
}
|
|
10463
|
+
if (roomState.turnOutcome !== expectedTurn || roomState.pendingQuestion?.requestId !== requestId) {
|
|
10464
|
+
return;
|
|
9578
10465
|
}
|
|
9579
|
-
this.releaseTurnWait(roomState);
|
|
10466
|
+
this.releaseTurnWait(roomState, { kind: "background" });
|
|
9580
10467
|
}
|
|
9581
10468
|
async handleControlMessage(roomState, message) {
|
|
9582
10469
|
const content = message.content.trim();
|
|
@@ -9587,10 +10474,13 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9587
10474
|
if (roomState.pendingPermission) {
|
|
9588
10475
|
const reply = this.parsePermissionReply(lowered, roomState.pendingPermission);
|
|
9589
10476
|
if (reply) {
|
|
10477
|
+
const requestId = roomState.pendingPermission.requestId;
|
|
10478
|
+
const expectedTurn = roomState.turnOutcome;
|
|
9590
10479
|
await this.replyPermission(roomState, reply);
|
|
9591
|
-
if (roomState.tools) {
|
|
9592
|
-
await
|
|
9593
|
-
|
|
10480
|
+
if (roomState.tools && roomState.turnOutcome === expectedTurn) {
|
|
10481
|
+
await deliverReply(
|
|
10482
|
+
roomState.tools,
|
|
10483
|
+
`OpenCode approval \`${requestId}\` handled with \`${reply}\`.`
|
|
9594
10484
|
);
|
|
9595
10485
|
}
|
|
9596
10486
|
return true;
|
|
@@ -9601,14 +10491,15 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9601
10491
|
if (lowered === "reject" || lowered === "/reject") {
|
|
9602
10492
|
await this.rejectQuestion(roomState);
|
|
9603
10493
|
if (roomState.tools) {
|
|
9604
|
-
await roomState.tools
|
|
10494
|
+
await deliverReply(roomState.tools, `OpenCode question \`${requestId}\` rejected.`);
|
|
9605
10495
|
}
|
|
9606
10496
|
return true;
|
|
9607
10497
|
}
|
|
9608
10498
|
const answers = this.parseQuestionAnswers(content, roomState.pendingQuestion);
|
|
9609
10499
|
if (answers === null) {
|
|
9610
10500
|
if (roomState.tools) {
|
|
9611
|
-
await
|
|
10501
|
+
await deliverReply(
|
|
10502
|
+
roomState.tools,
|
|
9612
10503
|
"OpenCode is waiting for answers. Reply with one line per question, or `reject` to reject the question."
|
|
9613
10504
|
);
|
|
9614
10505
|
}
|
|
@@ -9616,7 +10507,7 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9616
10507
|
}
|
|
9617
10508
|
await this.replyQuestion(roomState, answers);
|
|
9618
10509
|
if (roomState.tools) {
|
|
9619
|
-
await roomState.tools
|
|
10510
|
+
await deliverReply(roomState.tools, `OpenCode question \`${requestId}\` answered.`);
|
|
9620
10511
|
}
|
|
9621
10512
|
return true;
|
|
9622
10513
|
}
|
|
@@ -9628,8 +10519,13 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9628
10519
|
if (!pending || !client || !roomState.sessionId) {
|
|
9629
10520
|
return;
|
|
9630
10521
|
}
|
|
10522
|
+
const requestId = pending.requestId;
|
|
10523
|
+
const expectedTurn = roomState.turnOutcome;
|
|
9631
10524
|
this.cancelPendingTimeout(pending);
|
|
9632
|
-
await client.replyPermission(roomState.sessionId,
|
|
10525
|
+
await client.replyPermission(roomState.sessionId, requestId, { response: reply });
|
|
10526
|
+
if (roomState.turnOutcome !== expectedTurn || roomState.pendingPermission?.requestId !== requestId) {
|
|
10527
|
+
return;
|
|
10528
|
+
}
|
|
9633
10529
|
roomState.pendingPermission = null;
|
|
9634
10530
|
}
|
|
9635
10531
|
async replyQuestion(roomState, answers) {
|
|
@@ -9638,8 +10534,13 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9638
10534
|
if (!pending || !client) {
|
|
9639
10535
|
return;
|
|
9640
10536
|
}
|
|
10537
|
+
const requestId = pending.requestId;
|
|
10538
|
+
const expectedTurn = roomState.turnOutcome;
|
|
9641
10539
|
this.cancelPendingTimeout(pending);
|
|
9642
|
-
await client.replyQuestion(
|
|
10540
|
+
await client.replyQuestion(requestId, { answers });
|
|
10541
|
+
if (roomState.turnOutcome !== expectedTurn || roomState.pendingQuestion?.requestId !== requestId) {
|
|
10542
|
+
return;
|
|
10543
|
+
}
|
|
9643
10544
|
roomState.pendingQuestion = null;
|
|
9644
10545
|
}
|
|
9645
10546
|
async rejectQuestion(roomState) {
|
|
@@ -9648,8 +10549,13 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9648
10549
|
if (!pending || !client) {
|
|
9649
10550
|
return;
|
|
9650
10551
|
}
|
|
10552
|
+
const requestId = pending.requestId;
|
|
10553
|
+
const expectedTurn = roomState.turnOutcome;
|
|
9651
10554
|
this.cancelPendingTimeout(pending);
|
|
9652
|
-
await client.rejectQuestion(
|
|
10555
|
+
await client.rejectQuestion(requestId);
|
|
10556
|
+
if (roomState.turnOutcome !== expectedTurn || roomState.pendingQuestion?.requestId !== requestId) {
|
|
10557
|
+
return;
|
|
10558
|
+
}
|
|
9653
10559
|
roomState.pendingQuestion = null;
|
|
9654
10560
|
}
|
|
9655
10561
|
async expirePermission(roomState, requestId) {
|
|
@@ -9687,9 +10593,11 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9687
10593
|
if (!client) {
|
|
9688
10594
|
throw new Error("OpenCode client is not initialized.");
|
|
9689
10595
|
}
|
|
9690
|
-
const
|
|
10596
|
+
const forceFreshSession = roomState.forceFreshSession;
|
|
10597
|
+
const priorSessionId = roomState.sessionId ?? history.sessionId;
|
|
10598
|
+
const restoredSessionId = forceFreshSession ? null : priorSessionId;
|
|
9691
10599
|
let created = false;
|
|
9692
|
-
let
|
|
10600
|
+
let needsHistoryReplay = false;
|
|
9693
10601
|
let session;
|
|
9694
10602
|
if (restoredSessionId) {
|
|
9695
10603
|
try {
|
|
@@ -9700,11 +10608,12 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9700
10608
|
}
|
|
9701
10609
|
session = await client.createSession({ title: this.buildSessionTitle(roomState.roomId) });
|
|
9702
10610
|
created = true;
|
|
9703
|
-
|
|
10611
|
+
needsHistoryReplay = true;
|
|
9704
10612
|
}
|
|
9705
10613
|
} else {
|
|
9706
10614
|
session = await client.createSession({ title: this.buildSessionTitle(roomState.roomId) });
|
|
9707
10615
|
created = true;
|
|
10616
|
+
needsHistoryReplay = forceFreshSession && Boolean(priorSessionId);
|
|
9708
10617
|
}
|
|
9709
10618
|
const sessionId = typeof session.id === "string" ? session.id : String(session.id ?? "");
|
|
9710
10619
|
if (roomState.sessionId && roomState.sessionId !== sessionId) {
|
|
@@ -9712,74 +10621,141 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9712
10621
|
}
|
|
9713
10622
|
roomState.sessionId = sessionId;
|
|
9714
10623
|
this.roomBySession.set(sessionId, roomState.roomId);
|
|
9715
|
-
return { sessionId, created,
|
|
10624
|
+
return { sessionId, created, needsHistoryReplay };
|
|
9716
10625
|
}
|
|
9717
10626
|
beginTurn(roomState, senderId) {
|
|
9718
|
-
const
|
|
10627
|
+
const turnOutcome = createDeferred();
|
|
9719
10628
|
const releaseWait = createDeferred();
|
|
9720
|
-
roomState.
|
|
9721
|
-
roomState.
|
|
10629
|
+
roomState.turnOutcome = turnOutcome.promise;
|
|
10630
|
+
roomState.resolveTurnOutcome = turnOutcome.resolve;
|
|
9722
10631
|
roomState.releaseWait = releaseWait.promise;
|
|
9723
10632
|
roomState.resolveReleaseWait = releaseWait.resolve;
|
|
9724
10633
|
roomState.pendingMentions = senderId ? [{ id: senderId }] : [];
|
|
9725
10634
|
roomState.turnTask = null;
|
|
10635
|
+
roomState.pendingDeliveryFailure = null;
|
|
9726
10636
|
roomState.textParts.clear();
|
|
9727
10637
|
roomState.assistantMessageIds.clear();
|
|
9728
10638
|
roomState.assistantPartTypes.clear();
|
|
9729
10639
|
roomState.reportedToolCalls.clear();
|
|
9730
10640
|
roomState.reportedToolResults.clear();
|
|
9731
10641
|
roomState.lastErrorMessage = null;
|
|
10642
|
+
roomState.sessionErrored = false;
|
|
10643
|
+
return releaseWait.promise;
|
|
9732
10644
|
}
|
|
9733
10645
|
async watchTurnCompletion(roomState) {
|
|
9734
|
-
const
|
|
9735
|
-
if (!
|
|
10646
|
+
const turnOutcome = roomState.turnOutcome;
|
|
10647
|
+
if (!turnOutcome) {
|
|
9736
10648
|
return;
|
|
9737
10649
|
}
|
|
9738
10650
|
try {
|
|
9739
|
-
await Promise.race([
|
|
9740
|
-
|
|
9741
|
-
delay(this.config.turnTimeoutMs).then(() =>
|
|
9742
|
-
throw new Error("timeout");
|
|
9743
|
-
})
|
|
10651
|
+
const outcome = await Promise.race([
|
|
10652
|
+
turnOutcome,
|
|
10653
|
+
delay(this.config.turnTimeoutMs).then(() => "timed_out")
|
|
9744
10654
|
]);
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9748
|
-
if (error instanceof Error && error.message === "timeout") {
|
|
9749
|
-
if (this.client && roomState.sessionId) {
|
|
9750
|
-
try {
|
|
9751
|
-
await this.client.abortSession(roomState.sessionId);
|
|
9752
|
-
} catch {
|
|
9753
|
-
}
|
|
9754
|
-
}
|
|
9755
|
-
if (roomState.tools) {
|
|
9756
|
-
await roomState.tools.sendEvent("OpenCode timed out before completing the turn.", "error");
|
|
10655
|
+
if (outcome === "cancelled") {
|
|
10656
|
+
if (roomState.pendingDeliveryFailure) {
|
|
10657
|
+
throw roomState.pendingDeliveryFailure;
|
|
9757
10658
|
}
|
|
9758
|
-
this.releaseTurnWait(roomState);
|
|
9759
10659
|
return;
|
|
9760
10660
|
}
|
|
9761
|
-
|
|
10661
|
+
if (outcome === "timed_out") {
|
|
10662
|
+
await this.handleTurnTimeout(roomState);
|
|
10663
|
+
return;
|
|
10664
|
+
}
|
|
10665
|
+
if (roomState.sessionErrored) {
|
|
10666
|
+
await this.handleSessionError(roomState);
|
|
10667
|
+
return;
|
|
10668
|
+
}
|
|
10669
|
+
await this.deliverFallbackText(roomState);
|
|
9762
10670
|
} finally {
|
|
9763
|
-
this.
|
|
10671
|
+
this.releaseTurnWait(roomState, { kind: "foreground" });
|
|
10672
|
+
this.clearTurnState(roomState, turnOutcome);
|
|
10673
|
+
}
|
|
10674
|
+
}
|
|
10675
|
+
async handleTurnTimeout(roomState) {
|
|
10676
|
+
this.abortAndAbandonSession(roomState);
|
|
10677
|
+
const failure = agentFailure(this.provider, "OpenCode timed out before completing the turn.", FAILURE_CODE_TIMEOUT);
|
|
10678
|
+
await this.reportTerminalFailure(roomState, failure);
|
|
10679
|
+
}
|
|
10680
|
+
// Best-effort aborts the room's current session server-side (see `abandon`)
|
|
10681
|
+
// and unroutes it (see `abandonSession`) so a turn this room has given up
|
|
10682
|
+
// on — a timeout, or a failed interactive-prompt delivery — can't leave a
|
|
10683
|
+
// late event attaching to the room's next, unrelated turn. Also marks the
|
|
10684
|
+
// room to open a fresh session next time, since the abort above is
|
|
10685
|
+
// fire-and-forget and this session may still be settling server-side.
|
|
10686
|
+
abortAndAbandonSession(roomState) {
|
|
10687
|
+
const client = this.client;
|
|
10688
|
+
const abandonedSessionId = roomState.sessionId;
|
|
10689
|
+
if (!client || !abandonedSessionId) {
|
|
10690
|
+
return;
|
|
10691
|
+
}
|
|
10692
|
+
roomState.forceFreshSession = true;
|
|
10693
|
+
abandon(
|
|
10694
|
+
() => client.abortSession(abandonedSessionId),
|
|
10695
|
+
(abortError) => {
|
|
10696
|
+
this.logger.warn("opencode_adapter.turn_abort_failed", {
|
|
10697
|
+
roomId: roomState.roomId,
|
|
10698
|
+
sessionId: abandonedSessionId,
|
|
10699
|
+
error: abortError
|
|
10700
|
+
});
|
|
10701
|
+
}
|
|
10702
|
+
);
|
|
10703
|
+
this.abandonSession(abandonedSessionId);
|
|
10704
|
+
}
|
|
10705
|
+
// Unroutes a session this room has given up on (a turn timeout, or a
|
|
10706
|
+
// failed interactive-prompt delivery) so a late event the still-settling
|
|
10707
|
+
// turn emits afterward — `roomStateForEvent` resolves purely off
|
|
10708
|
+
// `roomBySession`, with no notion of "this room moved on" of its own — is
|
|
10709
|
+
// dropped instead of attaching to the room's current, unrelated state.
|
|
10710
|
+
// Left to `ensureSession`'s own cleanup, this wouldn't happen until the
|
|
10711
|
+
// room's *next* turn, which can be arbitrarily later than this point.
|
|
10712
|
+
abandonSession(sessionId) {
|
|
10713
|
+
this.roomBySession.delete(sessionId);
|
|
10714
|
+
}
|
|
10715
|
+
async handleSessionError(roomState) {
|
|
10716
|
+
await this.flushTurnText(roomState);
|
|
10717
|
+
const failure = agentFailure(this.provider, roomState.lastErrorMessage ?? "OpenCode reported a session error.");
|
|
10718
|
+
await this.reportTerminalFailure(roomState, failure);
|
|
10719
|
+
}
|
|
10720
|
+
async reportTerminalFailure(roomState, failure) {
|
|
10721
|
+
if (roomState.tools) {
|
|
10722
|
+
await safeSendFailure(roomState.tools, failure, this.logger, { roomId: roomState.roomId });
|
|
9764
10723
|
}
|
|
10724
|
+
throw new ProviderTurnFailedError(failure);
|
|
9765
10725
|
}
|
|
9766
|
-
releaseTurnWait(roomState) {
|
|
9767
|
-
roomState.resolveReleaseWait?.();
|
|
10726
|
+
releaseTurnWait(roomState, outcome) {
|
|
10727
|
+
roomState.resolveReleaseWait?.(outcome);
|
|
9768
10728
|
}
|
|
9769
10729
|
finishTurn(roomState) {
|
|
9770
|
-
roomState.
|
|
9771
|
-
|
|
10730
|
+
roomState.resolveTurnOutcome?.("completed");
|
|
10731
|
+
}
|
|
10732
|
+
failInteractivePromptDelivery(roomState, error, rejectInteraction) {
|
|
10733
|
+
this.abortAndAbandonSession(roomState);
|
|
10734
|
+
if (rejectInteraction) {
|
|
10735
|
+
abandon(rejectInteraction, (rejectionError) => {
|
|
10736
|
+
this.logger.warn("opencode_adapter.interaction_rejection_failed", {
|
|
10737
|
+
roomId: roomState.roomId,
|
|
10738
|
+
error: rejectionError
|
|
10739
|
+
});
|
|
10740
|
+
});
|
|
10741
|
+
}
|
|
10742
|
+
const deliveryFailure = new DeliveryFailedError(error);
|
|
10743
|
+
this.releaseTurnWait(roomState, { kind: "delivery_failed", error: deliveryFailure });
|
|
10744
|
+
roomState.pendingDeliveryFailure = deliveryFailure;
|
|
10745
|
+
roomState.resolveTurnOutcome?.("cancelled");
|
|
9772
10746
|
}
|
|
9773
10747
|
clearTurnState(roomState, expectedTurn) {
|
|
9774
|
-
if (expectedTurn && roomState.
|
|
10748
|
+
if (expectedTurn && roomState.turnOutcome !== expectedTurn) {
|
|
9775
10749
|
return;
|
|
9776
10750
|
}
|
|
9777
10751
|
this.cancelPendingTimeout(roomState.pendingPermission);
|
|
9778
10752
|
this.cancelPendingTimeout(roomState.pendingQuestion);
|
|
9779
10753
|
roomState.pendingPermission = null;
|
|
9780
10754
|
roomState.pendingQuestion = null;
|
|
9781
|
-
roomState.
|
|
9782
|
-
roomState.
|
|
10755
|
+
roomState.resolveTurnOutcome?.("cancelled");
|
|
10756
|
+
roomState.turnOutcome = null;
|
|
10757
|
+
roomState.resolveTurnOutcome = null;
|
|
10758
|
+
roomState.resolveReleaseWait?.({ kind: "cancelled" });
|
|
9783
10759
|
roomState.releaseWait = null;
|
|
9784
10760
|
roomState.resolveReleaseWait = null;
|
|
9785
10761
|
roomState.turnTask = null;
|
|
@@ -9800,22 +10776,30 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9800
10776
|
);
|
|
9801
10777
|
roomState.persistedSessionId = roomState.sessionId;
|
|
9802
10778
|
}
|
|
9803
|
-
|
|
10779
|
+
/**
|
|
10780
|
+
* "blocked" and "empty" are both a no-op today, but `deliverFallbackText`
|
|
10781
|
+
* needs to tell them apart: only "empty" means the shared guard already
|
|
10782
|
+
* passed, so it's safe to go on and send its own fallback message.
|
|
10783
|
+
*/
|
|
10784
|
+
async flushTurnText(roomState) {
|
|
9804
10785
|
if (!roomState.tools || !this.config.fallbackSendAgentText) {
|
|
9805
|
-
return;
|
|
10786
|
+
return "blocked";
|
|
9806
10787
|
}
|
|
9807
10788
|
const text = [...roomState.textParts.values()].map((value) => value.trim()).filter((value) => value.length > 0).join("\n").trim();
|
|
9808
|
-
if (text.length
|
|
9809
|
-
|
|
9810
|
-
roomState.pendingMentions = [];
|
|
9811
|
-
return;
|
|
10789
|
+
if (text.length === 0) {
|
|
10790
|
+
return "empty";
|
|
9812
10791
|
}
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
10792
|
+
await deliverReply(roomState.tools, text, roomState.pendingMentions);
|
|
10793
|
+
roomState.pendingMentions = [];
|
|
10794
|
+
return "sent";
|
|
10795
|
+
}
|
|
10796
|
+
async deliverFallbackText(roomState) {
|
|
10797
|
+
const outcome = await this.flushTurnText(roomState);
|
|
10798
|
+
if (outcome !== "empty" || !roomState.tools) {
|
|
9816
10799
|
return;
|
|
9817
10800
|
}
|
|
9818
|
-
await
|
|
10801
|
+
await deliverReply(
|
|
10802
|
+
roomState.tools,
|
|
9819
10803
|
"OpenCode completed the turn without a text reply.",
|
|
9820
10804
|
roomState.pendingMentions
|
|
9821
10805
|
);
|
|
@@ -9829,7 +10813,7 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9829
10813
|
await roomState.tools.sendEvent(
|
|
9830
10814
|
JSON.stringify({
|
|
9831
10815
|
name: toolName,
|
|
9832
|
-
args:
|
|
10816
|
+
args: asOptionalRecord2(state.input) ?? {},
|
|
9833
10817
|
tool_call_id: callId
|
|
9834
10818
|
}),
|
|
9835
10819
|
"tool_call"
|
|
@@ -9921,21 +10905,22 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
|
|
|
9921
10905
|
lines.push("Reply with one line per question, or `reject`.");
|
|
9922
10906
|
return lines.join("\n");
|
|
9923
10907
|
}
|
|
9924
|
-
|
|
10908
|
+
toAgentFailure(error) {
|
|
9925
10909
|
if (error instanceof HttpStatusError) {
|
|
9926
|
-
|
|
10910
|
+
const body = typeof error.body === "string" ? error.body : toDisplayText2(error.body);
|
|
10911
|
+
const message = `OpenCode request failed (${error.status}): ${body}`;
|
|
10912
|
+
return agentFailure(this.provider, message, String(error.status), error.body);
|
|
9927
10913
|
}
|
|
9928
|
-
return `OpenCode failed while processing the message: ${asErrorMessage(error)}
|
|
10914
|
+
return agentFailure(this.provider, `OpenCode failed while processing the message: ${asErrorMessage(error)}`);
|
|
9929
10915
|
}
|
|
9930
10916
|
formatOpenCodeError(error) {
|
|
9931
|
-
const payload =
|
|
10917
|
+
const payload = asOptionalRecord2(error);
|
|
9932
10918
|
if (!payload) {
|
|
9933
10919
|
return "OpenCode reported an unknown error.";
|
|
9934
10920
|
}
|
|
9935
10921
|
const name = typeof payload.name === "string" ? payload.name : "OpenCodeError";
|
|
9936
|
-
const
|
|
9937
|
-
|
|
9938
|
-
return message ? `${name}: ${message}` : `${name}: OpenCode reported an error.`;
|
|
10922
|
+
const message = asNestedMessage(payload.data);
|
|
10923
|
+
return message ? `${name}: ${truncate(message)}` : `${name}: OpenCode reported an error.`;
|
|
9939
10924
|
}
|
|
9940
10925
|
};
|
|
9941
10926
|
function delay(ms) {
|
|
@@ -10010,6 +10995,7 @@ var bandMcpBridgeFactory = new LazyAsyncValue({
|
|
|
10010
10995
|
}
|
|
10011
10996
|
});
|
|
10012
10997
|
var ClaudeSDKAdapter = class extends SimpleAdapter {
|
|
10998
|
+
provider = "claude-sdk";
|
|
10013
10999
|
model;
|
|
10014
11000
|
customSection;
|
|
10015
11001
|
includeBaseInstructions;
|
|
@@ -10040,7 +11026,7 @@ var ClaudeSDKAdapter = class extends SimpleAdapter {
|
|
|
10040
11026
|
this.additionalMcpTools = options?.additionalMcpTools ?? [];
|
|
10041
11027
|
this.cwd = options?.cwd;
|
|
10042
11028
|
this.queryFnOverride = options?.queryFn;
|
|
10043
|
-
this.logger = options?.logger
|
|
11029
|
+
this.logger = resolveLogger(options?.logger);
|
|
10044
11030
|
}
|
|
10045
11031
|
async onStarted(agentName, agentDescription) {
|
|
10046
11032
|
await super.onStarted(agentName, agentDescription);
|
|
@@ -10080,6 +11066,35 @@ var ClaudeSDKAdapter = class extends SimpleAdapter {
|
|
|
10080
11066
|
}
|
|
10081
11067
|
}
|
|
10082
11068
|
async doMessage(message, tools, history, participantsMessage, contactsMessage, context) {
|
|
11069
|
+
let finalText = "";
|
|
11070
|
+
let resultFailure = null;
|
|
11071
|
+
try {
|
|
11072
|
+
const query = await this.startQuery(message, history, participantsMessage, contactsMessage, context, tools);
|
|
11073
|
+
const consumed = await this.consumeQueryEvents(query, tools, context.roomId);
|
|
11074
|
+
finalText = consumed.finalText;
|
|
11075
|
+
resultFailure = consumed.resultFailure;
|
|
11076
|
+
} catch (error) {
|
|
11077
|
+
await reportProviderTurnFailure(tools, this.logger, this.provider, "Claude SDK adapter request failed", error, { roomId: context.roomId });
|
|
11078
|
+
}
|
|
11079
|
+
const mention = [{ id: message.senderId, handle: message.senderName ?? message.senderType }];
|
|
11080
|
+
const replyText = finalText.trim();
|
|
11081
|
+
if (resultFailure) {
|
|
11082
|
+
const failure = agentFailure(this.provider, resultFailure.message, resultFailure.code, resultFailure.detail);
|
|
11083
|
+
if (replyText) {
|
|
11084
|
+
try {
|
|
11085
|
+
await deliverReply(tools, replyText, mention);
|
|
11086
|
+
} catch (error) {
|
|
11087
|
+
await safeSendFailure(tools, failure, this.logger, { roomId: context.roomId });
|
|
11088
|
+
throw error;
|
|
11089
|
+
}
|
|
11090
|
+
}
|
|
11091
|
+
await reportTurnFailure(tools, failure, this.logger, { roomId: context.roomId });
|
|
11092
|
+
}
|
|
11093
|
+
if (replyText) {
|
|
11094
|
+
await deliverReply(tools, replyText, mention);
|
|
11095
|
+
}
|
|
11096
|
+
}
|
|
11097
|
+
async startQuery(message, history, participantsMessage, contactsMessage, context, tools) {
|
|
10083
11098
|
const queryFn = this.queryFnOverride ?? await loadClaudeQuery();
|
|
10084
11099
|
const options = {
|
|
10085
11100
|
model: this.model,
|
|
@@ -10109,7 +11124,7 @@ var ClaudeSDKAdapter = class extends SimpleAdapter {
|
|
|
10109
11124
|
const roomToolHint = this.enableMcpTools ? `
|
|
10110
11125
|
|
|
10111
11126
|
[Tooling note]: For any mcp__band__* tool call, pass room_id="${context.roomId}".` : "";
|
|
10112
|
-
|
|
11127
|
+
return queryFn({
|
|
10113
11128
|
prompt: buildConversationPrompt({
|
|
10114
11129
|
history,
|
|
10115
11130
|
isSessionBootstrap: context.isSessionBootstrap,
|
|
@@ -10121,15 +11136,18 @@ var ClaudeSDKAdapter = class extends SimpleAdapter {
|
|
|
10121
11136
|
}) + roomToolHint,
|
|
10122
11137
|
options
|
|
10123
11138
|
});
|
|
11139
|
+
}
|
|
11140
|
+
async consumeQueryEvents(query, tools, roomId) {
|
|
10124
11141
|
let finalText = "";
|
|
11142
|
+
let resultFailure = null;
|
|
10125
11143
|
for await (const event of query) {
|
|
10126
11144
|
const type = event.type;
|
|
10127
11145
|
const sessionId = event.session_id;
|
|
10128
11146
|
if (typeof sessionId === "string" && sessionId) {
|
|
10129
|
-
const previousSessionId = this.sessionIds.get(
|
|
10130
|
-
this.sessionIds.set(
|
|
11147
|
+
const previousSessionId = this.sessionIds.get(roomId) ?? null;
|
|
11148
|
+
this.sessionIds.set(roomId, sessionId);
|
|
10131
11149
|
if (sessionId !== previousSessionId) {
|
|
10132
|
-
await this.reportSessionId(tools,
|
|
11150
|
+
await this.reportSessionId(tools, roomId, sessionId);
|
|
10133
11151
|
}
|
|
10134
11152
|
}
|
|
10135
11153
|
if (type === "assistant") {
|
|
@@ -10138,24 +11156,25 @@ var ClaudeSDKAdapter = class extends SimpleAdapter {
|
|
|
10138
11156
|
finalText = text;
|
|
10139
11157
|
}
|
|
10140
11158
|
}
|
|
10141
|
-
if (type === "result"
|
|
10142
|
-
|
|
11159
|
+
if (type === "result") {
|
|
11160
|
+
resultFailure = claudeNonSuccessResult(event);
|
|
11161
|
+
if (!resultFailure && typeof event.result === "string") {
|
|
11162
|
+
finalText = event.result;
|
|
11163
|
+
}
|
|
10143
11164
|
}
|
|
10144
11165
|
if (this.enableExecutionReporting && type === "tool_use_summary") {
|
|
10145
11166
|
try {
|
|
10146
11167
|
await tools.sendEvent(JSON.stringify(event), "tool_call");
|
|
10147
11168
|
} catch (error) {
|
|
10148
11169
|
this.logger.warn("Claude SDK execution reporting failed", {
|
|
10149
|
-
roomId
|
|
10150
|
-
sessionId: this.sessionIds.get(
|
|
11170
|
+
roomId,
|
|
11171
|
+
sessionId: this.sessionIds.get(roomId) ?? null,
|
|
10151
11172
|
error
|
|
10152
11173
|
});
|
|
10153
11174
|
}
|
|
10154
11175
|
}
|
|
10155
11176
|
}
|
|
10156
|
-
|
|
10157
|
-
await tools.sendMessage(finalText.trim(), [{ id: message.senderId, handle: message.senderName ?? message.senderType }]);
|
|
10158
|
-
}
|
|
11177
|
+
return { finalText, resultFailure };
|
|
10159
11178
|
}
|
|
10160
11179
|
async onCleanup(roomId) {
|
|
10161
11180
|
this.sessionIds.delete(roomId);
|
|
@@ -10187,6 +11206,27 @@ async function loadClaudeQuery() {
|
|
|
10187
11206
|
}
|
|
10188
11207
|
return module2.query;
|
|
10189
11208
|
}
|
|
11209
|
+
function claudeNonSuccessResult(event) {
|
|
11210
|
+
const flaggedError = event.is_error === true;
|
|
11211
|
+
if (!flaggedError && event.subtype === "success") {
|
|
11212
|
+
return null;
|
|
11213
|
+
}
|
|
11214
|
+
const subtype = typeof event.subtype === "string" ? event.subtype.trim() : "";
|
|
11215
|
+
const code = subtype.length === 0 || subtype === "success" ? "error" : subtype;
|
|
11216
|
+
const resultText = typeof event.result === "string" ? event.result.trim() : "";
|
|
11217
|
+
const summaryText = typeof event.summary === "string" ? event.summary.trim() : "";
|
|
11218
|
+
const message = resultText || summaryText || `Claude Agent SDK result: ${code}`;
|
|
11219
|
+
return {
|
|
11220
|
+
code,
|
|
11221
|
+
message,
|
|
11222
|
+
detail: {
|
|
11223
|
+
subtype: event.subtype ?? null,
|
|
11224
|
+
is_error: event.is_error ?? null,
|
|
11225
|
+
result: event.result ?? null,
|
|
11226
|
+
session_id: event.session_id ?? null
|
|
11227
|
+
}
|
|
11228
|
+
};
|
|
11229
|
+
}
|
|
10190
11230
|
function extractAssistantText3(event) {
|
|
10191
11231
|
if (event.type !== "assistant") {
|
|
10192
11232
|
return "";
|
|
@@ -10197,6 +11237,7 @@ function extractAssistantText3(event) {
|
|
|
10197
11237
|
|
|
10198
11238
|
// src/adapters/codex/CodexAdapter.ts
|
|
10199
11239
|
init_protocols();
|
|
11240
|
+
init_errors();
|
|
10200
11241
|
init_schemas();
|
|
10201
11242
|
|
|
10202
11243
|
// src/adapters/codex/appServerClient.ts
|
|
@@ -10241,7 +11282,7 @@ var CodexAppServerStdioClient = class {
|
|
|
10241
11282
|
} else {
|
|
10242
11283
|
this.env = void 0;
|
|
10243
11284
|
}
|
|
10244
|
-
this.logger = options?.logger
|
|
11285
|
+
this.logger = resolveLogger(options?.logger);
|
|
10245
11286
|
}
|
|
10246
11287
|
async connect() {
|
|
10247
11288
|
if (this.process) {
|
|
@@ -10285,16 +11326,22 @@ var CodexAppServerStdioClient = class {
|
|
|
10285
11326
|
await this.request("initialize", toJsonRpcParams(params));
|
|
10286
11327
|
await this.notify("initialized", {});
|
|
10287
11328
|
}
|
|
10288
|
-
async request(method, params) {
|
|
11329
|
+
async request(method, params, timeoutMs) {
|
|
10289
11330
|
const id = ++this.nextRequestId;
|
|
10290
|
-
const
|
|
10291
|
-
this.pending.set(id, {
|
|
11331
|
+
const call = new Promise((resolve, reject) => {
|
|
11332
|
+
this.pending.set(id, {
|
|
11333
|
+
resolve: (value) => resolve(value),
|
|
11334
|
+
reject
|
|
11335
|
+
});
|
|
10292
11336
|
void this.sendJson({ id, method, params: params ?? {} }).catch((error) => {
|
|
10293
|
-
this.pending.delete(id);
|
|
10294
11337
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
10295
11338
|
});
|
|
10296
11339
|
});
|
|
10297
|
-
return
|
|
11340
|
+
return withTimeout(
|
|
11341
|
+
call,
|
|
11342
|
+
timeoutMs ?? Infinity,
|
|
11343
|
+
() => new Error(`Codex app-server request "${method}" timed out after ${timeoutMs}ms.`)
|
|
11344
|
+
).finally(() => this.pending.delete(id));
|
|
10298
11345
|
}
|
|
10299
11346
|
async notify(method, params) {
|
|
10300
11347
|
await this.sendJson({ method, params: params ?? {} });
|
|
@@ -10338,11 +11385,16 @@ var CodexAppServerStdioClient = class {
|
|
|
10338
11385
|
}
|
|
10339
11386
|
proc.stdin.end();
|
|
10340
11387
|
await new Promise((resolve) => {
|
|
10341
|
-
const
|
|
11388
|
+
const termTimer = setTimeout(() => {
|
|
10342
11389
|
proc.kill("SIGTERM");
|
|
10343
11390
|
}, 500);
|
|
11391
|
+
const killTimer = setTimeout(() => {
|
|
11392
|
+
proc.kill("SIGKILL");
|
|
11393
|
+
resolve();
|
|
11394
|
+
}, 1500);
|
|
10344
11395
|
proc.once("close", () => {
|
|
10345
|
-
clearTimeout(
|
|
11396
|
+
clearTimeout(termTimer);
|
|
11397
|
+
clearTimeout(killTimer);
|
|
10346
11398
|
resolve();
|
|
10347
11399
|
});
|
|
10348
11400
|
});
|
|
@@ -10500,7 +11552,14 @@ var SILENT_REPORTING_TOOLS = /* @__PURE__ */ new Set([
|
|
|
10500
11552
|
SEND_MESSAGE_TOOL_NAME,
|
|
10501
11553
|
SEND_EVENT_TOOL_NAME
|
|
10502
11554
|
]);
|
|
11555
|
+
var CodexTurnTimeoutError = class extends Error {
|
|
11556
|
+
constructor() {
|
|
11557
|
+
super("Turn timed out");
|
|
11558
|
+
this.name = "CodexTurnTimeoutError";
|
|
11559
|
+
}
|
|
11560
|
+
};
|
|
10503
11561
|
var CodexAdapter = class extends SimpleAdapter {
|
|
11562
|
+
provider = "codex";
|
|
10504
11563
|
baseConfig;
|
|
10505
11564
|
roomConfigOverrides = /* @__PURE__ */ new Map();
|
|
10506
11565
|
customTools;
|
|
@@ -10538,7 +11597,7 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10538
11597
|
this.customToolIndex = buildCustomToolIndex(this.customTools);
|
|
10539
11598
|
this.includeMemoryTools = options?.includeMemoryTools ?? false;
|
|
10540
11599
|
this.factoryOverride = options?.factory;
|
|
10541
|
-
this.logger = options?.logger
|
|
11600
|
+
this.logger = resolveLogger(options?.logger);
|
|
10542
11601
|
this.debugEnabled = options?.config?.debug ?? false;
|
|
10543
11602
|
}
|
|
10544
11603
|
async onStarted(agentName, agentDescription) {
|
|
@@ -10571,17 +11630,7 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10571
11630
|
}
|
|
10572
11631
|
}
|
|
10573
11632
|
}
|
|
10574
|
-
this.
|
|
10575
|
-
const client = await this.ensureClient();
|
|
10576
|
-
this.debug("codex_adapter.client.ensure.done", { roomId: context.roomId });
|
|
10577
|
-
const threadId = await this.getOrCreateThread(
|
|
10578
|
-
context.roomId,
|
|
10579
|
-
tools,
|
|
10580
|
-
history,
|
|
10581
|
-
context.isSessionBootstrap,
|
|
10582
|
-
message.metadata?.linear_reset_room_session !== true,
|
|
10583
|
-
config
|
|
10584
|
-
);
|
|
11633
|
+
const { client, threadId } = await this.ensureClientAndThread(context, tools, history, message, config);
|
|
10585
11634
|
this.debug("codex_adapter.thread.ready", {
|
|
10586
11635
|
roomId: context.roomId,
|
|
10587
11636
|
threadId
|
|
@@ -10609,73 +11658,170 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10609
11658
|
threadId,
|
|
10610
11659
|
toolNames
|
|
10611
11660
|
});
|
|
10612
|
-
const turnStarted =
|
|
10613
|
-
"turn/start",
|
|
10614
|
-
toRpcParams(turnParams)
|
|
10615
|
-
));
|
|
10616
|
-
if (!turnStarted) {
|
|
10617
|
-
throw new Error("Codex returned an invalid turn/start payload");
|
|
10618
|
-
}
|
|
11661
|
+
const turnStarted = await this.startTurn(client, turnParams, tools, context.roomId);
|
|
10619
11662
|
this.debug("codex_adapter.turn.started", {
|
|
10620
11663
|
roomId: context.roomId,
|
|
10621
11664
|
threadId,
|
|
10622
11665
|
turnId: turnStarted.turn.id
|
|
10623
11666
|
});
|
|
10624
11667
|
const turnId = turnStarted.turn.id;
|
|
11668
|
+
const { finalText, sawSendMessageTool, turnStatus, turnError, reportedFailureInLoop } = await this.runEventLoop(
|
|
11669
|
+
client,
|
|
11670
|
+
threadId,
|
|
11671
|
+
turnId,
|
|
11672
|
+
tools,
|
|
11673
|
+
config,
|
|
11674
|
+
context.roomId
|
|
11675
|
+
);
|
|
11676
|
+
await this.emitTurnOutcome({
|
|
11677
|
+
tools,
|
|
11678
|
+
message,
|
|
11679
|
+
roomId: context.roomId,
|
|
11680
|
+
threadId,
|
|
11681
|
+
turnId,
|
|
11682
|
+
turnStatus,
|
|
11683
|
+
turnError,
|
|
11684
|
+
finalText,
|
|
11685
|
+
sawSendMessageTool,
|
|
11686
|
+
reportedFailureInLoop,
|
|
11687
|
+
fallbackSendAgentText: config.fallbackSendAgentText ?? true
|
|
11688
|
+
});
|
|
11689
|
+
this.debug("codex_adapter.turn.completed", {
|
|
11690
|
+
roomId: context.roomId,
|
|
11691
|
+
threadId,
|
|
11692
|
+
turnId,
|
|
11693
|
+
turnStatus,
|
|
11694
|
+
turnError
|
|
11695
|
+
});
|
|
11696
|
+
}
|
|
11697
|
+
async ensureClientAndThread(context, tools, history, message, config) {
|
|
11698
|
+
let client = null;
|
|
11699
|
+
try {
|
|
11700
|
+
this.debug("codex_adapter.client.ensure.start", { roomId: context.roomId });
|
|
11701
|
+
client = await this.ensureClient();
|
|
11702
|
+
this.debug("codex_adapter.client.ensure.done", { roomId: context.roomId });
|
|
11703
|
+
const threadId = await this.getOrCreateThread(
|
|
11704
|
+
client,
|
|
11705
|
+
context.roomId,
|
|
11706
|
+
tools,
|
|
11707
|
+
history,
|
|
11708
|
+
context.isSessionBootstrap,
|
|
11709
|
+
message.metadata?.linear_reset_room_session !== true,
|
|
11710
|
+
config
|
|
11711
|
+
);
|
|
11712
|
+
return { client, threadId };
|
|
11713
|
+
} catch (error) {
|
|
11714
|
+
await this.evictOnTransportFailure(error, client);
|
|
11715
|
+
return reportTurnFailure(tools, agentFailure(this.provider, asErrorMessage(error)), this.logger, { roomId: context.roomId });
|
|
11716
|
+
}
|
|
11717
|
+
}
|
|
11718
|
+
// A provider call like any other, and deliberately not folded into
|
|
11719
|
+
// ensureClientAndThread's catch: a bad model id or revoked auth reaching
|
|
11720
|
+
// the queue as a bare error must fail only this turn, not stop every room.
|
|
11721
|
+
async startTurn(client, turnParams, tools, roomId) {
|
|
11722
|
+
const logContext = { roomId, threadId: turnParams.threadId };
|
|
11723
|
+
let turnStarted;
|
|
11724
|
+
try {
|
|
11725
|
+
turnStarted = parseTurnStartResponse(await client.request(
|
|
11726
|
+
"turn/start",
|
|
11727
|
+
toRpcParams(turnParams)
|
|
11728
|
+
));
|
|
11729
|
+
} catch (error) {
|
|
11730
|
+
await this.evictOnTransportFailure(error, client);
|
|
11731
|
+
return reportTurnFailure(tools, agentFailure(this.provider, asErrorMessage(error)), this.logger, logContext);
|
|
11732
|
+
}
|
|
11733
|
+
if (!turnStarted) {
|
|
11734
|
+
return reportTurnFailure(tools, agentFailure(this.provider, "Codex returned an invalid turn/start payload."), this.logger, logContext);
|
|
11735
|
+
}
|
|
11736
|
+
return turnStarted;
|
|
11737
|
+
}
|
|
11738
|
+
async runEventLoop(client, threadId, turnId, tools, config, roomId) {
|
|
10625
11739
|
let finalText = "";
|
|
10626
11740
|
let sawSendMessageTool = false;
|
|
10627
11741
|
let turnStatus = "failed";
|
|
10628
11742
|
let turnError = "";
|
|
11743
|
+
let reportedFailureInLoop = false;
|
|
10629
11744
|
while (true) {
|
|
10630
11745
|
let event;
|
|
10631
11746
|
try {
|
|
10632
|
-
event = await
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
11747
|
+
event = await withTimeout(
|
|
11748
|
+
client.recvEvent(),
|
|
11749
|
+
config.turnTimeoutMs ?? Infinity,
|
|
11750
|
+
() => new CodexTurnTimeoutError()
|
|
11751
|
+
);
|
|
11752
|
+
} catch (error) {
|
|
11753
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
11754
|
+
if (error instanceof CodexTurnTimeoutError) {
|
|
11755
|
+
if (turnId) {
|
|
11756
|
+
abandon(
|
|
11757
|
+
() => {
|
|
11758
|
+
const interrupt = { threadId, turnId };
|
|
11759
|
+
return client.request("turn/interrupt", toRpcParams(interrupt), config.turnTimeoutMs);
|
|
11760
|
+
},
|
|
11761
|
+
(interruptError) => {
|
|
11762
|
+
this.logger.warn("codex_adapter.turn_interrupt_failed", {
|
|
11763
|
+
threadId,
|
|
11764
|
+
turnId,
|
|
11765
|
+
error: interruptError
|
|
11766
|
+
});
|
|
11767
|
+
}
|
|
11768
|
+
);
|
|
11769
|
+
this.abandonClient(client);
|
|
10644
11770
|
}
|
|
11771
|
+
turnStatus = "interrupted";
|
|
11772
|
+
turnError = "Turn timed out";
|
|
11773
|
+
break;
|
|
10645
11774
|
}
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
11775
|
+
this.abandonClient(client);
|
|
11776
|
+
return reportTurnFailure(
|
|
11777
|
+
tools,
|
|
11778
|
+
agentFailure(this.provider, asErrorMessage(error)),
|
|
11779
|
+
this.logger,
|
|
11780
|
+
{ roomId, threadId, turnId }
|
|
11781
|
+
);
|
|
10649
11782
|
}
|
|
10650
11783
|
if (event.kind === "request") {
|
|
10651
|
-
|
|
10652
|
-
|
|
10653
|
-
|
|
10654
|
-
|
|
10655
|
-
|
|
10656
|
-
|
|
10657
|
-
|
|
10658
|
-
|
|
11784
|
+
try {
|
|
11785
|
+
const usedSendMessage = await this.handleServerRequest({
|
|
11786
|
+
client,
|
|
11787
|
+
tools,
|
|
11788
|
+
roomId,
|
|
11789
|
+
event,
|
|
11790
|
+
enableExecutionReporting: config.enableExecutionReporting ?? false
|
|
11791
|
+
});
|
|
11792
|
+
sawSendMessageTool = sawSendMessageTool || usedSendMessage;
|
|
11793
|
+
} catch (error) {
|
|
11794
|
+
rethrowIfRecoverableTurnFailure(error);
|
|
11795
|
+
await this.evictOnTransportFailure(error, client);
|
|
11796
|
+
return reportTurnFailure(
|
|
11797
|
+
tools,
|
|
11798
|
+
agentFailure(this.provider, asErrorMessage(error)),
|
|
11799
|
+
this.logger,
|
|
11800
|
+
{ roomId, threadId, turnId }
|
|
11801
|
+
);
|
|
11802
|
+
}
|
|
10659
11803
|
continue;
|
|
10660
11804
|
}
|
|
10661
11805
|
if (event.method === "transport/closed") {
|
|
10662
11806
|
turnStatus = "failed";
|
|
10663
11807
|
turnError = "Codex transport closed unexpectedly";
|
|
10664
|
-
|
|
11808
|
+
this.abandonClient(client);
|
|
10665
11809
|
break;
|
|
10666
11810
|
}
|
|
10667
|
-
const params =
|
|
11811
|
+
const params = asOptionalRecord2(event.params) ?? {};
|
|
10668
11812
|
if (event.method === "error") {
|
|
10669
|
-
const error =
|
|
11813
|
+
const error = asOptionalRecord2(params.error) ?? {};
|
|
10670
11814
|
const errorMessage = asNonEmptyString2(error.message) ?? "Unknown Codex error";
|
|
10671
11815
|
if (params.willRetry === true) {
|
|
10672
|
-
this.logger.warn("codex_adapter.retryable_error", { error: errorMessage, roomId
|
|
11816
|
+
this.logger.warn("codex_adapter.retryable_error", { error: errorMessage, roomId });
|
|
10673
11817
|
} else {
|
|
10674
|
-
await
|
|
10675
|
-
|
|
10676
|
-
|
|
10677
|
-
|
|
10678
|
-
|
|
11818
|
+
await safeSendFailure(
|
|
11819
|
+
tools,
|
|
11820
|
+
agentFailure(this.provider, errorMessage, asString(error.code), error),
|
|
11821
|
+
this.logger,
|
|
11822
|
+
{ roomId }
|
|
11823
|
+
);
|
|
11824
|
+
reportedFailureInLoop = true;
|
|
10679
11825
|
}
|
|
10680
11826
|
continue;
|
|
10681
11827
|
}
|
|
@@ -10696,7 +11842,7 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10696
11842
|
continue;
|
|
10697
11843
|
}
|
|
10698
11844
|
await this.emitItemCompletedEvents(tools, item, {
|
|
10699
|
-
roomId
|
|
11845
|
+
roomId,
|
|
10700
11846
|
threadId,
|
|
10701
11847
|
turnId,
|
|
10702
11848
|
enableExecutionReporting: config.enableExecutionReporting ?? false,
|
|
@@ -10708,7 +11854,7 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10708
11854
|
const turn = parseTurnRef(params.turn);
|
|
10709
11855
|
if (!turn) {
|
|
10710
11856
|
this.logger.warn("codex_adapter.invalid_turn_completed_payload", {
|
|
10711
|
-
roomId
|
|
11857
|
+
roomId,
|
|
10712
11858
|
threadId,
|
|
10713
11859
|
turnId
|
|
10714
11860
|
});
|
|
@@ -10723,25 +11869,7 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10723
11869
|
break;
|
|
10724
11870
|
}
|
|
10725
11871
|
}
|
|
10726
|
-
|
|
10727
|
-
tools,
|
|
10728
|
-
message,
|
|
10729
|
-
roomId: context.roomId,
|
|
10730
|
-
threadId,
|
|
10731
|
-
turnId,
|
|
10732
|
-
turnStatus,
|
|
10733
|
-
turnError,
|
|
10734
|
-
finalText,
|
|
10735
|
-
sawSendMessageTool,
|
|
10736
|
-
fallbackSendAgentText: config.fallbackSendAgentText ?? true
|
|
10737
|
-
});
|
|
10738
|
-
this.debug("codex_adapter.turn.completed", {
|
|
10739
|
-
roomId: context.roomId,
|
|
10740
|
-
threadId,
|
|
10741
|
-
turnId,
|
|
10742
|
-
turnStatus,
|
|
10743
|
-
turnError
|
|
10744
|
-
});
|
|
11872
|
+
return { finalText, sawSendMessageTool, turnStatus, turnError, reportedFailureInLoop };
|
|
10745
11873
|
}
|
|
10746
11874
|
async onCleanup(roomId) {
|
|
10747
11875
|
this.roomThreadIds.delete(roomId);
|
|
@@ -10818,18 +11946,64 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10818
11946
|
})();
|
|
10819
11947
|
return await this.clientPromise;
|
|
10820
11948
|
}
|
|
10821
|
-
|
|
11949
|
+
takeClientForReset(expectedClient) {
|
|
10822
11950
|
const client = this.client;
|
|
11951
|
+
if (expectedClient && client !== expectedClient) {
|
|
11952
|
+
return null;
|
|
11953
|
+
}
|
|
10823
11954
|
this.client = null;
|
|
10824
11955
|
this.clientPromise = null;
|
|
10825
|
-
|
|
10826
|
-
|
|
10827
|
-
|
|
10828
|
-
|
|
10829
|
-
|
|
10830
|
-
|
|
10831
|
-
|
|
11956
|
+
for (const roomId of [
|
|
11957
|
+
...this.roomThreadIds.keys(),
|
|
11958
|
+
...this.roomThreadInitPromises.keys()
|
|
11959
|
+
]) {
|
|
11960
|
+
this.needsHistoryInjection.add(roomId);
|
|
11961
|
+
}
|
|
11962
|
+
this.roomThreadIds.clear();
|
|
11963
|
+
this.roomThreadInitPromises.clear();
|
|
11964
|
+
return client;
|
|
11965
|
+
}
|
|
11966
|
+
// Runtime stop still awaits process exit. Failure/timeout paths must not:
|
|
11967
|
+
// production `close()` only SIGTERMs after 500ms and never SIGKILLs, so a
|
|
11968
|
+
// wedged child would hold the failure report and retry hostage.
|
|
11969
|
+
async resetClient(expectedClient) {
|
|
11970
|
+
const client = this.takeClientForReset(expectedClient);
|
|
11971
|
+
if (!client) {
|
|
11972
|
+
return;
|
|
11973
|
+
}
|
|
11974
|
+
try {
|
|
11975
|
+
await client.close();
|
|
11976
|
+
} catch (error) {
|
|
11977
|
+
this.logger.warn("codex_adapter.client_close_failed", {
|
|
11978
|
+
error
|
|
11979
|
+
});
|
|
11980
|
+
}
|
|
11981
|
+
}
|
|
11982
|
+
abandonClient(expectedClient) {
|
|
11983
|
+
const client = this.takeClientForReset(expectedClient);
|
|
11984
|
+
if (!client) {
|
|
11985
|
+
return;
|
|
11986
|
+
}
|
|
11987
|
+
abandon(
|
|
11988
|
+
() => client.close(),
|
|
11989
|
+
(error) => {
|
|
11990
|
+
this.logger.warn("codex_adapter.client_close_failed", { error });
|
|
10832
11991
|
}
|
|
11992
|
+
);
|
|
11993
|
+
}
|
|
11994
|
+
// `CodexJsonRpcError` is a real answer from a live app-server — only this
|
|
11995
|
+
// specific request was rejected (bad model id, revoked auth, ...), so the
|
|
11996
|
+
// client is still healthy and every other room's turn can keep using it.
|
|
11997
|
+
// Anything else — the transport closing mid-request, a write failing on a
|
|
11998
|
+
// dead pipe, `request`'s own timeout — means this client can no longer be
|
|
11999
|
+
// trusted for any future call, and must not be handed to the next turn by
|
|
12000
|
+
// `ensureClient()`. `runEventLoop`'s own `transport/closed` handling only
|
|
12001
|
+
// catches this once a turn reaches the event loop at all; a closure that
|
|
12002
|
+
// instead rejects `getOrCreateThread`'s or `startTurn`'s RPC first (before
|
|
12003
|
+
// any turn gets that far) would otherwise never evict the closed client.
|
|
12004
|
+
async evictOnTransportFailure(error, failedClient) {
|
|
12005
|
+
if (failedClient && !(error instanceof CodexJsonRpcError)) {
|
|
12006
|
+
this.abandonClient(failedClient);
|
|
10833
12007
|
}
|
|
10834
12008
|
}
|
|
10835
12009
|
debug(message, context) {
|
|
@@ -10838,7 +12012,7 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10838
12012
|
}
|
|
10839
12013
|
this.logger.info(message, context);
|
|
10840
12014
|
}
|
|
10841
|
-
async getOrCreateThread(roomId, tools, history, isSessionBootstrap, allowHistoryThreadResume, config) {
|
|
12015
|
+
async getOrCreateThread(client, roomId, tools, history, isSessionBootstrap, allowHistoryThreadResume, config) {
|
|
10842
12016
|
const existing = this.roomThreadIds.get(roomId);
|
|
10843
12017
|
if (existing) {
|
|
10844
12018
|
return existing;
|
|
@@ -10848,7 +12022,6 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10848
12022
|
return await pending;
|
|
10849
12023
|
}
|
|
10850
12024
|
const initPromise = (async () => {
|
|
10851
|
-
const client = await this.ensureClient();
|
|
10852
12025
|
const resumeThreadId = isSessionBootstrap && allowHistoryThreadResume ? extractThreadIdFromHistory(history.raw) : null;
|
|
10853
12026
|
if (resumeThreadId) {
|
|
10854
12027
|
try {
|
|
@@ -10860,10 +12033,15 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10860
12033
|
throw new Error("Codex returned an invalid thread/resume payload");
|
|
10861
12034
|
}
|
|
10862
12035
|
const threadId2 = resumed.thread.id;
|
|
12036
|
+
this.assertCurrentClient(client);
|
|
10863
12037
|
this.roomThreadIds.set(roomId, threadId2);
|
|
10864
12038
|
await this.sendThreadMappingEvent(tools, roomId, threadId2, "resumed");
|
|
12039
|
+
this.assertCurrentClient(client);
|
|
10865
12040
|
return threadId2;
|
|
10866
12041
|
} catch (error) {
|
|
12042
|
+
if (this.client !== client) {
|
|
12043
|
+
throw error;
|
|
12044
|
+
}
|
|
10867
12045
|
this.logger.warn("codex_adapter.thread_resume_failed", {
|
|
10868
12046
|
roomId,
|
|
10869
12047
|
threadId: resumeThreadId,
|
|
@@ -10883,8 +12061,10 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10883
12061
|
throw new Error("Codex returned an invalid thread/start payload");
|
|
10884
12062
|
}
|
|
10885
12063
|
const threadId = started.thread.id;
|
|
12064
|
+
this.assertCurrentClient(client);
|
|
10886
12065
|
this.roomThreadIds.set(roomId, threadId);
|
|
10887
12066
|
await this.sendThreadMappingEvent(tools, roomId, threadId, "mapped");
|
|
12067
|
+
this.assertCurrentClient(client);
|
|
10888
12068
|
return threadId;
|
|
10889
12069
|
})();
|
|
10890
12070
|
this.roomThreadInitPromises.set(roomId, initPromise);
|
|
@@ -10896,6 +12076,11 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
10896
12076
|
}
|
|
10897
12077
|
}
|
|
10898
12078
|
}
|
|
12079
|
+
assertCurrentClient(client) {
|
|
12080
|
+
if (this.client !== client) {
|
|
12081
|
+
throw new Error("Codex client was replaced during thread initialization.");
|
|
12082
|
+
}
|
|
12083
|
+
}
|
|
10899
12084
|
buildThreadStartParams(tools, config) {
|
|
10900
12085
|
return {
|
|
10901
12086
|
model: config.model ?? null,
|
|
@@ -11155,7 +12340,7 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
11155
12340
|
];
|
|
11156
12341
|
}
|
|
11157
12342
|
if (item.type === "mcpToolCall") {
|
|
11158
|
-
const args =
|
|
12343
|
+
const args = asOptionalRecord2(item.arguments) ?? {};
|
|
11159
12344
|
const output = item.result ?? item.error ?? "completed";
|
|
11160
12345
|
return [`mcp:${item.server}/${item.tool}`, args, toWireString(output)];
|
|
11161
12346
|
}
|
|
@@ -11195,16 +12380,29 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
11195
12380
|
const mention = this.currentMention(input.message);
|
|
11196
12381
|
if (input.turnStatus === "completed") {
|
|
11197
12382
|
if (input.fallbackSendAgentText && input.finalText.trim() && !input.sawSendMessageTool) {
|
|
11198
|
-
await input.tools
|
|
12383
|
+
await deliverReply(input.tools, input.finalText.trim(), mention);
|
|
11199
12384
|
}
|
|
11200
12385
|
return;
|
|
11201
12386
|
}
|
|
11202
12387
|
if (input.turnStatus === "interrupted") {
|
|
11203
|
-
|
|
11204
|
-
|
|
12388
|
+
const interrupted = "I stopped before completing this request.";
|
|
12389
|
+
const failure2 = agentFailure(this.provider, input.turnError || interrupted, FAILURE_CODE_TIMEOUT);
|
|
12390
|
+
return this.reportAndDeliverTurnFailure(input.tools, failure2, input.roomId, interrupted, mention, input.reportedFailureInLoop);
|
|
11205
12391
|
}
|
|
11206
12392
|
const errorText = input.turnError ? `I couldn't complete this request (${input.turnStatus}): ${input.turnError}` : `I couldn't complete this request (${input.turnStatus}).`;
|
|
11207
|
-
|
|
12393
|
+
const failure = agentFailure(this.provider, errorText, input.turnStatus);
|
|
12394
|
+
return this.reportAndDeliverTurnFailure(input.tools, failure, input.roomId, errorText, mention, input.reportedFailureInLoop);
|
|
12395
|
+
}
|
|
12396
|
+
// Shared by every non-"completed" branch of `emitTurnOutcome`: report the
|
|
12397
|
+
// failure (unless a mid-loop `error` event already reported this same
|
|
12398
|
+
// incident), reply with the mentioned, human-readable text alongside it —
|
|
12399
|
+
// `safeSendFailure` cannot throw, so the machine-readable record lands even
|
|
12400
|
+
// when the reply after it does not — then throw so PlatformRuntime marks
|
|
12401
|
+
// the turn failed and retries it, instead of flipping it to "processed".
|
|
12402
|
+
async reportAndDeliverTurnFailure(tools, failure, roomId, replyText, mention, reportedFailureInLoop) {
|
|
12403
|
+
const failureReport = reportedFailureInLoop ? Promise.resolve() : safeSendFailure(tools, failure, this.logger, { roomId });
|
|
12404
|
+
await Promise.all([failureReport, deliverReply(tools, replyText, mention)]);
|
|
12405
|
+
throw new ProviderTurnFailedError(failure);
|
|
11208
12406
|
}
|
|
11209
12407
|
extractTurnError(error) {
|
|
11210
12408
|
if (!error) {
|
|
@@ -11220,97 +12418,131 @@ var CodexAdapter = class extends SimpleAdapter {
|
|
|
11220
12418
|
}
|
|
11221
12419
|
async handleLocalCommand(input) {
|
|
11222
12420
|
const mention = this.currentMention(input.message);
|
|
11223
|
-
|
|
11224
|
-
|
|
11225
|
-
|
|
11226
|
-
|
|
11227
|
-
|
|
11228
|
-
|
|
11229
|
-
|
|
12421
|
+
switch (input.command) {
|
|
12422
|
+
case "help":
|
|
12423
|
+
await this.handleHelpCommand(input.tools, mention);
|
|
12424
|
+
return true;
|
|
12425
|
+
case "status":
|
|
12426
|
+
await this.handleStatusCommand(input.tools, input.roomId, input.history, mention);
|
|
12427
|
+
return true;
|
|
12428
|
+
case "model":
|
|
12429
|
+
case "models":
|
|
12430
|
+
await this.handleModelCommand(input.tools, input.roomId, input.args, mention);
|
|
12431
|
+
return true;
|
|
12432
|
+
case "reasoning":
|
|
12433
|
+
await this.handleReasoningCommand(input.tools, input.roomId, input.args, mention);
|
|
12434
|
+
return true;
|
|
12435
|
+
default:
|
|
12436
|
+
return false;
|
|
11230
12437
|
}
|
|
11231
|
-
|
|
11232
|
-
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
|
|
12438
|
+
}
|
|
12439
|
+
async handleHelpCommand(tools, mention) {
|
|
12440
|
+
await deliverReply(
|
|
12441
|
+
tools,
|
|
12442
|
+
"Codex commands: `/status`, `/model`, `/models`, `/model list`, `/models list`, `/model <id>`, `/reasoning [low|medium|high|xhigh]`, `/help`.",
|
|
12443
|
+
mention
|
|
12444
|
+
);
|
|
12445
|
+
}
|
|
12446
|
+
async handleStatusCommand(tools, roomId, history, mention) {
|
|
12447
|
+
const roomConfig = this.getConfig(roomId);
|
|
12448
|
+
const mappedThreadId = this.roomThreadIds.get(roomId) ?? extractThreadIdFromHistory(history.raw);
|
|
12449
|
+
await deliverReply(
|
|
12450
|
+
tools,
|
|
12451
|
+
[
|
|
12452
|
+
"Codex status:",
|
|
12453
|
+
`- selected_model: ${roomConfig.model ?? "default"}`,
|
|
12454
|
+
`- room_id: ${roomId}`,
|
|
12455
|
+
`- thread_id: ${mappedThreadId ?? "not mapped"}`,
|
|
12456
|
+
`- approval_policy: ${String(roomConfig.approvalPolicy ?? "never")}`,
|
|
12457
|
+
`- sandbox_mode: ${roomConfig.sandboxMode ?? "workspace-write"}`,
|
|
12458
|
+
`- reasoning_effort: ${roomConfig.reasoningEffort ?? "default"}`
|
|
12459
|
+
].join("\n"),
|
|
12460
|
+
mention
|
|
12461
|
+
);
|
|
12462
|
+
}
|
|
12463
|
+
async handleModelCommand(tools, roomId, args, mention) {
|
|
12464
|
+
const arg = args.trim();
|
|
12465
|
+
if (!arg) {
|
|
12466
|
+
const roomConfig = this.getConfig(roomId);
|
|
12467
|
+
await deliverReply(
|
|
12468
|
+
tools,
|
|
12469
|
+
`Current model: \`${roomConfig.model ?? "default"}\`. Use \`/model list\` or \`/model <id>\`.`,
|
|
11243
12470
|
mention
|
|
11244
12471
|
);
|
|
11245
|
-
return
|
|
12472
|
+
return;
|
|
11246
12473
|
}
|
|
11247
|
-
if (
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
}
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
|
|
11266
|
-
|
|
11267
|
-
|
|
11268
|
-
|
|
11269
|
-
|
|
11270
|
-
|
|
11271
|
-
|
|
11272
|
-
|
|
11273
|
-
|
|
11274
|
-
|
|
11275
|
-
|
|
11276
|
-
|
|
11277
|
-
|
|
11278
|
-
|
|
11279
|
-
|
|
11280
|
-
|
|
11281
|
-
|
|
11282
|
-
|
|
12474
|
+
if (arg === "list" || arg === "ls") {
|
|
12475
|
+
await this.handleModelListCommand(tools, roomId, mention);
|
|
12476
|
+
return;
|
|
12477
|
+
}
|
|
12478
|
+
const overrides = this.roomConfigOverrides.get(roomId) ?? {};
|
|
12479
|
+
overrides.model = arg;
|
|
12480
|
+
this.roomConfigOverrides.set(roomId, overrides);
|
|
12481
|
+
await deliverReply(
|
|
12482
|
+
tools,
|
|
12483
|
+
`Model override set to \`${arg}\` for subsequent turns.`,
|
|
12484
|
+
mention
|
|
12485
|
+
);
|
|
12486
|
+
}
|
|
12487
|
+
// The one local command that reaches Codex, and local commands run before
|
|
12488
|
+
// onMessage's failure catch — so it reports its own, or a `/model list`
|
|
12489
|
+
// against a downed app server stops every room.
|
|
12490
|
+
async handleModelListCommand(tools, roomId, mention) {
|
|
12491
|
+
let response;
|
|
12492
|
+
let client = null;
|
|
12493
|
+
try {
|
|
12494
|
+
client = await this.ensureClient();
|
|
12495
|
+
response = await client.request("model/list", {});
|
|
12496
|
+
} catch (error) {
|
|
12497
|
+
await this.evictOnTransportFailure(error, client);
|
|
12498
|
+
return reportTurnFailure(tools, agentFailure(this.provider, asErrorMessage(error)), this.logger, { roomId });
|
|
12499
|
+
}
|
|
12500
|
+
const result = parseModelListResponse(response);
|
|
12501
|
+
if (!result) {
|
|
12502
|
+
await deliverReply(tools, "Received an invalid model list from Codex.", mention);
|
|
12503
|
+
return;
|
|
12504
|
+
}
|
|
12505
|
+
const visible = result.data.filter((entry) => !entry.hidden);
|
|
12506
|
+
if (visible.length === 0) {
|
|
12507
|
+
await deliverReply(tools, "No visible models returned by Codex.", mention);
|
|
12508
|
+
return;
|
|
12509
|
+
}
|
|
12510
|
+
await deliverReply(
|
|
12511
|
+
tools,
|
|
12512
|
+
[
|
|
12513
|
+
"Available models:",
|
|
12514
|
+
...visible.map((entry) => `- \`${entry.id}\`${entry.isDefault ? " (default)" : ""}`)
|
|
12515
|
+
].join("\n"),
|
|
12516
|
+
mention
|
|
12517
|
+
);
|
|
12518
|
+
}
|
|
12519
|
+
async handleReasoningCommand(tools, roomId, args, mention) {
|
|
12520
|
+
const effort = args.trim().toLowerCase();
|
|
12521
|
+
if (!effort) {
|
|
12522
|
+
const roomConfig = this.getConfig(roomId);
|
|
12523
|
+
await deliverReply(
|
|
12524
|
+
tools,
|
|
12525
|
+
`Current reasoning effort: \`${roomConfig.reasoningEffort ?? "default"}\`. Use \`/reasoning ${CODEX_REASONING_EFFORTS.join("|")}\`.`,
|
|
11283
12526
|
mention
|
|
11284
12527
|
);
|
|
11285
|
-
return
|
|
12528
|
+
return;
|
|
11286
12529
|
}
|
|
11287
|
-
if (
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
await input.tools.sendMessage(
|
|
11292
|
-
`Current reasoning effort: \`${roomConfig.reasoningEffort ?? "default"}\`. Use \`/reasoning ${CODEX_REASONING_EFFORTS.join("|")}\`.`,
|
|
11293
|
-
mention
|
|
11294
|
-
);
|
|
11295
|
-
return true;
|
|
11296
|
-
}
|
|
11297
|
-
if (!CODEX_REASONING_EFFORTS.includes(effort)) {
|
|
11298
|
-
await input.tools.sendMessage(
|
|
11299
|
-
`Invalid reasoning effort \`${effort}\`. Valid values: ${CODEX_REASONING_EFFORTS.join(", ")}.`,
|
|
11300
|
-
mention
|
|
11301
|
-
);
|
|
11302
|
-
return true;
|
|
11303
|
-
}
|
|
11304
|
-
const overrides = this.roomConfigOverrides.get(input.roomId) ?? {};
|
|
11305
|
-
overrides.reasoningEffort = effort;
|
|
11306
|
-
this.roomConfigOverrides.set(input.roomId, overrides);
|
|
11307
|
-
await input.tools.sendMessage(
|
|
11308
|
-
`Reasoning effort set to \`${effort}\` for subsequent turns.`,
|
|
12530
|
+
if (!CODEX_REASONING_EFFORTS.includes(effort)) {
|
|
12531
|
+
await deliverReply(
|
|
12532
|
+
tools,
|
|
12533
|
+
`Invalid reasoning effort \`${effort}\`. Valid values: ${CODEX_REASONING_EFFORTS.join(", ")}.`,
|
|
11309
12534
|
mention
|
|
11310
12535
|
);
|
|
11311
|
-
return
|
|
12536
|
+
return;
|
|
11312
12537
|
}
|
|
11313
|
-
|
|
12538
|
+
const overrides = this.roomConfigOverrides.get(roomId) ?? {};
|
|
12539
|
+
overrides.reasoningEffort = effort;
|
|
12540
|
+
this.roomConfigOverrides.set(roomId, overrides);
|
|
12541
|
+
await deliverReply(
|
|
12542
|
+
tools,
|
|
12543
|
+
`Reasoning effort set to \`${effort}\` for subsequent turns.`,
|
|
12544
|
+
mention
|
|
12545
|
+
);
|
|
11314
12546
|
}
|
|
11315
12547
|
async safeSendEvent(tools, content, messageType, metadata) {
|
|
11316
12548
|
try {
|
|
@@ -11435,7 +12667,7 @@ function toRpcParams(value) {
|
|
|
11435
12667
|
return value;
|
|
11436
12668
|
}
|
|
11437
12669
|
function parseDynamicToolCallParams(value) {
|
|
11438
|
-
const record =
|
|
12670
|
+
const record = asOptionalRecord2(value);
|
|
11439
12671
|
if (!record) {
|
|
11440
12672
|
return null;
|
|
11441
12673
|
}
|
|
@@ -11451,11 +12683,11 @@ function parseDynamicToolCallParams(value) {
|
|
|
11451
12683
|
turnId,
|
|
11452
12684
|
callId,
|
|
11453
12685
|
tool: tool2,
|
|
11454
|
-
arguments:
|
|
12686
|
+
arguments: asOptionalRecord2(record.arguments) ?? {}
|
|
11455
12687
|
};
|
|
11456
12688
|
}
|
|
11457
12689
|
function parseThreadItem(value) {
|
|
11458
|
-
const record =
|
|
12690
|
+
const record = asOptionalRecord2(value);
|
|
11459
12691
|
if (!record) {
|
|
11460
12692
|
return null;
|
|
11461
12693
|
}
|
|
@@ -11481,7 +12713,7 @@ function parseTurnErrorInfo(value) {
|
|
|
11481
12713
|
}
|
|
11482
12714
|
return { message: message2, additionalDetails: null };
|
|
11483
12715
|
}
|
|
11484
|
-
const record =
|
|
12716
|
+
const record = asOptionalRecord2(value);
|
|
11485
12717
|
if (!record) {
|
|
11486
12718
|
return null;
|
|
11487
12719
|
}
|
|
@@ -11501,7 +12733,7 @@ function parseTurnStatus(value) {
|
|
|
11501
12733
|
return TURN_STATUS_VALUES.has(value) ? value : null;
|
|
11502
12734
|
}
|
|
11503
12735
|
function parseTurnRef(value) {
|
|
11504
|
-
const record =
|
|
12736
|
+
const record = asOptionalRecord2(value);
|
|
11505
12737
|
if (!record) {
|
|
11506
12738
|
return null;
|
|
11507
12739
|
}
|
|
@@ -11517,7 +12749,7 @@ function parseTurnRef(value) {
|
|
|
11517
12749
|
};
|
|
11518
12750
|
}
|
|
11519
12751
|
function parseTurnStartResponse(value) {
|
|
11520
|
-
const record =
|
|
12752
|
+
const record = asOptionalRecord2(value);
|
|
11521
12753
|
if (!record) {
|
|
11522
12754
|
return null;
|
|
11523
12755
|
}
|
|
@@ -11528,11 +12760,11 @@ function parseTurnStartResponse(value) {
|
|
|
11528
12760
|
return { turn };
|
|
11529
12761
|
}
|
|
11530
12762
|
function parseThreadResponse(value) {
|
|
11531
|
-
const record =
|
|
12763
|
+
const record = asOptionalRecord2(value);
|
|
11532
12764
|
if (!record) {
|
|
11533
12765
|
return null;
|
|
11534
12766
|
}
|
|
11535
|
-
const thread =
|
|
12767
|
+
const thread = asOptionalRecord2(record.thread);
|
|
11536
12768
|
if (!thread) {
|
|
11537
12769
|
return null;
|
|
11538
12770
|
}
|
|
@@ -11547,7 +12779,7 @@ function parseThreadResponse(value) {
|
|
|
11547
12779
|
};
|
|
11548
12780
|
}
|
|
11549
12781
|
function parseModelListResponse(value) {
|
|
11550
|
-
const record =
|
|
12782
|
+
const record = asOptionalRecord2(value);
|
|
11551
12783
|
if (!record) {
|
|
11552
12784
|
return null;
|
|
11553
12785
|
}
|
|
@@ -11555,7 +12787,7 @@ function parseModelListResponse(value) {
|
|
|
11555
12787
|
return null;
|
|
11556
12788
|
}
|
|
11557
12789
|
const data = record.data.map((entry) => {
|
|
11558
|
-
const item =
|
|
12790
|
+
const item = asOptionalRecord2(entry);
|
|
11559
12791
|
if (!item) {
|
|
11560
12792
|
return null;
|
|
11561
12793
|
}
|
|
@@ -11590,6 +12822,9 @@ function parseModelListResponse(value) {
|
|
|
11590
12822
|
CodexAdapter,
|
|
11591
12823
|
CodexAppServerStdioClient,
|
|
11592
12824
|
CodexJsonRpcError,
|
|
12825
|
+
CopilotACPAdapter,
|
|
12826
|
+
DEFAULT_COPILOT_ACP_COMMAND,
|
|
12827
|
+
DEFAULT_OMP_ACP_COMMAND,
|
|
11593
12828
|
GatewayHistoryConverter,
|
|
11594
12829
|
GatewayServer,
|
|
11595
12830
|
GeminiAdapter,
|
|
@@ -11601,6 +12836,7 @@ function parseModelListResponse(value) {
|
|
|
11601
12836
|
LangGraphAdapter,
|
|
11602
12837
|
LettaAdapter,
|
|
11603
12838
|
LettaHistoryConverter,
|
|
12839
|
+
OmpACPAdapter,
|
|
11604
12840
|
OpenAIAdapter,
|
|
11605
12841
|
OpenAIToolCallingModel,
|
|
11606
12842
|
OpencodeAdapter,
|