@lucascouts/claude-agent-tui 0.4.0 → 0.5.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.
@@ -12,6 +12,7 @@ import type { JsonlWatcher, GetMessages, SessionMessage } from "./engine-watcher
12
12
  import type { LocateOptions } from "./jsonl.js";
13
13
  import type { DiffEnrichedReaderOptions } from "./diff-enriched-reader.js";
14
14
  import type { ListSubagents, GetSubagentMessages } from "./subagent-source.js";
15
+ import { type SidechainToolUse } from "./subagent-gate.js";
15
16
  import type { SubagentWatcher } from "./subagent-watcher.js";
16
17
  import type { DetectorSchedule, EndOfTurnDetector } from "./end-of-turn.js";
17
18
  import type { SessionGate, SessionGateOptions } from "./permissions/gate-wiring.js";
@@ -48,6 +49,12 @@ type Session = {
48
49
  * was surfaced in an earlier pump). Per-session — sub-agent row uuids are session-scoped.
49
50
  */
50
51
  emittedNested: Set<string>;
52
+ /** Story 054 — per-session dedup Set of sidechain inner tool_use ids already fed to the gate
53
+ * correlator (R3 exactly-once). Lazy-init on the gated pump path; absent on a no-gate session. */
54
+ registeredSidechain?: Set<string>;
55
+ /** Story 054 — maps a sidechain inner tool_use id → its SidechainToolUse (parentId/toolName/
56
+ * toolInput), for the decide()-time parent-Task dialog relay (Tasks 3/4). */
57
+ sidechainParentMap?: Map<string, SidechainToolUse>;
51
58
  /** The managed engine that owns the PTY + watcher; used for idempotent teardown (story 014). */
52
59
  engine?: SessionEngine;
53
60
  cancelled: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"acp-agent.d.ts","sourceRoot":"","sources":["../src/acp-agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"acp-agent.d.ts","sourceRoot":"","sources":["../src/acp-agent.ts"],"names":[],"mappings":"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"}
package/dist/acp-agent.js CHANGED
@@ -17,6 +17,7 @@ import { resolveWatchTarget } from "./jsonl.js";
17
17
  import { linearizeTurns, readOrderedMessages, defaultGetMessages } from "./linearize.js";
18
18
  import { createDiffEnrichedReader } from "./diff-enriched-reader.js";
19
19
  import { sourceSubagentRows, defaultListSubagents, defaultGetSubagentMessages, hasSubagentSpawn, spawnIdsOpen, } from "./subagent-source.js";
20
+ import { collectSidechainToolUses, registerSidechainGateToolUses, } from "./subagent-gate.js";
20
21
  import { createSubagentWatcher } from "./subagent-watcher.js";
21
22
  import { classifyDiffSource, diffToolCallUpdate } from "./diff-source.js";
22
23
  import { guardEvent } from "./billing/entrypoint-guard.js";
@@ -437,6 +438,47 @@ function registerGateToolUses(raw, gate) {
437
438
  }
438
439
  }
439
440
  }
