acpx 0.10.0 → 0.11.1
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 +25 -19
- package/dist/{cli-8dP_TqBp.js → cli-D4XUKXcD.js} +5 -5
- package/dist/{cli-8dP_TqBp.js.map → cli-D4XUKXcD.js.map} +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +429 -88
- package/dist/cli.js.map +1 -1
- package/dist/{client-C4iJBO0j.d.ts → client-DIlpCkHw.d.ts} +38 -5
- package/dist/client-DIlpCkHw.d.ts.map +1 -0
- package/dist/{flags--2oX_ubW.js → flags-Dvgmpq_l.js} +5 -5
- package/dist/flags-Dvgmpq_l.js.map +1 -0
- package/dist/{flows-e4umXVbY.js → flows-Cvsc-_AW.js} +4 -3
- package/dist/flows-Cvsc-_AW.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-BWkYxMeS.js} +848 -207
- package/dist/live-checkpoint-BWkYxMeS.js.map +1 -0
- package/dist/{output-Di77Yugq.js → output-BEv_BB7T.js} +359 -141
- package/dist/output-BEv_BB7T.js.map +1 -0
- package/dist/runtime.d.ts +71 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +192 -35
- package/dist/runtime.js.map +1 -1
- package/dist/{session-options-Bh1bIqQ2.d.ts → session-options-jkYbBxGE.d.ts} +27 -2
- package/dist/session-options-jkYbBxGE.d.ts.map +1 -0
- package/package.json +21 -20
- package/skills/acpx/SKILL.md +58 -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
- package/dist/session-options-Bh1bIqQ2.d.ts.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
|
|
@@ -222,13 +222,13 @@ const OUTPUT_ERROR_ORIGINS = [
|
|
|
222
222
|
const SESSION_RECORD_SCHEMA = "acpx.session.v1";
|
|
223
223
|
//#endregion
|
|
224
224
|
//#region src/acp/error-shapes.ts
|
|
225
|
-
const RESOURCE_NOT_FOUND_ACP_CODES = new Set([-32001, -32002]);
|
|
226
|
-
function asRecord$
|
|
225
|
+
const RESOURCE_NOT_FOUND_ACP_CODES = /* @__PURE__ */ new Set([-32001, -32002]);
|
|
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
|
}
|
|
@@ -295,9 +295,9 @@ function isAcpResourceNotFoundError(error) {
|
|
|
295
295
|
}
|
|
296
296
|
//#endregion
|
|
297
297
|
//#region src/acp/error-normalization.ts
|
|
298
|
-
const AUTH_REQUIRED_ACP_CODES = new Set([-32e3]);
|
|
298
|
+
const AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ 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,15 +831,18 @@ 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
|
}
|
|
838
|
+
function isAcpMessageObject(value) {
|
|
839
|
+
return asRecord$5(value) !== null;
|
|
840
|
+
}
|
|
835
841
|
function hasValidId(value) {
|
|
836
842
|
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
837
843
|
}
|
|
838
844
|
function isErrorObject(value) {
|
|
839
|
-
const record = asRecord$
|
|
845
|
+
const record = asRecord$5(value);
|
|
840
846
|
return !!record && typeof record.code === "number" && Number.isFinite(record.code) && typeof record.message === "string";
|
|
841
847
|
}
|
|
842
848
|
function hasResultOrError(value) {
|
|
@@ -861,7 +867,7 @@ function isJsonRpcResponse(value) {
|
|
|
861
867
|
return hasResultOrError(value);
|
|
862
868
|
}
|
|
863
869
|
function isAcpJsonRpcMessage(value) {
|
|
864
|
-
const record = asRecord$
|
|
870
|
+
const record = asRecord$5(value);
|
|
865
871
|
if (!record || record.jsonrpc !== "2.0") return false;
|
|
866
872
|
return isJsonRpcNotificationRecord(record) || isJsonRpcRequest(record) || isJsonRpcResponse(record);
|
|
867
873
|
}
|
|
@@ -873,11 +879,11 @@ function isSessionUpdateNotification(message) {
|
|
|
873
879
|
}
|
|
874
880
|
function extractSessionUpdateNotification(message) {
|
|
875
881
|
if (!isSessionUpdateNotification(message)) return;
|
|
876
|
-
const params = asRecord$
|
|
882
|
+
const params = asRecord$5(message.params);
|
|
877
883
|
if (!params) return;
|
|
878
884
|
const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
|
|
879
885
|
if (!sessionId) return;
|
|
880
|
-
const update = asRecord$
|
|
886
|
+
const update = asRecord$5(params.update);
|
|
881
887
|
if (!update || typeof update.sessionUpdate !== "string") return;
|
|
882
888
|
return {
|
|
883
889
|
sessionId,
|
|
@@ -886,13 +892,13 @@ function extractSessionUpdateNotification(message) {
|
|
|
886
892
|
}
|
|
887
893
|
function parsePromptStopReason(message) {
|
|
888
894
|
if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
|
|
889
|
-
const record = asRecord$
|
|
895
|
+
const record = asRecord$5(message.result);
|
|
890
896
|
if (!record) return;
|
|
891
897
|
return typeof record.stopReason === "string" ? record.stopReason : void 0;
|
|
892
898
|
}
|
|
893
899
|
function parseJsonRpcErrorMessage(message) {
|
|
894
900
|
if (!Object.hasOwn(message, "error")) return;
|
|
895
|
-
const errorRecord = asRecord$
|
|
901
|
+
const errorRecord = asRecord$5(message.error);
|
|
896
902
|
if (!errorRecord || typeof errorRecord.message !== "string") return;
|
|
897
903
|
return errorRecord.message;
|
|
898
904
|
}
|
|
@@ -987,6 +993,7 @@ function serializeSessionRecordForDisk(record) {
|
|
|
987
993
|
messages: canonical.messages,
|
|
988
994
|
updated_at: canonical.updated_at,
|
|
989
995
|
cumulative_token_usage: canonical.cumulative_token_usage,
|
|
996
|
+
cumulative_cost: canonical.cumulative_cost,
|
|
990
997
|
request_token_usage: canonical.request_token_usage,
|
|
991
998
|
acpx: canonical.acpx,
|
|
992
999
|
imported_from: canonical.importedFrom ? {
|
|
@@ -999,7 +1006,7 @@ function serializeSessionRecordForDisk(record) {
|
|
|
999
1006
|
}
|
|
1000
1007
|
//#endregion
|
|
1001
1008
|
//#region src/session/persistence/parse.ts
|
|
1002
|
-
function asRecord$
|
|
1009
|
+
function asRecord$4(value) {
|
|
1003
1010
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
1004
1011
|
return value;
|
|
1005
1012
|
}
|
|
@@ -1009,16 +1016,55 @@ function hasOwn$1(source, key) {
|
|
|
1009
1016
|
function isStringArray(value) {
|
|
1010
1017
|
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
1011
1018
|
}
|
|
1019
|
+
function hasModelConfigOption(options) {
|
|
1020
|
+
if (!Array.isArray(options)) return false;
|
|
1021
|
+
return options.some((entry) => {
|
|
1022
|
+
const option = asRecord$4(entry);
|
|
1023
|
+
return option?.category === "model" || option?.id === "model";
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
function parseConfigOptions(raw) {
|
|
1027
|
+
if (!Array.isArray(raw) || !raw.every((entry) => asRecord$4(entry) !== void 0)) return;
|
|
1028
|
+
return raw;
|
|
1029
|
+
}
|
|
1030
|
+
function parseAvailableCommand(raw) {
|
|
1031
|
+
if (typeof raw === "string") {
|
|
1032
|
+
const name = raw.trim();
|
|
1033
|
+
return name ? { name } : void 0;
|
|
1034
|
+
}
|
|
1035
|
+
const record = asRecord$4(raw);
|
|
1036
|
+
if (!record) return;
|
|
1037
|
+
const name = parseNonEmptyString(record.name);
|
|
1038
|
+
if (!name) return;
|
|
1039
|
+
const description = parseNonEmptyString(record.description);
|
|
1040
|
+
return {
|
|
1041
|
+
name,
|
|
1042
|
+
...description ? { description } : {},
|
|
1043
|
+
...typeof record.has_input === "boolean" ? { has_input: record.has_input } : {}
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function parseAvailableCommands(raw) {
|
|
1047
|
+
if (!Array.isArray(raw)) return;
|
|
1048
|
+
const commands = raw.map((entry) => parseAvailableCommand(entry)).filter((entry) => entry !== void 0);
|
|
1049
|
+
return commands.length > 0 ? commands : void 0;
|
|
1050
|
+
}
|
|
1051
|
+
function parseNonEmptyString(value) {
|
|
1052
|
+
if (typeof value !== "string") return;
|
|
1053
|
+
const trimmed = value.trim();
|
|
1054
|
+
return trimmed ? trimmed : void 0;
|
|
1055
|
+
}
|
|
1012
1056
|
function parseTokenUsage(raw) {
|
|
1013
1057
|
if (raw === void 0 || raw === null) return;
|
|
1014
|
-
const record = asRecord$
|
|
1058
|
+
const record = asRecord$4(raw);
|
|
1015
1059
|
if (!record) return null;
|
|
1016
1060
|
const usage = {};
|
|
1017
1061
|
for (const field of [
|
|
1018
1062
|
"input_tokens",
|
|
1019
1063
|
"output_tokens",
|
|
1020
1064
|
"cache_creation_input_tokens",
|
|
1021
|
-
"cache_read_input_tokens"
|
|
1065
|
+
"cache_read_input_tokens",
|
|
1066
|
+
"thought_tokens",
|
|
1067
|
+
"total_tokens"
|
|
1022
1068
|
]) {
|
|
1023
1069
|
const value = record[field];
|
|
1024
1070
|
if (value === void 0) continue;
|
|
@@ -1030,9 +1076,35 @@ function parseTokenUsage(raw) {
|
|
|
1030
1076
|
function isNonNegativeFiniteNumber(value) {
|
|
1031
1077
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1032
1078
|
}
|
|
1079
|
+
function parseUsageCost(raw) {
|
|
1080
|
+
if (raw === void 0 || raw === null) return;
|
|
1081
|
+
const record = asRecord$4(raw);
|
|
1082
|
+
if (!record) return null;
|
|
1083
|
+
return parseUsageCostRecord(record);
|
|
1084
|
+
}
|
|
1085
|
+
function parseUsageCostRecord(record) {
|
|
1086
|
+
const amount = parseCostAmount(record.amount);
|
|
1087
|
+
const currency = parseCostCurrency(record.currency);
|
|
1088
|
+
if (amount === null || currency === null) return null;
|
|
1089
|
+
const cost = {
|
|
1090
|
+
...amount !== void 0 ? { amount } : {},
|
|
1091
|
+
...currency !== void 0 ? { currency } : {}
|
|
1092
|
+
};
|
|
1093
|
+
return Object.keys(cost).length > 0 ? cost : void 0;
|
|
1094
|
+
}
|
|
1095
|
+
function parseCostAmount(value) {
|
|
1096
|
+
if (value === void 0) return;
|
|
1097
|
+
return isNonNegativeFiniteNumber(value) ? value : null;
|
|
1098
|
+
}
|
|
1099
|
+
function parseCostCurrency(value) {
|
|
1100
|
+
if (value === void 0) return;
|
|
1101
|
+
if (typeof value !== "string") return null;
|
|
1102
|
+
const currency = value.trim();
|
|
1103
|
+
return currency.length > 0 ? currency : void 0;
|
|
1104
|
+
}
|
|
1033
1105
|
function parseRequestTokenUsage(raw) {
|
|
1034
1106
|
if (raw === void 0 || raw === null) return;
|
|
1035
|
-
const record = asRecord$
|
|
1107
|
+
const record = asRecord$4(raw);
|
|
1036
1108
|
if (!record) return null;
|
|
1037
1109
|
const usage = {};
|
|
1038
1110
|
for (const [key, value] of Object.entries(record)) {
|
|
@@ -1043,25 +1115,25 @@ function parseRequestTokenUsage(raw) {
|
|
|
1043
1115
|
return usage;
|
|
1044
1116
|
}
|
|
1045
1117
|
function isSessionMessageImage(raw) {
|
|
1046
|
-
const record = asRecord$
|
|
1118
|
+
const record = asRecord$4(raw);
|
|
1047
1119
|
if (!record || typeof record.source !== "string") return false;
|
|
1048
1120
|
if (record.size === void 0 || record.size === null) return true;
|
|
1049
|
-
const size = asRecord$
|
|
1121
|
+
const size = asRecord$4(record.size);
|
|
1050
1122
|
return !!size && isFiniteNumber(size.width) && isFiniteNumber(size.height);
|
|
1051
1123
|
}
|
|
1052
1124
|
function isSessionMessageAudio(raw) {
|
|
1053
|
-
const record = asRecord$
|
|
1125
|
+
const record = asRecord$4(raw);
|
|
1054
1126
|
return !!record && typeof record.source === "string" && typeof record.mime_type === "string";
|
|
1055
1127
|
}
|
|
1056
1128
|
function isFiniteNumber(value) {
|
|
1057
1129
|
return typeof value === "number" && Number.isFinite(value);
|
|
1058
1130
|
}
|
|
1059
1131
|
function isUserContent(raw) {
|
|
1060
|
-
const record = asRecord$
|
|
1132
|
+
const record = asRecord$4(raw);
|
|
1061
1133
|
if (!record) return false;
|
|
1062
1134
|
if (typeof record.Text === "string") return true;
|
|
1063
1135
|
if (record.Mention !== void 0) {
|
|
1064
|
-
const mention = asRecord$
|
|
1136
|
+
const mention = asRecord$4(record.Mention);
|
|
1065
1137
|
return !!mention && typeof mention.uri === "string" && typeof mention.content === "string";
|
|
1066
1138
|
}
|
|
1067
1139
|
if (record.Image !== void 0) return isSessionMessageImage(record.Image);
|
|
@@ -1069,7 +1141,7 @@ function isUserContent(raw) {
|
|
|
1069
1141
|
return false;
|
|
1070
1142
|
}
|
|
1071
1143
|
function isToolUse(raw) {
|
|
1072
|
-
const record = asRecord$
|
|
1144
|
+
const record = asRecord$4(raw);
|
|
1073
1145
|
return !!record && hasStringFields(record, [
|
|
1074
1146
|
"id",
|
|
1075
1147
|
"name",
|
|
@@ -1083,18 +1155,18 @@ function isOptionalString(value) {
|
|
|
1083
1155
|
return value === void 0 || value === null || typeof value === "string";
|
|
1084
1156
|
}
|
|
1085
1157
|
function isToolResultContent(raw) {
|
|
1086
|
-
const record = asRecord$
|
|
1158
|
+
const record = asRecord$4(raw);
|
|
1087
1159
|
if (!record) return false;
|
|
1088
1160
|
if (typeof record.Text === "string") return true;
|
|
1089
1161
|
if (record.Image !== void 0) return isSessionMessageImage(record.Image);
|
|
1090
1162
|
return false;
|
|
1091
1163
|
}
|
|
1092
1164
|
function isToolResult(raw) {
|
|
1093
|
-
const record = asRecord$
|
|
1165
|
+
const record = asRecord$4(raw);
|
|
1094
1166
|
return !!record && typeof record.tool_use_id === "string" && typeof record.tool_name === "string" && typeof record.is_error === "boolean" && isToolResultContent(record.content);
|
|
1095
1167
|
}
|
|
1096
1168
|
function isAgentContent(raw) {
|
|
1097
|
-
const record = asRecord$
|
|
1169
|
+
const record = asRecord$4(raw);
|
|
1098
1170
|
if (!record) return false;
|
|
1099
1171
|
if (typeof record.Text === "string") return true;
|
|
1100
1172
|
if (record.Thinking !== void 0) return isThinkingContent(record.Thinking);
|
|
@@ -1103,21 +1175,21 @@ function isAgentContent(raw) {
|
|
|
1103
1175
|
return false;
|
|
1104
1176
|
}
|
|
1105
1177
|
function isThinkingContent(raw) {
|
|
1106
|
-
const thinking = asRecord$
|
|
1178
|
+
const thinking = asRecord$4(raw);
|
|
1107
1179
|
return !!thinking && typeof thinking.text === "string" && isOptionalString(thinking.signature);
|
|
1108
1180
|
}
|
|
1109
1181
|
function isUserMessage$1(raw) {
|
|
1110
|
-
const record = asRecord$
|
|
1182
|
+
const record = asRecord$4(raw);
|
|
1111
1183
|
if (!record || record.User === void 0) return false;
|
|
1112
|
-
const user = asRecord$
|
|
1184
|
+
const user = asRecord$4(record.User);
|
|
1113
1185
|
return !!user && typeof user.id === "string" && Array.isArray(user.content) && user.content.every((entry) => isUserContent(entry));
|
|
1114
1186
|
}
|
|
1115
1187
|
function isAgentMessage$1(raw) {
|
|
1116
|
-
const record = asRecord$
|
|
1188
|
+
const record = asRecord$4(raw);
|
|
1117
1189
|
if (!record || record.Agent === void 0) return false;
|
|
1118
|
-
const agent = asRecord$
|
|
1190
|
+
const agent = asRecord$4(record.Agent);
|
|
1119
1191
|
if (!agent || !Array.isArray(agent.content) || !agent.content.every(isAgentContent)) return false;
|
|
1120
|
-
const toolResults = asRecord$
|
|
1192
|
+
const toolResults = asRecord$4(agent.tool_results);
|
|
1121
1193
|
if (!toolResults) return false;
|
|
1122
1194
|
return Object.values(toolResults).every(isToolResult);
|
|
1123
1195
|
}
|
|
@@ -1129,13 +1201,15 @@ function parseConversationRecord(record) {
|
|
|
1129
1201
|
const title = parseConversationTitle(record.title);
|
|
1130
1202
|
if (title === INVALID_VALUE) return;
|
|
1131
1203
|
const cumulativeTokenUsage = parseTokenUsage(record.cumulative_token_usage);
|
|
1204
|
+
const cumulativeCost = parseUsageCost(record.cumulative_cost);
|
|
1132
1205
|
const requestTokenUsage = parseRequestTokenUsage(record.request_token_usage);
|
|
1133
|
-
if (cumulativeTokenUsage === null || requestTokenUsage === null) return;
|
|
1206
|
+
if (cumulativeTokenUsage === null || cumulativeCost === null || requestTokenUsage === null) return;
|
|
1134
1207
|
return {
|
|
1135
1208
|
title,
|
|
1136
1209
|
messages: record.messages,
|
|
1137
1210
|
updated_at: record.updated_at,
|
|
1138
1211
|
cumulative_token_usage: cumulativeTokenUsage ?? {},
|
|
1212
|
+
cumulative_cost: cumulativeCost,
|
|
1139
1213
|
request_token_usage: requestTokenUsage ?? {}
|
|
1140
1214
|
};
|
|
1141
1215
|
}
|
|
@@ -1148,20 +1222,27 @@ function hasValidConversationCore(record) {
|
|
|
1148
1222
|
return Array.isArray(record.messages) && record.messages.every(isConversationMessage) && typeof record.updated_at === "string";
|
|
1149
1223
|
}
|
|
1150
1224
|
function parseAcpxState(raw) {
|
|
1151
|
-
const record = asRecord$
|
|
1225
|
+
const record = asRecord$4(raw);
|
|
1152
1226
|
if (!record) return;
|
|
1153
1227
|
const state = {};
|
|
1154
1228
|
assignBooleanTrue(state, "reset_on_next_ensure", record.reset_on_next_ensure);
|
|
1155
1229
|
assignStringState(state, "current_mode_id", record.current_mode_id);
|
|
1156
1230
|
assignStringState(state, "desired_mode_id", record.desired_mode_id);
|
|
1157
1231
|
assignDesiredConfigOptions(state, record.desired_config_options);
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
if (
|
|
1161
|
-
if (Array.isArray(record.config_options)) state.config_options = record.config_options;
|
|
1232
|
+
assignParsedModelState(state, record);
|
|
1233
|
+
const availableCommands = parseAvailableCommands(record.available_commands);
|
|
1234
|
+
if (availableCommands) state.available_commands = availableCommands;
|
|
1162
1235
|
assignParsedSessionOptions(state, record.session_options);
|
|
1163
1236
|
return state;
|
|
1164
1237
|
}
|
|
1238
|
+
function assignParsedModelState(state, record) {
|
|
1239
|
+
assignStringState(state, "current_model_id", record.current_model_id);
|
|
1240
|
+
if (isStringArray(record.available_models)) state.available_models = [...record.available_models];
|
|
1241
|
+
if (record.model_control === "config_option" || record.model_control === "legacy_set_model") state.model_control = record.model_control;
|
|
1242
|
+
const configOptions = parseConfigOptions(record.config_options);
|
|
1243
|
+
if (configOptions) state.config_options = configOptions;
|
|
1244
|
+
if (state.model_control === void 0 && state.available_models !== void 0) state.model_control = hasModelConfigOption(state.config_options) ? "config_option" : "legacy_set_model";
|
|
1245
|
+
}
|
|
1165
1246
|
function assignBooleanTrue(state, key, value) {
|
|
1166
1247
|
if (value === true) state[key] = true;
|
|
1167
1248
|
}
|
|
@@ -1169,7 +1250,7 @@ function assignStringState(state, key, value) {
|
|
|
1169
1250
|
if (typeof value === "string") state[key] = value;
|
|
1170
1251
|
}
|
|
1171
1252
|
function assignDesiredConfigOptions(state, raw) {
|
|
1172
|
-
const desiredConfigOptions = asRecord$
|
|
1253
|
+
const desiredConfigOptions = asRecord$4(raw);
|
|
1173
1254
|
if (!desiredConfigOptions) return;
|
|
1174
1255
|
const parsed = Object.fromEntries(Object.entries(desiredConfigOptions).filter((entry) => {
|
|
1175
1256
|
const [, value] = entry;
|
|
@@ -1178,13 +1259,14 @@ function assignDesiredConfigOptions(state, raw) {
|
|
|
1178
1259
|
if (Object.keys(parsed).length > 0) state.desired_config_options = parsed;
|
|
1179
1260
|
}
|
|
1180
1261
|
function assignParsedSessionOptions(state, raw) {
|
|
1181
|
-
const sessionOptions = asRecord$
|
|
1262
|
+
const sessionOptions = asRecord$4(raw);
|
|
1182
1263
|
if (!sessionOptions) return;
|
|
1183
1264
|
const parsedSessionOptions = {};
|
|
1184
1265
|
assignSessionOptionModel(parsedSessionOptions, sessionOptions.model);
|
|
1185
1266
|
assignSessionOptionAllowedTools(parsedSessionOptions, sessionOptions.allowed_tools);
|
|
1186
1267
|
assignSessionOptionMaxTurns(parsedSessionOptions, sessionOptions.max_turns);
|
|
1187
1268
|
assignSessionOptionSystemPrompt(parsedSessionOptions, sessionOptions.system_prompt);
|
|
1269
|
+
assignSessionOptionEnv(parsedSessionOptions, sessionOptions.env);
|
|
1188
1270
|
if (Object.keys(parsedSessionOptions).length > 0) state.session_options = parsedSessionOptions;
|
|
1189
1271
|
}
|
|
1190
1272
|
function assignSessionOptionModel(options, value) {
|
|
@@ -1201,11 +1283,20 @@ function assignSessionOptionSystemPrompt(options, value) {
|
|
|
1201
1283
|
options.system_prompt = value;
|
|
1202
1284
|
return;
|
|
1203
1285
|
}
|
|
1204
|
-
const appendRecord = asRecord$
|
|
1286
|
+
const appendRecord = asRecord$4(value);
|
|
1205
1287
|
if (appendRecord && typeof appendRecord.append === "string" && appendRecord.append.length > 0) options.system_prompt = { append: appendRecord.append };
|
|
1206
1288
|
}
|
|
1289
|
+
function assignSessionOptionEnv(options, value) {
|
|
1290
|
+
const env = asRecord$4(value);
|
|
1291
|
+
if (!env) return;
|
|
1292
|
+
const parsed = Object.fromEntries(Object.entries(env).filter((entry) => {
|
|
1293
|
+
const [, raw] = entry;
|
|
1294
|
+
return typeof raw === "string";
|
|
1295
|
+
}));
|
|
1296
|
+
if (Object.keys(parsed).length > 0) options.env = parsed;
|
|
1297
|
+
}
|
|
1207
1298
|
function parseEventLog(raw, sessionId) {
|
|
1208
|
-
const record = asRecord$
|
|
1299
|
+
const record = asRecord$4(raw);
|
|
1209
1300
|
if (!record || !hasValidEventLogCore(record)) return defaultSessionEventLog(sessionId);
|
|
1210
1301
|
return {
|
|
1211
1302
|
active_path: record.active_path,
|
|
@@ -1224,7 +1315,7 @@ function isPositiveInteger(value) {
|
|
|
1224
1315
|
}
|
|
1225
1316
|
function parseImportedFrom(raw) {
|
|
1226
1317
|
if (raw == null) return;
|
|
1227
|
-
const record = asRecord$
|
|
1318
|
+
const record = asRecord$4(raw);
|
|
1228
1319
|
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
1320
|
return {
|
|
1230
1321
|
recordId: record.record_id,
|
|
@@ -1275,7 +1366,7 @@ function normalizeOptionalSignal(value) {
|
|
|
1275
1366
|
return Symbol("invalid");
|
|
1276
1367
|
}
|
|
1277
1368
|
function parseSessionRecord(raw) {
|
|
1278
|
-
const record = asRecord$
|
|
1369
|
+
const record = asRecord$4(raw);
|
|
1279
1370
|
if (!record) return null;
|
|
1280
1371
|
if (record.schema !== "acpx.session.v1") return null;
|
|
1281
1372
|
const optionals = validSessionOptionals({
|
|
@@ -1319,11 +1410,12 @@ function parseSessionRecord(raw) {
|
|
|
1319
1410
|
lastAgentExitAt: optionals.lastAgentExitAt,
|
|
1320
1411
|
lastAgentDisconnectReason: optionals.lastAgentDisconnectReason,
|
|
1321
1412
|
protocolVersion: typeof record.protocol_version === "number" ? record.protocol_version : void 0,
|
|
1322
|
-
agentCapabilities: asRecord$
|
|
1413
|
+
agentCapabilities: asRecord$4(record.agent_capabilities),
|
|
1323
1414
|
title: conversation.title,
|
|
1324
1415
|
messages: conversation.messages,
|
|
1325
1416
|
updated_at: conversation.updated_at,
|
|
1326
1417
|
cumulative_token_usage: conversation.cumulative_token_usage,
|
|
1418
|
+
cumulative_cost: conversation.cumulative_cost,
|
|
1327
1419
|
request_token_usage: conversation.request_token_usage,
|
|
1328
1420
|
acpx: parseAcpxState(record.acpx),
|
|
1329
1421
|
importedFrom: metadata.importedFrom
|
|
@@ -1427,7 +1519,7 @@ function formatPerfMetric(name, durationMsValue) {
|
|
|
1427
1519
|
//#endregion
|
|
1428
1520
|
//#region src/persisted-key-policy.ts
|
|
1429
1521
|
const SNAKE_CASE_KEY = /^[a-z][a-z0-9_]*$/;
|
|
1430
|
-
const ZED_TAG_KEYS = new Set([
|
|
1522
|
+
const ZED_TAG_KEYS = /* @__PURE__ */ new Set([
|
|
1431
1523
|
"User",
|
|
1432
1524
|
"Agent",
|
|
1433
1525
|
"Resume",
|
|
@@ -1439,8 +1531,8 @@ const ZED_TAG_KEYS = new Set([
|
|
|
1439
1531
|
"RedactedThinking",
|
|
1440
1532
|
"ToolUse"
|
|
1441
1533
|
]);
|
|
1442
|
-
const MAP_OBJECT_PATHS = new Set(["request_token_usage", "messages.Agent.tool_results"]);
|
|
1443
|
-
const OPAQUE_VALUE_PATHS = new Set([
|
|
1534
|
+
const MAP_OBJECT_PATHS = /* @__PURE__ */ new Set(["request_token_usage", "messages.Agent.tool_results"]);
|
|
1535
|
+
const OPAQUE_VALUE_PATHS = /* @__PURE__ */ new Set([
|
|
1444
1536
|
"agent_capabilities",
|
|
1445
1537
|
"messages.Agent.content.ToolUse.input",
|
|
1446
1538
|
"acpx.desired_config_options",
|
|
@@ -1498,12 +1590,12 @@ function assertPersistedKeyPolicy(value) {
|
|
|
1498
1590
|
//#endregion
|
|
1499
1591
|
//#region src/session/persistence/index.ts
|
|
1500
1592
|
const SESSION_INDEX_SCHEMA = "acpx.session-index.v1";
|
|
1501
|
-
function asRecord$
|
|
1593
|
+
function asRecord$3(value) {
|
|
1502
1594
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
1503
1595
|
return value;
|
|
1504
1596
|
}
|
|
1505
1597
|
function parseIndexEntry(raw) {
|
|
1506
|
-
const record = asRecord$
|
|
1598
|
+
const record = asRecord$3(raw);
|
|
1507
1599
|
if (!record) return;
|
|
1508
1600
|
if (!hasRequiredIndexEntryFields(record)) return;
|
|
1509
1601
|
if (record.name !== void 0 && typeof record.name !== "string") return;
|
|
@@ -1547,7 +1639,7 @@ async function readSessionIndex(sessionDir) {
|
|
|
1547
1639
|
const filePath = sessionIndexPath(sessionDir);
|
|
1548
1640
|
try {
|
|
1549
1641
|
const payload = await fs$1.readFile(filePath, "utf8");
|
|
1550
|
-
const record = asRecord$
|
|
1642
|
+
const record = asRecord$3(JSON.parse(payload));
|
|
1551
1643
|
if (!record || record.schema !== SESSION_INDEX_SCHEMA || !Array.isArray(record.files)) return;
|
|
1552
1644
|
const files = record.files.filter((entry) => typeof entry === "string");
|
|
1553
1645
|
if (files.length !== record.files.length || !Array.isArray(record.entries)) return;
|
|
@@ -1645,7 +1737,7 @@ async function writeSessionRecord(record) {
|
|
|
1645
1737
|
const entries = index.entries.filter((entry) => entry.file !== fileName);
|
|
1646
1738
|
entries.push(toSessionIndexEntry(record, fileName));
|
|
1647
1739
|
await writeSessionIndex(sessionDir, {
|
|
1648
|
-
files: [
|
|
1740
|
+
files: [.../* @__PURE__ */ new Set([...index.files.filter((entry) => entry !== fileName), fileName])],
|
|
1649
1741
|
entries
|
|
1650
1742
|
});
|
|
1651
1743
|
});
|
|
@@ -2259,11 +2351,42 @@ function findExistingCommandInDirectory(directory, candidates) {
|
|
|
2259
2351
|
if (trimmedDirectory.length === 0) return;
|
|
2260
2352
|
return candidates.map((candidate) => path.join(trimmedDirectory, candidate)).find((resolved) => fs.existsSync(resolved));
|
|
2261
2353
|
}
|
|
2354
|
+
function resolveWindowsWrapperToken(token, wrapperPath) {
|
|
2355
|
+
const relative = token.match(/%~?dp0%?\s*[\\/]*(.*)$/i)?.[1]?.trim();
|
|
2356
|
+
if (!relative) return;
|
|
2357
|
+
const candidate = path.resolve(path.dirname(wrapperPath), relative.replace(/[\\/]+/g, path.sep).replace(/^[\\/]+/, ""));
|
|
2358
|
+
return path.extname(candidate).toLowerCase() === ".exe" && fs.existsSync(candidate) ? candidate : void 0;
|
|
2359
|
+
}
|
|
2360
|
+
function resolveWindowsWrapperExecutable(wrapperPath) {
|
|
2361
|
+
if (!fs.existsSync(wrapperPath)) return;
|
|
2362
|
+
try {
|
|
2363
|
+
return [...fs.readFileSync(wrapperPath, "utf8").matchAll(/"([^"\r\n]*)"/g)].map((match) => resolveWindowsWrapperToken(match[1] ?? "", wrapperPath)).find((candidate) => candidate !== void 0);
|
|
2364
|
+
} catch {
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2262
2368
|
function resolveWindowsCommand(command, env = process.env) {
|
|
2263
2369
|
const candidates = commandCandidates(command, env);
|
|
2264
2370
|
if (commandHasPath(command)) return candidates.find((candidate) => fs.existsSync(candidate));
|
|
2265
2371
|
return resolveWindowsPathCommand(command, env);
|
|
2266
2372
|
}
|
|
2373
|
+
/**
|
|
2374
|
+
* Resolve a Windows command to a native executable suitable for direct spawn.
|
|
2375
|
+
*
|
|
2376
|
+
* Batch and PowerShell shims are intentionally rejected unless they point at a
|
|
2377
|
+
* real `.exe` entrypoint. Callers that need shell execution should use the
|
|
2378
|
+
* command-specific shell policy instead.
|
|
2379
|
+
*/
|
|
2380
|
+
function resolveWindowsExecutablePath(command, env = process.env) {
|
|
2381
|
+
const resolved = resolveWindowsCommand(command, env);
|
|
2382
|
+
if (!resolved) return;
|
|
2383
|
+
const absolute = path.resolve(resolved);
|
|
2384
|
+
const extension = path.extname(absolute).toLowerCase();
|
|
2385
|
+
if (extension === ".exe") return absolute;
|
|
2386
|
+
if (extension !== ".cmd" && extension !== ".bat" && extension !== ".ps1") return;
|
|
2387
|
+
const siblingExecutable = `${absolute.slice(0, -extension.length)}.exe`;
|
|
2388
|
+
return fs.existsSync(siblingExecutable) ? siblingExecutable : resolveWindowsWrapperExecutable(absolute);
|
|
2389
|
+
}
|
|
2267
2390
|
function shouldUseWindowsBatchShell(command, platform = process.platform, env = process.env) {
|
|
2268
2391
|
if (platform !== "win32") return false;
|
|
2269
2392
|
const resolvedCommand = resolveWindowsCommand(command, env) ?? command;
|
|
@@ -2302,6 +2425,49 @@ function buildTerminalShellSpawnCommand(command, platform = process.platform) {
|
|
|
2302
2425
|
};
|
|
2303
2426
|
}
|
|
2304
2427
|
//#endregion
|
|
2428
|
+
//#region src/version.ts
|
|
2429
|
+
const UNKNOWN_VERSION = "0.0.0-unknown";
|
|
2430
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
2431
|
+
let cachedVersion = null;
|
|
2432
|
+
function parseVersion(value) {
|
|
2433
|
+
if (typeof value !== "string") return null;
|
|
2434
|
+
const trimmed = value.trim();
|
|
2435
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2436
|
+
}
|
|
2437
|
+
function readPackageVersion(packageJsonPath) {
|
|
2438
|
+
try {
|
|
2439
|
+
return parseVersion(JSON.parse(readFileSync(packageJsonPath, "utf8")).version);
|
|
2440
|
+
} catch {
|
|
2441
|
+
return null;
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
function resolveVersionFromAncestors(startDir) {
|
|
2445
|
+
let current = startDir;
|
|
2446
|
+
while (true) {
|
|
2447
|
+
const packageVersion = readPackageVersion(path.join(current, "package.json"));
|
|
2448
|
+
if (packageVersion) return packageVersion;
|
|
2449
|
+
const parent = path.dirname(current);
|
|
2450
|
+
if (parent === current) return null;
|
|
2451
|
+
current = parent;
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
function resolveAcpxVersion(params) {
|
|
2455
|
+
const envVersion = resolvePackageEnvVersion(params?.env ?? process.env);
|
|
2456
|
+
if (envVersion) return envVersion;
|
|
2457
|
+
if (params?.packageJsonPath) return readPackageVersion(params.packageJsonPath) ?? UNKNOWN_VERSION;
|
|
2458
|
+
return resolveVersionFromAncestors(MODULE_DIR) ?? UNKNOWN_VERSION;
|
|
2459
|
+
}
|
|
2460
|
+
function resolvePackageEnvVersion(env) {
|
|
2461
|
+
const envPackageName = parseVersion(env.npm_package_name);
|
|
2462
|
+
const envVersion = parseVersion(env.npm_package_version);
|
|
2463
|
+
return envPackageName === "acpx" ? envVersion : null;
|
|
2464
|
+
}
|
|
2465
|
+
function getAcpxVersion() {
|
|
2466
|
+
if (cachedVersion) return cachedVersion;
|
|
2467
|
+
cachedVersion = resolveAcpxVersion();
|
|
2468
|
+
return cachedVersion;
|
|
2469
|
+
}
|
|
2470
|
+
//#endregion
|
|
2305
2471
|
//#region src/acp/client-process.ts
|
|
2306
2472
|
const execFileAsync = promisify(execFile);
|
|
2307
2473
|
function isoNow$1() {
|
|
@@ -2355,22 +2521,29 @@ function splitCommandLine(value) {
|
|
|
2355
2521
|
let current = "";
|
|
2356
2522
|
let quote = null;
|
|
2357
2523
|
let escaping = false;
|
|
2524
|
+
let hasPart = false;
|
|
2358
2525
|
for (const ch of value) {
|
|
2359
2526
|
const next = readCommandLineChar({
|
|
2360
2527
|
ch,
|
|
2361
2528
|
current,
|
|
2362
2529
|
quote,
|
|
2363
2530
|
escaping,
|
|
2364
|
-
parts
|
|
2531
|
+
parts,
|
|
2532
|
+
hasPart
|
|
2365
2533
|
});
|
|
2366
2534
|
current = next.current;
|
|
2367
2535
|
quote = next.quote;
|
|
2368
2536
|
escaping = next.escaping;
|
|
2537
|
+
hasPart = next.hasPart;
|
|
2538
|
+
}
|
|
2539
|
+
if (escaping) {
|
|
2540
|
+
current += "\\";
|
|
2541
|
+
hasPart = true;
|
|
2369
2542
|
}
|
|
2370
|
-
if (escaping) current += "\\";
|
|
2371
2543
|
if (quote) throw new Error("Invalid --agent command: unterminated quote");
|
|
2372
|
-
if (
|
|
2544
|
+
if (hasPart) parts.push(current);
|
|
2373
2545
|
if (parts.length === 0) throw new Error("Invalid --agent command: empty command");
|
|
2546
|
+
if (parts[0] === "") throw new Error("Invalid --agent command: empty command");
|
|
2374
2547
|
return {
|
|
2375
2548
|
command: parts[0],
|
|
2376
2549
|
args: parts.slice(1)
|
|
@@ -2380,17 +2553,20 @@ function readCommandLineChar(state) {
|
|
|
2380
2553
|
if (state.escaping) return {
|
|
2381
2554
|
current: state.current + state.ch,
|
|
2382
2555
|
quote: state.quote,
|
|
2383
|
-
escaping: false
|
|
2556
|
+
escaping: false,
|
|
2557
|
+
hasPart: true
|
|
2384
2558
|
};
|
|
2385
2559
|
if (state.ch === "\\" && state.quote !== "'") return {
|
|
2386
2560
|
current: state.current,
|
|
2387
2561
|
quote: state.quote,
|
|
2388
|
-
escaping: true
|
|
2562
|
+
escaping: true,
|
|
2563
|
+
hasPart: state.hasPart
|
|
2389
2564
|
};
|
|
2390
2565
|
if (state.quote) return readQuotedCommandLineChar({
|
|
2391
2566
|
ch: state.ch,
|
|
2392
2567
|
current: state.current,
|
|
2393
|
-
quote: state.quote
|
|
2568
|
+
quote: state.quote,
|
|
2569
|
+
hasPart: state.hasPart
|
|
2394
2570
|
});
|
|
2395
2571
|
return readUnquotedCommandLineChar(state);
|
|
2396
2572
|
}
|
|
@@ -2398,36 +2574,41 @@ function readQuotedCommandLineChar(state) {
|
|
|
2398
2574
|
if (state.ch === state.quote) return {
|
|
2399
2575
|
current: state.current,
|
|
2400
2576
|
quote: null,
|
|
2401
|
-
escaping: false
|
|
2577
|
+
escaping: false,
|
|
2578
|
+
hasPart: true
|
|
2402
2579
|
};
|
|
2403
2580
|
return {
|
|
2404
2581
|
current: state.current + state.ch,
|
|
2405
2582
|
quote: state.quote,
|
|
2406
|
-
escaping: false
|
|
2583
|
+
escaping: false,
|
|
2584
|
+
hasPart: true
|
|
2407
2585
|
};
|
|
2408
2586
|
}
|
|
2409
2587
|
function readUnquotedCommandLineChar(state) {
|
|
2410
2588
|
if (state.ch === "'" || state.ch === "\"") return {
|
|
2411
2589
|
current: state.current,
|
|
2412
2590
|
quote: state.ch,
|
|
2413
|
-
escaping: false
|
|
2591
|
+
escaping: false,
|
|
2592
|
+
hasPart: true
|
|
2414
2593
|
};
|
|
2415
2594
|
if (/\s/.test(state.ch)) {
|
|
2416
|
-
flushCommandLinePart(state.parts, state.current);
|
|
2595
|
+
flushCommandLinePart(state.parts, state.current, state.hasPart);
|
|
2417
2596
|
return {
|
|
2418
2597
|
current: "",
|
|
2419
2598
|
quote: null,
|
|
2420
|
-
escaping: false
|
|
2599
|
+
escaping: false,
|
|
2600
|
+
hasPart: false
|
|
2421
2601
|
};
|
|
2422
2602
|
}
|
|
2423
2603
|
return {
|
|
2424
2604
|
current: state.current + state.ch,
|
|
2425
2605
|
quote: null,
|
|
2426
|
-
escaping: false
|
|
2606
|
+
escaping: false,
|
|
2607
|
+
hasPart: true
|
|
2427
2608
|
};
|
|
2428
2609
|
}
|
|
2429
|
-
function flushCommandLinePart(parts, current) {
|
|
2430
|
-
if (
|
|
2610
|
+
function flushCommandLinePart(parts, current, hasPart) {
|
|
2611
|
+
if (hasPart) parts.push(current);
|
|
2431
2612
|
}
|
|
2432
2613
|
function asAbsoluteCwd(cwd) {
|
|
2433
2614
|
return path.resolve(cwd);
|
|
@@ -2477,7 +2658,8 @@ const GEMINI_ACP_FLAG_VERSION = [
|
|
|
2477
2658
|
0
|
|
2478
2659
|
];
|
|
2479
2660
|
const COPILOT_HELP_TIMEOUT_MS = 2e3;
|
|
2480
|
-
const
|
|
2661
|
+
const CLAUDE_CODE_DEFAULT_SETTING_SOURCES = ["project", "local"];
|
|
2662
|
+
const QODER_BENIGN_STDOUT_LINES = /* @__PURE__ */ new Set(["Received interrupt signal. Cleaning up resources...", "Cleanup completed. Exiting..."]);
|
|
2481
2663
|
function resolveAgentCloseAfterStdinEndMs(agentCommand) {
|
|
2482
2664
|
const { command } = splitCommandLine(agentCommand);
|
|
2483
2665
|
return basenameToken(command) === "qodercli" ? QODER_AGENT_CLOSE_AFTER_STDIN_END_MS : DEFAULT_AGENT_CLOSE_AFTER_STDIN_END_MS;
|
|
@@ -2499,6 +2681,13 @@ function isCopilotAcpCommand(command, args) {
|
|
|
2499
2681
|
function isQoderAcpCommand(command, args) {
|
|
2500
2682
|
return basenameToken(command) === "qodercli" && args.includes("--acp");
|
|
2501
2683
|
}
|
|
2684
|
+
function isCursorAcpCommand(command, args) {
|
|
2685
|
+
const commandToken = basenameToken(command);
|
|
2686
|
+
return commandToken === "cursor-agent" || commandToken === "agent" && args.includes("acp");
|
|
2687
|
+
}
|
|
2688
|
+
function isDevinAcpCommand(command, args) {
|
|
2689
|
+
return basenameToken(command) === "devin" && (args.includes("acp") || args.includes("--acp") || args.includes("--experimental-acp"));
|
|
2690
|
+
}
|
|
2502
2691
|
function hasCommandFlag(args, flagName) {
|
|
2503
2692
|
return args.some((arg) => arg === flagName || arg.startsWith(`${flagName}=`));
|
|
2504
2693
|
}
|
|
@@ -2639,16 +2828,20 @@ async function ensureCopilotAcpSupport(command) {
|
|
|
2639
2828
|
const helpOutput = await readCommandOutput(command, ["--help"], COPILOT_HELP_TIMEOUT_MS);
|
|
2640
2829
|
if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) throw new CopilotAcpUnsupportedError(await buildCopilotAcpUnsupportedMessage(command), { retryable: false });
|
|
2641
2830
|
}
|
|
2642
|
-
function buildClaudeCodeOptionsMeta(options) {
|
|
2643
|
-
if (!options) return;
|
|
2831
|
+
function buildClaudeCodeOptionsMeta(options, isolateUserSettings = false) {
|
|
2644
2832
|
const claudeCodeOptions = {};
|
|
2645
|
-
|
|
2833
|
+
if (isolateUserSettings) claudeCodeOptions.settingSources = resolveClaudeCodeSettingSources();
|
|
2834
|
+
if (options) assignClaudeCodeOptions(claudeCodeOptions, options);
|
|
2646
2835
|
const meta = {};
|
|
2647
2836
|
if (Object.keys(claudeCodeOptions).length > 0) meta.claudeCode = { options: claudeCodeOptions };
|
|
2648
|
-
assignClaudeCodeSystemPrompt(meta, options
|
|
2837
|
+
assignClaudeCodeSystemPrompt(meta, options?.systemPrompt);
|
|
2649
2838
|
if (Object.keys(meta).length === 0) return;
|
|
2650
2839
|
return meta;
|
|
2651
2840
|
}
|
|
2841
|
+
function resolveClaudeCodeSettingSources(env = process.env) {
|
|
2842
|
+
if (env.ACPX_CLAUDE_INCLUDE_USER_SETTINGS?.trim() === "1") return ["user", ...CLAUDE_CODE_DEFAULT_SETTING_SOURCES];
|
|
2843
|
+
return [...CLAUDE_CODE_DEFAULT_SETTING_SOURCES];
|
|
2844
|
+
}
|
|
2652
2845
|
function assignClaudeCodeOptions(target, options) {
|
|
2653
2846
|
if (typeof options.model === "string" && options.model.trim().length > 0) target.model = options.model;
|
|
2654
2847
|
if (Array.isArray(options.allowedTools)) target.allowedTools = [...options.allowedTools];
|
|
@@ -2667,9 +2860,7 @@ function isAppendSystemPrompt(value) {
|
|
|
2667
2860
|
function resolveClaudeCodeExecutable(platform = process.platform, env = process.env) {
|
|
2668
2861
|
if (platform !== "win32") return;
|
|
2669
2862
|
if (readWindowsEnvValue(env, "CLAUDE_CODE_EXECUTABLE")) return;
|
|
2670
|
-
|
|
2671
|
-
if (!resolved) return;
|
|
2672
|
-
return path.resolve(resolved);
|
|
2863
|
+
return resolveWindowsExecutablePath("claude", env);
|
|
2673
2864
|
}
|
|
2674
2865
|
//#endregion
|
|
2675
2866
|
//#region src/acp/auth-env.ts
|
|
@@ -2695,22 +2886,58 @@ function readEnvCredential(methodId) {
|
|
|
2695
2886
|
const value = process.env[key];
|
|
2696
2887
|
if (typeof value === "string" && value.trim().length > 0) return value;
|
|
2697
2888
|
}
|
|
2889
|
+
function protectedEnvKey(key) {
|
|
2890
|
+
return process.platform === "win32" ? key.toUpperCase() : key;
|
|
2891
|
+
}
|
|
2892
|
+
function isAuthEnvKey(key) {
|
|
2893
|
+
return protectedEnvKey(key).startsWith(AUTH_ENV_PREFIX);
|
|
2894
|
+
}
|
|
2895
|
+
function authEnvSuffix(key) {
|
|
2896
|
+
return key.slice(10);
|
|
2897
|
+
}
|
|
2898
|
+
function protectEnvKey(protectedKeys, key) {
|
|
2899
|
+
protectedKeys.add(protectedEnvKey(key));
|
|
2900
|
+
}
|
|
2698
2901
|
function promotePrefixedAuthEnvironment(env) {
|
|
2902
|
+
const protectedKeys = /* @__PURE__ */ new Set();
|
|
2699
2903
|
for (const [key, value] of Object.entries(env)) {
|
|
2700
|
-
if (!key
|
|
2904
|
+
if (!isAuthEnvKey(key)) continue;
|
|
2701
2905
|
if (typeof value !== "string" || value.trim().length === 0) continue;
|
|
2702
|
-
const normalized = key
|
|
2703
|
-
if (!normalized
|
|
2704
|
-
|
|
2906
|
+
const normalized = toEnvToken(authEnvSuffix(key));
|
|
2907
|
+
if (!normalized) continue;
|
|
2908
|
+
protectEnvKey(protectedKeys, key);
|
|
2909
|
+
protectEnvKey(protectedKeys, normalized);
|
|
2910
|
+
if (env[normalized] == null) env[normalized] = value;
|
|
2705
2911
|
}
|
|
2912
|
+
return protectedKeys;
|
|
2706
2913
|
}
|
|
2707
|
-
function buildAgentEnvironment(authCredentials) {
|
|
2914
|
+
function buildAgentEnvironment(authCredentials, sessionEnv) {
|
|
2708
2915
|
const env = { ...process.env };
|
|
2709
|
-
promotePrefixedAuthEnvironment(env);
|
|
2710
|
-
if (
|
|
2711
|
-
|
|
2916
|
+
const protectedAuthEnvKeys = promotePrefixedAuthEnvironment(env);
|
|
2917
|
+
if (authCredentials) for (const [methodId, credential] of Object.entries(authCredentials)) {
|
|
2918
|
+
addAuthCredentialEnvKeys(protectedAuthEnvKeys, methodId, credential);
|
|
2919
|
+
assignAuthCredentialEnv(env, methodId, credential);
|
|
2920
|
+
}
|
|
2921
|
+
if (sessionEnv) for (const [key, value] of Object.entries(sessionEnv)) {
|
|
2922
|
+
if (typeof value !== "string" || protectedAuthEnvKeys.has(protectedEnvKey(key))) continue;
|
|
2923
|
+
assignSessionEnv(env, key, value);
|
|
2924
|
+
}
|
|
2712
2925
|
return env;
|
|
2713
2926
|
}
|
|
2927
|
+
function assignSessionEnv(env, key, value) {
|
|
2928
|
+
const normalizedKey = protectedEnvKey(key);
|
|
2929
|
+
for (const existingKey of Object.keys(env)) if (protectedEnvKey(existingKey) === normalizedKey) delete env[existingKey];
|
|
2930
|
+
env[key] = value;
|
|
2931
|
+
}
|
|
2932
|
+
function addAuthCredentialEnvKeys(protectedKeys, methodId, credential) {
|
|
2933
|
+
if (typeof credential !== "string" || credential.trim().length === 0) return;
|
|
2934
|
+
if (!methodId.includes("=") && !methodId.includes("\0")) protectEnvKey(protectedKeys, methodId);
|
|
2935
|
+
const normalized = toEnvToken(methodId);
|
|
2936
|
+
if (normalized) {
|
|
2937
|
+
protectEnvKey(protectedKeys, `${AUTH_ENV_PREFIX}${normalized}`);
|
|
2938
|
+
protectEnvKey(protectedKeys, normalized);
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2714
2941
|
function assignAuthCredentialEnv(env, methodId, credential) {
|
|
2715
2942
|
if (typeof credential !== "string" || credential.trim().length === 0) return;
|
|
2716
2943
|
if (!methodId.includes("=") && !methodId.includes("\0") && env[methodId] == null) env[methodId] = credential;
|
|
@@ -2727,10 +2954,10 @@ function resolveConfiguredAuthCredential(methodId, authCredentials) {
|
|
|
2727
2954
|
const configCredentials = authCredentials ?? {};
|
|
2728
2955
|
return configCredentials[methodId] ?? configCredentials[toEnvToken(methodId)];
|
|
2729
2956
|
}
|
|
2730
|
-
function buildAgentSpawnOptions(cwd, authCredentials) {
|
|
2957
|
+
function buildAgentSpawnOptions(cwd, authCredentials, sessionEnv) {
|
|
2731
2958
|
return {
|
|
2732
2959
|
cwd,
|
|
2733
|
-
env: buildAgentEnvironment(authCredentials),
|
|
2960
|
+
env: buildAgentEnvironment(authCredentials, sessionEnv),
|
|
2734
2961
|
stdio: [
|
|
2735
2962
|
"pipe",
|
|
2736
2963
|
"pipe",
|
|
@@ -2740,8 +2967,127 @@ function buildAgentSpawnOptions(cwd, authCredentials) {
|
|
|
2740
2967
|
};
|
|
2741
2968
|
}
|
|
2742
2969
|
//#endregion
|
|
2970
|
+
//#region src/acp/model-support.ts
|
|
2971
|
+
const REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE = "ACP_MODEL_UNSUPPORTED";
|
|
2972
|
+
const REQUESTED_MODEL_UNSUPPORTED_REASONS = ["missing-capability", "unadvertised-model"];
|
|
2973
|
+
var RequestedModelUnsupportedError = class extends Error {
|
|
2974
|
+
code = REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE;
|
|
2975
|
+
reason;
|
|
2976
|
+
constructor(message, reason) {
|
|
2977
|
+
super(message);
|
|
2978
|
+
this.name = "RequestedModelUnsupportedError";
|
|
2979
|
+
this.reason = reason;
|
|
2980
|
+
}
|
|
2981
|
+
};
|
|
2982
|
+
function isRequestedModelUnsupportedReason(value) {
|
|
2983
|
+
return typeof value === "string" && REQUESTED_MODEL_UNSUPPORTED_REASONS.includes(value);
|
|
2984
|
+
}
|
|
2985
|
+
function isRequestedModelUnsupportedError(value) {
|
|
2986
|
+
if (value instanceof RequestedModelUnsupportedError) return true;
|
|
2987
|
+
if (!value || typeof value !== "object") return false;
|
|
2988
|
+
const candidate = value;
|
|
2989
|
+
return candidate.name === "RequestedModelUnsupportedError" && candidate.code === "ACP_MODEL_UNSUPPORTED" && isRequestedModelUnsupportedReason(candidate.reason);
|
|
2990
|
+
}
|
|
2991
|
+
function supportsLegacyClaudeCodeModelMetadata(agentCommand) {
|
|
2992
|
+
if (!agentCommand) return false;
|
|
2993
|
+
const { command, args } = splitCommandLine(agentCommand);
|
|
2994
|
+
return isClaudeAcpCommand(command, args);
|
|
2995
|
+
}
|
|
2996
|
+
function asRecord$2(value) {
|
|
2997
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
2998
|
+
return value;
|
|
2999
|
+
}
|
|
3000
|
+
function parseAvailableModel(value) {
|
|
3001
|
+
const option = asRecord$2(value);
|
|
3002
|
+
if (!option || typeof option.value !== "string" || typeof option.name !== "string") return;
|
|
3003
|
+
return {
|
|
3004
|
+
modelId: option.value,
|
|
3005
|
+
name: option.name
|
|
3006
|
+
};
|
|
3007
|
+
}
|
|
3008
|
+
function parseAvailableModelGroup(value) {
|
|
3009
|
+
const group = asRecord$2(value);
|
|
3010
|
+
if (!group || typeof group.group !== "string" || typeof group.name !== "string" || !Array.isArray(group.options)) return;
|
|
3011
|
+
const models = group.options.map((option) => parseAvailableModel(option));
|
|
3012
|
+
return models.every((model) => model !== void 0) ? models : void 0;
|
|
3013
|
+
}
|
|
3014
|
+
function parseAvailableModels(value) {
|
|
3015
|
+
if (!Array.isArray(value)) return;
|
|
3016
|
+
const directModels = value.map((option) => parseAvailableModel(option));
|
|
3017
|
+
if (directModels.every((model) => model !== void 0)) return directModels;
|
|
3018
|
+
const groupedModels = value.map((group) => parseAvailableModelGroup(group));
|
|
3019
|
+
return groupedModels.every((models) => models !== void 0) ? groupedModels.flat() : void 0;
|
|
3020
|
+
}
|
|
3021
|
+
function isModelSelectOption(option) {
|
|
3022
|
+
return option.type === "select" && (option.category === "model" || option.id === "model");
|
|
3023
|
+
}
|
|
3024
|
+
function parseModelConfigOption(value) {
|
|
3025
|
+
const option = asRecord$2(value);
|
|
3026
|
+
if (!option || !isModelSelectOption(option) || typeof option.id !== "string" || typeof option.currentValue !== "string") return;
|
|
3027
|
+
const availableModels = parseAvailableModels(option.options);
|
|
3028
|
+
return availableModels ? {
|
|
3029
|
+
configId: option.id,
|
|
3030
|
+
currentModelId: option.currentValue,
|
|
3031
|
+
availableModels
|
|
3032
|
+
} : void 0;
|
|
3033
|
+
}
|
|
3034
|
+
function modelStateFromConfigOptions(configOptions) {
|
|
3035
|
+
if (!Array.isArray(configOptions)) return;
|
|
3036
|
+
for (const value of configOptions) {
|
|
3037
|
+
const models = parseModelConfigOption(value);
|
|
3038
|
+
if (models) return models;
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
function modelStateFromLegacyResponse(response) {
|
|
3042
|
+
if (!response || typeof response !== "object") return;
|
|
3043
|
+
const models = response.models;
|
|
3044
|
+
if (!models || typeof models.currentModelId !== "string" || !Array.isArray(models.availableModels)) return;
|
|
3045
|
+
const availableModels = models.availableModels.flatMap((entry) => {
|
|
3046
|
+
if (!entry || typeof entry !== "object") return [];
|
|
3047
|
+
const candidate = entry;
|
|
3048
|
+
return typeof candidate.modelId === "string" && typeof candidate.name === "string" ? [{
|
|
3049
|
+
modelId: candidate.modelId,
|
|
3050
|
+
name: candidate.name
|
|
3051
|
+
}] : [];
|
|
3052
|
+
});
|
|
3053
|
+
return {
|
|
3054
|
+
currentModelId: models.currentModelId,
|
|
3055
|
+
availableModels
|
|
3056
|
+
};
|
|
3057
|
+
}
|
|
3058
|
+
function modelStateFromSessionResponse(params) {
|
|
3059
|
+
return modelStateFromConfigOptions(params.configOptions) ?? modelStateFromLegacyResponse(params.response);
|
|
3060
|
+
}
|
|
3061
|
+
function formatAvailableModelIds(models) {
|
|
3062
|
+
const ids = models?.availableModels.map((model) => model.modelId.trim()).filter((modelId) => modelId.length > 0) ?? [];
|
|
3063
|
+
return ids.length > 0 ? ids.join(", ") : "none advertised";
|
|
3064
|
+
}
|
|
3065
|
+
function resolveRequestedModelId(params) {
|
|
3066
|
+
if (!params.models || !isCursorAcpCommandForModelAlias(params.agentCommand)) return params.requestedModel;
|
|
3067
|
+
if (params.models.availableModels.some((model) => model.modelId === params.requestedModel)) return params.requestedModel;
|
|
3068
|
+
const candidates = params.models.availableModels.map((model) => model.modelId).filter((modelId) => modelId.startsWith(`${params.requestedModel}[`));
|
|
3069
|
+
return candidates.length === 1 ? candidates[0] : params.requestedModel;
|
|
3070
|
+
}
|
|
3071
|
+
function isCursorAcpCommandForModelAlias(agentCommand) {
|
|
3072
|
+
if (!agentCommand) return false;
|
|
3073
|
+
const { command, args } = splitCommandLine(agentCommand);
|
|
3074
|
+
return isCursorAcpCommand(command, args);
|
|
3075
|
+
}
|
|
3076
|
+
function assertRequestedModelSupported(params) {
|
|
3077
|
+
if (!params.models) {
|
|
3078
|
+
if (supportsLegacyClaudeCodeModelMetadata(params.agentCommand)) return;
|
|
3079
|
+
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.`, "missing-capability");
|
|
3080
|
+
}
|
|
3081
|
+
if (!new Set(params.models.availableModels.map((model) => model.modelId)).has(params.requestedModel)) {
|
|
3082
|
+
const resolvedModel = resolveRequestedModelId(params);
|
|
3083
|
+
if (resolvedModel !== params.requestedModel) return `Cursor ACP advertised "${resolvedModel}" for requested model "${params.requestedModel}"; using the advertised id.`;
|
|
3084
|
+
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.`;
|
|
3085
|
+
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)}.`, "unadvertised-model");
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
//#endregion
|
|
2743
3089
|
//#region src/acp/session-control-errors.ts
|
|
2744
|
-
const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = new Set([-32601, -32602]);
|
|
3090
|
+
const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
|
|
2745
3091
|
function asRecord$1(value) {
|
|
2746
3092
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
2747
3093
|
return value;
|
|
@@ -3356,11 +3702,54 @@ const DRAIN_POLL_INTERVAL_MS = 20;
|
|
|
3356
3702
|
const AGENT_CLOSE_TERM_GRACE_MS = 1500;
|
|
3357
3703
|
const AGENT_CLOSE_KILL_GRACE_MS = 1e3;
|
|
3358
3704
|
const STARTUP_STDERR_MAX_CHARS = 8192;
|
|
3705
|
+
const DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META = Object.freeze({ "cognition.ai/requestDiagnostics": true });
|
|
3706
|
+
const DEVIN_COMPATIBILITY_CLIENT_NAME = "windsurf";
|
|
3707
|
+
const DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION = "1.110.1";
|
|
3708
|
+
function resolveClientInfo(devinAcp) {
|
|
3709
|
+
if (!devinAcp) return {
|
|
3710
|
+
name: "acpx",
|
|
3711
|
+
version: getAcpxVersion()
|
|
3712
|
+
};
|
|
3713
|
+
return {
|
|
3714
|
+
name: DEVIN_COMPATIBILITY_CLIENT_NAME,
|
|
3715
|
+
version: process.env.ACPX_DEVIN_WINDSURF_VERSION ?? DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION
|
|
3716
|
+
};
|
|
3717
|
+
}
|
|
3718
|
+
function resolveClientCapabilities(params) {
|
|
3719
|
+
const baseCapabilities = {
|
|
3720
|
+
fs: {
|
|
3721
|
+
readTextFile: true,
|
|
3722
|
+
writeTextFile: true
|
|
3723
|
+
},
|
|
3724
|
+
terminal: params.terminal
|
|
3725
|
+
};
|
|
3726
|
+
if (!params.devinAcp) return baseCapabilities;
|
|
3727
|
+
return {
|
|
3728
|
+
...baseCapabilities,
|
|
3729
|
+
_meta: DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META
|
|
3730
|
+
};
|
|
3731
|
+
}
|
|
3732
|
+
function isDevinRequestDiagnosticsMethod(method) {
|
|
3733
|
+
return method === "_cognition.ai/request_diagnostics";
|
|
3734
|
+
}
|
|
3735
|
+
function hasResponseField(response, field) {
|
|
3736
|
+
return !!response && typeof response === "object" && field in response;
|
|
3737
|
+
}
|
|
3738
|
+
function normalizeResponseConfigOptions(response) {
|
|
3739
|
+
if (!response || !("configOptions" in response)) return;
|
|
3740
|
+
return response.configOptions ?? [];
|
|
3741
|
+
}
|
|
3359
3742
|
function toReconnectedSessionResult(response) {
|
|
3743
|
+
const configOptions = normalizeResponseConfigOptions(response);
|
|
3360
3744
|
return {
|
|
3361
3745
|
agentSessionId: extractRuntimeSessionId(response?._meta),
|
|
3362
|
-
configOptions
|
|
3363
|
-
models:
|
|
3746
|
+
configOptions,
|
|
3747
|
+
models: modelStateFromSessionResponse({
|
|
3748
|
+
configOptions,
|
|
3749
|
+
response
|
|
3750
|
+
}),
|
|
3751
|
+
configOptionsPresent: hasResponseField(response, "configOptions"),
|
|
3752
|
+
legacyModelMetadataPresent: hasResponseField(response, "models")
|
|
3364
3753
|
};
|
|
3365
3754
|
}
|
|
3366
3755
|
function childProcessIsRunning(agent) {
|
|
@@ -3388,12 +3777,16 @@ function enqueueNdJsonLine(agentCommand, line, controller) {
|
|
|
3388
3777
|
const trimmedLine = line.trim();
|
|
3389
3778
|
if (!trimmedLine || shouldIgnoreNonJsonAgentOutputLine(agentCommand, trimmedLine)) return;
|
|
3390
3779
|
try {
|
|
3391
|
-
const message =
|
|
3392
|
-
controller.enqueue(message);
|
|
3780
|
+
const message = parseAcpJsonMessageLine(trimmedLine);
|
|
3781
|
+
if (message) controller.enqueue(message);
|
|
3393
3782
|
} catch (err) {
|
|
3394
3783
|
console.error("Failed to parse JSON message:", trimmedLine, err);
|
|
3395
3784
|
}
|
|
3396
3785
|
}
|
|
3786
|
+
function parseAcpJsonMessageLine(line) {
|
|
3787
|
+
const message = JSON.parse(line);
|
|
3788
|
+
return isAcpMessageObject(message) ? message : void 0;
|
|
3789
|
+
}
|
|
3397
3790
|
function enqueueNdJsonLines(agentCommand, lines, controller) {
|
|
3398
3791
|
for (const line of lines) enqueueNdJsonLine(agentCommand, line, controller);
|
|
3399
3792
|
}
|
|
@@ -3459,6 +3852,8 @@ var AcpClient = class {
|
|
|
3459
3852
|
lastKnownPid;
|
|
3460
3853
|
promptPermissionFailures = /* @__PURE__ */ new Map();
|
|
3461
3854
|
pendingConnectionRequests = /* @__PURE__ */ new Set();
|
|
3855
|
+
modelConfigIds = /* @__PURE__ */ new Map();
|
|
3856
|
+
legacyModelSessionIds = /* @__PURE__ */ new Set();
|
|
3462
3857
|
constructor(options) {
|
|
3463
3858
|
this.options = {
|
|
3464
3859
|
...options,
|
|
@@ -3570,7 +3965,7 @@ var AcpClient = class {
|
|
|
3570
3965
|
const input = Writable.toWeb(child.stdin);
|
|
3571
3966
|
const output = Readable.toWeb(child.stdout);
|
|
3572
3967
|
const stream = this.createTappedStream(createNdJsonMessageStream(this.options.agentCommand, input, output));
|
|
3573
|
-
const connection = this.createConnection(stream);
|
|
3968
|
+
const connection = this.createConnection(stream, launch);
|
|
3574
3969
|
connection.signal.addEventListener("abort", () => {
|
|
3575
3970
|
this.recordAgentExit("connection_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
3576
3971
|
}, { once: true });
|
|
@@ -3594,10 +3989,11 @@ var AcpClient = class {
|
|
|
3594
3989
|
spawnCommand,
|
|
3595
3990
|
args,
|
|
3596
3991
|
resolvedBuiltInLaunch,
|
|
3992
|
+
devinAcp: isDevinAcpCommand(spawnCommand, args),
|
|
3597
3993
|
geminiAcp: isGeminiAcpCommand(spawnCommand, args),
|
|
3598
3994
|
copilotAcp: isCopilotAcpCommand(spawnCommand, args),
|
|
3599
3995
|
claudeAcp: isClaudeAcpCommand(spawnCommand, args),
|
|
3600
|
-
spawnOptions: buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials)
|
|
3996
|
+
spawnOptions: buildAgentSpawnOptions(this.options.cwd, this.options.authCredentials, this.options.sessionOptions?.env)
|
|
3601
3997
|
};
|
|
3602
3998
|
}
|
|
3603
3999
|
logAgentLaunch(plan) {
|
|
@@ -3630,7 +4026,7 @@ var AcpClient = class {
|
|
|
3630
4026
|
}
|
|
3631
4027
|
return requireAgentStdio(spawnedChild);
|
|
3632
4028
|
}
|
|
3633
|
-
createConnection(stream) {
|
|
4029
|
+
createConnection(stream, launch) {
|
|
3634
4030
|
return new ClientSideConnection(() => ({
|
|
3635
4031
|
sessionUpdate: async (params) => {
|
|
3636
4032
|
await this.handleSessionUpdate(params);
|
|
@@ -3638,6 +4034,12 @@ var AcpClient = class {
|
|
|
3638
4034
|
requestPermission: async (params) => {
|
|
3639
4035
|
return this.handlePermissionRequest(params);
|
|
3640
4036
|
},
|
|
4037
|
+
extMethod: async (method) => {
|
|
4038
|
+
if (launch.devinAcp && isDevinRequestDiagnosticsMethod(method)) return {};
|
|
4039
|
+
const error = RequestError.methodNotFound(method);
|
|
4040
|
+
if (!this.options.suppressSdkConsoleErrors) console.error(error.message);
|
|
4041
|
+
throw error;
|
|
4042
|
+
},
|
|
3641
4043
|
readTextFile: async (params) => {
|
|
3642
4044
|
return this.handleReadTextFile(params);
|
|
3643
4045
|
},
|
|
@@ -3658,12 +4060,13 @@ var AcpClient = class {
|
|
|
3658
4060
|
},
|
|
3659
4061
|
releaseTerminal: async (params) => {
|
|
3660
4062
|
return this.handleReleaseTerminal(params);
|
|
3661
|
-
}
|
|
4063
|
+
},
|
|
4064
|
+
extNotification: async () => {}
|
|
3662
4065
|
}), stream);
|
|
3663
4066
|
}
|
|
3664
4067
|
async initializeAgentConnection(params) {
|
|
3665
4068
|
try {
|
|
3666
|
-
const initResult = await Promise.race([this.initializeProtocolConnection(params.connection, params.launch
|
|
4069
|
+
const initResult = await Promise.race([this.initializeProtocolConnection(params.connection, params.launch), params.startupFailure.promise]);
|
|
3667
4070
|
params.startupFailure.dispose();
|
|
3668
4071
|
this.connection = params.connection;
|
|
3669
4072
|
this.agent = params.child;
|
|
@@ -3673,22 +4076,16 @@ var AcpClient = class {
|
|
|
3673
4076
|
await this.handleInitializeFailure(params, error);
|
|
3674
4077
|
}
|
|
3675
4078
|
}
|
|
3676
|
-
async initializeProtocolConnection(connection,
|
|
4079
|
+
async initializeProtocolConnection(connection, launch) {
|
|
3677
4080
|
const initializePromise = connection.initialize({
|
|
3678
4081
|
protocolVersion: PROTOCOL_VERSION,
|
|
3679
|
-
clientCapabilities: {
|
|
3680
|
-
|
|
3681
|
-
readTextFile: true,
|
|
3682
|
-
writeTextFile: true
|
|
3683
|
-
},
|
|
4082
|
+
clientCapabilities: resolveClientCapabilities({
|
|
4083
|
+
devinAcp: launch.devinAcp,
|
|
3684
4084
|
terminal: this.options.terminal !== false
|
|
3685
|
-
},
|
|
3686
|
-
clientInfo:
|
|
3687
|
-
name: "acpx",
|
|
3688
|
-
version: "0.1.0"
|
|
3689
|
-
}
|
|
4085
|
+
}),
|
|
4086
|
+
clientInfo: resolveClientInfo(launch.devinAcp)
|
|
3690
4087
|
});
|
|
3691
|
-
const initialized = geminiAcp ? await withTimeout(initializePromise, resolveGeminiAcpStartupTimeoutMs()) : await initializePromise;
|
|
4088
|
+
const initialized = launch.geminiAcp ? await withTimeout(initializePromise, resolveGeminiAcpStartupTimeoutMs()) : await initializePromise;
|
|
3692
4089
|
await this.authenticateIfRequired(connection, initialized.authMethods ?? []);
|
|
3693
4090
|
return initialized;
|
|
3694
4091
|
}
|
|
@@ -3751,7 +4148,7 @@ var AcpClient = class {
|
|
|
3751
4148
|
const createPromise = this.runConnectionRequest(() => connection.newSession({
|
|
3752
4149
|
cwd: sessionCwd,
|
|
3753
4150
|
mcpServers: this.options.mcpServers ?? [],
|
|
3754
|
-
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions)
|
|
4151
|
+
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions, claudeAcp)
|
|
3755
4152
|
}));
|
|
3756
4153
|
result = claudeAcp ? await withTimeout(createPromise, resolveClaudeAcpSessionCreateTimeoutMs()) : await createPromise;
|
|
3757
4154
|
} catch (error) {
|
|
@@ -3762,11 +4159,19 @@ var AcpClient = class {
|
|
|
3762
4159
|
throw error;
|
|
3763
4160
|
}
|
|
3764
4161
|
this.loadedSessionId = result.sessionId;
|
|
4162
|
+
const configOptions = normalizeResponseConfigOptions(result);
|
|
4163
|
+
const models = modelStateFromSessionResponse({
|
|
4164
|
+
configOptions,
|
|
4165
|
+
response: result
|
|
4166
|
+
});
|
|
4167
|
+
this.rememberSessionModels(result.sessionId, models);
|
|
3765
4168
|
return {
|
|
3766
4169
|
sessionId: result.sessionId,
|
|
3767
4170
|
agentSessionId: extractRuntimeSessionId(result._meta),
|
|
3768
|
-
configOptions
|
|
3769
|
-
models
|
|
4171
|
+
configOptions,
|
|
4172
|
+
models,
|
|
4173
|
+
configOptionsPresent: hasResponseField(result, "configOptions"),
|
|
4174
|
+
legacyModelMetadataPresent: hasResponseField(result, "models")
|
|
3770
4175
|
};
|
|
3771
4176
|
}
|
|
3772
4177
|
async loadSession(sessionId, cwd = this.options.cwd) {
|
|
@@ -3789,7 +4194,9 @@ var AcpClient = class {
|
|
|
3789
4194
|
this.restoreSessionUpdateSuppression(previousSuppression);
|
|
3790
4195
|
}
|
|
3791
4196
|
this.loadedSessionId = sessionId;
|
|
3792
|
-
|
|
4197
|
+
const result = toReconnectedSessionResult(response);
|
|
4198
|
+
this.updateRememberedSessionModels(sessionId, result);
|
|
4199
|
+
return result;
|
|
3793
4200
|
}
|
|
3794
4201
|
async resumeSession(sessionId, cwd = this.options.cwd) {
|
|
3795
4202
|
const connection = this.getConnection();
|
|
@@ -3800,7 +4207,9 @@ var AcpClient = class {
|
|
|
3800
4207
|
mcpServers: this.options.mcpServers ?? []
|
|
3801
4208
|
}));
|
|
3802
4209
|
this.loadedSessionId = sessionId;
|
|
3803
|
-
|
|
4210
|
+
const result = toReconnectedSessionResult(response);
|
|
4211
|
+
this.updateRememberedSessionModels(sessionId, result);
|
|
4212
|
+
return result;
|
|
3804
4213
|
}
|
|
3805
4214
|
applySessionUpdateSuppression(enabled) {
|
|
3806
4215
|
const previous = {
|
|
@@ -3883,21 +4292,78 @@ var AcpClient = class {
|
|
|
3883
4292
|
throw maybeWrapSessionControlError("session/set_config_option", error, `for "${configId}"="${value}"`);
|
|
3884
4293
|
}
|
|
3885
4294
|
}
|
|
3886
|
-
async setSessionModel(sessionId, modelId) {
|
|
4295
|
+
async setSessionModel(sessionId, modelId, controlOverride) {
|
|
4296
|
+
const control = this.resolveModelControl(sessionId, controlOverride);
|
|
4297
|
+
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.`, "missing-capability");
|
|
4298
|
+
const resolvedModelId = resolveRequestedModelId({
|
|
4299
|
+
requestedModel: modelId,
|
|
4300
|
+
models: controlOverride?.availableModels ? { availableModels: controlOverride.availableModels } : void 0,
|
|
4301
|
+
agentCommand: this.options.agentCommand
|
|
4302
|
+
});
|
|
4303
|
+
return control.kind === "config_option" ? await this.setSessionModelThroughConfig(sessionId, resolvedModelId, control.configId) : await this.setSessionModelThroughLegacyMethod(sessionId, resolvedModelId);
|
|
4304
|
+
}
|
|
4305
|
+
async setSessionModelThroughConfig(sessionId, modelId, configId) {
|
|
3887
4306
|
const connection = this.getConnection();
|
|
3888
4307
|
try {
|
|
3889
|
-
await this.runConnectionRequest(() => connection.
|
|
4308
|
+
const response = await this.runConnectionRequest(() => connection.setSessionConfigOption({
|
|
4309
|
+
sessionId,
|
|
4310
|
+
configId,
|
|
4311
|
+
value: modelId
|
|
4312
|
+
}));
|
|
4313
|
+
this.rememberSessionModels(sessionId, modelStateFromConfigOptions(response.configOptions));
|
|
4314
|
+
return response;
|
|
4315
|
+
} catch (error) {
|
|
4316
|
+
return this.throwSessionModelError("session/set_config_option", modelId, error);
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
async setSessionModelThroughLegacyMethod(sessionId, modelId) {
|
|
4320
|
+
const connection = this.getConnection();
|
|
4321
|
+
try {
|
|
4322
|
+
await this.runConnectionRequest(() => connection.extMethod("session/set_model", {
|
|
3890
4323
|
sessionId,
|
|
3891
4324
|
modelId
|
|
3892
4325
|
}));
|
|
4326
|
+
return;
|
|
3893
4327
|
} catch (error) {
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
4328
|
+
return this.throwSessionModelError("session/set_model", modelId, error);
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
throwSessionModelError(method, modelId, error) {
|
|
4332
|
+
const wrapped = maybeWrapSessionControlError(method, error, `for model "${modelId}"`);
|
|
4333
|
+
if (wrapped !== error) throw wrapped;
|
|
4334
|
+
const acp = extractAcpError(error);
|
|
4335
|
+
const summary = acp ? formatSessionControlAcpSummary(acp) : error instanceof Error ? error.message : String(error);
|
|
4336
|
+
throw new Error(`Failed ${method} for model "${modelId}": ${summary}`, { cause: error });
|
|
4337
|
+
}
|
|
4338
|
+
resolveModelControl(sessionId, controlOverride) {
|
|
4339
|
+
if (controlOverride) return controlOverride.configId ? {
|
|
4340
|
+
kind: "config_option",
|
|
4341
|
+
configId: controlOverride.configId
|
|
4342
|
+
} : { kind: "legacy_set_model" };
|
|
4343
|
+
const configId = this.modelConfigIds.get(sessionId);
|
|
4344
|
+
if (configId) return {
|
|
4345
|
+
kind: "config_option",
|
|
4346
|
+
configId
|
|
4347
|
+
};
|
|
4348
|
+
return this.legacyModelSessionIds.has(sessionId) ? { kind: "legacy_set_model" } : void 0;
|
|
4349
|
+
}
|
|
4350
|
+
rememberSessionModels(sessionId, models) {
|
|
4351
|
+
if (!models) {
|
|
4352
|
+
this.modelConfigIds.delete(sessionId);
|
|
4353
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
4354
|
+
return;
|
|
4355
|
+
}
|
|
4356
|
+
if (models.configId) {
|
|
4357
|
+
this.modelConfigIds.set(sessionId, models.configId);
|
|
4358
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
4359
|
+
return;
|
|
3900
4360
|
}
|
|
4361
|
+
this.modelConfigIds.delete(sessionId);
|
|
4362
|
+
this.legacyModelSessionIds.add(sessionId);
|
|
4363
|
+
}
|
|
4364
|
+
updateRememberedSessionModels(sessionId, result) {
|
|
4365
|
+
const explicitConfigRemoval = result.configOptionsPresent && this.modelConfigIds.has(sessionId);
|
|
4366
|
+
if (result.models || result.legacyModelMetadataPresent || explicitConfigRemoval) this.rememberSessionModels(sessionId, result.models);
|
|
3901
4367
|
}
|
|
3902
4368
|
async cancel(sessionId) {
|
|
3903
4369
|
const connection = this.getConnection();
|
|
@@ -3909,6 +4375,8 @@ var AcpClient = class {
|
|
|
3909
4375
|
const connection = this.getConnection();
|
|
3910
4376
|
await this.runConnectionRequest(() => connection.closeSession({ sessionId }));
|
|
3911
4377
|
if (this.loadedSessionId === sessionId) this.loadedSessionId = void 0;
|
|
4378
|
+
this.modelConfigIds.delete(sessionId);
|
|
4379
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
3912
4380
|
}
|
|
3913
4381
|
async listSessions(params = {}) {
|
|
3914
4382
|
const connection = this.getConnection();
|
|
@@ -3957,6 +4425,8 @@ var AcpClient = class {
|
|
|
3957
4425
|
this.permissionAbortControllers.clear();
|
|
3958
4426
|
this.promptPermissionFailures.clear();
|
|
3959
4427
|
this.loadedSessionId = void 0;
|
|
4428
|
+
this.modelConfigIds.clear();
|
|
4429
|
+
this.legacyModelSessionIds.clear();
|
|
3960
4430
|
this.initResult = void 0;
|
|
3961
4431
|
this.connection = void 0;
|
|
3962
4432
|
this.agent = void 0;
|
|
@@ -4010,7 +4480,7 @@ var AcpClient = class {
|
|
|
4010
4480
|
target.push(text);
|
|
4011
4481
|
if (target.join("").length - STARTUP_STDERR_MAX_CHARS <= 0) return;
|
|
4012
4482
|
const joined = target.join("");
|
|
4013
|
-
target.splice(0, target.length, joined.slice(-
|
|
4483
|
+
target.splice(0, target.length, joined.slice(-8192));
|
|
4014
4484
|
}
|
|
4015
4485
|
summarizeStartupStderr(target) {
|
|
4016
4486
|
const joined = target.join("").trim();
|
|
@@ -4369,6 +4839,7 @@ function applyConversation(record, conversation) {
|
|
|
4369
4839
|
record.updated_at = conversation.updated_at;
|
|
4370
4840
|
record.messages = conversation.messages;
|
|
4371
4841
|
record.cumulative_token_usage = conversation.cumulative_token_usage;
|
|
4842
|
+
record.cumulative_cost = conversation.cumulative_cost;
|
|
4372
4843
|
record.request_token_usage = conversation.request_token_usage;
|
|
4373
4844
|
}
|
|
4374
4845
|
//#endregion
|
|
@@ -4379,8 +4850,16 @@ function mergeSessionOptions(preferred, fallback) {
|
|
|
4379
4850
|
assignDefinedOption(merged, "allowedTools", preferred?.allowedTools);
|
|
4380
4851
|
assignDefinedOption(merged, "maxTurns", preferred?.maxTurns);
|
|
4381
4852
|
assignDefinedOption(merged, "systemPrompt", preferred?.systemPrompt);
|
|
4853
|
+
assignDefinedOption(merged, "env", mergeEnvRecords(fallback?.env, preferred?.env));
|
|
4382
4854
|
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
4383
4855
|
}
|
|
4856
|
+
function mergeEnvRecords(fallback, preferred) {
|
|
4857
|
+
if (!fallback && !preferred) return;
|
|
4858
|
+
return {
|
|
4859
|
+
...fallback,
|
|
4860
|
+
...preferred
|
|
4861
|
+
};
|
|
4862
|
+
}
|
|
4384
4863
|
function assignDefinedOption(target, key, value) {
|
|
4385
4864
|
if (value !== void 0) target[key] = value;
|
|
4386
4865
|
}
|
|
@@ -4404,6 +4883,7 @@ function sessionOptionsFromRecord(record) {
|
|
|
4404
4883
|
assignStoredOption(sessionOptions, "allowedTools", storedAllowedTools(stored.allowed_tools));
|
|
4405
4884
|
assignStoredOption(sessionOptions, "maxTurns", storedMaxTurns(stored.max_turns));
|
|
4406
4885
|
assignStoredOption(sessionOptions, "systemPrompt", storedSystemPromptOption(stored.system_prompt));
|
|
4886
|
+
assignStoredOption(sessionOptions, "env", storedEnvRecord(stored.env));
|
|
4407
4887
|
return Object.keys(sessionOptions).length > 0 ? sessionOptions : void 0;
|
|
4408
4888
|
}
|
|
4409
4889
|
function persistedSessionOptions(options) {
|
|
@@ -4411,12 +4891,23 @@ function persistedSessionOptions(options) {
|
|
|
4411
4891
|
model: nonEmptyString(options.model),
|
|
4412
4892
|
allowed_tools: Array.isArray(options.allowedTools) ? [...options.allowedTools] : void 0,
|
|
4413
4893
|
max_turns: typeof options.maxTurns === "number" ? options.maxTurns : void 0,
|
|
4414
|
-
system_prompt: normalizeSystemPromptOption(options.systemPrompt)
|
|
4894
|
+
system_prompt: normalizeSystemPromptOption(options.systemPrompt),
|
|
4895
|
+
env: storedEnvRecord(options.env)
|
|
4415
4896
|
};
|
|
4416
4897
|
return hasPersistedSessionOptions(next) ? next : void 0;
|
|
4417
4898
|
}
|
|
4418
4899
|
function hasPersistedSessionOptions(options) {
|
|
4419
|
-
return options.model !== void 0 || options.allowed_tools !== void 0 || options.max_turns !== void 0 || options.system_prompt !== void 0;
|
|
4900
|
+
return options.model !== void 0 || options.allowed_tools !== void 0 || options.max_turns !== void 0 || options.system_prompt !== void 0 || options.env !== void 0;
|
|
4901
|
+
}
|
|
4902
|
+
function storedEnvRecord(value) {
|
|
4903
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return;
|
|
4904
|
+
const entries = Object.entries(value);
|
|
4905
|
+
const result = {};
|
|
4906
|
+
for (const [key, raw] of entries) {
|
|
4907
|
+
if (typeof raw !== "string") continue;
|
|
4908
|
+
result[key] = raw;
|
|
4909
|
+
}
|
|
4910
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
4420
4911
|
}
|
|
4421
4912
|
function normalizeSystemPromptOption(value) {
|
|
4422
4913
|
const prompt = nonEmptyString(value);
|
|
@@ -4444,6 +4935,51 @@ function nonEmptyString(value) {
|
|
|
4444
4935
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
4445
4936
|
}
|
|
4446
4937
|
//#endregion
|
|
4938
|
+
//#region src/session/model-state.ts
|
|
4939
|
+
function configOptionsAreAuthoritative(state) {
|
|
4940
|
+
return state.model_control === "config_option";
|
|
4941
|
+
}
|
|
4942
|
+
function legacyModelState(state) {
|
|
4943
|
+
if (!Array.isArray(state.available_models)) return;
|
|
4944
|
+
return {
|
|
4945
|
+
currentModelId: state.current_model_id ?? "",
|
|
4946
|
+
availableModels: state.available_models.map((modelId) => ({
|
|
4947
|
+
modelId,
|
|
4948
|
+
name: modelId
|
|
4949
|
+
}))
|
|
4950
|
+
};
|
|
4951
|
+
}
|
|
4952
|
+
function advertisedModelState(state) {
|
|
4953
|
+
if (!state) return;
|
|
4954
|
+
const configModels = modelStateFromConfigOptions(state?.config_options);
|
|
4955
|
+
if (configModels) return configModels;
|
|
4956
|
+
if (configOptionsAreAuthoritative(state)) return;
|
|
4957
|
+
return legacyModelState(state);
|
|
4958
|
+
}
|
|
4959
|
+
function applyAdvertisedModelState(state, models) {
|
|
4960
|
+
state.current_model_id = models.currentModelId;
|
|
4961
|
+
state.available_models = models.availableModels.map((model) => model.modelId);
|
|
4962
|
+
state.model_control = models.configId ? "config_option" : "legacy_set_model";
|
|
4963
|
+
}
|
|
4964
|
+
function clearAdvertisedModelState(state) {
|
|
4965
|
+
delete state.current_model_id;
|
|
4966
|
+
delete state.available_models;
|
|
4967
|
+
delete state.model_control;
|
|
4968
|
+
}
|
|
4969
|
+
function removeModelConfigOptions(state) {
|
|
4970
|
+
if (!state.config_options) return;
|
|
4971
|
+
state.config_options = state.config_options.filter((option) => option.category !== "model" && option.id !== "model");
|
|
4972
|
+
}
|
|
4973
|
+
function applyConfigOptionsModelState(state, configOptions) {
|
|
4974
|
+
const previousConfigModels = modelStateFromConfigOptions(state.config_options);
|
|
4975
|
+
const preservesLegacyControl = state.model_control === "legacy_set_model" || state.model_control === void 0 && previousConfigModels === void 0 && legacyModelState(state) !== void 0;
|
|
4976
|
+
state.config_options = structuredClone(configOptions);
|
|
4977
|
+
const models = modelStateFromConfigOptions(configOptions);
|
|
4978
|
+
if (models) applyAdvertisedModelState(state, models);
|
|
4979
|
+
else if (preservesLegacyControl) state.model_control = "legacy_set_model";
|
|
4980
|
+
else clearAdvertisedModelState(state);
|
|
4981
|
+
}
|
|
4982
|
+
//#endregion
|
|
4447
4983
|
//#region src/session/conversation-model.ts
|
|
4448
4984
|
const MAX_RUNTIME_MESSAGES = 200;
|
|
4449
4985
|
const MAX_RUNTIME_AGENT_TEXT_CHARS = 8e3;
|
|
@@ -4464,10 +5000,25 @@ function hasOwn(source, key) {
|
|
|
4464
5000
|
return Object.prototype.hasOwnProperty.call(source, key);
|
|
4465
5001
|
}
|
|
4466
5002
|
function normalizeAgentName(value) {
|
|
5003
|
+
return trimmedString(value);
|
|
5004
|
+
}
|
|
5005
|
+
function trimmedString(value) {
|
|
4467
5006
|
if (typeof value !== "string") return;
|
|
4468
5007
|
const trimmed = value.trim();
|
|
4469
5008
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
4470
5009
|
}
|
|
5010
|
+
function normalizeAvailableCommand(value) {
|
|
5011
|
+
const record = asRecord(value);
|
|
5012
|
+
if (!record) return;
|
|
5013
|
+
const name = trimmedString(record.name);
|
|
5014
|
+
if (!name) return;
|
|
5015
|
+
const description = trimmedString(record.description);
|
|
5016
|
+
return {
|
|
5017
|
+
name,
|
|
5018
|
+
...description ? { description } : {},
|
|
5019
|
+
has_input: record.input != null
|
|
5020
|
+
};
|
|
5021
|
+
}
|
|
4471
5022
|
function extractText(content) {
|
|
4472
5023
|
switch (content.type) {
|
|
4473
5024
|
case "text": return content.text;
|
|
@@ -4695,7 +5246,9 @@ function usageToTokenUsage(update) {
|
|
|
4695
5246
|
"cache_read_input_tokens",
|
|
4696
5247
|
"cacheReadInputTokens",
|
|
4697
5248
|
"cachedReadTokens"
|
|
4698
|
-
])
|
|
5249
|
+
]),
|
|
5250
|
+
thought_tokens: numberField(source, ["thought_tokens", "thoughtTokens"]),
|
|
5251
|
+
total_tokens: numberField(source, ["total_tokens", "totalTokens"])
|
|
4699
5252
|
};
|
|
4700
5253
|
if (!hasTokenUsageValue(normalized)) return;
|
|
4701
5254
|
return normalized;
|
|
@@ -4703,6 +5256,21 @@ function usageToTokenUsage(update) {
|
|
|
4703
5256
|
function hasTokenUsageValue(usage) {
|
|
4704
5257
|
return Object.values(usage).some((value) => value !== void 0);
|
|
4705
5258
|
}
|
|
5259
|
+
function usageCost(update) {
|
|
5260
|
+
const cost = asRecord(asRecord(update)?.cost);
|
|
5261
|
+
if (!cost) return;
|
|
5262
|
+
return buildUsageCost(numberField(cost, ["amount"]), stringField(cost.currency));
|
|
5263
|
+
}
|
|
5264
|
+
function stringField(value) {
|
|
5265
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
5266
|
+
}
|
|
5267
|
+
function buildUsageCost(amount, currency) {
|
|
5268
|
+
const cost = {
|
|
5269
|
+
...amount !== void 0 ? { amount } : {},
|
|
5270
|
+
...currency !== void 0 ? { currency } : {}
|
|
5271
|
+
};
|
|
5272
|
+
return Object.keys(cost).length > 0 ? cost : void 0;
|
|
5273
|
+
}
|
|
4706
5274
|
function ensureAcpxState$1(state) {
|
|
4707
5275
|
return state ?? {};
|
|
4708
5276
|
}
|
|
@@ -4718,6 +5286,7 @@ function createSessionConversation(timestamp = isoNow()) {
|
|
|
4718
5286
|
messages: [],
|
|
4719
5287
|
updated_at: timestamp,
|
|
4720
5288
|
cumulative_token_usage: {},
|
|
5289
|
+
cumulative_cost: void 0,
|
|
4721
5290
|
request_token_usage: {}
|
|
4722
5291
|
};
|
|
4723
5292
|
}
|
|
@@ -4728,9 +5297,13 @@ function cloneSessionConversation(conversation) {
|
|
|
4728
5297
|
messages: deepClone(conversation.messages ?? []),
|
|
4729
5298
|
updated_at: conversation.updated_at,
|
|
4730
5299
|
cumulative_token_usage: deepClone(conversation.cumulative_token_usage ?? {}),
|
|
5300
|
+
cumulative_cost: cloneUsageCost(conversation.cumulative_cost),
|
|
4731
5301
|
request_token_usage: deepClone(conversation.request_token_usage ?? {})
|
|
4732
5302
|
};
|
|
4733
5303
|
}
|
|
5304
|
+
function cloneUsageCost(cost) {
|
|
5305
|
+
return cost ? { ...cost } : void 0;
|
|
5306
|
+
}
|
|
4734
5307
|
function cloneSessionAcpxState(state) {
|
|
4735
5308
|
if (!state) return;
|
|
4736
5309
|
return {
|
|
@@ -4739,7 +5312,8 @@ function cloneSessionAcpxState(state) {
|
|
|
4739
5312
|
desired_config_options: state.desired_config_options ? { ...state.desired_config_options } : void 0,
|
|
4740
5313
|
current_model_id: state.current_model_id,
|
|
4741
5314
|
available_models: state.available_models ? [...state.available_models] : void 0,
|
|
4742
|
-
|
|
5315
|
+
model_control: state.model_control,
|
|
5316
|
+
available_commands: state.available_commands ? state.available_commands.map((command) => ({ ...command })) : void 0,
|
|
4743
5317
|
config_options: state.config_options ? deepClone(state.config_options) : void 0,
|
|
4744
5318
|
session_options: cloneSessionOptions(state.session_options)
|
|
4745
5319
|
};
|
|
@@ -4750,7 +5324,8 @@ function cloneSessionOptions(options) {
|
|
|
4750
5324
|
model: options.model,
|
|
4751
5325
|
allowed_tools: options.allowed_tools ? [...options.allowed_tools] : void 0,
|
|
4752
5326
|
max_turns: options.max_turns,
|
|
4753
|
-
...options.system_prompt !== void 0 ? { system_prompt: cloneSystemPromptOption(options.system_prompt) } : {}
|
|
5327
|
+
...options.system_prompt !== void 0 ? { system_prompt: cloneSystemPromptOption(options.system_prompt) } : {},
|
|
5328
|
+
...options.env !== void 0 ? { env: { ...options.env } } : {}
|
|
4754
5329
|
};
|
|
4755
5330
|
}
|
|
4756
5331
|
function cloneSystemPromptOption(option) {
|
|
@@ -4820,13 +5395,13 @@ const SESSION_UPDATE_HANDLERS = {
|
|
|
4820
5395
|
if (update.sessionUpdate === "session_info_update") applySessionInfoUpdate(conversation, update);
|
|
4821
5396
|
},
|
|
4822
5397
|
available_commands_update: (_conversation, acpx, update) => {
|
|
4823
|
-
if (update.sessionUpdate === "available_commands_update") acpx.available_commands = update.availableCommands.map((entry) => entry
|
|
5398
|
+
if (update.sessionUpdate === "available_commands_update") acpx.available_commands = update.availableCommands.map((entry) => normalizeAvailableCommand(entry)).filter((entry) => entry !== void 0);
|
|
4824
5399
|
},
|
|
4825
5400
|
current_mode_update: (_conversation, acpx, update) => {
|
|
4826
5401
|
if (update.sessionUpdate === "current_mode_update") acpx.current_mode_id = update.currentModeId;
|
|
4827
5402
|
},
|
|
4828
5403
|
config_option_update: (_conversation, acpx, update) => {
|
|
4829
|
-
if (update.sessionUpdate === "config_option_update") acpx
|
|
5404
|
+
if (update.sessionUpdate === "config_option_update") applyConfigOptionsModelState(acpx, deepClone(update.configOptions));
|
|
4830
5405
|
}
|
|
4831
5406
|
};
|
|
4832
5407
|
function appendUserMessageChunk(conversation, content) {
|
|
@@ -4843,10 +5418,14 @@ function appendAgentMessageChunk(conversation, content, append) {
|
|
|
4843
5418
|
}
|
|
4844
5419
|
function applyUsageUpdate(conversation, update) {
|
|
4845
5420
|
const usage = usageToTokenUsage(update);
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
5421
|
+
const cost = usageCost(update);
|
|
5422
|
+
if (!usage && !cost) return;
|
|
5423
|
+
if (usage) {
|
|
5424
|
+
conversation.cumulative_token_usage = usage;
|
|
5425
|
+
const userId = lastUserMessageId(conversation);
|
|
5426
|
+
if (userId) conversation.request_token_usage[userId] = usage;
|
|
5427
|
+
}
|
|
5428
|
+
if (cost) conversation.cumulative_cost = cost;
|
|
4850
5429
|
}
|
|
4851
5430
|
function applySessionInfoUpdate(conversation, update) {
|
|
4852
5431
|
if (hasOwn(update, "title")) conversation.title = update.title ?? null;
|
|
@@ -4859,10 +5438,10 @@ function recordClientOperation(conversation, state, operation, timestamp = isoNo
|
|
|
4859
5438
|
return acpx;
|
|
4860
5439
|
}
|
|
4861
5440
|
function trimConversationForRuntime(conversation) {
|
|
4862
|
-
if (conversation.messages.length > MAX_RUNTIME_MESSAGES) conversation.messages = conversation.messages.slice(-
|
|
5441
|
+
if (conversation.messages.length > MAX_RUNTIME_MESSAGES) conversation.messages = conversation.messages.slice(-200);
|
|
4863
5442
|
for (const message of conversation.messages) trimRuntimeMessage(message);
|
|
4864
5443
|
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(-
|
|
5444
|
+
if (requestUsageEntries.length > MAX_RUNTIME_REQUEST_TOKEN_USAGE) conversation.request_token_usage = Object.fromEntries(requestUsageEntries.slice(-100));
|
|
4866
5445
|
}
|
|
4867
5446
|
function trimRuntimeMessage(message) {
|
|
4868
5447
|
if (isUserMessage(message)) {
|
|
@@ -4892,12 +5471,15 @@ function trimRuntimeToolResult(result) {
|
|
|
4892
5471
|
}
|
|
4893
5472
|
//#endregion
|
|
4894
5473
|
//#region src/session/config-options.ts
|
|
5474
|
+
function applyConfigOptionsToState(state, configOptions) {
|
|
5475
|
+
const acpxState = cloneSessionAcpxState(state) ?? {};
|
|
5476
|
+
applyConfigOptionsModelState(acpxState, configOptions);
|
|
5477
|
+
return acpxState;
|
|
5478
|
+
}
|
|
4895
5479
|
function applyConfigOptionsToRecord(record, result) {
|
|
4896
5480
|
const configOptions = result?.configOptions;
|
|
4897
5481
|
if (!configOptions) return;
|
|
4898
|
-
|
|
4899
|
-
acpxState.config_options = structuredClone(configOptions);
|
|
4900
|
-
record.acpx = acpxState;
|
|
5482
|
+
record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
|
|
4901
5483
|
}
|
|
4902
5484
|
//#endregion
|
|
4903
5485
|
//#region src/session/mode-preference.ts
|
|
@@ -4943,17 +5525,29 @@ function setDesiredConfigOption(record, configId, value) {
|
|
|
4943
5525
|
else delete acpx.desired_config_options;
|
|
4944
5526
|
record.acpx = acpx;
|
|
4945
5527
|
}
|
|
5528
|
+
function clearDesiredConfigOption(state, configId) {
|
|
5529
|
+
const normalizedConfigId = normalizeModeId(configId);
|
|
5530
|
+
if (!normalizedConfigId || !state.desired_config_options) return;
|
|
5531
|
+
const desired = { ...state.desired_config_options };
|
|
5532
|
+
delete desired[normalizedConfigId];
|
|
5533
|
+
if (Object.keys(desired).length > 0) state.desired_config_options = desired;
|
|
5534
|
+
else delete state.desired_config_options;
|
|
5535
|
+
}
|
|
4946
5536
|
function getDesiredModelId(state) {
|
|
4947
5537
|
return normalizeModelId(state?.session_options?.model);
|
|
4948
5538
|
}
|
|
4949
|
-
function
|
|
5539
|
+
function hasStoredSessionOptions(options) {
|
|
5540
|
+
return typeof options.model === "string" || Array.isArray(options.allowed_tools) || typeof options.max_turns === "number" || options.system_prompt !== void 0 || options.env !== void 0;
|
|
5541
|
+
}
|
|
5542
|
+
function setDesiredModelId(record, modelId, modelConfigId) {
|
|
4950
5543
|
const acpx = ensureAcpxState(record.acpx);
|
|
4951
5544
|
const normalized = normalizeModelId(modelId);
|
|
4952
5545
|
const sessionOptions = { ...acpx.session_options };
|
|
4953
5546
|
if (normalized) sessionOptions.model = normalized;
|
|
4954
5547
|
else delete sessionOptions.model;
|
|
4955
|
-
if (
|
|
5548
|
+
if (hasStoredSessionOptions(sessionOptions)) acpx.session_options = sessionOptions;
|
|
4956
5549
|
else delete acpx.session_options;
|
|
5550
|
+
clearDesiredConfigOption(acpx, modelConfigId ?? modelStateFromConfigOptions(acpx.config_options)?.configId);
|
|
4957
5551
|
record.acpx = acpx;
|
|
4958
5552
|
}
|
|
4959
5553
|
function setCurrentModelId(record, modelId) {
|
|
@@ -4966,49 +5560,30 @@ function setCurrentModelId(record, modelId) {
|
|
|
4966
5560
|
function syncAdvertisedModelState(record, models) {
|
|
4967
5561
|
if (!models) return;
|
|
4968
5562
|
const acpx = ensureAcpxState(record.acpx);
|
|
4969
|
-
acpx
|
|
4970
|
-
acpx.available_models = models.availableModels.map((model) => model.modelId);
|
|
5563
|
+
applyAdvertisedModelState(acpx, models);
|
|
4971
5564
|
record.acpx = acpx;
|
|
4972
5565
|
}
|
|
4973
5566
|
//#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
5567
|
//#region src/session/model-application.ts
|
|
5568
|
+
function currentModelIdFromSetModelResponse(response, fallbackModelId) {
|
|
5569
|
+
return modelStateFromConfigOptions(response?.configOptions)?.currentModelId ?? fallbackModelId;
|
|
5570
|
+
}
|
|
4999
5571
|
async function applyRequestedModelIfAdvertised(params) {
|
|
5000
5572
|
const requestedModel = typeof params.requestedModel === "string" ? params.requestedModel.trim() : "";
|
|
5001
|
-
if (!requestedModel) return false;
|
|
5002
|
-
assertRequestedModelSupported({
|
|
5573
|
+
if (!requestedModel) return { applied: false };
|
|
5574
|
+
const warning = assertRequestedModelSupported({
|
|
5003
5575
|
requestedModel,
|
|
5004
5576
|
models: params.models,
|
|
5005
5577
|
agentCommand: params.agentCommand,
|
|
5006
5578
|
context: "apply"
|
|
5007
5579
|
});
|
|
5008
|
-
if (
|
|
5009
|
-
if (params.models
|
|
5010
|
-
|
|
5011
|
-
return
|
|
5580
|
+
if (warning) params.onWarning?.(warning);
|
|
5581
|
+
if (!params.models) return { applied: false };
|
|
5582
|
+
if (params.models.currentModelId === requestedModel) return { applied: true };
|
|
5583
|
+
return {
|
|
5584
|
+
applied: true,
|
|
5585
|
+
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models), params.timeoutMs)
|
|
5586
|
+
};
|
|
5012
5587
|
}
|
|
5013
5588
|
//#endregion
|
|
5014
5589
|
//#region src/runtime/engine/reconnect.ts
|
|
@@ -5021,7 +5596,7 @@ function isProcessAlive(pid) {
|
|
|
5021
5596
|
return false;
|
|
5022
5597
|
}
|
|
5023
5598
|
}
|
|
5024
|
-
const SESSION_LOAD_UNSUPPORTED_CODES = new Set([-32601, -32602]);
|
|
5599
|
+
const SESSION_LOAD_UNSUPPORTED_CODES = /* @__PURE__ */ new Set([-32601, -32602]);
|
|
5025
5600
|
function shouldFallbackToNewSession(error, record) {
|
|
5026
5601
|
if (isHardReconnectFailure(error)) return false;
|
|
5027
5602
|
const acp = extractAcpError(error);
|
|
@@ -5056,17 +5631,27 @@ async function replayDesiredMode(params) {
|
|
|
5056
5631
|
}
|
|
5057
5632
|
}
|
|
5058
5633
|
async function replayDesiredModel(params) {
|
|
5059
|
-
if (!params.desiredModelId) return;
|
|
5634
|
+
if (!params.desiredModelId) return { replayed: false };
|
|
5060
5635
|
try {
|
|
5061
|
-
assertRequestedModelSupported({
|
|
5636
|
+
emitModelSupportWarning(assertRequestedModelSupported({
|
|
5062
5637
|
requestedModel: params.desiredModelId,
|
|
5063
5638
|
models: params.models,
|
|
5064
5639
|
agentCommand: params.record.agentCommand,
|
|
5065
5640
|
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);
|
|
5641
|
+
}), params.suppressWarnings);
|
|
5642
|
+
if (!params.models || params.models.currentModelId === params.desiredModelId) return { replayed: false };
|
|
5643
|
+
const response = await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId, params.models), params.timeoutMs);
|
|
5644
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5645
|
+
const models = response ? modelStateFromConfigOptions(response.configOptions) : {
|
|
5646
|
+
...params.models,
|
|
5647
|
+
currentModelId: params.desiredModelId
|
|
5648
|
+
};
|
|
5069
5649
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5650
|
+
return {
|
|
5651
|
+
replayed: true,
|
|
5652
|
+
models,
|
|
5653
|
+
configOptionsPresent: response !== void 0
|
|
5654
|
+
};
|
|
5070
5655
|
} catch (error) {
|
|
5071
5656
|
throw new SessionModelReplayError(`Failed to replay saved session model ${params.desiredModelId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
|
|
5072
5657
|
cause: error instanceof Error ? error : void 0,
|
|
@@ -5074,9 +5659,18 @@ async function replayDesiredModel(params) {
|
|
|
5074
5659
|
});
|
|
5075
5660
|
}
|
|
5076
5661
|
}
|
|
5662
|
+
function emitModelSupportWarning(warning, suppressWarnings) {
|
|
5663
|
+
if (warning && !suppressWarnings) process.stderr.write(`[acpx] warning: ${warning}\n`);
|
|
5664
|
+
}
|
|
5077
5665
|
async function replayDesiredConfigOptions(params) {
|
|
5666
|
+
let result = { replayed: false };
|
|
5078
5667
|
for (const [configId, value] of Object.entries(params.desiredConfigOptions)) try {
|
|
5079
|
-
await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
|
|
5668
|
+
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
|
|
5669
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5670
|
+
result = {
|
|
5671
|
+
replayed: true,
|
|
5672
|
+
models: modelStateFromConfigOptions(response.configOptions)
|
|
5673
|
+
};
|
|
5080
5674
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5081
5675
|
} catch (error) {
|
|
5082
5676
|
throw new SessionConfigOptionReplayError(`Failed to replay saved session config option ${configId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
|
|
@@ -5084,6 +5678,7 @@ async function replayDesiredConfigOptions(params) {
|
|
|
5084
5678
|
retryable: true
|
|
5085
5679
|
});
|
|
5086
5680
|
}
|
|
5681
|
+
return result;
|
|
5087
5682
|
}
|
|
5088
5683
|
function restoreOriginalSessionState(params) {
|
|
5089
5684
|
params.record.acpSessionId = params.sessionId;
|
|
@@ -5095,6 +5690,7 @@ async function connectAndLoadSession(options) {
|
|
|
5095
5690
|
const sameSessionOnly = requiresSameSession(options.resumePolicy) || Boolean(record.importedFrom);
|
|
5096
5691
|
const originalSessionId = record.acpSessionId;
|
|
5097
5692
|
const originalAgentSessionId = record.agentSessionId;
|
|
5693
|
+
const originalAcpx = cloneSessionAcpxState(record.acpx);
|
|
5098
5694
|
const desiredModeId = getDesiredModeId(record.acpx);
|
|
5099
5695
|
const desiredModelId = getDesiredModelId(record.acpx);
|
|
5100
5696
|
const desiredConfigOptions = getDesiredConfigOptions(record.acpx);
|
|
@@ -5127,7 +5723,7 @@ async function connectAndLoadSession(options) {
|
|
|
5127
5723
|
createdFreshSession = loadState.createdFreshSession;
|
|
5128
5724
|
pendingAgentSessionId = loadState.pendingAgentSessionId;
|
|
5129
5725
|
sessionModels = loadState.sessionModels;
|
|
5130
|
-
await replayFreshSessionPreferences({
|
|
5726
|
+
const preferenceReplay = await replayFreshSessionPreferences({
|
|
5131
5727
|
client,
|
|
5132
5728
|
record,
|
|
5133
5729
|
createdFreshSession,
|
|
@@ -5135,14 +5731,16 @@ async function connectAndLoadSession(options) {
|
|
|
5135
5731
|
pendingAgentSessionId,
|
|
5136
5732
|
originalSessionId,
|
|
5137
5733
|
originalAgentSessionId,
|
|
5734
|
+
originalAcpx,
|
|
5138
5735
|
desiredModeId,
|
|
5139
5736
|
desiredModelId,
|
|
5140
5737
|
desiredConfigOptions,
|
|
5141
5738
|
sessionModels,
|
|
5142
5739
|
timeoutMs: options.timeoutMs,
|
|
5143
|
-
verbose: options.verbose
|
|
5740
|
+
verbose: options.verbose,
|
|
5741
|
+
suppressWarnings: options.suppressWarnings
|
|
5144
5742
|
});
|
|
5145
|
-
applyReconnectedModelState(record, sessionModels,
|
|
5743
|
+
applyReconnectedModelState(record, resolveModelsAfterReplay(preferenceReplay, sessionModels), resolveConfigOptionsPresenceAfterReplay(preferenceReplay, loadState.configOptionsPresent), loadState.legacyModelMetadataPresent, createdFreshSession);
|
|
5146
5744
|
options.onSessionIdResolved?.(sessionId);
|
|
5147
5745
|
return {
|
|
5148
5746
|
sessionId,
|
|
@@ -5151,9 +5749,28 @@ async function connectAndLoadSession(options) {
|
|
|
5151
5749
|
loadError
|
|
5152
5750
|
};
|
|
5153
5751
|
}
|
|
5154
|
-
function
|
|
5155
|
-
|
|
5156
|
-
|
|
5752
|
+
function resolveModelsAfterReplay(replay, initialModels) {
|
|
5753
|
+
if (replay.configReplay.replayed) return replay.configReplay.models ?? preserveLegacyModels(replay.modelReplay.replayed ? replay.modelReplay.models : initialModels);
|
|
5754
|
+
return replay.modelReplay.replayed ? replay.modelReplay.models : initialModels;
|
|
5755
|
+
}
|
|
5756
|
+
function preserveLegacyModels(models) {
|
|
5757
|
+
return models && !models.configId ? models : void 0;
|
|
5758
|
+
}
|
|
5759
|
+
function resolveConfigOptionsPresenceAfterReplay(replay, initiallyPresent) {
|
|
5760
|
+
return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.replayed && replay.modelReplay.configOptionsPresent;
|
|
5761
|
+
}
|
|
5762
|
+
function applyReconnectedModelState(record, sessionModels, configOptionsPresent, legacyModelMetadataPresent, createdFreshSession) {
|
|
5763
|
+
clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent);
|
|
5764
|
+
if (sessionModels) {
|
|
5765
|
+
if (legacyModelMetadataPresent && !sessionModels.configId && record.acpx) removeModelConfigOptions(record.acpx);
|
|
5766
|
+
syncAdvertisedModelState(record, sessionModels);
|
|
5767
|
+
} else clearRemovedModelState(record, legacyModelMetadataPresent || createdFreshSession);
|
|
5768
|
+
}
|
|
5769
|
+
function clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent) {
|
|
5770
|
+
if (createdFreshSession && !configOptionsPresent && record.acpx) delete record.acpx.config_options;
|
|
5771
|
+
}
|
|
5772
|
+
function clearRemovedModelState(record, shouldClear) {
|
|
5773
|
+
if (shouldClear && record.acpx) clearAdvertisedModelState(record.acpx);
|
|
5157
5774
|
}
|
|
5158
5775
|
function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbose) {
|
|
5159
5776
|
if (!verbose) return;
|
|
@@ -5164,7 +5781,12 @@ function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbos
|
|
|
5164
5781
|
if (shouldReconnect) process.stderr.write(`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session reconnect\n`);
|
|
5165
5782
|
}
|
|
5166
5783
|
async function replayFreshSessionPreferences(params) {
|
|
5167
|
-
if (!params.createdFreshSession) return
|
|
5784
|
+
if (!params.createdFreshSession) return {
|
|
5785
|
+
modelReplay: { replayed: false },
|
|
5786
|
+
configReplay: { replayed: false }
|
|
5787
|
+
};
|
|
5788
|
+
let modelReplay = { replayed: false };
|
|
5789
|
+
let configReplay = { replayed: false };
|
|
5168
5790
|
try {
|
|
5169
5791
|
await replayDesiredMode({
|
|
5170
5792
|
client: params.client,
|
|
@@ -5174,7 +5796,7 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5174
5796
|
timeoutMs: params.timeoutMs,
|
|
5175
5797
|
verbose: params.verbose
|
|
5176
5798
|
});
|
|
5177
|
-
await replayDesiredModel({
|
|
5799
|
+
modelReplay = await replayDesiredModel({
|
|
5178
5800
|
client: params.client,
|
|
5179
5801
|
sessionId: params.sessionId,
|
|
5180
5802
|
desiredModelId: params.desiredModelId,
|
|
@@ -5182,10 +5804,12 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5182
5804
|
record: params.record,
|
|
5183
5805
|
models: params.sessionModels,
|
|
5184
5806
|
timeoutMs: params.timeoutMs,
|
|
5185
|
-
verbose: params.verbose
|
|
5807
|
+
verbose: params.verbose,
|
|
5808
|
+
suppressWarnings: params.suppressWarnings
|
|
5186
5809
|
});
|
|
5187
|
-
await replayDesiredConfigOptions({
|
|
5810
|
+
configReplay = await replayDesiredConfigOptions({
|
|
5188
5811
|
client: params.client,
|
|
5812
|
+
record: params.record,
|
|
5189
5813
|
sessionId: params.sessionId,
|
|
5190
5814
|
desiredConfigOptions: params.desiredConfigOptions,
|
|
5191
5815
|
previousSessionId: params.originalSessionId,
|
|
@@ -5198,17 +5822,24 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5198
5822
|
sessionId: params.originalSessionId,
|
|
5199
5823
|
agentSessionId: params.originalAgentSessionId
|
|
5200
5824
|
});
|
|
5825
|
+
params.record.acpx = cloneSessionAcpxState(params.originalAcpx);
|
|
5201
5826
|
if (params.verbose) process.stderr.write(`[acpx] ${formatErrorMessage(error)}\n`);
|
|
5202
5827
|
throw error;
|
|
5203
5828
|
}
|
|
5204
5829
|
params.record.acpSessionId = params.sessionId;
|
|
5205
5830
|
reconcileAgentSessionId(params.record, params.pendingAgentSessionId);
|
|
5831
|
+
return {
|
|
5832
|
+
modelReplay,
|
|
5833
|
+
configReplay
|
|
5834
|
+
};
|
|
5206
5835
|
}
|
|
5207
5836
|
async function loadOrCreateRuntimeSession(params) {
|
|
5208
5837
|
if (params.reusingLoadedSession) return {
|
|
5209
5838
|
sessionId: params.record.acpSessionId,
|
|
5210
5839
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5211
5840
|
sessionModels: void 0,
|
|
5841
|
+
configOptionsPresent: false,
|
|
5842
|
+
legacyModelMetadataPresent: false,
|
|
5212
5843
|
resumed: true,
|
|
5213
5844
|
createdFreshSession: false
|
|
5214
5845
|
};
|
|
@@ -5229,6 +5860,8 @@ async function resumeRuntimeSession(params) {
|
|
|
5229
5860
|
sessionId: params.record.acpSessionId,
|
|
5230
5861
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5231
5862
|
sessionModels: resumeResult.models,
|
|
5863
|
+
configOptionsPresent: resumeResult.configOptionsPresent,
|
|
5864
|
+
legacyModelMetadataPresent: resumeResult.legacyModelMetadataPresent,
|
|
5232
5865
|
resumed: true,
|
|
5233
5866
|
createdFreshSession: false
|
|
5234
5867
|
};
|
|
@@ -5245,6 +5878,8 @@ async function loadRuntimeSession(params) {
|
|
|
5245
5878
|
sessionId: params.record.acpSessionId,
|
|
5246
5879
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5247
5880
|
sessionModels: loadResult.models,
|
|
5881
|
+
configOptionsPresent: loadResult.configOptionsPresent,
|
|
5882
|
+
legacyModelMetadataPresent: loadResult.legacyModelMetadataPresent,
|
|
5248
5883
|
resumed: true,
|
|
5249
5884
|
createdFreshSession: false
|
|
5250
5885
|
};
|
|
@@ -5272,6 +5907,8 @@ async function createFreshRuntimeSession(client, record, timeoutMs) {
|
|
|
5272
5907
|
sessionId: createdSession.sessionId,
|
|
5273
5908
|
pendingAgentSessionId: createdSession.agentSessionId,
|
|
5274
5909
|
sessionModels: createdSession.models,
|
|
5910
|
+
configOptionsPresent: createdSession.configOptionsPresent,
|
|
5911
|
+
legacyModelMetadataPresent: createdSession.legacyModelMetadataPresent,
|
|
5275
5912
|
resumed: false,
|
|
5276
5913
|
createdFreshSession: true
|
|
5277
5914
|
};
|
|
@@ -5287,7 +5924,10 @@ function createActiveSessionController(params) {
|
|
|
5287
5924
|
await params.client.setSessionMode(getActiveSessionId(), modeId);
|
|
5288
5925
|
},
|
|
5289
5926
|
setSessionModel: async (modelId) => {
|
|
5290
|
-
|
|
5927
|
+
const models = advertisedModelState(params.record.acpx);
|
|
5928
|
+
const response = await params.client.setSessionModel(getActiveSessionId(), modelId, models);
|
|
5929
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5930
|
+
return response;
|
|
5291
5931
|
},
|
|
5292
5932
|
setSessionConfigOption: async (configId, value) => {
|
|
5293
5933
|
return await params.client.setSessionConfigOption(getActiveSessionId(), configId, value);
|
|
@@ -5325,6 +5965,7 @@ async function withConnectedSession(options) {
|
|
|
5325
5965
|
let notifiedClientAvailable = false;
|
|
5326
5966
|
const activeController = createActiveSessionController({
|
|
5327
5967
|
client,
|
|
5968
|
+
record,
|
|
5328
5969
|
getActiveSessionId: () => activeSessionIdForControl
|
|
5329
5970
|
});
|
|
5330
5971
|
try {
|
|
@@ -5468,6 +6109,6 @@ var LiveSessionCheckpoint = class {
|
|
|
5468
6109
|
}
|
|
5469
6110
|
};
|
|
5470
6111
|
//#endregion
|
|
5471
|
-
export {
|
|
6112
|
+
export { getPerfMetricsSnapshot as $, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE as A, listBuiltInAgents as At, absolutePath as B, AUTH_POLICIES as Bt, mergeSessionOptions as C, promptToDisplayText as Ct, applyLifecycleSnapshotToRecord as D, withInterrupt as Dt, applyConversation as E, TimeoutError as Et, modelStateFromConfigOptions as F, isRetryablePromptError as Ft, listSessions as G, OUTPUT_FORMATS as Gt, findSession as H, NON_INTERACTIVE_PERMISSION_POLICIES as Ht, splitCommandLine as I, normalizeOutputError as It, pruneSessions as J, SESSION_RECORD_SCHEMA as Jt, listSessionsForAgent as K, PERMISSION_MODES as Kt, getAcpxVersion as L, extractAcpError as Lt, RequestedModelUnsupportedError as M, resolveAgentCommand as Mt, assertRequestedModelSupported as N, exitCodeForOutputErrorCode as Nt, reconcileAgentSessionId as O, withTimeout as Ot, isRequestedModelUnsupportedError as P, formatErrorMessage as Pt, formatPerfMetric as Q, QueueProtocolError as Qt, permissionModeSatisfies as R, isAcpResourceNotFoundError as Rt, advertisedModelState as S, parsePromptSource as St, sessionOptionsFromRecord as T, InterruptedError as Tt, findSessionByDirectoryWalk as U, OUTPUT_ERROR_CODES as Ut, findGitRepositoryRoot as V, EXIT_CODES as Vt, isoNow$2 as W, OUTPUT_ERROR_ORIGINS as Wt, writeSessionRecord as X, AgentSpawnError as Xt, resolveSessionRecord as Y, AcpxOperationalError as Yt, assertPersistedKeyPolicy as Z, QueueConnectionError as Zt, createSessionConversation as _, parseJsonRpcErrorMessage as _t, applyRequestedModelIfAdvertised as a, startPerfTimer as at, recordSessionUpdate as b, isPromptInput as bt, setCurrentModelId as c, normalizeRuntimeSessionId as ct, setDesiredModelId as d, sessionBaseDir$1 as dt, incrementPerfCounter as et, syncAdvertisedModelState as f, sessionEventActivePath as ft, cloneSessionConversation as g, isAcpJsonRpcMessage as gt, cloneSessionAcpxState as h, extractSessionUpdateNotification as ht, connectAndLoadSession as i, setPerfGauge as it, REQUESTED_MODEL_UNSUPPORTED_REASONS as j, normalizeAgentName$1 as jt, AcpClient as k, DEFAULT_AGENT_NAME as kt, setDesiredConfigOption as l, DEFAULT_EVENT_SEGMENT_MAX_BYTES as lt, applyConfigOptionsToState as m, sessionEventSegmentPath as mt, runPromptTurn as n, recordPerfDuration as nt, currentModelIdFromSetModelResponse as o, parseSessionRecord as ot, applyConfigOptionsToRecord as p, sessionEventLockPath as pt, normalizeName as q, PERMISSION_POLICY_ACTIONS as qt, withConnectedSession as r, resetPerfMetrics as rt, clearDesiredConfigOption as s, serializeSessionRecordForDisk as st, LiveSessionCheckpoint as t, measurePerf as tt, setDesiredModeId as u, defaultSessionEventLog as ut, recordClientOperation as v, parsePromptStopReason as vt, persistSessionOptions as w, textPrompt as wt, trimConversationForRuntime as x, mergePromptSourceWithText as xt, recordPromptSubmission as y, PromptInputValidationError as yt, DEFAULT_HISTORY_LIMIT as z, toAcpErrorPayload as zt };
|
|
5472
6113
|
|
|
5473
|
-
//# sourceMappingURL=live-checkpoint-
|
|
6114
|
+
//# sourceMappingURL=live-checkpoint-BWkYxMeS.js.map
|