acpx 0.9.0 → 0.10.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.
@@ -3,12 +3,12 @@ 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
9
  import { ClientSideConnection, PROTOCOL_VERSION } 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;
@@ -827,6 +827,104 @@ function resourceBlockDisplayText(block) {
827
827
  return "text" in block.resource && typeof block.resource.text === "string" ? block.resource.text : block.resource.uri;
828
828
  }
829
829
  //#endregion
830
+ //#region src/acp/jsonrpc.ts
831
+ function asRecord$4(value) {
832
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
833
+ return value;
834
+ }
835
+ function hasValidId(value) {
836
+ return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
837
+ }
838
+ function isErrorObject(value) {
839
+ const record = asRecord$4(value);
840
+ return !!record && typeof record.code === "number" && Number.isFinite(record.code) && typeof record.message === "string";
841
+ }
842
+ function hasResultOrError(value) {
843
+ const hasResult = Object.hasOwn(value, "result");
844
+ const hasError = Object.hasOwn(value, "error");
845
+ if (hasResult && hasError) return false;
846
+ if (!hasResult && !hasError) return false;
847
+ if (hasError && !isErrorObject(value.error)) return false;
848
+ return true;
849
+ }
850
+ function hasMethod(value) {
851
+ return typeof value.method === "string" && value.method.length > 0;
852
+ }
853
+ function isJsonRpcRequest(value) {
854
+ return hasMethod(value) && Object.hasOwn(value, "id") && hasValidId(value.id);
855
+ }
856
+ function isJsonRpcNotificationRecord(value) {
857
+ return hasMethod(value) && !Object.hasOwn(value, "id");
858
+ }
859
+ function isJsonRpcResponse(value) {
860
+ if (hasMethod(value) || !Object.hasOwn(value, "id") || !hasValidId(value.id)) return false;
861
+ return hasResultOrError(value);
862
+ }
863
+ function isAcpJsonRpcMessage(value) {
864
+ const record = asRecord$4(value);
865
+ if (!record || record.jsonrpc !== "2.0") return false;
866
+ return isJsonRpcNotificationRecord(record) || isJsonRpcRequest(record) || isJsonRpcResponse(record);
867
+ }
868
+ function isJsonRpcNotification(message) {
869
+ return Object.hasOwn(message, "method") && typeof message.method === "string" && !Object.hasOwn(message, "id");
870
+ }
871
+ function isSessionUpdateNotification(message) {
872
+ return isJsonRpcNotification(message) && message.method === "session/update";
873
+ }
874
+ function extractSessionUpdateNotification(message) {
875
+ if (!isSessionUpdateNotification(message)) return;
876
+ const params = asRecord$4(message.params);
877
+ if (!params) return;
878
+ const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
879
+ if (!sessionId) return;
880
+ const update = asRecord$4(params.update);
881
+ if (!update || typeof update.sessionUpdate !== "string") return;
882
+ return {
883
+ sessionId,
884
+ update
885
+ };
886
+ }
887
+ function parsePromptStopReason(message) {
888
+ if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
889
+ const record = asRecord$4(message.result);
890
+ if (!record) return;
891
+ return typeof record.stopReason === "string" ? record.stopReason : void 0;
892
+ }
893
+ function parseJsonRpcErrorMessage(message) {
894
+ if (!Object.hasOwn(message, "error")) return;
895
+ const errorRecord = asRecord$4(message.error);
896
+ if (!errorRecord || typeof errorRecord.message !== "string") return;
897
+ return errorRecord.message;
898
+ }
899
+ //#endregion
900
+ //#region src/session/event-log.ts
901
+ const DEFAULT_EVENT_SEGMENT_MAX_BYTES = 64 * 1024 * 1024;
902
+ function sessionBaseDir$1() {
903
+ return path.join(os.homedir(), ".acpx", "sessions");
904
+ }
905
+ function safeSessionId(sessionId) {
906
+ return encodeURIComponent(sessionId);
907
+ }
908
+ function sessionEventActivePath(sessionId) {
909
+ return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.ndjson`);
910
+ }
911
+ function sessionEventSegmentPath(sessionId, segment) {
912
+ return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.${segment}.ndjson`);
913
+ }
914
+ function sessionEventLockPath(sessionId) {
915
+ return path.join(sessionBaseDir$1(), `${safeSessionId(sessionId)}.stream.lock`);
916
+ }
917
+ function defaultSessionEventLog(sessionId) {
918
+ return {
919
+ active_path: sessionEventActivePath(sessionId),
920
+ segment_count: 5,
921
+ max_segment_bytes: DEFAULT_EVENT_SEGMENT_MAX_BYTES,
922
+ max_segments: 5,
923
+ last_write_at: void 0,
924
+ last_write_error: null
925
+ };
926
+ }
927
+ //#endregion
830
928
  //#region src/acp/agent-session-id.ts