441
+ /**
442
+ * Story 054 — best-effort label for the subagent that spawned a sidechain inner tool, for the ACP
443
+ * dialog title + the R4 visible-deny line. `parentId` is the spawning `Task`/`Agent` tool_use id; we
444
+ * scan the MAIN chain `messages` for the assistant `tool_use` block with `id === parentId` whose `name`
445
+ * is `Task`/`Agent`, and return its `input.subagent_type ?? input.description` when a string, else
446
+ * undefined. An orphan (`parentId === null`) has no spawn to name → undefined. Tolerant of the reduced
447
+ * shape in the {@link hasSubagentSpawn} style: non-object rows/messages and non-array content are skipped.
448
+ */
449
+ function deriveSubagentLabel(messages, parentId) {
450
+ if (parentId === null)
451
+ return undefined;
452
+ for (const msg of messages) {
453
+ if (msg === null || typeof msg !== "object")
454
+ continue;
455
+ const inner = msg.message;
456
+ if (inner === null || typeof inner !== "object")
457
+ continue;
458
+ const content = inner.content;
459
+ if (!Array.isArray(content))
460
+ continue;
461
+ for (const block of content) {
462
+ if (block === null || typeof block !== "object")
463
+ continue;
464
+ const b = block;
465
+ if (b.type !== "tool_use" || b.id !== parentId)
466
+ continue;
467
+ if (b.name !== "Task" && b.name !== "Agent")
468
+ continue;
469
+ const input = b.input;
470
+ if (input === null || typeof input !== "object")
471
+ return undefined;
472
+ const i = input;
473
+ if (typeof i.subagent_type === "string")
474
+ return i.subagent_type;
475
+ if (typeof i.description === "string")
476
+ return i.description;
477
+ return undefined;
478
+ }
479
+ }
480
+ return undefined;
481
+ }
440
482
  export class ClaudeAcpAgent {
441
483
  constructor(client, logger, engine = createStubEngine(), deps = {}) {
442
484
  this.backgroundTerminals = {};
@@ -1480,6 +1522,20 @@ export class ClaudeAcpAgent {
1480
1522
  // un-merged `messages` slice exactly-once (R4.1 structural: sub-agent rows never advance
1481
1523
  // `detectorCursor` nor register as gate tool_uses).
1482
1524
  await this.emitLinearizedWithNested(sessionId, session, messages);
1525
+ // === SEAM(054) — feed the gate correlator from the sidechain rows (R3). Gated + LIVE-ONLY: runs
1526
+ // only with a present, non-torndown gate, and NEVER inside the shared emitLinearizedWithNested that
1527
+ // session/load replay also calls — so replay stays pure (U3). Additive: touches only the correlator +
1528
+ // the per-session dedup Set/parentMap, never emittedNested nor the detector cursor (U2/U5).
1529
+ if (session.gate && !session.gate.isTorndown) {
1530
+ const subagentRows = await sourceSubagentRows(sessionId, messages, {
1531
+ dir: session.cwd,
1532
+ listSubagents: this.listSubagents,
1533
+ getSubagentMessages: this.getSubagentMessages,
1534
+ });
1535
+ session.registeredSidechain ??= new Set();
1536
+ session.sidechainParentMap ??= new Map();
1537
+ registerSidechainGateToolUses(collectSidechainToolUses(subagentRows), session.gate.correlator, session.registeredSidechain, session.sidechainParentMap);
1538
+ }
1483
1539
  // === SEAM(044) — Option-B sub-agent watcher: arm/refresh/teardown rides the MAIN-CHAIN spawn
1484
1540
  // signal (`hasSubagentSpawn` + `spawnIdsOpen` over the FULL pumped messages — design key
1485
1541
  // decision 4: NOT the detector's `openTaskIds`, the very inference that failed live in the
@@ -1811,7 +1867,20 @@ export class ClaudeAcpAgent {
1811
1867
  // (idempotent with teardownSession) so a crashed TUI leaks no port/server/scratch.
1812
1868
  if (gate) {
1813
1869
  const boundGate = gate;
1814
- boundGate.bindSession(startedSessionId, () => void this.pumpUpdates(startedSessionId));
1870
+ // Story 054 — the third arg is a LAZY subagent relay resolver: at decide()-time it reads the
1871
+ // session's sidechainParentMap (populated by the pump) to map an inner tool_use id to its parent
1872
+ // Task id + a derived label, so the gate relays a subagent tool's dialog under the parent Task
1873
+ // (R1/R2) or fails loud (R4). A main-chain id (no map entry) returns undefined → unchanged (U1).
1874
+ boundGate.bindSession(startedSessionId, () => void this.pumpUpdates(startedSessionId), (innerId) => {
1875
+ const s = this.sessions[startedSessionId];
1876
+ const entry = s?.sidechainParentMap?.get(innerId);
1877
+ if (!entry)
1878
+ return undefined;
1879
+ return {
1880
+ parentId: entry.parentId,
1881
+ subagentLabel: deriveSubagentLabel(s?.lastMessages ?? [], entry.parentId) ?? "subagent",
1882
+ };
1883
+ });
1815
1884
  boundGate.bindPty(started.pty);
1816
1885
  started.pty.onExit(() => void boundGate.teardown());
1817
1886
  }
@@ -1,12 +1,55 @@
1
+ /**
2
+ * Provenance pin the live `claude` version is measured against — kept in lockstep
3
+ * with `fork/.fork-provenance.json`'s externalRuntimeDeps `claude` entry. A live
4
+ * binary whose version differs only triggers a soft drift *warning* (R1.2), never
5
+ * a resolution failure.
6
+ */
7
+ export declare const PROVENANCE_CLAUDE_VERSION = "2.1.159";
8
+ /**
9
+ * Extract the leading `x.y.z` semver from `claude --version` output.
10
+ *
11
+ * e.g. `"2.1.186 (Claude Code)\n"` → `"2.1.186"`. Pure; returns `null` when no
12
+ * `x.y.z` triple is present (`""`, `"not a version"`, `"\n\n"` → `null`).
13
+ */
14
+ export declare function parseClaudeVersion(output: string): string | null;
15
+ /**
16
+ * Injectable seam for running `claude --version` — args-list, NEVER a shell (C5).
17
+ * Returns the binary's stdout. The default implementation is `defaultVersionExec`.
18
+ */
19
+ export type VersionExec = (binPath: string, args: readonly string[]) => string;
20
+ /**
21
+ * Best-effort detection of the live `claude` version at `binPath`.
22
+ *
23
+ * Runs `--version` (args-list, no shell) via the injectable `exec` seam and parses
24
+ * the result. Observability only — it NEVER throws: any failure (spawn error,
25
+ * timeout, unparseable output) yields `null`.
26
+ */
27
+ export declare function detectClaudeVersion(binPath: string, exec?: VersionExec): string | null;
28
+ /**
29
+ * Emit a one-line drift warning via `log` IFF `detected` is known AND differs from
30
+ * `pinned` (R1.2). Silent when there is no drift or detection failed (`detected`
31
+ * null). Pure aside from the single `log` call.
32
+ */
33
+ export declare function reportVersionDrift(detected: string | null, pinned: string, log: (...args: unknown[]) => void): void;
34
+ /** Injectable seams for `resolveClaudePath` so callers/tests can stub the version probe. */
35
+ export interface ResolveOptions {
36
+ /** Override the version probe (default: {@link detectClaudeVersion}). */
37
+ detectVersion?: (binPath: string) => string | null;
38
+ /** Override the warning sink (default: `console.error` → stderr). */
39
+ log?: (...args: unknown[]) => void;
40
+ }
1
41
  /**
2
42
  * Resolve an absolute path to an executable subscription `claude` binary.
3
43
  *
4
- * Resolution order:
5
- * 1. `claude` found on `process.env.PATH` (which-style scan).
6
- * 2. The documented native-binary path under the vscode extension.
44
+ * Resolution is PATH-only: scan each `process.env.PATH` entry for an executable
45
+ * `claude` (which-style). On a hit, run a BEST-EFFORT version probe + drift
46
+ * warning (R1.1, R1.2) and return the SAME absolute PATH candidate (R5.2 — the
47
+ * probe never alters the returned path). On a miss, throw a fail-loud Error
48
+ * naming the PATH lookup plus the install hint (R1.3, R1.4) — there is NO
49
+ * native-binary / `.vscode` fallback anymore.
7
50
  *
8
51
  * @returns absolute path to an executable `claude`.
9
- * @throws Error naming both attempted locations if neither is executable.
52
+ * @throws Error naming the PATH lookup attempt if no executable `claude` is found.
10
53
  */
11
- export declare function resolveClaudePath(): string;
54
+ export declare function resolveClaudePath(opts?: ResolveOptions): string;
12
55
  //# sourceMappingURL=claude-path.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"claude-path.d.ts","sourceRoot":"","sources":["../src/claude-path.ts"],"names":[],"mappings":"AAsCA;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAyB1C"}
1
+ {"version":3,"file":"claude-path.d.ts","sourceRoot":"","sources":["../src/claude-path.ts"],"names":[],"mappings":"AA6BA;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAenD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;AAc/E;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,WAAgC,GACrC,MAAM,GAAG,IAAI,CAMf;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAChC,IAAI,CAON;AAED,4FAA4F;AAC5F,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IACnD,qEAAqE;IACrE,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,GAAE,cAAmB,GAAG,MAAM,CA6BnE"}
@@ -1,22 +1,40 @@
1
- // Resolves the subscription `claude` binary from PATH (story 012, R1/R1.2).
1
+ // Resolves the subscription `claude` binary from PATH (story 012 R1/R1.2;
2
+ // story 049 R1.1–R1.5, R5.2 — version-aware + fail-loud).
2
3
  //
3
4
  // The fork must drive the user's *subscription* claude (resolved from PATH),
4
5
  // NOT the binary embedded in @anthropic-ai/claude-agent-sdk — the SDK-embedded
5
6
  // path bills as credit, while the PATH `claude` bills as signature
6
7
  // (`entrypoint == 'cli'`, the E1 keystone — experiments/DEGRAU0-RESULTS.md).
7
- // This helper is ported 1:1 from the E1-validated experiments/lib/claude-path.ts,
8
- // preserving its resolution order: PATH first, then the documented native-binary
9
- // fallback (IMPLEMENTACAO-FORK-ACP §17), else throw naming BOTH attempted
10
- // locations (R1.3).
8
+ //
9
+ // Resolution is PATH-only. Story 049 DROPPED the hardcoded `2.1.159`
10
+ // native-binary fallback (a version-specific `.vscode` path that almost
11
+ // certainly no longer exists is a silent dead end): on a miss we now throw a
12
+ // fail-loud Error naming the PATH lookup that was attempted plus the install
13
+ // hint, so a missing CLI surfaces here rather than as an opaque spawn ENOENT
14
+ // deep in the PTY engine (story 013).
15
+ //
16
+ // On a successful resolve the helper additionally performs a BEST-EFFORT
17
+ // `claude --version` probe and logs a one-line provenance-drift warning when
18
+ // the live binary differs from the pinned `PROVENANCE_CLAUDE_VERSION`
19
+ // (fork/.fork-provenance.json). Drift is observability, never a hard stop: the
20
+ // detect/log path never throws out of `resolveClaudePath` and never changes the
21
+ // returned path (R5.2 — byte-identical return). The version probe spawns the
22
+ // binary directly (execFileSync, args-list, NEVER a shell — C5).
11
23
  //
12
24
  // Dependency-free on purpose: only node: builtins (FORK.md pins node-pty + the
13
- // SDK as the only runtime deps; this adds none).
25
+ // SDK as the only runtime deps; this adds none). R1.5 / C4.
26
+ import { execFileSync } from "node:child_process";
14
27
  import { accessSync, constants } from "node:fs";
15
- import { homedir } from "node:os";
16
28
  import { delimiter, join } from "node:path";
17
- // Documented native-binary fallback (IMPLEMENTACAO-FORK-ACP §17), for claude
18
- // 2.1.159 (frozen base doc anchor, §3). `~` is expanded via os.homedir.
19
- const FALLBACK_RELATIVE_PATH = join(".vscode", "extensions", "anthropic.claude-code-2.1.159-linux-x64", "resources", "native-binary", "claude");
29
+ /**
30
+ * Provenance pin the live `claude` version is measured against — kept in lockstep
31
+ * with `fork/.fork-provenance.json`'s externalRuntimeDeps `claude` entry. A live
32
+ * binary whose version differs only triggers a soft drift *warning* (R1.2), never
33
+ * a resolution failure.
34
+ */
35
+ export const PROVENANCE_CLAUDE_VERSION = "2.1.159";
36
+ /** Shared install hint appended to the fail-loud resolution error (R1.3). */
37
+ const INSTALL_HINT = 'Install the Claude Code subscription CLI or ensure "claude" is on PATH.';
20
38
  /** True if `p` exists and is executable by the current process. */
21
39
  function isExecutable(p) {
22
40
  try {
@@ -27,35 +45,91 @@ function isExecutable(p) {
27
45
  return false;
28
46
  }
29
47
  }
48
+ /**
49
+ * Extract the leading `x.y.z` semver from `claude --version` output.
50
+ *
51
+ * e.g. `"2.1.186 (Claude Code)\n"` → `"2.1.186"`. Pure; returns `null` when no
52
+ * `x.y.z` triple is present (`""`, `"not a version"`, `"\n\n"` → `null`).
53
+ */
54
+ export function parseClaudeVersion(output) {
55
+ const match = /(\d+\.\d+\.\d+)/.exec(output);
56
+ return match ? match[1] : null;
57
+ }
58
+ /**
59
+ * Production `VersionExec`: spawn the binary directly via `execFileSync` (args-list,
60
+ * NO shell — C5) and capture stdout. Bounded by a short timeout; stderr discarded.
61
+ */
62
+ function defaultVersionExec(binPath, args) {
63
+ return execFileSync(binPath, [...args], {
64
+ encoding: "utf8",
65
+ timeout: 5000,
66
+ stdio: ["ignore", "pipe", "ignore"],
67
+ });
68
+ }
69
+ /**
70
+ * Best-effort detection of the live `claude` version at `binPath`.
71
+ *
72
+ * Runs `--version` (args-list, no shell) via the injectable `exec` seam and parses
73
+ * the result. Observability only — it NEVER throws: any failure (spawn error,
74
+ * timeout, unparseable output) yields `null`.
75
+ */
76
+ export function detectClaudeVersion(binPath, exec = defaultVersionExec) {
77
+ try {
78
+ return parseClaudeVersion(exec(binPath, ["--version"]));
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ /**
85
+ * Emit a one-line drift warning via `log` IFF `detected` is known AND differs from
86
+ * `pinned` (R1.2). Silent when there is no drift or detection failed (`detected`
87
+ * null). Pure aside from the single `log` call.
88
+ */
89
+ export function reportVersionDrift(detected, pinned, log) {
90
+ if (detected !== null && detected !== pinned) {
91
+ log(`[claude-path] claude version drift: detected ${detected} but provenance pins ${pinned} ` +
92
+ `(continuing — drift is a warning, not a hard stop)`);
93
+ }
94
+ }
30
95
  /**
31
96
  * Resolve an absolute path to an executable subscription `claude` binary.
32
97
  *
33
- * Resolution order:
34
- * 1. `claude` found on `process.env.PATH` (which-style scan).
35
- * 2. The documented native-binary path under the vscode extension.
98
+ * Resolution is PATH-only: scan each `process.env.PATH` entry for an executable
99
+ * `claude` (which-style). On a hit, run a BEST-EFFORT version probe + drift
100
+ * warning (R1.1, R1.2) and return the SAME absolute PATH candidate (R5.2 — the
101
+ * probe never alters the returned path). On a miss, throw a fail-loud Error
102
+ * naming the PATH lookup plus the install hint (R1.3, R1.4) — there is NO
103
+ * native-binary / `.vscode` fallback anymore.
36
104
  *
37
105
  * @returns absolute path to an executable `claude`.
38
- * @throws Error naming both attempted locations if neither is executable.
106
+ * @throws Error naming the PATH lookup attempt if no executable `claude` is found.
39
107
  */
40
- export function resolveClaudePath() {
41
- // 1. PATH lookup: scan each PATH entry for an executable `claude`.
108
+ export function resolveClaudePath(opts = {}) {
109
+ // PATH lookup: scan each PATH entry for an executable `claude`.
42
110
  const pathEnv = process.env.PATH ?? "";
43
111
  const pathEntries = pathEnv.split(delimiter).filter((entry) => entry.length > 0);
44
112
  for (const entry of pathEntries) {
45
113
  const candidate = join(entry, "claude");
46
114
  if (isExecutable(candidate)) {
115
+ // Best-effort, non-fatal version observability — must NOT change the
116
+ // returned path and must NEVER throw out of the success path (R5.2).
117
+ const log = opts.log ?? console.error;
118
+ try {
119
+ const detected = (opts.detectVersion ?? detectClaudeVersion)(candidate);
120
+ if (detected) {
121
+ log(`[claude-path] resolved claude ${candidate} (version ${detected})`);
122
+ }
123
+ reportVersionDrift(detected, PROVENANCE_CLAUDE_VERSION, log);
124
+ }
125
+ catch {
126
+ // Observability is best-effort; never let it break resolution.
127
+ }
47
128
  return candidate;
48
129
  }
49
130
  }
50
- // 2. Documented native-binary fallback (~ expanded via os.homedir).
51
- const fallback = join(homedir(), FALLBACK_RELATIVE_PATH);
52
- if (isExecutable(fallback)) {
53
- return fallback;
54
- }
55
- // 3. Neither exists: throw naming BOTH attempted locations.
56
- throw new Error(`Could not resolve an executable "claude" binary. Tried: ` +
57
- `(1) PATH lookup over ${pathEntries.length} entr${pathEntries.length === 1 ? "y" : "ies"} ` +
58
- `("${pathEnv}"), and ` +
59
- `(2) the documented native-binary fallback "${fallback}". ` +
60
- `Install the Claude Code subscription CLI or ensure "claude" is on PATH.`);
131
+ // No executable `claude` on PATH: fail loud naming the lookup (no fallback).
132
+ throw new Error(`Could not resolve an executable "claude" binary. ` +
133
+ `Tried: PATH lookup over ${pathEntries.length} entr${pathEntries.length === 1 ? "y" : "ies"} ` +
134
+ `("${pathEnv}"). ${INSTALL_HINT}`);
61
135
  }
@@ -0,0 +1,57 @@
1
+ /** The verdict a single drift check returns. `surface` names which binary-contact surface was checked. */
2
+ export interface CheckResult {
3
+ /** The binary-contact surface this verdict is about (e.g. "stop_reason", "jsonl_shape"). */
4
+ surface: string;
5
+ /** True iff no drift was detected on this surface. */
6
+ ok: boolean;
7
+ /** Human-readable summary; on `ok=false` it NAMES every offending value (the consumers print it). */
8
+ detail: string;
9
+ }
10
+ /**
11
+ * The stop_reason values mapStopReason handles EXPLICITLY (stop-reason-map.ts:26-33). Everything else
12
+ * hits its default branch. These are the "terminal" taxonomy the bridge answers a turn with.
13
+ */
14
+ export declare const HANDLED_STOP_REASONS: ReadonlySet<string>;
15
+ /**
16
+ * stop_reason values that are NON-TERMINAL but EXPECTED (so not drift): the model pauses mid-turn. They
17
+ * hit mapStopReason's default branch BY DESIGN. `tool_use` is the real one seen in transcripts.
18
+ */
19
+ export declare const KNOWN_NONTERMINAL_STOP_REASONS: ReadonlySet<string>;
20
+ /** The core top-level keys EVERY raw JSONL event line must carry (jsonl.ts:513-523 — the required set). */
21
+ export declare const REQUIRED_JSONL_KEYS: ReadonlySet<string>;
22
+ /**
23
+ * Every top-level key a raw JSONL line is ALLOWED to carry = {@link REQUIRED_JSONL_KEYS} ∪ the known
24
+ * optional universal / per-type fields documented in jsonl.ts:528-543 (plus the universal fields seen
25
+ * in real fixtures). A key OUTSIDE this set is a NEW/unexpected field → drift.
26
+ */
27
+ export declare const KNOWN_JSONL_KEYS: ReadonlySet<string>;
28
+ /** The keystroke the native TUI permission prompt accepts for Yes — mirror of allow-inject.ts:29. */
29
+ export declare const EXPECTED_ALLOW_KEYSTROKE: string;
30
+ /**
31
+ * R2.1 — verify every non-null `message.stop_reason` is either HANDLED or a KNOWN non-terminal value.
32
+ *
33
+ * `null`/`undefined`/absent stop_reason is NOT drift (absence) and is ignored. A non-null value outside
34
+ * {@link HANDLED_STOP_REASONS} ∪ {@link KNOWN_NONTERMINAL_STOP_REASONS} (e.g. a new `pause_turn`) trips
35
+ * the check; `detail` names every offending value. Pure: no I/O, depends only on `lines`.
36
+ */
37
+ export declare function checkStopReasons(lines: ReadonlyArray<Record<string, unknown>>): CheckResult;
38
+ /**
39
+ * R2.2 — verify every SDK content-block type is covered by a handled `switch` case. A block type in
40
+ * `sdkBlockTypes` but NOT in `handledCases` (e.g. a new `server_tool_use`) is uncovered drift; `detail`
41
+ * names each uncovered type. Pure: depends only on its two iterable arguments.
42
+ */
43
+ export declare function checkContentBlockCoverage(sdkBlockTypes: Iterable<string>, handledCases: Iterable<string>): CheckResult;
44
+ /**
45
+ * R2.3 — verify each raw JSONL line carries every {@link REQUIRED_JSONL_KEYS} core key and NO key
46
+ * outside {@link KNOWN_JSONL_KEYS}. A missing core key OR a new/unexpected top-level key is drift;
47
+ * `detail` names the offending key(s). Guards the RAW on-disk shape (camelCase), not the reduced
48
+ * getSessionMessages shape. Pure: depends only on `lines`.
49
+ */
50
+ export declare function checkJsonlShape(lines: ReadonlyArray<Record<string, unknown>>): CheckResult;
51
+ /**
52
+ * R2.4 — verify the gate's allow keystroke is still the canonical {@link EXPECTED_ALLOW_KEYSTROKE}
53
+ * (`"1\r"`). On mismatch `detail` references both the expected and the actual keystroke. Mirror of
54
+ * allow-inject.ts:29 `KEYSTROKE_YES`. Pure: depends only on `keystroke`.
55
+ */
56
+ export declare function checkAllowKeystroke(keystroke: string): CheckResult;
57
+ //# sourceMappingURL=drift-checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drift-checks.d.ts","sourceRoot":"","sources":["../src/drift-checks.ts"],"names":[],"mappings":"AA4BA,0GAA0G;AAC1G,MAAM,WAAW,WAAW;IAC1B,4FAA4F;IAC5F,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,EAAE,EAAE,OAAO,CAAC;IACZ,qGAAqG;IACrG,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAKnD,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,WAAW,CAAC,MAAM,CAAyB,CAAC;AAEzF,2GAA2G;AAC3G,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,MAAM,CAMlD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAsB/C,CAAC;AAEH,qGAAqG;AACrG,eAAO,MAAM,wBAAwB,EAAE,MAAc,CAAC;AA2BtD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,WAAW,CAe3F;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC/B,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,GAC7B,WAAW,CASb;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,WAAW,CAiB1F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAalE"}
@@ -0,0 +1,183 @@
1
+ // === Story 049 / Task 2.1 (R2.1, R2.2, R2.3, R2.4) — pure binary-drift checks (no I/O) ============
2
+ //
3
+ // Single source of truth for "what binary-drift looks like" across the four surfaces the bridge reads
4
+ // from the claude binary. Each check is PURE and referentially transparent: it takes ALREADY-PARSED
5
+ // input and returns a `{surface, ok, detail}` verdict. There is NO `fs`, NO `child_process`/`spawn`,
6
+ // NO `process` here — the I/O (read fixtures / spawn the live binary) belongs to the OFFLINE detector
7
+ // (Task 3) and `smoke:live` (Task 5), which both reuse these verdicts. Keeping this module I/O-free is
8
+ // the foundation for Task 3's spawn-import guard.
9
+ //
10
+ // MIRROR ANCHORS (the constants below mirror these real source surfaces — keep them honest):
11
+ // - stop_reason taxonomy ........ src/stop-reason-map.ts:26-33 (the 4 explicit, non-default cases of
12
+ // mapStopReason: end_turn / stop_sequence / max_tokens / refusal)
13
+ // - allow keystroke ............. src/permissions/allow-inject.ts:29 (KEYSTROKE_YES = "1\r")
14
+ // - raw JSONL field set ......... src/jsonl.ts:513-544 (the documented universal + per-type fields)
15
+ //
16
+ // TWO NON-OBVIOUS DECISIONS (both deliberate reads of the requirements, not oversights):
17
+ // 1. `tool_use` is KNOWN-NON-TERMINAL, NOT drift. mapStopReason switches on the 4 terminal cases and
18
+ // DEFAULTS everything else to "end_turn" (with a drift log). `tool_use` legitimately appears in
19
+ // real transcripts — the model pauses to call a tool; that is NOT a turn end — and it hits that
20
+ // default branch BY DESIGN. So it is recorded as expected (KNOWN_NONTERMINAL_STOP_REASONS) and
21
+ // must NOT fail the check. A genuinely NEW value (e.g. `pause_turn`) still fails.
22
+ // 2. `checkJsonlShape` guards the RAW on-disk JSONL shape (camelCase `sessionId`, `parentUuid`, ...),
23
+ // NOT the reduced `getSessionMessages` shape (`session_id` snake_case) that R2.3 literally lists.
24
+ // This is a deliberate read of R2.3's intent: the drift we want to detect is in the on-disk JSONL
25
+ // the bridge actually tails, so the check mirrors jsonl.ts's documented raw field set.
26
+ //
27
+ // node:test runner: `node --experimental-strip-types --test test/drift-checks.test.ts`
28
+ // --- mirror constants ---------------------------------------------------------------------------
29
+ /**
30
+ * The stop_reason values mapStopReason handles EXPLICITLY (stop-reason-map.ts:26-33). Everything else
31
+ * hits its default branch. These are the "terminal" taxonomy the bridge answers a turn with.
32
+ */
33
+ export const HANDLED_STOP_REASONS = new Set([
34
+ "end_turn",
35
+ "stop_sequence",
36
+ "max_tokens",
37
+ "refusal",
38
+ ]);
39
+ /**
40
+ * stop_reason values that are NON-TERMINAL but EXPECTED (so not drift): the model pauses mid-turn. They
41
+ * hit mapStopReason's default branch BY DESIGN. `tool_use` is the real one seen in transcripts.
42
+ */
43
+ export const KNOWN_NONTERMINAL_STOP_REASONS = new Set(["tool_use"]);
44
+ /** The core top-level keys EVERY raw JSONL event line must carry (jsonl.ts:513-523 — the required set). */
45
+ export const REQUIRED_JSONL_KEYS = new Set([
46
+ "uuid",
47
+ "type",
48
+ "timestamp",
49
+ "sessionId",
50
+ "message",
51
+ ]);
52
+ /**
53
+ * Every top-level key a raw JSONL line is ALLOWED to carry = {@link REQUIRED_JSONL_KEYS} ∪ the known
54
+ * optional universal / per-type fields documented in jsonl.ts:528-543 (plus the universal fields seen
55
+ * in real fixtures). A key OUTSIDE this set is a NEW/unexpected field → drift.
56
+ */
57
+ export const KNOWN_JSONL_KEYS = new Set([
58
+ ...REQUIRED_JSONL_KEYS,
59
+ // universal fields (present in raw JSONL but absent from the reduced getSessionMessages shape)
60
+ "parentUuid",
61
+ "parentToolUseId",
62
+ "userType",
63
+ "version",
64
+ "isSidechain",
65
+ "entrypoint",
66
+ "cwd",
67
+ "gitBranch",
68
+ "logicalParentUuid",
69
+ "isMeta",
70
+ "isCompactSummary",
71
+ "level",
72
+ "isApiErrorMessage",
73
+ // per-type extras
74
+ "requestId",
75
+ "promptId",
76
+ "usage",
77
+ "toolUseResult",
78
+ "permissionMode",
79
+ ]);
80
+ /** The keystroke the native TUI permission prompt accepts for Yes — mirror of allow-inject.ts:29. */
81
+ export const EXPECTED_ALLOW_KEYSTROKE = "1\r";
82
+ // --- shared helpers -----------------------------------------------------------------------------
83
+ /**
84
+ * Build a verdict from a collected list of offending items: `ok=true` (with a positive summary) when
85
+ * the list is empty, otherwise `ok=false` with a detail that NAMES every offending item (the consumers
86
+ * and the unit tests assert each offending value appears in `detail`).
87
+ */
88
+ function verdict(surface, offenders, okSummary) {
89
+ if (offenders.length === 0) {
90
+ return { surface, ok: true, detail: okSummary };
91
+ }
92
+ return { surface, ok: false, detail: `${surface} drift: ${offenders.join(", ")}` };
93
+ }
94
+ /** Read `line.message?.stop_reason` defensively (the message object is `unknown`). */
95
+ function readStopReason(line) {
96
+ const message = line.message;
97
+ if (typeof message !== "object" || message === null) {
98
+ return undefined;
99
+ }
100
+ return message.stop_reason;
101
+ }
102
+ // --- the four checks ----------------------------------------------------------------------------
103
+ /**
104
+ * R2.1 — verify every non-null `message.stop_reason` is either HANDLED or a KNOWN non-terminal value.
105
+ *
106
+ * `null`/`undefined`/absent stop_reason is NOT drift (absence) and is ignored. A non-null value outside
107
+ * {@link HANDLED_STOP_REASONS} ∪ {@link KNOWN_NONTERMINAL_STOP_REASONS} (e.g. a new `pause_turn`) trips
108
+ * the check; `detail` names every offending value. Pure: no I/O, depends only on `lines`.
109
+ */
110
+ export function checkStopReasons(lines) {
111
+ const offenders = [];
112
+ for (const line of lines) {
113
+ const raw = readStopReason(line);
114
+ if (raw === null || raw === undefined) {
115
+ continue; // absence is not drift
116
+ }
117
+ const value = String(raw);
118
+ if (!HANDLED_STOP_REASONS.has(value) && !KNOWN_NONTERMINAL_STOP_REASONS.has(value)) {
119
+ if (!offenders.includes(value)) {
120
+ offenders.push(value);
121
+ }
122
+ }
123
+ }
124
+ return verdict("stop_reason", offenders, "all stop_reasons are handled or known-non-terminal");
125
+ }
126
+ /**
127
+ * R2.2 — verify every SDK content-block type is covered by a handled `switch` case. A block type in
128
+ * `sdkBlockTypes` but NOT in `handledCases` (e.g. a new `server_tool_use`) is uncovered drift; `detail`
129
+ * names each uncovered type. Pure: depends only on its two iterable arguments.
130
+ */
131
+ export function checkContentBlockCoverage(sdkBlockTypes, handledCases) {
132
+ const handled = new Set(handledCases);
133
+ const offenders = [];
134
+ for (const type of sdkBlockTypes) {
135
+ if (!handled.has(type) && !offenders.includes(type)) {
136
+ offenders.push(type);
137
+ }
138
+ }
139
+ return verdict("content_block", offenders, "every SDK content-block type is a handled case");
140
+ }
141
+ /**
142
+ * R2.3 — verify each raw JSONL line carries every {@link REQUIRED_JSONL_KEYS} core key and NO key
143
+ * outside {@link KNOWN_JSONL_KEYS}. A missing core key OR a new/unexpected top-level key is drift;
144
+ * `detail` names the offending key(s). Guards the RAW on-disk shape (camelCase), not the reduced
145
+ * getSessionMessages shape. Pure: depends only on `lines`.
146
+ */
147
+ export function checkJsonlShape(lines) {
148
+ const offenders = [];
149
+ for (const line of lines) {
150
+ for (const required of REQUIRED_JSONL_KEYS) {
151
+ const missing = `missing:${required}`;
152
+ if (!Object.prototype.hasOwnProperty.call(line, required) && !offenders.includes(missing)) {
153
+ offenders.push(missing);
154
+ }
155
+ }
156
+ for (const key of Object.keys(line)) {
157
+ const unexpected = `unexpected:${key}`;
158
+ if (!KNOWN_JSONL_KEYS.has(key) && !offenders.includes(unexpected)) {
159
+ offenders.push(unexpected);
160
+ }
161
+ }
162
+ }
163
+ return verdict("jsonl_shape", offenders, "every line matches the known raw JSONL shape");
164
+ }
165
+ /**
166
+ * R2.4 — verify the gate's allow keystroke is still the canonical {@link EXPECTED_ALLOW_KEYSTROKE}
167
+ * (`"1\r"`). On mismatch `detail` references both the expected and the actual keystroke. Mirror of
168
+ * allow-inject.ts:29 `KEYSTROKE_YES`. Pure: depends only on `keystroke`.
169
+ */
170
+ export function checkAllowKeystroke(keystroke) {
171
+ if (keystroke === EXPECTED_ALLOW_KEYSTROKE) {
172
+ return {
173
+ surface: "allow_keystroke",
174
+ ok: true,
175
+ detail: "allow keystroke matches the canonical 1\\r",
176
+ };
177
+ }
178
+ return {
179
+ surface: "allow_keystroke",
180
+ ok: false,
181
+ detail: `allow_keystroke drift: expected ${JSON.stringify(EXPECTED_ALLOW_KEYSTROKE)} but got ${JSON.stringify(keystroke)}`,
182
+ };
183
+ }
@@ -8,7 +8,18 @@ import { type PtyWriter, type Schedule } from "./allow-inject.js";
8
8
  * prompt's rendering we have. ANY hit ⇒ the native prompt is showing (#52822 reproduced).
9
9
  */
10
10
  export declare const NATIVE_PERMISSION_PROMPT_MARKERS: readonly string[];
11
- /** True iff any native-prompt marker appears in `text` (after ANSI stripping). */
11
+ /**
12
+ * Story 054 (R6) — the native SUBAGENT permission prompt header. claude renders a subagent tool's
13
+ * permission box with a "Tool use · from the <name> agent" header, distinct from the main-chain
14
+ * "Do you want to proceed?" markers above. Without this marker {@link textShowsNativePrompt} misses
15
+ * the subagent box, {@link clearNativePrompt} returns `'suppressed'` and types nothing, and the inner
16
+ * subagent tool hangs. The substring is specific enough not to match ordinary subagent narration
17
+ * ("the … agent finished its work"). The exact middot/wording is confirmed in the deferred in-Zed
18
+ * proof (task 8); kept separate from the verbatim Degrau-0 probe markers above.
19
+ */
20
+ export declare const SUBAGENT_PROMPT_MARKER = "Tool use \u00B7 from the";
21
+ /** True iff any native-prompt marker — main-chain or the story-054 subagent box — appears in `text`
22
+ * (after ANSI stripping). */
12
23
  export declare function textShowsNativePrompt(text: string): boolean;
13
24
  /** Default bounded wait for the JSONL `tool_use` correlation to land after a hook fires (ms).
14
25
  * The hook fires AFTER claude appended the assistant `tool_use` line, but the fs-watch → pump
@@ -17,6 +28,12 @@ export declare function textShowsNativePrompt(text: string): boolean;
17
28
  export declare const DEFAULT_CORRELATION_WAIT_MS = 5000;
18
29
  /** Default poll interval for the correlation wait (ms). */
19
30
  export declare const DEFAULT_CORRELATION_POLL_MS = 50;
31
+ /** Story 054 — re-nudge cadence inside the correlation wait (ms): a sidechain inner tool_use line can
32
+ * materialize MID-WAIT (after the first nudge) — periodically re-kicking the pump sources + registers
33
+ * that lagging row before the wait expires, so the subagent tool reaches a clean match instead of a
34
+ * fail-closed timeout deny. Tracked separately from the poll interval (the poll is 10-50ms; nudging on
35
+ * every poll would hammer the pump) — a nudge fires only once ~250ms has elapsed since the last one. */
36
+ export declare const DEFAULT_CORRELATION_RENUDGE_MS = 250;
20
37
  /** Default window for the native prompt to APPEAR after an allow decision (#52822 sweep, ms).
21
38
  * If no marker renders within it, allow-suppression held (the 2.1.161 case) — nothing to clear. */
22
39
  export declare const DEFAULT_PROMPT_APPEAR_MS = 1500;
@@ -56,6 +73,8 @@ export interface SessionGateOptions {
56
73
  correlationWaitMs?: number;
57
74
  /** See {@link DEFAULT_CORRELATION_POLL_MS}. */
58
75
  correlationPollMs?: number;
76
+ /** See {@link DEFAULT_CORRELATION_RENUDGE_MS}. */
77
+ correlationRenudgeMs?: number;
59
78
  /** See {@link DEFAULT_PROMPT_APPEAR_MS}. */
60
79
  promptAppearMs?: number;
61
80
  /** See {@link DEFAULT_PROMPT_POLL_MS}. */
@@ -67,6 +86,15 @@ export interface SessionGateOptions {
67
86
  /** Injectable port allocator (default: the story-032 `findFreePort`). */
68
87
  findPort?: () => Promise<number>;
69
88
  }
89
+ /** Story 054 — the parent-Task relay info for a subagent inner tool, resolved at decide() time. */
90
+ export interface SubagentRelay {
91
+ /** The spawning Task/Agent tool_use id to attach the ACP dialog to; null = orphan (no safe target). */
92
+ parentId: string | null;
93
+ /** Best-effort subagent name for the dialog title / the R4 deny line. */
94
+ subagentLabel: string;
95
+ }
96
+ /** Resolve a subagent inner tool_use id → its relay info, or undefined for a main-chain tool. */
97
+ export type ResolveSubagentRelay = (innerToolUseId: string) => SubagentRelay | undefined;
70
98
  /** The per-session gate runtime handle `createSession` owns and `teardownSession` disposes. */
71
99
  export interface SessionGate {
72
100
  /** The verified-free loopback port the hook server bound (== the port in the scratch hook URL). */
@@ -81,8 +109,14 @@ export interface SessionGate {
81
109
  * and an optional `nudge` invoked on every hook arrival to force an immediate pump re-read
82
110
  * (shrinking the JSONL-correlation race). MUST be called before the first tool call can be
83
111
  * approved — an unbound gate fails closed (deny).
112
+ *
113
+ * Story 054 — the third optional arg is a lazy resolver that maps an inner `tool_use.id` to its
114
+ * subagent {@link SubagentRelay} (parent Task id + label) by reading the session's
115
+ * `sidechainParentMap` (populated by the pump). `decide` uses it AFTER the correlation wait to
116
+ * relay a KNOWN subagent tool's dialog under its parent Task id (R1/R2), or to fail LOUD (R4) on
117
+ * an orphan / uncorrelatable subagent. A main-chain tool (no resolver entry) is unchanged (U1).
84
118
  */
85
- bindSession(sessionId: string, nudge?: () => void): void;
119
+ bindSession(sessionId: string, nudge?: () => void, resolveSubagentRelay?: ResolveSubagentRelay): void;
86
120
  /** Bind the live PTY: stores the raw writer for the allow keystroke and (when the PTY exposes
87
121
  * `onData`) attaches the recent-output tap feeding the native-prompt probe. */
88
122
  bindPty(pty: GatePty): void;
@@ -1 +1 @@
1
- {"version":3,"file":"gate-wiring.d.ts","sourceRoot":"","sources":["../../src/permissions/gate-wiring.ts"],"names":[],"mappings":"AAqCA,OAAO,EAEL,iBAAiB,EACjB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAqB,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAErF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,EAAE,SAAS,MAAM,EAO7D,CAAC;AAQF,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED;;;+EAG+E;AAC/E,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAChD,2DAA2D;AAC3D,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C;oGACoG;AACpG,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,0FAA0F;AAC1F,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C;;gDAEgD;AAChD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAI7C,2FAA2F;AAC3F,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAMjE;;0FAE0F;AAC1F,MAAM,WAAW,OAAQ,SAAQ,SAAS;IACxC,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG;QAAE,OAAO,IAAI,IAAI,CAAA;KAAE,CAAC;CAC1D;AAED,2FAA2F;AAC3F,MAAM,WAAW,kBAAkB;IACjC,gGAAgG;IAChG,MAAM,EAAE,gBAAgB,CAAC;IACzB,gGAAgG;IAChG,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,gGAAgG;IAChG,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uFAAuF;IACvF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAClC;AAED,+FAA+F;AAC/F,MAAM,WAAW,WAAW;IAC1B,mGAAmG;IACnG,IAAI,EAAE,MAAM,CAAC;IACb,kGAAkG;IAClG,YAAY,EAAE,MAAM,CAAC;IACrB;mGAC+F;IAC/F,UAAU,EAAE,iBAAiB,CAAC;IAC9B;;;;;OAKG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACzD;oFACgF;IAChF,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B;8EAC0E;IAC1E,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAqSD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,CAIrF"}
1
+ {"version":3,"file":"gate-wiring.d.ts","sourceRoot":"","sources":["../../src/permissions/gate-wiring.ts"],"names":[],"mappings":"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"}
@@ -51,15 +51,27 @@ export const NATIVE_PERMISSION_PROMPT_MARKERS = [
51
51
  "1. Yes",
52
52
  "No, and tell Claude",
53
53
  ];
54
+ /**
55
+ * Story 054 (R6) — the native SUBAGENT permission prompt header. claude renders a subagent tool's
56
+ * permission box with a "Tool use · from the <name> agent" header, distinct from the main-chain
57
+ * "Do you want to proceed?" markers above. Without this marker {@link textShowsNativePrompt} misses
58
+ * the subagent box, {@link clearNativePrompt} returns `'suppressed'` and types nothing, and the inner
59
+ * subagent tool hangs. The substring is specific enough not to match ordinary subagent narration
60
+ * ("the … agent finished its work"). The exact middot/wording is confirmed in the deferred in-Zed
61
+ * proof (task 8); kept separate from the verbatim Degrau-0 probe markers above.
62
+ */
63
+ export const SUBAGENT_PROMPT_MARKER = "Tool use · from the";
54
64
  /** Strip CSI / common ANSI escape sequences so prompt markers match the plain text (e-gate probe). */
55
65
  function stripAnsiText(s) {
56
66
  // eslint-disable-next-line no-control-regex
57
67
  return s.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, "");
58
68
  }
59
- /** True iff any native-prompt marker appears in `text` (after ANSI stripping). */
69
+ /** True iff any native-prompt marker — main-chain or the story-054 subagent box — appears in `text`
70
+ * (after ANSI stripping). */
60
71
  export function textShowsNativePrompt(text) {
61
72
  const stripped = stripAnsiText(text);
62
- return NATIVE_PERMISSION_PROMPT_MARKERS.some((m) => stripped.includes(m));
73
+ return (NATIVE_PERMISSION_PROMPT_MARKERS.some((m) => stripped.includes(m)) ||
74
+ stripped.includes(SUBAGENT_PROMPT_MARKER));
63
75
  }
64
76
  /** Default bounded wait for the JSONL `tool_use` correlation to land after a hook fires (ms).
65
77
  * The hook fires AFTER claude appended the assistant `tool_use` line, but the fs-watch → pump
@@ -68,6 +80,12 @@ export function textShowsNativePrompt(text) {
68
80
  export const DEFAULT_CORRELATION_WAIT_MS = 5000;
69
81
  /** Default poll interval for the correlation wait (ms). */
70
82
  export const DEFAULT_CORRELATION_POLL_MS = 50;
83
+ /** Story 054 — re-nudge cadence inside the correlation wait (ms): a sidechain inner tool_use line can
84
+ * materialize MID-WAIT (after the first nudge) — periodically re-kicking the pump sources + registers
85
+ * that lagging row before the wait expires, so the subagent tool reaches a clean match instead of a
86
+ * fail-closed timeout deny. Tracked separately from the poll interval (the poll is 10-50ms; nudging on
87
+ * every poll would hammer the pump) — a nudge fires only once ~250ms has elapsed since the last one. */
88
+ export const DEFAULT_CORRELATION_RENUDGE_MS = 250;
71
89
  /** Default window for the native prompt to APPEAR after an allow decision (#52822 sweep, ms).
72
90
  * If no marker renders within it, allow-suppression held (the 2.1.161 case) — nothing to clear. */
73
91
  export const DEFAULT_PROMPT_APPEAR_MS = 1500;
@@ -100,6 +118,10 @@ class SessionGateImpl {
100
118
  this.outputTail = "";
101
119
  this.totalOutput = 0;
102
120
  this.torndown = false;
121
+ /** Story 054 (R5) — per-session serial queue: the request+sweep critical section runs one at a
122
+ * time so two parallel subagent dialogs never cross their shared-PTY keystrokes. The wait/
123
+ * correlate/resolve prelude stays concurrent (only the raise+inject section serializes). */
124
+ this.permissionQueue = Promise.resolve();
103
125
  }
104
126
  get isTorndown() {
105
127
  return this.torndown;
@@ -110,6 +132,15 @@ class SessionGateImpl {
110
132
  get schedule() {
111
133
  return this.opts.schedule ?? defaultSchedule;
112
134
  }
135
+ /** Story 054 (R5) — append `fn` to the per-session serial chain so it runs only after the
136
+ * previous critical section settles (success OR failure), serializing concurrent decides'
137
+ * request+sweep sections. The chain is kept alive (and its errors swallowed) so one rejected
138
+ * permission never poisons the next; the returned promise still surfaces `fn`'s own result. */
139
+ enqueuePermission(fn) {
140
+ const run = this.permissionQueue.then(fn, fn);
141
+ this.permissionQueue = run.then(() => undefined, () => undefined);
142
+ return run;
143
+ }
113
144
  /** Start the hook server, then write the scratch settings (server first, so the URL the settings
114
145
  * point at is live before claude can ever read them; settings BEFORE the spawn is the caller's
115
146
  * ordering contract — blocker c). */
@@ -138,9 +169,10 @@ class SessionGateImpl {
138
169
  throw err;
139
170
  }
140
171
  }
141
- bindSession(sessionId, nudge) {
172
+ bindSession(sessionId, nudge, resolveSubagentRelay) {
142
173
  this.sessionId = sessionId;
143
174
  this.nudge = nudge;
175
+ this.resolveSubagentRelay = resolveSubagentRelay;
144
176
  }
145
177
  bindPty(pty) {
146
178
  this.pty = pty;
@@ -189,38 +221,92 @@ class SessionGateImpl {
189
221
  return "deny";
190
222
  }
191
223
  await this.waitForCorrelation(call.toolUseId);
192
- const decision = await requestPermission({
193
- client: this.opts.client,
194
- sessionId,
195
- toolCall: {
196
- toolUseId: call.toolUseId,
197
- toolName: call.toolName,
198
- toolInput: call.toolInput,
199
- },
200
- correlator: this.correlator,
201
- onWarn: (m) => this.warn(m),
202
- });
203
- if (decision === "allow") {
204
- // Return the allow body FIRST (claude is blocked on this response); sweep out of band.
205
- this.armAllowSweep(call);
224
+ // === Story 054 (§9 subagent relay) — AFTER the correlation wait, BEFORE the ACP prompt. ========
225
+ // The hook payload carries NO parent id (ForwardedToolCall has no parent_tool_use_id); the ONLY
226
+ // source of a subagent inner tool's parent is the session's sidechainParentMap, read lazily via
227
+ // the bound resolver. An undefined relay = a MAIN-CHAIN tool → both fields stay undefined and the
228
+ // requestPermission call below is byte-identical to today (U1). A KNOWN subagent inner tool relays
229
+ // its dialog under the parent Task id Zed already rendered (R1/R2) — UNLESS it cannot be safely
230
+ // relayed (orphan parent, or it never became a clean JSONL match within the wait window), in which
231
+ // case we fail LOUD (R4): a VISIBLE deny through the gate's warn surface (→ this.logger.error),
232
+ // naming the subagent + inner tool, and return "deny" WITHOUT raising a dialog against a bogus id.
233
+ const relay = this.resolveSubagentRelay?.(call.toolUseId);
234
+ let dialogToolCallId;
235
+ let subagentLabel;
236
+ if (relay) {
237
+ // isCleanMatch PROBES (does not consume); requestPermission's correlator.decide still consumes
238
+ // the inner id. The orphan branch short-circuits BEFORE the probe via `||`.
239
+ if (relay.parentId === null || !this.correlator.isCleanMatch(call.toolUseId)) {
240
+ this.warn(`[gate §9 subagent] FAIL CLOSED: subagent (${relay.subagentLabel}) tool "${call.toolName}" ` +
241
+ `(tool_use ${call.toolUseId}) ${relay.parentId === null
242
+ ? "is an orphan — no parent Task to attach the permission dialog to"
243
+ : "never correlated in the JSONL within the wait window"} — denying (R4 visible deny).`);
244
+ return "deny";
245
+ }
246
+ dialogToolCallId = relay.parentId;
247
+ subagentLabel = relay.subagentLabel;
206
248
  }
207
- return decision;
249
+ // === Story 054 (R5) — SERIALIZE only the raise+inject critical section. ========================
250
+ // dialogToolCallId/subagentLabel were computed above in the CONCURRENT prelude, so each enqueued
251
+ // request still carries its own parent Task id regardless of interleaving. Two parallel subagent
252
+ // decides therefore resolve INDEPENDENTLY (distinct inner ids in the correlator) but run their
253
+ // requestPermission + armAllowSweep one at a time — no native-prompt keystroke crossing on the
254
+ // shared PTY. A single sequential main-chain tool is a no-op through the queue (U1).
255
+ return this.enqueuePermission(async () => {
256
+ const decision = await requestPermission({
257
+ client: this.opts.client,
258
+ sessionId,
259
+ toolCall: {
260
+ toolUseId: call.toolUseId,
261
+ toolName: call.toolName,
262
+ toolInput: call.toolInput,
263
+ },
264
+ correlator: this.correlator,
265
+ onWarn: (m) => this.warn(m),
266
+ dialogToolCallId,
267
+ subagentLabel,
268
+ });
269
+ if (decision === "allow") {
270
+ // Return the allow body FIRST (claude is blocked on this response); sweep out of band.
271
+ this.armAllowSweep(call);
272
+ }
273
+ return decision;
274
+ });
208
275
  }
209
276
  /** Bounded poll until the pump has registered `toolUseId` as a clean single JSONL match. On
210
- * expiry, resolve anyway — `requestPermission` then fails closed on the missing correlation. */
277
+ * expiry, resolve anyway — `requestPermission` then fails closed on the missing correlation.
278
+ *
279
+ * Story 054 — re-nudge the pump on a ~{@link DEFAULT_CORRELATION_RENUDGE_MS} cadence (tracked
280
+ * SEPARATELY from the poll interval) so a sidechain inner tool_use line materializing MID-WAIT is
281
+ * sourced + registered before expiry — turning a would-be timeout deny into a clean subagent match.
282
+ * The nudge is best-effort: a throw never rejects the wait (it only widens the correlation window). */
211
283
  waitForCorrelation(toolUseId) {
212
284
  const waitMs = this.opts.correlationWaitMs ?? DEFAULT_CORRELATION_WAIT_MS;
213
285
  const pollMs = this.opts.correlationPollMs ?? DEFAULT_CORRELATION_POLL_MS;
286
+ const renudgeMs = this.opts.correlationRenudgeMs ?? DEFAULT_CORRELATION_RENUDGE_MS;
214
287
  if (this.correlator.isCleanMatch(toolUseId))
215
288
  return Promise.resolve();
216
289
  return new Promise((resolve) => {
217
290
  let elapsed = 0;
291
+ let sinceNudge = 0;
218
292
  const poll = () => {
219
293
  if (this.torndown || this.correlator.isCleanMatch(toolUseId)) {
220
294
  resolve();
221
295
  return;
222
296
  }
223
297
  elapsed += pollMs;
298
+ // Re-nudge on its own cadence (NOT every poll): a lagging sidechain row gets re-sourced so it
299
+ // can register before the wait elapses. A nudge failure must never reject the wait.
300
+ sinceNudge += pollMs;
301
+ if (sinceNudge >= renudgeMs) {
302
+ sinceNudge = 0;
303
+ try {
304
+ this.nudge?.();
305
+ }
306
+ catch {
307
+ // a nudge failure only widens the correlation wait; never rejects or decides by itself
308
+ }
309
+ }
224
310
  if (elapsed >= waitMs) {
225
311
  this.warn(`[gate §9] correlation wait expired (${waitMs}ms) for tool_use ${toolUseId} — the JSONL ` +
226
312
  `tool_use line never reached the pump; the decision will fail closed (deny).`);
@@ -89,6 +89,11 @@ export interface RequestPermissionOptions {
89
89
  correlator: ToolUseCorrelator;
90
90
  /** Optional sink for the fail-closed diagnostics (defaults to no-op; production wires the logger). */
91
91
  onWarn?: (message: string) => void;
92
+ /** Story 054 — when set, the ACP prompt attaches to this PARENT Task tool_call id (the id Zed already
93
+ * rendered) instead of the inner tool_use id; the correlator STILL decides on the inner id. */
94
+ dialogToolCallId?: string;
95
+ /** Story 054 — the subagent's label, named in the prompt title alongside the inner tool. */
96
+ subagentLabel?: string;
92
97
  }
93
98
  /**
94
99
  * Correlate a forwarded tool call by `tool_use.id` and raise ACP `session/request_permission`,
@@ -1 +1 @@
1
- {"version":3,"file":"request-permission.d.ts","sourceRoot":"","sources":["../../src/permissions/request-permission.ts"],"names":[],"mappings":"AAoBA,yEAAyE;AACzE,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,eAAe,CAAC;AAEnG,oGAAoG;AACpG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,oBAAoB,CAAC;CAC5B;AAED,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,kEAAkE;AAClE,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED,yDAAyD;AACzD,MAAM,MAAM,wBAAwB,GAChC;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,GACxB;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,8DAA8D;AAC9D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,wBAAwB,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACtF;AAED,iGAAiG;AACjG,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wFAAwF;AACxF,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC,gFAAgF;AAChF,wBAAgB,sBAAsB,IAAI,gBAAgB,EAAE,CAK3D;AAED;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAC5B,8FAA8F;IAC9F,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAE9C;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAMpC,iGAAiG;IACjG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIxC;;;;;OAKG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM;CAS9C;AAED,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,sFAAsF;IACtF,UAAU,EAAE,iBAAiB,CAAC;IAC9B,sGAAsG;IACtG,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,CAiDjG"}
1
+ {"version":3,"file":"request-permission.d.ts","sourceRoot":"","sources":["../../src/permissions/request-permission.ts"],"names":[],"mappings":"AAoBA,yEAAyE;AACzE,MAAM,MAAM,oBAAoB,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,eAAe,CAAC;AAEnG,oGAAoG;AACpG,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,oBAAoB,CAAC;CAC5B;AAED,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,kEAAkE;AAClE,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED,yDAAyD;AACzD,MAAM,MAAM,wBAAwB,GAChC;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,GACxB;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,8DAA8D;AAC9D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,wBAAwB,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACtF;AAED,iGAAiG;AACjG,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wFAAwF;AACxF,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC,gFAAgF;AAChF,wBAAgB,sBAAsB,IAAI,gBAAgB,EAAE,CAK3D;AAED;;;;;;;;GAQG;AACH,qBAAa,iBAAiB;IAC5B,8FAA8F;IAC9F,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,qGAAqG;IACrG,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAE9C;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAMpC,iGAAiG;IACjG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAIxC;;;;;OAKG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM;CAS9C;AAED,6CAA6C;AAC7C,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,sFAAsF;IACtF,UAAU,EAAE,iBAAiB,CAAC;IAC9B,sGAAsG;IACtG,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;oGACgG;IAChG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4FAA4F;IAC5F,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,wBAAwB,GAAG,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,CAuDjG"}
@@ -81,7 +81,7 @@ export class ToolUseCorrelator {
81
81
  * `'deny'` otherwise.
82
82
  */
83
83
  export async function requestPermission(opts) {
84
- const { client, sessionId, toolCall, correlator, onWarn } = opts;
84
+ const { client, sessionId, toolCall, correlator, onWarn, dialogToolCallId, subagentLabel } = opts;
85
85
  // 1) Correlate by tool_use.id BEFORE asking — an uncorrelated/duplicate call is denied, not prompted.
86
86
  const correlation = correlator.decide(toolCall.toolUseId);
87
87
  if (correlation === "deny") {
@@ -96,8 +96,13 @@ export async function requestPermission(opts) {
96
96
  result = await client.requestPermission({
97
97
  sessionId,
98
98
  toolCall: {
99
- toolCallId: toolCall.toolUseId,
100
- title: toolCall.toolName,
99
+ // Story 054: a subagent inner tool relays under the PARENT Task id Zed already rendered
100
+ // (dialogToolCallId), naming the inner tool + subagent in the title. A main-chain call (no
101
+ // dialogToolCallId) is byte-identical to today: toolCallId = inner id, title = bare tool name.
102
+ toolCallId: dialogToolCallId ?? toolCall.toolUseId,
103
+ title: dialogToolCallId !== undefined
104
+ ? `${toolCall.toolName} · from the ${subagentLabel ?? "subagent"} agent`
105
+ : toolCall.toolName,
101
106
  rawInput: toolCall.toolInput,
102
107
  },
103
108
  options: buildPermissionOptions(),
@@ -0,0 +1,58 @@
1
+ import type { SessionMessage } from "./engine-watcher.js";
2
+ import type { ToolUseCorrelator } from "./permissions/request-permission.js";
3
+ /**
4
+ * One inner subagent `tool_use` extracted from a sidechain row — the unit the gate correlator is fed
5
+ * and the dialog is built from.
6
+ */
7
+ export interface SidechainToolUse {
8
+ /** The inner `tool_use.id` — the correlation key fed into the gate correlator. */
9
+ id: string;
10
+ /** The spawning `Task`/`Agent` `tool_use.id` (the row's `parent_tool_use_id`); `null` = orphan. */
11
+ parentId: string | null;
12
+ /** The inner tool name (e.g. `"Bash"`, `"perplexity"`) — shown in the Zed prompt title. */
13
+ toolName: string;
14
+ /** The inner tool's raw input — forwarded to the dialog (R7). */
15
+ toolInput: unknown;
16
+ }
17
+ /**
18
+ * Collect every inner subagent `tool_use` from the pump's sidechain `rows` (R3, R7).
19
+ *
20
+ * Walks each SIDECHAIN `type === "assistant"` row's `message.content` and emits one
21
+ * {@link SidechainToolUse} per `tool_use` block, in encounter order:
22
+ * - `id` ← block.id
23
+ * - `parentId` ← row.parent_tool_use_id ?? null (a row with NO parent field → `null`, an orphan)
24
+ * - `toolName` ← block.name
25
+ * - `toolInput` ← block.input (raw, for the dialog — R7)
26
+ *
27
+ * A row counts as a SIDECHAIN row when flagged `isSidechain === true` (raw-shaped fixtures) OR — in the
28
+ * SDK's reduced LIVE shape, which carries NO `isSidechain` field — when it has a non-empty string
29
+ * `parent_tool_use_id` (the only sidechain signal in the reduced shape; mirrors `linearize.ts`
30
+ * `isSidechainMsg`, which the proven story-041 nested render relies on). The spawning `Task`/`Agent`
31
+ * `tool_use` lives on the MAIN chain (no `parent_tool_use_id`, no `isSidechain`), so it is excluded —
32
+ * the gate owns the main chain separately. (In production the pump hands us sidechain-only rows from
33
+ * `sourceSubagentRows`; the guard also keeps the helper correct when fed a full transcript.)
34
+ *
35
+ * Non-`tool_use` content blocks (text, tool_result, …) contribute nothing — a text-only sidechain row
36
+ * yields no entries (the live fixture's sidechain rows are text-only → `[]`). Tolerant of the reduced
37
+ * shape in the {@link import("./subagent-source.js").hasSubagentSpawn} style: non-object rows/messages
38
+ * and non-array `content` are simply skipped.
39
+ */
40
+ export declare function collectSidechainToolUses(rows: SessionMessage[]): SidechainToolUse[];
41
+ /**
42
+ * Feed each collected {@link SidechainToolUse} into the gate `correlator` EXACTLY ONCE (R3).
43
+ *
44
+ * For every `use` whose `id` is NOT already in the `registered` dedup Set:
45
+ * - `correlator.register(use.id)` — record the id in the JSONL correlation map so a subsequent hook
46
+ * tool call is a clean single match (its return value is ignored; the Set is the dedup truth);
47
+ * - `registered.add(use.id)` — mark it fed for this session;
48
+ * - `parentMap.set(use.id, use)` — map the inner id to its use entry (carries parentId + input).
49
+ *
50
+ * Ids already in `registered` are SKIPPED. The pump re-reads overlapping rows on each pass; a naive
51
+ * re-`register` of the same id would increment its count to 2 → the correlator treats it as a DUPLICATE
52
+ * → fail-closed deny. The dedup Set is what keeps every clean id `isCleanMatch === true` across those
53
+ * overlapping re-reads.
54
+ *
55
+ * Mutates `correlator`, `registered`, and `parentMap` in place; returns nothing.
56
+ */
57
+ export declare function registerSidechainGateToolUses(uses: SidechainToolUse[], correlator: ToolUseCorrelator, registered: Set<string>, parentMap: Map<string, SidechainToolUse>): void;
58
+ //# sourceMappingURL=subagent-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-gate.d.ts","sourceRoot":"","sources":["../src/subagent-gate.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAE7E;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,kFAAkF;IAClF,EAAE,EAAE,MAAM,CAAC;IACX,mGAAmG;IACnG,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,SAAS,EAAE,OAAO,CAAC;CACpB;AAOD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,gBAAgB,EAAE,CAgCnF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,6BAA6B,CAC3C,IAAI,EAAE,gBAAgB,EAAE,EACxB,UAAU,EAAE,iBAAiB,EAC7B,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACvC,IAAI,CAON"}
@@ -0,0 +1,110 @@
1
+ // Story 054 / Group 1 (tasks 1.1 + 1.2) — pure helpers that turn the pump's already-sourced sidechain
2
+ // rows into the gate correlator's feed.
3
+ //
4
+ // The original `canUseTool` path covered subagent tool calls automatically; the JSONL+PTY rewrite's
5
+ // gate (request-permission.ts) only correlates ids it has been fed from the MAIN chain, so a subagent
6
+ // tool call has no clean match and fails closed (deny) — the gap proven by the story-053 in-Zed run.
7
+ // This module closes that gap by sourcing the inner `tool_use` ids from the sidechain rows the pump
8
+ // already reads (subagent-source.ts `sourceSubagentRows`) and registering each into the correlator
9
+ // EXACTLY ONCE, so the subagent tool call becomes a clean single match and reaches the ACP prompt with
10
+ // the inner tool's input (R3, R7).
11
+ //
12
+ // MODULE LOCATION (intentional, frozen by the pre-authored test): design.md/tasks.md name this
13
+ // `src/permissions/subagent-gate.ts`, but the test imports `../dist/subagent-gate.js` (the ROOT of
14
+ // dist/). With tsconfig rootDir:src → outDir:dist this module MUST live at `src/subagent-gate.ts` so it
15
+ // compiles to `dist/subagent-gate.js`. The test's import line is immutable; the file location bends to
16
+ // it (call-surface only, no behavior change).
17
+ //
18
+ // Pure: no I/O, no logging, no SDK eval. The only effects are the mutations of the passed-in
19
+ // `correlator` / `registered` Set / `parentMap` (the per-session feed state the pump owns).
20
+ /** Narrowing helper: a non-null, non-array plain object (so we can read string-keyed props safely). */
21
+ function isObject(value) {
22
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23
+ }
24
+ /**
25
+ * Collect every inner subagent `tool_use` from the pump's sidechain `rows` (R3, R7).
26
+ *
27
+ * Walks each SIDECHAIN `type === "assistant"` row's `message.content` and emits one
28
+ * {@link SidechainToolUse} per `tool_use` block, in encounter order:
29
+ * - `id` ← block.id
30
+ * - `parentId` ← row.parent_tool_use_id ?? null (a row with NO parent field → `null`, an orphan)
31
+ * - `toolName` ← block.name
32
+ * - `toolInput` ← block.input (raw, for the dialog — R7)
33
+ *
34
+ * A row counts as a SIDECHAIN row when flagged `isSidechain === true` (raw-shaped fixtures) OR — in the
35
+ * SDK's reduced LIVE shape, which carries NO `isSidechain` field — when it has a non-empty string
36
+ * `parent_tool_use_id` (the only sidechain signal in the reduced shape; mirrors `linearize.ts`
37
+ * `isSidechainMsg`, which the proven story-041 nested render relies on). The spawning `Task`/`Agent`
38
+ * `tool_use` lives on the MAIN chain (no `parent_tool_use_id`, no `isSidechain`), so it is excluded —
39
+ * the gate owns the main chain separately. (In production the pump hands us sidechain-only rows from
40
+ * `sourceSubagentRows`; the guard also keeps the helper correct when fed a full transcript.)
41
+ *
42
+ * Non-`tool_use` content blocks (text, tool_result, …) contribute nothing — a text-only sidechain row
43
+ * yields no entries (the live fixture's sidechain rows are text-only → `[]`). Tolerant of the reduced
44
+ * shape in the {@link import("./subagent-source.js").hasSubagentSpawn} style: non-object rows/messages
45
+ * and non-array `content` are simply skipped.
46
+ */
47
+ export function collectSidechainToolUses(rows) {
48
+ const uses = [];
49
+ for (const row of rows) {
50
+ if (!isObject(row))
51
+ continue;
52
+ if (row.type !== "assistant")
53
+ continue;
54
+ // Sidechain detection: `isSidechain === true` (raw-shaped fixtures) OR a non-empty string
55
+ // `parent_tool_use_id` — the ONLY sidechain signal in the SDK's reduced LIVE shape, which omits
56
+ // `isSidechain` (mirrors linearize.ts `isSidechainMsg`). A row with no `parent_tool_use_id` field is
57
+ // an orphan → parentId is `null`, not `undefined`.
58
+ const rawParent = row.parent_tool_use_id;
59
+ const parentId = typeof rawParent === "string" && rawParent.length > 0 ? rawParent : null;
60
+ // The main-chain Task/Agent spawn has neither the flag nor a parent → excluded; the gate owns it.
61
+ if (row.isSidechain !== true && parentId === null)
62
+ continue;
63
+ const inner = row.message;
64
+ if (!isObject(inner))
65
+ continue;
66
+ const content = inner.content;
67
+ if (!Array.isArray(content))
68
+ continue;
69
+ for (const block of content) {
70
+ if (!isObject(block))
71
+ continue;
72
+ if (block.type !== "tool_use")
73
+ continue;
74
+ if (typeof block.id !== "string" || typeof block.name !== "string")
75
+ continue;
76
+ uses.push({
77
+ id: block.id,
78
+ parentId,
79
+ toolName: block.name,
80
+ toolInput: block.input,
81
+ });
82
+ }
83
+ }
84
+ return uses;
85
+ }
86
+ /**
87
+ * Feed each collected {@link SidechainToolUse} into the gate `correlator` EXACTLY ONCE (R3).
88
+ *
89
+ * For every `use` whose `id` is NOT already in the `registered` dedup Set:
90
+ * - `correlator.register(use.id)` — record the id in the JSONL correlation map so a subsequent hook
91
+ * tool call is a clean single match (its return value is ignored; the Set is the dedup truth);
92
+ * - `registered.add(use.id)` — mark it fed for this session;
93
+ * - `parentMap.set(use.id, use)` — map the inner id to its use entry (carries parentId + input).
94
+ *
95
+ * Ids already in `registered` are SKIPPED. The pump re-reads overlapping rows on each pass; a naive
96
+ * re-`register` of the same id would increment its count to 2 → the correlator treats it as a DUPLICATE
97
+ * → fail-closed deny. The dedup Set is what keeps every clean id `isCleanMatch === true` across those
98
+ * overlapping re-reads.
99
+ *
100
+ * Mutates `correlator`, `registered`, and `parentMap` in place; returns nothing.
101
+ */
102
+ export function registerSidechainGateToolUses(uses, correlator, registered, parentMap) {
103
+ for (const use of uses) {
104
+ if (registered.has(use.id))
105
+ continue; // already fed this session — never double-register.
106
+ correlator.register(use.id);
107
+ registered.add(use.id);
108
+ parentMap.set(use.id, use);
109
+ }
110
+ }
package/package.json CHANGED
@@ -3,8 +3,8 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.4.0",
7
- "description": "Run the official Claude Code TUI in Zed's agent panel on your Claude Pro/Max subscription — an ACP-over-PTY bridge created for Anthropic's June 2026 billing split (paused June 15; see README).",
6
+ "version": "0.5.1",
7
+ "description": "Run the official Claude Code TUI in Zed's agent panel on your Claude Pro/Max subscription — an ACP-over-PTY bridge created for Anthropic's billing split (paused June 15; see README).",
8
8
  "main": "dist/lib.js",
9
9
  "types": "dist/lib.d.ts",
10
10
  "bin": {
@@ -36,6 +36,7 @@
36
36
  "format:check": "prettier --check .",
37
37
  "check": "npm run lint && npm run format:check",
38
38
  "test": "npm run build && node --experimental-strip-types --test test/*.test.ts",
39
+ "smoke:live": "node experiments/smoke-live.mjs",
39
40
  "prepublishOnly": "npm run build"
40
41
  },
41
42
  "keywords": [