@particle-academy/prism-acp 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/claude/session-store.d.ts +15 -0
- package/dist/claude/session-store.js +47 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,6 +172,23 @@ proceeds exactly as it did before. Calling a real session absent would refuse a
|
|
|
172
172
|
resume that would have worked, which is worse than the late error this replaces;
|
|
173
173
|
that error is still there as the backstop.
|
|
174
174
|
|
|
175
|
+
**It looks where the CLI looks, including `CLAUDE_CONFIG_DIR`.** The CLI resolves
|
|
176
|
+
its configuration home as that variable or, unset, `<home>/.claude`, and keeps
|
|
177
|
+
`projects` under whichever it picked — so the probe does the same. This is not a
|
|
178
|
+
detail: an installation that sets it does so because the stored subscription
|
|
179
|
+
credential lives there, which is exactly the installation whose resumes matter,
|
|
180
|
+
and reading the wrong store would report a real conversation `absent` and refuse
|
|
181
|
+
a resume that would have worked.
|
|
182
|
+
|
|
183
|
+
`childEnv` passes that variable through unchanged, so a driver spawned from this
|
|
184
|
+
process resolves the same store the probe read, by construction. Hand the driver
|
|
185
|
+
a `parentEnv` you built yourself and hand the same environment to the probe, or
|
|
186
|
+
name the store outright:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
const probeSession = (sessionId: string) => probeSessionStore(sessionId, { env: parentEnv });
|
|
190
|
+
```
|
|
191
|
+
|
|
175
192
|
`probeSession` is yours to supply because the answer belongs to the agent being
|
|
176
193
|
driven, not to ACP: `probeSessionStore` reads claude's session store, and a Codex
|
|
177
194
|
driver would resolve the same question through `thread/resume`. Omit it and
|
|
@@ -15,5 +15,20 @@ export interface SessionProbe {
|
|
|
15
15
|
export interface SessionStoreOptions {
|
|
16
16
|
/** Overridable so tests never read the developer's real sessions. */
|
|
17
17
|
readonly home?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The CLI's configuration home, naming the store directly. Outranks both
|
|
20
|
+
* {@link home} and the environment -- for a caller that knows where the store
|
|
21
|
+
* is, or that builds the driven CLI's environment itself.
|
|
22
|
+
*/
|
|
23
|
+
readonly configDir?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The environment the DRIVEN CLI will see, read for `CLAUDE_CONFIG_DIR`.
|
|
26
|
+
*
|
|
27
|
+
* Defaults to this process's own, which is correct by construction when the
|
|
28
|
+
* driver is spawned from here: `childEnv` passes `CLAUDE_CONFIG_DIR` through
|
|
29
|
+
* unchanged, so the probe and the child resolve one store. Hand the driver a
|
|
30
|
+
* `parentEnv` of your own and hand the same one here, or they will not.
|
|
31
|
+
*/
|
|
32
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
18
33
|
}
|
|
19
34
|
export declare function probeSessionStore(sessionId: string, options?: SessionStoreOptions): SessionProbe;
|
|
@@ -26,9 +26,51 @@
|
|
|
26
26
|
// every project, so looking for the FILE is enough and needs no slug at all.
|
|
27
27
|
import { readdirSync, statSync } from 'node:fs';
|
|
28
28
|
import { homedir } from 'node:os';
|
|
29
|
-
import { join } from 'node:path';
|
|
29
|
+
import { isAbsolute, join } from 'node:path';
|
|
30
30
|
/** The CLI accepts a UUID, or a session TITLE, and nothing else. */
|
|
31
31
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
32
|
+
/**
|
|
33
|
+
* WHERE THE STORE IS. The CLI resolves its configuration home as
|
|
34
|
+
* `CLAUDE_CONFIG_DIR` or, unset, `<home>/.claude`, and keeps `projects` under
|
|
35
|
+
* whichever it picked.
|
|
36
|
+
*
|
|
37
|
+
* Reading only the second was this probe's one serious bug (0.4.0). An
|
|
38
|
+
* installation that sets the variable -- Genie forwards it deliberately,
|
|
39
|
+
* because the stored subscription credential lives there and is what lets a
|
|
40
|
+
* child run with no API key at all -- had the probe read a store the CLI does
|
|
41
|
+
* not use, find nothing, and report `absent` for a conversation that exists.
|
|
42
|
+
* That refuses a resume that would have worked: the failure this whole
|
|
43
|
+
* three-state design exists to avoid, reintroduced by the check meant to
|
|
44
|
+
* prevent it.
|
|
45
|
+
*
|
|
46
|
+
* The variable names the configuration home ITSELF, so `projects` sits directly
|
|
47
|
+
* inside it and no `.claude` is appended. Resolving it as
|
|
48
|
+
* `home: dirname(CLAUDE_CONFIG_DIR)` instead would work only while the
|
|
49
|
+
* directory happens to be named `.claude`.
|
|
50
|
+
*/
|
|
51
|
+
function storeRoot(options) {
|
|
52
|
+
const configured = options.configDir ?? (options.env ?? process.env).CLAUDE_CONFIG_DIR;
|
|
53
|
+
const trimmed = configured?.trim();
|
|
54
|
+
// The CLI reads it with `||`, so an empty or blank value is no value. Taking
|
|
55
|
+
// it literally would resolve `projects` against this process's working
|
|
56
|
+
// directory.
|
|
57
|
+
if (trimmed === undefined || trimmed === '') {
|
|
58
|
+
return { root: join(options.home ?? homedir(), '.claude', 'projects') };
|
|
59
|
+
}
|
|
60
|
+
// The CLI refuses to run at all with a relative configuration home -- "the
|
|
61
|
+
// configuration home (CLAUDE_CONFIG_DIR) is not an absolute path" -- so there
|
|
62
|
+
// is no store to name, and resolving it against our own cwd would answer
|
|
63
|
+
// about a directory the CLI never looks in. It is also not a case for the
|
|
64
|
+
// `<home>/.claude` fallback: the CLI will not fall back either.
|
|
65
|
+
if (!isAbsolute(trimmed)) {
|
|
66
|
+
// The value itself stays out of the message. Every other `detail` here
|
|
67
|
+
// names the path it looked at, which is useful and harmless for a path we
|
|
68
|
+
// derived; this one is an environment value, and a detail string travels to
|
|
69
|
+
// the client.
|
|
70
|
+
return { reason: 'CLAUDE_CONFIG_DIR is not an absolute path, so the session store it names cannot be located' };
|
|
71
|
+
}
|
|
72
|
+
return { root: join(trimmed, 'projects') };
|
|
73
|
+
}
|
|
32
74
|
export function probeSessionStore(sessionId, options = {}) {
|
|
33
75
|
// A non-UUID is refused by the CLI outright -- verified against claude
|
|
34
76
|
// 2.1.292: "Provided value ... is not a UUID and does not match any session
|
|
@@ -45,7 +87,10 @@ export function probeSessionStore(sessionId, options = {}) {
|
|
|
45
87
|
detail: `${sessionId} is not a session id the CLI can resume: it is not a UUID.`,
|
|
46
88
|
};
|
|
47
89
|
}
|
|
48
|
-
const
|
|
90
|
+
const resolved = storeRoot(options);
|
|
91
|
+
if ('reason' in resolved)
|
|
92
|
+
return { existence: 'indeterminate', detail: resolved.reason };
|
|
93
|
+
const { root } = resolved;
|
|
49
94
|
let projects;
|
|
50
95
|
try {
|
|
51
96
|
if (!statSync(root).isDirectory()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@particle-academy/prism-acp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Speak the Agent Client Protocol to a coding-agent CLI the user has already authenticated. No API key, no third-party adapter.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|