@lucascouts/claude-agent-tui 0.5.2 → 0.7.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.
Files changed (108) hide show
  1. package/NOTICE +1 -1
  2. package/README.md +1 -1
  3. package/dist/acp-agent.d.ts +249 -21
  4. package/dist/acp-agent.js +573 -73
  5. package/dist/agent-catalog.d.ts +95 -0
  6. package/dist/agent-catalog.js +287 -0
  7. package/dist/ansi-mirror.d.ts +0 -1
  8. package/dist/besteffort.d.ts +0 -1
  9. package/dist/billing/entrypoint-guard.d.ts +0 -1
  10. package/dist/claude-path.d.ts +0 -1
  11. package/dist/claude-path.js +6 -0
  12. package/dist/command-catalog.d.ts +84 -0
  13. package/dist/command-catalog.js +339 -0
  14. package/dist/diff-enriched-reader.d.ts +0 -1
  15. package/dist/diff-source.d.ts +0 -1
  16. package/dist/drift-checks.d.ts +0 -1
  17. package/dist/end-of-turn.d.ts +6 -1
  18. package/dist/end-of-turn.js +8 -1
  19. package/dist/engine-lifecycle.d.ts +66 -2
  20. package/dist/engine-lifecycle.js +43 -4
  21. package/dist/engine-pty.d.ts +70 -3
  22. package/dist/engine-pty.js +80 -6
  23. package/dist/engine-watcher.d.ts +0 -1
  24. package/dist/engine.d.ts +0 -1
  25. package/dist/event-switch.d.ts +0 -1
  26. package/dist/gate/port.d.ts +0 -1
  27. package/dist/gate/settings-writer.d.ts +14 -1
  28. package/dist/gate/settings-writer.js +49 -0
  29. package/dist/image-input.d.ts +30 -0
  30. package/dist/image-input.js +79 -0
  31. package/dist/image-vision-smoke.d.ts +51 -0
  32. package/dist/image-vision-smoke.js +111 -0
  33. package/dist/index.d.ts +0 -1
  34. package/dist/index.js +6 -0
  35. package/dist/jsonl.d.ts +0 -1
  36. package/dist/lib.d.ts +0 -1
  37. package/dist/linearize.d.ts +1 -2
  38. package/dist/linearize.js +1 -1
  39. package/dist/live-diff-env.d.ts +0 -1
  40. package/dist/live-subagent-env.d.ts +0 -1
  41. package/dist/mcp-config-writer.d.ts +60 -0
  42. package/dist/mcp-config-writer.js +172 -0
  43. package/dist/model-catalog.d.ts +68 -3
  44. package/dist/model-catalog.js +123 -13
  45. package/dist/permissions/allow-inject.d.ts +0 -1
  46. package/dist/permissions/deny.d.ts +12 -1
  47. package/dist/permissions/deny.js +18 -0
  48. package/dist/permissions/elicitation-bridge.d.ts +71 -0
  49. package/dist/permissions/elicitation-bridge.js +146 -0
  50. package/dist/permissions/gate-wiring.d.ts +23 -3
  51. package/dist/permissions/gate-wiring.js +123 -1
  52. package/dist/permissions/hook-server.d.ts +11 -3
  53. package/dist/permissions/hook-server.js +10 -1
  54. package/dist/permissions/permission-mode.d.ts +0 -1
  55. package/dist/permissions/request-permission.d.ts +0 -1
  56. package/dist/settings.d.ts +0 -1
  57. package/dist/settings.js +9 -0
  58. package/dist/stop-reason-map.d.ts +0 -1
  59. package/dist/subagent-gate.d.ts +0 -1
  60. package/dist/subagent-source.d.ts +0 -1
  61. package/dist/subagent-watcher.d.ts +0 -1
  62. package/dist/tools.d.ts +0 -1
  63. package/dist/tools.js +5 -1
  64. package/dist/usage-env.d.ts +0 -1
  65. package/dist/usage.d.ts +3 -1
  66. package/dist/usage.js +3 -0
  67. package/dist/utils.d.ts +0 -1
  68. package/dist/zed-register.d.ts +0 -1
  69. package/package.json +12 -9
  70. package/dist/acp-agent.d.ts.map +0 -1
  71. package/dist/ansi-mirror.d.ts.map +0 -1
  72. package/dist/besteffort.d.ts.map +0 -1
  73. package/dist/billing/entrypoint-guard.d.ts.map +0 -1
  74. package/dist/claude-path.d.ts.map +0 -1
  75. package/dist/diff-enriched-reader.d.ts.map +0 -1
  76. package/dist/diff-source.d.ts.map +0 -1
  77. package/dist/drift-checks.d.ts.map +0 -1
  78. package/dist/end-of-turn.d.ts.map +0 -1
  79. package/dist/engine-lifecycle.d.ts.map +0 -1
  80. package/dist/engine-pty.d.ts.map +0 -1
  81. package/dist/engine-watcher.d.ts.map +0 -1
  82. package/dist/engine.d.ts.map +0 -1
  83. package/dist/event-switch.d.ts.map +0 -1
  84. package/dist/gate/port.d.ts.map +0 -1
  85. package/dist/gate/settings-writer.d.ts.map +0 -1
  86. package/dist/index.d.ts.map +0 -1
  87. package/dist/jsonl.d.ts.map +0 -1
  88. package/dist/lib.d.ts.map +0 -1
  89. package/dist/linearize.d.ts.map +0 -1
  90. package/dist/live-diff-env.d.ts.map +0 -1
  91. package/dist/live-subagent-env.d.ts.map +0 -1
  92. package/dist/model-catalog.d.ts.map +0 -1
  93. package/dist/permissions/allow-inject.d.ts.map +0 -1
  94. package/dist/permissions/deny.d.ts.map +0 -1
  95. package/dist/permissions/gate-wiring.d.ts.map +0 -1
  96. package/dist/permissions/hook-server.d.ts.map +0 -1
  97. package/dist/permissions/permission-mode.d.ts.map +0 -1
  98. package/dist/permissions/request-permission.d.ts.map +0 -1
  99. package/dist/settings.d.ts.map +0 -1
  100. package/dist/stop-reason-map.d.ts.map +0 -1
  101. package/dist/subagent-gate.d.ts.map +0 -1
  102. package/dist/subagent-source.d.ts.map +0 -1
  103. package/dist/subagent-watcher.d.ts.map +0 -1
  104. package/dist/tools.d.ts.map +0 -1
  105. package/dist/usage-env.d.ts.map +0 -1
  106. package/dist/usage.d.ts.map +0 -1
  107. package/dist/utils.d.ts.map +0 -1
  108. package/dist/zed-register.d.ts.map +0 -1