831
929
  const AGENT_SESSION_ID_META_KEYS = ["agentSessionId", "sessionId"];
832
930
  function normalizeAgentSessionId(value) {
@@ -890,177 +988,18 @@ function serializeSessionRecordForDisk(record) {
890
988
  updated_at: canonical.updated_at,
891
989
  cumulative_token_usage: canonical.cumulative_token_usage,
892
990
  request_token_usage: canonical.request_token_usage,
893
- acpx: canonical.acpx
894
- };
895
- }
896
- //#endregion
897
- //#region src/perf-metrics.ts
898
- const counters = /* @__PURE__ */ new Map();
899
- const gauges = /* @__PURE__ */ new Map();
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
991
+ acpx: canonical.acpx,
992
+ imported_from: canonical.importedFrom ? {
993
+ record_id: canonical.importedFrom.recordId,
994
+ cwd_original: canonical.importedFrom.cwdOriginal,
995
+ exported_by: canonical.importedFrom.exportedBy,
996
+ exported_at: canonical.importedFrom.exportedAt
997
+ } : void 0
1059
998
  };
1060
999
  }
1061
1000
  //#endregion
1062
1001
  //#region src/session/persistence/parse.ts
1063
- function asRecord$4(value) {
1002
+ function asRecord$3(value) {
1064
1003
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
1065
1004
  return value;
1066
1005
  }
@@ -1072,7 +1011,7 @@ function isStringArray(value) {
1072
1011
  }
1073
1012
  function parseTokenUsage(raw) {
1074
1013
  if (raw === void 0 || raw === null) return;
1075
- const record = asRecord$4(raw);
1014
+ const record = asRecord$3(raw);
1076
1015
  if (!record) return null;
1077
1016
  const usage = {};
1078
1017
  for (const field of [
@@ -1093,7 +1032,7 @@ function isNonNegativeFiniteNumber(value) {
1093
1032
  }
1094
1033
  function parseRequestTokenUsage(raw) {
1095
1034
  if (raw === void 0 || raw === null) return;
1096
- const record = asRecord$4(raw);
1035
+ const record = asRecord$3(raw);
1097
1036
  if (!record) return null;
1098
1037
  const usage = {};
1099
1038
  for (const [key, value] of Object.entries(record)) {
@@ -1104,25 +1043,25 @@ function parseRequestTokenUsage(raw) {
1104
1043
  return usage;
1105
1044
  }
1106
1045
  function isSessionMessageImage(raw) {
1107
- const record = asRecord$4(raw);
1046
+ const record = asRecord$3(raw);
1108
1047
  if (!record || typeof record.source !== "string") return false;
1109
1048
  if (record.size === void 0 || record.size === null) return true;
1110
- const size = asRecord$4(record.size);
1049
+ const size = asRecord$3(record.size);
1111
1050
  return !!size && isFiniteNumber(size.width) && isFiniteNumber(size.height);
1112
1051
  }
1113
1052
  function isSessionMessageAudio(raw) {
1114
- const record = asRecord$4(raw);
1053
+ const record = asRecord$3(raw);
1115
1054
  return !!record && typeof record.source === "string" && typeof record.mime_type === "string";
1116
1055
  }
1117
1056
  function isFiniteNumber(value) {
1118
1057
  return typeof value === "number" && Number.isFinite(value);
1119
1058
  }
1120
1059
  function isUserContent(raw) {
1121
- const record = asRecord$4(raw);
1060
+ const record = asRecord$3(raw);
1122
1061
  if (!record) return false;
1123
1062
  if (typeof record.Text === "string") return true;
1124
1063
  if (record.Mention !== void 0) {
1125
- const mention = asRecord$4(record.Mention);
1064
+ const mention = asRecord$3(record.Mention);
1126
1065
  return !!mention && typeof mention.uri === "string" && typeof mention.content === "string";
1127
1066
  }
1128
1067
  if (record.Image !== void 0) return isSessionMessageImage(record.Image);
@@ -1130,7 +1069,7 @@ function isUserContent(raw) {
1130
1069
  return false;
1131
1070
  }
1132
1071
  function isToolUse(raw) {
1133
- const record = asRecord$4(raw);
1072
+ const record = asRecord$3(raw);
1134
1073
  return !!record && hasStringFields(record, [
1135
1074
  "id",
1136
1075
  "name",
@@ -1144,18 +1083,18 @@ function isOptionalString(value) {
1144
1083
  return value === void 0 || value === null || typeof value === "string";
1145
1084
  }
1146
1085
  function isToolResultContent(raw) {
1147
- const record = asRecord$4(raw);
1086
+ const record = asRecord$3(raw);
1148
1087
  if (!record) return false;
1149
1088
  if (typeof record.Text === "string") return true;
1150
1089
  if (record.Image !== void 0) return isSessionMessageImage(record.Image);
1151
1090
  return false;
1152
1091
  }
1153
1092
  function isToolResult(raw) {
1154
- const record = asRecord$4(raw);
1093
+ const record = asRecord$3(raw);
1155
1094
  return !!record && typeof record.tool_use_id === "string" && typeof record.tool_name === "string" && typeof record.is_error === "boolean" && isToolResultContent(record.content);
1156
1095
  }
1157
1096
  function isAgentContent(raw) {
1158
- const record = asRecord$4(raw);
1097
+ const record = asRecord$3(raw);
1159
1098
  if (!record) return false;
1160
1099
  if (typeof record.Text === "string") return true;
1161
1100
  if (record.Thinking !== void 0) return isThinkingContent(record.Thinking);
@@ -1164,21 +1103,21 @@ function isAgentContent(raw) {
1164
1103
  return false;
1165
1104
  }
1166
1105
  function isThinkingContent(raw) {
1167
- const thinking = asRecord$4(raw);
1106
+ const thinking = asRecord$3(raw);
1168
1107
  return !!thinking && typeof thinking.text === "string" && isOptionalString(thinking.signature);
1169
1108
  }
1170
1109
  function isUserMessage$1(raw) {
1171
- const record = asRecord$4(raw);
1110
+ const record = asRecord$3(raw);
1172
1111
  if (!record || record.User === void 0) return false;
1173
- const user = asRecord$4(record.User);
1112
+ const user = asRecord$3(record.User);
1174
1113
  return !!user && typeof user.id === "string" && Array.isArray(user.content) && user.content.every((entry) => isUserContent(entry));
1175
1114
  }
1176
1115
  function isAgentMessage$1(raw) {
1177
- const record = asRecord$4(raw);
1116
+ const record = asRecord$3(raw);
1178
1117
  if (!record || record.Agent === void 0) return false;
1179
- const agent = asRecord$4(record.Agent);
1118
+ const agent = asRecord$3(record.Agent);
1180
1119
  if (!agent || !Array.isArray(agent.content) || !agent.content.every(isAgentContent)) return false;
1181
- const toolResults = asRecord$4(agent.tool_results);
1120
+ const toolResults = asRecord$3(agent.tool_results);
1182
1121
  if (!toolResults) return false;
1183
1122
  return Object.values(toolResults).every(isToolResult);
1184
1123
  }
@@ -1209,7 +1148,7 @@ function hasValidConversationCore(record) {
1209
1148
  return Array.isArray(record.messages) && record.messages.every(isConversationMessage) && typeof record.updated_at === "string";
1210
1149
  }
1211
1150
  function parseAcpxState(raw) {
1212
- const record = asRecord$4(raw);
1151
+ const record = asRecord$3(raw);
1213
1152
  if (!record) return;
1214
1153
  const state = {};
1215
1154
  assignBooleanTrue(state, "reset_on_next_ensure", record.reset_on_next_ensure);
@@ -1230,7 +1169,7 @@ function assignStringState(state, key, value) {
1230
1169
  if (typeof value === "string") state[key] = value;
1231
1170
  }
1232
1171
  function assignDesiredConfigOptions(state, raw) {
1233
- const desiredConfigOptions = asRecord$4(raw);
1172
+ const desiredConfigOptions = asRecord$3(raw);
1234
1173
  if (!desiredConfigOptions) return;
1235
1174
  const parsed = Object.fromEntries(Object.entries(desiredConfigOptions).filter((entry) => {
1236
1175
  const [, value] = entry;
@@ -1239,7 +1178,7 @@ function assignDesiredConfigOptions(state, raw) {
1239
1178
  if (Object.keys(parsed).length > 0) state.desired_config_options = parsed;
1240
1179
  }
1241
1180
  function assignParsedSessionOptions(state, raw) {
1242
- const sessionOptions = asRecord$4(raw);
1181
+ const sessionOptions = asRecord$3(raw);
1243
1182
  if (!sessionOptions) return;
1244
1183
  const parsedSessionOptions = {};
1245
1184
  assignSessionOptionModel(parsedSessionOptions, sessionOptions.model);
@@ -1262,11 +1201,11 @@ function assignSessionOptionSystemPrompt(options, value) {
1262
1201
  options.system_prompt = value;
1263
1202
  return;
1264
1203
  }
1265
- const appendRecord = asRecord$4(value);
1204
+ const appendRecord = asRecord$3(value);
1266
1205
  if (appendRecord && typeof appendRecord.append === "string" && appendRecord.append.length > 0) options.system_prompt = { append: appendRecord.append };
1267
1206
  }
1268
1207
  function parseEventLog(raw, sessionId) {
1269
- const record = asRecord$4(raw);
1208
+ const record = asRecord$3(raw);
1270
1209
  if (!record || !hasValidEventLogCore(record)) return defaultSessionEventLog(sessionId);
1271
1210
  return {
1272
1211
  active_path: record.active_path,
@@ -1283,6 +1222,27 @@ function hasValidEventLogCore(record) {
1283
1222
  function isPositiveInteger(value) {
1284
1223
  return typeof value === "number" && Number.isInteger(value) && value > 0;
1285
1224
  }
1225
+ function parseImportedFrom(raw) {
1226
+ if (raw == null) return;
1227
+ const record = asRecord$3(raw);
1228
+ if (!record || typeof record.record_id !== "string" || typeof record.cwd_original !== "string" || typeof record.exported_by !== "string" || typeof record.exported_at !== "string") return null;
1229
+ return {
1230
+ recordId: record.record_id,
1231
+ cwdOriginal: record.cwd_original,
1232
+ exportedBy: record.exported_by,
1233
+ exportedAt: record.exported_at
1234
+ };
1235
+ }
1236
+ function parseSessionRecordMetadata(record) {
1237
+ const lastRequestId = normalizeOptionalString(record.last_request_id);
1238
+ if (lastRequestId === null) return null;
1239
+ const importedFrom = parseImportedFrom(record.imported_from);
1240
+ if (importedFrom === null) return null;
1241
+ return {
1242
+ lastRequestId,
1243
+ importedFrom
1244
+ };
1245
+ }
1286
1246
  function normalizeOptionalName(value) {
1287
1247
  if (value == null) return;
1288
1248
  if (typeof value !== "string") return null;
@@ -1315,7 +1275,7 @@ function normalizeOptionalSignal(value) {
1315
1275
  return Symbol("invalid");
1316
1276
  }
1317
1277
  function parseSessionRecord(raw) {
1318
- const record = asRecord$4(raw);
1278
+ const record = asRecord$3(raw);
1319
1279
  if (!record) return null;
1320
1280
  if (record.schema !== "acpx.session.v1") return null;
1321
1281
  const optionals = validSessionOptionals({
@@ -1334,8 +1294,8 @@ function parseSessionRecord(raw) {
1334
1294
  const conversation = parseConversationRecord(record);
1335
1295
  if (!conversation) return null;
1336
1296
  const eventLog = parseEventLog(record.event_log, record.acpx_record_id);
1337
- const lastRequestId = normalizeOptionalString(record.last_request_id);
1338
- if (lastRequestId === null) return null;
1297
+ const metadata = parseSessionRecordMetadata(record);
1298
+ if (!metadata) return null;
1339
1299
  return {
1340
1300
  schema: SESSION_RECORD_SCHEMA,
1341
1301
  acpxRecordId: record.acpx_record_id,
@@ -1347,7 +1307,7 @@ function parseSessionRecord(raw) {
1347
1307
  createdAt: record.created_at,
1348
1308
  lastUsedAt: record.last_used_at,
1349
1309
  lastSeq: record.last_seq,
1350
- lastRequestId,
1310
+ lastRequestId: metadata.lastRequestId,
1351
1311
  eventLog,
1352
1312
  closed: optionals.closed,
1353
1313
  closedAt: optionals.closedAt,
@@ -1359,13 +1319,14 @@ function parseSessionRecord(raw) {
1359
1319
  lastAgentExitAt: optionals.lastAgentExitAt,
1360
1320
  lastAgentDisconnectReason: optionals.lastAgentDisconnectReason,
1361
1321
  protocolVersion: typeof record.protocol_version === "number" ? record.protocol_version : void 0,
1362
- agentCapabilities: asRecord$4(record.agent_capabilities),
1322
+ agentCapabilities: asRecord$3(record.agent_capabilities),
1363
1323
  title: conversation.title,
1364
1324
  messages: conversation.messages,
1365
1325
  updated_at: conversation.updated_at,
1366
1326
  cumulative_token_usage: conversation.cumulative_token_usage,
1367
1327
  request_token_usage: conversation.request_token_usage,
1368
- acpx: parseAcpxState(record.acpx)
1328
+ acpx: parseAcpxState(record.acpx),
1329
+ importedFrom: metadata.importedFrom
1369
1330
  };
1370
1331
  }
1371
1332
  function hasValidSessionRecordCore(record) {
@@ -1398,14 +1359,151 @@ function hasInvalidExitStatus(options) {
1398
1359
  return typeof options.lastAgentExitCode === "symbol" || typeof options.lastAgentExitSignal === "symbol";
1399
1360
  }
1400
1361
  //#endregion
1362
+ //#region src/perf-metrics.ts
1363
+ const counters = /* @__PURE__ */ new Map();
1364
+ const gauges = /* @__PURE__ */ new Map();
1365
+ const timings = /* @__PURE__ */ new Map();
1366
+ function hrNow() {
1367
+ return process.hrtime.bigint();
1368
+ }
1369
+ function durationMs(start) {
1370
+ return Number(process.hrtime.bigint() - start) / 1e6;
1371
+ }
1372
+ function roundMetric(value) {
1373
+ return Number(value.toFixed(3));
1374
+ }
1375
+ function incrementPerfCounter(name, delta = 1) {
1376
+ counters.set(name, (counters.get(name) ?? 0) + delta);
1377
+ }
1378
+ function setPerfGauge(name, value) {
1379
+ gauges.set(name, value);
1380
+ }
1381
+ function recordPerfDuration(name, durationMsValue) {
1382
+ const next = timings.get(name) ?? {
1383
+ count: 0,
1384
+ totalMs: 0,
1385
+ maxMs: 0
1386
+ };
1387
+ next.count += 1;
1388
+ next.totalMs += durationMsValue;
1389
+ next.maxMs = Math.max(next.maxMs, durationMsValue);
1390
+ timings.set(name, next);
1391
+ }
1392
+ async function measurePerf(name, run) {
1393
+ const startedAt = hrNow();
1394
+ try {
1395
+ return await run();
1396
+ } finally {
1397
+ recordPerfDuration(name, durationMs(startedAt));
1398
+ }
1399
+ }
1400
+ function startPerfTimer(name) {
1401
+ const startedAt = hrNow();
1402
+ return () => {
1403
+ const elapsedMs = durationMs(startedAt);
1404
+ recordPerfDuration(name, elapsedMs);
1405
+ return elapsedMs;
1406
+ };
1407
+ }
1408
+ function getPerfMetricsSnapshot() {
1409
+ return {
1410
+ counters: Object.fromEntries(counters.entries()),
1411
+ gauges: Object.fromEntries(gauges.entries()),
1412
+ timings: Object.fromEntries([...timings.entries()].map(([name, bucket]) => [name, {
1413
+ count: bucket.count,
1414
+ totalMs: roundMetric(bucket.totalMs),
1415
+ maxMs: roundMetric(bucket.maxMs)
1416
+ }]))
1417
+ };
1418
+ }
1419
+ function resetPerfMetrics() {
1420
+ counters.clear();
1421
+ gauges.clear();
1422
+ timings.clear();
1423
+ }
1424
+ function formatPerfMetric(name, durationMsValue) {
1425
+ return `${name}=${roundMetric(durationMsValue)}ms`;
1426
+ }
1427
+ //#endregion
1428
+ //#region src/persisted-key-policy.ts
1429
+ const SNAKE_CASE_KEY = /^[a-z][a-z0-9_]*$/;
1430
+ const ZED_TAG_KEYS = new Set([
1431
+ "User",
1432
+ "Agent",
1433
+ "Resume",
1434
+ "Text",
1435
+ "Mention",
1436
+ "Image",
1437
+ "Audio",
1438
+ "Thinking",
1439
+ "RedactedThinking",
1440
+ "ToolUse"
1441
+ ]);
1442
+ const MAP_OBJECT_PATHS = new Set(["request_token_usage", "messages.Agent.tool_results"]);
1443
+ const OPAQUE_VALUE_PATHS = new Set([
1444
+ "agent_capabilities",
1445
+ "messages.Agent.content.ToolUse.input",
1446
+ "acpx.desired_config_options",
1447
+ "acpx.config_options"
1448
+ ]);
1449
+ function isRecord(value) {
1450
+ return !!value && typeof value === "object" && !Array.isArray(value);
1451
+ }
1452
+ function joinPath(path) {
1453
+ return path.join(".");
1454
+ }
1455
+ function isAllowedKey(path, key) {
1456
+ if (ZED_TAG_KEYS.has(key)) return true;
1457
+ return false;
1458
+ }
1459
+ function shouldSkipKeyRule(path) {
1460
+ return MAP_OBJECT_PATHS.has(joinPath(path));
1461
+ }
1462
+ function shouldSkipDescend(path) {
1463
+ return OPAQUE_VALUE_PATHS.has(joinPath(path)) || isToolResultOutputPath(path);
1464
+ }
1465
+ function isToolResultOutputTail(path, toolResultsIndex) {
1466
+ return toolResultsIndex !== -1 && toolResultsIndex + 2 === path.length - 1;
1467
+ }
1468
+ function isToolResultOutputPath(path) {
1469
+ if (path.length < 5 || path[path.length - 1] !== "output") return false;
1470
+ const toolResultsIndex = path.lastIndexOf("tool_results");
1471
+ if (!isToolResultOutputTail(path, toolResultsIndex)) return false;
1472
+ return path.slice(0, toolResultsIndex + 1).join(".") === "messages.Agent.tool_results";
1473
+ }
1474
+ function collectViolations(value, path, violations) {
1475
+ if (Array.isArray(value)) {
1476
+ for (const entry of value) collectViolations(entry, path, violations);
1477
+ return;
1478
+ }
1479
+ if (!isRecord(value)) return;
1480
+ const skipKeyRule = shouldSkipKeyRule(path);
1481
+ for (const [key, child] of Object.entries(value)) collectKeyViolation(child, key, path, skipKeyRule, violations);
1482
+ }
1483
+ function collectKeyViolation(child, key, path, skipKeyRule, violations) {
1484
+ if (!skipKeyRule && !SNAKE_CASE_KEY.test(key) && !isAllowedKey(path, key)) violations.push(`${joinPath(path)}.${key}`.replace(/^\./, ""));
1485
+ const childPath = [...path, key];
1486
+ if (!shouldSkipDescend(childPath)) collectViolations(child, childPath, violations);
1487
+ }
1488
+ function findPersistedKeyPolicyViolations(value) {
1489
+ const violations = [];
1490
+ collectViolations(value, [], violations);
1491
+ return violations;
1492
+ }
1493
+ function assertPersistedKeyPolicy(value) {
1494
+ const violations = findPersistedKeyPolicyViolations(value);
1495
+ if (violations.length === 0) return;
1496
+ throw new Error(`Persisted key policy violation (expected snake_case keys): ${violations.join(", ")}`);
1497
+ }
1498
+ //#endregion
1401
1499
  //#region src/session/persistence/index.ts
1402
1500
  const SESSION_INDEX_SCHEMA = "acpx.session-index.v1";
1403
- function asRecord$3(value) {
1501
+ function asRecord$2(value) {
1404
1502
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
1405
1503
  return value;
1406
1504
  }
1407
1505
  function parseIndexEntry(raw) {
1408
- const record = asRecord$3(raw);
1506
+ const record = asRecord$2(raw);
1409
1507
  if (!record) return;
1410
1508
  if (!hasRequiredIndexEntryFields(record)) return;
1411
1509
  if (record.name !== void 0 && typeof record.name !== "string") return;
@@ -1449,7 +1547,7 @@ async function readSessionIndex(sessionDir) {
1449
1547
  const filePath = sessionIndexPath(sessionDir);
1450
1548
  try {
1451
1549
  const payload = await fs$1.readFile(filePath, "utf8");
1452
- const record = asRecord$3(JSON.parse(payload));
1550
+ const record = asRecord$2(JSON.parse(payload));
1453
1551
  if (!record || record.schema !== SESSION_INDEX_SCHEMA || !Array.isArray(record.files)) return;
1454
1552
  const files = record.files.filter((entry) => typeof entry === "string");
1455
1553
  if (files.length !== record.files.length || !Array.isArray(record.entries)) return;
@@ -2642,76 +2740,6 @@ function buildAgentSpawnOptions(cwd, authCredentials) {
2642
2740
  };
2643
2741
  }
2644
2742
  //#endregion
2645
- //#region src/acp/jsonrpc.ts
2646
- function asRecord$2(value) {
2647
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2648
- return value;
2649
- }
2650
- function hasValidId(value) {
2651
- return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
2652
- }
2653
- function isErrorObject(value) {
2654
- const record = asRecord$2(value);
2655
- return !!record && typeof record.code === "number" && Number.isFinite(record.code) && typeof record.message === "string";
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");
2685
- }
2686
- function isSessionUpdateNotification(message) {
2687
- return isJsonRpcNotification(message) && message.method === "session/update";
2688
- }
2689
- function extractSessionUpdateNotification(message) {
2690
- if (!isSessionUpdateNotification(message)) return;
2691
- const params = asRecord$2(message.params);
2692
- if (!params) return;
2693
- const sessionId = typeof params.sessionId === "string" ? params.sessionId : null;
2694
- if (!sessionId) return;
2695
- const update = asRecord$2(params.update);
2696
- if (!update || typeof update.sessionUpdate !== "string") return;
2697
- return {
2698
- sessionId,
2699
- update
2700
- };
2701
- }
2702
- function parsePromptStopReason(message) {
2703
- if (!Object.hasOwn(message, "id") || !Object.hasOwn(message, "result")) return;
2704
- const record = asRecord$2(message.result);
2705
- if (!record) return;
2706
- return typeof record.stopReason === "string" ? record.stopReason : void 0;
2707
- }
2708
- function parseJsonRpcErrorMessage(message) {
2709
- if (!Object.hasOwn(message, "error")) return;
2710
- const errorRecord = asRecord$2(message.error);
2711
- if (!errorRecord || typeof errorRecord.message !== "string") return;
2712
- return errorRecord.message;
2713
- }
2714
- //#endregion
2715
2743
  //#region src/acp/session-control-errors.ts
2716
2744
  const SESSION_CONTROL_UNSUPPORTED_ACP_CODES = new Set([-32601, -32602]);
2717
2745
  function asRecord$1(value) {
@@ -5064,7 +5092,7 @@ function restoreOriginalSessionState(params) {
5064
5092
  async function connectAndLoadSession(options) {
5065
5093
  const record = options.record;
5066
5094
  const client = options.client;
5067
- const sameSessionOnly = requiresSameSession(options.resumePolicy);
5095
+ const sameSessionOnly = requiresSameSession(options.resumePolicy) || Boolean(record.importedFrom);
5068
5096
  const originalSessionId = record.acpSessionId;
5069
5097
  const originalAgentSessionId = record.agentSessionId;
5070
5098
  const desiredModeId = getDesiredModeId(record.acpx);
@@ -5440,6 +5468,6 @@ var LiveSessionCheckpoint = class {
5440
5468
  }
5441
5469
  };
5442
5470
  //#endregion
5443
- export { getPerfMetricsSnapshot as $, parsePromptStopReason as A, EXIT_CODES as At, normalizeName as B, QueueProtocolError as Bt, applyConversation as C, formatErrorMessage as Ct, extractSessionUpdateNotification as D, isAcpResourceNotFoundError as Dt, AcpClient as E, extractAcpError as Et, findSession as F, PERMISSION_MODES as Ft, DEFAULT_EVENT_SEGMENT_MAX_BYTES as G, resolveSessionRecord as H, findSessionByDirectoryWalk as I, PERMISSION_POLICY_ACTIONS as It, sessionEventActivePath as J, defaultSessionEventLog as K, isoNow$2 as L, SESSION_RECORD_SCHEMA as Lt, DEFAULT_HISTORY_LIMIT as M, OUTPUT_ERROR_CODES as Mt, absolutePath as N, OUTPUT_ERROR_ORIGINS as Nt, isAcpJsonRpcMessage as O, toAcpErrorPayload as Ot, findGitRepositoryRoot as P, OUTPUT_FORMATS as Pt, formatPerfMetric as Q, listSessions as R, AgentSpawnError as Rt, sessionOptionsFromRecord as S, exitCodeForOutputErrorCode as St, reconcileAgentSessionId as T, normalizeOutputError as Tt, writeSessionRecord as U, pruneSessions as V, parseSessionRecord as W, sessionEventSegmentPath as X, sessionEventLockPath as Y, assertPersistedKeyPolicy as Z, recordPromptSubmission as _, withTimeout as _t, applyRequestedModelIfAdvertised as a, startPerfTimer as at, mergeSessionOptions as b, normalizeAgentName$1 as bt, setDesiredConfigOption as c, PromptInputValidationError as ct, syncAdvertisedModelState as d, parsePromptSource as dt, incrementPerfCounter as et, applyConfigOptionsToRecord as f, promptToDisplayText as ft, recordClientOperation as g, withInterrupt as gt, createSessionConversation as h, TimeoutError as ht, connectAndLoadSession as i, setPerfGauge as it, permissionModeSatisfies as j, NON_INTERACTIVE_PERMISSION_POLICIES as jt, parseJsonRpcErrorMessage as k, AUTH_POLICIES as kt, setDesiredModeId as l, isPromptInput as lt, cloneSessionConversation as m, InterruptedError as mt, runPromptTurn as n, recordPerfDuration as nt, assertRequestedModelSupported as o, serializeSessionRecordForDisk as ot, cloneSessionAcpxState as p, textPrompt as pt, sessionBaseDir$1 as q, withConnectedSession as r, resetPerfMetrics as rt, setCurrentModelId as s, normalizeRuntimeSessionId as st, LiveSessionCheckpoint as t, measurePerf as tt, setDesiredModelId as u, mergePromptSourceWithText as ut, recordSessionUpdate as v, DEFAULT_AGENT_NAME as vt, applyLifecycleSnapshotToRecord as w, isRetryablePromptError as wt, persistSessionOptions as x, resolveAgentCommand as xt, trimConversationForRuntime as y, listBuiltInAgents as yt, listSessionsForAgent as z, QueueConnectionError as zt };
5471
+ export { defaultSessionEventLog as $, findGitRepositoryRoot as A, EXIT_CODES as At, assertPersistedKeyPolicy as B, QueueConnectionError as Bt, applyConversation as C, formatErrorMessage as Ct, permissionModeSatisfies as D, isAcpResourceNotFoundError as Dt, AcpClient as E, extractAcpError as Et, listSessionsForAgent as F, PERMISSION_MODES as Ft, recordPerfDuration as G, getPerfMetricsSnapshot as H, normalizeName as I, PERMISSION_POLICY_ACTIONS as It, startPerfTimer as J, resetPerfMetrics as K, pruneSessions as L, SESSION_RECORD_SCHEMA as Lt, findSessionByDirectoryWalk as M, OUTPUT_ERROR_CODES as Mt, isoNow$2 as N, OUTPUT_ERROR_ORIGINS as Nt, DEFAULT_HISTORY_LIMIT as O, toAcpErrorPayload as Ot, listSessions as P, OUTPUT_FORMATS as Pt, DEFAULT_EVENT_SEGMENT_MAX_BYTES as Q, resolveSessionRecord as R, AcpxOperationalError as Rt, sessionOptionsFromRecord as S, exitCodeForOutputErrorCode as St, reconcileAgentSessionId as T, normalizeOutputError as Tt, incrementPerfCounter as U, formatPerfMetric as V, QueueProtocolError as Vt, measurePerf as W, serializeSessionRecordForDisk as X, parseSessionRecord as Y, normalizeRuntimeSessionId as Z, recordPromptSubmission as _, withTimeout as _t, applyRequestedModelIfAdvertised as a, isAcpJsonRpcMessage as at, mergeSessionOptions as b, normalizeAgentName$1 as bt, setDesiredConfigOption as c, PromptInputValidationError as ct, syncAdvertisedModelState as d, parsePromptSource as dt, sessionBaseDir$1 as et, applyConfigOptionsToRecord as f, promptToDisplayText as ft, recordClientOperation as g, withInterrupt as gt, createSessionConversation as h, TimeoutError as ht, connectAndLoadSession as i, extractSessionUpdateNotification as it, findSession as j, NON_INTERACTIVE_PERMISSION_POLICIES as jt, absolutePath as k, AUTH_POLICIES as kt, setDesiredModeId as l, isPromptInput as lt, cloneSessionConversation as m, InterruptedError as mt, runPromptTurn as n, sessionEventLockPath as nt, assertRequestedModelSupported as o, parseJsonRpcErrorMessage as ot, cloneSessionAcpxState as p, textPrompt as pt, setPerfGauge as q, withConnectedSession as r, sessionEventSegmentPath as rt, setCurrentModelId as s, parsePromptStopReason as st, LiveSessionCheckpoint as t, sessionEventActivePath as tt, setDesiredModelId as u, mergePromptSourceWithText as ut, recordSessionUpdate as v, DEFAULT_AGENT_NAME as vt, applyLifecycleSnapshotToRecord as w, isRetryablePromptError as wt, persistSessionOptions as x, resolveAgentCommand as xt, trimConversationForRuntime as y, listBuiltInAgents as yt, writeSessionRecord as z, AgentSpawnError as zt };
5444
5472
 
5445
- //# sourceMappingURL=live-checkpoint-D5d-K9s1.js.map
5473
+ //# sourceMappingURL=live-checkpoint-CuFft_Nd.js.map