@khorsheed/dsh-local-agent-claude-code 0.1.0-rc.6

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.
Files changed (38) hide show
  1. package/README.en.md +120 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +120 -0
  4. package/cordis.patch.yml +42 -0
  5. package/lib/client.js +1065 -0
  6. package/lib/index.js +3771 -0
  7. package/lib/invariant.js +31 -0
  8. package/lib/types/claude-cli-provider.d.ts +389 -0
  9. package/lib/types/claude-cli-provider.js +1509 -0
  10. package/lib/types/client/SettingsCard.d.ts +78 -0
  11. package/lib/types/client/SettingsCard.js +198 -0
  12. package/lib/types/client/icons.d.ts +18 -0
  13. package/lib/types/client/icons.js +2 -0
  14. package/lib/types/client/index.d.ts +36 -0
  15. package/lib/types/client/index.js +87 -0
  16. package/lib/types/client/locales.d.ts +64 -0
  17. package/lib/types/client/locales.js +55 -0
  18. package/lib/types/control-requests.d.ts +15 -0
  19. package/lib/types/control-requests.js +62 -0
  20. package/lib/types/index.d.ts +81 -0
  21. package/lib/types/index.js +300 -0
  22. package/lib/types/invariant.d.ts +16 -0
  23. package/lib/types/invariant.js +33 -0
  24. package/lib/types/live-driver.d.ts +172 -0
  25. package/lib/types/live-driver.js +1057 -0
  26. package/lib/types/live-switch.d.ts +67 -0
  27. package/lib/types/live-switch.js +85 -0
  28. package/lib/types/model-broker.d.ts +131 -0
  29. package/lib/types/model-broker.js +248 -0
  30. package/lib/types/model-catalog.d.ts +27 -0
  31. package/lib/types/model-catalog.js +118 -0
  32. package/lib/types/provision.d.ts +63 -0
  33. package/lib/types/provision.js +132 -0
  34. package/lib/types/records.d.ts +120 -0
  35. package/lib/types/records.js +687 -0
  36. package/locale/en.json +6 -0
  37. package/locale/zh.json +6 -0
  38. package/package.json +106 -0
