acpx 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -19
- package/dist/{cli-Bf3yjqzE.js → cli-CC2w0U-A.js} +4 -4
- package/dist/{cli-Bf3yjqzE.js.map → cli-CC2w0U-A.js.map} +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +685 -67
- package/dist/cli.js.map +1 -1
- package/dist/{client-BssohYqM.d.ts → client-j3sLnpcM.d.ts} +27 -4
- package/dist/client-j3sLnpcM.d.ts.map +1 -0
- package/dist/{flags-C-rwARqg.js → flags-BKjjl3tF.js} +4 -4
- package/dist/flags-BKjjl3tF.js.map +1 -0
- package/dist/{flows-WLs26_5Y.js → flows-BabqiU0u.js} +5 -4
- package/dist/flows-BabqiU0u.js.map +1 -0
- package/dist/flows.d.ts +1 -1
- package/dist/flows.d.ts.map +1 -1
- package/dist/flows.js +1 -1
- package/dist/{live-checkpoint-D5d-K9s1.js → live-checkpoint-BZrk9Mjz.js} +894 -384
- package/dist/live-checkpoint-BZrk9Mjz.js.map +1 -0
- package/dist/{output-DPg20dvn.js → output-D_BGt1YI.js} +180 -98
- package/dist/output-D_BGt1YI.js.map +1 -0
- package/dist/runtime.d.ts +71 -5
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +188 -32
- package/dist/runtime.js.map +1 -1
- package/dist/{session-options-CFudjdkU.d.ts → session-options-Co1oGEK8.d.ts} +22 -2
- package/dist/{session-options-CFudjdkU.d.ts.map → session-options-Co1oGEK8.d.ts.map} +1 -1
- package/package.json +23 -17
- package/skills/acpx/SKILL.md +66 -5
- package/dist/client-BssohYqM.d.ts.map +0 -1
- package/dist/flags-C-rwARqg.js.map +0 -1
- package/dist/flows-WLs26_5Y.js.map +0 -1
- package/dist/live-checkpoint-D5d-K9s1.js.map +0 -1
- package/dist/output-DPg20dvn.js.map +0 -1
|
@@ -1,14 +1,14 @@
|
|
|
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";
|
|
5
5
|
import os from "node:os";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
6
7
|
import { execFile, spawn } from "node:child_process";
|
|
7
8
|
import { Readable, Writable } from "node:stream";
|
|
8
|
-
import { ClientSideConnection, PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
|
9
|
+
import { ClientSideConnection, PROTOCOL_VERSION, RequestError } from "@agentclientprotocol/sdk";
|
|
9
10
|
import readline from "node:readline/promises";
|
|
10
11
|
import { promisify } from "node:util";
|
|
11
|
-
import { randomUUID } from "node:crypto";
|
|
12
12
|
//#region src/errors.ts
|
|
13
13
|
var AcpxOperationalError = class extends Error {
|
|
14
14
|
outputCode;
|
|
@@ -223,12 +223,12 @@ const SESSION_RECORD_SCHEMA = "acpx.session.v1";
|
|
|
223
223
|
//#endregion
|
|
224
224
|
//#region src/acp/error-shapes.ts
|
|
225
225
|
const RESOURCE_NOT_FOUND_ACP_CODES = new Set([-32001, -32002]);
|
|
226
|
-
function asRecord$
|
|
226
|
+
function asRecord$8(value) {
|
|
227
227
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
228
228
|
return value;
|
|
229
229
|
}
|
|
230
230
|
function toAcpErrorPayload(value) {
|
|
231
|
-
const record = asRecord$
|
|
231
|
+
const record = asRecord$8(value);
|
|
232
232
|
if (!record) return;
|
|
233
233
|
if (typeof record.code !== "number" || !Number.isFinite(record.code)) return;
|
|
234
234
|
if (typeof record.message !== "string" || record.message.length === 0) return;
|
|
@@ -242,7 +242,7 @@ function extractAcpErrorInternal(value, depth) {
|
|
|
242
242
|
if (depth > 5) return;
|
|
243
243
|
const direct = toAcpErrorPayload(value);
|
|
244
244
|
if (direct) return direct;
|
|
245
|
-
const record = asRecord$
|
|
245
|
+
const record = asRecord$8(value);
|
|
246
246
|
if (!record) return;
|
|
247
247
|
return extractNestedAcpError(record, depth);
|
|
248
248
|
}
|
|
@@ -277,7 +277,7 @@ function hasSessionNotFoundHint(value, depth = 0) {
|
|
|
277
277
|
if (depth > 4) return false;
|
|
278
278
|
if (isSessionNotFoundText(value)) return true;
|
|
279
279
|
if (Array.isArray(value)) return value.some((entry) => hasSessionNotFoundHint(entry, depth + 1));
|
|
280
|
-
const record = asRecord$
|
|
280
|
+
const record = asRecord$8(value);
|
|
281
281
|
if (!record) return false;
|
|
282
282
|
return Object.values(record).some((entry) => hasSessionNotFoundHint(entry, depth + 1));
|
|
283
283
|
}
|
|
@@ -297,7 +297,7 @@ function isAcpResourceNotFoundError(error) {
|
|
|
297
297
|
//#region src/acp/error-normalization.ts
|
|
298
298
|
const AUTH_REQUIRED_ACP_CODES = new Set([-32e3]);
|
|
299
299
|
const QUERY_CLOSED_BEFORE_RESPONSE_DETAIL = "query closed before response received";
|
|
300
|
-
function asRecord$
|
|
300
|
+
function asRecord$7(value) {
|
|
301
301
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
302
302
|
return value;
|
|
303
303
|
}
|
|
@@ -318,7 +318,7 @@ function isAcpAuthRequiredPayload(acp) {
|
|
|
318
318
|
if (!acp) return false;
|
|
319
319
|
if (!AUTH_REQUIRED_ACP_CODES.has(acp.code)) return false;
|
|
320
320
|
if (isAuthRequiredMessage(acp.message)) return true;
|
|
321
|
-
const data = asRecord$
|
|
321
|
+
const data = asRecord$7(acp.data);
|
|
322
322
|
if (!data) return false;
|
|
323
323
|
return hasAuthRequiredData(data);
|
|
324
324
|
}
|
|
@@ -338,7 +338,7 @@ function isOutputErrorOrigin(value) {
|
|
|
338
338
|
return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
|
|
339
339
|
}
|
|
340
340
|
function readOutputErrorMeta(error) {
|
|
341
|
-
const record = asRecord$
|
|
341
|
+
const record = asRecord$7(error);
|
|
342
342
|
if (!record) return {};
|
|
343
343
|
return {
|
|
344
344
|
outputCode: isOutputErrorCode(record.outputCode) ? record.outputCode : void 0,
|
|
@@ -356,7 +356,7 @@ function isNoSessionLike(error) {
|
|
|
356
356
|
}
|
|
357
357
|
function isUsageLike(error) {
|
|
358
358
|
if (!(error instanceof Error)) return false;
|
|
359
|
-
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord$
|
|
359
|
+
return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord$7(error)?.code === "commander.invalidArgument";
|
|
360
360
|
}
|
|
361
361
|
function formatErrorMessage(error) {
|
|
362
362
|
return formatUnknownErrorMessage(error);
|
|
@@ -364,7 +364,7 @@ function formatErrorMessage(error) {
|
|
|
364
364
|
function isAcpQueryClosedBeforeResponseError(error) {
|
|
365
365
|
const acp = extractAcpError(error);
|
|
366
366
|
if (!acp || acp.code !== -32603) return false;
|
|
367
|
-
const details = asRecord$
|
|
367
|
+
const details = asRecord$7(acp.data)?.details;
|
|
368
368
|
if (typeof details !== "string") return false;
|
|
369
369
|
return details.toLowerCase().includes(QUERY_CLOSED_BEFORE_RESPONSE_DETAIL);
|
|
370
370
|
}
|
|
@@ -432,7 +432,8 @@ function exitCodeForOutputErrorCode(code) {
|
|
|
432
432
|
const ACP_ADAPTER_PACKAGE_RANGES = {
|
|
433
433
|
pi: "^0.0.26",
|
|
434
434
|
codex: "^0.0.44",
|
|
435
|
-
claude: "^0.
|
|
435
|
+
claude: "^0.37.0",
|
|
436
|
+
mux: "^0.27.0"
|
|
436
437
|
};
|
|
437
438
|
const AGENT_REGISTRY = {
|
|
438
439
|
pi: `npx pi-acp@${ACP_ADAPTER_PACKAGE_RANGES.pi}`,
|
|
@@ -443,10 +444,12 @@ const AGENT_REGISTRY = {
|
|
|
443
444
|
cursor: "cursor-agent acp",
|
|
444
445
|
copilot: "copilot --acp --stdio",
|
|
445
446
|
droid: "droid exec --output-format acp",
|
|
447
|
+
"fast-agent": "uvx fast-agent-mcp acp",
|
|
446
448
|
iflow: "iflow --experimental-acp",
|
|
447
449
|
kilocode: "npx -y @kilocode/cli acp",
|
|
448
450
|
kimi: "kimi acp",
|
|
449
451
|
kiro: "kiro-cli-chat acp",
|
|
452
|
+
mux: `npx -y mux@${ACP_ADAPTER_PACKAGE_RANGES.mux} acp`,
|
|
450
453
|
opencode: "npx -y opencode-ai acp",
|
|
451
454
|
qoder: "qodercli --acp",
|
|
452
455
|
qwen: "qwen --acp",
|
|
@@ -658,7 +661,7 @@ var PromptInputValidationError = class extends Error {
|
|
|
658
661
|
this.name = "PromptInputValidationError";
|
|
659
662
|
}
|
|
660
663
|
};
|
|
661
|
-
function asRecord$
|
|
664
|
+
function asRecord$6(value) {
|
|
662
665
|
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
663
666
|
return value;
|
|
664
667
|
}
|
|
@@ -676,28 +679,28 @@ function isAudioMimeType(value) {
|
|
|
676
679
|
return /^audio\/[A-Za-z0-9.+-]+$/i.test(value);
|
|
677
680
|
}
|
|
678
681
|
function isTextBlock(value) {
|
|
679
|
-
const record = asRecord$
|
|
682
|
+
const record = asRecord$6(value);
|
|
680
683
|
return record?.type === "text" && typeof record.text === "string";
|
|
681
684
|
}
|
|
682
685
|
function isImageBlock(value) {
|
|
683
|
-
const record = asRecord$
|
|
686
|
+
const record = asRecord$6(value);
|
|
684
687
|
return record?.type === "image" && isNonEmptyString(record.mimeType) && isImageMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
|
|
685
688
|
}
|
|
686
689
|
function isAudioBlock(value) {
|
|
687
|
-
const record = asRecord$
|
|
690
|
+
const record = asRecord$6(value);
|
|
688
691
|
return record?.type === "audio" && isNonEmptyString(record.mimeType) && isAudioMimeType(record.mimeType) && typeof record.data === "string" && isBase64Data(record.data);
|
|
689
692
|
}
|
|
690
693
|
function isResourceLinkBlock(value) {
|
|
691
|
-
const record = asRecord$
|
|
694
|
+
const record = asRecord$6(value);
|
|
692
695
|
return record?.type === "resource_link" && isNonEmptyString(record.uri) && (record.title === void 0 || typeof record.title === "string") && (record.name === void 0 || typeof record.name === "string");
|
|
693
696
|
}
|
|
694
697
|
function isResourcePayload(value) {
|
|
695
|
-
const record = asRecord$
|
|
698
|
+
const record = asRecord$6(value);
|
|
696
699
|
if (!record || !isNonEmptyString(record.uri)) return false;
|
|
697
700
|
return record.text === void 0 || typeof record.text === "string";
|
|
698
701
|
}
|
|
699
702
|
function isResourceBlock(value) {
|
|
700
|
-
const record = asRecord$
|
|
703
|
+
const record = asRecord$6(value);
|
|
701
704
|
return record?.type === "resource" && isResourcePayload(record.resource);
|
|
702
705
|
}
|
|
703
706
|
const CONTENT_BLOCK_VALIDATORS = [
|
|
@@ -741,11 +744,11 @@ function validateResourceLinkContentBlock(record, index) {
|
|
|
741
744
|
if (record.name !== void 0 && typeof record.name !== "string") return `prompt[${index}] resource_link block name must be a string when present`;
|
|
742
745
|
}
|
|
743
746
|
function validateResourceContentBlock(record, index) {
|
|
744
|
-
if (!asRecord$
|
|
747
|
+
if (!asRecord$6(record.resource)) return `prompt[${index}] resource block must include a resource object`;
|
|
745
748
|
return isResourcePayload(record.resource) ? void 0 : `prompt[${index}] resource block resource must include a non-empty uri and optional text`;
|
|
746
749
|
}
|
|
747
750
|
function getContentBlockValidationError(value, index) {
|
|
748
|
-
const record = asRecord$
|
|
751
|
+
const record = asRecord$6(value);
|
|
749
752
|
if (!record || typeof record.type !== "string") return `prompt[${index}] must be an ACP content block object`;
|
|
750
753
|
const validator = contentBlockErrorValidator(record.type);
|
|
751
754
|
return validator ? validator(record, index) : `prompt[${index}] has unsupported content block type ${JSON.stringify(record.type)}`;
|
|
@@ -827,6 +830,104 @@ function resourceBlockDisplayText(block) {
|
|
|
827
830
|
return "text" in block.resource && typeof block.resource.text === "string" ? block.resource.text : block.resource.uri;
|
|
828
831
|
}
|
|
829
832
|
//#endregion
|
|
833
|
+
//#region src/acp/jsonrpc.ts
|
|
834
|
+
function asRecord$5(value) {
|
|
835
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
836
|
+
return value;
|
|
837
|
+
}
|
|
838
|
+
function hasValidId(value) {
|
|
839
|
+
return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
|
|
840
|
+
}
|
|
841
|
+
function isErrorObject(value) {
|
|
842
|
+
const record = asRecord$5(value);
|
|
843
|
+
return !!record && typeof record.code === "number" && Number.isFinite(record.code) && typeof record.message === "string";
|
|
844
|
+
}
|
|
845
|
+
function hasResultOrError(value) {
|
|
846
|
+
const hasResult = Object.hasOwn(value, "result");
|
|
847
|
+
const hasError = Object.hasOwn(value, "error");
|
|
848
|
+
if (hasResult && hasError) return false;
|
|
849
|
+
if (!hasResult && !hasError) return false;
|
|
850
|
+
if (hasError && !isErrorObject(value.error)) return false;
|
|
851
|
+
return true;
|
|
852
|
+
}
|
|
853
|
+
function hasMethod(value) {
|
|
854
|
+
return typeof value.method === "string" && value.method.length > 0;
|
|
855
|
+
}
|
|
856
|
+
function isJsonRpcRequest(value) {
|
|
857
|
+
return hasMethod(value) && Object.hasOwn(value, "id") && hasValidId(value.id);
|
|
858
|
+
}
|
|
859
|
+
function isJsonRpcNotificationRecord(value) {
|
|
860
|
+
return hasMethod(value) && !Object.hasOwn(value, "id");
|
|
861
|
+
}
|
|
862
|
+
function isJsonRpcResponse(value) {
|
|
863
|
+
if (hasMethod(value) || !Object.hasOwn(value, "id") || !hasValidId(value.id)) return false;
|
|
864
|
+
return hasResultOrError(value);
|
|
865
|
+
}
|
|
866
|
+
function isAcpJsonRpcMessage(value) {
|
|
867
|
+
const record = asRecord$5(value);
|
|
868
|
+
if (!record || record.jsonrpc !== "2.0") return false;
|
|
869
|
+
return isJsonRpcNotificationRecord(record) || isJsonRpcRequest(record) || isJsonRpcResponse(record);
|
|
870
|
+
}
|
|
871
|
+
function isJsonRpcNotification(message) {
|
|
872
|
+
return Object.hasOwn(message, "method") && typeof message.method === "string" && !Object.hasOwn(message, "id");
|
|
873
|
+
}
|
|
874
|
+
function isSessionUpdateNotification(message) {
|
|
875
|
+
return isJsonRpcNotification(message) && message.method === "session/update";
|
|
876
|
+
}
|
|
877
|
+
function extractSessionUpdateNotification(message) {
|
|
878
|
+
if (!isSessionUpdateNotification(message)) return;
|
|
879
|
+
const params = asRecord$5(message.params);
|
|
880
|
+
if (!params) return;
|
|
881
|
+
const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
|
|
882
|
+
if (!sessionId) return;
|
|
883
|
+
const update = asRecord$5(params.update);
|
|
884
|
+
if (!update || typeof update.sessionUpdate !== "string") return;
|
|
885
|
+
return {
|
|
886
|
+
sessionId,
|
|
887
|
+
update
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
function parsePromptStopReason(message) {
|
|
891
|
+
if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
|
|
892
|
+
const record = asRecord$5(message.result);
|
|
893
|
+
if (!record) return;
|
|
894
|
+
return typeof record.stopReason === "string" ? record.stopReason : void 0;
|
|
895
|
+
}
|
|
896
|
+
function parseJsonRpcErrorMessage(message) {
|
|
897
|
+
if (!Object.hasOwn(message, "error")) return;
|
|
898
|
+
const errorRecord = asRecord$5(message.error);
|
|
899
|
+
if (!errorRecord || typeof errorRecord.message !== "string") return;
|
|
900
|
+
return errorRecord.message;
|
|
901
|
+
}
|
|
902
|
+
//#endregion
|
|
903
|
+
//#region src/session/event-log.ts
|
|
904
|
+
const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 64 * 1024 * 1024;
|
|
905
|
+
function sessionBaseDir$1() {
|
|
906
|
+
return path.join(os.homedir(), ".acpx", "sessions");
|
|
907
|
+
}
|
|
908
|
+
function safeSessionId(sessionId) {
|
|
909
|
+
return encodeURIComponent(sessionId);
|
|
910
|
+
}
|
|
911
|
+
function sessionEventActivePath(sessionId) {
|
|
912
|
+
return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.ndjson`);
|
|
913
|
+
}
|
|
914
|
+
function sessionEventSegmentPath(sessionId, segment) {
|
|
915
|
+
return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.${segment}.ndjson`);
|
|
916
|
+
}
|
|
917
|
+
function sessionEventLockPath(sessionId) {
|
|
918
|
+
return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.lock`);
|
|
919
|
+
}
|
|
920
|
+
function defaultSessionEventLog(sessionId) {
|
|
921
|
+
return {
|
|
922
|
+
active_path: sessionEventActivePath(sessionId),
|
|
923
|
+
segment_count: 5,
|
|
924
|
+
max_segment_bytes: DEFAULT_EVENT_SEGMENT_MAX_BYTES,
|
|
925
|
+
max_segments: 5,
|
|
926
|
+
last_write_at: void 0,
|
|
927
|
+
last_write_error: null
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
//#endregion
|
|
830
931
|
//#region src/acp/agent-session-id.ts
|
|
831
932
|
const AGENT_SESSION_ID_META_KEYS = ["agentSessionId", "sessionId"];
|
|
832
933
|
function normalizeAgentSessionId(value) {
|
|
@@ -889,173 +990,15 @@ function serializeSessionRecordForDisk(record) {
|
|
|
889
990
|
messages: canonical.messages,
|
|
890
991
|
updated_at: canonical.updated_at,
|
|
891
992
|
cumulative_token_usage: canonical.cumulative_token_usage,
|
|
993
|
+
cumulative_cost: canonical.cumulative_cost,
|
|
892
994
|
request_token_usage: canonical.request_token_usage,
|
|
893
|
-
acpx: canonical.acpx
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
const timings = /* @__PURE__ */ new Map();
|
|
901
|
-
function hrNow() {
|
|
902
|
-
return process.hrtime.bigint();
|
|
903
|
-
}
|
|
904
|
-
function durationMs(start) {
|
|
905
|
-
return Number(process.hrtime.bigint() - start) / 1e6;
|
|
906
|
-
}
|
|
907
|
-
function roundMetric(value) {
|
|
908
|
-
return Number(value.toFixed(3));
|
|
909
|
-
}
|
|
910
|
-
function incrementPerfCounter(name, delta = 1) {
|
|
911
|
-
counters.set(name, (counters.get(name) ?? 0) + delta);
|
|
912
|
-
}
|
|
913
|
-
function setPerfGauge(name, value) {
|
|
914
|
-
gauges.set(name, value);
|
|
915
|
-
}
|
|
916
|
-
function recordPerfDuration(name, durationMsValue) {
|
|
917
|
-
const next = timings.get(name) ?? {
|
|
918
|
-
count: 0,
|
|
919
|
-
totalMs: 0,
|
|
920
|
-
maxMs: 0
|
|
921
|
-
};
|
|
922
|
-
next.count += 1;
|
|
923
|
-
next.totalMs += durationMsValue;
|
|
924
|
-
next.maxMs = Math.max(next.maxMs, durationMsValue);
|
|
925
|
-
timings.set(name, next);
|
|
926
|
-
}
|
|
927
|
-
async function measurePerf(name, run) {
|
|
928
|
-
const startedAt = hrNow();
|
|
929
|
-
try {
|
|
930
|
-
return await run();
|
|
931
|
-
} finally {
|
|
932
|
-
recordPerfDuration(name, durationMs(startedAt));
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
function startPerfTimer(name) {
|
|
936
|
-
const startedAt = hrNow();
|
|
937
|
-
return () => {
|
|
938
|
-
const elapsedMs = durationMs(startedAt);
|
|
939
|
-
recordPerfDuration(name, elapsedMs);
|
|
940
|
-
return elapsedMs;
|
|
941
|
-
};
|
|
942
|
-
}
|
|
943
|
-
function getPerfMetricsSnapshot() {
|
|
944
|
-
return {
|
|
945
|
-
counters: Object.fromEntries(counters.entries()),
|
|
946
|
-
gauges: Object.fromEntries(gauges.entries()),
|
|
947
|
-
timings: Object.fromEntries([...timings.entries()].map(([name, bucket]) => [name, {
|
|
948
|
-
count: bucket.count,
|
|
949
|
-
totalMs: roundMetric(bucket.totalMs),
|
|
950
|
-
maxMs: roundMetric(bucket.maxMs)
|
|
951
|
-
}]))
|
|
952
|
-
};
|
|
953
|
-
}
|
|
954
|
-
function resetPerfMetrics() {
|
|
955
|
-
counters.clear();
|
|
956
|
-
gauges.clear();
|
|
957
|
-
timings.clear();
|
|
958
|
-
}
|
|
959
|
-
function formatPerfMetric(name, durationMsValue) {
|
|
960
|
-
return `${name}=${roundMetric(durationMsValue)}ms`;
|
|
961
|
-
}
|
|
962
|
-
//#endregion
|
|
963
|
-
//#region src/persisted-key-policy.ts
|
|
964
|
-
const SNAKE_CASE_KEY = /^[a-z][a-z0-9_]*$/;
|
|
965
|
-
const ZED_TAG_KEYS = new Set([
|
|
966
|
-
"User",
|
|
967
|
-
"Agent",
|
|
968
|
-
"Resume",
|
|
969
|
-
"Text",
|
|
970
|
-
"Mention",
|
|
971
|
-
"Image",
|
|
972
|
-
"Audio",
|
|
973
|
-
"Thinking",
|
|
974
|
-
"RedactedThinking",
|
|
975
|
-
"ToolUse"
|
|
976
|
-
]);
|
|
977
|
-
const MAP_OBJECT_PATHS = new Set(["request_token_usage", "messages.Agent.tool_results"]);
|
|
978
|
-
const OPAQUE_VALUE_PATHS = new Set([
|
|
979
|
-
"agent_capabilities",
|
|
980
|
-
"messages.Agent.content.ToolUse.input",
|
|
981
|
-
"acpx.desired_config_options",
|
|
982
|
-
"acpx.config_options"
|
|
983
|
-
]);
|
|
984
|
-
function isRecord(value) {
|
|
985
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
986
|
-
}
|
|
987
|
-
function joinPath(path) {
|
|
988
|
-
return path.join(".");
|
|
989
|
-
}
|
|
990
|
-
function isAllowedKey(path, key) {
|
|
991
|
-
if (ZED_TAG_KEYS.has(key)) return true;
|
|
992
|
-
return false;
|
|
993
|
-
}
|
|
994
|
-
function shouldSkipKeyRule(path) {
|
|
995
|
-
return MAP_OBJECT_PATHS.has(joinPath(path));
|
|
996
|
-
}
|
|
997
|
-
function shouldSkipDescend(path) {
|
|
998
|
-
return OPAQUE_VALUE_PATHS.has(joinPath(path)) || isToolResultOutputPath(path);
|
|
999
|
-
}
|
|
1000
|
-
function isToolResultOutputTail(path, toolResultsIndex) {
|
|
1001
|
-
return toolResultsIndex !== -1 && toolResultsIndex + 2 === path.length - 1;
|
|
1002
|
-
}
|
|
1003
|
-
function isToolResultOutputPath(path) {
|
|
1004
|
-
if (path.length < 5 || path[path.length - 1] !== "output") return false;
|
|
1005
|
-
const toolResultsIndex = path.lastIndexOf("tool_results");
|
|
1006
|
-
if (!isToolResultOutputTail(path, toolResultsIndex)) return false;
|
|
1007
|
-
return path.slice(0, toolResultsIndex + 1).join(".") === "messages.Agent.tool_results";
|
|
1008
|
-
}
|
|
1009
|
-
function collectViolations(value, path, violations) {
|
|
1010
|
-
if (Array.isArray(value)) {
|
|
1011
|
-
for (const entry of value) collectViolations(entry, path, violations);
|
|
1012
|
-
return;
|
|
1013
|
-
}
|
|
1014
|
-
if (!isRecord(value)) return;
|
|
1015
|
-
const skipKeyRule = shouldSkipKeyRule(path);
|
|
1016
|
-
for (const [key, child] of Object.entries(value)) collectKeyViolation(child, key, path, skipKeyRule, violations);
|
|
1017
|
-
}
|
|
1018
|
-
function collectKeyViolation(child, key, path, skipKeyRule, violations) {
|
|
1019
|
-
if (!skipKeyRule && !SNAKE_CASE_KEY.test(key) && !isAllowedKey(path, key)) violations.push(`${joinPath(path)}.${key}`.replace(/^\./, ""));
|
|
1020
|
-
const childPath = [...path, key];
|
|
1021
|
-
if (!shouldSkipDescend(childPath)) collectViolations(child, childPath, violations);
|
|
1022
|
-
}
|
|
1023
|
-
function findPersistedKeyPolicyViolations(value) {
|
|
1024
|
-
const violations = [];
|
|
1025
|
-
collectViolations(value, [], violations);
|
|
1026
|
-
return violations;
|
|
1027
|
-
}
|
|
1028
|
-
function assertPersistedKeyPolicy(value) {
|
|
1029
|
-
const violations = findPersistedKeyPolicyViolations(value);
|
|
1030
|
-
if (violations.length === 0) return;
|
|
1031
|
-
throw new Error(`Persisted key policy violation (expected snake_case keys): ${violations.join(", ")}`);
|
|
1032
|
-
}
|
|
1033
|
-
//#endregion
|
|
1034
|
-
//#region src/session/event-log.ts
|
|
1035
|
-
const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 64 * 1024 * 1024;
|
|
1036
|
-
function sessionBaseDir$1() {
|
|
1037
|
-
return path.join(os.homedir(), ".acpx", "sessions");
|
|
1038
|
-
}
|
|
1039
|
-
function safeSessionId(sessionId) {
|
|
1040
|
-
return encodeURIComponent(sessionId);
|
|
1041
|
-
}
|
|
1042
|
-
function sessionEventActivePath(sessionId) {
|
|
1043
|
-
return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.ndjson`);
|
|
1044
|
-
}
|
|
1045
|
-
function sessionEventSegmentPath(sessionId, segment) {
|
|
1046
|
-
return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.${segment}.ndjson`);
|
|
1047
|
-
}
|
|
1048
|
-
function sessionEventLockPath(sessionId) {
|
|
1049
|
-
return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.lock`);
|
|
1050
|
-
}
|
|
1051
|
-
function defaultSessionEventLog(sessionId) {
|
|
1052
|
-
return {
|
|
1053
|
-
active_path: sessionEventActivePath(sessionId),
|
|
1054
|
-
segment_count: 5,
|
|
1055
|
-
max_segment_bytes: DEFAULT_EVENT_SEGMENT_MAX_BYTES,
|
|
1056
|
-
max_segments: 5,
|
|
1057
|
-
last_write_at: void 0,
|
|
1058
|
-
last_write_error: null
|
|
995
|
+
acpx: canonical.acpx,
|
|
996
|
+
imported_from: canonical.importedFrom ? {
|
|
997
|
+
record_id: canonical.importedFrom.recordId,
|
|
998
|
+
cwd_original: canonical.importedFrom.cwdOriginal,
|
|
999
|
+
exported_by: canonical.importedFrom.exportedBy,
|
|
1000
|
+
exported_at: canonical.importedFrom.exportedAt
|
|
1001
|
+
} : void 0
|
|
1059
1002
|
};
|
|
1060
1003
|
}
|
|
1061
1004
|
//#endregion
|
|
@@ -1070,6 +1013,43 @@ function hasOwn$1(source, key) {
|
|
|
1070
1013
|
function isStringArray(value) {
|
|
1071
1014
|
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
1072
1015
|
}
|
|
1016
|
+
function hasModelConfigOption(options) {
|
|
1017
|
+
if (!Array.isArray(options)) return false;
|
|
1018
|
+
return options.some((entry) => {
|
|
1019
|
+
const option = asRecord$4(entry);
|
|
1020
|
+
return option?.category === "model" || option?.id === "model";
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
function parseConfigOptions(raw) {
|
|
1024
|
+
if (!Array.isArray(raw) || !raw.every((entry) => asRecord$4(entry) !== void 0)) return;
|
|
1025
|
+
return raw;
|
|
1026
|
+
}
|
|
1027
|
+
function parseAvailableCommand(raw) {
|
|
1028
|
+
if (typeof raw === "string") {
|
|
1029
|
+
const name = raw.trim();
|
|
1030
|
+
return name ? { name } : void 0;
|
|
1031
|
+
}
|
|
1032
|
+
const record = asRecord$4(raw);
|
|
1033
|
+
if (!record) return;
|
|
1034
|
+
const name = parseNonEmptyString(record.name);
|
|
1035
|
+
if (!name) return;
|
|
1036
|
+
const description = parseNonEmptyString(record.description);
|
|
1037
|
+
return {
|
|
1038
|
+
name,
|
|
1039
|
+
...description ? { description } : {},
|
|
1040
|
+
...typeof record.has_input === "boolean" ? { has_input: record.has_input } : {}
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
function parseAvailableCommands(raw) {
|
|
1044
|
+
if (!Array.isArray(raw)) return;
|
|
1045
|
+
const commands = raw.map((entry) => parseAvailableCommand(entry)).filter((entry) => entry !== void 0);
|
|
1046
|
+
return commands.length > 0 ? commands : void 0;
|
|
1047
|
+
}
|
|
1048
|
+
function parseNonEmptyString(value) {
|
|
1049
|
+
if (typeof value !== "string") return;
|
|
1050
|
+
const trimmed = value.trim();
|
|
1051
|
+
return trimmed ? trimmed : void 0;
|
|
1052
|
+
}
|
|
1073
1053
|
function parseTokenUsage(raw) {
|
|
1074
1054
|
if (raw === void 0 || raw === null) return;
|
|
1075
1055
|
const record = asRecord$4(raw);
|
|
@@ -1079,7 +1059,9 @@ function parseTokenUsage(raw) {
|
|
|
1079
1059
|
"input_tokens",
|
|
1080
1060
|
"output_tokens",
|
|
1081
1061
|
"cache_creation_input_tokens",
|
|
1082
|
-
"cache_read_input_tokens"
|
|
1062
|
+
"cache_read_input_tokens",
|
|
1063
|
+
"thought_tokens",
|
|
1064
|
+
"total_tokens"
|
|
1083
1065
|
]) {
|
|
1084
1066
|
const value = record[field];
|
|
1085
1067
|
if (value === void 0) continue;
|
|
@@ -1091,6 +1073,32 @@ function parseTokenUsage(raw) {
|
|
|
1091
1073
|
function isNonNegativeFiniteNumber(value) {
|
|
1092
1074
|
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1093
1075
|
}
|
|
1076
|
+
function parseUsageCost(raw) {
|
|
1077
|
+
if (raw === void 0 || raw === null) return;
|
|
1078
|
+
const record = asRecord$4(raw);
|
|
1079
|
+
if (!record) return null;
|
|
1080
|
+
return parseUsageCostRecord(record);
|
|
1081
|
+
}
|
|
1082
|
+
function parseUsageCostRecord(record) {
|
|
1083
|
+
const amount = parseCostAmount(record.amount);
|
|
1084
|
+
const currency = parseCostCurrency(record.currency);
|
|
1085
|
+
if (amount === null || currency === null) return null;
|
|
1086
|
+
const cost = {
|
|
1087
|
+
...amount !== void 0 ? { amount } : {},
|
|
1088
|
+
...currency !== void 0 ? { currency } : {}
|
|
1089
|
+
};
|
|
1090
|
+
return Object.keys(cost).length > 0 ? cost : void 0;
|
|
1091
|
+
}
|
|
1092
|
+
function parseCostAmount(value) {
|
|
1093
|
+
if (value === void 0) return;
|
|
1094
|
+
return isNonNegativeFiniteNumber(value) ? value : null;
|
|
1095
|
+
}
|
|
1096
|
+
function parseCostCurrency(value) {
|
|
1097
|
+
if (value === void 0) return;
|
|
1098
|
+
if (typeof value !== "string") return null;
|
|
1099
|
+
const currency = value.trim();
|
|
1100
|
+
return currency.length > 0 ? currency : void 0;
|
|
1101
|
+
}
|
|
1094
1102
|
function parseRequestTokenUsage(raw) {
|
|
1095
1103
|
if (raw === void 0 || raw === null) return;
|
|
1096
1104
|
const record = asRecord$4(raw);
|
|
@@ -1190,13 +1198,15 @@ function parseConversationRecord(record) {
|
|
|
1190
1198
|
const title = parseConversationTitle(record.title);
|
|
1191
1199
|
if (title === INVALID_VALUE) return;
|
|
1192
1200
|
const cumulativeTokenUsage = parseTokenUsage(record.cumulative_token_usage);
|
|
1201
|
+
const cumulativeCost = parseUsageCost(record.cumulative_cost);
|
|
1193
1202
|
const requestTokenUsage = parseRequestTokenUsage(record.request_token_usage);
|
|
1194
|
-
if (cumulativeTokenUsage === null || requestTokenUsage === null) return;
|
|
1203
|
+
if (cumulativeTokenUsage === null || cumulativeCost === null || requestTokenUsage === null) return;
|
|
1195
1204
|
return {
|
|
1196
1205
|
title,
|
|
1197
1206
|
messages: record.messages,
|
|
1198
1207
|
updated_at: record.updated_at,
|
|
1199
1208
|
cumulative_token_usage: cumulativeTokenUsage ?? {},
|
|
1209
|
+
cumulative_cost: cumulativeCost,
|
|
1200
1210
|
request_token_usage: requestTokenUsage ?? {}
|
|
1201
1211
|
};
|
|
1202
1212
|
}
|
|
@@ -1216,13 +1226,20 @@ function parseAcpxState(raw) {
|
|
|
1216
1226
|
assignStringState(state, "current_mode_id", record.current_mode_id);
|
|
1217
1227
|
assignStringState(state, "desired_mode_id", record.desired_mode_id);
|
|
1218
1228
|
assignDesiredConfigOptions(state, record.desired_config_options);
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
if (
|
|
1222
|
-
if (Array.isArray(record.config_options)) state.config_options = record.config_options;
|
|
1229
|
+
assignParsedModelState(state, record);
|
|
1230
|
+
const availableCommands = parseAvailableCommands(record.available_commands);
|
|
1231
|
+
if (availableCommands) state.available_commands = availableCommands;
|
|
1223
1232
|
assignParsedSessionOptions(state, record.session_options);
|
|
1224
1233
|
return state;
|
|
1225
1234
|
}
|
|
1235
|
+
function assignParsedModelState(state, record) {
|
|
1236
|
+
assignStringState(state, "current_model_id", record.current_model_id);
|
|
1237
|
+
if (isStringArray(record.available_models)) state.available_models = [...record.available_models];
|
|
1238
|
+
if (record.model_control === "config_option" || record.model_control === "legacy_set_model") state.model_control = record.model_control;
|
|
1239
|
+
const configOptions = parseConfigOptions(record.config_options);
|
|
1240
|
+
if (configOptions) state.config_options = configOptions;
|
|
1241
|
+
if (state.model_control === void 0 && state.available_models !== void 0) state.model_control = hasModelConfigOption(state.config_options) ? "config_option" : "legacy_set_model";
|
|
1242
|
+
}
|
|
1226
1243
|
function assignBooleanTrue(state, key, value) {
|
|
1227
1244
|
if (value === true) state[key] = true;
|
|
1228
1245
|
}
|
|
@@ -1283,6 +1300,27 @@ function hasValidEventLogCore(record) {
|
|
|
1283
1300
|
function isPositiveInteger(value) {
|
|
1284
1301
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
1285
1302
|
}
|
|
1303
|
+
function parseImportedFrom(raw) {
|
|
1304
|
+
if (raw == null) return;
|
|
1305
|
+
const record = asRecord$4(raw);
|
|
1306
|
+
if (!record || typeof record.record_id !== "string" || typeof record.cwd_original !== "string" || typeof record.exported_by !== "string" || typeof record.exported_at !== "string") return null;
|
|
1307
|
+
return {
|
|
1308
|
+
recordId: record.record_id,
|
|
1309
|
+
cwdOriginal: record.cwd_original,
|
|
1310
|
+
exportedBy: record.exported_by,
|
|
1311
|
+
exportedAt: record.exported_at
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
function parseSessionRecordMetadata(record) {
|
|
1315
|
+
const lastRequestId = normalizeOptionalString(record.last_request_id);
|
|
1316
|
+
if (lastRequestId === null) return null;
|
|
1317
|
+
const importedFrom = parseImportedFrom(record.imported_from);
|
|
1318
|
+
if (importedFrom === null) return null;
|
|
1319
|
+
return {
|
|
1320
|
+
lastRequestId,
|
|
1321
|
+
importedFrom
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1286
1324
|
function normalizeOptionalName(value) {
|
|
1287
1325
|
if (value == null) return;
|
|
1288
1326
|
if (typeof value !== "string") return null;
|
|
@@ -1334,8 +1372,8 @@ function parseSessionRecord(raw) {
|
|
|
1334
1372
|
const conversation = parseConversationRecord(record);
|
|
1335
1373
|
if (!conversation) return null;
|
|
1336
1374
|
const eventLog = parseEventLog(record.event_log, record.acpx_record_id);
|
|
1337
|
-
const
|
|
1338
|
-
if (
|
|
1375
|
+
const metadata = parseSessionRecordMetadata(record);
|
|
1376
|
+
if (!metadata) return null;
|
|
1339
1377
|
return {
|
|
1340
1378
|
schema: SESSION_RECORD_SCHEMA,
|
|
1341
1379
|
acpxRecordId: record.acpx_record_id,
|
|
@@ -1347,7 +1385,7 @@ function parseSessionRecord(raw) {
|
|
|
1347
1385
|
createdAt: record.created_at,
|
|
1348
1386
|
lastUsedAt: record.last_used_at,
|
|
1349
1387
|
lastSeq: record.last_seq,
|
|
1350
|
-
lastRequestId,
|
|
1388
|
+
lastRequestId: metadata.lastRequestId,
|
|
1351
1389
|
eventLog,
|
|
1352
1390
|
closed: optionals.closed,
|
|
1353
1391
|
closedAt: optionals.closedAt,
|
|
@@ -1364,38 +1402,177 @@ function parseSessionRecord(raw) {
|
|
|
1364
1402
|
messages: conversation.messages,
|
|
1365
1403
|
updated_at: conversation.updated_at,
|
|
1366
1404
|
cumulative_token_usage: conversation.cumulative_token_usage,
|
|
1405
|
+
cumulative_cost: conversation.cumulative_cost,
|
|
1367
1406
|
request_token_usage: conversation.request_token_usage,
|
|
1368
|
-
acpx: parseAcpxState(record.acpx)
|
|
1407
|
+
acpx: parseAcpxState(record.acpx),
|
|
1408
|
+
importedFrom: metadata.importedFrom
|
|
1369
1409
|
};
|
|
1370
1410
|
}
|
|
1371
|
-
function hasValidSessionRecordCore(record) {
|
|
1372
|
-
return hasStringFields(record, [
|
|
1373
|
-
"acpx_record_id",
|
|
1374
|
-
"acp_session_id",
|
|
1375
|
-
"agent_command",
|
|
1376
|
-
"cwd",
|
|
1377
|
-
"created_at",
|
|
1378
|
-
"last_used_at"
|
|
1379
|
-
]) && typeof record.last_seq === "number" && Number.isInteger(record.last_seq) && record.last_seq >= 0;
|
|
1411
|
+
function hasValidSessionRecordCore(record) {
|
|
1412
|
+
return hasStringFields(record, [
|
|
1413
|
+
"acpx_record_id",
|
|
1414
|
+
"acp_session_id",
|
|
1415
|
+
"agent_command",
|
|
1416
|
+
"cwd",
|
|
1417
|
+
"created_at",
|
|
1418
|
+
"last_used_at"
|
|
1419
|
+
]) && typeof record.last_seq === "number" && Number.isInteger(record.last_seq) && record.last_seq >= 0;
|
|
1420
|
+
}
|
|
1421
|
+
function validSessionOptionals(options) {
|
|
1422
|
+
if (hasNullOptionalSessionFields(options) || hasInvalidExitStatus(options)) return null;
|
|
1423
|
+
return options;
|
|
1424
|
+
}
|
|
1425
|
+
function hasNullOptionalSessionFields(options) {
|
|
1426
|
+
return [
|
|
1427
|
+
options.name,
|
|
1428
|
+
options.pid,
|
|
1429
|
+
options.closed,
|
|
1430
|
+
options.closedAt,
|
|
1431
|
+
options.agentStartedAt,
|
|
1432
|
+
options.lastPromptAt,
|
|
1433
|
+
options.lastAgentExitAt,
|
|
1434
|
+
options.lastAgentDisconnectReason
|
|
1435
|
+
].some((value) => value === null);
|
|
1436
|
+
}
|
|
1437
|
+
function hasInvalidExitStatus(options) {
|
|
1438
|
+
return typeof options.lastAgentExitCode === "symbol" || typeof options.lastAgentExitSignal === "symbol";
|
|
1439
|
+
}
|
|
1440
|
+
//#endregion
|
|
1441
|
+
//#region src/perf-metrics.ts
|
|
1442
|
+
const counters = /* @__PURE__ */ new Map();
|
|
1443
|
+
const gauges = /* @__PURE__ */ new Map();
|
|
1444
|
+
const timings = /* @__PURE__ */ new Map();
|
|
1445
|
+
function hrNow() {
|
|
1446
|
+
return process.hrtime.bigint();
|
|
1447
|
+
}
|
|
1448
|
+
function durationMs(start) {
|
|
1449
|
+
return Number(process.hrtime.bigint() - start) / 1e6;
|
|
1450
|
+
}
|
|
1451
|
+
function roundMetric(value) {
|
|
1452
|
+
return Number(value.toFixed(3));
|
|
1453
|
+
}
|
|
1454
|
+
function incrementPerfCounter(name, delta = 1) {
|
|
1455
|
+
counters.set(name, (counters.get(name) ?? 0) + delta);
|
|
1456
|
+
}
|
|
1457
|
+
function setPerfGauge(name, value) {
|
|
1458
|
+
gauges.set(name, value);
|
|
1459
|
+
}
|
|
1460
|
+
function recordPerfDuration(name, durationMsValue) {
|
|
1461
|
+
const next = timings.get(name) ?? {
|
|
1462
|
+
count: 0,
|
|
1463
|
+
totalMs: 0,
|
|
1464
|
+
maxMs: 0
|
|
1465
|
+
};
|
|
1466
|
+
next.count += 1;
|
|
1467
|
+
next.totalMs += durationMsValue;
|
|
1468
|
+
next.maxMs = Math.max(next.maxMs, durationMsValue);
|
|
1469
|
+
timings.set(name, next);
|
|
1470
|
+
}
|
|
1471
|
+
async function measurePerf(name, run) {
|
|
1472
|
+
const startedAt = hrNow();
|
|
1473
|
+
try {
|
|
1474
|
+
return await run();
|
|
1475
|
+
} finally {
|
|
1476
|
+
recordPerfDuration(name, durationMs(startedAt));
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
function startPerfTimer(name) {
|
|
1480
|
+
const startedAt = hrNow();
|
|
1481
|
+
return () => {
|
|
1482
|
+
const elapsedMs = durationMs(startedAt);
|
|
1483
|
+
recordPerfDuration(name, elapsedMs);
|
|
1484
|
+
return elapsedMs;
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
function getPerfMetricsSnapshot() {
|
|
1488
|
+
return {
|
|
1489
|
+
counters: Object.fromEntries(counters.entries()),
|
|
1490
|
+
gauges: Object.fromEntries(gauges.entries()),
|
|
1491
|
+
timings: Object.fromEntries([...timings.entries()].map(([name, bucket]) => [name, {
|
|
1492
|
+
count: bucket.count,
|
|
1493
|
+
totalMs: roundMetric(bucket.totalMs),
|
|
1494
|
+
maxMs: roundMetric(bucket.maxMs)
|
|
1495
|
+
}]))
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
function resetPerfMetrics() {
|
|
1499
|
+
counters.clear();
|
|
1500
|
+
gauges.clear();
|
|
1501
|
+
timings.clear();
|
|
1502
|
+
}
|
|
1503
|
+
function formatPerfMetric(name, durationMsValue) {
|
|
1504
|
+
return `${name}=${roundMetric(durationMsValue)}ms`;
|
|
1505
|
+
}
|
|
1506
|
+
//#endregion
|
|
1507
|
+
//#region src/persisted-key-policy.ts
|
|
1508
|
+
const SNAKE_CASE_KEY = /^[a-z][a-z0-9_]*$/;
|
|
1509
|
+
const ZED_TAG_KEYS = new Set([
|
|
1510
|
+
"User",
|
|
1511
|
+
"Agent",
|
|
1512
|
+
"Resume",
|
|
1513
|
+
"Text",
|
|
1514
|
+
"Mention",
|
|
1515
|
+
"Image",
|
|
1516
|
+
"Audio",
|
|
1517
|
+
"Thinking",
|
|
1518
|
+
"RedactedThinking",
|
|
1519
|
+
"ToolUse"
|
|
1520
|
+
]);
|
|
1521
|
+
const MAP_OBJECT_PATHS = new Set(["request_token_usage", "messages.Agent.tool_results"]);
|
|
1522
|
+
const OPAQUE_VALUE_PATHS = new Set([
|
|
1523
|
+
"agent_capabilities",
|
|
1524
|
+
"messages.Agent.content.ToolUse.input",
|
|
1525
|
+
"acpx.desired_config_options",
|
|
1526
|
+
"acpx.config_options"
|
|
1527
|
+
]);
|
|
1528
|
+
function isRecord(value) {
|
|
1529
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1530
|
+
}
|
|
1531
|
+
function joinPath(path) {
|
|
1532
|
+
return path.join(".");
|
|
1533
|
+
}
|
|
1534
|
+
function isAllowedKey(path, key) {
|
|
1535
|
+
if (ZED_TAG_KEYS.has(key)) return true;
|
|
1536
|
+
return false;
|
|
1537
|
+
}
|
|
1538
|
+
function shouldSkipKeyRule(path) {
|
|
1539
|
+
return MAP_OBJECT_PATHS.has(joinPath(path));
|
|
1540
|
+
}
|
|
1541
|
+
function shouldSkipDescend(path) {
|
|
1542
|
+
return OPAQUE_VALUE_PATHS.has(joinPath(path)) || isToolResultOutputPath(path);
|
|
1543
|
+
}
|
|
1544
|
+
function isToolResultOutputTail(path, toolResultsIndex) {
|
|
1545
|
+
return toolResultsIndex !== -1 && toolResultsIndex + 2 === path.length - 1;
|
|
1380
1546
|
}
|
|
1381
|
-
function
|
|
1382
|
-
if (
|
|
1383
|
-
|
|
1547
|
+
function isToolResultOutputPath(path) {
|
|
1548
|
+
if (path.length < 5 || path[path.length - 1] !== "output") return false;
|
|
1549
|
+
const toolResultsIndex = path.lastIndexOf("tool_results");
|
|
1550
|
+
if (!isToolResultOutputTail(path, toolResultsIndex)) return false;
|
|
1551
|
+
return path.slice(0, toolResultsIndex + 1).join(".") === "messages.Agent.tool_results";
|
|
1384
1552
|
}
|
|
1385
|
-
function
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
options.lastAgentExitAt,
|
|
1394
|
-
options.lastAgentDisconnectReason
|
|
1395
|
-
].some((value) => value === null);
|
|
1553
|
+
function collectViolations(value, path, violations) {
|
|
1554
|
+
if (Array.isArray(value)) {
|
|
1555
|
+
for (const entry of value) collectViolations(entry, path, violations);
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
if (!isRecord(value)) return;
|
|
1559
|
+
const skipKeyRule = shouldSkipKeyRule(path);
|
|
1560
|
+
for (const [key, child] of Object.entries(value)) collectKeyViolation(child, key, path, skipKeyRule, violations);
|
|
1396
1561
|
}
|
|
1397
|
-
function
|
|
1398
|
-
|
|
1562
|
+
function collectKeyViolation(child, key, path, skipKeyRule, violations) {
|
|
1563
|
+
if (!skipKeyRule && !SNAKE_CASE_KEY.test(key) && !isAllowedKey(path, key)) violations.push(`${joinPath(path)}.${key}`.replace(/^\./, ""));
|
|
1564
|
+
const childPath = [...path, key];
|
|
1565
|
+
if (!shouldSkipDescend(childPath)) collectViolations(child, childPath, violations);
|
|
1566
|
+
}
|
|
1567
|
+
function findPersistedKeyPolicyViolations(value) {
|
|
1568
|
+
const violations = [];
|
|
1569
|
+
collectViolations(value, [], violations);
|
|
1570
|
+
return violations;
|
|
1571
|
+
}
|
|
1572
|
+
function assertPersistedKeyPolicy(value) {
|
|
1573
|
+
const violations = findPersistedKeyPolicyViolations(value);
|
|
1574
|
+
if (violations.length === 0) return;
|
|
1575
|
+
throw new Error(`Persisted key policy violation (expected snake_case keys): ${violations.join(", ")}`);
|
|
1399
1576
|
}
|
|
1400
1577
|
//#endregion
|
|
1401
1578
|
//#region src/session/persistence/index.ts
|
|
@@ -2204,6 +2381,49 @@ function buildTerminalShellSpawnCommand(command, platform = process.platform) {
|
|
|
2204
2381
|
};
|
|
2205
2382
|
}
|
|
2206
2383
|
//#endregion
|
|
2384
|
+
//#region src/version.ts
|
|
2385
|
+
const UNKNOWN_VERSION = "0.0.0-unknown";
|
|
2386
|
+
const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
2387
|
+
let cachedVersion = null;
|
|
2388
|
+
function parseVersion(value) {
|
|
2389
|
+
if (typeof value !== "string") return null;
|
|
2390
|
+
const trimmed = value.trim();
|
|
2391
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
2392
|
+
}
|
|
2393
|
+
function readPackageVersion(packageJsonPath) {
|
|
2394
|
+
try {
|
|
2395
|
+
return parseVersion(JSON.parse(readFileSync(packageJsonPath, "utf8")).version);
|
|
2396
|
+
} catch {
|
|
2397
|
+
return null;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
function resolveVersionFromAncestors(startDir) {
|
|
2401
|
+
let current = startDir;
|
|
2402
|
+
while (true) {
|
|
2403
|
+
const packageVersion = readPackageVersion(path.join(current, "package.json"));
|
|
2404
|
+
if (packageVersion) return packageVersion;
|
|
2405
|
+
const parent = path.dirname(current);
|
|
2406
|
+
if (parent === current) return null;
|
|
2407
|
+
current = parent;
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
function resolveAcpxVersion(params) {
|
|
2411
|
+
const envVersion = resolvePackageEnvVersion(params?.env ?? process.env);
|
|
2412
|
+
if (envVersion) return envVersion;
|
|
2413
|
+
if (params?.packageJsonPath) return readPackageVersion(params.packageJsonPath) ?? UNKNOWN_VERSION;
|
|
2414
|
+
return resolveVersionFromAncestors(MODULE_DIR) ?? UNKNOWN_VERSION;
|
|
2415
|
+
}
|
|
2416
|
+
function resolvePackageEnvVersion(env) {
|
|
2417
|
+
const envPackageName = parseVersion(env.npm_package_name);
|
|
2418
|
+
const envVersion = parseVersion(env.npm_package_version);
|
|
2419
|
+
return envPackageName === "acpx" ? envVersion : null;
|
|
2420
|
+
}
|
|
2421
|
+
function getAcpxVersion() {
|
|
2422
|
+
if (cachedVersion) return cachedVersion;
|
|
2423
|
+
cachedVersion = resolveAcpxVersion();
|
|
2424
|
+
return cachedVersion;
|
|
2425
|
+
}
|
|
2426
|
+
//#endregion
|
|
2207
2427
|
//#region src/acp/client-process.ts
|
|
2208
2428
|
const execFileAsync = promisify(execFile);
|
|
2209
2429
|
function isoNow$1() {
|
|
@@ -2379,6 +2599,7 @@ const GEMINI_ACP_FLAG_VERSION = [
|
|
|
2379
2599
|
0
|
|
2380
2600
|
];
|
|
2381
2601
|
const COPILOT_HELP_TIMEOUT_MS = 2e3;
|
|
2602
|
+
const CLAUDE_CODE_DEFAULT_SETTING_SOURCES = ["project", "local"];
|
|
2382
2603
|
const QODER_BENIGN_STDOUT_LINES = new Set(["Received interrupt signal. Cleaning up resources...", "Cleanup completed. Exiting..."]);
|
|
2383
2604
|
function resolveAgentCloseAfterStdinEndMs(agentCommand) {
|
|
2384
2605
|
const { command } = splitCommandLine(agentCommand);
|
|
@@ -2401,6 +2622,9 @@ function isCopilotAcpCommand(command, args) {
|
|
|
2401
2622
|
function isQoderAcpCommand(command, args) {
|
|
2402
2623
|
return basenameToken(command) === "qodercli" && args.includes("--acp");
|
|
2403
2624
|
}
|
|
2625
|
+
function isDevinAcpCommand(command, args) {
|
|
2626
|
+
return basenameToken(command) === "devin" && (args.includes("acp") || args.includes("--acp") || args.includes("--experimental-acp"));
|
|
2627
|
+
}
|
|
2404
2628
|
function hasCommandFlag(args, flagName) {
|
|
2405
2629
|
return args.some((arg) => arg === flagName || arg.startsWith(`${flagName}=`));
|
|
2406
2630
|
}
|
|
@@ -2541,16 +2765,20 @@ async function ensureCopilotAcpSupport(command) {
|
|
|
2541
2765
|
const helpOutput = await readCommandOutput(command, ["--help"], COPILOT_HELP_TIMEOUT_MS);
|
|
2542
2766
|
if (typeof helpOutput === "string" && !helpOutput.includes("--acp")) throw new CopilotAcpUnsupportedError(await buildCopilotAcpUnsupportedMessage(command), { retryable: false });
|
|
2543
2767
|
}
|
|
2544
|
-
function buildClaudeCodeOptionsMeta(options) {
|
|
2545
|
-
if (!options) return;
|
|
2768
|
+
function buildClaudeCodeOptionsMeta(options, isolateUserSettings = false) {
|
|
2546
2769
|
const claudeCodeOptions = {};
|
|
2547
|
-
|
|
2770
|
+
if (isolateUserSettings) claudeCodeOptions.settingSources = resolveClaudeCodeSettingSources();
|
|
2771
|
+
if (options) assignClaudeCodeOptions(claudeCodeOptions, options);
|
|
2548
2772
|
const meta = {};
|
|
2549
2773
|
if (Object.keys(claudeCodeOptions).length > 0) meta.claudeCode = { options: claudeCodeOptions };
|
|
2550
|
-
assignClaudeCodeSystemPrompt(meta, options
|
|
2774
|
+
assignClaudeCodeSystemPrompt(meta, options?.systemPrompt);
|
|
2551
2775
|
if (Object.keys(meta).length === 0) return;
|
|
2552
2776
|
return meta;
|
|
2553
2777
|
}
|
|
2778
|
+
function resolveClaudeCodeSettingSources(env = process.env) {
|
|
2779
|
+
if (env.ACPX_CLAUDE_INCLUDE_USER_SETTINGS?.trim() === "1") return ["user", ...CLAUDE_CODE_DEFAULT_SETTING_SOURCES];
|
|
2780
|
+
return [...CLAUDE_CODE_DEFAULT_SETTING_SOURCES];
|
|
2781
|
+
}
|
|
2554
2782
|
function assignClaudeCodeOptions(target, options) {
|
|
2555
2783
|
if (typeof options.model === "string" && options.model.trim().length > 0) target.model = options.model;
|
|
2556
2784
|
if (Array.isArray(options.allowedTools)) target.allowedTools = [...options.allowedTools];
|
|
@@ -2642,74 +2870,96 @@ function buildAgentSpawnOptions(cwd, authCredentials) {
|
|
|
2642
2870
|
};
|
|
2643
2871
|
}
|
|
2644
2872
|
//#endregion
|
|
2645
|
-
//#region src/acp/
|
|
2873
|
+
//#region src/acp/model-support.ts
|
|
2874
|
+
var RequestedModelUnsupportedError = class extends Error {
|
|
2875
|
+
constructor(message) {
|
|
2876
|
+
super(message);
|
|
2877
|
+
this.name = "RequestedModelUnsupportedError";
|
|
2878
|
+
}
|
|
2879
|
+
};
|
|
2880
|
+
function supportsLegacyClaudeCodeModelMetadata(agentCommand) {
|
|
2881
|
+
if (!agentCommand) return false;
|
|
2882
|
+
const { command, args } = splitCommandLine(agentCommand);
|
|
2883
|
+
return isClaudeAcpCommand(command, args);
|
|
2884
|
+
}
|
|
2646
2885
|
function asRecord$2(value) {
|
|
2647
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return
|
|
2886
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
2648
2887
|
return value;
|
|
2649
2888
|
}
|
|
2650
|
-
function
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
}
|
|
2657
|
-
function hasResultOrError(value) {
|
|
2658
|
-
const hasResult = Object.hasOwn(value, "result");
|
|
2659
|
-
const hasError = Object.hasOwn(value, "error");
|
|
2660
|
-
if (hasResult && hasError) return false;
|
|
2661
|
-
if (!hasResult && !hasError) return false;
|
|
2662
|
-
if (hasError && !isErrorObject(value.error)) return false;
|
|
2663
|
-
return true;
|
|
2664
|
-
}
|
|
2665
|
-
function hasMethod(value) {
|
|
2666
|
-
return typeof value.method === "string" && value.method.length > 0;
|
|
2667
|
-
}
|
|
2668
|
-
function isJsonRpcRequest(value) {
|
|
2669
|
-
return hasMethod(value) && Object.hasOwn(value, "id") && hasValidId(value.id);
|
|
2670
|
-
}
|
|
2671
|
-
function isJsonRpcNotificationRecord(value) {
|
|
2672
|
-
return hasMethod(value) && !Object.hasOwn(value, "id");
|
|
2673
|
-
}
|
|
2674
|
-
function isJsonRpcResponse(value) {
|
|
2675
|
-
if (hasMethod(value) || !Object.hasOwn(value, "id") || !hasValidId(value.id)) return false;
|
|
2676
|
-
return hasResultOrError(value);
|
|
2677
|
-
}
|
|
2678
|
-
function isAcpJsonRpcMessage(value) {
|
|
2679
|
-
const record = asRecord$2(value);
|
|
2680
|
-
if (!record || record.jsonrpc !== "2.0") return false;
|
|
2681
|
-
return isJsonRpcNotificationRecord(record) || isJsonRpcRequest(record) || isJsonRpcResponse(record);
|
|
2682
|
-
}
|
|
2683
|
-
function isJsonRpcNotification(message) {
|
|
2684
|
-
return Object.hasOwn(message, "method") && typeof message.method === "string" && !Object.hasOwn(message, "id");
|
|
2889
|
+
function parseAvailableModel(value) {
|
|
2890
|
+
const option = asRecord$2(value);
|
|
2891
|
+
if (!option || typeof option.value !== "string" || typeof option.name !== "string") return;
|
|
2892
|
+
return {
|
|
2893
|
+
modelId: option.value,
|
|
2894
|
+
name: option.name
|
|
2895
|
+
};
|
|
2685
2896
|
}
|
|
2686
|
-
function
|
|
2687
|
-
|
|
2897
|
+
function parseAvailableModelGroup(value) {
|
|
2898
|
+
const group = asRecord$2(value);
|
|
2899
|
+
if (!group || typeof group.group !== "string" || typeof group.name !== "string" || !Array.isArray(group.options)) return;
|
|
2900
|
+
const models = group.options.map((option) => parseAvailableModel(option));
|
|
2901
|
+
return models.every((model) => model !== void 0) ? models : void 0;
|
|
2902
|
+
}
|
|
2903
|
+
function parseAvailableModels(value) {
|
|
2904
|
+
if (!Array.isArray(value)) return;
|
|
2905
|
+
const directModels = value.map((option) => parseAvailableModel(option));
|
|
2906
|
+
if (directModels.every((model) => model !== void 0)) return directModels;
|
|
2907
|
+
const groupedModels = value.map((group) => parseAvailableModelGroup(group));
|
|
2908
|
+
return groupedModels.every((models) => models !== void 0) ? groupedModels.flat() : void 0;
|
|
2909
|
+
}
|
|
2910
|
+
function isModelSelectOption(option) {
|
|
2911
|
+
return option.type === "select" && (option.category === "model" || option.id === "model");
|
|
2912
|
+
}
|
|
2913
|
+
function parseModelConfigOption(value) {
|
|
2914
|
+
const option = asRecord$2(value);
|
|
2915
|
+
if (!option || !isModelSelectOption(option) || typeof option.id !== "string" || typeof option.currentValue !== "string") return;
|
|
2916
|
+
const availableModels = parseAvailableModels(option.options);
|
|
2917
|
+
return availableModels ? {
|
|
2918
|
+
configId: option.id,
|
|
2919
|
+
currentModelId: option.currentValue,
|
|
2920
|
+
availableModels
|
|
2921
|
+
} : void 0;
|
|
2688
2922
|
}
|
|
2689
|
-
function
|
|
2690
|
-
if (!
|
|
2691
|
-
const
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2923
|
+
function modelStateFromConfigOptions(configOptions) {
|
|
2924
|
+
if (!Array.isArray(configOptions)) return;
|
|
2925
|
+
for (const value of configOptions) {
|
|
2926
|
+
const models = parseModelConfigOption(value);
|
|
2927
|
+
if (models) return models;
|
|
2928
|
+
}
|
|
2929
|
+
}
|
|
2930
|
+
function modelStateFromLegacyResponse(response) {
|
|
2931
|
+
if (!response || typeof response !== "object") return;
|
|
2932
|
+
const models = response.models;
|
|
2933
|
+
if (!models || typeof models.currentModelId !== "string" || !Array.isArray(models.availableModels)) return;
|
|
2934
|
+
const availableModels = models.availableModels.flatMap((entry) => {
|
|
2935
|
+
if (!entry || typeof entry !== "object") return [];
|
|
2936
|
+
const candidate = entry;
|
|
2937
|
+
return typeof candidate.modelId === "string" && typeof candidate.name === "string" ? [{
|
|
2938
|
+
modelId: candidate.modelId,
|
|
2939
|
+
name: candidate.name
|
|
2940
|
+
}] : [];
|
|
2941
|
+
});
|
|
2697
2942
|
return {
|
|
2698
|
-
|
|
2699
|
-
|
|
2943
|
+
currentModelId: models.currentModelId,
|
|
2944
|
+
availableModels
|
|
2700
2945
|
};
|
|
2701
2946
|
}
|
|
2702
|
-
function
|
|
2703
|
-
|
|
2704
|
-
const record = asRecord$2(message.result);
|
|
2705
|
-
if (!record) return;
|
|
2706
|
-
return typeof record.stopReason === "string" ? record.stopReason : void 0;
|
|
2947
|
+
function modelStateFromSessionResponse(params) {
|
|
2948
|
+
return modelStateFromConfigOptions(params.configOptions) ?? modelStateFromLegacyResponse(params.response);
|
|
2707
2949
|
}
|
|
2708
|
-
function
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2950
|
+
function formatAvailableModelIds(models) {
|
|
2951
|
+
const ids = models?.availableModels.map((model) => model.modelId.trim()).filter((modelId) => modelId.length > 0) ?? [];
|
|
2952
|
+
return ids.length > 0 ? ids.join(", ") : "none advertised";
|
|
2953
|
+
}
|
|
2954
|
+
function assertRequestedModelSupported(params) {
|
|
2955
|
+
if (!params.models) {
|
|
2956
|
+
if (supportsLegacyClaudeCodeModelMetadata(params.agentCommand)) return;
|
|
2957
|
+
throw new RequestedModelUnsupportedError(`Cannot ${params.context === "replay" ? "replay saved model" : "apply --model"} "${params.requestedModel}": the ACP agent did not advertise model support through a session config option or legacy models metadata, and the adapter does not support a startup model flag.`);
|
|
2958
|
+
}
|
|
2959
|
+
if (!new Set(params.models.availableModels.map((model) => model.modelId)).has(params.requestedModel)) {
|
|
2960
|
+
if (supportsLegacyClaudeCodeModelMetadata(params.agentCommand)) return `requested model "${params.requestedModel}" was not in the Claude ACP advertised model list (${formatAvailableModelIds(params.models)}); forwarding it to Claude Code so the adapter can accept or reject it.`;
|
|
2961
|
+
throw new RequestedModelUnsupportedError(`Cannot ${params.context === "replay" ? "replay saved model" : "apply --model"} "${params.requestedModel}": the ACP agent did not advertise that model. Available models: ${formatAvailableModelIds(params.models)}.`);
|
|
2962
|
+
}
|
|
2713
2963
|
}
|
|
2714
2964
|
//#endregion
|
|
2715
2965
|
//#region src/acp/session-control-errors.ts
|
|
@@ -3328,11 +3578,54 @@ const DRAIN_POLL_INTERVAL_MS = 20;
|
|
|
3328
3578
|
const AGENT_CLOSE_TERM_GRACE_MS = 1500;
|
|
3329
3579
|
const AGENT_CLOSE_KILL_GRACE_MS = 1e3;
|
|
3330
3580
|
const STARTUP_STDERR_MAX_CHARS = 8192;
|
|
3581
|
+
const DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META = Object.freeze({ "cognition.ai/requestDiagnostics": true });
|
|
3582
|
+
const DEVIN_COMPATIBILITY_CLIENT_NAME = "windsurf";
|
|
3583
|
+
const DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION = "1.110.1";
|
|
3584
|
+
function resolveClientInfo(devinAcp) {
|
|
3585
|
+
if (!devinAcp) return {
|
|
3586
|
+
name: "acpx",
|
|
3587
|
+
version: getAcpxVersion()
|
|
3588
|
+
};
|
|
3589
|
+
return {
|
|
3590
|
+
name: DEVIN_COMPATIBILITY_CLIENT_NAME,
|
|
3591
|
+
version: process.env.ACPX_DEVIN_WINDSURF_VERSION ?? DEFAULT_DEVIN_COMPATIBILITY_CLIENT_VERSION
|
|
3592
|
+
};
|
|
3593
|
+
}
|
|
3594
|
+
function resolveClientCapabilities(params) {
|
|
3595
|
+
const baseCapabilities = {
|
|
3596
|
+
fs: {
|
|
3597
|
+
readTextFile: true,
|
|
3598
|
+
writeTextFile: true
|
|
3599
|
+
},
|
|
3600
|
+
terminal: params.terminal
|
|
3601
|
+
};
|
|
3602
|
+
if (!params.devinAcp) return baseCapabilities;
|
|
3603
|
+
return {
|
|
3604
|
+
...baseCapabilities,
|
|
3605
|
+
_meta: DEVIN_COMPATIBILITY_CLIENT_CAPABILITIES_META
|
|
3606
|
+
};
|
|
3607
|
+
}
|
|
3608
|
+
function isDevinRequestDiagnosticsMethod(method) {
|
|
3609
|
+
return method === "_cognition.ai/request_diagnostics";
|
|
3610
|
+
}
|
|
3611
|
+
function hasResponseField(response, field) {
|
|
3612
|
+
return !!response && typeof response === "object" && field in response;
|
|
3613
|
+
}
|
|
3614
|
+
function normalizeResponseConfigOptions(response) {
|
|
3615
|
+
if (!response || !("configOptions" in response)) return;
|
|
3616
|
+
return response.configOptions ?? [];
|
|
3617
|
+
}
|
|
3331
3618
|
function toReconnectedSessionResult(response) {
|
|
3619
|
+
const configOptions = normalizeResponseConfigOptions(response);
|
|
3332
3620
|
return {
|
|
3333
3621
|
agentSessionId: extractRuntimeSessionId(response?._meta),
|
|
3334
|
-
configOptions
|
|
3335
|
-
models:
|
|
3622
|
+
configOptions,
|
|
3623
|
+
models: modelStateFromSessionResponse({
|
|
3624
|
+
configOptions,
|
|
3625
|
+
response
|
|
3626
|
+
}),
|
|
3627
|
+
configOptionsPresent: hasResponseField(response, "configOptions"),
|
|
3628
|
+
legacyModelMetadataPresent: hasResponseField(response, "models")
|
|
3336
3629
|
};
|
|
3337
3630
|
}
|
|
3338
3631
|
function childProcessIsRunning(agent) {
|
|
@@ -3431,6 +3724,8 @@ var AcpClient = class {
|
|
|
3431
3724
|
lastKnownPid;
|
|
3432
3725
|
promptPermissionFailures = /* @__PURE__ */ new Map();
|
|
3433
3726
|
pendingConnectionRequests = /* @__PURE__ */ new Set();
|
|
3727
|
+
modelConfigIds = /* @__PURE__ */ new Map();
|
|
3728
|
+
legacyModelSessionIds = /* @__PURE__ */ new Set();
|
|
3434
3729
|
constructor(options) {
|
|
3435
3730
|
this.options = {
|
|
3436
3731
|
...options,
|
|
@@ -3542,7 +3837,7 @@ var AcpClient = class {
|
|
|
3542
3837
|
const input = Writable.toWeb(child.stdin);
|
|
3543
3838
|
const output = Readable.toWeb(child.stdout);
|
|
3544
3839
|
const stream = this.createTappedStream(createNdJsonMessageStream(this.options.agentCommand, input, output));
|
|
3545
|
-
const connection = this.createConnection(stream);
|
|
3840
|
+
const connection = this.createConnection(stream, launch);
|
|
3546
3841
|
connection.signal.addEventListener("abort", () => {
|
|
3547
3842
|
this.recordAgentExit("connection_close", child.exitCode ?? null, child.signalCode ?? null);
|
|
3548
3843
|
}, { once: true });
|
|
@@ -3566,6 +3861,7 @@ var AcpClient = class {
|
|
|
3566
3861
|
spawnCommand,
|
|
3567
3862
|
args,
|
|
3568
3863
|
resolvedBuiltInLaunch,
|
|
3864
|
+
devinAcp: isDevinAcpCommand(spawnCommand, args),
|
|
3569
3865
|
geminiAcp: isGeminiAcpCommand(spawnCommand, args),
|
|
3570
3866
|
copilotAcp: isCopilotAcpCommand(spawnCommand, args),
|
|
3571
3867
|
claudeAcp: isClaudeAcpCommand(spawnCommand, args),
|
|
@@ -3602,7 +3898,7 @@ var AcpClient = class {
|
|
|
3602
3898
|
}
|
|
3603
3899
|
return requireAgentStdio(spawnedChild);
|
|
3604
3900
|
}
|
|
3605
|
-
createConnection(stream) {
|
|
3901
|
+
createConnection(stream, launch) {
|
|
3606
3902
|
return new ClientSideConnection(() => ({
|
|
3607
3903
|
sessionUpdate: async (params) => {
|
|
3608
3904
|
await this.handleSessionUpdate(params);
|
|
@@ -3610,6 +3906,10 @@ var AcpClient = class {
|
|
|
3610
3906
|
requestPermission: async (params) => {
|
|
3611
3907
|
return this.handlePermissionRequest(params);
|
|
3612
3908
|
},
|
|
3909
|
+
extMethod: async (method) => {
|
|
3910
|
+
if (launch.devinAcp && isDevinRequestDiagnosticsMethod(method)) return {};
|
|
3911
|
+
throw RequestError.methodNotFound(method);
|
|
3912
|
+
},
|
|
3613
3913
|
readTextFile: async (params) => {
|
|
3614
3914
|
return this.handleReadTextFile(params);
|
|
3615
3915
|
},
|
|
@@ -3630,12 +3930,13 @@ var AcpClient = class {
|
|
|
3630
3930
|
},
|
|
3631
3931
|
releaseTerminal: async (params) => {
|
|
3632
3932
|
return this.handleReleaseTerminal(params);
|
|
3633
|
-
}
|
|
3933
|
+
},
|
|
3934
|
+
extNotification: async () => {}
|
|
3634
3935
|
}), stream);
|
|
3635
3936
|
}
|
|
3636
3937
|
async initializeAgentConnection(params) {
|
|
3637
3938
|
try {
|
|
3638
|
-
const initResult = await Promise.race([this.initializeProtocolConnection(params.connection, params.launch
|
|
3939
|
+
const initResult = await Promise.race([this.initializeProtocolConnection(params.connection, params.launch), params.startupFailure.promise]);
|
|
3639
3940
|
params.startupFailure.dispose();
|
|
3640
3941
|
this.connection = params.connection;
|
|
3641
3942
|
this.agent = params.child;
|
|
@@ -3645,22 +3946,16 @@ var AcpClient = class {
|
|
|
3645
3946
|
await this.handleInitializeFailure(params, error);
|
|
3646
3947
|
}
|
|
3647
3948
|
}
|
|
3648
|
-
async initializeProtocolConnection(connection,
|
|
3949
|
+
async initializeProtocolConnection(connection, launch) {
|
|
3649
3950
|
const initializePromise = connection.initialize({
|
|
3650
3951
|
protocolVersion: PROTOCOL_VERSION,
|
|
3651
|
-
clientCapabilities: {
|
|
3652
|
-
|
|
3653
|
-
readTextFile: true,
|
|
3654
|
-
writeTextFile: true
|
|
3655
|
-
},
|
|
3952
|
+
clientCapabilities: resolveClientCapabilities({
|
|
3953
|
+
devinAcp: launch.devinAcp,
|
|
3656
3954
|
terminal: this.options.terminal !== false
|
|
3657
|
-
},
|
|
3658
|
-
clientInfo:
|
|
3659
|
-
name: "acpx",
|
|
3660
|
-
version: "0.1.0"
|
|
3661
|
-
}
|
|
3955
|
+
}),
|
|
3956
|
+
clientInfo: resolveClientInfo(launch.devinAcp)
|
|
3662
3957
|
});
|
|
3663
|
-
const initialized = geminiAcp ? await withTimeout(initializePromise, resolveGeminiAcpStartupTimeoutMs()) : await initializePromise;
|
|
3958
|
+
const initialized = launch.geminiAcp ? await withTimeout(initializePromise, resolveGeminiAcpStartupTimeoutMs()) : await initializePromise;
|
|
3664
3959
|
await this.authenticateIfRequired(connection, initialized.authMethods ?? []);
|
|
3665
3960
|
return initialized;
|
|
3666
3961
|
}
|
|
@@ -3723,7 +4018,7 @@ var AcpClient = class {
|
|
|
3723
4018
|
const createPromise = this.runConnectionRequest(() => connection.newSession({
|
|
3724
4019
|
cwd: sessionCwd,
|
|
3725
4020
|
mcpServers: this.options.mcpServers ?? [],
|
|
3726
|
-
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions)
|
|
4021
|
+
_meta: buildClaudeCodeOptionsMeta(this.options.sessionOptions, claudeAcp)
|
|
3727
4022
|
}));
|
|
3728
4023
|
result = claudeAcp ? await withTimeout(createPromise, resolveClaudeAcpSessionCreateTimeoutMs()) : await createPromise;
|
|
3729
4024
|
} catch (error) {
|
|
@@ -3734,11 +4029,19 @@ var AcpClient = class {
|
|
|
3734
4029
|
throw error;
|
|
3735
4030
|
}
|
|
3736
4031
|
this.loadedSessionId = result.sessionId;
|
|
4032
|
+
const configOptions = normalizeResponseConfigOptions(result);
|
|
4033
|
+
const models = modelStateFromSessionResponse({
|
|
4034
|
+
configOptions,
|
|
4035
|
+
response: result
|
|
4036
|
+
});
|
|
4037
|
+
this.rememberSessionModels(result.sessionId, models);
|
|
3737
4038
|
return {
|
|
3738
4039
|
sessionId: result.sessionId,
|
|
3739
4040
|
agentSessionId: extractRuntimeSessionId(result._meta),
|
|
3740
|
-
configOptions
|
|
3741
|
-
models
|
|
4041
|
+
configOptions,
|
|
4042
|
+
models,
|
|
4043
|
+
configOptionsPresent: hasResponseField(result, "configOptions"),
|
|
4044
|
+
legacyModelMetadataPresent: hasResponseField(result, "models")
|
|
3742
4045
|
};
|
|
3743
4046
|
}
|
|
3744
4047
|
async loadSession(sessionId, cwd = this.options.cwd) {
|
|
@@ -3761,7 +4064,9 @@ var AcpClient = class {
|
|
|
3761
4064
|
this.restoreSessionUpdateSuppression(previousSuppression);
|
|
3762
4065
|
}
|
|
3763
4066
|
this.loadedSessionId = sessionId;
|
|
3764
|
-
|
|
4067
|
+
const result = toReconnectedSessionResult(response);
|
|
4068
|
+
this.updateRememberedSessionModels(sessionId, result);
|
|
4069
|
+
return result;
|
|
3765
4070
|
}
|
|
3766
4071
|
async resumeSession(sessionId, cwd = this.options.cwd) {
|
|
3767
4072
|
const connection = this.getConnection();
|
|
@@ -3772,7 +4077,9 @@ var AcpClient = class {
|
|
|
3772
4077
|
mcpServers: this.options.mcpServers ?? []
|
|
3773
4078
|
}));
|
|
3774
4079
|
this.loadedSessionId = sessionId;
|
|
3775
|
-
|
|
4080
|
+
const result = toReconnectedSessionResult(response);
|
|
4081
|
+
this.updateRememberedSessionModels(sessionId, result);
|
|
4082
|
+
return result;
|
|
3776
4083
|
}
|
|
3777
4084
|
applySessionUpdateSuppression(enabled) {
|
|
3778
4085
|
const previous = {
|
|
@@ -3855,21 +4162,73 @@ var AcpClient = class {
|
|
|
3855
4162
|
throw maybeWrapSessionControlError("session/set_config_option", error, `for "${configId}"="${value}"`);
|
|
3856
4163
|
}
|
|
3857
4164
|
}
|
|
3858
|
-
async setSessionModel(sessionId, modelId) {
|
|
4165
|
+
async setSessionModel(sessionId, modelId, controlOverride) {
|
|
4166
|
+
const control = this.resolveModelControl(sessionId, controlOverride);
|
|
4167
|
+
if (!control) throw new RequestedModelUnsupportedError(`Cannot set model "${modelId}": the ACP session did not advertise a model config option or legacy session/set_model support.`);
|
|
4168
|
+
return control.kind === "config_option" ? await this.setSessionModelThroughConfig(sessionId, modelId, control.configId) : await this.setSessionModelThroughLegacyMethod(sessionId, modelId);
|
|
4169
|
+
}
|
|
4170
|
+
async setSessionModelThroughConfig(sessionId, modelId, configId) {
|
|
4171
|
+
const connection = this.getConnection();
|
|
4172
|
+
try {
|
|
4173
|
+
const response = await this.runConnectionRequest(() => connection.setSessionConfigOption({
|
|
4174
|
+
sessionId,
|
|
4175
|
+
configId,
|
|
4176
|
+
value: modelId
|
|
4177
|
+
}));
|
|
4178
|
+
this.rememberSessionModels(sessionId, modelStateFromConfigOptions(response.configOptions));
|
|
4179
|
+
return response;
|
|
4180
|
+
} catch (error) {
|
|
4181
|
+
return this.throwSessionModelError("session/set_config_option", modelId, error);
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
async setSessionModelThroughLegacyMethod(sessionId, modelId) {
|
|
3859
4185
|
const connection = this.getConnection();
|
|
3860
4186
|
try {
|
|
3861
|
-
await this.runConnectionRequest(() => connection.
|
|
4187
|
+
await this.runConnectionRequest(() => connection.extMethod("session/set_model", {
|
|
3862
4188
|
sessionId,
|
|
3863
4189
|
modelId
|
|
3864
4190
|
}));
|
|
4191
|
+
return;
|
|
3865
4192
|
} catch (error) {
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
4193
|
+
return this.throwSessionModelError("session/set_model", modelId, error);
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
throwSessionModelError(method, modelId, error) {
|
|
4197
|
+
const wrapped = maybeWrapSessionControlError(method, error, `for model "${modelId}"`);
|
|
4198
|
+
if (wrapped !== error) throw wrapped;
|
|
4199
|
+
const acp = extractAcpError(error);
|
|
4200
|
+
const summary = acp ? formatSessionControlAcpSummary(acp) : error instanceof Error ? error.message : String(error);
|
|
4201
|
+
throw new Error(`Failed ${method} for model "${modelId}": ${summary}`, { cause: error });
|
|
4202
|
+
}
|
|
4203
|
+
resolveModelControl(sessionId, controlOverride) {
|
|
4204
|
+
if (controlOverride) return controlOverride.configId ? {
|
|
4205
|
+
kind: "config_option",
|
|
4206
|
+
configId: controlOverride.configId
|
|
4207
|
+
} : { kind: "legacy_set_model" };
|
|
4208
|
+
const configId = this.modelConfigIds.get(sessionId);
|
|
4209
|
+
if (configId) return {
|
|
4210
|
+
kind: "config_option",
|
|
4211
|
+
configId
|
|
4212
|
+
};
|
|
4213
|
+
return this.legacyModelSessionIds.has(sessionId) ? { kind: "legacy_set_model" } : void 0;
|
|
4214
|
+
}
|
|
4215
|
+
rememberSessionModels(sessionId, models) {
|
|
4216
|
+
if (!models) {
|
|
4217
|
+
this.modelConfigIds.delete(sessionId);
|
|
4218
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
4219
|
+
return;
|
|
4220
|
+
}
|
|
4221
|
+
if (models.configId) {
|
|
4222
|
+
this.modelConfigIds.set(sessionId, models.configId);
|
|
4223
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
4224
|
+
return;
|
|
3872
4225
|
}
|
|
4226
|
+
this.modelConfigIds.delete(sessionId);
|
|
4227
|
+
this.legacyModelSessionIds.add(sessionId);
|
|
4228
|
+
}
|
|
4229
|
+
updateRememberedSessionModels(sessionId, result) {
|
|
4230
|
+
const explicitConfigRemoval = result.configOptionsPresent && this.modelConfigIds.has(sessionId);
|
|
4231
|
+
if (result.models || result.legacyModelMetadataPresent || explicitConfigRemoval) this.rememberSessionModels(sessionId, result.models);
|
|
3873
4232
|
}
|
|
3874
4233
|
async cancel(sessionId) {
|
|
3875
4234
|
const connection = this.getConnection();
|
|
@@ -3881,6 +4240,8 @@ var AcpClient = class {
|
|
|
3881
4240
|
const connection = this.getConnection();
|
|
3882
4241
|
await this.runConnectionRequest(() => connection.closeSession({ sessionId }));
|
|
3883
4242
|
if (this.loadedSessionId === sessionId) this.loadedSessionId = void 0;
|
|
4243
|
+
this.modelConfigIds.delete(sessionId);
|
|
4244
|
+
this.legacyModelSessionIds.delete(sessionId);
|
|
3884
4245
|
}
|
|
3885
4246
|
async listSessions(params = {}) {
|
|
3886
4247
|
const connection = this.getConnection();
|
|
@@ -3929,6 +4290,8 @@ var AcpClient = class {
|
|
|
3929
4290
|
this.permissionAbortControllers.clear();
|
|
3930
4291
|
this.promptPermissionFailures.clear();
|
|
3931
4292
|
this.loadedSessionId = void 0;
|
|
4293
|
+
this.modelConfigIds.clear();
|
|
4294
|
+
this.legacyModelSessionIds.clear();
|
|
3932
4295
|
this.initResult = void 0;
|
|
3933
4296
|
this.connection = void 0;
|
|
3934
4297
|
this.agent = void 0;
|
|
@@ -3982,7 +4345,7 @@ var AcpClient = class {
|
|
|
3982
4345
|
target.push(text);
|
|
3983
4346
|
if (target.join("").length - STARTUP_STDERR_MAX_CHARS <= 0) return;
|
|
3984
4347
|
const joined = target.join("");
|
|
3985
|
-
target.splice(0, target.length, joined.slice(-
|
|
4348
|
+
target.splice(0, target.length, joined.slice(-8192));
|
|
3986
4349
|
}
|
|
3987
4350
|
summarizeStartupStderr(target) {
|
|
3988
4351
|
const joined = target.join("").trim();
|
|
@@ -4341,6 +4704,7 @@ function applyConversation(record, conversation) {
|
|
|
4341
4704
|
record.updated_at = conversation.updated_at;
|
|
4342
4705
|
record.messages = conversation.messages;
|
|
4343
4706
|
record.cumulative_token_usage = conversation.cumulative_token_usage;
|
|
4707
|
+
record.cumulative_cost = conversation.cumulative_cost;
|
|
4344
4708
|
record.request_token_usage = conversation.request_token_usage;
|
|
4345
4709
|
}
|
|
4346
4710
|
//#endregion
|
|
@@ -4416,6 +4780,51 @@ function nonEmptyString(value) {
|
|
|
4416
4780
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
4417
4781
|
}
|
|
4418
4782
|
//#endregion
|
|
4783
|
+
//#region src/session/model-state.ts
|
|
4784
|
+
function configOptionsAreAuthoritative(state) {
|
|
4785
|
+
return state.model_control === "config_option";
|
|
4786
|
+
}
|
|
4787
|
+
function legacyModelState(state) {
|
|
4788
|
+
if (!Array.isArray(state.available_models)) return;
|
|
4789
|
+
return {
|
|
4790
|
+
currentModelId: state.current_model_id ?? "",
|
|
4791
|
+
availableModels: state.available_models.map((modelId) => ({
|
|
4792
|
+
modelId,
|
|
4793
|
+
name: modelId
|
|
4794
|
+
}))
|
|
4795
|
+
};
|
|
4796
|
+
}
|
|
4797
|
+
function advertisedModelState(state) {
|
|
4798
|
+
if (!state) return;
|
|
4799
|
+
const configModels = modelStateFromConfigOptions(state?.config_options);
|
|
4800
|
+
if (configModels) return configModels;
|
|
4801
|
+
if (configOptionsAreAuthoritative(state)) return;
|
|
4802
|
+
return legacyModelState(state);
|
|
4803
|
+
}
|
|
4804
|
+
function applyAdvertisedModelState(state, models) {
|
|
4805
|
+
state.current_model_id = models.currentModelId;
|
|
4806
|
+
state.available_models = models.availableModels.map((model) => model.modelId);
|
|
4807
|
+
state.model_control = models.configId ? "config_option" : "legacy_set_model";
|
|
4808
|
+
}
|
|
4809
|
+
function clearAdvertisedModelState(state) {
|
|
4810
|
+
delete state.current_model_id;
|
|
4811
|
+
delete state.available_models;
|
|
4812
|
+
delete state.model_control;
|
|
4813
|
+
}
|
|
4814
|
+
function removeModelConfigOptions(state) {
|
|
4815
|
+
if (!state.config_options) return;
|
|
4816
|
+
state.config_options = state.config_options.filter((option) => option.category !== "model" && option.id !== "model");
|
|
4817
|
+
}
|
|
4818
|
+
function applyConfigOptionsModelState(state, configOptions) {
|
|
4819
|
+
const previousConfigModels = modelStateFromConfigOptions(state.config_options);
|
|
4820
|
+
const preservesLegacyControl = state.model_control === "legacy_set_model" || state.model_control === void 0 && previousConfigModels === void 0 && legacyModelState(state) !== void 0;
|
|
4821
|
+
state.config_options = structuredClone(configOptions);
|
|
4822
|
+
const models = modelStateFromConfigOptions(configOptions);
|
|
4823
|
+
if (models) applyAdvertisedModelState(state, models);
|
|
4824
|
+
else if (preservesLegacyControl) state.model_control = "legacy_set_model";
|
|
4825
|
+
else clearAdvertisedModelState(state);
|
|
4826
|
+
}
|
|
4827
|
+
//#endregion
|
|
4419
4828
|
//#region src/session/conversation-model.ts
|
|
4420
4829
|
const MAX_RUNTIME_MESSAGES = 200;
|
|
4421
4830
|
const MAX_RUNTIME_AGENT_TEXT_CHARS = 8e3;
|
|
@@ -4436,10 +4845,25 @@ function hasOwn(source, key) {
|
|
|
4436
4845
|
return Object.prototype.hasOwnProperty.call(source, key);
|
|
4437
4846
|
}
|
|
4438
4847
|
function normalizeAgentName(value) {
|
|
4848
|
+
return trimmedString(value);
|
|
4849
|
+
}
|
|
4850
|
+
function trimmedString(value) {
|
|
4439
4851
|
if (typeof value !== "string") return;
|
|
4440
4852
|
const trimmed = value.trim();
|
|
4441
4853
|
return trimmed.length > 0 ? trimmed : void 0;
|
|
4442
4854
|
}
|
|
4855
|
+
function normalizeAvailableCommand(value) {
|
|
4856
|
+
const record = asRecord(value);
|
|
4857
|
+
if (!record) return;
|
|
4858
|
+
const name = trimmedString(record.name);
|
|
4859
|
+
if (!name) return;
|
|
4860
|
+
const description = trimmedString(record.description);
|
|
4861
|
+
return {
|
|
4862
|
+
name,
|
|
4863
|
+
...description ? { description } : {},
|
|
4864
|
+
has_input: record.input != null
|
|
4865
|
+
};
|
|
4866
|
+
}
|
|
4443
4867
|
function extractText(content) {
|
|
4444
4868
|
switch (content.type) {
|
|
4445
4869
|
case "text": return content.text;
|
|
@@ -4667,7 +5091,9 @@ function usageToTokenUsage(update) {
|
|
|
4667
5091
|
"cache_read_input_tokens",
|
|
4668
5092
|
"cacheReadInputTokens",
|
|
4669
5093
|
"cachedReadTokens"
|
|
4670
|
-
])
|
|
5094
|
+
]),
|
|
5095
|
+
thought_tokens: numberField(source, ["thought_tokens", "thoughtTokens"]),
|
|
5096
|
+
total_tokens: numberField(source, ["total_tokens", "totalTokens"])
|
|
4671
5097
|
};
|
|
4672
5098
|
if (!hasTokenUsageValue(normalized)) return;
|
|
4673
5099
|
return normalized;
|
|
@@ -4675,6 +5101,21 @@ function usageToTokenUsage(update) {
|
|
|
4675
5101
|
function hasTokenUsageValue(usage) {
|
|
4676
5102
|
return Object.values(usage).some((value) => value !== void 0);
|
|
4677
5103
|
}
|
|
5104
|
+
function usageCost(update) {
|
|
5105
|
+
const cost = asRecord(asRecord(update)?.cost);
|
|
5106
|
+
if (!cost) return;
|
|
5107
|
+
return buildUsageCost(numberField(cost, ["amount"]), stringField(cost.currency));
|
|
5108
|
+
}
|
|
5109
|
+
function stringField(value) {
|
|
5110
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
5111
|
+
}
|
|
5112
|
+
function buildUsageCost(amount, currency) {
|
|
5113
|
+
const cost = {
|
|
5114
|
+
...amount !== void 0 ? { amount } : {},
|
|
5115
|
+
...currency !== void 0 ? { currency } : {}
|
|
5116
|
+
};
|
|
5117
|
+
return Object.keys(cost).length > 0 ? cost : void 0;
|
|
5118
|
+
}
|
|
4678
5119
|
function ensureAcpxState$1(state) {
|
|
4679
5120
|
return state ?? {};
|
|
4680
5121
|
}
|
|
@@ -4690,6 +5131,7 @@ function createSessionConversation(timestamp = isoNow()) {
|
|
|
4690
5131
|
messages: [],
|
|
4691
5132
|
updated_at: timestamp,
|
|
4692
5133
|
cumulative_token_usage: {},
|
|
5134
|
+
cumulative_cost: void 0,
|
|
4693
5135
|
request_token_usage: {}
|
|
4694
5136
|
};
|
|
4695
5137
|
}
|
|
@@ -4700,9 +5142,13 @@ function cloneSessionConversation(conversation) {
|
|
|
4700
5142
|
messages: deepClone(conversation.messages ?? []),
|
|
4701
5143
|
updated_at: conversation.updated_at,
|
|
4702
5144
|
cumulative_token_usage: deepClone(conversation.cumulative_token_usage ?? {}),
|
|
5145
|
+
cumulative_cost: cloneUsageCost(conversation.cumulative_cost),
|
|
4703
5146
|
request_token_usage: deepClone(conversation.request_token_usage ?? {})
|
|
4704
5147
|
};
|
|
4705
5148
|
}
|
|
5149
|
+
function cloneUsageCost(cost) {
|
|
5150
|
+
return cost ? { ...cost } : void 0;
|
|
5151
|
+
}
|
|
4706
5152
|
function cloneSessionAcpxState(state) {
|
|
4707
5153
|
if (!state) return;
|
|
4708
5154
|
return {
|
|
@@ -4711,7 +5157,8 @@ function cloneSessionAcpxState(state) {
|
|
|
4711
5157
|
desired_config_options: state.desired_config_options ? { ...state.desired_config_options } : void 0,
|
|
4712
5158
|
current_model_id: state.current_model_id,
|
|
4713
5159
|
available_models: state.available_models ? [...state.available_models] : void 0,
|
|
4714
|
-
|
|
5160
|
+
model_control: state.model_control,
|
|
5161
|
+
available_commands: state.available_commands ? state.available_commands.map((command) => ({ ...command })) : void 0,
|
|
4715
5162
|
config_options: state.config_options ? deepClone(state.config_options) : void 0,
|
|
4716
5163
|
session_options: cloneSessionOptions(state.session_options)
|
|
4717
5164
|
};
|
|
@@ -4792,13 +5239,13 @@ const SESSION_UPDATE_HANDLERS = {
|
|
|
4792
5239
|
if (update.sessionUpdate === "session_info_update") applySessionInfoUpdate(conversation, update);
|
|
4793
5240
|
},
|
|
4794
5241
|
available_commands_update: (_conversation, acpx, update) => {
|
|
4795
|
-
if (update.sessionUpdate === "available_commands_update") acpx.available_commands = update.availableCommands.map((entry) => entry
|
|
5242
|
+
if (update.sessionUpdate === "available_commands_update") acpx.available_commands = update.availableCommands.map((entry) => normalizeAvailableCommand(entry)).filter((entry) => entry !== void 0);
|
|
4796
5243
|
},
|
|
4797
5244
|
current_mode_update: (_conversation, acpx, update) => {
|
|
4798
5245
|
if (update.sessionUpdate === "current_mode_update") acpx.current_mode_id = update.currentModeId;
|
|
4799
5246
|
},
|
|
4800
5247
|
config_option_update: (_conversation, acpx, update) => {
|
|
4801
|
-
if (update.sessionUpdate === "config_option_update") acpx
|
|
5248
|
+
if (update.sessionUpdate === "config_option_update") applyConfigOptionsModelState(acpx, deepClone(update.configOptions));
|
|
4802
5249
|
}
|
|
4803
5250
|
};
|
|
4804
5251
|
function appendUserMessageChunk(conversation, content) {
|
|
@@ -4815,10 +5262,14 @@ function appendAgentMessageChunk(conversation, content, append) {
|
|
|
4815
5262
|
}
|
|
4816
5263
|
function applyUsageUpdate(conversation, update) {
|
|
4817
5264
|
const usage = usageToTokenUsage(update);
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
5265
|
+
const cost = usageCost(update);
|
|
5266
|
+
if (!usage && !cost) return;
|
|
5267
|
+
if (usage) {
|
|
5268
|
+
conversation.cumulative_token_usage = usage;
|
|
5269
|
+
const userId = lastUserMessageId(conversation);
|
|
5270
|
+
if (userId) conversation.request_token_usage[userId] = usage;
|
|
5271
|
+
}
|
|
5272
|
+
if (cost) conversation.cumulative_cost = cost;
|
|
4822
5273
|
}
|
|
4823
5274
|
function applySessionInfoUpdate(conversation, update) {
|
|
4824
5275
|
if (hasOwn(update, "title")) conversation.title = update.title ?? null;
|
|
@@ -4831,10 +5282,10 @@ function recordClientOperation(conversation, state, operation, timestamp = isoNo
|
|
|
4831
5282
|
return acpx;
|
|
4832
5283
|
}
|
|
4833
5284
|
function trimConversationForRuntime(conversation) {
|
|
4834
|
-
if (conversation.messages.length > MAX_RUNTIME_MESSAGES) conversation.messages = conversation.messages.slice(-
|
|
5285
|
+
if (conversation.messages.length > MAX_RUNTIME_MESSAGES) conversation.messages = conversation.messages.slice(-200);
|
|
4835
5286
|
for (const message of conversation.messages) trimRuntimeMessage(message);
|
|
4836
5287
|
const requestUsageEntries = Object.entries(conversation.request_token_usage);
|
|
4837
|
-
if (requestUsageEntries.length > MAX_RUNTIME_REQUEST_TOKEN_USAGE) conversation.request_token_usage = Object.fromEntries(requestUsageEntries.slice(-
|
|
5288
|
+
if (requestUsageEntries.length > MAX_RUNTIME_REQUEST_TOKEN_USAGE) conversation.request_token_usage = Object.fromEntries(requestUsageEntries.slice(-100));
|
|
4838
5289
|
}
|
|
4839
5290
|
function trimRuntimeMessage(message) {
|
|
4840
5291
|
if (isUserMessage(message)) {
|
|
@@ -4864,12 +5315,15 @@ function trimRuntimeToolResult(result) {
|
|
|
4864
5315
|
}
|
|
4865
5316
|
//#endregion
|
|
4866
5317
|
//#region src/session/config-options.ts
|
|
5318
|
+
function applyConfigOptionsToState(state, configOptions) {
|
|
5319
|
+
const acpxState = cloneSessionAcpxState(state) ?? {};
|
|
5320
|
+
applyConfigOptionsModelState(acpxState, configOptions);
|
|
5321
|
+
return acpxState;
|
|
5322
|
+
}
|
|
4867
5323
|
function applyConfigOptionsToRecord(record, result) {
|
|
4868
5324
|
const configOptions = result?.configOptions;
|
|
4869
5325
|
if (!configOptions) return;
|
|
4870
|
-
|
|
4871
|
-
acpxState.config_options = structuredClone(configOptions);
|
|
4872
|
-
record.acpx = acpxState;
|
|
5326
|
+
record.acpx = applyConfigOptionsToState(record.acpx, configOptions);
|
|
4873
5327
|
}
|
|
4874
5328
|
//#endregion
|
|
4875
5329
|
//#region src/session/mode-preference.ts
|
|
@@ -4915,10 +5369,18 @@ function setDesiredConfigOption(record, configId, value) {
|
|
|
4915
5369
|
else delete acpx.desired_config_options;
|
|
4916
5370
|
record.acpx = acpx;
|
|
4917
5371
|
}
|
|
5372
|
+
function clearDesiredConfigOption(state, configId) {
|
|
5373
|
+
const normalizedConfigId = normalizeModeId(configId);
|
|
5374
|
+
if (!normalizedConfigId || !state.desired_config_options) return;
|
|
5375
|
+
const desired = { ...state.desired_config_options };
|
|
5376
|
+
delete desired[normalizedConfigId];
|
|
5377
|
+
if (Object.keys(desired).length > 0) state.desired_config_options = desired;
|
|
5378
|
+
else delete state.desired_config_options;
|
|
5379
|
+
}
|
|
4918
5380
|
function getDesiredModelId(state) {
|
|
4919
5381
|
return normalizeModelId(state?.session_options?.model);
|
|
4920
5382
|
}
|
|
4921
|
-
function setDesiredModelId(record, modelId) {
|
|
5383
|
+
function setDesiredModelId(record, modelId, modelConfigId) {
|
|
4922
5384
|
const acpx = ensureAcpxState(record.acpx);
|
|
4923
5385
|
const normalized = normalizeModelId(modelId);
|
|
4924
5386
|
const sessionOptions = { ...acpx.session_options };
|
|
@@ -4926,6 +5388,7 @@ function setDesiredModelId(record, modelId) {
|
|
|
4926
5388
|
else delete sessionOptions.model;
|
|
4927
5389
|
if (typeof sessionOptions.model === "string" || Array.isArray(sessionOptions.allowed_tools) || typeof sessionOptions.max_turns === "number" || sessionOptions.system_prompt !== void 0) acpx.session_options = sessionOptions;
|
|
4928
5390
|
else delete acpx.session_options;
|
|
5391
|
+
clearDesiredConfigOption(acpx, modelConfigId ?? modelStateFromConfigOptions(acpx.config_options)?.configId);
|
|
4929
5392
|
record.acpx = acpx;
|
|
4930
5393
|
}
|
|
4931
5394
|
function setCurrentModelId(record, modelId) {
|
|
@@ -4938,49 +5401,30 @@ function setCurrentModelId(record, modelId) {
|
|
|
4938
5401
|
function syncAdvertisedModelState(record, models) {
|
|
4939
5402
|
if (!models) return;
|
|
4940
5403
|
const acpx = ensureAcpxState(record.acpx);
|
|
4941
|
-
acpx
|
|
4942
|
-
acpx.available_models = models.availableModels.map((model) => model.modelId);
|
|
5404
|
+
applyAdvertisedModelState(acpx, models);
|
|
4943
5405
|
record.acpx = acpx;
|
|
4944
5406
|
}
|
|
4945
5407
|
//#endregion
|
|
4946
|
-
//#region src/acp/model-support.ts
|
|
4947
|
-
var RequestedModelUnsupportedError = class extends Error {
|
|
4948
|
-
constructor(message) {
|
|
4949
|
-
super(message);
|
|
4950
|
-
this.name = "RequestedModelUnsupportedError";
|
|
4951
|
-
}
|
|
4952
|
-
};
|
|
4953
|
-
function supportsLegacyClaudeCodeModelMetadata(agentCommand) {
|
|
4954
|
-
if (!agentCommand) return false;
|
|
4955
|
-
const { command, args } = splitCommandLine(agentCommand);
|
|
4956
|
-
return isClaudeAcpCommand(command, args);
|
|
4957
|
-
}
|
|
4958
|
-
function formatAvailableModelIds(models) {
|
|
4959
|
-
const ids = models?.availableModels.map((model) => model.modelId.trim()).filter((modelId) => modelId.length > 0) ?? [];
|
|
4960
|
-
return ids.length > 0 ? ids.join(", ") : "none advertised";
|
|
4961
|
-
}
|
|
4962
|
-
function assertRequestedModelSupported(params) {
|
|
4963
|
-
if (!params.models) {
|
|
4964
|
-
if (supportsLegacyClaudeCodeModelMetadata(params.agentCommand)) return;
|
|
4965
|
-
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.`);
|
|
4966
|
-
}
|
|
4967
|
-
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)}.`);
|
|
4968
|
-
}
|
|
4969
|
-
//#endregion
|
|
4970
5408
|
//#region src/session/model-application.ts
|
|
5409
|
+
function currentModelIdFromSetModelResponse(response, fallbackModelId) {
|
|
5410
|
+
return modelStateFromConfigOptions(response?.configOptions)?.currentModelId ?? fallbackModelId;
|
|
5411
|
+
}
|
|
4971
5412
|
async function applyRequestedModelIfAdvertised(params) {
|
|
4972
5413
|
const requestedModel = typeof params.requestedModel === "string" ? params.requestedModel.trim() : "";
|
|
4973
|
-
if (!requestedModel) return false;
|
|
4974
|
-
assertRequestedModelSupported({
|
|
5414
|
+
if (!requestedModel) return { applied: false };
|
|
5415
|
+
const warning = assertRequestedModelSupported({
|
|
4975
5416
|
requestedModel,
|
|
4976
5417
|
models: params.models,
|
|
4977
5418
|
agentCommand: params.agentCommand,
|
|
4978
5419
|
context: "apply"
|
|
4979
5420
|
});
|
|
4980
|
-
if (
|
|
4981
|
-
if (params.models
|
|
4982
|
-
|
|
4983
|
-
return
|
|
5421
|
+
if (warning) params.onWarning?.(warning);
|
|
5422
|
+
if (!params.models) return { applied: false };
|
|
5423
|
+
if (params.models.currentModelId === requestedModel) return { applied: true };
|
|
5424
|
+
return {
|
|
5425
|
+
applied: true,
|
|
5426
|
+
response: await withTimeout(params.client.setSessionModel(params.sessionId, requestedModel, params.models), params.timeoutMs)
|
|
5427
|
+
};
|
|
4984
5428
|
}
|
|
4985
5429
|
//#endregion
|
|
4986
5430
|
//#region src/runtime/engine/reconnect.ts
|
|
@@ -5028,17 +5472,27 @@ async function replayDesiredMode(params) {
|
|
|
5028
5472
|
}
|
|
5029
5473
|
}
|
|
5030
5474
|
async function replayDesiredModel(params) {
|
|
5031
|
-
if (!params.desiredModelId) return;
|
|
5475
|
+
if (!params.desiredModelId) return { replayed: false };
|
|
5032
5476
|
try {
|
|
5033
|
-
assertRequestedModelSupported({
|
|
5477
|
+
emitModelSupportWarning(assertRequestedModelSupported({
|
|
5034
5478
|
requestedModel: params.desiredModelId,
|
|
5035
5479
|
models: params.models,
|
|
5036
5480
|
agentCommand: params.record.agentCommand,
|
|
5037
5481
|
context: "replay"
|
|
5038
|
-
});
|
|
5039
|
-
if (!params.models || params.models.currentModelId === params.desiredModelId) return;
|
|
5040
|
-
await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId), params.timeoutMs);
|
|
5482
|
+
}), params.suppressWarnings);
|
|
5483
|
+
if (!params.models || params.models.currentModelId === params.desiredModelId) return { replayed: false };
|
|
5484
|
+
const response = await withTimeout(params.client.setSessionModel(params.sessionId, params.desiredModelId, params.models), params.timeoutMs);
|
|
5485
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5486
|
+
const models = response ? modelStateFromConfigOptions(response.configOptions) : {
|
|
5487
|
+
...params.models,
|
|
5488
|
+
currentModelId: params.desiredModelId
|
|
5489
|
+
};
|
|
5041
5490
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired model ${params.desiredModelId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5491
|
+
return {
|
|
5492
|
+
replayed: true,
|
|
5493
|
+
models,
|
|
5494
|
+
configOptionsPresent: response !== void 0
|
|
5495
|
+
};
|
|
5042
5496
|
} catch (error) {
|
|
5043
5497
|
throw new SessionModelReplayError(`Failed to replay saved session model ${params.desiredModelId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
|
|
5044
5498
|
cause: error instanceof Error ? error : void 0,
|
|
@@ -5046,9 +5500,18 @@ async function replayDesiredModel(params) {
|
|
|
5046
5500
|
});
|
|
5047
5501
|
}
|
|
5048
5502
|
}
|
|
5503
|
+
function emitModelSupportWarning(warning, suppressWarnings) {
|
|
5504
|
+
if (warning && !suppressWarnings) process.stderr.write(`[acpx] warning: ${warning}\n`);
|
|
5505
|
+
}
|
|
5049
5506
|
async function replayDesiredConfigOptions(params) {
|
|
5507
|
+
let result = { replayed: false };
|
|
5050
5508
|
for (const [configId, value] of Object.entries(params.desiredConfigOptions)) try {
|
|
5051
|
-
await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
|
|
5509
|
+
const response = await withTimeout(params.client.setSessionConfigOption(params.sessionId, configId, value), params.timeoutMs);
|
|
5510
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5511
|
+
result = {
|
|
5512
|
+
replayed: true,
|
|
5513
|
+
models: modelStateFromConfigOptions(response.configOptions)
|
|
5514
|
+
};
|
|
5052
5515
|
if (params.verbose) process.stderr.write(`[acpx] replayed desired config option ${configId} on fresh ACP session ${params.sessionId} (previous ${params.previousSessionId})\n`);
|
|
5053
5516
|
} catch (error) {
|
|
5054
5517
|
throw new SessionConfigOptionReplayError(`Failed to replay saved session config option ${configId} on fresh ACP session ${params.sessionId}: ${formatErrorMessage(error)}`, {
|
|
@@ -5056,6 +5519,7 @@ async function replayDesiredConfigOptions(params) {
|
|
|
5056
5519
|
retryable: true
|
|
5057
5520
|
});
|
|
5058
5521
|
}
|
|
5522
|
+
return result;
|
|
5059
5523
|
}
|
|
5060
5524
|
function restoreOriginalSessionState(params) {
|
|
5061
5525
|
params.record.acpSessionId = params.sessionId;
|
|
@@ -5064,9 +5528,10 @@ function restoreOriginalSessionState(params) {
|
|
|
5064
5528
|
async function connectAndLoadSession(options) {
|
|
5065
5529
|
const record = options.record;
|
|
5066
5530
|
const client = options.client;
|
|
5067
|
-
const sameSessionOnly = requiresSameSession(options.resumePolicy);
|
|
5531
|
+
const sameSessionOnly = requiresSameSession(options.resumePolicy) || Boolean(record.importedFrom);
|
|
5068
5532
|
const originalSessionId = record.acpSessionId;
|
|
5069
5533
|
const originalAgentSessionId = record.agentSessionId;
|
|
5534
|
+
const originalAcpx = cloneSessionAcpxState(record.acpx);
|
|
5070
5535
|
const desiredModeId = getDesiredModeId(record.acpx);
|
|
5071
5536
|
const desiredModelId = getDesiredModelId(record.acpx);
|
|
5072
5537
|
const desiredConfigOptions = getDesiredConfigOptions(record.acpx);
|
|
@@ -5099,7 +5564,7 @@ async function connectAndLoadSession(options) {
|
|
|
5099
5564
|
createdFreshSession = loadState.createdFreshSession;
|
|
5100
5565
|
pendingAgentSessionId = loadState.pendingAgentSessionId;
|
|
5101
5566
|
sessionModels = loadState.sessionModels;
|
|
5102
|
-
await replayFreshSessionPreferences({
|
|
5567
|
+
const preferenceReplay = await replayFreshSessionPreferences({
|
|
5103
5568
|
client,
|
|
5104
5569
|
record,
|
|
5105
5570
|
createdFreshSession,
|
|
@@ -5107,14 +5572,16 @@ async function connectAndLoadSession(options) {
|
|
|
5107
5572
|
pendingAgentSessionId,
|
|
5108
5573
|
originalSessionId,
|
|
5109
5574
|
originalAgentSessionId,
|
|
5575
|
+
originalAcpx,
|
|
5110
5576
|
desiredModeId,
|
|
5111
5577
|
desiredModelId,
|
|
5112
5578
|
desiredConfigOptions,
|
|
5113
5579
|
sessionModels,
|
|
5114
5580
|
timeoutMs: options.timeoutMs,
|
|
5115
|
-
verbose: options.verbose
|
|
5581
|
+
verbose: options.verbose,
|
|
5582
|
+
suppressWarnings: options.suppressWarnings
|
|
5116
5583
|
});
|
|
5117
|
-
applyReconnectedModelState(record, sessionModels,
|
|
5584
|
+
applyReconnectedModelState(record, resolveModelsAfterReplay(preferenceReplay, sessionModels), resolveConfigOptionsPresenceAfterReplay(preferenceReplay, loadState.configOptionsPresent), loadState.legacyModelMetadataPresent, createdFreshSession);
|
|
5118
5585
|
options.onSessionIdResolved?.(sessionId);
|
|
5119
5586
|
return {
|
|
5120
5587
|
sessionId,
|
|
@@ -5123,9 +5590,28 @@ async function connectAndLoadSession(options) {
|
|
|
5123
5590
|
loadError
|
|
5124
5591
|
};
|
|
5125
5592
|
}
|
|
5126
|
-
function
|
|
5127
|
-
|
|
5128
|
-
|
|
5593
|
+
function resolveModelsAfterReplay(replay, initialModels) {
|
|
5594
|
+
if (replay.configReplay.replayed) return replay.configReplay.models ?? preserveLegacyModels(replay.modelReplay.replayed ? replay.modelReplay.models : initialModels);
|
|
5595
|
+
return replay.modelReplay.replayed ? replay.modelReplay.models : initialModels;
|
|
5596
|
+
}
|
|
5597
|
+
function preserveLegacyModels(models) {
|
|
5598
|
+
return models && !models.configId ? models : void 0;
|
|
5599
|
+
}
|
|
5600
|
+
function resolveConfigOptionsPresenceAfterReplay(replay, initiallyPresent) {
|
|
5601
|
+
return initiallyPresent || replay.configReplay.replayed || replay.modelReplay.replayed && replay.modelReplay.configOptionsPresent;
|
|
5602
|
+
}
|
|
5603
|
+
function applyReconnectedModelState(record, sessionModels, configOptionsPresent, legacyModelMetadataPresent, createdFreshSession) {
|
|
5604
|
+
clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent);
|
|
5605
|
+
if (sessionModels) {
|
|
5606
|
+
if (legacyModelMetadataPresent && !sessionModels.configId && record.acpx) removeModelConfigOptions(record.acpx);
|
|
5607
|
+
syncAdvertisedModelState(record, sessionModels);
|
|
5608
|
+
} else clearRemovedModelState(record, legacyModelMetadataPresent || createdFreshSession);
|
|
5609
|
+
}
|
|
5610
|
+
function clearOmittedFreshSessionConfigOptions(record, createdFreshSession, configOptionsPresent) {
|
|
5611
|
+
if (createdFreshSession && !configOptionsPresent && record.acpx) delete record.acpx.config_options;
|
|
5612
|
+
}
|
|
5613
|
+
function clearRemovedModelState(record, shouldClear) {
|
|
5614
|
+
if (shouldClear && record.acpx) clearAdvertisedModelState(record.acpx);
|
|
5129
5615
|
}
|
|
5130
5616
|
function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbose) {
|
|
5131
5617
|
if (!verbose) return;
|
|
@@ -5136,7 +5622,12 @@ function logReconnectAttempt(record, storedProcessAlive, shouldReconnect, verbos
|
|
|
5136
5622
|
if (shouldReconnect) process.stderr.write(`[acpx] saved session pid ${record.pid} is dead; respawning agent and attempting session reconnect\n`);
|
|
5137
5623
|
}
|
|
5138
5624
|
async function replayFreshSessionPreferences(params) {
|
|
5139
|
-
if (!params.createdFreshSession) return
|
|
5625
|
+
if (!params.createdFreshSession) return {
|
|
5626
|
+
modelReplay: { replayed: false },
|
|
5627
|
+
configReplay: { replayed: false }
|
|
5628
|
+
};
|
|
5629
|
+
let modelReplay = { replayed: false };
|
|
5630
|
+
let configReplay = { replayed: false };
|
|
5140
5631
|
try {
|
|
5141
5632
|
await replayDesiredMode({
|
|
5142
5633
|
client: params.client,
|
|
@@ -5146,7 +5637,7 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5146
5637
|
timeoutMs: params.timeoutMs,
|
|
5147
5638
|
verbose: params.verbose
|
|
5148
5639
|
});
|
|
5149
|
-
await replayDesiredModel({
|
|
5640
|
+
modelReplay = await replayDesiredModel({
|
|
5150
5641
|
client: params.client,
|
|
5151
5642
|
sessionId: params.sessionId,
|
|
5152
5643
|
desiredModelId: params.desiredModelId,
|
|
@@ -5154,10 +5645,12 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5154
5645
|
record: params.record,
|
|
5155
5646
|
models: params.sessionModels,
|
|
5156
5647
|
timeoutMs: params.timeoutMs,
|
|
5157
|
-
verbose: params.verbose
|
|
5648
|
+
verbose: params.verbose,
|
|
5649
|
+
suppressWarnings: params.suppressWarnings
|
|
5158
5650
|
});
|
|
5159
|
-
await replayDesiredConfigOptions({
|
|
5651
|
+
configReplay = await replayDesiredConfigOptions({
|
|
5160
5652
|
client: params.client,
|
|
5653
|
+
record: params.record,
|
|
5161
5654
|
sessionId: params.sessionId,
|
|
5162
5655
|
desiredConfigOptions: params.desiredConfigOptions,
|
|
5163
5656
|
previousSessionId: params.originalSessionId,
|
|
@@ -5170,17 +5663,24 @@ async function replayFreshSessionPreferences(params) {
|
|
|
5170
5663
|
sessionId: params.originalSessionId,
|
|
5171
5664
|
agentSessionId: params.originalAgentSessionId
|
|
5172
5665
|
});
|
|
5666
|
+
params.record.acpx = cloneSessionAcpxState(params.originalAcpx);
|
|
5173
5667
|
if (params.verbose) process.stderr.write(`[acpx] ${formatErrorMessage(error)}\n`);
|
|
5174
5668
|
throw error;
|
|
5175
5669
|
}
|
|
5176
5670
|
params.record.acpSessionId = params.sessionId;
|
|
5177
5671
|
reconcileAgentSessionId(params.record, params.pendingAgentSessionId);
|
|
5672
|
+
return {
|
|
5673
|
+
modelReplay,
|
|
5674
|
+
configReplay
|
|
5675
|
+
};
|
|
5178
5676
|
}
|
|
5179
5677
|
async function loadOrCreateRuntimeSession(params) {
|
|
5180
5678
|
if (params.reusingLoadedSession) return {
|
|
5181
5679
|
sessionId: params.record.acpSessionId,
|
|
5182
5680
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5183
5681
|
sessionModels: void 0,
|
|
5682
|
+
configOptionsPresent: false,
|
|
5683
|
+
legacyModelMetadataPresent: false,
|
|
5184
5684
|
resumed: true,
|
|
5185
5685
|
createdFreshSession: false
|
|
5186
5686
|
};
|
|
@@ -5201,6 +5701,8 @@ async function resumeRuntimeSession(params) {
|
|
|
5201
5701
|
sessionId: params.record.acpSessionId,
|
|
5202
5702
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5203
5703
|
sessionModels: resumeResult.models,
|
|
5704
|
+
configOptionsPresent: resumeResult.configOptionsPresent,
|
|
5705
|
+
legacyModelMetadataPresent: resumeResult.legacyModelMetadataPresent,
|
|
5204
5706
|
resumed: true,
|
|
5205
5707
|
createdFreshSession: false
|
|
5206
5708
|
};
|
|
@@ -5217,6 +5719,8 @@ async function loadRuntimeSession(params) {
|
|
|
5217
5719
|
sessionId: params.record.acpSessionId,
|
|
5218
5720
|
pendingAgentSessionId: params.record.agentSessionId,
|
|
5219
5721
|
sessionModels: loadResult.models,
|
|
5722
|
+
configOptionsPresent: loadResult.configOptionsPresent,
|
|
5723
|
+
legacyModelMetadataPresent: loadResult.legacyModelMetadataPresent,
|
|
5220
5724
|
resumed: true,
|
|
5221
5725
|
createdFreshSession: false
|
|
5222
5726
|
};
|
|
@@ -5244,6 +5748,8 @@ async function createFreshRuntimeSession(client, record, timeoutMs) {
|
|
|
5244
5748
|
sessionId: createdSession.sessionId,
|
|
5245
5749
|
pendingAgentSessionId: createdSession.agentSessionId,
|
|
5246
5750
|
sessionModels: createdSession.models,
|
|
5751
|
+
configOptionsPresent: createdSession.configOptionsPresent,
|
|
5752
|
+
legacyModelMetadataPresent: createdSession.legacyModelMetadataPresent,
|
|
5247
5753
|
resumed: false,
|
|
5248
5754
|
createdFreshSession: true
|
|
5249
5755
|
};
|
|
@@ -5259,7 +5765,10 @@ function createActiveSessionController(params) {
|
|
|
5259
5765
|
await params.client.setSessionMode(getActiveSessionId(), modeId);
|
|
5260
5766
|
},
|
|
5261
5767
|
setSessionModel: async (modelId) => {
|
|
5262
|
-
|
|
5768
|
+
const models = advertisedModelState(params.record.acpx);
|
|
5769
|
+
const response = await params.client.setSessionModel(getActiveSessionId(), modelId, models);
|
|
5770
|
+
applyConfigOptionsToRecord(params.record, response);
|
|
5771
|
+
return response;
|
|
5263
5772
|
},
|
|
5264
5773
|
setSessionConfigOption: async (configId, value) => {
|
|
5265
5774
|
return await params.client.setSessionConfigOption(getActiveSessionId(), configId, value);
|
|
@@ -5297,6 +5806,7 @@ async function withConnectedSession(options) {
|
|
|
5297
5806
|
let notifiedClientAvailable = false;
|
|
5298
5807
|
const activeController = createActiveSessionController({
|
|
5299
5808
|
client,
|
|
5809
|
+
record,
|
|
5300
5810
|
getActiveSessionId: () => activeSessionIdForControl
|
|
5301
5811
|
});
|
|
5302
5812
|
try {
|
|
@@ -5440,6 +5950,6 @@ var LiveSessionCheckpoint = class {
|
|
|
5440
5950
|
}
|
|
5441
5951
|
};
|
|
5442
5952
|
//#endregion
|
|
5443
|
-
export {
|
|
5953
|
+
export { setPerfGauge as $, assertRequestedModelSupported as A, isRetryablePromptError as At, listSessions as B, OUTPUT_FORMATS as Bt, mergeSessionOptions as C, withTimeout as Ct, applyLifecycleSnapshotToRecord as D, resolveAgentCommand as Dt, applyConversation as E, normalizeAgentName$1 as Et, absolutePath as F, AUTH_POLICIES as Ft, writeSessionRecord as G, AgentSpawnError as Gt, normalizeName as H, PERMISSION_POLICY_ACTIONS as Ht, findGitRepositoryRoot as I, EXIT_CODES as It, getPerfMetricsSnapshot as J, assertPersistedKeyPolicy as K, QueueConnectionError as Kt, findSession as L, NON_INTERACTIVE_PERMISSION_POLICIES as Lt, getAcpxVersion as M, extractAcpError as Mt, permissionModeSatisfies as N, isAcpResourceNotFoundError as Nt, reconcileAgentSessionId as O, exitCodeForOutputErrorCode as Ot, DEFAULT_HISTORY_LIMIT as P, toAcpErrorPayload as Pt, resetPerfMetrics as Q, findSessionByDirectoryWalk as R, OUTPUT_ERROR_CODES as Rt, advertisedModelState as S, withInterrupt as St, sessionOptionsFromRecord as T, listBuiltInAgents as Tt, pruneSessions as U, SESSION_RECORD_SCHEMA as Ut, listSessionsForAgent as V, PERMISSION_MODES as Vt, resolveSessionRecord as W, AcpxOperationalError as Wt, measurePerf as X, incrementPerfCounter as Y, recordPerfDuration as Z, createSessionConversation as _, parsePromptSource as _t, applyRequestedModelIfAdvertised as a, defaultSessionEventLog as at, recordSessionUpdate as b, InterruptedError as bt, setCurrentModelId as c, sessionEventLockPath as ct, setDesiredModelId as d, isAcpJsonRpcMessage as dt, startPerfTimer as et, syncAdvertisedModelState as f, parseJsonRpcErrorMessage as ft, cloneSessionConversation as g, mergePromptSourceWithText as gt, cloneSessionAcpxState as h, isPromptInput as ht, connectAndLoadSession as i, DEFAULT_EVENT_SEGMENT_MAX_BYTES as it, modelStateFromConfigOptions as j, normalizeOutputError as jt, AcpClient as k, formatErrorMessage as kt, setDesiredConfigOption as l, sessionEventSegmentPath as lt, applyConfigOptionsToState as m, PromptInputValidationError as mt, runPromptTurn as n, serializeSessionRecordForDisk as nt, currentModelIdFromSetModelResponse as o, sessionBaseDir$1 as ot, applyConfigOptionsToRecord as p, parsePromptStopReason as pt, formatPerfMetric as q, QueueProtocolError as qt, withConnectedSession as r, normalizeRuntimeSessionId as rt, clearDesiredConfigOption as s, sessionEventActivePath as st, LiveSessionCheckpoint as t, parseSessionRecord as tt, setDesiredModeId as u, extractSessionUpdateNotification as ut, recordClientOperation as v, promptToDisplayText as vt, persistSessionOptions as w, DEFAULT_AGENT_NAME as wt, trimConversationForRuntime as x, TimeoutError as xt, recordPromptSubmission as y, textPrompt as yt, isoNow$2 as z, OUTPUT_ERROR_ORIGINS as zt };
|
|
5444
5954
|
|
|
5445
|
-
//# sourceMappingURL=live-checkpoint-
|
|
5955
|
+
//# sourceMappingURL=live-checkpoint-BZrk9Mjz.js.map
|