@agents-ensemble/core 0.21.0 → 0.22.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/assets/pi/pi-cli-env-wrapper.cmd +28 -0
- package/assets/pi/pi-cli-env-wrapper.sh +19 -0
- package/assets/pi/settings.json +4 -0
- package/dist/acp/acp-preset-bins.d.ts +3 -3
- package/dist/acp/acp-preset-bins.d.ts.map +1 -1
- package/dist/acp/acp-preset-bins.js +3 -3
- package/dist/acp/acp-preset-bins.js.map +1 -1
- package/dist/acp/index.d.ts +3 -1
- package/dist/acp/index.d.ts.map +1 -1
- package/dist/acp/index.js +2 -1
- package/dist/acp/index.js.map +1 -1
- package/dist/acp/pi-acp-wrapper.d.ts +3 -0
- package/dist/acp/pi-acp-wrapper.d.ts.map +1 -0
- package/dist/acp/pi-acp-wrapper.js +41 -0
- package/dist/acp/pi-acp-wrapper.js.map +1 -0
- package/dist/acp/pi-builtin-config.d.ts +10 -0
- package/dist/acp/pi-builtin-config.d.ts.map +1 -0
- package/dist/acp/pi-builtin-config.js +36 -0
- package/dist/acp/pi-builtin-config.js.map +1 -0
- package/dist/acp/pi-runtime.d.ts +23 -0
- package/dist/acp/pi-runtime.d.ts.map +1 -0
- package/dist/acp/pi-runtime.js +160 -0
- package/dist/acp/pi-runtime.js.map +1 -0
- package/dist/acp/pi-session-map.d.ts +36 -0
- package/dist/acp/pi-session-map.d.ts.map +1 -0
- package/dist/acp/pi-session-map.js +266 -0
- package/dist/acp/pi-session-map.js.map +1 -0
- package/dist/acp/resolve-acp-spawn.d.ts +9 -3
- package/dist/acp/resolve-acp-spawn.d.ts.map +1 -1
- package/dist/acp/resolve-acp-spawn.js +41 -9
- package/dist/acp/resolve-acp-spawn.js.map +1 -1
- package/dist/acp/resolve-bundled-acp-bin.d.ts +2 -2
- package/dist/acp/resolve-bundled-acp-bin.d.ts.map +1 -1
- package/dist/acp/resolve-bundled-acp-bin.js +4 -4
- package/dist/acp/resolve-bundled-acp-bin.js.map +1 -1
- package/dist/acp/validate-acp-preset-prerequisites.d.ts +1 -1
- package/dist/acp/validate-acp-preset-prerequisites.d.ts.map +1 -1
- package/dist/acp/validate-acp-preset-prerequisites.js +30 -10
- package/dist/acp/validate-acp-preset-prerequisites.js.map +1 -1
- package/dist/conductor/conductor-agent.d.ts +15 -3
- package/dist/conductor/conductor-agent.d.ts.map +1 -1
- package/dist/conductor/conductor-auth.d.ts +2 -3
- package/dist/conductor/conductor-auth.d.ts.map +1 -1
- package/dist/conductor/conductor-auth.js +5 -2
- package/dist/conductor/conductor-auth.js.map +1 -1
- package/dist/conductor/conductor-dispatch-lifecycle.d.ts +85 -0
- package/dist/conductor/conductor-dispatch-lifecycle.d.ts.map +1 -0
- package/dist/conductor/conductor-dispatch-lifecycle.js +190 -0
- package/dist/conductor/conductor-dispatch-lifecycle.js.map +1 -0
- package/dist/conductor/conductor-history.d.ts +6 -0
- package/dist/conductor/conductor-history.d.ts.map +1 -1
- package/dist/conductor/conductor-history.js +11 -6
- package/dist/conductor/conductor-history.js.map +1 -1
- package/dist/conductor/conductor-path-safety.d.ts +23 -0
- package/dist/conductor/conductor-path-safety.d.ts.map +1 -0
- package/dist/conductor/conductor-path-safety.js +319 -0
- package/dist/conductor/conductor-path-safety.js.map +1 -0
- package/dist/conductor/conductor-pi-auth.d.ts +57 -2
- package/dist/conductor/conductor-pi-auth.d.ts.map +1 -1
- package/dist/conductor/conductor-pi-auth.js +718 -40
- package/dist/conductor/conductor-pi-auth.js.map +1 -1
- package/dist/conductor/conductor-recovery-inbox.d.ts +73 -0
- package/dist/conductor/conductor-recovery-inbox.d.ts.map +1 -0
- package/dist/conductor/conductor-recovery-inbox.js +181 -0
- package/dist/conductor/conductor-recovery-inbox.js.map +1 -0
- package/dist/conductor/conductor-send-reconnect.d.ts +27 -5
- package/dist/conductor/conductor-send-reconnect.d.ts.map +1 -1
- package/dist/conductor/conductor-send-reconnect.js +88 -39
- package/dist/conductor/conductor-send-reconnect.js.map +1 -1
- package/dist/conductor/conductor-session-driver.d.ts +32 -7
- package/dist/conductor/conductor-session-driver.d.ts.map +1 -1
- package/dist/conductor/conductor-session-driver.js +474 -233
- package/dist/conductor/conductor-session-driver.js.map +1 -1
- package/dist/conductor/conductor-session.d.ts +2 -0
- package/dist/conductor/conductor-session.d.ts.map +1 -1
- package/dist/conductor/conductor-session.js +404 -156
- package/dist/conductor/conductor-session.js.map +1 -1
- package/dist/conductor/conductor-setup.d.ts +30 -0
- package/dist/conductor/conductor-setup.d.ts.map +1 -0
- package/dist/conductor/conductor-setup.js +243 -0
- package/dist/conductor/conductor-setup.js.map +1 -0
- package/dist/conductor/cursor-bootstrap-state.d.ts +9 -0
- package/dist/conductor/cursor-bootstrap-state.d.ts.map +1 -0
- package/dist/conductor/cursor-bootstrap-state.js +11 -0
- package/dist/conductor/cursor-bootstrap-state.js.map +1 -0
- package/dist/conductor/cursor-run-snapshots.d.ts +30 -0
- package/dist/conductor/cursor-run-snapshots.d.ts.map +1 -0
- package/dist/conductor/cursor-run-snapshots.js +120 -0
- package/dist/conductor/cursor-run-snapshots.js.map +1 -0
- package/dist/conductor/index.d.ts +15 -6
- package/dist/conductor/index.d.ts.map +1 -1
- package/dist/conductor/index.js +8 -4
- package/dist/conductor/index.js.map +1 -1
- package/dist/conductor/operator-command.d.ts +1 -1
- package/dist/conductor/operator-command.js +1 -1
- package/dist/conductor/operator-command.js.map +1 -1
- package/dist/conductor/operator-input-binding.d.ts +12 -0
- package/dist/conductor/operator-input-binding.d.ts.map +1 -1
- package/dist/conductor/pi-auth-recovery-snapshot-store.d.ts +4 -1
- package/dist/conductor/pi-auth-recovery-snapshot-store.d.ts.map +1 -1
- package/dist/conductor/pi-auth-recovery-snapshot-store.js +4 -1
- package/dist/conductor/pi-auth-recovery-snapshot-store.js.map +1 -1
- package/dist/conductor/pi-conductor-agent.d.ts +6 -3
- package/dist/conductor/pi-conductor-agent.d.ts.map +1 -1
- package/dist/conductor/pi-conductor-agent.js +272 -53
- package/dist/conductor/pi-conductor-agent.js.map +1 -1
- package/dist/conductor/pi-conductor-session-file.d.ts +13 -0
- package/dist/conductor/pi-conductor-session-file.d.ts.map +1 -0
- package/dist/conductor/pi-conductor-session-file.js +21 -0
- package/dist/conductor/pi-conductor-session-file.js.map +1 -0
- package/dist/conductor/pi-resource-loader.d.ts +6 -0
- package/dist/conductor/pi-resource-loader.d.ts.map +1 -1
- package/dist/conductor/pi-resource-loader.js +26 -12
- package/dist/conductor/pi-resource-loader.js.map +1 -1
- package/dist/conductor/pi-user-root-lock.d.ts +8 -0
- package/dist/conductor/pi-user-root-lock.d.ts.map +1 -0
- package/dist/conductor/pi-user-root-lock.js +53 -0
- package/dist/conductor/pi-user-root-lock.js.map +1 -0
- package/dist/conductor/recovery-inbox-command.d.ts +29 -0
- package/dist/conductor/recovery-inbox-command.d.ts.map +1 -0
- package/dist/conductor/recovery-inbox-command.js +248 -0
- package/dist/conductor/recovery-inbox-command.js.map +1 -0
- package/dist/conductor/session/events/session-event.d.ts +15 -2
- package/dist/conductor/session/events/session-event.d.ts.map +1 -1
- package/dist/conductor/session/events/session-log-event-groups.d.ts +1 -1
- package/dist/conductor/session/events/session-log-event-groups.d.ts.map +1 -1
- package/dist/conductor/session/events/session-log-event-groups.js +1 -0
- package/dist/conductor/session/events/session-log-event-groups.js.map +1 -1
- package/dist/conductor/session/events/session-log-event.d.ts +4 -0
- package/dist/conductor/session/events/session-log-event.d.ts.map +1 -1
- package/dist/conductor/session/format-session-event.js +25 -4
- package/dist/conductor/session/format-session-event.js.map +1 -1
- package/dist/conductor/session/select-dispatch-batch.d.ts +2 -0
- package/dist/conductor/session/select-dispatch-batch.d.ts.map +1 -1
- package/dist/conductor/session/select-dispatch-batch.js +31 -2
- package/dist/conductor/session/select-dispatch-batch.js.map +1 -1
- package/dist/conductor/session/session-event-queue.d.ts +5 -0
- package/dist/conductor/session/session-event-queue.d.ts.map +1 -1
- package/dist/conductor/session/session-event-queue.js +41 -4
- package/dist/conductor/session/session-event-queue.js.map +1 -1
- package/dist/conductor/session/session-event.d.ts +1 -1
- package/dist/conductor/session/session-event.d.ts.map +1 -1
- package/dist/conductor/session-policy.d.ts.map +1 -1
- package/dist/conductor/session-policy.js +4 -2
- package/dist/conductor/session-policy.js.map +1 -1
- package/dist/conductor/submit-operator-input.d.ts +3 -0
- package/dist/conductor/submit-operator-input.d.ts.map +1 -1
- package/dist/conductor/submit-operator-input.js +21 -0
- package/dist/conductor/submit-operator-input.js.map +1 -1
- package/dist/config/deep-merge.d.ts.map +1 -1
- package/dist/config/deep-merge.js +13 -3
- package/dist/config/deep-merge.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/doctor-settings.d.ts +59 -0
- package/dist/config/doctor-settings.d.ts.map +1 -0
- package/dist/config/doctor-settings.js +343 -0
- package/dist/config/doctor-settings.js.map +1 -0
- package/dist/config/ensemble-config-digest.d.ts +2 -4
- package/dist/config/ensemble-config-digest.d.ts.map +1 -1
- package/dist/config/ensemble-config-digest.js +2 -4
- package/dist/config/ensemble-config-digest.js.map +1 -1
- package/dist/config/index.d.ts +3 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +1 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/load-ensemble-config.d.ts.map +1 -1
- package/dist/config/load-ensemble-config.js +8 -1
- package/dist/config/load-ensemble-config.js.map +1 -1
- package/dist/config/parse-config.d.ts.map +1 -1
- package/dist/config/parse-config.js +34 -14
- package/dist/config/parse-config.js.map +1 -1
- package/dist/config/resolve-settings.d.ts +3 -1
- package/dist/config/resolve-settings.d.ts.map +1 -1
- package/dist/config/resolve-settings.js +7 -4
- package/dist/config/resolve-settings.js.map +1 -1
- package/dist/config/types.d.ts +14 -5
- package/dist/config/types.d.ts.map +1 -1
- package/dist/dispatch/attach-worker.js +1 -1
- package/dist/dispatch/attach-worker.js.map +1 -1
- package/dist/dispatch/worker-acp-session.d.ts +21 -3
- package/dist/dispatch/worker-acp-session.d.ts.map +1 -1
- package/dist/dispatch/worker-acp-session.js +50 -13
- package/dist/dispatch/worker-acp-session.js.map +1 -1
- package/dist/ensemble-paths.d.ts +19 -19
- package/dist/ensemble-paths.d.ts.map +1 -1
- package/dist/ensemble-paths.js +40 -22
- package/dist/ensemble-paths.js.map +1 -1
- package/dist/issue/resolve-issue-url.d.ts +4 -0
- package/dist/issue/resolve-issue-url.d.ts.map +1 -1
- package/dist/issue/resolve-issue-url.js +2 -2
- package/dist/issue/resolve-issue-url.js.map +1 -1
- package/dist/operator-runtime.d.ts +17 -4
- package/dist/operator-runtime.d.ts.map +1 -1
- package/dist/operator-runtime.js +12 -3
- package/dist/operator-runtime.js.map +1 -1
- package/dist/profile/types.d.ts +4 -0
- package/dist/profile/types.d.ts.map +1 -1
- package/dist/profile/types.js +29 -12
- package/dist/profile/types.js.map +1 -1
- package/dist/representation/escape-terminal-control-characters.d.ts +9 -0
- package/dist/representation/escape-terminal-control-characters.d.ts.map +1 -0
- package/dist/representation/escape-terminal-control-characters.js +34 -0
- package/dist/representation/escape-terminal-control-characters.js.map +1 -0
- package/dist/representation/index.d.ts +1 -0
- package/dist/representation/index.d.ts.map +1 -1
- package/dist/representation/index.js +1 -0
- package/dist/representation/index.js.map +1 -1
- package/dist/runtime/types.d.ts +9 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/runtime/worker-runtime.d.ts +14 -1
- package/dist/runtime/worker-runtime.d.ts.map +1 -1
- package/dist/runtime/worker-runtime.js +197 -50
- package/dist/runtime/worker-runtime.js.map +1 -1
- package/dist/runtime/worker-session.d.ts +2 -1
- package/dist/runtime/worker-session.d.ts.map +1 -1
- package/dist/runtime/worker-session.js +1 -0
- package/dist/runtime/worker-session.js.map +1 -1
- package/dist/session/export-session-bundle.d.ts +77 -0
- package/dist/session/export-session-bundle.d.ts.map +1 -0
- package/dist/session/export-session-bundle.js +640 -0
- package/dist/session/export-session-bundle.js.map +1 -0
- package/dist/session/index.d.ts +4 -3
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session/index.js +2 -1
- package/dist/session/index.js.map +1 -1
- package/dist/session/open-question-control.d.ts +9 -0
- package/dist/session/open-question-control.d.ts.map +1 -1
- package/dist/session/open-question-control.js +26 -1
- package/dist/session/open-question-control.js.map +1 -1
- package/dist/session/open-questions-store.d.ts +1 -1
- package/dist/session/open-questions-store.d.ts.map +1 -1
- package/dist/session/open-questions-store.js +3 -6
- package/dist/session/open-questions-store.js.map +1 -1
- package/dist/session/persisted-file.d.ts +5 -0
- package/dist/session/persisted-file.d.ts.map +1 -0
- package/dist/session/persisted-file.js +56 -0
- package/dist/session/persisted-file.js.map +1 -0
- package/dist/session/session-resume-store.d.ts +77 -10
- package/dist/session/session-resume-store.d.ts.map +1 -1
- package/dist/session/session-resume-store.js +346 -83
- package/dist/session/session-resume-store.js.map +1 -1
- package/dist/session/session-snapshot-paths.d.ts +7 -1
- package/dist/session/session-snapshot-paths.d.ts.map +1 -1
- package/dist/session/session-snapshot-paths.js +29 -26
- package/dist/session/session-snapshot-paths.js.map +1 -1
- package/dist/session/session-state-store.d.ts +2 -2
- package/dist/session/session-state-store.d.ts.map +1 -1
- package/dist/session/session-state-store.js +5 -16
- package/dist/session/session-state-store.js.map +1 -1
- package/dist/session/tool-call-snapshot-history.d.ts +12 -1
- package/dist/session/tool-call-snapshot-history.d.ts.map +1 -1
- package/dist/session/tool-call-snapshot-history.js +75 -70
- package/dist/session/tool-call-snapshot-history.js.map +1 -1
- package/dist/session/worker-session-history.d.ts +13 -1
- package/dist/session/worker-session-history.d.ts.map +1 -1
- package/dist/session/worker-session-history.js +171 -50
- package/dist/session/worker-session-history.js.map +1 -1
- package/dist/workspace/index.d.ts +1 -1
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/worktree/worktree.d.ts +6 -1
- package/dist/worktree/worktree.d.ts.map +1 -1
- package/dist/worktree/worktree.js +123 -2
- package/dist/worktree/worktree.js.map +1 -1
- package/package.json +9 -4
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { chmod, mkdir, readdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { dirname, join
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
3
|
import { computeAcpSpawnEnvDigest, } from '../acp/resolve-acp-spawn.js';
|
|
4
|
+
import { parseIssueUrl } from '../issue/issue-ref.js';
|
|
4
5
|
import { resolveEnsemblePaths } from '../ensemble-paths.js';
|
|
6
|
+
import { resolveUserEnsembleRoot } from '../resolve-user-ensemble-root.js';
|
|
5
7
|
export const SESSION_RESUME_STORE_VERSION = 1;
|
|
6
8
|
/** @deprecated Use SESSION_RESUME_STORE_VERSION; supported for one release only. */
|
|
7
9
|
export const SESSION_SIDECAR_VERSION = SESSION_RESUME_STORE_VERSION;
|
|
@@ -28,6 +30,13 @@ export class SessionResumeStoreNotFoundError extends Error {
|
|
|
28
30
|
this.name = 'SessionResumeStoreNotFoundError';
|
|
29
31
|
}
|
|
30
32
|
}
|
|
33
|
+
class InvalidSessionResumeStoreDataError extends Error {
|
|
34
|
+
constructor(path, reason, cause) {
|
|
35
|
+
const detail = cause instanceof Error ? `: ${cause.message}` : '';
|
|
36
|
+
super(`Invalid session resume store at ${path} (${reason})${detail}`);
|
|
37
|
+
this.name = 'InvalidSessionResumeStoreDataError';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
31
40
|
/** @deprecated Use SessionResumeStoreNotFoundError; supported for one release only. */
|
|
32
41
|
export class SessionSidecarNotFoundError extends SessionResumeStoreNotFoundError {
|
|
33
42
|
code = 'SESSION_SIDECAR_NOT_FOUND';
|
|
@@ -36,8 +45,12 @@ export class SessionSidecarNotFoundError extends SessionResumeStoreNotFoundError
|
|
|
36
45
|
this.name = 'SessionSidecarNotFoundError';
|
|
37
46
|
}
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
const SESSION_RESUME_STORE_PAGE_MAX_LIMIT = 500;
|
|
49
|
+
const SESSION_RESUME_STORE_PAGE_CURSOR_VERSION = 1;
|
|
50
|
+
/** Return the canonical user-store directory for an Issue repository. */
|
|
51
|
+
export function sessionResumeStoreDir(input) {
|
|
52
|
+
return resolveEnsemblePaths({ repoRoot: input.repoRoot })
|
|
53
|
+
.canonicalRepoSessionsDir(input.issueUrl);
|
|
41
54
|
}
|
|
42
55
|
export function legacySessionResumeStoreDir(repoRoot) {
|
|
43
56
|
return resolveEnsemblePaths({ repoRoot }).legacySessionsDir;
|
|
@@ -47,30 +60,47 @@ export function legacySessionResumeStorePath(options) {
|
|
|
47
60
|
repoRoot: options.repoRoot,
|
|
48
61
|
}).legacySessionResumeStorePath(options.conductorAgentId);
|
|
49
62
|
}
|
|
63
|
+
/** Return the canonical user-store path; an Issue URL is required for identity. */
|
|
50
64
|
export function sessionResumeStorePath(options) {
|
|
51
|
-
return
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
return canonicalSessionResumeStorePath(options);
|
|
66
|
+
}
|
|
67
|
+
export function canonicalSessionResumeStorePath(input) {
|
|
68
|
+
return resolveEnsemblePaths({ repoRoot: input.repoRoot })
|
|
69
|
+
.canonicalSessionResumeStorePath(input.issueUrl, input.conductorAgentId);
|
|
54
70
|
}
|
|
55
71
|
export async function loadSessionResumeStore(path) {
|
|
56
72
|
const loaded = await readSessionResumeStore(path);
|
|
57
73
|
return loaded?.resumeStore;
|
|
58
74
|
}
|
|
59
75
|
async function readSessionResumeStore(path) {
|
|
76
|
+
let raw;
|
|
60
77
|
try {
|
|
61
|
-
|
|
62
|
-
const value = JSON.parse(raw);
|
|
63
|
-
return {
|
|
64
|
-
resumeStore: parseSessionResumeStore(value),
|
|
65
|
-
hasLegacyAcpSpawnEnv: containsLegacyAcpSpawnEnv(value),
|
|
66
|
-
rawValue: value,
|
|
67
|
-
};
|
|
78
|
+
raw = await readFile(path, 'utf8');
|
|
68
79
|
}
|
|
69
80
|
catch (error) {
|
|
70
81
|
if (isEnoent(error))
|
|
71
82
|
return undefined;
|
|
72
83
|
throw error;
|
|
73
84
|
}
|
|
85
|
+
let value;
|
|
86
|
+
try {
|
|
87
|
+
value = JSON.parse(raw);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
throw new InvalidSessionResumeStoreDataError(path, 'invalid JSON', error);
|
|
91
|
+
}
|
|
92
|
+
let resumeStore;
|
|
93
|
+
try {
|
|
94
|
+
resumeStore = parseSessionResumeStore(value);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
throw new InvalidSessionResumeStoreDataError(path, 'invalid schema', error);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
resumeStore,
|
|
101
|
+
hasLegacyAcpSpawnEnv: containsLegacyAcpSpawnEnv(value),
|
|
102
|
+
rawValue: value,
|
|
103
|
+
};
|
|
74
104
|
}
|
|
75
105
|
/** `resumeAgentId` 指定時に SessionResumeStore が無い場合は起動失敗とする。 */
|
|
76
106
|
export async function requireSessionResumeStoreForResume(input) {
|
|
@@ -78,33 +108,36 @@ export async function requireSessionResumeStoreForResume(input) {
|
|
|
78
108
|
return located.resumeStore;
|
|
79
109
|
}
|
|
80
110
|
export async function requireLocatedSessionResumeStoreForResume(input) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
if (typeof input.issueUrl !== 'string' ||
|
|
112
|
+
!input.issueUrl.trim()) {
|
|
113
|
+
throw new Error('issueUrl is required to resume a session.');
|
|
114
|
+
}
|
|
115
|
+
const locations = await listLocatedSessionResumeStoresPage(input.repoRoot, {
|
|
116
|
+
issueUrl: input.issueUrl,
|
|
117
|
+
conductorAgentId: input.conductorAgentId,
|
|
118
|
+
}, { onDuplicate: input.onDuplicate });
|
|
119
|
+
const located = locations.sessions[0];
|
|
85
120
|
if (!located) {
|
|
86
|
-
const path =
|
|
121
|
+
const path = canonicalSessionResumeStorePath(input);
|
|
87
122
|
throw new SessionResumeStoreNotFoundError(input.conductorAgentId, path);
|
|
88
123
|
}
|
|
89
124
|
const loaded = await readSessionResumeStore(located.path);
|
|
90
125
|
if (!loaded) {
|
|
91
126
|
throw new SessionResumeStoreNotFoundError(input.conductorAgentId, located.path);
|
|
92
127
|
}
|
|
128
|
+
const persistedValue = loaded.hasLegacyAcpSpawnEnv
|
|
129
|
+
? redactLegacyAcpSpawnEnv(loaded.rawValue, loaded.resumeStore)
|
|
130
|
+
: loaded.resumeStore;
|
|
131
|
+
const resumeStore = parseSessionResumeStore(persistedValue);
|
|
93
132
|
if (loaded.hasLegacyAcpSpawnEnv) {
|
|
94
|
-
// Scrub
|
|
95
|
-
//
|
|
96
|
-
await writeSessionResumeStoreFile(located.path,
|
|
133
|
+
// Scrub canonical data before resume validation can reject the session
|
|
134
|
+
// and bypass its normal first write.
|
|
135
|
+
await writeSessionResumeStoreFile(located.path, persistedValue);
|
|
97
136
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
resumeStore: loaded.resumeStore,
|
|
103
|
-
path: migratedPath,
|
|
104
|
-
location: 'user-path-hash',
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
return { ...located, resumeStore: loaded.resumeStore };
|
|
137
|
+
return {
|
|
138
|
+
...located,
|
|
139
|
+
resumeStore,
|
|
140
|
+
};
|
|
108
141
|
}
|
|
109
142
|
/** @deprecated Use requireSessionResumeStoreForResume; supported for one release only. */
|
|
110
143
|
export async function requireSessionSidecarForResume(input) {
|
|
@@ -153,13 +186,142 @@ export async function listLocatedSessionResumeStores(repoRoot, options = {}) {
|
|
|
153
186
|
return listSessionResumeStoreLocations(repoRoot, options);
|
|
154
187
|
}
|
|
155
188
|
export async function findSessionResumeStore(repoRoot, conductorAgentId, options = {}) {
|
|
156
|
-
|
|
189
|
+
const matches = (await listLocatedSessionResumeStores(repoRoot, options)).filter((candidate) => candidate.resumeStore.conductorAgentId === conductorAgentId);
|
|
190
|
+
if (matches.length > 1) {
|
|
191
|
+
throw new Error(`Session ${conductorAgentId} is ambiguous across saved Issues; use findSessionResumeStoreBySelector with githubRepo or issueUrl.`);
|
|
192
|
+
}
|
|
193
|
+
return matches[0];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* List local sessions using canonical GitHub repository identity.
|
|
197
|
+
*
|
|
198
|
+
* With no limit this returns every matching local store. Supplying a limit
|
|
199
|
+
* enables keyset pagination; no GitHub API repository discovery is performed.
|
|
200
|
+
*/
|
|
201
|
+
export async function listLocatedSessionResumeStoresPage(repoRoot, query = {}, options = {}) {
|
|
202
|
+
const scope = resolveSessionResumeStoreScope(query);
|
|
203
|
+
const stores = (await listSessionResumeStoreLocations(repoRoot, options))
|
|
204
|
+
.filter(({ resumeStore }) => {
|
|
205
|
+
const identity = sessionIssueIdentity(resumeStore.issueUrl);
|
|
206
|
+
return ((!scope.githubRepo || identity.repository === scope.githubRepo) &&
|
|
207
|
+
(!scope.issueKey || identity.key === scope.issueKey) &&
|
|
208
|
+
(!query.conductorAgentId ||
|
|
209
|
+
resumeStore.conductorAgentId === query.conductorAgentId));
|
|
210
|
+
})
|
|
211
|
+
.sort(compareLocatedSessionResumeStores);
|
|
212
|
+
const limit = query.limit === undefined
|
|
213
|
+
? query.cursor === undefined
|
|
214
|
+
? undefined
|
|
215
|
+
: SESSION_RESUME_STORE_PAGE_MAX_LIMIT
|
|
216
|
+
: validateSessionResumeStorePageLimit(query.limit);
|
|
217
|
+
if (limit === undefined) {
|
|
218
|
+
return { sessions: stores, total: stores.length };
|
|
219
|
+
}
|
|
220
|
+
const cursor = query.cursor === undefined
|
|
221
|
+
? undefined
|
|
222
|
+
: decodeSessionResumeStoreCursor(query.cursor);
|
|
223
|
+
const remaining = cursor
|
|
224
|
+
? stores.filter((store) => compareLocatedSessionResumeStoreToCursor(store, cursor) > 0)
|
|
225
|
+
: stores;
|
|
226
|
+
const sessions = remaining.slice(0, limit);
|
|
227
|
+
const next = remaining.length > sessions.length ? sessions.at(-1) : undefined;
|
|
228
|
+
return {
|
|
229
|
+
sessions,
|
|
230
|
+
total: stores.length,
|
|
231
|
+
...(next ? { nextCursor: encodeSessionResumeStoreCursor(next) } : {}),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* List user-scoped canonical slug stores.
|
|
236
|
+
*
|
|
237
|
+
* This is intended for repository-independent operator tools. It does not
|
|
238
|
+
* inspect path-hash or checkout-local session data, or call GitHub.
|
|
239
|
+
*/
|
|
240
|
+
export async function listUserLocatedSessionResumeStoresPage(query = {}, options = {}) {
|
|
241
|
+
const userRoot = options.userEnsembleRoot ?? resolveUserEnsembleRoot();
|
|
242
|
+
const scope = resolveSessionResumeStoreScope(query);
|
|
243
|
+
const stores = (await listSessionResumeStoreLocations(userRoot, {
|
|
244
|
+
onDuplicate: options.onDuplicate,
|
|
245
|
+
userEnsembleRoot: userRoot,
|
|
246
|
+
}))
|
|
247
|
+
.filter(({ resumeStore }) => {
|
|
248
|
+
const identity = sessionIssueIdentity(resumeStore.issueUrl);
|
|
249
|
+
return ((!scope.githubRepo || identity.repository === scope.githubRepo) &&
|
|
250
|
+
(!scope.issueKey || identity.key === scope.issueKey) &&
|
|
251
|
+
(!query.conductorAgentId ||
|
|
252
|
+
resumeStore.conductorAgentId === query.conductorAgentId));
|
|
253
|
+
})
|
|
254
|
+
.sort(compareLocatedSessionResumeStores);
|
|
255
|
+
return paginateLocatedSessionResumeStores(stores, query);
|
|
256
|
+
}
|
|
257
|
+
export async function findUserSessionResumeStore(conductorAgentId, options = {}) {
|
|
258
|
+
const id = conductorAgentId.trim();
|
|
259
|
+
if (!id) {
|
|
260
|
+
throw new Error('conductorAgentId must be a non-empty string.');
|
|
261
|
+
}
|
|
262
|
+
const page = await listUserLocatedSessionResumeStoresPage({ conductorAgentId: id }, options);
|
|
263
|
+
if (page.sessions.length > 1) {
|
|
264
|
+
throw new Error(`Session ${id} is ambiguous across saved Issues; use a repository or Issue selector.`);
|
|
265
|
+
}
|
|
266
|
+
return page.sessions[0];
|
|
267
|
+
}
|
|
268
|
+
function paginateLocatedSessionResumeStores(stores, query) {
|
|
269
|
+
const limit = query.limit === undefined
|
|
270
|
+
? query.cursor === undefined
|
|
271
|
+
? undefined
|
|
272
|
+
: SESSION_RESUME_STORE_PAGE_MAX_LIMIT
|
|
273
|
+
: validateSessionResumeStorePageLimit(query.limit);
|
|
274
|
+
if (limit === undefined) {
|
|
275
|
+
return { sessions: stores, total: stores.length };
|
|
276
|
+
}
|
|
277
|
+
const cursor = query.cursor === undefined
|
|
278
|
+
? undefined
|
|
279
|
+
: decodeSessionResumeStoreCursor(query.cursor);
|
|
280
|
+
const remaining = cursor
|
|
281
|
+
? stores.filter((store) => compareLocatedSessionResumeStoreToCursor(store, cursor) > 0)
|
|
282
|
+
: stores;
|
|
283
|
+
const sessions = remaining.slice(0, limit);
|
|
284
|
+
const next = remaining.length > sessions.length ? sessions.at(-1) : undefined;
|
|
285
|
+
return {
|
|
286
|
+
sessions,
|
|
287
|
+
total: stores.length,
|
|
288
|
+
...(next ? { nextCursor: encodeSessionResumeStoreCursor(next) } : {}),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
export async function listSessionResumeStoresPage(repoRoot, query = {}, options = {}) {
|
|
292
|
+
const page = await listLocatedSessionResumeStoresPage(repoRoot, query, options);
|
|
293
|
+
return {
|
|
294
|
+
sessions: page.sessions.map(({ resumeStore }) => resumeStore),
|
|
295
|
+
total: page.total,
|
|
296
|
+
...(page.nextCursor ? { nextCursor: page.nextCursor } : {}),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/** Read a session only inside an explicit GitHub repository / Issue scope. */
|
|
300
|
+
export async function findSessionResumeStoreBySelector(repoRoot, selector, options = {}) {
|
|
301
|
+
const conductorAgentId = selector.conductorAgentId.trim();
|
|
302
|
+
if (!conductorAgentId) {
|
|
303
|
+
throw new Error('conductorAgentId must be a non-empty string.');
|
|
304
|
+
}
|
|
305
|
+
const scope = resolveSessionResumeStoreScope(selector);
|
|
306
|
+
if (!scope.githubRepo && !scope.issueKey) {
|
|
307
|
+
throw new Error('Repository scope is required to read a session; pass --github-repo or --issue-url.');
|
|
308
|
+
}
|
|
309
|
+
const page = await listLocatedSessionResumeStoresPage(repoRoot, { ...selector, conductorAgentId }, options);
|
|
310
|
+
if (page.sessions.length > 1) {
|
|
311
|
+
throw new Error(`Session ${conductorAgentId} is ambiguous in this repository; specify --issue-url.`);
|
|
312
|
+
}
|
|
313
|
+
return page.sessions[0];
|
|
157
314
|
}
|
|
158
315
|
async function listSessionResumeStoreLocations(repoRoot, options = {}) {
|
|
159
|
-
const paths = resolveEnsemblePaths({
|
|
316
|
+
const paths = resolveEnsemblePaths({
|
|
317
|
+
repoRoot,
|
|
318
|
+
...(options.userEnsembleRoot
|
|
319
|
+
? { userEnsembleRoot: options.userEnsembleRoot }
|
|
320
|
+
: {}),
|
|
321
|
+
});
|
|
160
322
|
const candidates = [];
|
|
161
323
|
const visited = new Set();
|
|
162
|
-
const readDirectory = async (dir, location, slugRoot) => {
|
|
324
|
+
const readDirectory = async (dir, location, { slugRoot, skipMalformedStoreData = false, } = {}) => {
|
|
163
325
|
let entries;
|
|
164
326
|
try {
|
|
165
327
|
entries = await readdir(dir, { withFileTypes: true });
|
|
@@ -176,23 +338,29 @@ async function listSessionResumeStoreLocations(repoRoot, options = {}) {
|
|
|
176
338
|
if (visited.has(path))
|
|
177
339
|
continue;
|
|
178
340
|
visited.add(path);
|
|
179
|
-
|
|
341
|
+
let resumeStore;
|
|
342
|
+
try {
|
|
343
|
+
resumeStore = await loadSessionResumeStore(path);
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
if (skipMalformedStoreData &&
|
|
347
|
+
error instanceof InvalidSessionResumeStoreDataError) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
180
352
|
if (!resumeStore)
|
|
181
353
|
continue;
|
|
182
354
|
if (slugRoot &&
|
|
183
|
-
(
|
|
184
|
-
canonicalSessionDirectory(paths, resumeStore.issueUrl) !== slugRoot)) {
|
|
355
|
+
canonicalSessionDirectory(paths, resumeStore.issueUrl) !== slugRoot) {
|
|
185
356
|
continue;
|
|
186
357
|
}
|
|
187
358
|
candidates.push({ resumeStore, path, location });
|
|
188
359
|
}
|
|
189
360
|
};
|
|
190
|
-
// The current path-hash and project-local directories are bounded and remain
|
|
191
|
-
// the discovery roots for legacy data.
|
|
192
|
-
await readDirectory(paths.sessionsDir, 'user-path-hash');
|
|
193
|
-
await readDirectory(paths.legacySessionsDir, 'project-legacy');
|
|
194
361
|
// Slug stores are keyed by GitHub repository identity. Search only the
|
|
195
|
-
// host/owner/repository depth
|
|
362
|
+
// host/owner/repository depth; repoRoot is execution context, not a lookup
|
|
363
|
+
// filter for canonical records.
|
|
196
364
|
let hosts;
|
|
197
365
|
try {
|
|
198
366
|
hosts = await readdir(paths.sessionsRoot, { withFileTypes: true });
|
|
@@ -235,30 +403,29 @@ async function listSessionResumeStoreLocations(repoRoot, options = {}) {
|
|
|
235
403
|
if (!repository.isDirectory())
|
|
236
404
|
continue;
|
|
237
405
|
const slugRoot = join(paths.sessionsRoot, host.name, owner.name, repository.name);
|
|
238
|
-
await readDirectory(slugRoot, 'canonical-slug', slugRoot);
|
|
406
|
+
await readDirectory(slugRoot, 'canonical-slug', { slugRoot });
|
|
239
407
|
}
|
|
240
408
|
}
|
|
241
409
|
}
|
|
242
|
-
const
|
|
410
|
+
const bySession = new Map();
|
|
243
411
|
for (const candidate of candidates) {
|
|
244
|
-
const
|
|
412
|
+
const sessionKey = JSON.stringify([
|
|
413
|
+
sessionIssueIdentity(candidate.resumeStore.issueUrl).key,
|
|
414
|
+
candidate.resumeStore.conductorAgentId,
|
|
415
|
+
]);
|
|
416
|
+
const matching = bySession.get(sessionKey) ?? [];
|
|
245
417
|
matching.push(candidate);
|
|
246
|
-
|
|
418
|
+
bySession.set(sessionKey, matching);
|
|
247
419
|
}
|
|
248
|
-
const locationPriority = {
|
|
249
|
-
'canonical-slug': 0,
|
|
250
|
-
'user-path-hash': 1,
|
|
251
|
-
'project-legacy': 2,
|
|
252
|
-
};
|
|
253
420
|
const selected = [];
|
|
254
|
-
for (const
|
|
255
|
-
matches.sort((left, right) =>
|
|
256
|
-
right.resumeStore.updatedAt - left.resumeStore.updatedAt);
|
|
421
|
+
for (const matches of bySession.values()) {
|
|
422
|
+
matches.sort((left, right) => right.resumeStore.updatedAt - left.resumeStore.updatedAt);
|
|
257
423
|
const chosen = matches[0];
|
|
258
424
|
selected.push(chosen);
|
|
259
425
|
if (matches.length > 1) {
|
|
260
426
|
options.onDuplicate?.({
|
|
261
|
-
conductorAgentId,
|
|
427
|
+
conductorAgentId: chosen.resumeStore.conductorAgentId,
|
|
428
|
+
issueUrl: chosen.resumeStore.issueUrl,
|
|
262
429
|
selectedPath: chosen.path,
|
|
263
430
|
selectedLocation: chosen.location,
|
|
264
431
|
candidates: matches.map((candidate) => ({
|
|
@@ -279,6 +446,102 @@ function canonicalSessionDirectory(paths, issueUrl) {
|
|
|
279
446
|
return undefined;
|
|
280
447
|
}
|
|
281
448
|
}
|
|
449
|
+
function resolveSessionResumeStoreScope(input) {
|
|
450
|
+
const githubRepo = input.githubRepo === undefined
|
|
451
|
+
? undefined
|
|
452
|
+
: normalizeGitHubRepo(input.githubRepo);
|
|
453
|
+
let issue;
|
|
454
|
+
if (input.issueUrl !== undefined) {
|
|
455
|
+
parseIssueUrl(input.issueUrl);
|
|
456
|
+
issue = sessionIssueIdentity(input.issueUrl);
|
|
457
|
+
}
|
|
458
|
+
if (githubRepo !== undefined &&
|
|
459
|
+
issue !== undefined &&
|
|
460
|
+
githubRepo !== issue.repository) {
|
|
461
|
+
throw new Error('--github-repo and --issue-url refer to different GitHub repositories.');
|
|
462
|
+
}
|
|
463
|
+
return {
|
|
464
|
+
...(githubRepo ?? issue?.repository
|
|
465
|
+
? { githubRepo: githubRepo ?? issue?.repository }
|
|
466
|
+
: {}),
|
|
467
|
+
...(issue ? { issueKey: issue.key } : {}),
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
function normalizeGitHubRepo(value) {
|
|
471
|
+
const segments = value.trim().split('/');
|
|
472
|
+
if (segments.length !== 2 ||
|
|
473
|
+
segments.some((segment) => !segment ||
|
|
474
|
+
segment === '.' ||
|
|
475
|
+
segment === '..' ||
|
|
476
|
+
!/^[A-Za-z0-9_.-]+$/u.test(segment))) {
|
|
477
|
+
throw new Error(`Invalid GitHub repository "${value}" (expected <owner>/<repo>).`);
|
|
478
|
+
}
|
|
479
|
+
return `${segments[0].toLowerCase()}/${segments[1].replace(/\.git$/iu, '').toLowerCase()}`;
|
|
480
|
+
}
|
|
481
|
+
function sessionIssueIdentity(issueUrl) {
|
|
482
|
+
try {
|
|
483
|
+
const issue = parseIssueUrl(issueUrl);
|
|
484
|
+
const repository = `${issue.owner.toLowerCase()}/${issue.repo.toLowerCase()}`;
|
|
485
|
+
return {
|
|
486
|
+
repository,
|
|
487
|
+
key: `${repository}#${issue.number}`,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
return { repository: '', key: `invalid:${issueUrl.toLowerCase()}` };
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function compareLocatedSessionResumeStores(left, right) {
|
|
495
|
+
return (right.resumeStore.updatedAt - left.resumeStore.updatedAt ||
|
|
496
|
+
sessionIssueIdentity(left.resumeStore.issueUrl).key.localeCompare(sessionIssueIdentity(right.resumeStore.issueUrl).key) ||
|
|
497
|
+
left.resumeStore.conductorAgentId.localeCompare(right.resumeStore.conductorAgentId));
|
|
498
|
+
}
|
|
499
|
+
function compareLocatedSessionResumeStoreToCursor(store, cursor) {
|
|
500
|
+
const resumeStore = store.resumeStore;
|
|
501
|
+
return (cursor.updatedAt - resumeStore.updatedAt ||
|
|
502
|
+
sessionIssueIdentity(resumeStore.issueUrl).key.localeCompare(cursor.issueKey) ||
|
|
503
|
+
resumeStore.conductorAgentId.localeCompare(cursor.conductorAgentId));
|
|
504
|
+
}
|
|
505
|
+
function validateSessionResumeStorePageLimit(limit) {
|
|
506
|
+
if (!Number.isSafeInteger(limit) ||
|
|
507
|
+
limit < 1 ||
|
|
508
|
+
limit > SESSION_RESUME_STORE_PAGE_MAX_LIMIT) {
|
|
509
|
+
throw new Error(`limit must be an integer between 1 and ${SESSION_RESUME_STORE_PAGE_MAX_LIMIT}.`);
|
|
510
|
+
}
|
|
511
|
+
return limit;
|
|
512
|
+
}
|
|
513
|
+
function encodeSessionResumeStoreCursor(store) {
|
|
514
|
+
const cursor = {
|
|
515
|
+
version: SESSION_RESUME_STORE_PAGE_CURSOR_VERSION,
|
|
516
|
+
updatedAt: store.resumeStore.updatedAt,
|
|
517
|
+
issueKey: sessionIssueIdentity(store.resumeStore.issueUrl).key,
|
|
518
|
+
conductorAgentId: store.resumeStore.conductorAgentId,
|
|
519
|
+
};
|
|
520
|
+
return Buffer.from(JSON.stringify(cursor), 'utf8').toString('base64url');
|
|
521
|
+
}
|
|
522
|
+
function decodeSessionResumeStoreCursor(value) {
|
|
523
|
+
let cursor;
|
|
524
|
+
try {
|
|
525
|
+
cursor = JSON.parse(Buffer.from(value, 'base64url').toString('utf8'));
|
|
526
|
+
}
|
|
527
|
+
catch {
|
|
528
|
+
throw new Error('Invalid session list cursor.');
|
|
529
|
+
}
|
|
530
|
+
if (!isRecord(cursor) ||
|
|
531
|
+
cursor.version !== SESSION_RESUME_STORE_PAGE_CURSOR_VERSION ||
|
|
532
|
+
typeof cursor.updatedAt !== 'number' ||
|
|
533
|
+
!Number.isFinite(cursor.updatedAt) ||
|
|
534
|
+
typeof cursor.issueKey !== 'string' ||
|
|
535
|
+
typeof cursor.conductorAgentId !== 'string') {
|
|
536
|
+
throw new Error('Invalid session list cursor.');
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
version: cursor.version,
|
|
540
|
+
updatedAt: cursor.updatedAt,
|
|
541
|
+
issueKey: cursor.issueKey,
|
|
542
|
+
conductorAgentId: cursor.conductorAgentId,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
282
545
|
/** @deprecated Use listSessionResumeStores; supported for one release only. */
|
|
283
546
|
export const listSessionSidecars = listSessionResumeStores;
|
|
284
547
|
/**
|
|
@@ -286,8 +549,12 @@ export const listSessionSidecars = listSessionResumeStores;
|
|
|
286
549
|
* updatedAt が最も新しい SessionResumeStore を返す。
|
|
287
550
|
*/
|
|
288
551
|
export async function findLatestSessionResumeStoreForIssue(input) {
|
|
289
|
-
const
|
|
290
|
-
|
|
552
|
+
const issueKey = sessionIssueIdentity(input.issueUrl).key;
|
|
553
|
+
const matches = (await listLocatedSessionResumeStoresPage(input.repoRoot, {
|
|
554
|
+
issueUrl: input.issueUrl,
|
|
555
|
+
})).sessions
|
|
556
|
+
.map(({ resumeStore }) => resumeStore)
|
|
557
|
+
.filter((resumeStore) => sessionIssueIdentity(resumeStore.issueUrl).key === issueKey &&
|
|
291
558
|
(resumeStore.harnessPhase ??
|
|
292
559
|
harnessPhaseFromLegacyLifecycle(resumeStore.lifecycle)) !== 'exit');
|
|
293
560
|
if (matches.length === 0)
|
|
@@ -311,31 +578,10 @@ export function assertSessionResumeStoreMatches(resumeStore, input) {
|
|
|
311
578
|
if (resumeStore.conductorAgentId !== input.conductorAgentId) {
|
|
312
579
|
throw new Error(`Session resume store conductorAgentId mismatch: ${resumeStore.conductorAgentId} !== ${input.conductorAgentId}`);
|
|
313
580
|
}
|
|
314
|
-
if (resumeStore.issueUrl !==
|
|
581
|
+
if (sessionIssueIdentity(resumeStore.issueUrl).key !==
|
|
582
|
+
sessionIssueIdentity(input.issueUrl).key) {
|
|
315
583
|
throw new Error(`Session resume store issueUrl mismatch: ${resumeStore.issueUrl} !== ${input.issueUrl}`);
|
|
316
584
|
}
|
|
317
|
-
if (resumeStore.repoRoot !== input.repoRoot) {
|
|
318
|
-
throw new Error(`Session resume store repoRoot mismatch: ${resumeStore.repoRoot} !== ${input.repoRoot}`);
|
|
319
|
-
}
|
|
320
|
-
if (input.conductorBackend !== undefined &&
|
|
321
|
-
(resumeStore.conductorBackend ?? 'cursor') !== input.conductorBackend) {
|
|
322
|
-
throw new Error(`Session resume store conductorBackend mismatch: ${resumeStore.conductorBackend ?? 'cursor'} !== ${input.conductorBackend}`);
|
|
323
|
-
}
|
|
324
|
-
if (input.mcpConfigDigest !== undefined) {
|
|
325
|
-
if (resumeStore.mcpConfigDigest === undefined) {
|
|
326
|
-
if (input.mcpConfigConfigured) {
|
|
327
|
-
throw new Error('Session resume store MCP config digest missing; cannot safely resume with MCP configuration.');
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
else if (resumeStore.mcpConfigDigest !== input.mcpConfigDigest) {
|
|
331
|
-
throw new Error(`Session resume store MCP config digest mismatch: ${resumeStore.mcpConfigDigest} !== ${input.mcpConfigDigest}`);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
if (input.ensembleConfigDigest !== undefined &&
|
|
335
|
-
resumeStore.ensembleConfigDigest !== undefined &&
|
|
336
|
-
resumeStore.ensembleConfigDigest !== input.ensembleConfigDigest) {
|
|
337
|
-
throw new Error(`Session resume store ensemble config digest mismatch: ${resumeStore.ensembleConfigDigest} !== ${input.ensembleConfigDigest}`);
|
|
338
|
-
}
|
|
339
585
|
}
|
|
340
586
|
/** @deprecated Use assertSessionResumeStoreMatches; supported for one release only. */
|
|
341
587
|
export const assertSessionSidecarMatches = assertSessionResumeStoreMatches;
|
|
@@ -416,6 +662,10 @@ function parseSessionResumeStore(value) {
|
|
|
416
662
|
record.cursorBootstrapRunId.trim() === '')) {
|
|
417
663
|
throw new Error('Invalid session resume store: cursorBootstrapRunId');
|
|
418
664
|
}
|
|
665
|
+
if (record.cursorBootstrapComplete !== undefined &&
|
|
666
|
+
typeof record.cursorBootstrapComplete !== 'boolean') {
|
|
667
|
+
throw new Error('Invalid session resume store: cursorBootstrapComplete');
|
|
668
|
+
}
|
|
419
669
|
if (record.lifecycle !== undefined &&
|
|
420
670
|
record.lifecycle !== 'closed' &&
|
|
421
671
|
record.lifecycle !== 'exited' &&
|
|
@@ -471,6 +721,9 @@ function parseSessionResumeStore(value) {
|
|
|
471
721
|
...(typeof record.cursorBootstrapRunId === 'string'
|
|
472
722
|
? { cursorBootstrapRunId: record.cursorBootstrapRunId }
|
|
473
723
|
: {}),
|
|
724
|
+
...(typeof record.cursorBootstrapComplete === 'boolean'
|
|
725
|
+
? { cursorBootstrapComplete: record.cursorBootstrapComplete }
|
|
726
|
+
: {}),
|
|
474
727
|
...(typeof record.lazyWorkers === 'boolean'
|
|
475
728
|
? { lazyWorkers: record.lazyWorkers }
|
|
476
729
|
: {}),
|
|
@@ -528,6 +781,9 @@ function parseConductorBackend(value) {
|
|
|
528
781
|
}
|
|
529
782
|
throw new Error('Invalid session resume store: conductorBackend');
|
|
530
783
|
}
|
|
784
|
+
function isRecord(value) {
|
|
785
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
786
|
+
}
|
|
531
787
|
function isEnoent(error) {
|
|
532
788
|
return (typeof error === 'object' &&
|
|
533
789
|
error !== null &&
|
|
@@ -588,11 +844,17 @@ function parseAcpSpawnFingerprint(raw, workerName) {
|
|
|
588
844
|
if (!Array.isArray(record.args) || record.args.some((arg) => typeof arg !== 'string')) {
|
|
589
845
|
throw new Error(`Invalid session resume store worker acpSpawn args: ${workerName}`);
|
|
590
846
|
}
|
|
591
|
-
|
|
847
|
+
let piRuntime = undefined;
|
|
848
|
+
if (record.piRuntime === 'builtin') {
|
|
849
|
+
piRuntime = 'builtin';
|
|
850
|
+
}
|
|
851
|
+
else if (record.piRuntime !== undefined &&
|
|
852
|
+
record.piRuntime !== 'byo') {
|
|
853
|
+
throw new Error(`Invalid session resume store worker acpSpawn piRuntime: ${workerName}`);
|
|
854
|
+
}
|
|
592
855
|
let modelTag;
|
|
593
856
|
if (record.modelTag !== undefined) {
|
|
594
|
-
if (typeof record.modelTag !== 'string'
|
|
595
|
-
!validModelTags.includes(record.modelTag)) {
|
|
857
|
+
if (typeof record.modelTag !== 'string') {
|
|
596
858
|
throw new Error(`Invalid session resume store worker acpSpawn modelTag: ${workerName}`);
|
|
597
859
|
}
|
|
598
860
|
modelTag = record.modelTag;
|
|
@@ -657,6 +919,7 @@ function parseAcpSpawnFingerprint(raw, workerName) {
|
|
|
657
919
|
preset: record.preset,
|
|
658
920
|
command: record.command,
|
|
659
921
|
args: record.args,
|
|
922
|
+
...(piRuntime !== undefined ? { piRuntime } : {}),
|
|
660
923
|
...(modelTag !== undefined ? { modelTag } : {}),
|
|
661
924
|
...(model !== undefined ? { model } : {}),
|
|
662
925
|
...(reasoning !== undefined ? { reasoning } : {}),
|