@@ -0,0 +1,118 @@
1
+ import { dirname, join } from 'node:path';
2
+ import { StringDecoder } from 'node:string_decoder';
3
+ import { ModelDirectoryCache, delegationEnv, modelDirectoryContextKey } from '@khorsheed/dsh-local-agent';
4
+ import { ClaudeControlRequests } from './control-requests.js';
5
+ /** Initialize's native picker vocabulary, not an account entitlement claim. */
6
+ export function claudeDirectory(result) {
7
+ const models = result['models'];
8
+ if (models === undefined)
9
+ return { entries: [], complete: false, customInput: true, unsupported: true, reason: 'This Claude CLI does not expose a native model directory' };
10
+ if (!Array.isArray(models))
11
+ throw new Error('Claude initialize returned an invalid model directory');
12
+ const entries = new Map();
13
+ for (const candidate of models) {
14
+ if (typeof candidate !== 'object' || candidate === null)
15
+ continue;
16
+ const model = candidate;
17
+ const value = typeof model['value'] === 'string' ? model['value'].trim() : '';
18
+ if (value === '' || entries.has(value))
19
+ continue;
20
+ const levels = model['supportedEffortLevels'];
21
+ const options = Array.isArray(levels) ? levels.flatMap(level => typeof level === 'string' && level !== '' ? [{ value: level, label: level }] : []) : undefined;
22
+ entries.set(value, {
23
+ value, label: typeof model['displayName'] === 'string' ? model['displayName'] : value, source: 'native',
24
+ ...typeof model['resolvedModel'] === 'string' ? { resolvedModel: model['resolvedModel'] } : {},
25
+ ...typeof model['description'] === 'string' ? { description: model['description'] } : {},
26
+ ...options === undefined ? {} : { reasoning: { options } },
27
+ });
28
+ }
29
+ return { entries: [...entries.values()], complete: true, customInput: true, ...entries.has('default') ? { defaultModel: 'default' } : {} };
30
+ }
31
+ /** Scoped native control probe. Never syncs credentials, writes settings or submits a user turn. */
32
+ export class ClaudeModelCatalog {
33
+ deps;
34
+ cache;
35
+ constructor(deps) {
36
+ this.deps = deps;
37
+ this.cache = new ModelDirectoryCache({
38
+ load: (key, signal) => {
39
+ const context = JSON.parse(key);
40
+ return this.probe(context.homeDir, context.cwd, signal);
41
+ },
42
+ ...deps.ttlMs === undefined ? {} : { ttlMs: deps.ttlMs },
43
+ onError: error => deps.warn?.(`Claude model discovery failed: ${error instanceof Error ? error.message : String(error)}`),
44
+ });
45
+ }
46
+ key(homeDir, cwd = homeDir) {
47
+ const files = [join(homeDir, 'settings.json'), join(homeDir, '.credentials.json'), join(homeDir, '.claude.json')];
48
+ for (let directory = cwd;; directory = dirname(directory)) {
49
+ files.push(join(directory, '.claude', 'settings.json'), join(directory, '.claude', 'settings.local.json'));
50
+ if (dirname(directory) === directory)
51
+ break;
52
+ }
53
+ return modelDirectoryContextKey({ provider: 'claude-code', homeDir, cwd, cli: [this.deps.cli ?? 'claude'], files, ...this.deps.baseUrl === undefined ? {} : { endpoint: this.deps.baseUrl } });
54
+ }
55
+ read(homeDir, cwd) { return this.cache.read(this.key(homeDir, cwd)); }
56
+ refresh(homeDir, cwd) { return this.cache.refresh(this.key(homeDir, cwd)); }
57
+ follow(homeDir, cwd, signal) { return this.cache.follow(() => this.key(homeDir, cwd), signal); }
58
+ invalidate(homeDir, cwd) { this.cache.invalidate(this.key(homeDir, cwd)); }
59
+ dispose() { this.cache.dispose(); }
60
+ async probe(homeDir, cwd, signal) {
61
+ const child = this.deps.spawn({
62
+ argv: [this.deps.cli ?? 'claude', '-p', '--verbose', '--input-format', 'stream-json', '--output-format', 'stream-json',
63
+ '--no-session-persistence', '--settings', '{"disableAllHooks":true}', '--strict-mcp-config', '--mcp-config', '{"mcpServers":{}}', '--'],
64
+ cwd, stdio: { stdin: 'pipe', stdout: 'pipe', stderr: 'pipe' }, graceMs: 2_000,
65
+ env: delegationEnv({ CLAUDE_CONFIG_DIR: homeDir, ...this.deps.baseUrl === undefined ? {} : { ANTHROPIC_BASE_URL: this.deps.baseUrl } }),
66
+ });
67
+ const controls = new ClaudeControlRequests(frame => {
68
+ if (child.stdin === undefined)
69
+ throw new Error('Claude discovery stdin is unavailable');
70
+ child.stdin.write(JSON.stringify(frame) + '\n');
71
+ }, this.deps.timeoutMs ?? 5_000);
72
+ const decoder = new StringDecoder('utf8');
73
+ let buffer = '';
74
+ child.stdout?.on('data', (chunk) => {
75
+ buffer += decoder.write(chunk);
76
+ if (buffer.length > 1_048_576) {
77
+ controls.close();
78
+ child.terminate();
79
+ return;
80
+ }
81
+ let index;
82
+ while ((index = buffer.indexOf('\n')) >= 0) {
83
+ const line = buffer.slice(0, index);
84
+ buffer = buffer.slice(index + 1);
85
+ let event;
86
+ try {
87
+ event = JSON.parse(line);
88
+ }
89
+ catch {
90
+ continue;
91
+ }
92
+ if (controls.accept(event))
93
+ continue;
94
+ if (event['type'] === 'control_request' && typeof event['request_id'] === 'string') {
95
+ child.stdin?.write(JSON.stringify({ type: 'control_response', response: { subtype: 'error', request_id: event['request_id'], error: 'Model discovery does not execute tools' } }) + '\n');
96
+ }
97
+ }
98
+ });
99
+ child.stderr?.on('data', () => { });
100
+ void child.done.then(() => controls.close(), () => controls.close());
101
+ let cleanupTimer;
102
+ try {
103
+ return claudeDirectory(await controls.request({ subtype: 'initialize', hooks: {} }, signal));
104
+ }
105
+ finally {
106
+ controls.close();
107
+ child.stdin?.end();
108
+ await Promise.race([child.done.catch(() => { }), new Promise(resolve => {
109
+ cleanupTimer = setTimeout(resolve, 1_000);
110
+ cleanupTimer.unref();
111
+ })]);
112
+ if (cleanupTimer !== undefined)
113
+ clearTimeout(cleanupTimer);
114
+ child.terminate();
115
+ await child.waitForExit().catch(() => { });
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Claude Code scoped-home provisioning. Unlike codex (whose credential store
3
+ * must be pinned to a file) and kimi (which needs a provider config),
4
+ * claude's `CLAUDE_CONFIG_DIR` scoped home needs nothing beyond the
5
+ * directory itself: login via `claude auth login` writes `.claude.json`
6
+ * (with the `oauthAccount` record) into it, and the real credential lands in
7
+ * the macOS keychain under a hashed entry or — on Linux, despite upstream
8
+ * bug #47661 — in the default `~/.claude/.credentials.json` while the config
9
+ * stays scoped. Sign-out removes the scoped config so `status` reports not
10
+ * authenticated; the keychain entry on macOS persists until the CLI removes
11
+ * it, which the framework's file-based logout cannot reach.
12
+ * @module @khorsheed/dsh-local-agent-claude-code/provision
13
+ */
14
+ /**
15
+ * Provision the scoped home directory. Claude creates it lazily on first
16
+ * use, but creating it eagerly keeps the harness's homeDir contract uniform
17
+ * with the other harnesses. When `proxyUrl` is configured, the scoped
18
+ * `settings.json` gains an env block pointing the CLI's own traffic at it —
19
+ * claude reads proxy settings from this file, so the child works even when
20
+ * the host process environment carries no proxy (a supervisor-spawned
21
+ * instance's env is not the user's shell env). An existing settings file is
22
+ * merged, not overwritten; only the two proxy keys are managed.
23
+ * @param homeDir - the `claude-code` harness's scoped home.
24
+ * @param proxyUrl - optional HTTP proxy URL for the child CLI's traffic.
25
+ */
26
+ export declare function provisionClaudeHome(homeDir: string, proxyUrl?: string): Promise<void>;
27
+ /**
28
+ * Read the scoped settings' configured model: the `model` key of
29
+ * `<homeDir>/settings.json` — the model a delegation round pins when the
30
+ * scoped settings name one at all. Claude's own default model is decided by
31
+ * the CLI and is deliberately NOT guessed here. Only the scoped file is
32
+ * consulted (never the host's `~/.claude`); absent or malformed content
33
+ * yields undefined.
34
+ * @param homeDir - the `claude-code` harness's scoped home.
35
+ * @returns the configured model identifier, or undefined when none is set.
36
+ */
37
+ export declare function readClaudeConfiguredModel(homeDir: string): Promise<string | undefined>;
38
+ /** Read the scoped native effort setting without changing the settings file. */
39
+ export declare function readClaudeConfiguredEffort(homeDir: string): Promise<string | undefined>;
40
+ /**
41
+ * Write (or, with undefined, remove) the scoped settings.json `model` key,
42
+ * merging the rest of the file exactly as {@link provisionClaudeHome} merges
43
+ * its env block. Only the live driver calls this: a resident runtime needs
44
+ * its member's effective model IN THE FILE, because a `--resume` respawn
45
+ * restores the session's own stored model over the `--model` flag. The exec
46
+ * path never calls it — its `--model` flag always applies, and the file
47
+ * stays exactly as the person edited it.
48
+ * @param homeDir - the `claude-code` harness's scoped home.
49
+ * @param model - the model to scratch in, or undefined to remove the key.
50
+ */
51
+ export declare function writeClaudeScopedModel(homeDir: string, model: string | undefined): Promise<void>;
52
+ /**
53
+ * Sign out of the scoped account: remove the scoped config file, so
54
+ * `/<name> status` reports not authenticated and the next login authorizes
55
+ * a fresh account. The macOS keychain entry (a hashed
56
+ * `Claude Code-credentials-<sha256(path)[:8]>` record keyed to the scoped
57
+ * home path) is NOT removed — the CLI's `claude auth logout` owns that, and
58
+ * the harness logout contract is file-based; the stale entry is harmless
59
+ * because a fresh login rewrites the same hashed slot.
60
+ * @param homeDir - the `claude-code` harness's scoped home.
61
+ */
62
+ export declare function claudeLogout(homeDir: string): Promise<void>;
63
+ //# sourceMappingURL=provision.d.ts.map
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Claude Code scoped-home provisioning. Unlike codex (whose credential store
3
+ * must be pinned to a file) and kimi (which needs a provider config),
4
+ * claude's `CLAUDE_CONFIG_DIR` scoped home needs nothing beyond the
5
+ * directory itself: login via `claude auth login` writes `.claude.json`
6
+ * (with the `oauthAccount` record) into it, and the real credential lands in
7
+ * the macOS keychain under a hashed entry or — on Linux, despite upstream
8
+ * bug #47661 — in the default `~/.claude/.credentials.json` while the config
9
+ * stays scoped. Sign-out removes the scoped config so `status` reports not
10
+ * authenticated; the keychain entry on macOS persists until the CLI removes
11
+ * it, which the framework's file-based logout cannot reach.
12
+ * @module @khorsheed/dsh-local-agent-claude-code/provision
13
+ */
14
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
15
+ import { join } from 'node:path';
16
+ /** The scoped config file holding the oauthAccount auth marker. */
17
+ const SCOPED_CONFIG = '.claude.json';
18
+ /** The scoped settings file carrying the CLI's env block. */
19
+ const SCOPED_SETTINGS = 'settings.json';
20
+ /**
21
+ * Provision the scoped home directory. Claude creates it lazily on first
22
+ * use, but creating it eagerly keeps the harness's homeDir contract uniform
23
+ * with the other harnesses. When `proxyUrl` is configured, the scoped
24
+ * `settings.json` gains an env block pointing the CLI's own traffic at it —
25
+ * claude reads proxy settings from this file, so the child works even when
26
+ * the host process environment carries no proxy (a supervisor-spawned
27
+ * instance's env is not the user's shell env). An existing settings file is
28
+ * merged, not overwritten; only the two proxy keys are managed.
29
+ * @param homeDir - the `claude-code` harness's scoped home.
30
+ * @param proxyUrl - optional HTTP proxy URL for the child CLI's traffic.
31
+ */
32
+ export async function provisionClaudeHome(homeDir, proxyUrl) {
33
+ await mkdir(homeDir, { recursive: true });
34
+ if (proxyUrl === undefined)
35
+ return;
36
+ const path = join(homeDir, SCOPED_SETTINGS);
37
+ let settings = {};
38
+ try {
39
+ settings = JSON.parse(await readFile(path, 'utf8'));
40
+ }
41
+ catch {
42
+ // Absent or malformed: start fresh (a malformed file is replaced, not merged).
43
+ }
44
+ const env = (settings.env ?? {});
45
+ if (env.https_proxy === proxyUrl && env.http_proxy === proxyUrl)
46
+ return;
47
+ env.https_proxy = proxyUrl;
48
+ env.http_proxy = proxyUrl;
49
+ settings.env = env;
50
+ await writeFile(path, JSON.stringify(settings, undefined, 2) + '\n');
51
+ }
52
+ /**
53
+ * Read the scoped settings' configured model: the `model` key of
54
+ * `<homeDir>/settings.json` — the model a delegation round pins when the
55
+ * scoped settings name one at all. Claude's own default model is decided by
56
+ * the CLI and is deliberately NOT guessed here. Only the scoped file is
57
+ * consulted (never the host's `~/.claude`); absent or malformed content
58
+ * yields undefined.
59
+ * @param homeDir - the `claude-code` harness's scoped home.
60
+ * @returns the configured model identifier, or undefined when none is set.
61
+ */
62
+ export async function readClaudeConfiguredModel(homeDir) {
63
+ let text;
64
+ try {
65
+ text = await readFile(join(homeDir, SCOPED_SETTINGS), 'utf8');
66
+ }
67
+ catch {
68
+ return undefined;
69
+ }
70
+ try {
71
+ const settings = JSON.parse(text);
72
+ return typeof settings.model === 'string' && settings.model !== '' ? settings.model : undefined;
73
+ }
74
+ catch {
75
+ return undefined;
76
+ }
77
+ }
78
+ /** Read the scoped native effort setting without changing the settings file. */
79
+ export async function readClaudeConfiguredEffort(homeDir) {
80
+ try {
81
+ const settings = JSON.parse(await readFile(join(homeDir, SCOPED_SETTINGS), 'utf8'));
82
+ return typeof settings.effortLevel === 'string' && settings.effortLevel.trim() !== '' ? settings.effortLevel.trim() : undefined;
83
+ }
84
+ catch {
85
+ return undefined;
86
+ }
87
+ }
88
+ /**
89
+ * Write (or, with undefined, remove) the scoped settings.json `model` key,
90
+ * merging the rest of the file exactly as {@link provisionClaudeHome} merges
91
+ * its env block. Only the live driver calls this: a resident runtime needs
92
+ * its member's effective model IN THE FILE, because a `--resume` respawn
93
+ * restores the session's own stored model over the `--model` flag. The exec
94
+ * path never calls it — its `--model` flag always applies, and the file
95
+ * stays exactly as the person edited it.
96
+ * @param homeDir - the `claude-code` harness's scoped home.
97
+ * @param model - the model to scratch in, or undefined to remove the key.
98
+ */
99
+ export async function writeClaudeScopedModel(homeDir, model) {
100
+ const path = join(homeDir, SCOPED_SETTINGS);
101
+ let settings = {};
102
+ try {
103
+ settings = JSON.parse(await readFile(path, 'utf8'));
104
+ }
105
+ catch {
106
+ // Absent or malformed: start fresh (a malformed file is replaced, not merged).
107
+ }
108
+ if (model === undefined) {
109
+ if (!('model' in settings))
110
+ return;
111
+ delete settings['model'];
112
+ }
113
+ else {
114
+ if (settings['model'] === model)
115
+ return;
116
+ settings['model'] = model;
117
+ }
118
+ await writeFile(path, JSON.stringify(settings, undefined, 2) + '\n');
119
+ }
120
+ /**
121
+ * Sign out of the scoped account: remove the scoped config file, so
122
+ * `/<name> status` reports not authenticated and the next login authorizes
123
+ * a fresh account. The macOS keychain entry (a hashed
124
+ * `Claude Code-credentials-<sha256(path)[:8]>` record keyed to the scoped
125
+ * home path) is NOT removed — the CLI's `claude auth logout` owns that, and
126
+ * the harness logout contract is file-based; the stale entry is harmless
127
+ * because a fresh login rewrites the same hashed slot.
128
+ * @param homeDir - the `claude-code` harness's scoped home.
129
+ */
130
+ export async function claudeLogout(homeDir) {
131
+ await rm(join(homeDir, SCOPED_CONFIG), { force: true });
132
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Claude Code session records adapter: scans the scoped home's `projects/`
3
+ * session files (one JSONL per session under `projects/<cwd-slug>/`, the
4
+ * slug being the workspace path with separators replaced by dashes), plus
5
+ * the transcript model read-back ({@link readClaudeTranscriptModel}) the
6
+ * model broker's `lastObserved` backstop runs on. The
7
+ * slug is a LOSSY encoding of the cwd — different paths can collide on the
8
+ * same slug — so the listed cwd always comes from the file CONTENT (the
9
+ * first `user` event's `cwd` field), never from the directory name. Only the
10
+ * head of each file is read — the first user event carries the id, cwd, and
11
+ * timestamp the listing needs; the rest of the file (conversation) is not
12
+ * parsed. Files are append-only with torn-tail tolerance, and contents are
13
+ * always untrusted input.
14
+ * @module @khorsheed/dsh-local-agent-claude-code/records
15
+ */
16
+ import { execFile } from 'node:child_process';
17
+ import type { LocalAgentSessionRecord } from '@khorsheed/dsh-local-agent';
18
+ /** Test-swappable process exec (the keychain read goes through `security`). */
19
+ export declare const internals: {
20
+ exec: typeof execFile.__promisify__;
21
+ };
22
+ /**
23
+ * The scoped `.claude.json`'s modification stamp (epoch ms), undefined when
24
+ * absent. A completed login rewrites this file, so the stamp distinguishes a
25
+ * fresh login from a leftover marker (a revoked token's record lingers).
26
+ * @param homeDir - the `claude-code` harness's scoped home.
27
+ * @returns the marker's mtime, or undefined when no scoped config exists.
28
+ */
29
+ export declare function claudeCredentialStamp(homeDir: string): Promise<number | undefined>;
30
+ /**
31
+ * Reconcile the keychain credential with `<homeDir>/.credentials.json`,
32
+ * NEWER WINS. Claude 2.1.236 on macOS WRITES the scoped login to the hashed
33
+ * keychain entry but READS the credentials file at runtime (the same
34
+ * write/read split as the Linux #47661 bug) — a login that lands only in the
35
+ * keychain still answers "Not logged in", so the file has to be kept current.
36
+ *
37
+ * The file is not this process's private mirror, though: a containerized
38
+ * round bind-mounts this very directory read-write (the unit has no keychain,
39
+ * so the CLI inside it reads and writes the file), and the pre-spawn sync runs
40
+ * against the HOST path moments before the unit reads it through the mount.
41
+ * An unconditional mirror therefore makes this function the one participant
42
+ * that can move the credential chain BACKWARD: it hands the next run a refresh
43
+ * token the other writer already spent, the endpoint rejects it, and the CLI
44
+ * answers by CLEARING the file — which logs the instance out, because that
45
+ * file is the instance's credential. So the keychain blob is written only when
46
+ * it is not older than the file's. Undefined expiry on either side keeps the
47
+ * pre-newer-wins behavior (the keychain writes), and a file that is not USABLE
48
+ * never wins.
49
+ *
50
+ * Only a USABLE blob (non-empty token, see `readKeychainCredential`) is
51
+ * mirrored; with none in the keychain, a file holding an empty-token shell —
52
+ * debris from an earlier first-match read, or a cleared credential — is
53
+ * REMOVED, so the runtime says "Not logged in" instead of the misleading
54
+ * "OAuth session expired". Idempotent and content-compare before write.
55
+ * @param homeDir - the `claude-code` harness's scoped home.
56
+ * @param log - warn channel for the two decisions that used to be silent.
57
+ * @returns true when a usable credential is in the file after the call.
58
+ */
59
+ export declare function syncClaudeCredentialFile(homeDir: string, log?: (message: string) => void): Promise<boolean>;
60
+ /**
61
+ * Whether the scoped home holds usable Claude Code credentials: the scoped
62
+ * `.claude.json` carries an `oauthAccount` record once a device-code login
63
+ * has completed, AND the keychain credential is not past its stored expiry
64
+ * (when the expiry is readable — a server-side REVOCATION is not visible
65
+ * locally; providers report those through `localAgent.reportAuthFailure`).
66
+ * The real credential lives in the macOS keychain under a hashed entry
67
+ * (`Claude Code-credentials-<sha256(path)[:8]>`, hashed from the config-dir
68
+ * path) or in the default `~/.claude/.credentials.json` on Linux (upstream
69
+ * bug #47661: `CLAUDE_CONFIG_DIR` does not isolate the credentials file
70
+ * there — it writes the scoped dir but READS the default home, so Linux
71
+ * credentials may exist even when the scoped home has none). Spawning the
72
+ * CLI to probe auth is avoided; the platform differences are documented in
73
+ * the README.
74
+ * @param homeDir - the `claude-code` harness's scoped home.
75
+ * @param log - warn channel handed to the credential reconcile below.
76
+ * @returns true when a completed login is recorded and not locally expired.
77
+ */
78
+ export declare function claudeAuthenticated(homeDir: string, log?: (message: string) => void): Promise<boolean>;
79
+ /**
80
+ * Parse the head of a session file for the listing fields. The first `user`
81
+ * event carries `sessionId`, `cwd`, and `timestamp`; its `cwd` is the only
82
+ * trustworthy workspace source (the directory slug is lossy). Unknown or
83
+ * malformed values yield an undefined record rather than failing the list.
84
+ * @param text - the raw session file head (only the head is read).
85
+ * @returns the record, or undefined when no user event is found.
86
+ */
87
+ export declare function projectRecord(text: string): LocalAgentSessionRecord | undefined;
88
+ /**
89
+ * Read the scoped home's session files. The listing walks the projects
90
+ * directory and reads each jsonl head; a torn or malformed file is skipped
91
+ * rather than failing the whole list.
92
+ * @param homeDir - the `claude-code` harness's scoped home.
93
+ * @returns the parsed records, empty when no sessions exist yet.
94
+ */
95
+ export declare function listClaudeSessions(homeDir: string): Promise<readonly LocalAgentSessionRecord[]>;
96
+ /**
97
+ * The model a member's OWN CLI transcript names — the history backstop for
98
+ * the broker's `lastObserved` when no delegation record observed one (every
99
+ * member whose rounds predate the live-settle report). The live-settle
100
+ * channel remains primary (the record's `observedModel` wins when present);
101
+ * this reader only answers "what did this session last run" from the CLI's
102
+ * own files, read-only — nothing is backfilled into `delegations.jsonl`.
103
+ *
104
+ * Location rule: with `cliSessionId`, the main transcript
105
+ * `projects/<cwd-slug>/<cliSessionId>.jsonl` (found by NAME across the
106
+ * project dirs — the slug is lossy, so it is never computed); when it is
107
+ * absent, a sidechain `agent-*.jsonl` whose head names the session id
108
+ * (bounded candidate count) answers instead. Without `cliSessionId` (the
109
+ * settings card's memberless read), the NEWEST transcript across the
110
+ * projects tree (mtime, bounded dir/file scan) answers; a newest file that
111
+ * names no model yields to the next, up to a few tries. Every failure —
112
+ * missing tree, unreadable file, no model in the read window — is
113
+ * `undefined`, never a throw.
114
+ * @param homeDir - the `claude-code` harness's scoped home.
115
+ * @param cliSessionId - the delegation record's CLI session id, when the
116
+ * member's own transcript is wanted.
117
+ * @returns the model identifier, or undefined.
118
+ */
119
+ export declare function readClaudeTranscriptModel(homeDir: string, cliSessionId?: string): Promise<string | undefined>;
120
+ //# sourceMappingURL=records.d.ts.map