@@ -49,19 +49,63 @@ export declare function resolveShell(baseEnv?: Record<string, string | undefined
49
49
  * non-"default" mode is emitted as `--permission-mode <mode>`; "default"/undefined emit nothing;
50
50
  * `permissionMode: "plan"` reproduces the old planMode=true path. Still interactive-only — adds no
51
51
  * `-p`/`stream-json`, billing stays subscription `cli`.
52
+ *
53
+ * Story 056 (R3.3): an optional `agent` persona name is emitted as `--agent "<name>"` (DOUBLE-QUOTED,
54
+ * grouped with the other behavior flags). This is the SECOND layer of the two-layer command-injection
55
+ * defense — `agent-catalog.ts` already drops any name outside `SAFE_AGENT_REF`
56
+ * (`/^[A-Za-z0-9_-]+(?::[A-Za-z0-9_-]+)?$/`, allowing a namespaced `plugin:name`) at discovery, and
57
+ * here we re-assert via {@link isSafeAgentRef} so an unsafe name is silently DROPPED (no flag), never
58
+ * interpolated into the `-lc` shell string. Interactive-only — adds no `-p`/`stream-json`.
59
+ *
60
+ * Story 057 (R1.1/R1.2): an optional `additionalDirectories` list is emitted as ONE double-quoted
61
+ * `--add-dir "<dir>"` flag PER directory (never a single space-joined value — each dir is its own flag,
62
+ * matching the upstream/CLI shape). Each dir is re-asserted via {@link isSafeDir} (absolute + existing +
63
+ * free of shell metacharacters); an unsafe dir is DROPPED and a one-line warning is logged, never
64
+ * interpolated into the `-lc` shell string. Interactive-only — adds no `-p`/`stream-json`.
65
+ *
66
+ * Story 057 (R2.2): an optional `mcpConfigFile` PATH is emitted as the DOUBLE-QUOTED `--mcp-config
67
+ * "<file>"` flag (same style as `--settings` — a file path, so the secret-bearing JSON stays OFF the
68
+ * command line and survives the `-lc` shell word-splitting). The file is the fork-controlled
69
+ * uuid-named scratch from `mcp-config-writer.ts`. R2.2 is a HARD rule: `--strict-mcp-config` is NEVER
70
+ * emitted, so claude MERGES the scratch with the user's own `.mcp.json`/settings MCP servers (the
71
+ * parity target) instead of discarding them. Interactive-only — adds no `-p`/`stream-json`.
72
+ */
73
+ export declare function buildClaudeCmd(sessionId: string, permissionMode?: string, settingsFile?: string, effortLevel?: string, agent?: string, additionalDirectories?: string[], mcpConfigFile?: string): string;
74
+ /**
75
+ * Story 057 (R1.2). The directory-allowlist predicate, mirroring the discipline of
76
+ * {@link isSafeAgentRef}: a directory is safe to interpolate into a double-quoted `--add-dir "<dir>"`
77
+ * flag ONLY when it is an ABSOLUTE path, currently EXISTS on disk, and is FREE of the shell
78
+ * metacharacters that could break out of (or inject into) the `-lc` double-quoted argument — `"`, `$`,
79
+ * a backtick, or a newline/CR. Single source of truth — exported so engine-lifecycle.ts's resume path
80
+ * reuses it rather than re-implementing the rule.
81
+ */
82
+ export declare function isSafeDir(p: string): boolean;
83
+ /**
84
+ * Story 057 (R1.1/R1.2). Build the trailing ` --add-dir "<dir>"` fragment for a list of additional
85
+ * directories: ONE double-quoted flag per dir that passes {@link isSafeDir}. An unsafe dir (relative,
86
+ * non-existent, or containing a shell metacharacter) is DROPPED — never interpolated — and a one-line
87
+ * warning is logged to stderr (the fork's diagnostic channel, never the ACP stdout wire). An
88
+ * empty/undefined list emits the empty string (no flags). Shared by the fresh and resume spawn paths.
52
89
  */
53
- export declare function buildClaudeCmd(sessionId: string, permissionMode?: string, settingsFile?: string, effortLevel?: string): string;
90
+ export declare function buildAddDirFlags(additionalDirectories?: string[]): string;
54
91
  /**
55
92
  * Login-shell argv (§5, R1.3). The `-lc` flag is MANDATORY: node-pty's `posix_spawnp`
56
93
  * does not execute the `claude` npm launcher's `#!/usr/bin/env node` shebang, so the
57
94
  * launcher must run *through* a login shell. (The compiled native-binary is the only
58
95
  * thing that could be spawned directly — §5 / IMPLEMENTACAO-FORK-ACP.md §17.)
59
96
  */
60
- export declare function buildSpawnArgv(sessionId: string, permissionMode?: string, settingsFile?: string, effortLevel?: string): [string, string];
97
+ export declare function buildSpawnArgv(sessionId: string, permissionMode?: string, settingsFile?: string, effortLevel?: string, agent?: string, additionalDirectories?: string[], mcpConfigFile?: string): [string, string];
61
98
  /** Options for {@link spawnClaudePty}. */
