acpx 0.10.0 → 0.11.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 +22 -19
- package/dist/{cli-8dP_TqBp.js → cli-CC2w0U-A.js} +4 -4
- package/dist/{cli-8dP_TqBp.js.map → cli-CC2w0U-A.js.map} +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +299 -61
- package/dist/cli.js.map +1 -1
- package/dist/{client-C4iJBO0j.d.ts → client-j3sLnpcM.d.ts} +27 -4
- package/dist/client-j3sLnpcM.d.ts.map +1 -0
- package/dist/{flags--2oX_ubW.js → flags-BKjjl3tF.js} +4 -4
- package/dist/flags-BKjjl3tF.js.map +1 -0
- package/dist/{flows-e4umXVbY.js → flows-BabqiU0u.js} +4 -3
- package/dist/flows-BabqiU0u.js.map +1 -0
- package/dist/flows.d.ts +1 -1
- package/dist/flows.d.ts.map +1 -1
- package/dist/flows.js +1 -1
- package/dist/{live-checkpoint-CuFft_Nd.js → live-checkpoint-BZrk9Mjz.js} +646 -164
- package/dist/live-checkpoint-BZrk9Mjz.js.map +1 -0
- package/dist/{output-Di77Yugq.js → output-D_BGt1YI.js} +170 -88
- package/dist/output-D_BGt1YI.js.map +1 -0
- package/dist/runtime.d.ts +71 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +188 -32
- package/dist/runtime.js.map +1 -1
- package/dist/{session-options-Bh1bIqQ2.d.ts → session-options-Co1oGEK8.d.ts} +15 -2
- package/dist/{session-options-Bh1bIqQ2.d.ts.map → session-options-Co1oGEK8.d.ts.map} +1 -1
- package/package.json +17 -17
- package/skills/acpx/SKILL.md +55 -3
- package/dist/client-C4iJBO0j.d.ts.map +0 -1
- package/dist/flags--2oX_ubW.js.map +0 -1
- package/dist/flows-e4umXVbY.js.map +0 -1
- package/dist/live-checkpoint-CuFft_Nd.js.map +0 -1
- package/dist/output-Di77Yugq.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fs, { statSync } from "node:fs";
|
|
1
|
+
import fs, { readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import fs$1 from "node:fs/promises";
|
|
@@ -6,7 +6,7 @@ import os from "node:os";
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import { execFile, spawn } from "node:child_process";
|
|
8
8
|
import { Readable, Writable } from "node:stream";
|
|
9
|
-
import { ClientSideConnection, PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
|
9
|
+
import { ClientSideConnection, PROTOCOL_VERSION, RequestError } from "@agentclientprotocol/sdk";
|
|
10
10
|
import readline from "node:readline/promises";
|
|
11
11
|
import { promisify } from "node:util";
|
|
12
12
|
//#region src/errors.ts
|
|
@@ -223,12 +223,12 @@ const SESSION_RECORD_SCHEMA = "acpx.session.v1";
|
|
|
223
223
|
//#endregion
|
|
224
224
|
//#region src/acp/error-shapes.ts
|
|
225
225
|
const RESOURCE_NOT_FOUND_ACP_CODES = new Set([-32001, -32002]);
|
|
226
|
-
function asRecord$
|
|
226
|
+
function asRecord$8(value) {
|
|
227
227
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
228
228
|
return value;
|
|
229
229
|
}
|
|
230
230
|
function toAcpErrorPayload(value) {
|
|
231
|
-
const record = asRecord$
|
|
231
|
+
const record = asRecord$8(value);
|
|
232
232
|
if (!record) return;
|
|
233
233
|
if (typeof record.code !== "number" || !Number.isFinite(record.code)) return;
|
|
234
234
|
if (typeof record.message !== "string" || record.message.length === 0) return;
|
|
@@ -242,7 +242,7 @@ function extractAcpErrorInternal(value, depth) {
|
|
|
242
242
|
if (depth > 5) return;
|
|
243
243
|
const direct = toAcpErrorPayload(value);
|
|
244
244
|
if (direct) return direct;
|
|
245
|
-
const record = asRecord$
|
|
245
|
+
const record = asRecord$8(value);
|
|
246
246
|
if (!record) return;
|
|
247
247
|
return extractNestedAcpError(record, depth);
|
|
248
248
|
}
|
|
@@ -277,7 +277,7 @@ function hasSessionNotFoundHint(value, depth = 0) {
|
|
|
277
277
|
if (depth > 4) return false;
|
|
278
278
|
if (isSessionNotFoundText(value)) return true;
|
|
279
279
|
if (Array.isArray(value)) return value.some((entry) => hasSessionNotFoundHint(entry, depth + 1));
|
|
280
|
-
const record = asRecord$
|
|
280
|
+
const record = asRecord$8(value);
|
|
281
281
|
if (!record) return false;
|
|
282
282
|
return Object.values(record).some((entry) => hasSessionNotFoundHint(entry, depth + 1));
|
|
283
283
|
}
|
|
@@ -297,7 +297,7 @@ function isAcpResourceNotFoundError(error) {
|
|
|
297
297
|
//#region src/acp/error-normalization.ts
|
|
298
298
|
const AUTH_REQUIRED_ACP_CODES = new Set([-32e3]);
|
|
299
299
|
const QUERY_CLOSED_BEFORE_RESPONSE_DETAIL = "query closed before response received";
|
|
300
|
-
function asRecord$
|
|
300
|
+
function asRecord$7(value) {
|
|
301
301
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
302
302
|
return value;
|
|
303
303
|
}
|
|
@@ -318,7 +318,7 @@ function isAcpAuthRequiredPayload(acp) {
|
|
|
318
318
|
if (!acp) return false;
|
|
319
319
|
if (!AUTH_REQUIRED_ACP_CODES.has(acp.code)) return false;
|
|
320
320
|
if (isAuthRequiredMessage(acp.message)) return true;
|
|
321
|
-
const data = asRecord$
|
|
321
|
+
const data = asRecord$7(acp.data);
|
|
322
322
|
if (!data) return false;
|
|
323
323
|
return hasAuthRequiredData(data);
|
|
324
324
|
}
|
|
@@ -338,7 +338,7 @@ function isOutputErrorOrigin(value) {
|
|
|
338
338
|
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
339
339
|
}
|
|
340
340
|
function readOutputErrorMeta(error) {
|
|
341
|
-
const record = asRecord$
|
|
341
|
+
const record = asRecord$7(error);
|
|
342
342
|
if (!record) return {};
|
|
343
343
|
return {
|
|
344
344
|
outputCode: isOutputErrorCode(record.outputCode) ? record.outputCode : void 0,
|
|
@@ -356,7 +356,7 @@ function isNoSessionLike(error) {
|
|
|
356
356
|
}
|
|
357
357
|
function isUsageLike(error) {
|
|
358
358
|
if (!(error instanceof Error)) return false;
|
|
359
|
-
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord$
|
|
359
|
+
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord$7(error)?.code === "commander.invalidArgument";
|
|
360
360
|
}
|
|
361
361
|
function formatErrorMessage(error) {
|
|
362
362
|
return formatUnknownErrorMessage(error);
|
|
@@ -364,7 +364,7 @@ function formatErrorMessage(error) {
|
|
|
364
364
|
function isAcpQueryClosedBeforeResponseError(error) {
|
|
365
365
|
const acp = extractAcpError(error);
|
|
366
366
|
if (!acp || acp.code !== -32603) return false;
|
|
367
|
-
const details = asRecord$
|
|
367
|
+
const details = asRecord$7(acp.data)?.details;
|
|
368
368
|
if (typeof details !== "string") return false;
|
|
369
369
|
return details.toLowerCase().includes(QUERY_CLOSED_BEFORE_RESPONSE_DETAIL);
|
|
370
370
|
}
|
|
@@ -432,7 +432,8 @@ function exitCodeForOutputErrorCode(code) {
|
|
|
432
432
|
const ACP_ADAPTER_PACKAGE_RANGES = {
|
|
433
433
|
pi: "^0.0.26",
|
|
434
434
|
codex: "^0.0.44",
|
|
435
|
-
claude: "^0.
|
|
435
|
+
claude: "^0.37.0",
|
|
436
|
+
mux: "^0.27.0"
|
|
436
437
|
};
|
|
437
438
|
const AGENT_REGISTRY = {
|
|
438
439
|
pi: `npx pi-acp@${ACP_ADAPTER_PACKAGE_RANGES.pi}`,
|
|
@@ -443,10 +444,12 @@ const AGENT_REGISTRY = {
|
|
|
443
444
|
cursor: "cursor-agent acp",
|
|
444
445
|
copilot: "copilot --acp --stdio",
|
|
445
446
|
droid: "droid exec --output-format acp",
|
|
447
|
+
"fast-agent": "uvx fast-agent-mcp acp",
|
|
446
448
|
iflow: "iflow --experimental-acp",
|
|
447
449
|
kilocode: "npx -y @kilocode/cli acp",
|
|
448
450
|
kimi: "kimi acp",
|
|
449
451
|
kiro: "kiro-cli-chat acp",
|
|
452
|
+
mux: `npx -y mux@${ACP_ADAPTER_PACKAGE_RANGES.mux} acp`,
|
|
450
453
|
opencode: "npx -y opencode-ai acp",
|
|
451
454
|
qoder: "qodercli --acp",
|
|
452
455
|
qwen: "qwen --acp",
|
|
@@ -658,7 +661,7 @@ var PromptInputValidationError = class extends Error {
|
|
|
658
661
|
this.name = "PromptInputValidationError";
|
|
659
662
|
}
|
|
660
663
|
};
|
|
661
|
-
function asRecord$
|
|
664
|
+
function asRecord$6(value) {
|
|
662
665
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
663
666
|
return value;
|
|
664
667
|
}
|
|
@@ -676,28 +679,28 @@ function isAudioMimeType(value) {
|
|
|
676
679
|
return /^audio\/[A-Za-z0-9.+-]+$/i.test(value);
|
|
677
680
|
}
|
|
678
681
|
function isTextBlock(value) {
|
|
679
|
-
const record = asRecord$
|
|
682
|
+
const record = asRecord$6(value);
|
|
680
683
|
return record?.type === "text" && typeof record.text === "string";
|
|
681
684
|
}
|
|
682
685
|
function isImageBlock(value) {
|
|
683
|
-
const record = asRecord$
|
|
686
|
+
const record = asRecord$6(value);
|
|
684
687
|
return record?.type === "image" && isNonEmptyString(record.mimeType) && isImageMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
|
|
685
688
|
}
|
|
686
689
|
function isAudioBlock(value) {
|
|
687
|
-
const record = asRecord$
|
|
690
|
+
const record = asRecord$6(value);
|
|
688
691
|
return record?.type === "audio" && isNonEmptyString(record.mimeType) && isAudioMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
|
|
689
692
|
}
|
|
690
693
|
function isResourceLinkBlock(value) {
|
|
691
|
-
const record = asRecord$
|
|
694
|
+
const record = asRecord$6(value);
|
|
692
695
|
return record?.type === "resource_link" && isNonEmptyString(record.uri) && (record.title === void 0 || typeof record.title === "string") && (record.name === void 0 || typeof record.name === "string");
|
|
693
696
|
}
|
|
694
697
|
function isResourcePayload(value) {
|
|
695
|
-
const record = asRecord$
|
|
698
|
+
const record = asRecord$6(value);
|
|
696
699
|
if (!record || !isNonEmptyString(record.uri)) return false;
|
|
697
700
|
return record.text === void 0 || typeof record.text === "string";
|
|
698
701
|
}
|
|
699
702
|
function isResourceBlock(value) {
|
|
700
|
-
const record = asRecord$
|
|
703
|
+
const record = asRecord$6(value);
|
|
701
704
|
return record?.type === "resource" && isResourcePayload(record.resource);
|
|
702
705
|
}
|
|
703
706
|
const CONTENT_BLOCK_VALIDATORS = [
|
|
@@ -741,11 +744,11 @@ function validateResourceLinkContentBlock(record, index) {
|
|
|
741
744
|
if (record.name !== void 0 && typeof record.name !== "string") return `prompt[${index}] resource_link block name must be a string when present`;
|
|
742
745
|
}
|
|
743
746
|
function validateResourceContentBlock(record, index) {
|
|
744
|
-
if (!asRecord$
|
|
747
|
+
if (!asRecord$6(record.resource)) return `prompt[${index}] resource block must include a resource object`;
|
|
745
748
|
return isResourcePayload(record.resource) ? void 0 : `prompt[${index}] resource block resource must include a non-empty uri and optional text`;
|
|
746
749
|
}
|
|
747
750
|
function getContentBlockValidationError(value, index) {
|
|
748
|
-
const record = asRecord$
|
|
751
|
+
const record = asRecord$6(value);
|
|
749
752
|
if (!record || typeof record.type !== "string") return `prompt[${index}] must be an ACP content block object`;
|
|
750
753
|
const validator = contentBlockErrorValidator(record.type);
|
|
751
754
|
return validator ? validator(record, index) : `prompt[${index}] has unsupported content block type ${JSON.stringify(record.type)}`;
|
|
@@ -828,7 +831,7 @@ function resourceBlockDisplayText(block) {
|
|
|
828
831
|
}
|
|
829
832
|
//#endregion
|
|
830
833
|
//#region src/acp/jsonrpc.ts
|
|
831
|
-
function asRecord$
|
|
834
|
+
function asRecord$5(value) {
|
|
832
835
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
833
836
|
return value;
|
|
834
837
|
}
|
|
@@ -836,7 +839,7 @@ function hasValidId(value) {
|
|
|
836
839
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
837
840
|
}
|
|
838
841
|
function isErrorObject(value) {
|
|
839
|
-
const record = asRecord$
|
|
842
|
+
const record = asRecord$5(value);
|
|
840
843
|
return !!record && typeof record.code === "number" && Number.isFinite(record.code) && typeof record.message === "string";
|
|
841
844
|
}
|
|
842
845
|
function hasResultOrError(value) {
|
|
@@ -861,7 +864,7 @@ function isJsonRpcResponse(value) {
|
|
|
861
864
|
return hasResultOrError(value);
|
|
862
865
|
}
|
|
863
866
|
function isAcpJsonRpcMessage(value) {
|
|
864
|
-
const record = asRecord$
|
|
867
|
+
const record = asRecord$5(value);
|
|
865
868
|
if (!record || record.jsonrpc !== "2.0") return false;
|
|
866
869
|
return isJsonRpcNotificationRecord(record) || isJsonRpcRequest(record) || isJsonRpcResponse(record);
|
|
867
870
|
}
|
|
@@ -873,11 +876,11 @@ function isSessionUpdateNotification(message) {
|
|
|
873
876
|
}
|
|
874
877
|
function extractSessionUpdateNotification(message) {
|
|
875
878
|
if (!isSessionUpdateNotification(message)) return;
|
|
876
|
-
const params = asRecord$
|
|
879
|
+
const params = asRecord$5(message.params);
|
|
877
880
|
if (!params) return;
|
|
878
881
|
const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
|
|
879
882
|
if (!sessionId) return;
|
|
880
|
-
const update = asRecord$
|
|
883
|
+
const update = asRecord$5(params.update);
|
|
881
884
|
if (!update || typeof update.sessionUpdate !== "string") return;
|
|
882
885
|
return {
|
|
883
886
|
sessionId,
|
|
@@ -886,13 +889,13 @@ function extractSessionUpdateNotification(message) {
|
|
|
886
889
|
}
|
|
887
890
|
function parsePromptStopReason(message) {
|
|
888
891
|
if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
|
|
889
|
-
const record = asRecord$
|
|
892
|
+
const record = asRecord$5(message.result);
|
|
890
893
|
if (!record) return;
|
|
891
894
|
return typeof record.stopReason === "string" ? record.stopReason : void 0;
|
|
892
895
|
}
|
|
893
896
|
function parseJsonRpcErrorMessage(message) {
|
|
894
897
|
if (!Object.hasOwn(message, "error")) return;
|
|
895
|
-
const errorRecord = asRecord$
|
|
898
|
+
const errorRecord = asRecord$5(message.error);
|
|
896
899
|
if (!errorRecord || typeof errorRecord.message !== "string") return;
|
|
897
900
|
return errorRecord.message;
|
|
898
901
|
}
|
|
@@ -987,6 +990,7 @@ function serializeSessionRecordForDisk(record) {
|
|
|
987
990
|
messages: canonical.messages,
|
|
988
991
|
updated_at: canonical.updated_at,
|
|
989
992
|
cumulative_token_usage: canonical.cumulative_token_usage,
|
|
993
|
+
cumulative_cost: canonical.cumulative_cost,
|
|
990
994
|
request_token_usage: canonical.request_token_usage,
|
|
991
995
|
acpx: canonical.acpx,
|
|
992
996
|
imported_from: canonical.importedFrom ? {
|
|
@@ -999,7 +1003,7 @@ function serializeSessionRecordForDisk(record) {
|
|
|
999
1003
|
}
|
|
1000
1004
|
//#endregion
|
|
1001
1005
|
//#region src/session/persistence/parse.ts
|
|
1002
|
-
function asRecord$
|
|
1006
|
+
function asRecord$4(value) {
|
|
1003
1007
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
1004
1008
|
return value;
|
|
1005
1009
|
}
|
|
@@ -1009,16 +1013,55 @@ function hasOwn$1(source, key) {
|
|
|
1009
1013
|
function isStringArray(value) {
|
|
1010
1014
|
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
1011
1015
|
}
|
|
1016
|
+
function hasModelConfigOption(options) {
|
|
1017
|
+
if (!Array.isArray(options)) return false;
|
|
1018
|
+
return options.some((entry) => {
|
|
1019
|
+
const option = asRecord$4(entry);
|
|
1020
|
+
return option?.category === "model" || option?.id === "model";
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
function parseConfigOptions(raw) {
|
|
1024
|
+
if (!Array.isArray(raw) || !raw.every((entry) => asRecord$4(entry) !== void 0)) return;
|
|
1025
|
+
return raw;
|
|
1026
|
+
}
|
|
1027
|
+
function parseAvailableCommand(raw) {
|
|
1028
|
+
if (typeof raw === "string") {
|
|
1029
|
+
const name = raw.trim();
|
|
1030
|
+
return name ? { name } : void 0;
|
|
1031
|
+
}
|
|
1032
|
+
const record = asRecord$4(raw);
|
|
1033
|
+
if (!record) return;
|
|
1034
|
+
const name = parseNonEmptyString(record.name);
|
|
1035
|
+
if (!name) return;
|
|
1036
|
+
const description = parseNonEmptyString(record.description);
|
|
1037
|
+
return {
|
|
1038
|
+
name,
|
|
1039
|
+
...description ? { description } : {},
|
|
1040
|
+
...typeof record.has_input === "boolean" ? { has_input: record.has_input } : {}
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
function parseAvailableCommands(raw) {
|
|
1044
|
+
if (!Array.isArray(raw)) return;
|
|
1045
|
+
const commands = raw.map((entry) => parseAvailableCommand(entry)).filter((entry) => entry !== void 0);
|
|
1046
|
+
return commands.length > 0 ? commands : void 0;
|
|
1047
|
+
}
|
|
1048
|
+
function parseNonEmptyString(value) {
|
|
1049
|
+
if (typeof value !== "string") return;
|
|
1050
|
+
const trimmed = value.trim();
|
|
1051
|
+
return trimmed ? trimmed : void 0;
|
|
1052
|
+
}
|
|
1012
1053
|
function parseTokenUsage(raw) {
|
|
1013
1054
|
if (raw === void 0 || raw === null) return;
|
|
1014
|
-
const record = asRecord$
|
|
1055
|
+
const record = asRecord$4(raw);
|
|
1015
1056
|
if (!record) return null;
|
|
1016
1057
|
const usage = {};
|
|
1017
1058
|
for (const field of [
|
|
1018
1059
|
"input_tokens",
|
|
1019
1060
|
"output_tokens",
|
|
1020
1061
|
"cache_creation_input_tokens",
|
|
1021
|
-
"cache_read_input_tokens"
|
|
1062
|
+
"cache_read_input_tokens",
|
|
1063
|
+
"thought_tokens",
|
|
1064
|
+
"total_tokens"
|
|
1022
1065
|
]) {
|
|
1023
1066
|
const value = record[field];
|
|
1024
1067
|
if (value === void 0) continue;
|
|
@@ -1030,9 +1073,35 @@ function parseTokenUsage(raw) {
|
|
|
1030
1073
|
function isNonNegativeFiniteNumber(value) {
|
|
1031
1074
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1032
1075
|
}
|
|
1076
|
+
function parseUsageCost(raw) {
|
|
1077
|
+
if (raw === void 0 || raw === null) return;
|
|
1078
|
+
const record = asRecord$4(raw);
|
|
1079
|
+
if (!record) return null;
|
|
1080
|
+
return parseUsageCostRecord(record);
|
|
1081
|
+
}
|
|
1082
|
+
function parseUsageCostRecord(record) {
|
|
1083
|
+
const amount = parseCostAmount(record.amount);
|
|
1084
|
+
const currency = parseCostCurrency(record.currency);
|
|
1085
|
+
if (amount === null || currency === null) return null;
|
|
1086
|
+
const cost = {
|
|
1087
|
+
...amount !== void 0 ? { amount } : {},
|
|
1088
|
+
...currency !== void 0 ? { currency } : {}
|
|
1089
|
+
};
|
|
1090
|
+
return Object.keys(cost).length > 0 ? cost : void 0;
|
|
1091
|
+
}
|
|
1092
|
+
function parseCostAmount(value) {
|
|
1093
|
+
if (value === void 0) return;
|
|
1094
|
+
return isNonNegativeFiniteNumber(value) ? value : null;
|
|
1095
|
+
}
|
|
1096
|
+
function parseCostCurrency(value) {
|
|
1097
|
+
if (value === void 0) return;
|
|
1098
|
+
if (typeof value !== "string") return null;
|
|
1099
|
+
const currency = value.trim();
|
|
1100
|
+
return currency.length > 0 ? currency : void 0;
|
|
1101
|
+
}
|
|
1033
1102
|
function parseRequestTokenUsage(raw) {
|
|
1034
1103
|
if (raw === void 0 || raw === null) return;
|
|
1035
|
-
const record = asRecord$
|
|
1104
|
+
const record = asRecord$4(raw);
|
|
1036
1105
|
if (!record) return null;
|
|
1037
1106
|
const usage = {};
|
|
1038
1107
|
for (const [key, value] of Object.entries(record)) {
|
|
@@ -1043,25 +1112,25 @@ function parseRequestTokenUsage(raw) {
|
|
|
1043
1112
|
return usage;
|
|
1044
1113
|
}
|
|
1045
1114
|
function isSessionMessageImage(raw) {
|
|
1046
|
-
const record = asRecord$
|
|
1115
|
+
const record = asRecord$4(raw);
|
|
1047
1116
|
if (!record || typeof record.source !== "string") return false;
|
|
1048
1117
|
if (record.size === void 0 || record.size === null) return true;
|
|
1049
|
-
const size = asRecord$
|
|
1118
|
+
const size = asRecord$4(record.size);
|
|
1050
1119
|
return !!size && isFiniteNumber(size.width) && isFiniteNumber(size.height);
|
|
1051
1120
|
}
|
|
1052
1121
|
function isSessionMessageAudio(raw) {
|
|
1053
|
-
const record = asRecord$
|
|
1122
|
+
const record = asRecord$4(raw);
|
|
1054
1123
|
return !!record && typeof record.source === "string" && typeof record.mime_type === "string";
|
|
1055
1124
|
}
|
|
1056
1125
|
function isFiniteNumber(value) {
|
|
1057
1126
|
return typeof value === "number" && Number.isFinite(value);
|
|
1058
1127
|
}
|
|
1059
1128
|
function isUserContent(raw) {
|
|
1060
|
-
const record = asRecord$
|
|
1129
|
+
const record = asRecord$4(raw);
|
|
1061
1130
|
if (!record) return false;
|
|
1062
1131
|
if (typeof record.Text === "string") return true;
|
|
1063
1132
|
if (record.Mention !== void 0) {
|
|
1064
|
-
const mention = asRecord$
|
|
1133
|
+
const mention = asRecord$4(record.Mention);
|
|
1065
1134
|
return !!mention && typeof mention.uri === "string" && typeof mention.content === "string";
|
|
1066
1135
|
}
|
|
1067
1136
|
if (record.Image !== void 0) return isSessionMessageImage(record.Image);
|
|
@@ -1069,7 +1138,7 @@ function isUserContent(raw) {
|
|
|
1069
1138
|
return false;
|
|
1070
1139
|
}
|
|
1071
1140
|
function isToolUse(raw) {
|
|
1072
|
-
const record = asRecord$
|
|
1141
|
+
const record = asRecord$4(raw);
|
|
1073
1142
|
return !!record && hasStringFields(record, [
|
|
1074
1143
|
"id",
|
|
1075
1144
|
"name",
|
|
@@ -1083,18 +1152,18 @@ function isOptionalString(value) {
|
|
|
1083
1152
|
return value === void 0 || value === null || typeof value === "string";
|
|
1084
1153
|
}
|
|
1085
1154
|
function isToolResultContent(raw) {
|
|
1086
|
-
const record = asRecord$
|
|
1155
|
+
const record = asRecord$4(raw);
|
|
1087
1156
|
if (!record) return false;
|
|
1088
1157
|
if (typeof record.Text === "string") return true;
|
|
1089
1158
|
if (record.Image !== void 0) return isSessionMessageImage(record.Image);
|
|
1090
1159
|
return false;
|
|
1091
1160
|
}
|
|
1092
1161
|
function isToolResult(raw) {
|
|
1093
|
-
const record = asRecord$
|
|
1162
|
+
const record = asRecord$4(raw);
|
|
1094
1163
|
return !!record && typeof record.tool_use_id === "string" && typeof record.tool_name === "string" && typeof record.is_error === "boolean" && isToolResultContent(record.content);
|
|
1095
1164
|
}
|
|
1096
1165
|
function isAgentContent(raw) {
|
|
1097
|
-
const record = asRecord$
|
|
1166
|
+
const record = asRecord$4(raw);
|
|
1098
1167
|
if (!record) return false;
|
|
1099
1168
|
if (typeof record.Text === "string") return true;
|
|
1100
1169
|
if (record.Thinking !== void 0) return isThinkingContent(record.Thinking);
|
|
@@ -1103,21 +1172,21 @@ function isAgentContent(raw) {
|
|
|
1103
1172
|
return false;
|
|
1104
1173
|
}
|
|
1105
1174
|
function isThinkingContent(raw) {
|
|
1106
|
-
const thinking = asRecord$
|
|
1175
|
+
const thinking = asRecord$4(raw);
|
|
1107
1176
|
return !!thinking && typeof thinking.text === "string" && isOptionalString(thinking.signature);
|
|
1108
1177
|
}
|
|
1109
1178
|
function isUserMessage$1(raw) {
|
|
1110
|
-
const record = asRecord$
|
|
1179
|
+
const record = asRecord$4(raw);
|
|
1111
1180
|
if (!record || record.User === void 0) return false;
|
|
1112
|
-
const user = asRecord$
|
|
1181
|
+
const user = asRecord$4(record.User);
|
|
1113
1182
|
return !!user && typeof user.id === "string" && Array.isArray(user.content) && user.content.every((entry) => isUserContent(entry));
|
|
1114
1183
|
}
|
|
1115
1184
|
function isAgentMessage$1(raw) {
|
|
1116
|
-
const record = asRecord$
|
|
1185
|
+
const record = asRecord$4(raw);
|
|
1117
1186
|
if (!record || record.Agent === void 0) return false;
|
|
1118
|
-
const agent = asRecord$
|
|
1187
|
+
const agent = asRecord$4(record.Agent);
|
|
1119
1188
|
if (!agent || !Array.isArray(agent.content) || !agent.content.every(isAgentContent)) return false;
|
|
1120
|
-
const toolResults = asRecord$
|
|
1189
|
+
const toolResults = asRecord$4(agent.tool_results);
|
|
1121
1190
|
if (!toolResults) return false;
|
|
1122
1191
|
return Object.values(toolResults).every(isToolResult);
|
|
1123
1192
|
}
|
|
@@ -1129,13 +1198,15 @@ function parseConversationRecord(record) {
|
|
|
1129
1198
|
const title = parseConversationTitle(record.title);
|
|
1130
1199
|
if (title === INVALID_VALUE) return;
|
|
1131
1200
|
const cumulativeTokenUsage = parseTokenUsage(record.cumulative_token_usage);
|
|
1201
|
+
const cumulativeCost = parseUsageCost(record.cumulative_cost);
|
|
1132
1202
|
const requestTokenUsage = parseRequestTokenUsage(record.request_token_usage);
|
|
1133
|
-
if (cumulativeTokenUsage === null || requestTokenUsage === null) return;
|
|
1203
|
+
if (cumulativeTokenUsage === null || cumulativeCost === null || requestTokenUsage === null) return;
|
|
1134
1204
|
return {
|
|
1135
1205
|
title,
|
|
1136
1206
|
messages: record.messages,
|
|
1137
1207
|
updated_at: record.updated_at,
|
|
1138
1208
|
cumulative_token_usage: cumulativeTokenUsage ?? {},
|
|
1209
|
+
cumulative_cost: cumulativeCost,
|
|
1139
1210
|
request_token_usage: requestTokenUsage ?? {}
|
|
1140
1211
|
};
|
|
1141
1212
|
}
|
|
@@ -1148,20 +1219,27 @@ function hasValidConversationCore(record) {
|
|
|
1148
1219
|
return Array.isArray(record.messages) && record.messages.every(isConversationMessage) && typeof record.updated_at === "string";
|
|
1149
1220
|
}
|
|
1150
1221
|
function parseAcpxState(raw) {
|
|
1151
|
-
const record = asRecord$
|
|
1222
|
+
const record = asRecord$4(raw);
|
|
1152
1223
|
if (!record) return;
|
|
1153
1224
|
const state = {};
|
|
1154
1225
|
assignBooleanTrue(state, "reset_on_next_ensure", record.reset_on_next_ensure);
|
|
1155
1226
|
assignStringState(state, "current_mode_id", record.current_mode_id);
|
|
1156
1227
|
assignStringState(state, "desired_mode_id", record.desired_mode_id);
|
|
1157
1228
|
assignDesiredConfigOptions(state, record.desired_config_options);
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
if (
|
|
1161
|
-
if (Array.isArray(record.config_options)) state.config_options = record.config_options;
|
|
1229
|
+
assignParsedModelState(state, record);
|
|
1230
|
+
const availableCommands = parseAvailableCommands(record.available_commands);
|
|
1231
|
+
if (availableCommands) state.available_commands = availableCommands;
|
|
1162
1232
|
assignParsedSessionOptions(state, record.session_options);
|
|
1163
1233
|
return state;
|
|
1164
1234
|
}
|
|
1235
|
+
function assignParsedModelState(state, record) {
|
|
1236
|
+
assignStringState(state, "current_model_id", record.current_model_id);
|
|
1237
|
+
if (isStringArray(record.available_models)) state.available_models = [...record.available_models];
|
|
1238
|
+
if (record.model_control === "config_option" || record.model_control === "legacy_set_model") state.model_control = record.model_control;
|
|
1239
|
+
const configOptions = parseConfigOptions(record.config_options);
|
|
1240
|
+
if (configOptions) state.config_options = configOptions;
|
|
1241
|
+
if (state.model_control === void 0 && state.available_models !== void 0) state.model_control = hasModelConfigOption(state.config_options) ? "config_option" : "legacy_set_model";
|
|
1242
|
+
}
|
|
1165
1243
|
function assignBooleanTrue(state, key, value) {
|
|
1166
1244
|
if (value === true) state[key] = true;
|
|
1167
1245
|
}
|
|
@@ -1169,7 +1247,7 @@ function assignStringState(state, key, value) {
|
|
|
1169
1247
|
if (typeof value === "string") state[key] = value;
|
|
1170
1248
|
}
|
|
1171
1249
|
function assignDesiredConfigOptions(state, raw) {
|
|
1172
|
-
const desiredConfigOptions = asRecord$
|
|
1250
|
+
const desiredConfigOptions = asRecord$4(raw);
|
|
1173
1251
|
if (!desiredConfigOptions) return;
|
|
1174
1252
|
const parsed = Object.fromEntries(Object.entries(desiredConfigOptions).filter((entry) => {
|
|
1175
1253
|
const [, value] = entry;
|
|
@@ -1178,7 +1256,7 @@ function assignDesiredConfigOptions(state, raw) {
|
|
|
1178
1256
|
if (Object.keys(parsed).length > 0) state.desired_config_options = parsed;
|
|
1179
1257
|
}
|
|
1180
1258
|
function assignParsedSessionOptions(state, raw) {
|
|
1181
|
-
const sessionOptions = asRecord$
|
|
1259
|
+
const sessionOptions = asRecord$4(raw);
|
|
1182
1260
|
if (!sessionOptions) return;
|
|
1183
1261
|
const parsedSessionOptions = {};
|
|
1184
1262
|
assignSessionOptionModel(parsedSessionOptions, sessionOptions.model);
|
|
@@ -1201,11 +1279,11 @@ function assignSessionOptionSystemPrompt(options, value) {
|
|
|
1201
1279
|
options.system_prompt = value;
|
|
1202
1280
|
return;
|
|
1203
1281
|
}
|
|
1204
|
-
const appendRecord = asRecord$
|
|
1282
|
+
const appendRecord = asRecord$4(value);
|
|
1205
1283
|
if (appendRecord && typeof appendRecord.append === "string" && appendRecord.append.length > 0) options.system_prompt = { append: appendRecord.append };
|
|
1206
1284
|
}
|
|
1207
1285
|
function parseEventLog(raw, sessionId) {
|
|
1208
|
-
const record = asRecord$
|
|
1286
|
+
const record = asRecord$4(raw);
|
|
1209
1287
|
if (!record || !hasValidEventLogCore(record)) return defaultSessionEventLog(sessionId);
|
|
1210
1288
|
return {
|
|
1211
1289
|
active_path: record.active_path,
|
|
@@ -1224,7 +1302,7 @@ function isPositiveInteger(value) {
|
|
|
1224
1302
|
}
|
|
1225
1303
|
function parseImportedFrom(raw) {
|
|
1226
1304
|
if (raw == null) return;
|
|
1227
|
-
const record = asRecord$
|
|
1305
|
+
const record = asRecord$4(raw);
|
|
1228
1306
|
if (!record || typeof record.record_id !== "string" || typeof record.cwd_original !== "string" || typeof record.exported_by !== "string" || typeof record.exported_at !== "string") return null;
|
|
1229
1307
|
return {
|
|
1230
1308
|
recordId: record.record_id,
|
|
@@ -1275,7 +1353,7 @@ function normalizeOptionalSignal(value) {
|
|
|
1275
1353
|
return Symbol("invalid");
|
|
1276
1354
|
}
|
|
1277
1355
|
function parseSessionRecord(raw) {
|
|
1278
|
-
const record = asRecord$
|
|
1356
|
+
const record = asRecord$4(raw);
|
|
1279
1357
|
if (!record) return null;
|
|
1280
1358
|
if (record.schema !== "acpx.session.v1") return null;
|
|
1281
1359
|
const optionals = validSessionOptionals({
|
|
@@ -1319,11 +1397,12 @@ function parseSessionRecord(raw) {
|
|
|
1319
1397
|
lastAgentExitAt: optionals.lastAgentExitAt,
|
|
1320
1398
|
lastAgentDisconnectReason: optionals.lastAgentDisconnectReason,
|
|
1321
1399
|
protocolVersion: typeof record.protocol_version === "number" ? record.protocol_version : void 0,
|
|
1322
|
-
agentCapabilities: asRecord$
|
|
1400
|
+
agentCapabilities: asRecord$4(record.agent_capabilities),
|
|
1323
1401
|
title: conversation.title,
|
|
1324
1402
|
messages: conversation.messages,
|
|
1325
1403
|
updated_at: conversation.updated_at,
|
|
1326
1404
|
cumulative_token_usage: conversation.cumulative_token_usage,
|
|
1405
|
+
cumulative_cost: conversation.cumulative_cost,
|
|
1327
1406
|
request_token_usage: conversation.request_token_usage,
|
|
1328
1407
|
acpx: parseAcpxState(record.acpx),
|
|
1329
1408
|
importedFrom: metadata.importedFrom
|
|
@@ -1498,12 +1577,12 @@ function assertPersistedKeyPolicy(value) {
|
|
|
1498
1577
|
//#endregion
|
|
1499
1578
|
//#region src/session/persistence/index.ts
|
|
1500
1579
|
const SESSION_INDEX_SCHEMA = "acpx.session-index.v1";
|
|
1501
|
-
function asRecord$
|
|
1580
|
+
function asRecord$3(value) {
|
|
1502
1581
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
1503
1582
|
return value;
|
|
1504
1583
|
}
|
|
1505
1584
|
function parseIndexEntry(raw) {
|
|
1506
|
-
const record = asRecord$
|
|
1585
|
+
const record = asRecord$3(raw);
|
|
1507
1586
|
if (!record) return;
|
|
1508
1587
|
if (!hasRequiredIndexEntryFields(record)) return;
|
|
1509
1588
|
if (record.name !== void 0 && typeof record.name !== "string") return;
|
|
@@ -1547,7 +1626,7 @@ async function readSessionIndex(sessionDir) {
|
|
|
1547
1626
|
const filePath = sessionIndexPath(sessionDir);
|
|
1548
1627
|
try {
|
|
1549
1628
|
const payload = await fs$1.readFile(filePath, "utf8");
|
|
1550
|
-
const record = asRecord$
|
|
1629
|
+
const record = asRecord$3(JSON.parse(payload));
|
|
1551
1630
|
if (!record || record.schema !== SESSION_INDEX_SCHEMA || !Array.isArray(record.files)) return;
|
|
1552
1631
|
const files = record.files.filter((entry) => typeof entry === "string");
|
|
1553
1632
|
if (files.length !== record.files.length || !Array.isArray(record.entries)) return;
|
|
@@ -2302,6 +2381,49 @@ function buildTerminalShellSpawnCommand(command, platform = process.platform) {
|
|
|
2302
2381
|
};
|
|
2303
2382
|
}
|
|
2304
2383
|
//#endregion
|
|
2384
|
+
//#region src/version.ts
|
|
2385
|
+
const UNKNOWN_VERSION = "0.0.0-unknown";
|
|
2386
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
2387
|
+
let cachedVersion = null;
|
|
2388
|
+
function parseVersion(value) {
|
|
2389
|
+
if (typeof value !== "string") return null;
|
|
2390
|
+
const trimmed = value.trim();
|
|
2391
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2392
|
+
}
|
|
2393
|
+
function readPackageVersion(packageJsonPath) {
|
|
2394
|
+
try {
|
|
2395
|
+
return parseVersion(JSON.parse(readFileSync(packageJsonPath, "utf8")).version);
|
|
2396
|
+
} catch {
|
|
2397
|
+
return null;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
function resolveVersionFromAncestors(startDir) {
|
|
2401
|
+
let current = startDir;
|
|
2402
|
+
while (true) {
|
|
2403
|
+
const packageVersion = readPackageVersion(path.join(current, "package.json"));
|
|
2404
|
+
if (packageVersion) return packageVersion;
|
|
2405
|
+
const parent = path.dirname(current);
|
|
2406
|
+
if (parent === current) return null;
|
|
2407
|
+
current = parent;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
function resolveAcpxVersion(params) {
|
|
2411
|
+
const envVersion = resolvePackageEnvVersion(params?.env ?? process.env);
|
|
2412
|
+
if (envVersion) return envVersion;
|
|
2413
|
+
if (params?.packageJsonPath) return readPackageVersion(params.packageJsonPath) ?? UNKNOWN_VERSION;
|
|
2414
|
+
return resolveVersionFromAncestors(MODULE_DIR) ?? UNKNOWN_VERSION;
|
|
2415
|
+
}
|
|
2416
|
+
function resolvePackageEnvVersion(env) {
|
|
2417
|
+
const envPackageName = parseVersion(env.npm_package_name);
|
|
2418
|
+
const envVersion = parseVersion(env.npm_package_version);
|
|
2419
|
+
return envPackageName === "acpx" ? envVersion : null;
|
|
2420
|
+
}
|
|
2421
|
+
function getAcpxVersion() {
|
|
2422
|
+
if (cachedVersion) return cachedVersion;
|
|
2423
|
+
cachedVersion = resolveAcpxVersion();
|
|
2424
|
+
return cachedVersion;
|
|
2425
|
+
}
|
|
2426
|
+
//#endregion
|
|
2305
2427
|
//#region src/acp/client-process.ts
|
|
2306
2428
|
const execFileAsync = promisify(execFile);
|
|
2307
2429
|
function isoNow$1() {
|
|
@@ -2477,6 +2599,7 @@ const GEMINI_ACP_FLAG_VERSION = [
|
|
|
2477
2599
|
0
|
|
2478
2600
|
];
|
|
2479
2601
|
const COPILOT_HELP_TIMEOUT_MS = 2e3;
|
|
2602
|
+
const CLAUDE_CODE_DEFAULT_SETTING_SOURCES = ["project", "local"];
|
|
2480
2603
|
const QODER_BENIGN_STDOUT_LINES = new Set(["Received interrupt signal. Cleaning up resources...", "Cleanup completed. Exiting..."]);
|
|
2481
2604
|
function resolveAgentCloseAfterStdinEndMs(agentCommand) {
|
|
2482
2605
|
const { command } = splitCommandLine(agentCommand);
|
|
@@ -2499,6 +2622,9 @@ function isCopilotAcpCommand(command, args) {
|
|
|
2499
2622
|
function isQoderAcpCommand(command, args) {
|
|
2500
2623
|
return basenameToken(command) === "qodercli" && args.includes("--acp");
|
|
2501
2624
|
}
|
|
2625
|
+
function isDevinAcpCommand(command, args) {
|
|
2626
|
+
return basenameToken(command) === "devin" && (args.includes("acp") || args.includes("--acp") || args.includes("--experimental-acp"));
|
|
2627
|
+
}
|
|
2502
2628
|
function hasCommandFlag(args, flagName) {
|
|
2503
2629
|
return args.some((arg) => arg === flagName || arg.startsWith(`${flagName}=`));
|
|
2504
2630
|
}
|
|
@@ -2639,16 +2765,20 @@ async function ensureCopilotAcpSupport(command) {
|
|
|
2639
2765
|
const helpOutput = await readCommandOutput(command, ["--help"], COPILOT_HELP_TIMEOUT_MS);
|
|
2640
2766
|
if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) throw new CopilotAcpUnsupportedError(await buildCopilotAcpUnsupportedMessage(command), { retryable: false });
|
|
2641
2767
|
}
|
|
2642
|
-
function buildClaudeCodeOptionsMeta(options) {
|
|
2643
|
-
if (!options) return;
|
|
2768
|
+
function buildClaudeCodeOptionsMeta(options, isolateUserSettings = false) {
|
|
2644
2769
|
const claudeCodeOptions = {};
|
|
2645
|
-
|
|
2770
|
+
if (isolateUserSettings) claudeCodeOptions.settingSources = resolveClaudeCodeSettingSources();
|
|
2771
|
+
if (options) assignClaudeCodeOptions(claudeCodeOptions, options);
|
|
2646
2772
|
const meta = {};
|
|
2647
2773
|
if (Object.keys(claudeCodeOptions).length > 0) meta.claudeCode = { options: claudeCodeOptions };
|
|
2648
|
-
assignClaudeCodeSystemPrompt(meta, options
|
|
2774
|
+
assignClaudeCodeSystemPrompt(meta, options?.systemPrompt);
|
|
2649
2775
|
if (Object.keys(meta).length === 0) return;
|
|
2650
2776
|
return meta;
|
|
2651
2777
|
}
|
|
2778
|
+
function resolveClaudeCodeSettingSources(env = process.env) {
|
|
2779
|
+
if (env.ACPX_CLAUDE_INCLUDE_USER_SETTINGS?.trim() === "1") return ["user", ...CLAUDE_CODE_DEFAULT_SETTING_SOURCES];
|
|
2780
|
+
return [...CLAUDE_CODE_DEFAULT_SETTING_SOURCES];
|
|
2781
|
+
}
|
|
2652
2782
|
function assignClaudeCodeOptions(target, options) {
|
|
2653
2783
|
if (typeof options.model === "string" && options.model.trim().length > 0) target.model = options.model;
|
|
2654
2784
|
if (Array.isArray(options.allowedTools)) target.allowedTools = [...options.allowedTools];
|
|
@@ -2740,6 +2870,98 @@ function buildAgentSpawnOptions(cwd, authCredentials) {
|
|
|
2740
2870
|
};
|
|
2741
2871
|
}
|
|
2742
2872
|
//#endregion
|
|
2873
|
+
//#region src/acp/model-support.ts
|
|
2874
|
+
var RequestedModelUnsupportedError = class extends Error {
|
|
2875
|
+
constructor(message) {
|
|
2876
|
+
super(message);
|
|
2877
|
+
this.name = "RequestedModelUnsupportedError";
|
|
2878
|
+
}
|
|
2879
|
+
};
|
|
2880
|
+
function supportsLegacyClaudeCodeModelMetadata(agentCommand) {
|
|
2881
|
+
if (!agentCommand) return false;
|
|
2882
|
+
const { command, args } = splitCommandLine(agentCommand);
|
|
2883
|
+
return isClaudeAcpCommand(command, args);
|
|
2884
|
+
}
|
|
2885
|
+
function asRecord$2(value) {
|
|
2886
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
2887
|
+
return value;
|
|
2888
|
+
}
|
|
2889
|
+
function parseAvailableModel(value) {
|
|
2890
|
+
const option = asRecord$2(value);
|
|
2891
|
+
if (!option || typeof option.value !== "string" || typeof option.name !== "string") return;
|
|
2892
|
+
return {
|
|
2893
|
+
modelId: option.value,
|
|
2894
|
+
name: option.name
|
|
2895
|
+
};
|
|
2896
|
+
}
|
|
2897
|
+
function parseAvailableModelGroup(value) {
|
|
2898
|
+
const group = asRecord$2(value);
|
|
2899
|
+
if (!group || typeof group.group !== "string" || typeof group.name !== "string" || !Array.isArray(group.options)) return;
|
|
2900
|
+
const models = group.options.map((option) => parseAvailableModel(option));
|
|
2901
|
+
return models.every((model) => model !== void 0) ? models : void 0;
|
|
2902
|
+
}
|
|
2903
|
+
function parseAvailableModels(value) {
|
|
2904
|
+
if (!Array.isArray(value)) return;
|
|
2905
|
+
const directModels = value.map((option) => parseAvailableModel(option));
|
|
2906
|
+
if (directModels.every((model) => model !== void 0)) return directModels;
|
|
2907
|
+
const groupedModels = value.map((group) => parseAvailableModelGroup(group));
|
|
2908
|
+
return groupedModels.every((models) => models !== void 0) ? groupedModels.flat() : void 0;
|
|
2909
|
+
}
|
|
2910
|
+
function isModelSelectOption(option) {
|
|
2911
|
+
return option.type === "select" && (option.category === "model" || option.id === "model");
|
|
2912
|
+
}
|
|
2913
|
+
function parseModelConfigOption(value) {
|
|
2914
|
+
const option = asRecord$2(value);
|
|
2915
|
+
if (!option || !isModelSelectOption(option) || typeof option.id !== "string" || typeof option.currentValue !== "string") return;
|
|
2916
|
+
const availableModels = parseAvailableModels(option.options);
|
|
2917
|
+
return availableModels ? {
|
|
2918
|
+
configId: option.id,
|
|
2919
|
+
currentModelId: option.currentValue,
|
|
2920
|
+
availableModels
|
|
2921
|
+
} : void 0;
|
|
2922
|
+
}
|
|
2923
|
+
function modelStateFromConfigOptions(configOptions) {
|
|
2924
|
+
if (!Array.isArray(configOptions)) return;
|
|
2925
|
+
for (const value of configOptions) {
|
|
2926
|
+
const models = parseModelConfigOption(value);
|
|
2927
|
+
if (models) return models;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
function modelStateFromLegacyResponse(response) {
|
|
2931
|
+
if (!response || typeof response !== "object") return;
|
|
2932
|
+
const models = response.models;
|
|
2933
|
+
if (!models || typeof models.currentModelId !== "string" || !Array.isArray(models.availableModels)) return;
|
|
2934
|
+
const availableModels = models.availableModels.flatMap((entry) => {
|
|
2935
|
+
if (!entry || typeof entry !== "object") return [];
|
|
2936
|
+
const candidate = entry;
|
|
2937
|
+
return typeof candidate.modelId === "string" && typeof candidate.name === "string" ? [{
|
|
2938
|
+
modelId: candidate.modelId,
|
|
2939
|
+
name: candidate.name
|
|
2940
|
+
}] : [];
|
|
2941
|
+
});
|
|
2942
|
+
return {
|
|
2943
|
+
currentModelId: models.currentModelId,
|
|
2944
|
+
availableModels
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
function modelStateFromSessionResponse(params) {
|
|
2948
|
+
return modelStateFromConfigOptions(params.configOptions) ?? modelStateFromLegacyResponse(params.response);
|
|
2949
|
+
}
|
|
2950
|
+
function formatAvailableModelIds(models) {
|
|
2951
|
+
const ids = models?.availableModels.map((model) => model.modelId.trim()).filter((modelId) => modelId.length > 0) ?? [];
|
|
2952
|
+
return ids.length > 0 ? ids.join(", ") : "none advertised";
|
|
2953
|
+
}
|
|
2954
|
+
function assertRequestedModelSupported(params) {
|
|
2955
|
+
if (!params.models) {
|
|
2956
|
+
if (supportsLegacyClaudeCodeModelMetadata(params.agentCommand)) return;
|
|
2957
|
+
throw new RequestedModelUnsupportedError(`Cannot ${params.context === "replay" ? "replay saved model" : "apply --model"} "${params.requestedModel}": the ACP agent did not advertise model support through a session config option or legacy models metadata, and the adapter does not support a startup model flag.`);
|
|
2958
|
+
}
|
|
2959
|
+
if (!new Set(params.models.availableModels.map((model) => model.modelId)).has(params.requestedModel)) {
|
|
2960
|
+
if (supportsLegacyClaudeCodeModelMetadata(params.agentCommand)) return `requested model "${params.requestedModel}" was not in the Claude ACP advertised model list (${formatAvailableModelIds(params.models)}); forwarding it to Claude Code so the adapter can accept or reject it.`;
|
|
2961
|
+
throw new RequestedModelUnsupportedError(`Cannot ${params.context === "replay" ? "replay saved model" : "apply --model"} "${params.requestedModel}": the ACP agent did not advertise that model. Available models: ${formatAvailableModelIds(params.models)}.`);
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
//#endregion
|
|
2743
2965
|
//#region src/acp/session-control-errors.ts
|
|
2744
2966
|
const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = new Set([-32601, -32602]);
|
|
2745
2967
|
function asRecord$1(value) {
|
|
@@ -3356,11 +3578,54 @@ const DRAIN_POLL_INTERVAL_MS = 20;
|
|
|
3356
3578
|
const AGENT_CLOSE_TERM_GRACE_MS = 1500;
|
|
3357
3579
|
const AGENT_CLOSE_KILL_GRACE_MS = 1e3;
|
|
3358
3580
|
const STARTUP_STDERR_MAX_CHARS = 8192;
|
|
3581
|
+
const DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META = Object.freeze({ "cognition.ai/requestDiagnostics": true });
|
|
3582
|
+
const DEVIN_COMPATIBILITY_CLIENT_NAME = "windsurf";
|
|
3583
|
+
const DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION = "1.110.1";
|
|
3584
|
+
function resolveClientInfo(devinAcp) {
|
|
3585
|
+
if (!devinAcp) return {
|
|
3586
|
+
name: "acpx",
|
|
3587
|
+
version: getAcpxVersion()
|
|
3588
|
+
};
|
|
3589
|
+
return {
|
|
3590
|
+
name: DEVIN_COMPATIBILITY_CLIENT_NAME,
|
|
3591
|
+
version: process.env.ACPX_DEVIN_WINDSURF_VERSION ?? DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION
|
|
3592
|
+
};
|
|
3593
|
+
}
|
|
3594
|
+
function resolveClientCapabilities(params) {
|
|
3595
|
+
const baseCapabilities = {
|
|
3596
|
+
fs: {
|
|
3597
|
+
readTextFile: true,
|
|
3598
|
+
writeTextFile: true
|
|
3599
|
+
},
|
|
3600
|
+
terminal: params.terminal
|
|
3601
|
+
};
|
|
3602
|
+
if (!params.devinAcp) return baseCapabilities;
|
|
3603
|
+
return {
|
|
3604
|
+
...baseCapabilities,
|
|
3605
|
+
_meta: DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META
|
|
3606
|
+
};
|
|
3607
|
+
}
|
|
3608
|
+
function isDevinRequestDiagnosticsMethod(method) {
|
|
3609
|
+
return method === "_cognition.ai/request_diagnostics";
|
|
3610
|
+
}
|
|
3611
|
+
function hasResponseField(response, field) {
|
|
3612
|
+
return !!response && typeof response === "object" && field in response;
|
|
3613
|
+
}
|
|
3614
|
+
function normalizeResponseConfigOptions(response) {
|
|
3615
|
+
if (!response || !("configOptions" in response)) return;
|
|
3616
|
+
return response.configOptions ?? [];
|
|
3617
|
+
}
|
|
3359
3618
|
function toReconnectedSessionResult(response) {
|
|
3619
|
+
const configOptions = normalizeResponseConfigOptions(response);
|
|
3360
3620
|
return {
|
|
3361
3621
|
agentSessionId: extractRuntimeSessionId(response?._meta),
|
|
3362
|
-
configOptions
|
|
3363
|
-
models:
|
|
3622
|
+
configOptions,
|
|
3623
|
+
models: modelStateFromSessionResponse({
|
|
3624
|
+
configOptions,
|
|
3625
|
+
response
|
|
3626
|
+
}),
|
|
3627
|
+
configOptionsPresent: hasResponseField(response, "configOptions"),
|
|
3628
|
+
legacyModelMetadataPresent: hasResponseField(response, "models")
|
|
3364
3629
|
};
|
|
3365
3630
|
}
|
|
3366
3631
|
function childProcessIsRunning(agent) {
|
|
@@ -3459,6 +3724,8 @@ var AcpClient = class {
|
|
|
3459
3724
|
lastKnownPid;
|
|
3460
3725
|
promptPermissionFailures = /* @__PURE__ */ new Map();
|
|
3461
3726
|
pendingConnectionRequests = /* @__PURE__ */ new Set();
|
|
3727
|
+
modelConfigIds = /* @__PURE__ */ new Map();
|
|
3728
|
+
legacyModelSessionIds = /* @__PURE__ */ new Set();
|
|
3462
3729
|
constructor(options) {
|
|
3463
3730
|
this.options = {
|
|
3464
3731
|
...options,
|
|
@@ -3570,7 +3837,7 @@ var AcpClient = class {
|
|
|
3570
3837
|
const input = Writable.toWeb(child.stdin);
|
|
3571
3838
|
const output = Readable.toWeb(child.stdout);
|
|
3572
3839
|
const stream = this.createTappedStream(createNdJsonMessageStream(this.options.agentCommand, input, output));
|
|
3573
|
-
const connection = this.createConnection(stream);
|
|
3840
|
+
const connection = this.createConnection(stream, launch);
|
|
3574
3841
|
connection.signal.addEventListener("abort", () => {
|
|
3575
3842
|
this.recordAgentExit("connection_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
3576
3843
|
}, { once: true });
|
|
@@ -3594,6 +3861,7 @@ var AcpClient = class {
|
|
|
3594
3861
|
spawnCommand,
|
|
3595
3862
|
args,
|
|
3596
3863
|
resolvedBuiltInLaunch,
|
|
3864
|
+
devinAcp: isDevinAcpCommand(spawnCommand, args),
|
|
3597
3865
|
geminiAcp: isGeminiAcpCommand(spawnCommand, args),
|
|
3598
3866
|
copilotAcp: isCopilotAcpCommand(spawnCommand, args),
|
|
3599
3867
|
claudeAcp: isClaudeAcpCommand(spawnCommand, args),
|
|
@@ -3630,7 +3898,7 @@ var AcpClient = class {
|
|
|
3630
3898
|
}
|
|
3631
3899
|
return requireAgentStdio(spawnedChild);
|
|
3632
3900
|
}
|
|
3633
|
-
createConnection(stream) {
|
|
3901
|
+
createConnection(stream, launch) {
|
|
3634
3902
|
return new ClientSideConnection(() => ({
|
|
3635
3903
|
sessionUpdate: async (params) => {
|
|
3636
3904
|
await this.handleSessionUpdate(params);
|
|
@@ -3638,6 +3906,10 @@ var AcpClient = class {
|
|
|
3638
3906
|
requestPermission: async (params) => {
|
|
3639
3907
|
return this.handlePermissionRequest(params);
|
|
3640
3908
|
},
|
|
3909
|
+
extMethod: async (method) => {
|
|
3910
|
+
if (launch.devinAcp && isDevinRequestDiagnosticsMethod(method)) return {};
|
|
3911
|
+
throw RequestError.methodNotFound(method);
|
|
3912
|
+
},
|
|
3641
3913
|
readTextFile: async (params) => {
|
|
3642
3914
|
return this.handleReadTextFile(params);
|
|
3643
3915
|
},
|
|
@@ -3658,12 +3930,13 @@ var AcpClient = class {
|
|
|
3658
3930
|
},
|
|
3659
3931
|
releaseTerminal: async (params) => {
|
|
3660
3932
|
return this.handleReleaseTerminal(params);
|
|
3661
|
-
}
|
|
3933
|
+
},
|
|
3934
|
+
extNotification: async () => {}
|
|
3662
3935
|
}), stream);
|
|
3663
3936
|
}
|
|
3664
3937
|
async initializeAgentConnection(params) {
|
|
3665
3938
|
try {
|
|
3666
|
-
const initResult = await Promise.race([this.initializeProtocolConnection(params.connection, params.launch
|
|
3939
|
+
const initResult = await Promise.race([this.initializeProtocolConnection(params.connection, params.launch), params.startupFailure.promise]);
|
|
3667
3940
|
params.startupFailure.dispose();
|
|
3668
3941
|
this.connection = params.connection;
|
|
3669
3942
|
this.agent = params.child;
|
|
@@ -3673,22 +3946,16 @@ var AcpClient = class {
|
|
|
3673
3946
|
await this.handleInitializeFailure(params, error);
|
|
3674
3947
|
}
|
|
3675
3948
|
}
|
|
3676
|
-
async initializeProtocolConnection(connection,
|
|
3949
|
+
async initializeProtocolConnection(connection, launch) {
|
|
3677
3950
|
const initializePromise = connection.initialize({
|
|
3678
3951
|
protocolVersion: PROTOCOL_VERSION,
|
|
3679
|
-
clientCapabilities: {
|
|
3680
|
-
|
|
3681
|
-
readTextFile: true,
|
|
3682
|
-
writeTextFile: true
|
|
3683
|
-
},
|
|
3952
|
+
clientCapabilities: resolveClientCapabilities({
|
|
3953
|
+
devinAcp: launch.devinAcp,
|
|
3684
3954
|
terminal: this.options.terminal !== false
|
|
3685
|
-
},
|
|
3686
|
-
clientInfo:
|
|
3687
|
-
name: "acpx",
|
|
3688
|
-
version: "0.1.0"
|
|
3689
|
-
}
|
|
3955
|
+
}),
|
|
3956
|
+
clientInfo: resolveClientInfo(launch.devinAcp)
|
|
3690
3957
|
});
|
|
3691
|
-
const initialized = geminiAcp ? await withTimeout(initializePromise, resolveGeminiAcpStartupTimeoutMs()) : await initializePromise;
|
|
3958
|
+
const initialized = launch.geminiAcp ? await withTimeout(initializePromise, resolveGeminiAcpStartupTimeoutMs()) : await initializePromise;
|
|
3692
3959
|
await this.authenticateIfRequired(connection, initialized.authMethods ?? []);
|
|
3693
3960
|
return initialized;
|
|
3694
3961
|
}
|
|
@@ -3751,7 +4018,7 @@ var AcpClient = class {
|
|
|
3751
4018
|
const createPromise = this.runConnectionRequest(() => connection.newSession({
|
|
3752
4019
|
cwd: sessionCwd,
|
|
3753
4020
|
mcpServers: this.options.mcpServers ?? [],
|
|
3754
|
-
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions)
|
|
4021
|
+
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions, claudeAcp)
|
|
3755
4022
|
}));
|
|
3756
4023
|
result = claudeAcp ? await withTimeout(createPromise, resolveClaudeAcpSessionCreateTimeoutMs()) : await createPromise;
|
|
3757
4024
|
} catch (error) {
|
|
@@ -3762,11 +4029,19 @@ var AcpClient = class {
|
|
|
3762
4029
|
throw error;
|
|
3763
4030
|
}
|
|
3764
4031
|
this.loadedSessionId = result.sessionId;
|
|
4032
|
+
const configOptions = normalizeResponseConfigOptions(result);
|
|
4033
|
+
const models = modelStateFromSessionResponse({
|
|
4034
|
+
configOptions,
|
|
4035
|
+
response: result
|
|
4036
|
+
});
|
|
4037
|
+
this.rememberSessionModels(result.sessionId, models);
|
|
3765
4038
|
return {
|
|
3766
4039
|
sessionId: result.sessionId,
|
|
3767
4040
|
agentSessionId: extractRuntimeSessionId(result._meta),
|
|
3768
|
-
configOptions
|
|
3769
|
-
models
|
|
4041
|
+
configOptions,
|
|
4042
|
+
models,
|
|
4043
|
+
configOptionsPresent: hasResponseField(result, "configOptions"),
|
|
4044
|
+
legacyModelMetadataPresent: hasResponseField(result, "models")
|
|
3770
4045
|
};
|
|
3771
4046
|
}
|
|
3772
4047
|
async loadSession(sessionId, cwd = this.options.cwd) {
|
|
@@ -3789,7 +4064,9 @@ var AcpClient = class {
|
|
|
3789
4064
|
this.restoreSessionUpdateSuppression(previousSuppression);
|
|
3790
4065
|
}
|
|
3791
4066
|
this.loadedSessionId = sessionId;
|
|
3792
|
-
|
|
4067
|
+
const result = toReconnectedSessionResult(response);
|
|
4068
|
+
this.updateRememberedSessionModels(sessionId, result);
|
|
4069
|
+
return result;
|
|
3793
4070
|
}
|
|
3794
4071
|
async resumeSession(sessionId, cwd = this.options.cwd) {
|
|
3795
4072
|
const connection = this.getConnection();
|
|
@@ -3800,7 +4077,9 @@ var AcpClient = class {
|
|
|
3800
4077
|
mcpServers: this.options.mcpServers ?? []
|
|
3801
4078
|
}));
|
|
3802
4079
|
this.loadedSessionId = sessionId;
|
|
3803
|
-
|
|
4080
|
+
const result = toReconnectedSessionResult(response);
|
|
4081
|
+
this.updateRememberedSessionModels(sessionId, result);
|
|
4082
|
+
return result;
|
|
3804
4083
|
}
|
|
3805
4084
|
applySessionUpdateSuppression(enabled) {
|
|
3806
4085
|
const previous = {
|
|
@@ -3883,21 +4162,73 @@ var AcpClient = class {
|
|
|
3883
4162
|
throw maybeWrapSessionControlError("session/set_config_option", error, `for "${configId}"="${value}"`);
|
|
3884
4163
|
}
|
|
3885
4164
|
}
|
|
3886
|
-
async setSessionModel(sessionId, modelId) {
|
|
4165
|
+
async setSessionModel(sessionId, modelId, controlOverride) {
|
|
4166
|
+
const control = this.resolveModelControl(sessionId, controlOverride);
|
|
4167
|
+
if (!control) throw new RequestedModelUnsupportedError(`Cannot set model "${modelId}": the ACP session did not advertise a model config option or legacy session/set_model support.`);
|
|
4168
|
+
return control.kind === "config_option" ? await this.setSessionModelThroughConfig(sessionId, modelId, control.configId) : await this.setSessionModelThroughLegacyMethod(sessionId, modelId);
|
|
4169
|
+
}
|
|
4170
|
+
async setSessionModelThroughConfig(sessionId, modelId, configId) {
|
|
4171
|
+
const connection = this.getConnection();
|
|
4172
|
+
try {
|
|
4173
|
+
const response = await this.runConnectionRequest(() => connection.setSessionConfigOption({
|
|
4174
|
+
sessionId,
|
|
4175
|
+
configId,
|
|
4176
|
+
value: modelId
|
|
4177
|
+
}));
|
|
4178
|
+
this.rememberSessionModels(sessionId, modelStateFromConfigOptions(response.configOptions));
|
|
4179
|
+
return response;
|
|
4180
|
+
} catch (error) {
|
|
4181
|
+
return this.throwSessionModelError("session/set_config_option", modelId, error);
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
async setSessionModelThroughLegacyMethod(sessionId, modelId) {
|
|
3887
4185
|
const connection = this.getConnection();
|
|
3888
4186
|
try {
|
|
3889
|
-
await this.runConnectionRequest(() => connection.
|
|
4187
|
+
await this.runConnectionRequest(() => connection.extMethod("session/set_model", {
|
|
3890
4188
|
sessionId,
|
|
3891
4189
|
modelId
|
|
3892
4190
|
}));
|
|
4191
|
+
return;
|
|
3893
4192
|
} catch (error) {
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
4193
|
+
return this.throwSessionModelError("session/set_model", modelId, error);
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
throwSessionModelError(method, modelId, error) {
|
|
4197
|
+
const wrapped = maybeWrapSessionControlError(method, error, `for model "${modelId}"`);
|
|
4198
|
+
if (wrapped !== error) throw wrapped;
|
|
4199
|
+
const acp = extractAcpError(error);
|
|
4200
|
+
const summary = acp ? formatSessionControlAcpSummary(acp) : error instanceof Error ? error.message : String(error);
|
|
4201
|
+
throw new Error(`Failed ${method} for model "${modelId}": ${summary}`, { cause: error });
|
|
4202
|
+
}
|
|
4203
|
+
resolveModelControl(sessionId, controlOverride) {
|
|
4204
|
+
if (controlOverride) return controlOverride.configId ? {
|
|
4205
|
+
kind: "config_option",
|
|
4206
|
+
configId: controlOverride.configId
|
|
4207
|
+
} : { kind: "legacy_set_model" };
|
|
4208
|
+
const configId = this.modelConfigIds.get(sessionId);
|
|
4209
|
+
if (configId) return {
|
|
4210
|
+
kind: "config_option",
|
|
4211
|
+
configId
|
|
4212
|
+
};
|
|
4213
|
+
return this.legacyModelSessionIds.has(sessionId) ? { kind: "legacy_set_model" } : void 0;
|
|
4214
|
+
}
|
|
4215
|
+
rememberSessionModels(sessionId, models) {
|
|
4216
|
+
if (!models) {
|
|
4217
|
+
this.modelConfigIds.delete(sessionId);
|
|
4218
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
4219
|
+
return;
|
|
3900
4220
|
}
|
|
4221
|
+
if (models.configId) {
|
|
4222
|
+
this.modelConfigIds.set(sessionId, models.configId);
|
|
4223
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
4224
|
+
return;
|
|
4225
|
+
}
|
|
4226
|
+
this.modelConfigIds.delete(sessionId);
|
|
4227
|
+
this.legacyModelSessionIds.add(sessionId);
|
|
4228
|
+
}
|
|
4229
|
+
updateRememberedSessionModels(sessionId, result) {
|
|
4230
|
+
const explicitConfigRemoval = result.configOptionsPresent && this.modelConfigIds.has(sessionId);
|
|
4231
|
+
if (result.models || result.legacyModelMetadataPresent || explicitConfigRemoval) this.rememberSessionModels(sessionId, result.models);
|
|
3901
4232
|
}
|
|
3902
4233
|
async cancel(sessionId) {
|
|
3903
4234
|
const connection = this.getConnection();
|
|
@@ -3909,6 +4240,8 @@ var AcpClient = class {
|
|
|
3909
4240
|
const connection = this.getConnection();
|
|
3910
4241
|
await this.runConnectionRequest(() => connection.closeSession({ sessionId }));
|
|
3911
4242
|
if (this.loadedSessionId === sessionId) this.loadedSessionId = void 0;
|
|
4243
|
+
this.modelConfigIds.delete(sessionId);
|
|
4244
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
3912
4245
|
}
|
|
3913
4246
|
async listSessions(params = {}) {
|
|
3914
4247
|
const connection = this.getConnection();
|
|
@@ -3957,6 +4290,8 @@ var AcpClient = class {
|
|
|
3957
4290
|
this.permissionAbortControllers.clear();
|
|
3958
4291
|
this.promptPermissionFailures.clear();
|
|
3959
4292
|
this.loadedSessionId = void 0;
|
|
4293
|
+
this.modelConfigIds.clear();
|
|
4294
|
+
this.legacyModelSessionIds.clear();
|
|
3960
4295
|
this.initResult = void 0;
|
|
3961
4296
|
this.connection = void 0;
|
|
3962
4297
|
this.agent = void 0;
|
|
@@ -4010,7 +4345,7 @@ var AcpClient = class {
|
|
|
4010
4345
|
target.push(text);
|
|
4011
4346
|
if (target.join("").length - STARTUP_STDERR_MAX_CHARS <= 0) return;
|
|
4012
4347
|
const joined = target.join("");
|
|
4013
|
-
target.splice(0, target.length, joined.slice(-
|
|
4348
|
+
target.splice(0, target.length, joined.slice(-8192));
|
|
4014
4349
|
}
|
|
4015
4350
|
summarizeStartupStderr(target) {
|
|
4016
4351
|
const joined = target.join("").trim();
|
|
@@ -4369,6 +4704,7 @@ function applyConversation(record, conversation) {
|
|
|
4369
4704
|
record.updated_at = conversation.updated_at;
|
|
4370
4705
|
record.messages = conversation.messages;
|
|
4371
4706
|
record.cumulative_token_usage = conversation.cumulative_token_usage;
|
|
4707
|
+
record.cumulative_cost = conversation.cumulative_cost;
|
|
4372
4708
|
record.request_token_usage = conversation.request_token_usage;
|
|
4373
4709
|
}
|
|
4374
4710
|
//#endregion
|
|
@@ -4444,6 +4780,51 @@ function nonEmptyString(value) {
|
|
|
4444
4780
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
4445
4781
|
}
|
|
4446
4782
|
//#endregion
|
|
4783
|
+
//#region src/session/model-state.ts
|
|
4784
|
+
function configOptionsAreAuthoritative(state) {
|
|
4785
|
+
return state.model_control === "config_option";
|
|
4786
|
+
}
|
|
4787
|
+
function legacyModelState(state) {
|
|
4788
|
+
if (!Array.isArray(state.available_models)) return;
|
|
4789
|
+
return {
|
|
4790
|
+
currentModelId: state.current_model_id ?? "",
|
|
4791
|
+
availableModels: state.available_models.map((modelId) => ({
|
|
4792
|
+
modelId,
|
|
4793
|
+
name: modelId
|
|
4794
|
+
}))
|
|
4795
|
+
};
|
|
4796
|
+
}
|
|
4797
|
+
function advertisedModelState(state) {
|
|
4798
|
+
if (!state) return;
|
|
4799
|
+
const configModels = modelStateFromConfigOptions(state?.config_options);
|
|
4800
|
+
if (configModels) return configModels;
|
|
4801
|
+
if (configOptionsAreAuthoritative(state)) return;
|
|
4802
|
+
return legacyModelState(state);
|
|
4803
|
+
}
|
|
4804
|
+
function applyAdvertisedModelState(state, models) {
|
|
4805
|
+
state.current_model_id = models.currentModelId;
|
|
4806
|
+
state.available_models = models.availableModels.map((model) => model.modelId);
|
|
4807
|
+
state.model_control = models.configId ? "config_option" : "legacy_set_model";
|
|
4808
|
+
}
|
|
4809
|
+
function clearAdvertisedModelState(state) {
|
|
4810
|
+
delete state.current_model_id;
|
|
4811
|
+
delete state.available_models;
|
|
4812
|
+
delete state.model_control;
|
|
4813
|
+
}
|
|
4814
|
+
function removeModelConfigOptions(state) {
|
|
4815
|
+
if (!state.config_options) return;
|
|
4816
|
+
state.config_options = state.config_options.filter((option) => option.category !== "model" && option.id !== "model");
|
|
4817
|
+
}
|
|
4818
|
+
function applyConfigOptionsModelState(state, configOptions) {
|
|
4819
|
+
const previousConfigModels = modelStateFromConfigOptions(state.config_options);
|
|
4820
|
+
const preservesLegacyControl = state.model_control === "legacy_set_model" || state.model_control === void 0 && previousConfigModels === void 0 && legacyModelState(state) !== void 0;
|
|
4821
|
+
state.config_options = structuredClone(configOptions);
|
|
4822
|
+
const models = modelStateFromConfigOptions(configOptions);
|
|
4823
|
+
if (models) applyAdvertisedModelState(state, models);
|
|
4824
|
+
else if (preservesLegacyControl) state.model_control = "legacy_set_model";
|
|
4825
|
+
else clearAdvertisedModelState(state);
|
|
4826
|
+
}
|
|
4827
|
+
//#endregion
|
|
4447
4828
|
//#region src/session/conversation-model.ts
|
|
4448
4829
|
const MAX_RUNTIME_MESSAGES = 200;
|
|
4449
4830
|
const MAX_RUNTIME_AGENT_TEXT_CHARS = 8e3;
|
|
@@ -4464,10 +4845,25 @@ function hasOwn(source, key) {
|
|
|
4464
4845
|
return Object.prototype.hasOwnProperty.call(source, key);
|
|
4465
4846
|
}
|
|
4466
4847
|
function normalizeAgentName(value) {
|
|
4848
|
+
return trimmedString(value);
|
|
4849
|
+
}
|
|
4850
|
+
function trimmedString(value) {
|
|
4467
4851
|
if (typeof value !== "string") return;
|
|
4468
4852
|
const trimmed = value.trim();
|
|
4469
4853
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
4470
4854
|
}
|
|
4855
|
+
function normalizeAvailableCommand(value) {
|
|
4856
|
+
const record = asRecord(value);
|
|
4857
|
+
if (!record) return;
|
|
4858
|
+
const name = trimmedString(record.name);
|
|
4859
|
+
if (!name) return;
|
|
4860
|
+
const description = trimmedString(record.description);
|
|
4861
|
+
return {
|
|
4862
|
+
name,
|
|
4863
|
+
...description ? { description } : {},
|
|
4864
|
+
has_input: record.input != null
|
|
4865
|
+
};
|
|
4866
|
+
}
|
|
4471
4867
|
function extractText(content) {
|
|
4472
4868
|
switch (content.type) {
|
|
4473
4869
|
case "text": return content.text;
|
|
@@ -4695,7 +5091,9 @@ function usageToTokenUsage(update) {
|
|
|
4695
5091
|
"cache_read_input_tokens",
|
|
4696
5092
|
"cacheReadInputTokens",
|
|
4697
5093
|
"cachedReadTokens"
|
|
4698
|
-
])
|
|
5094
|
+
]),
|
|
5095
|
+
thought_tokens: numberField(source, ["thought_tokens", "thoughtTokens"]),
|
|
5096
|
+
total_tokens: numberField(source, ["total_tokens", "totalTokens"])
|
|
4699
5097
|
};
|
|
4700
5098
|
if (!hasTokenUsageValue(normalized)) return;
|
|
4701
5099
|
return normalized;
|
|
@@ -4703,6 +5101,21 @@ function usageToTokenUsage(update) {
|
|
|
4703
5101
|
function hasTokenUsageValue(usage) {
|
|
4704
5102
|
return Object.values(usage).some((value) => value !== void 0);
|
|
4705
5103
|
}
|
|
5104
|
+
function usageCost(update) {
|
|
5105
|
+
const cost = asRecord(asRecord(update)?.cost);
|
|
5106
|
+
if (!cost) return;
|
|
5107
|
+
return buildUsageCost(numberField(cost, ["amount"]), stringField(cost.currency));
|
|
5108
|
+
}
|
|
5109
|
+
function stringField(value) {
|
|
5110
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
5111
|
+
}
|
|
5112
|
+
function buildUsageCost(amount, currency) {
|
|
5113
|
+
const cost = {
|
|
5114
|
+
...amount !== void 0 ? { amount } : {},
|
|
5115
|
+
...currency !== void 0 ? { currency } : {}
|
|
5116
|
+
};
|
|
5117
|
+
return Object.keys(cost).length > 0 ? cost : void 0;
|
|
5118
|
+
}
|
|
4706
5119
|
function ensureAcpxState$1(state) {
|
|
4707
5120
|
return state ?? {};
|
|
4708
5121
|
}
|
|
@@ -4718,6 +5131,7 @@ function createSessionConversation(timestamp = isoNow()) {
|
|
|
4718
5131
|
messages: [],
|
|
4719
5132
|
updated_at: timestamp,
|
|
4720
5133
|
cumulative_token_usage: {},
|
|
5134
|
+
cumulative_cost: void 0,
|
|
4721
5135
|
request_token_usage: {}
|
|
4722
5136
|
};
|
|
4723
5137
|
}
|
|
@@ -4728,9 +5142,13 @@ function cloneSessionConversation(conversation) {
|
|
|
4728
5142
|
messages: deepClone(conversation.messages ?? []),
|
|
4729
5143
|
updated_at: conversation.updated_at,
|
|
4730
5144
|
cumulative_token_usage: deepClone(conversation.cumulative_token_usage ?? {}),
|
|
5145
|
+
cumulative_cost: cloneUsageCost(conversation.cumulative_cost),
|
|
4731
5146
|
request_token_usage: deepClone(conversation.request_token_usage ?? {})
|
|
4732
5147
|
};
|
|
4733
5148
|
}
|
|
5149
|
+
function cloneUsageCost(cost) {
|
|
5150
|
+
return cost ? { ...cost } : void 0;
|
|
5151
|
+
}
|
|
4734
5152
|
function cloneSessionAcpxState(state) {
|
|
4735
5153
|
if (!state) return;
|
|
4736
5154
|
return {
|
|
@@ -4739,7 +5157,8 @@ function cloneSessionAcpxState(state) {
|
|
|
4739
5157
|
desired_config_options: state.desired_config_options ? { ...state.desired_config_options } : void 0,
|
|
4740
5158
|
current_model_id: state.current_model_id,
|
|
4741
5159
|
available_models: state.available_models ? [...state.available_models] : void 0,
|
|
4742
|
-
|
|
5160
|
+
model_control: state.model_control,
|
|
5161
|
+
available_commands: state.available_commands ? state.available_commands.map((command) => ({ ...command })) : void 0,
|
|
4743
5162
|
config_options: state.config_options ? deepClone(state.config_options) : void 0,
|
|
4744
5163
|
session_options: cloneSessionOptions(state.session_options)
|
|
4745
5164
|
};
|
|
@@ -4820,13 +5239,13 @@ const SESSION_UPDATE_HANDLERS = {
|
|
|
4820
5239
|
if (update.sessionUpdate === "session_info_update") applySessionInfoUpdate(conversation, update);
|
|
4821
5240
|
},
|
|
4822
5241
|
available_commands_update: (_conversation, acpx, update) => {
|
|
4823
|
-
if (update.sessionUpdate === "available_commands_update") acpx.available_commands = update.availableCommands.map((entry) => entry
|
|
5242
|
+
if (update.sessionUpdate === "available_commands_update") acpx.available_commands = update.availableCommands.map((entry) => normalizeAvailableCommand(entry)).filter((entry) => entry !== void 0);
|
|
4824
5243
|
},
|
|
4825
5244
|
current_mode_update: (_conversation, acpx, update) => {
|
|
4826
5245
|
if (update.sessionUpdate === "current_mode_update") acpx.current_mode_id = update.currentModeId;
|
|
4827
5246
|
},
|
|
4828
5247
|
config_option_update: (_conversation, acpx, update) => {
|
|
4829
|
-
if (update.sessionUpdate === "config_option_update") acpx
|
|
5248
|
+
if (update.sessionUpdate === "config_option_update") applyConfigOptionsModelState(acpx, deepClone(update.configOptions));
|
|
4830
5249
|
}
|
|
4831
5250
|
};
|
|
4832
5251
|
function appendUserMessageChunk(conversation, content) {
|
|
@@ -4843,10 +5262,14 @@ function appendAgentMessageChunk(conversation, content, append) {
|
|
|
4843
5262
|
}
|
|
4844
5263
|
function applyUsageUpdate(conversation, update) {
|
|
4845
5264
|
const usage = usageToTokenUsage(update);
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
5265
|
+
const cost = usageCost(update);
|
|
5266
|
+
if (!usage && !cost) return;
|
|
5267
|
+
if (usage) {
|
|
5268
|
+
conversation.cumulative_token_usage = usage;
|
|
5269
|
+
const userId = lastUserMessageId(conversation);
|
|
5270
|
+
if (userId) conversation.request_token_usage[userId] = usage;
|
|
5271
|
+
}
|
|
5272
|
+
if (cost) conversation.cumulative_cost = cost;
|
|
4850
5273
|
}
|
|
4851
5274
|
function applySessionInfoUpdate(conversation, update) {
|
|
4852
5275
|
if (hasOwn(update, "title")) conversation.title = update.title ?? null;
|
|
@@ -4859,10 +5282,10 @@ function recordClientOperation(conversation, state, operation, timestamp = isoNo
|
|
|
4859
5282
|
return acpx;
|
|
4860
5283
|
}
|
|
4861
5284
|
function trimConversationForRuntime(conversation) {
|
|
4862
|
-
if (conversation.messages.length > MAX_RUNTIME_MESSAGES) conversation.messages = conversation.messages.slice(-
|
|
5285
|
+
if (conversation.messages.length > MAX_RUNTIME_MESSAGES) conversation.messages = conversation.messages.slice(-200);
|
|
4863
5286
|
for (const message of conversation.messages) trimRuntimeMessage(message);
|
|
4864
5287
|
const requestUsageEntries = Object.entries(conversation.request_token_usage);
|
|
4865
|
-
if (requestUsageEntries.length > MAX_RUNTIME_REQUEST_TOKEN_USAGE) conversation.request_token_usage = Object.fromEntries(requestUsageEntries.slice(-
|
|
5288
|
+
if (requestUsageEntries.length > MAX_RUNTIME_REQUEST_TOKEN_USAGE) conversation.request_token_usage = Object.fromEntries(requestUsageEntries.slice(-100));
|
|
4866
5289
|
}
|
|
4867
5290
|
function trimRuntimeMessage(message) {
|
|
4868
5291
|
if (isUserMessage(message)) {
|
|
@@ -4892,12 +5315,15 @@ function trimRuntimeToolResult(result) {
|
|
|
4892
5315
|
}
|
|
4893
5316
|
//#endregion
|
|
4894
5317
|
//#region src/session/config-options.ts
|
|
5318
|
+
function applyConfigOptionsToState(state, configOptions) {
|
|
5319
|
+
const acpxState = cloneSessionAcpxState(state) ?? {};
|
|
5320
|
+
applyConfigOptionsModelState(acpxState, configOptions);
|
|
5321
|
+
return acpxState;
|
|
5322
|
+
}
|
|
4895
5323
|
function applyConfigOptionsToRecord(record, result) {
|
|
4896
5324
|
const configOptions = result?.configOptions;
|
|
4897
5325
|
if (!configOptions) return;
|
|
4898
|
-
|
|
4899
|
-
acpxState.config_options = structuredClone(configOptions);
|
|
4900
|
-
record.acpx = acpxState;
|
|
5326
|
+
record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
|
|
4901
5327
|
}
|
|
4902
5328
|
//#endregion
|
|
4903
5329
|
//#region src/session/mode-preference.ts
|
|
@@ -4943,10 +5369,18 @@ function setDesiredConfigOption(record, configId, value) {
|
|
|
4943
5369
|
else delete acpx.desired_config_options;
|
|
4944
5370
|
record.acpx = acpx;
|
|
4945
5371
|
}
|
|
5372
|
+
function clearDesiredConfigOption(state, configId) {
|
|
5373
|
+
const normalizedConfigId = normalizeModeId(configId);
|
|
5374
|
+
if (!normalizedConfigId || !state.desired_config_options) return;
|
|
5375
|
+
const desired = { ...state.desired_config_options };
|
|
5376
|
+
delete desired[normalizedConfigId];
|
|
5377
|
+
if (Object.keys(desired).length > 0) state.desired_config_options = desired;
|
|
5378
|
+
else delete state.desired_config_options;
|
|
5379
|
+
}
|
|
4946
5380
|
function getDesiredModelId(state) {
|
|
4947
5381
|
return normalizeModelId(state?.session_options?.model);
|
|
4948
5382
|
}
|
|
4949
|
-
function setDesiredModelId(record, modelId) {
|
|
5383
|
+
function setDesiredModelId(record, modelId, modelConfigId) {
|
|
4950
5384
|
const acpx = ensureAcpxState(record.acpx);
|
|
4951
5385
|
const normalized = normalizeModelId(modelId);
|
|
4952
5386
|
const sessionOptions = { ...acpx.session_options };
|
|
@@ -4954,6 +5388,7 @@ function setDesiredModelId(record, modelId) {
|
|
|
4954
5388
|
else delete sessionOptions.model;
|
|
4955
5389
|
if (typeof sessionOptions.model === "string" || Array.isArray(sessionOptions.allowed_tools) || typeof sessionOptions.max_turns === "number" || sessionOptions.system_prompt !== void 0) acpx.session_options = sessionOptions;
|
|
4956
5390
|
else delete acpx.session_options;
|
|
5391
|
+
clearDesiredConfigOption(acpx, modelConfigId ?? modelStateFromConfigOptions(acpx.config_options)?.configId);
|
|
4957
5392
|
record.acpx = acpx;
|
|
4958
5393
|
}
|
|
4959
5394
|
function setCurrentModelId(record, modelId) {
|
|
@@ -4966,49 +5401,30 @@ function setCurrentModelId(record, modelId) {
|
|
|
4966
5401
|
function syncAdvertisedModelState(record, models) {
|
|
4967
5402
|
if (!models) return;
|
|
4968
5403
|
const acpx = ensureAcpxState(record.acpx);
|
|
4969
|
-
acpx
|
|
4970
|
-
acpx.available_models = models.availableModels.map((model) => model.modelId);
|
|
5404
|
+
applyAdvertisedModelState(acpx, models);
|
|
4971
5405
|
record.acpx = acpx;
|
|
4972
5406
|
}
|
|
4973
5407
|
//#endregion
|
|
4974
|
-
//#region src/acp/model-support.ts
|
|
4975
|
-
var RequestedModelUnsupportedError = class extends Error {
|
|
4976
|
-
constructor(message) {
|
|
4977
|
-
super(message);
|
|
4978
|
-
this.name = "RequestedModelUnsupportedError";
|
|
4979
|
-
}
|
|
4980
|
-
};
|
|
4981
|
-
function supportsLegacyClaudeCodeModelMetadata(agentCommand) {
|
|
4982
|
-
if (!agentCommand) return false;
|
|
4983
|
-
const { command, args } = splitCommandLine(agentCommand);
|
|
4984
|
-
return isClaudeAcpCommand(command, args);
|
|
4985
|
-
}
|
|
4986
|
-
function formatAvailableModelIds(models) {
|
|
4987
|
-
const ids = models?.availableModels.map((model) => model.modelId.trim()).filter((modelId) => modelId.length > 0) ?? [];
|
|
4988
|
-
return ids.length > 0 ? ids.join(", ") : "none advertised";
|
|
4989
|
-
}
|
|
4990
|
-
function assertRequestedModelSupported(params) {
|
|
4991
|
-
if (!params.models) {
|
|
4992
|
-
if (supportsLegacyClaudeCodeModelMetadata(params.agentCommand)) return;
|
|
4993
|
-
throw new RequestedModelUnsupportedError(`Cannot ${params.context === "replay" ? "replay saved model" : "apply --model"} "${params.requestedModel}": the ACP agent did not advertise model support. Generic model selection requires ACP models plus session/set_model support, or an adapter-specific startup model flag.`);
|
|
4994
|
-
}
|
|
4995
|
-
if (!new Set(params.models.availableModels.map((model) => model.modelId)).has(params.requestedModel)) throw new RequestedModelUnsupportedError(`Cannot ${params.context === "replay" ? "replay saved model" : "apply --model"} "${params.requestedModel}": the ACP agent did not advertise that model. Available models: ${formatAvailableModelIds(params.models)}.`);
|
|
4996
|
-
}
|
|
4997
|
-
//#endregion
|
|
4998
5408
|
//#region src/session/model-application.ts
|
|
5409
|
+
function currentModelIdFromSetModelResponse(response, fallbackModelId) {
|
|
5410
|
+
return modelStateFromConfigOptions(response?.configOptions)?.currentModelId ?? fallbackModelId;
|
|
5411
|
+
}
|
|
4999
5412
|
async function applyRequestedModelIfAdvertised(params) {
|
|
5000
5413
|
const requestedModel = typeof params.requestedModel === "string" ? params.requestedModel.trim() : "";
|
|
5001
|
-
if (!requestedModel) return false;
|
|
5002
|
-
assertRequestedModelSupported({
|
|
5414
|
+
if (!requestedModel) return { applied: false };
|
|
5415
|
+
const warning = assertRequestedModelSupported({
|
|
5003
5416
|
requestedModel,
|
|
5004
5417
|
models: params.models,
|
|
5005
5418
|
agentCommand: params.agentCommand,
|
|
5006
5419
|
context: "apply"
|
|
5007
5420
|
});
|
|
5008
|
-
if (
|
|
5009
|
-
if (params.models
|
|
5010
|
-
|
|
5011
|
-
return
|
|
5421
|
+
if (warning) params.onWarning?.(warning);
|
|
5422
|
+
if (!params.models) return { applied: false };
|
|
5423
|
+
if (params.models.currentModelId === requestedModel) return { applied: true };
|
|
5424
|
+
return {
|
|
5425
|
+
applied: true,
|
|
5426
|
+
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models), params.timeoutMs)
|
|
5427
|
+
};
|
|
5012
5428
|
}
|
|
5013
5429
|
//#endregion
|
|
5014
5430
|
//#region src/runtime/engine/reconnect.ts
|
|
@@ -5056,17 +5472,27 @@ async function replayDesiredMode(params) {
|
|
|
5056
5472
|
}
|
|
5057
5473
|
}
|
|
5058
5474
|
async function replayDesiredModel(params) {
|
|
5059
|
-
if (!params.desiredModelId) return;
|
|
5475
|
+
if (!params.desiredModelId) return { replayed: false };
|
|
5060
5476
|
try {
|
|
5061
|
-
assertRequestedModelSupported({
|
|
5477
|
+
emitModelSupportWarning(assertRequestedModelSupported({
|
|
5062
5478
|
requestedModel: params.desiredModelId,
|
|
5063
5479
|
models: params.models,
|
|
5064
5480
|
agentCommand: params.record.agentCommand,
|
|
5065
5481
|
context: "replay"
|
|
5066
|
-
});
|
|
5067
|
-
if (!params.models || params.models.currentModelId === params.desiredModelId) return;
|
|
5068
|
-
await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId), params.timeoutMs);
|
|
5482
|
+
}), params.suppressWarnings);
|
|
5483
|
+
if (!params.models || params.models.currentModelId === params.desiredModelId) return { replayed: false };
|
|
5484
|
+
const response = await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId, params.models), params.timeoutMs);
|
|
5485
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5486
|
+
const models = response ? modelStateFromConfigOptions(response.configOptions) : {
|
|
5487
|
+
...params.models,
|
|
5488
|
+
currentModelId: params.desiredModelId
|
|
5489
|
+
};
|
|
5069
5490
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5491
|
+
return {
|
|
5492
|
+
replayed: true,
|
|
5493
|
+
models,
|
|
5494
|
+
configOptionsPresent: response !== void 0
|
|
5495
|
+
};
|
|
5070
5496
|
} catch (error) {
|
|
5071
5497
|
throw new SessionModelReplayError(`Failed to replay saved session model ${params.desiredModelId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
|
|
5072
5498
|
cause: error instanceof Error ? error : void 0,
|
|
@@ -5074,9 +5500,18 @@ async function replayDesiredModel(params) {
|
|
|
5074
5500
|
});
|
|
5075
5501
|
}
|
|
5076
5502
|
}
|
|
5503
|
+
function emitModelSupportWarning(warning, suppressWarnings) {
|
|
5504
|
+
if (warning && !suppressWarnings) process.stderr.write(`[acpx] warning: ${warning}\n`);
|
|
5505
|
+
}
|
|
5077
5506
|
async function replayDesiredConfigOptions(params) {
|
|
5507
|
+
let result = { replayed: false };
|
|
5078
5508
|
for (const [configId, value] of Object.entries(params.desiredConfigOptions)) try {
|
|
5079
|
-
await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
|
|
5509
|
+
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
|
|
5510
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5511
|
+
result = {
|
|
5512
|
+
replayed: true,
|
|
5513
|
+
models: modelStateFromConfigOptions(response.configOptions)
|
|
5514
|
+
};
|
|
5080
5515
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5081
5516
|
} catch (error) {
|
|
5082
5517
|
throw new SessionConfigOptionReplayError(`Failed to replay saved session config option ${configId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
|
|
@@ -5084,6 +5519,7 @@ async function replayDesiredConfigOptions(params) {
|
|
|
5084
5519
|
retryable: true
|
|
5085
5520
|
});
|
|
5086
5521
|
}
|
|
5522
|
+
return result;
|
|
5087
5523
|
}
|
|
5088
5524
|
function restoreOriginalSessionState(params) {
|
|
5089
5525
|
params.record.acpSessionId = params.sessionId;
|
|
@@ -5095,6 +5531,7 @@ async function connectAndLoadSession(options) {
|
|
|
5095
5531
|
const sameSessionOnly = requiresSameSession(options.resumePolicy) || Boolean(record.importedFrom);
|
|
5096
5532
|
const originalSessionId = record.acpSessionId;
|
|
5097
5533
|
const originalAgentSessionId = record.agentSessionId;
|
|
5534
|
+
const originalAcpx = cloneSessionAcpxState(record.acpx);
|
|
5098
5535
|
const desiredModeId = getDesiredModeId(record.acpx);
|
|
5099
5536
|
const desiredModelId = getDesiredModelId(record.acpx);
|
|
5100
5537
|
const desiredConfigOptions = getDesiredConfigOptions(record.acpx);
|
|
@@ -5127,7 +5564,7 @@ async function connectAndLoadSession(options) {
|
|
|
5127
5564
|
createdFreshSession = loadState.createdFreshSession;
|
|
5128
5565
|
pendingAgentSessionId = loadState.pendingAgentSessionId;
|
|
5129
5566
|
sessionModels = loadState.sessionModels;
|
|
5130
|
-
await replayFreshSessionPreferences({
|
|
5567
|
+
const preferenceReplay = await replayFreshSessionPreferences({
|
|
5131
5568
|
client,
|
|
5132
5569
|
record,
|
|
5133
5570
|
createdFreshSession,
|
|
@@ -5135,14 +5572,16 @@ async function connectAndLoadSession(options) {
|
|
|
5135
5572
|
pendingAgentSessionId,
|
|
5136
5573
|
originalSessionId,
|
|
5137
5574
|
originalAgentSessionId,
|
|
5575
|
+
originalAcpx,
|
|
5138
5576
|
desiredModeId,
|
|
5139
5577
|
desiredModelId,
|
|
5140
5578
|
desiredConfigOptions,
|
|
5141
5579
|
sessionModels,
|
|
5142
5580
|
timeoutMs: options.timeoutMs,
|
|
5143
|
-
verbose: options.verbose
|
|
5581
|
+
verbose: options.verbose,
|
|
5582
|
+
suppressWarnings: options.suppressWarnings
|
|
5144
5583
|
});
|
|
5145
|
-
applyReconnectedModelState(record, sessionModels,
|
|
5584
|
+
applyReconnectedModelState(record, resolveModelsAfterReplay(preferenceReplay, sessionModels), resolveConfigOptionsPresenceAfterReplay(preferenceReplay, loadState.configOptionsPresent), loadState.legacyModelMetadataPresent, createdFreshSession);
|
|
5146
5585
|
options.onSessionIdResolved?.(sessionId);
|
|
5147
5586
|
return {
|
|
5148
5587
|
sessionId,
|
|
@@ -5151,9 +5590,28 @@ async function connectAndLoadSession(options) {
|
|
|
5151
5590
|
loadError
|
|
5152
5591
|
};
|
|
5153
5592
|
}
|
|
5154
|
-
function
|
|
5155
|
-
|
|
5156
|
-
|
|
5593
|
+
function resolveModelsAfterReplay(replay, initialModels) {
|
|
5594
|
+
if (replay.configReplay.replayed) return replay.configReplay.models ?? preserveLegacyModels(replay.modelReplay.replayed ? replay.modelReplay.models : initialModels);
|
|
5595
|
+
return replay.modelReplay.replayed ? replay.modelReplay.models : initialModels;
|
|
5596
|
+
}
|
|
5597
|
+
function preserveLegacyModels(models) {
|
|
5598
|
+
return models && !models.configId ? models : void 0;
|
|
5599
|
+
}
|
|
5600
|
+
function resolveConfigOptionsPresenceAfterReplay(replay, initiallyPresent) {
|
|
5601
|
+
return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.replayed && replay.modelReplay.configOptionsPresent;
|
|
5602
|
+
}
|
|
5603
|
+
function applyReconnectedModelState(record, sessionModels, configOptionsPresent, legacyModelMetadataPresent, createdFreshSession) {
|
|
5604
|
+
clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent);
|
|
5605
|
+
if (sessionModels) {
|
|
5606
|
+
if (legacyModelMetadataPresent && !sessionModels.configId && record.acpx) removeModelConfigOptions(record.acpx);
|
|
5607
|
+
syncAdvertisedModelState(record, sessionModels);
|
|
5608
|
+
} else clearRemovedModelState(record, legacyModelMetadataPresent || createdFreshSession);
|
|
5609
|
+
}
|
|
5610
|
+
function clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent) {
|
|
5611
|
+
if (createdFreshSession && !configOptionsPresent && record.acpx) delete record.acpx.config_options;
|
|
5612
|
+
}
|
|
5613
|
+
function clearRemovedModelState(record, shouldClear) {
|
|
5614
|
+
if (shouldClear && record.acpx) clearAdvertisedModelState(record.acpx);
|
|
5157
5615
|
}
|
|
5158
5616
|
function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbose) {
|
|
5159
5617
|
if (!verbose) return;
|
|
@@ -5164,7 +5622,12 @@ function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbos
|
|
|
5164
5622
|
if (shouldReconnect) process.stderr.write(`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session reconnect\n`);
|
|
5165
5623
|
}
|
|
5166
5624
|
async function replayFreshSessionPreferences(params) {
|
|
5167
|
-
if (!params.createdFreshSession) return
|
|
5625
|
+
if (!params.createdFreshSession) return {
|
|
5626
|
+
modelReplay: { replayed: false },
|
|
5627
|
+
configReplay: { replayed: false }
|
|
5628
|
+
};
|
|
5629
|
+
let modelReplay = { replayed: false };
|
|
5630
|
+
let configReplay = { replayed: false };
|
|
5168
5631
|
try {
|
|
5169
5632
|
await replayDesiredMode({
|
|
5170
5633
|
client: params.client,
|
|
@@ -5174,7 +5637,7 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5174
5637
|
timeoutMs: params.timeoutMs,
|
|
5175
5638
|
verbose: params.verbose
|
|
5176
5639
|
});
|
|
5177
|
-
await replayDesiredModel({
|
|
5640
|
+
modelReplay = await replayDesiredModel({
|
|
5178
5641
|
client: params.client,
|
|
5179
5642
|
sessionId: params.sessionId,
|
|
5180
5643
|
desiredModelId: params.desiredModelId,
|
|
@@ -5182,10 +5645,12 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5182
5645
|
record: params.record,
|
|
5183
5646
|
models: params.sessionModels,
|
|
5184
5647
|
timeoutMs: params.timeoutMs,
|
|
5185
|
-
verbose: params.verbose
|
|
5648
|
+
verbose: params.verbose,
|
|
5649
|
+
suppressWarnings: params.suppressWarnings
|
|
5186
5650
|
});
|
|
5187
|
-
await replayDesiredConfigOptions({
|
|
5651
|
+
configReplay = await replayDesiredConfigOptions({
|
|
5188
5652
|
client: params.client,
|
|
5653
|
+
record: params.record,
|
|
5189
5654
|
sessionId: params.sessionId,
|
|
5190
5655
|
desiredConfigOptions: params.desiredConfigOptions,
|
|
5191
5656
|
previousSessionId: params.originalSessionId,
|
|
@@ -5198,17 +5663,24 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5198
5663
|
sessionId: params.originalSessionId,
|
|
5199
5664
|
agentSessionId: params.originalAgentSessionId
|
|
5200
5665
|
});
|
|
5666
|
+
params.record.acpx = cloneSessionAcpxState(params.originalAcpx);
|
|
5201
5667
|
if (params.verbose) process.stderr.write(`[acpx] ${formatErrorMessage(error)}\n`);
|
|
5202
5668
|
throw error;
|
|
5203
5669
|
}
|
|
5204
5670
|
params.record.acpSessionId = params.sessionId;
|
|
5205
5671
|
reconcileAgentSessionId(params.record, params.pendingAgentSessionId);
|
|
5672
|
+
return {
|
|
5673
|
+
modelReplay,
|
|
5674
|
+
configReplay
|
|
5675
|
+
};
|
|
5206
5676
|
}
|
|
5207
5677
|
async function loadOrCreateRuntimeSession(params) {
|
|
5208
5678
|
if (params.reusingLoadedSession) return {
|
|
5209
5679
|
sessionId: params.record.acpSessionId,
|
|
5210
5680
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5211
5681
|
sessionModels: void 0,
|
|
5682
|
+
configOptionsPresent: false,
|
|
5683
|
+
legacyModelMetadataPresent: false,
|
|
5212
5684
|
resumed: true,
|
|
5213
5685
|
createdFreshSession: false
|
|
5214
5686
|
};
|
|
@@ -5229,6 +5701,8 @@ async function resumeRuntimeSession(params) {
|
|
|
5229
5701
|
sessionId: params.record.acpSessionId,
|
|
5230
5702
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5231
5703
|
sessionModels: resumeResult.models,
|
|
5704
|
+
configOptionsPresent: resumeResult.configOptionsPresent,
|
|
5705
|
+
legacyModelMetadataPresent: resumeResult.legacyModelMetadataPresent,
|
|
5232
5706
|
resumed: true,
|
|
5233
5707
|
createdFreshSession: false
|
|
5234
5708
|
};
|
|
@@ -5245,6 +5719,8 @@ async function loadRuntimeSession(params) {
|
|
|
5245
5719
|
sessionId: params.record.acpSessionId,
|
|
5246
5720
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5247
5721
|
sessionModels: loadResult.models,
|
|
5722
|
+
configOptionsPresent: loadResult.configOptionsPresent,
|
|
5723
|
+
legacyModelMetadataPresent: loadResult.legacyModelMetadataPresent,
|
|
5248
5724
|
resumed: true,
|
|
5249
5725
|
createdFreshSession: false
|
|
5250
5726
|
};
|
|
@@ -5272,6 +5748,8 @@ async function createFreshRuntimeSession(client, record, timeoutMs) {
|
|
|
5272
5748
|
sessionId: createdSession.sessionId,
|
|
5273
5749
|
pendingAgentSessionId: createdSession.agentSessionId,
|
|
5274
5750
|
sessionModels: createdSession.models,
|
|
5751
|
+
configOptionsPresent: createdSession.configOptionsPresent,
|
|
5752
|
+
legacyModelMetadataPresent: createdSession.legacyModelMetadataPresent,
|
|
5275
5753
|
resumed: false,
|
|
5276
5754
|
createdFreshSession: true
|
|
5277
5755
|
};
|
|
@@ -5287,7 +5765,10 @@ function createActiveSessionController(params) {
|
|
|
5287
5765
|
await params.client.setSessionMode(getActiveSessionId(), modeId);
|
|
5288
5766
|
},
|
|
5289
5767
|
setSessionModel: async (modelId) => {
|
|
5290
|
-
|
|
5768
|
+
const models = advertisedModelState(params.record.acpx);
|
|
5769
|
+
const response = await params.client.setSessionModel(getActiveSessionId(), modelId, models);
|
|
5770
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5771
|
+
return response;
|
|
5291
5772
|
},
|
|
5292
5773
|
setSessionConfigOption: async (configId, value) => {
|
|
5293
5774
|
return await params.client.setSessionConfigOption(getActiveSessionId(), configId, value);
|
|
@@ -5325,6 +5806,7 @@ async function withConnectedSession(options) {
|
|
|
5325
5806
|
let notifiedClientAvailable = false;
|
|
5326
5807
|
const activeController = createActiveSessionController({
|
|
5327
5808
|
client,
|
|
5809
|
+
record,
|
|
5328
5810
|
getActiveSessionId: () => activeSessionIdForControl
|
|
5329
5811
|
});
|
|
5330
5812
|
try {
|
|
@@ -5468,6 +5950,6 @@ var LiveSessionCheckpoint = class {
|
|
|
5468
5950
|
}
|
|
5469
5951
|
};
|
|
5470
5952
|
//#endregion
|
|
5471
|
-
export {
|
|
5953
|
+
export { setPerfGauge as $, assertRequestedModelSupported as A, isRetryablePromptError as At, listSessions as B, OUTPUT_FORMATS as Bt, mergeSessionOptions as C, withTimeout as Ct, applyLifecycleSnapshotToRecord as D, resolveAgentCommand as Dt, applyConversation as E, normalizeAgentName$1 as Et, absolutePath as F, AUTH_POLICIES as Ft, writeSessionRecord as G, AgentSpawnError as Gt, normalizeName as H, PERMISSION_POLICY_ACTIONS as Ht, findGitRepositoryRoot as I, EXIT_CODES as It, getPerfMetricsSnapshot as J, assertPersistedKeyPolicy as K, QueueConnectionError as Kt, findSession as L, NON_INTERACTIVE_PERMISSION_POLICIES as Lt, getAcpxVersion as M, extractAcpError as Mt, permissionModeSatisfies as N, isAcpResourceNotFoundError as Nt, reconcileAgentSessionId as O, exitCodeForOutputErrorCode as Ot, DEFAULT_HISTORY_LIMIT as P, toAcpErrorPayload as Pt, resetPerfMetrics as Q, findSessionByDirectoryWalk as R, OUTPUT_ERROR_CODES as Rt, advertisedModelState as S, withInterrupt as St, sessionOptionsFromRecord as T, listBuiltInAgents as Tt, pruneSessions as U, SESSION_RECORD_SCHEMA as Ut, listSessionsForAgent as V, PERMISSION_MODES as Vt, resolveSessionRecord as W, AcpxOperationalError as Wt, measurePerf as X, incrementPerfCounter as Y, recordPerfDuration as Z, createSessionConversation as _, parsePromptSource as _t, applyRequestedModelIfAdvertised as a, defaultSessionEventLog as at, recordSessionUpdate as b, InterruptedError as bt, setCurrentModelId as c, sessionEventLockPath as ct, setDesiredModelId as d, isAcpJsonRpcMessage as dt, startPerfTimer as et, syncAdvertisedModelState as f, parseJsonRpcErrorMessage as ft, cloneSessionConversation as g, mergePromptSourceWithText as gt, cloneSessionAcpxState as h, isPromptInput as ht, connectAndLoadSession as i, DEFAULT_EVENT_SEGMENT_MAX_BYTES as it, modelStateFromConfigOptions as j, normalizeOutputError as jt, AcpClient as k, formatErrorMessage as kt, setDesiredConfigOption as l, sessionEventSegmentPath as lt, applyConfigOptionsToState as m, PromptInputValidationError as mt, runPromptTurn as n, serializeSessionRecordForDisk as nt, currentModelIdFromSetModelResponse as o, sessionBaseDir$1 as ot, applyConfigOptionsToRecord as p, parsePromptStopReason as pt, formatPerfMetric as q, QueueProtocolError as qt, withConnectedSession as r, normalizeRuntimeSessionId as rt, clearDesiredConfigOption as s, sessionEventActivePath as st, LiveSessionCheckpoint as t, parseSessionRecord as tt, setDesiredModeId as u, extractSessionUpdateNotification as ut, recordClientOperation as v, promptToDisplayText as vt, persistSessionOptions as w, DEFAULT_AGENT_NAME as wt, trimConversationForRuntime as x, TimeoutError as xt, recordPromptSubmission as y, textPrompt as yt, isoNow$2 as z, OUTPUT_ERROR_ORIGINS as zt };
|
|
5472
5954
|
|
|
5473
|
-
//# sourceMappingURL=live-checkpoint-
|
|
5955
|
+
//# sourceMappingURL=live-checkpoint-BZrk9Mjz.js.map
|