62
99
  export interface SpawnPtyOptions {
63
100
  /** Host working directory the TUI runs in; passed straight through to the PTY (§5). */
64
101
  cwd: string;
102
+ /**
103
+ * Story 056 v4 — an OPTIONAL pre-chosen session id for an in-place FRESH re-spawn (a selector change
104
+ * before the first interaction reuses the session's existing id). Absent → a new `randomUUID()` is
105
+ * generated (the normal createSession path). claude accepts a reused `--session-id` once the prior
106
+ * PTY for that id has exited (LIVE-VERIFIED).
107
+ */
108
+ sessionId?: string;
65
109
  /** Base environment to sanitize; defaults to the parent process env. */
66
110
  baseEnv?: Record<string, string | undefined>;
67
111
  /**
@@ -88,6 +132,30 @@ export interface SpawnPtyOptions {
88
132
  * Absent → no flag (the ungated/`FORK_GATE=off` spawn is byte-for-byte pre-034).
89
133
  */
90
134
  settingsFile?: string;
135
+ /**
136
+ * Story 056 (R3.3): a main-thread agent persona name (from `agent-catalog.ts`) emitted as the
137
+ * interactive-only `--agent "<name>"` spawn flag. DOUBLE-QUOTED and injection-safe — re-asserted
138
+ * against the R3.3 allowlist (`/^[A-Za-z0-9_-]+$/`) so an unsafe name is dropped, never interpolated.
139
+ * Absent → no flag. Adds no `-p`/`stream-json`; billing stays subscription `cli`.
140
+ */
141
+ agent?: string;
142
+ /**
143
+ * Story 057 (R1.1/R1.2): extra workspace directories surfaced as ONE double-quoted
144
+ * `--add-dir "<dir>"` flag per dir. Each dir is re-asserted via {@link isSafeDir} (absolute +
145
+ * existing + no shell metacharacter) — an unsafe dir is dropped (logged), never interpolated.
146
+ * INTERACTIVE-ONLY: adds no `-p`/`--print`/`stream-json`, so billing stays subscription `cli`.
147
+ * Absent/empty → no flag.
148
+ */
149
+ additionalDirectories?: string[];
150
+ /**
151
+ * Story 057 (R2.2): the fork-controlled uuid-named scratch MCP-config PATH (written by
152
+ * `mcp-config-writer.ts`), surfaced as the DOUBLE-QUOTED `--mcp-config "<file>"` flag. A file path —
153
+ * the secret-bearing JSON lives in the file, OFF the command line. NEVER paired with
154
+ * `--strict-mcp-config`, so claude MERGES it with the user's own `.mcp.json`/settings MCP servers
155
+ * (the R2.2 parity target) rather than discarding them. INTERACTIVE-ONLY: adds no
156
+ * `-p`/`--print`/`stream-json`, so billing stays subscription `cli`. Absent → no flag.
157
+ */
158
+ mcpConfigFile?: string;
91
159
  }
92
160
  /** Handle returned by {@link spawnClaudePty}; story 014 manages its lifecycle. */
93
161
  export interface PtyEngineHandle {
@@ -150,4 +218,3 @@ export declare const CLEAR_INPUT_DELAY_MS = 60;
150
218
  * try/catch); the payload and `\r` writes are scheduled and post-exit-safe.
151
219
  */
152
220
  export declare function sendPrompt(p: IPty, text: string, schedule?: (fn: () => void, ms: number) => void): void;
153
- //# sourceMappingURL=engine-pty.d.ts.map
@@ -16,6 +16,9 @@
16
16
  // the ACP createSession rewrite that wires this engine in is story 023.
17
17
  import pty from "node-pty";
18
18
  import { randomUUID } from "node:crypto";
19
+ import { existsSync } from "node:fs";
20
+ import * as path from "node:path";
21
+ import { isSafeAgentRef } from "./agent-catalog.js";
19
22
  // PTY geometry pinned by §5.
20
23
  const PTY_NAME = "xterm-256color";
21
24
  const PTY_COLS = 120;
@@ -94,8 +97,28 @@ export function resolveShell(baseEnv = process.env) {
94
97
  * non-"default" mode is emitted as `--permission-mode <mode>`; "default"/undefined emit nothing;
95
98
  * `permissionMode: "plan"` reproduces the old planMode=true path. Still interactive-only — adds no
96
99
  * `-p`/`stream-json`, billing stays subscription `cli`.
100
+ *
101
+ * Story 056 (R3.3): an optional `agent` persona name is emitted as `--agent "<name>"` (DOUBLE-QUOTED,
102
+ * grouped with the other behavior flags). This is the SECOND layer of the two-layer command-injection
103
+ * defense — `agent-catalog.ts` already drops any name outside `SAFE_AGENT_REF`
104
+ * (`/^[A-Za-z0-9_-]+(?::[A-Za-z0-9_-]+)?$/`, allowing a namespaced `plugin:name`) at discovery, and
105
+ * here we re-assert via {@link isSafeAgentRef} so an unsafe name is silently DROPPED (no flag), never
106
+ * interpolated into the `-lc` shell string. Interactive-only — adds no `-p`/`stream-json`.
107
+ *
108
+ * Story 057 (R1.1/R1.2): an optional `additionalDirectories` list is emitted as ONE double-quoted
109
+ * `--add-dir "<dir>"` flag PER directory (never a single space-joined value — each dir is its own flag,
110
+ * matching the upstream/CLI shape). Each dir is re-asserted via {@link isSafeDir} (absolute + existing +
111
+ * free of shell metacharacters); an unsafe dir is DROPPED and a one-line warning is logged, never
112
+ * interpolated into the `-lc` shell string. Interactive-only — adds no `-p`/`stream-json`.
113
+ *
114
+ * Story 057 (R2.2): an optional `mcpConfigFile` PATH is emitted as the DOUBLE-QUOTED `--mcp-config
115
+ * "<file>"` flag (same style as `--settings` — a file path, so the secret-bearing JSON stays OFF the
116
+ * command line and survives the `-lc` shell word-splitting). The file is the fork-controlled
117
+ * uuid-named scratch from `mcp-config-writer.ts`. R2.2 is a HARD rule: `--strict-mcp-config` is NEVER
118
+ * emitted, so claude MERGES the scratch with the user's own `.mcp.json`/settings MCP servers (the
119
+ * parity target) instead of discarding them. Interactive-only — adds no `-p`/`stream-json`.
97
120
  */
98
- export function buildClaudeCmd(sessionId, permissionMode, settingsFile, effortLevel) {
121
+ export function buildClaudeCmd(sessionId, permissionMode, settingsFile, effortLevel, agent, additionalDirectories, mcpConfigFile) {
99
122
  let cmd = `claude --session-id ${sessionId}`;
100
123
  if (permissionMode && permissionMode !== "default")
101
124
  cmd += ` --permission-mode ${permissionMode}`;
@@ -103,18 +126,69 @@ export function buildClaudeCmd(sessionId, permissionMode, settingsFile, effortLe
103
126
  // non-"default" level is seeded/re-spawned with `--effort <level>`. Interactive-only — no credit path.
104
127
  if (effortLevel && effortLevel !== "default")
105
128
  cmd += ` --effort ${effortLevel}`;
129
+ // Story 056 (R3.3): the agent persona, double-quoted, emitted ONLY for a real persona — the "default"
130
+ // sentinel (= no persona, mirrors --effort/--permission-mode) emits nothing — and only when it passes
131
+ // the R3.3 allowlist re-assert (defense-in-depth — an unsafe name is dropped, never interpolated).
132
+ if (agent && agent !== "default" && isSafeAgentRef(agent))
133
+ cmd += ` --agent "${agent}"`;
106
134
  if (settingsFile)
107
135
  cmd += ` --settings "${settingsFile}"`;
136
+ // Story 057 (R1.1/R1.2): ONE double-quoted `--add-dir "<dir>"` per safe dir (never a space-joined
137
+ // value). Each dir is re-asserted via isSafeDir (absolute + existing + no shell metachar) — an unsafe
138
+ // dir is DROPPED with a one-line warning, never interpolated into the `-lc` shell string.
139
+ cmd += buildAddDirFlags(additionalDirectories);
140
+ // Story 057 (R2.2): the DOUBLE-QUOTED `--mcp-config "<file>"` (mirrors `--settings` above — a file path,
141
+ // so the secret-bearing JSON stays off the command line). Emitted ONLY when set. R2.2 HARD rule: NEVER
142
+ // `--strict-mcp-config` — without it claude MERGES the scratch with the user's own .mcp.json/settings
143
+ // MCP servers (the parity target) instead of discarding them.
144
+ if (mcpConfigFile)
145
+ cmd += ` --mcp-config "${mcpConfigFile}"`;
108
146
  return cmd;
109
147
  }
148
+ /**
149
+ * Story 057 (R1.2). The directory-allowlist predicate, mirroring the discipline of
150
+ * {@link isSafeAgentRef}: a directory is safe to interpolate into a double-quoted `--add-dir "<dir>"`
151
+ * flag ONLY when it is an ABSOLUTE path, currently EXISTS on disk, and is FREE of the shell
152
+ * metacharacters that could break out of (or inject into) the `-lc` double-quoted argument — `"`, `$`,
153
+ * a backtick, or a newline/CR. Single source of truth — exported so engine-lifecycle.ts's resume path
154
+ * reuses it rather than re-implementing the rule.
155
+ */
156
+ export function isSafeDir(p) {
157
+ return path.isAbsolute(p) && existsSync(p) && !/["$`\n\r]/.test(p);
158
+ }
159
+ /**
160
+ * Story 057 (R1.1/R1.2). Build the trailing ` --add-dir "<dir>"` fragment for a list of additional
161
+ * directories: ONE double-quoted flag per dir that passes {@link isSafeDir}. An unsafe dir (relative,
162
+ * non-existent, or containing a shell metacharacter) is DROPPED — never interpolated — and a one-line
163
+ * warning is logged to stderr (the fork's diagnostic channel, never the ACP stdout wire). An
164
+ * empty/undefined list emits the empty string (no flags). Shared by the fresh and resume spawn paths.
165
+ */
166
+ export function buildAddDirFlags(additionalDirectories) {
167
+ if (!additionalDirectories || additionalDirectories.length === 0)
168
+ return "";
169
+ let fragment = "";
170
+ for (const dir of additionalDirectories) {
171
+ if (isSafeDir(dir)) {
172
+ fragment += ` --add-dir "${dir}"`;
173
+ }
174
+ else {
175
+ console.error(`engine-pty: dropping unsafe --add-dir entry (not absolute+existing, or contains a shell ` +
176
+ `metacharacter): ${JSON.stringify(dir)}`);
177
+ }
178
+ }
179
+ return fragment;
180
+ }
110
181
  /**
111
182
  * Login-shell argv (§5, R1.3). The `-lc` flag is MANDATORY: node-pty's `posix_spawnp`
112
183
  * does not execute the `claude` npm launcher's `#!/usr/bin/env node` shebang, so the
113
184
  * launcher must run *through* a login shell. (The compiled native-binary is the only
114
185
  * thing that could be spawned directly — §5 / IMPLEMENTACAO-FORK-ACP.md §17.)
115
186
  */
116
- export function buildSpawnArgv(sessionId, permissionMode, settingsFile, effortLevel) {
117
- return ["-lc", buildClaudeCmd(sessionId, permissionMode, settingsFile, effortLevel)];
187
+ export function buildSpawnArgv(sessionId, permissionMode, settingsFile, effortLevel, agent, additionalDirectories, mcpConfigFile) {
188
+ return [
189
+ "-lc",
190
+ buildClaudeCmd(sessionId, permissionMode, settingsFile, effortLevel, agent, additionalDirectories, mcpConfigFile),
191
+ ];
118
192
  }
119
193
  /**
120
194
  * Spawn the interactive `claude` TUI under a real PTY with the §5 dimensions and a
@@ -133,10 +207,10 @@ export function buildSpawnArgv(sessionId, permissionMode, settingsFile, effortLe
133
207
  * process-supervisor or non-PTY spawn variant is introduced.
134
208
  */
135
209
  export function spawnClaudePty(opts) {
136
- const { cwd, baseEnv = process.env, spawn = pty.spawn, permissionMode, settingsFile, effortLevel, } = opts;
137
- const sessionId = randomUUID(); // pre-generated → correlates to the JSONL transcript
210
+ const { cwd, baseEnv = process.env, spawn = pty.spawn, permissionMode, settingsFile, effortLevel, agent, additionalDirectories, mcpConfigFile, } = opts;
211
+ const sessionId = opts.sessionId ?? randomUUID(); // pre-generated → correlates to the JSONL transcript (v4: reused on a fresh re-spawn)
138
212
  const shell = resolveShell(baseEnv);
139
- const argv = buildSpawnArgv(sessionId, permissionMode, settingsFile, effortLevel);
213
+ const argv = buildSpawnArgv(sessionId, permissionMode, settingsFile, effortLevel, agent, additionalDirectories, mcpConfigFile);
140
214
  // Sanitized, subscription-billing env (§5/§10) via the single shared definition.
141
215
  const env = buildSanitizedEnv(baseEnv);
142
216
  // §10 no-spoof contract (R3.1/R3.3): the 'cli' entrypoint label MUST be produced
@@ -80,4 +80,3 @@ export interface JsonlWatcher extends SessionWatcher {
80
80
  * @returns a {@link JsonlWatcher} (a {@link SessionWatcher} plus `notifyEndOfTurn`).
81
81
  */
82
82
  export declare function createJsonlWatcher(opts: JsonlWatcherOptions): JsonlWatcher;
83
- //# sourceMappingURL=engine-watcher.d.ts.map
package/dist/engine.d.ts CHANGED
@@ -27,4 +27,3 @@ export declare class StubEngine implements Engine {
27
27
  }
28
28
  /** Factory for the default story-011 engine (the no-op stub). */
29
29
  export declare function createStubEngine(): Engine;
30
- //# sourceMappingURL=engine.d.ts.map
@@ -161,4 +161,3 @@ export type EventClassification = ContentClassification | LifecycleClassificatio
161
161
  */
162
162
  export declare function classifyEvent(event: JsonlEvent, opts?: ClassifyOptions): EventClassification;
163
163
  export {};
164
- //# sourceMappingURL=event-switch.d.ts.map
@@ -35,4 +35,3 @@ export type ServerFactory = () => PortServer;
35
35
  * the last bind error.
36
36
  */
37
37
  export declare function findFreePort(attempts?: number, makeServer?: ServerFactory): Promise<number>;
38
- //# sourceMappingURL=port.d.ts.map
@@ -144,4 +144,17 @@ export declare function injectHook(opts: {
144
144
  * underlying error so teardown never silently strands the fork hook.
145
145
  */
146
146
  export declare function restore(backup: Backup): Promise<RestoreResult>;
147
- //# sourceMappingURL=settings-writer.d.ts.map
147
+ /**
148
+ * Story 060 (R2.2/R2.3/R3.2) — toggle the ultracode keys in the per-session SCRATCH settings file the
149
+ * gate wrote (preserving the hook + EVERY other key). `active` → set `ultracode:true` (the per-session
150
+ * ACTIVATOR) + `ultracodeKeywordTrigger:true` (defensively ENABLE the keyword even if the user disabled
151
+ * it). `!active` → remove BOTH keys. Read tolerantly (JSONC) + written durably (temp+fsync+rename),
152
+ * mirroring {@link injectHook}. A missing/empty file is treated as an empty object. No-op-safe and
153
+ * idempotent: re-applying the same `active` produces a byte-equivalent document.
154
+ *
155
+ * @param settingsPath absolute path to the per-session scratch `settings.local.json` (the gate's file).
156
+ * @param active whether ultracode is selected (set the keys) or de-selected (remove them).
157
+ * @throws {Error} only on a genuine read/parse/write failure — NEVER on the benign absence of the file
158
+ * (ENOENT → treated as `{}`), mirroring the rest of the module's discipline.
159
+ */
160
+ export declare function applyUltracodeSettings(settingsPath: string, active: boolean): Promise<void>;
@@ -353,3 +353,52 @@ export async function restore(backup) {
353
353
  }
354
354
  }
355
355
  }
356
+ // ─────────────────────────────────────────────────────────────────────────────
357
+ // Story 060 (R2.2 / R2.3 / R3.2) — toggle the ultracode keys in the per-session SCRATCH settings file.
358
+ //
359
+ // The "ultracode" effort-selector sentinel (story 060) activates the binary's Workflow keyword-trigger.
360
+ // Its LIVE activation is a keyword prefix on the outgoing prompt (acp-agent.ts); THIS function is the
361
+ // declarative spawn-time complement — the keys `claude` reads only at (re-)spawn, so the scratch stays
362
+ // in sync for any (re-)spawn that DOES happen. It is NOT itself a re-spawn trigger (Option A).
363
+ // ─────────────────────────────────────────────────────────────────────────────
364
+ /** The two settings keys that opt a session into the ultracode keyword-trigger (story 060). */
365
+ const ULTRACODE_SETTINGS_KEY = "ultracode";
366
+ const ULTRACODE_KEYWORD_TRIGGER_KEY = "ultracodeKeywordTrigger";
367
+ /**
368
+ * Story 060 (R2.2/R2.3/R3.2) — toggle the ultracode keys in the per-session SCRATCH settings file the
369
+ * gate wrote (preserving the hook + EVERY other key). `active` → set `ultracode:true` (the per-session
370
+ * ACTIVATOR) + `ultracodeKeywordTrigger:true` (defensively ENABLE the keyword even if the user disabled
371
+ * it). `!active` → remove BOTH keys. Read tolerantly (JSONC) + written durably (temp+fsync+rename),
372
+ * mirroring {@link injectHook}. A missing/empty file is treated as an empty object. No-op-safe and
373
+ * idempotent: re-applying the same `active` produces a byte-equivalent document.
374
+ *
375
+ * @param settingsPath absolute path to the per-session scratch `settings.local.json` (the gate's file).
376
+ * @param active whether ultracode is selected (set the keys) or de-selected (remove them).
377
+ * @throws {Error} only on a genuine read/parse/write failure — NEVER on the benign absence of the file
378
+ * (ENOENT → treated as `{}`), mirroring the rest of the module's discipline.
379
+ */
380
+ export async function applyUltracodeSettings(settingsPath, active) {
381
+ // 1) Read the current bytes; a missing file (ENOENT) is treated as "no prior settings" (→ `{}`).
382
+ const priorBytes = await readPriorBytes(settingsPath);
383
+ let current = {};
384
+ if (priorBytes !== null) {
385
+ const text = priorBytes.toString("utf8").trim();
386
+ // An empty existing file is treated as an empty object rather than a parse error (mirrors inject).
387
+ if (text.length > 0) {
388
+ current = parsePriorSettings(text);
389
+ }
390
+ }
391
+ // 2) Clone (never mutate the parsed prior) and toggle ONLY the two ultracode keys — every other key
392
+ // (the gate hook, the user's own config) is preserved verbatim.
393
+ const next = structuredClone(current);
394
+ if (active) {
395
+ next[ULTRACODE_SETTINGS_KEY] = true;
396
+ next[ULTRACODE_KEYWORD_TRIGGER_KEY] = true;
397
+ }
398
+ else {
399
+ delete next[ULTRACODE_SETTINGS_KEY];
400
+ delete next[ULTRACODE_KEYWORD_TRIGGER_KEY];
401
+ }
402
+ // 3) Durably write (temp+fsync+rename) so a concurrent (re-)spawn read never sees a half-written file.
403
+ await durableWrite(settingsPath, `${JSON.stringify(next, null, 2)}\n`);
404
+ }
@@ -0,0 +1,30 @@
1
+ /** Filename prefix for every materialized image — shared so a later cleanup task can match it. */
2
+ export declare const MATERIALIZED_IMAGE_PREFIX = "fork-acp-img-";
3
+ /**
4
+ * Map an ACP `image` block `mimeType` to a file extension the TUI's Read tool recognizes.
5
+ *
6
+ * Tolerant of a `;`-parameterized mimeType (e.g. `image/png; charset=binary`): only the media type
7
+ * before the first `;` is matched. Anything unrecognized — including `undefined`/`null` — falls back
8
+ * to `.img` so a path is still produced (the Read tool sniffs the bytes regardless of extension).
9
+ * Never throws.
10
+ */
11
+ export declare function extFor(mimeType: string | undefined | null): string;
12
+ /**
13
+ * Decode a base64 `image` payload to a uniquely-named temp file and return its absolute path.
14
+ *
15
+ * The file lives under {@link os.tmpdir} with a uuid name (no user-controlled characters) and an
16
+ * extension derived from `mimeType` via {@link extFor}. The write is SYNCHRONOUS on purpose — the
17
+ * caller (`promptToClaude`) is sync. May throw if the decode or write fails; the caller isolates that
18
+ * in its per-block try/catch (R1.3), so a bad image is skipped rather than aborting the whole prompt.
19
+ */
20
+ export declare function materializeImage(data: string, mimeType: string | undefined | null): string;
21
+ /**
22
+ * Story 058 / Task 2.1 (R2.1/R2.2) — best-effort unlink of every materialized temp image.
23
+ *
24
+ * The single unlink path shared by the turn-settle cleanup (prompt()'s catch + finally) and the
25
+ * session-teardown backstop, kept here so it stays OFFLINE-testable. NEVER throws: each unlink is
26
+ * isolated so a missing file (already removed, or a torn-down/raced turn) is a no-op rather than an
27
+ * error — which makes the teardown backstop idempotent with the prompt-finally cleanup. `undefined`
28
+ * (no image materialized this turn) returns immediately.
29
+ */
30
+ export declare function cleanupMaterializedImages(paths: readonly string[] | undefined): void;
@@ -0,0 +1,79 @@
1
+ // Story 058 / Task 1.1 — materialize an ACP `image` block to a temp file for the @path vision path.
2
+ //
3
+ // The fork drives the real `claude` TUI over a PTY; it has no API surface to push raw image bytes.
4
+ // The proven claude-2.1.195 path to get an image vision-encoded is to write the bytes to a file and
5
+ // reference it with `@<path>` in the prompt text, then ask the model to Read it — the TUI's Read tool
6
+ // then vision-encodes the file. This module is the standalone, OFFLINE-testable materialize seam:
7
+ //
8
+ // extFor(mimeType) → ".png" | ".jpg" | ".gif" | ".webp" | ".img" (PURE — mime → ext)
9
+ // materializeImage(data, mt) → <abs temp path> (DURABLE — base64 → temp file, returns the path)
10
+ //
11
+ // `promptToClaude` (acp-agent.ts) is SYNCHRONOUS and runs before the `await` at its call site, so the
12
+ // write here is synchronous (`writeFileSync`) by design — do NOT make it async. The temp path is
13
+ // uuid-named and fork-controlled (no user input in the filename), which is what lets a later cleanup
14
+ // task (Task 2) treat it as shell-safe. The decoded bytes are not logged.
15
+ import { writeFileSync, unlinkSync } from "node:fs";
16
+ import * as path from "node:path";
17
+ import * as os from "node:os";
18
+ import { randomUUID } from "node:crypto";
19
+ /** Filename prefix for every materialized image — shared so a later cleanup task can match it. */
20
+ export const MATERIALIZED_IMAGE_PREFIX = "fork-acp-img-";
21
+ /**
22
+ * Map an ACP `image` block `mimeType` to a file extension the TUI's Read tool recognizes.
23
+ *
24
+ * Tolerant of a `;`-parameterized mimeType (e.g. `image/png; charset=binary`): only the media type
25
+ * before the first `;` is matched. Anything unrecognized — including `undefined`/`null` — falls back
26
+ * to `.img` so a path is still produced (the Read tool sniffs the bytes regardless of extension).
27
+ * Never throws.
28
+ */
29
+ export function extFor(mimeType) {
30
+ // Strip any `;`-delimited parameters and normalize, so `image/JPEG; q=…` still matches.
31
+ const media = (mimeType ?? "").split(";", 1)[0].trim().toLowerCase();
32
+ switch (media) {
33
+ case "image/png":
34
+ return ".png";
35
+ case "image/jpeg":
36
+ return ".jpg";
37
+ case "image/gif":
38
+ return ".gif";
39
+ case "image/webp":
40
+ return ".webp";
41
+ default:
42
+ return ".img";
43
+ }
44
+ }
45
+ /**
46
+ * Decode a base64 `image` payload to a uniquely-named temp file and return its absolute path.
47
+ *
48
+ * The file lives under {@link os.tmpdir} with a uuid name (no user-controlled characters) and an
49
+ * extension derived from `mimeType` via {@link extFor}. The write is SYNCHRONOUS on purpose — the
50
+ * caller (`promptToClaude`) is sync. May throw if the decode or write fails; the caller isolates that
51
+ * in its per-block try/catch (R1.3), so a bad image is skipped rather than aborting the whole prompt.
52
+ */
53
+ export function materializeImage(data, mimeType) {
54
+ const bytes = Buffer.from(data, "base64");
55
+ const tempPath = path.join(os.tmpdir(), MATERIALIZED_IMAGE_PREFIX + randomUUID() + extFor(mimeType));
56
+ writeFileSync(tempPath, bytes);
57
+ return tempPath;
58
+ }
59
+ /**
60
+ * Story 058 / Task 2.1 (R2.1/R2.2) — best-effort unlink of every materialized temp image.
61
+ *
62
+ * The single unlink path shared by the turn-settle cleanup (prompt()'s catch + finally) and the
63
+ * session-teardown backstop, kept here so it stays OFFLINE-testable. NEVER throws: each unlink is
64
+ * isolated so a missing file (already removed, or a torn-down/raced turn) is a no-op rather than an
65
+ * error — which makes the teardown backstop idempotent with the prompt-finally cleanup. `undefined`
66
+ * (no image materialized this turn) returns immediately.
67
+ */
68
+ export function cleanupMaterializedImages(paths) {
69
+ if (!paths)
70
+ return;
71
+ for (const p of paths) {
72
+ try {
73
+ unlinkSync(p);
74
+ }
75
+ catch {
76
+ /* best-effort: the file is already gone, or a raced teardown removed it first */
77
+ }
78
+ }
79
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The claude version where `@<path>` → Read-vision was PROVEN to work (story 058 research).
3
+ *
4
+ * WHY this exact anchor: on claude 2.1.170 a `@path` image prompt did NOT vision-encode the picture —
5
+ * the TUI did not turn it into a Read the model could see (story 030). On claude 2.1.195 it DOES: the
6
+ * two-arm proof (a blue vs a green image under the same uuid filename, each described correctly) showed
7
+ * `@path` → Read vision-encodes. So 2.1.195 is the earliest version we are willing to call "confirmed";
8
+ * anything below is treated as unconfirmed and warned about.
9
+ */
10
+ export declare const VISION_CONFIRMED_CLAUDE_VERSION = "2.1.195";
11
+ /** The verdict {@link imageVisionSmoke} returns. Observability only — there is NO capability toggle here. */
12
+ export interface VisionSmokeResult {
13
+ /** True iff the running `claude` version is one we have CONFIRMED vision-encodes `@image` via Read. */
14
+ confirmed: boolean;
15
+ /**
16
+ * Human-readable one-liner. On `confirmed=true` it is a positive note. On `confirmed=false` it is the
17
+ * fail-loud WARNING text and NAMES the detected version (or "undetected") plus the confirmed anchor
18
+ * (mirrors drift-checks.ts's "name the offending value" habit).
19
+ */
20
+ detail: string;
21
+ }
22
+ /**
23
+ * Compare two `x.y.z` semver strings: `true` iff `a >= b` componentwise (major, then minor, then patch).
24
+ *
25
+ * Splits on `.`, parses the three leading numeric components; a missing/non-numeric component counts as
26
+ * `0` (so `"2.1"` reads as `2.1.0`). Private to this module — exported only so the unit test can pin the
27
+ * ordering directly. Pure.
28
+ */
29
+ export declare function versionGte(a: string, b: string): boolean;
30
+ /**
31
+ * PURE verdict: is the running `claude` `version` one we have CONFIRMED vision-encodes `@image` via Read?
32
+ *
33
+ * `confirmed: true` IFF `version` is a parseable leading `x.y.z` that is `>= {@link
34
+ * VISION_CONFIRMED_CLAUDE_VERSION}`. In that case `detail` is a positive one-liner.
35
+ *
36
+ * Otherwise (`null` / `undefined` / unparseable, OR a parseable version BELOW the anchor) `confirmed:
37
+ * false` and `detail` is the fail-loud WARNING text — it NAMES the detected version (or "undetected")
38
+ * and the confirmed anchor, and states that the image is NOT blocked (R3.1). No I/O; depends only on
39
+ * its argument.
40
+ */
41
+ export declare function imageVisionSmoke(version: string | null | undefined): VisionSmokeResult;
42
+ /**
43
+ * Best-effort fail-loud reporter: compute {@link imageVisionSmoke} and, WHEN unconfirmed, log the
44
+ * verdict's `detail` EXACTLY ONCE via `log` with an `[image-vision]` prefix (mirrors claude-path.ts's
45
+ * reportVersionDrift `[claude-path] …` style). Returns the verdict.
46
+ *
47
+ * Logs NOTHING on a confirmed version (a confirmed `claude` is the expected case — no news is good news,
48
+ * matching reportVersionDrift which is silent when there is no drift). NEVER blocks and NEVER throws out
49
+ * of the caller's intent — it is pure aside from the single conditional `log` call (R3.1, R3.2).
50
+ */
51
+ export declare function reportImageVisionSmoke(version: string | null | undefined, log?: (...args: unknown[]) => void): VisionSmokeResult;
@@ -0,0 +1,111 @@
1
+ // === Story 058 / Task 3.1 (R3.1, R3.2) — version-aware image-vision smoke (pure verdict + fail-loud) ==
2
+ //
3
+ // The whole "image input via @path" feature rests on a VERSION-FRAGILE claude behavior: a prompt
4
+ // containing `@<path>` makes the native TUI invoke the Read tool, and Read vision-encodes the image so
5
+ // the model can see it. This was PROVEN on claude 2.1.195 (story 058 research — two-arm proof: a blue
6
+ // image vs a green image under the same filename, each correctly described). It did NOT work on claude
7
+ // 2.1.170 (story 030): there, `@path` did not vision-encode, so an image prompt silently dropped its
8
+ // picture.
9
+ //
10
+ // Because the behavior is version-fragile, this module makes a too-old / unknown `claude` LOUD instead
11
+ // of silently dropping the image. It mirrors story 049's drift discipline:
12
+ // - the VERDICT is PURE (no I/O — like src/drift-checks.ts's `{surface, ok, detail}` checks), and
13
+ // - the REPORTER is best-effort + fail-loud one-liner (like src/claude-path.ts's reportVersionDrift,
14
+ // `[claude-path] …`); here the prefix is `[image-vision]`.
15
+ //
16
+ // CRITICAL — OBSERVABILITY ONLY, NEVER A GATE (R3.1): this smoke NEVER blocks the prompt and NEVER
17
+ // touches `promptCapabilities.image`, which stays literally `image: true` (acp-agent.ts:1413),
18
+ // UNCONDITIONALLY. The verdict's only output is `{confirmed, detail}` — there is deliberately NO field
19
+ // or return shape here that could disable a capability. A too-old `claude` produces a WARNING, not a
20
+ // degraded capability: the image still rides the @path delivery; we just warn that this `claude` is not
21
+ // a version we have confirmed vision-encodes it.
22
+ //
23
+ // LIMITATION (documented honestly): this is an OFFLINE `>= anchor` version-gate. It catches too-old and
24
+ // undetected/unparseable versions, but it CANNOT catch a *future regression* — a hypothetical 2.1.300
25
+ // that re-breaks @path-vision would still parse as `>= 2.1.195` and report confirmed. Catching a future
26
+ // regression needs the LIVE smoke (opt-in — story 058 R4 probe: actually send @image and check the
27
+ // model saw it), which this offline gate intentionally does NOT implement.
28
+ //
29
+ // node:test runner: `node --experimental-strip-types --test test/image-vision-smoke.test.ts`
30
+ /**
31
+ * The claude version where `@<path>` → Read-vision was PROVEN to work (story 058 research).
32
+ *
33
+ * WHY this exact anchor: on claude 2.1.170 a `@path` image prompt did NOT vision-encode the picture —
34
+ * the TUI did not turn it into a Read the model could see (story 030). On claude 2.1.195 it DOES: the
35
+ * two-arm proof (a blue vs a green image under the same uuid filename, each described correctly) showed
36
+ * `@path` → Read vision-encodes. So 2.1.195 is the earliest version we are willing to call "confirmed";
37
+ * anything below is treated as unconfirmed and warned about.
38
+ */
39
+ export const VISION_CONFIRMED_CLAUDE_VERSION = "2.1.195";
40
+ /**
41
+ * Compare two `x.y.z` semver strings: `true` iff `a >= b` componentwise (major, then minor, then patch).
42
+ *
43
+ * Splits on `.`, parses the three leading numeric components; a missing/non-numeric component counts as
44
+ * `0` (so `"2.1"` reads as `2.1.0`). Private to this module — exported only so the unit test can pin the
45
+ * ordering directly. Pure.
46
+ */
47
+ export function versionGte(a, b) {
48
+ const partsA = a.split(".");
49
+ const partsB = b.split(".");
50
+ for (let i = 0; i < 3; i++) {
51
+ const na = toComponent(partsA[i]);
52
+ const nb = toComponent(partsB[i]);
53
+ if (na !== nb) {
54
+ return na > nb;
55
+ }
56
+ }
57
+ return true; // all three components equal → a >= b holds
58
+ }
59
+ /** Parse one semver component to a non-negative integer; missing / non-numeric → 0. */
60
+ function toComponent(part) {
61
+ if (part === undefined) {
62
+ return 0;
63
+ }
64
+ const n = Number.parseInt(part, 10);
65
+ return Number.isNaN(n) ? 0 : n;
66
+ }
67
+ /** Matches a leading `x.y.z` triple — same shape parseClaudeVersion extracts (claude-path.ts:58). */
68
+ const SEMVER_RE = /^(\d+\.\d+\.\d+)/;
69
+ /**
70
+ * PURE verdict: is the running `claude` `version` one we have CONFIRMED vision-encodes `@image` via Read?
71
+ *
72
+ * `confirmed: true` IFF `version` is a parseable leading `x.y.z` that is `>= {@link
73
+ * VISION_CONFIRMED_CLAUDE_VERSION}`. In that case `detail` is a positive one-liner.
74
+ *
75
+ * Otherwise (`null` / `undefined` / unparseable, OR a parseable version BELOW the anchor) `confirmed:
76
+ * false` and `detail` is the fail-loud WARNING text — it NAMES the detected version (or "undetected")
77
+ * and the confirmed anchor, and states that the image is NOT blocked (R3.1). No I/O; depends only on
78
+ * its argument.
79
+ */
80
+ export function imageVisionSmoke(version) {
81
+ const parsed = typeof version === "string" ? SEMVER_RE.exec(version) : null;
82
+ if (parsed && versionGte(parsed[1], VISION_CONFIRMED_CLAUDE_VERSION)) {
83
+ return {
84
+ confirmed: true,
85
+ detail: `claude ${parsed[1]} >= ${VISION_CONFIRMED_CLAUDE_VERSION} — @image is confirmed to vision-encode via Read`,
86
+ };
87
+ }
88
+ const named = parsed ? parsed[1] : "undetected";
89
+ return {
90
+ confirmed: false,
91
+ detail: `@image vision-encoding is UNCONFIRMED on this claude (detected ${named}; confirmed at ` +
92
+ `${VISION_CONFIRMED_CLAUDE_VERSION}+). Sending images anyway — image input is NOT blocked — but ` +
93
+ `if the picture is ignored, upgrade claude to ${VISION_CONFIRMED_CLAUDE_VERSION} or newer`,
94
+ };
95
+ }
96
+ /**
97
+ * Best-effort fail-loud reporter: compute {@link imageVisionSmoke} and, WHEN unconfirmed, log the
98
+ * verdict's `detail` EXACTLY ONCE via `log` with an `[image-vision]` prefix (mirrors claude-path.ts's
99
+ * reportVersionDrift `[claude-path] …` style). Returns the verdict.
100
+ *
101
+ * Logs NOTHING on a confirmed version (a confirmed `claude` is the expected case — no news is good news,
102
+ * matching reportVersionDrift which is silent when there is no drift). NEVER blocks and NEVER throws out
103
+ * of the caller's intent — it is pure aside from the single conditional `log` call (R3.1, R3.2).
104
+ */
105
+ export function reportImageVisionSmoke(version, log = console.error) {
106
+ const result = imageVisionSmoke(version);
107
+ if (!result.confirmed) {
108
+ log(`[image-vision] ${result.detail}`);
109
+ }
110
+ return result;
111
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  #!/usr/bin/env node
2
2
  export {};
3
- //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -88,6 +88,12 @@ else {
88
88
  // (src/live-subagent-env.ts) so the truth table is unit-checkable; threaded through
89
89
  // runAcp → AgentDeps → the agent.
90
90
  const liveSubagentWatch = liveSubagentWatchEnabled();
91
+ // Story 056 / Task 7 (R3.5): enable the live agent-discovery probe in PRODUCTION only. The probe
92
+ // (agent-catalog.ts) spawns `claude --agent <sentinel>` once per createSession to read `claude`'s
93
+ // canonical persona list (enabled plugins + built-ins) for the 4th `agent` selector. Gated opt-in so
94
+ // the unit suite — which imports modules directly without this flag — stays on the hermetic glob
95
+ // fallback and never spawns the real binary. `??=` keeps a user opt-out (`FORK_AGENT_PROBE=0`).
96
+ process.env.FORK_AGENT_PROBE ??= "1";
91
97
  const { connection, agent } = runAcp({ usageUpdate, gate, liveDiff, liveSubagentWatch });
92
98
  async function shutdown() {
93
99
  await agent.dispose().catch((err) => {
package/dist/jsonl.d.ts CHANGED
@@ -264,4 +264,3 @@ export declare function stripHeavyImages(message: unknown, imageSkipBytes?: numb
264
264
  * @returns the typed {@link JsonlEvent}.
265
265
  */
266
266
  export declare function projectEvent(message: unknown, opts?: ProjectOptions): JsonlEvent;
267
- //# sourceMappingURL=jsonl.d.ts.map
package/dist/lib.d.ts CHANGED
@@ -3,4 +3,3 @@ export { nodeToWebReadable, nodeToWebWritable, Pushable, unreachable } from "./u
3
3
  export { toolInfoFromToolUse, toDisplayPath, planEntries, toolUpdateFromToolResult, } from "./tools.js";
4
4
  export { SettingsManager, type SettingsManagerOptions } from "./settings.js";
5
5
  export type { ClaudePlanEntry } from "./tools.js";
6
- //# sourceMappingURL=lib.d.ts.map