@spinabot/brigade 1.23.0 → 1.24.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 (76) hide show
  1. package/dist/agents/agent-loop.d.ts.map +1 -1
  2. package/dist/agents/agent-loop.js +73 -3
  3. package/dist/agents/agent-loop.js.map +1 -1
  4. package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
  5. package/dist/agents/channels/inbound-pipeline.js +6 -0
  6. package/dist/agents/channels/inbound-pipeline.js.map +1 -1
  7. package/dist/agents/claude-cli/availability.d.ts +12 -0
  8. package/dist/agents/claude-cli/availability.d.ts.map +1 -0
  9. package/dist/agents/claude-cli/availability.js +79 -0
  10. package/dist/agents/claude-cli/availability.js.map +1 -0
  11. package/dist/agents/claude-cli/catalog.d.ts +91 -0
  12. package/dist/agents/claude-cli/catalog.d.ts.map +1 -0
  13. package/dist/agents/claude-cli/catalog.js +260 -0
  14. package/dist/agents/claude-cli/catalog.js.map +1 -0
  15. package/dist/agents/claude-cli/claude-config.d.ts +38 -0
  16. package/dist/agents/claude-cli/claude-config.d.ts.map +1 -0
  17. package/dist/agents/claude-cli/claude-config.js +111 -0
  18. package/dist/agents/claude-cli/claude-config.js.map +1 -0
  19. package/dist/agents/claude-cli/register.d.ts +32 -0
  20. package/dist/agents/claude-cli/register.d.ts.map +1 -0
  21. package/dist/agents/claude-cli/register.js +68 -0
  22. package/dist/agents/claude-cli/register.js.map +1 -0
  23. package/dist/agents/claude-cli/spawn.d.ts +42 -0
  24. package/dist/agents/claude-cli/spawn.d.ts.map +1 -0
  25. package/dist/agents/claude-cli/spawn.js +179 -0
  26. package/dist/agents/claude-cli/spawn.js.map +1 -0
  27. package/dist/agents/claude-cli/stream-json.d.ts +129 -0
  28. package/dist/agents/claude-cli/stream-json.d.ts.map +1 -0
  29. package/dist/agents/claude-cli/stream-json.js +84 -0
  30. package/dist/agents/claude-cli/stream-json.js.map +1 -0
  31. package/dist/agents/claude-cli/stream.d.ts +27 -0
  32. package/dist/agents/claude-cli/stream.d.ts.map +1 -0
  33. package/dist/agents/claude-cli/stream.js +380 -0
  34. package/dist/agents/claude-cli/stream.js.map +1 -0
  35. package/dist/agents/error-classifier.d.ts +2 -2
  36. package/dist/agents/error-classifier.d.ts.map +1 -1
  37. package/dist/agents/error-classifier.js +43 -0
  38. package/dist/agents/error-classifier.js.map +1 -1
  39. package/dist/agents/model-resolution.d.ts +36 -0
  40. package/dist/agents/model-resolution.d.ts.map +1 -1
  41. package/dist/agents/model-resolution.js +59 -0
  42. package/dist/agents/model-resolution.js.map +1 -1
  43. package/dist/agents/retry-policy.d.ts.map +1 -1
  44. package/dist/agents/retry-policy.js +25 -1
  45. package/dist/agents/retry-policy.js.map +1 -1
  46. package/dist/auth/auth-health.d.ts +89 -0
  47. package/dist/auth/auth-health.d.ts.map +1 -1
  48. package/dist/auth/auth-health.js +207 -1
  49. package/dist/auth/auth-health.js.map +1 -1
  50. package/dist/buildstamp.json +1 -1
  51. package/dist/cli/commands/auth.js +18 -0
  52. package/dist/cli/commands/auth.js.map +1 -1
  53. package/dist/cli/commands/doctor.d.ts.map +1 -1
  54. package/dist/cli/commands/doctor.js +46 -0
  55. package/dist/cli/commands/doctor.js.map +1 -1
  56. package/dist/cli/commands/gateway.d.ts +4 -0
  57. package/dist/cli/commands/gateway.d.ts.map +1 -1
  58. package/dist/cli/commands/gateway.js +44 -2
  59. package/dist/cli/commands/gateway.js.map +1 -1
  60. package/dist/cli/commands/login.d.ts.map +1 -1
  61. package/dist/cli/commands/login.js +107 -19
  62. package/dist/cli/commands/login.js.map +1 -1
  63. package/dist/core/auth-bridge.d.ts.map +1 -1
  64. package/dist/core/auth-bridge.js +16 -0
  65. package/dist/core/auth-bridge.js.map +1 -1
  66. package/dist/core/server.d.ts.map +1 -1
  67. package/dist/core/server.js +13 -0
  68. package/dist/core/server.js.map +1 -1
  69. package/dist/providers/catalog.d.ts.map +1 -1
  70. package/dist/providers/catalog.js +18 -0
  71. package/dist/providers/catalog.js.map +1 -1
  72. package/dist/ui/onboarding.d.ts +11 -0
  73. package/dist/ui/onboarding.d.ts.map +1 -1
  74. package/dist/ui/onboarding.js +343 -4
  75. package/dist/ui/onboarding.js.map +1 -1
  76. package/package.json +2 -1
@@ -0,0 +1,179 @@
1
+ // Subprocess plumbing for the claude-cli backend: spawn the `claude` binary,
2
+ // stream its stdout as parsed NDJSON frames, and enforce a no-output watchdog
3
+ // that hard-kills a wedged process. Kept separate from `stream.ts` so the
4
+ // Pi-event mapping and the OS-process concerns test independently.
5
+ import { spawn } from "node:child_process";
6
+ import { mkdtempSync, rmSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import path from "node:path";
9
+ import { buildClaudeCliEnv, resolveClaudeCliCommand } from "./catalog.js";
10
+ import { hasBrigadeClaudeLogin, resolveBrigadeClaudeConfigDir } from "./claude-config.js";
11
+ import { parseClaudeCliLine } from "./stream-json.js";
12
+ /**
13
+ * No-output watchdog. The timer is reset on every stdout/stderr chunk, so a
14
+ * slow-but-streaming turn survives while a genuinely wedged process (waiting on
15
+ * an interactive prompt that can never arrive, or hung on the network) trips it
16
+ * and is SIGKILL'd. Fresh runs get a longer grace (CLI startup + auth); we have
17
+ * no resume path in v1 so a single profile suffices.
18
+ */
19
+ export const CLAUDE_CLI_NO_OUTPUT_TIMEOUT_MS = 180_000;
20
+ /** Hard ceiling on a single turn regardless of trickle output. */
21
+ export const CLAUDE_CLI_OVERALL_TIMEOUT_MS = 600_000;
22
+ /**
23
+ * Spawn `claude` with the given argv, feed `stdin`, and stream parsed frames.
24
+ * The isolated empty temp cwd contains the CLI's tool blast radius: even under
25
+ * `bypassPermissions` any file/read tool it reaches acts on a throwaway dir,
26
+ * never the operator's project. The dir is removed when the process exits.
27
+ */
28
+ export function spawnClaudeCli(args) {
29
+ const command = resolveClaudeCliCommand();
30
+ const noOutputMs = args.noOutputTimeoutMs ?? CLAUDE_CLI_NO_OUTPUT_TIMEOUT_MS;
31
+ const overallMs = args.overallTimeoutMs ?? CLAUDE_CLI_OVERALL_TIMEOUT_MS;
32
+ const doSpawn = args.spawnFn ?? spawn;
33
+ // Isolated, empty working dir — the tool-containment boundary.
34
+ let cwd;
35
+ try {
36
+ cwd = mkdtempSync(path.join(tmpdir(), "brigade-claude-cli-"));
37
+ }
38
+ catch {
39
+ cwd = tmpdir();
40
+ }
41
+ // Prefer Brigade's OWN Claude login (dedicated grant in its managed config
42
+ // dir) when present — the binary auths + refreshes there, isolated from the
43
+ // operator's personal ~/.claude. Absent it, leave CLAUDE_CONFIG_DIR unset so
44
+ // the binary uses its default (the operator's own login).
45
+ const configDir = hasBrigadeClaudeLogin() ? resolveBrigadeClaudeConfigDir() : undefined;
46
+ const child = doSpawn(command, args.args, {
47
+ cwd,
48
+ env: buildClaudeCliEnv(process.env, { configDir }),
49
+ stdio: ["pipe", "pipe", "pipe"],
50
+ // No shell — argv is passed verbatim so nothing is word-split or expanded.
51
+ shell: false,
52
+ });
53
+ let killReason;
54
+ let settled = false;
55
+ let stderr = "";
56
+ const STDERR_CAP = 8_000;
57
+ // ── watchdog timers ──
58
+ let noOutputTimer;
59
+ let overallTimer;
60
+ const clearTimers = () => {
61
+ if (noOutputTimer)
62
+ clearTimeout(noOutputTimer);
63
+ if (overallTimer)
64
+ clearTimeout(overallTimer);
65
+ noOutputTimer = undefined;
66
+ overallTimer = undefined;
67
+ };
68
+ const kill = (reason) => {
69
+ if (settled || killReason)
70
+ return;
71
+ killReason = reason;
72
+ try {
73
+ child.kill("SIGKILL");
74
+ }
75
+ catch {
76
+ /* already gone */
77
+ }
78
+ };
79
+ const armNoOutput = () => {
80
+ if (noOutputTimer)
81
+ clearTimeout(noOutputTimer);
82
+ noOutputTimer = setTimeout(() => kill("no-output-timeout"), noOutputMs);
83
+ // Don't keep the event loop alive purely for the watchdog.
84
+ noOutputTimer.unref?.();
85
+ };
86
+ overallTimer = setTimeout(() => kill("overall-timeout"), overallMs);
87
+ overallTimer.unref?.();
88
+ armNoOutput();
89
+ const onAbort = () => kill("aborted");
90
+ if (args.signal) {
91
+ if (args.signal.aborted)
92
+ onAbort();
93
+ else
94
+ args.signal.addEventListener("abort", onAbort, { once: true });
95
+ }
96
+ // ── stderr capture (bounded) ──
97
+ child.stderr.setEncoding("utf8");
98
+ child.stderr.on("data", (chunk) => {
99
+ armNoOutput(); // stderr progress also counts as "alive"
100
+ if (stderr.length < STDERR_CAP)
101
+ stderr += chunk;
102
+ });
103
+ // ── stdout → line-buffered frame queue ──
104
+ // A small async queue bridges the 'data'/'close' events to the generator.
105
+ const queue = [];
106
+ let resolveNext;
107
+ let closed = false;
108
+ let buffer = "";
109
+ const pump = () => {
110
+ if (resolveNext) {
111
+ const r = resolveNext;
112
+ resolveNext = undefined;
113
+ r();
114
+ }
115
+ };
116
+ child.stdout.setEncoding("utf8");
117
+ child.stdout.on("data", (chunk) => {
118
+ armNoOutput();
119
+ buffer += chunk;
120
+ const lines = buffer.split("\n");
121
+ buffer = lines.pop() ?? "";
122
+ for (const line of lines) {
123
+ const frame = parseClaudeCliLine(line);
124
+ if (frame)
125
+ queue.push(frame);
126
+ }
127
+ pump();
128
+ });
129
+ const exit = new Promise((resolve) => {
130
+ const finish = (code) => {
131
+ if (settled)
132
+ return;
133
+ settled = true;
134
+ clearTimers();
135
+ if (args.signal)
136
+ args.signal.removeEventListener("abort", onAbort);
137
+ // Flush a final unterminated line.
138
+ const tail = parseClaudeCliLine(buffer);
139
+ if (tail)
140
+ queue.push(tail);
141
+ buffer = "";
142
+ closed = true;
143
+ pump();
144
+ // Best-effort cleanup of the throwaway cwd.
145
+ try {
146
+ rmSync(cwd, { recursive: true, force: true });
147
+ }
148
+ catch {
149
+ /* leave it for the OS temp reaper */
150
+ }
151
+ resolve({ code, killReason, stderr });
152
+ };
153
+ child.on("close", (code) => finish(code));
154
+ child.on("error", () => finish(null)); // spawn failure (binary missing, etc.)
155
+ });
156
+ // Feed stdin, then close it so the CLI starts generating.
157
+ try {
158
+ child.stdin.write(args.stdin);
159
+ child.stdin.end();
160
+ }
161
+ catch {
162
+ /* the 'error' handler above will settle the run */
163
+ }
164
+ async function* frames() {
165
+ while (true) {
166
+ if (queue.length > 0) {
167
+ yield queue.shift();
168
+ continue;
169
+ }
170
+ if (closed)
171
+ return;
172
+ await new Promise((r) => {
173
+ resolveNext = r;
174
+ });
175
+ }
176
+ }
177
+ return { frames: frames(), done: exit };
178
+ }
179
+ //# sourceMappingURL=spawn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../../src/agents/claude-cli/spawn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /** A raw Anthropic streaming SSE event, as wrapped in a `stream_event` frame. */
2
+ export interface AnthropicStreamEvent {
3
+ type?: string;
4
+ message?: {
5
+ usage?: AnthropicUsage;
6
+ [k: string]: unknown;
7
+ };
8
+ index?: number;
9
+ content_block?: {
10
+ type?: "text" | "thinking" | "tool_use" | string;
11
+ id?: string;
12
+ name?: string;
13
+ input?: unknown;
14
+ [k: string]: unknown;
15
+ };
16
+ delta?: {
17
+ type?: "text_delta" | "thinking_delta" | "input_json_delta" | "signature_delta" | string;
18
+ text?: string;
19
+ thinking?: string;
20
+ partial_json?: string;
21
+ signature?: string;
22
+ stop_reason?: string | null;
23
+ [k: string]: unknown;
24
+ };
25
+ usage?: AnthropicUsage;
26
+ }
27
+ export interface AnthropicUsage {
28
+ input_tokens?: number;
29
+ output_tokens?: number;
30
+ cache_read_input_tokens?: number;
31
+ cache_creation_input_tokens?: number;
32
+ [k: string]: unknown;
33
+ }
34
+ /** A complete assistant message block (`type:"assistant"`). */
35
+ export interface AssistantFrameMessage {
36
+ id?: string;
37
+ role?: string;
38
+ model?: string;
39
+ stop_reason?: string | null;
40
+ content?: Array<{
41
+ type?: "text" | "thinking" | "tool_use" | string;
42
+ text?: string;
43
+ thinking?: string;
44
+ signature?: string;
45
+ id?: string;
46
+ name?: string;
47
+ input?: unknown;
48
+ [k: string]: unknown;
49
+ }>;
50
+ usage?: AnthropicUsage;
51
+ [k: string]: unknown;
52
+ }
53
+ /** The terminal `type:"result"` frame. */
54
+ export interface ResultFrame {
55
+ type: "result";
56
+ subtype?: string;
57
+ is_error?: boolean;
58
+ result?: string;
59
+ stop_reason?: string | null;
60
+ session_id?: string;
61
+ total_cost_usd?: number;
62
+ usage?: AnthropicUsage & {
63
+ service_tier?: string;
64
+ };
65
+ error?: string;
66
+ message?: string;
67
+ [k: string]: unknown;
68
+ }
69
+ /** Discriminated union of every frame `stream.ts` acts on. */
70
+ export type ClaudeCliFrame = {
71
+ type: "system";
72
+ subtype?: string;
73
+ session_id?: string;
74
+ [k: string]: unknown;
75
+ } | {
76
+ type: "stream_event";
77
+ event?: AnthropicStreamEvent;
78
+ session_id?: string;
79
+ [k: string]: unknown;
80
+ } | {
81
+ type: "assistant";
82
+ message?: AssistantFrameMessage;
83
+ session_id?: string;
84
+ [k: string]: unknown;
85
+ } | {
86
+ type: "rate_limit_event";
87
+ rate_limit_info?: RateLimitInfo;
88
+ [k: string]: unknown;
89
+ } | ResultFrame | {
90
+ type: string;
91
+ [k: string]: unknown;
92
+ };
93
+ export interface RateLimitInfo {
94
+ status?: string;
95
+ resetsAt?: number;
96
+ rateLimitType?: string;
97
+ overageStatus?: string;
98
+ overageDisabledReason?: string;
99
+ isUsingOverage?: boolean;
100
+ [k: string]: unknown;
101
+ }
102
+ /**
103
+ * Parse a single stdout line into a typed frame, or `null` for a blank or
104
+ * unparseable line (never throws — a stray non-JSON diagnostic line must not
105
+ * abort the turn). The CLI occasionally prefixes debug text on stderr, but on
106
+ * stdout with `--output-format stream-json` every meaningful line is a JSON
107
+ * object; anything else is skipped.
108
+ */
109
+ export declare function parseClaudeCliLine(line: string): ClaudeCliFrame | null;
110
+ /** Extract the session id from any frame that carries one (system/result/stream). */
111
+ export declare function frameSessionId(frame: ClaudeCliFrame): string | undefined;
112
+ /**
113
+ * Classify the terminal `result` frame. Returns:
114
+ * - "success" — a normal completion.
115
+ * - "limit" — a plan-usage / overage rejection ("out of extra usage",
116
+ * usage-limit reached). Callers map this to the
117
+ * subscription_limit retry reason.
118
+ * - "auth" — the login is dead: expired/revoked token, needs re-auth.
119
+ * Callers surface a "re-run `brigade login claude-cli`" hint.
120
+ * - "error" — any other failure subtype.
121
+ * Pure — inspects subtype + is_error + any embedded message text.
122
+ */
123
+ export declare function classifyResultFrame(frame: ResultFrame): "success" | "limit" | "auth" | "error";
124
+ /** Sum an Anthropic usage block into {input,output} totals (cache folded into input). */
125
+ export declare function foldUsage(usage: AnthropicUsage | undefined): {
126
+ input: number;
127
+ output: number;
128
+ };
129
+ //# sourceMappingURL=stream-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-json.d.ts","sourceRoot":"","sources":["../../../src/agents/claude-cli/stream-json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ // Wire types + a line parser for the Claude Code CLI's `--output-format
2
+ // stream-json --include-partial-messages --verbose` protocol.
3
+ //
4
+ // The CLI emits one JSON object per line (NDJSON) on stdout. The frames we
5
+ // care about, in the order a normal turn produces them:
6
+ //
7
+ // {"type":"system","subtype":"init", ...} — session bootstrap (carries session_id)
8
+ // {"type":"stream_event","event":{...}} — a raw Anthropic SSE event (partial streaming)
9
+ // {"type":"assistant","message":{...}} — a COMPLETE assistant message block
10
+ // {"type":"rate_limit_event","rate_limit_info":{}} — plan-window telemetry (informational)
11
+ // {"type":"result","subtype":"success"|"error_*",} — terminal frame (final text, usage, cost)
12
+ //
13
+ // The `stream_event.event` payloads are the SAME shapes the Anthropic
14
+ // Messages streaming API emits (message_start / content_block_start /
15
+ // content_block_delta / content_block_stop / message_delta / message_stop),
16
+ // so the delta handling in `stream.ts` mirrors pi-ai's own anthropic
17
+ // provider. When partial frames are unavailable we fall back to the whole
18
+ // `assistant`/`result` message — the parser surfaces both so the consumer
19
+ // can degrade gracefully.
20
+ /* ─────────────────────────── line parser ─────────────────────────── */
21
+ /**
22
+ * Parse a single stdout line into a typed frame, or `null` for a blank or
23
+ * unparseable line (never throws — a stray non-JSON diagnostic line must not
24
+ * abort the turn). The CLI occasionally prefixes debug text on stderr, but on
25
+ * stdout with `--output-format stream-json` every meaningful line is a JSON
26
+ * object; anything else is skipped.
27
+ */
28
+ export function parseClaudeCliLine(line) {
29
+ const trimmed = line.trim();
30
+ if (!trimmed || trimmed[0] !== "{")
31
+ return null;
32
+ try {
33
+ const obj = JSON.parse(trimmed);
34
+ if (!obj || typeof obj !== "object" || typeof obj.type !== "string")
35
+ return null;
36
+ return obj;
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ /* ─────────────────────────── frame helpers ─────────────────────────── */
43
+ /** Extract the session id from any frame that carries one (system/result/stream). */
44
+ export function frameSessionId(frame) {
45
+ const sid = frame.session_id;
46
+ return typeof sid === "string" && sid.length > 0 ? sid : undefined;
47
+ }
48
+ /**
49
+ * Classify the terminal `result` frame. Returns:
50
+ * - "success" — a normal completion.
51
+ * - "limit" — a plan-usage / overage rejection ("out of extra usage",
52
+ * usage-limit reached). Callers map this to the
53
+ * subscription_limit retry reason.
54
+ * - "auth" — the login is dead: expired/revoked token, needs re-auth.
55
+ * Callers surface a "re-run `brigade login claude-cli`" hint.
56
+ * - "error" — any other failure subtype.
57
+ * Pure — inspects subtype + is_error + any embedded message text.
58
+ */
59
+ export function classifyResultFrame(frame) {
60
+ const isError = frame.is_error === true || (frame.subtype ?? "").startsWith("error");
61
+ if (!isError && (frame.subtype === "success" || frame.subtype === undefined))
62
+ return "success";
63
+ const text = `${frame.subtype ?? ""} ${frame.error ?? ""} ${frame.message ?? ""} ${frame.result ?? ""}`;
64
+ if (/out of extra usage|usage limit|claude\.ai\/settings\/usage|limit will reset/i.test(text)) {
65
+ return "limit";
66
+ }
67
+ // Dead-login signals: the binary's own login has expired/been revoked and it
68
+ // can't refresh headlessly. Distinct from a usage limit — the fix is re-auth,
69
+ // not waiting for a reset.
70
+ if (/\b401\b|unauthori[sz]ed|authenticat(?:e|ion)|invalid[_ ]grant|token (?:expired|revoked|invalid)|refresh[_ ]token|(?:please )?(?:re-?)?login|not (?:logged|signed) in|run `?claude`? login|session (?:expired|invalid)/i.test(text)) {
71
+ return "auth";
72
+ }
73
+ return isError ? "error" : "success";
74
+ }
75
+ /** Sum an Anthropic usage block into {input,output} totals (cache folded into input). */
76
+ export function foldUsage(usage) {
77
+ if (!usage)
78
+ return { input: 0, output: 0 };
79
+ const input = (usage.input_tokens ?? 0) +
80
+ (usage.cache_read_input_tokens ?? 0) +
81
+ (usage.cache_creation_input_tokens ?? 0);
82
+ return { input, output: usage.output_tokens ?? 0 };
83
+ }
84
+ //# sourceMappingURL=stream-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-json.js","sourceRoot":"","sources":["../../../src/agents/claude-cli/stream-json.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,8DAA8D;AAC9D,EAAE;AACF,2EAA2E;AAC3E,wDAAwD;AACxD,EAAE;AACF,8FAA8F;AAC9F,qGAAqG;AACrG,0FAA0F;AAC1F,6FAA6F;AAC7F,gGAAgG;AAChG,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,4EAA4E;AAC5E,qEAAqE;AACrE,0EAA0E;AAC1E,0EAA0E;AAC1E,0BAA0B;AAkG1B,yEAAyE;AAEzE;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAChD,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAmB,CAAC;QAClD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACjF,OAAO,GAAG,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,2EAA2E;AAE3E,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,KAAqB;IACnD,MAAM,GAAG,GAAI,KAAkC,CAAC,UAAU,CAAC;IAC3D,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACpE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAkB;IACrD,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACrF,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/F,MAAM,IAAI,GAAG,GAAG,KAAK,CAAC,OAAO,IAAI,EAAE,IAAI,KAAK,CAAC,KAAK,IAAI,EAAE,IAAI,KAAK,CAAC,OAAO,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;IACxG,IAAI,8EAA8E,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/F,OAAO,OAAO,CAAC;IAChB,CAAC;IACD,6EAA6E;IAC7E,8EAA8E;IAC9E,2BAA2B;IAC3B,IACC,wNAAwN,CAAC,IAAI,CAC5N,IAAI,CACJ,EACA,CAAC;QACF,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACtC,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,SAAS,CAAC,KAAiC;IAC1D,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IAC3C,MAAM,KAAK,GACV,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC;QACzB,CAAC,KAAK,CAAC,uBAAuB,IAAI,CAAC,CAAC;QACpC,CAAC,KAAK,CAAC,2BAA2B,IAAI,CAAC,CAAC,CAAC;IAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,aAAa,IAAI,CAAC,EAAE,CAAC;AACpD,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { StreamFn } from "@earendil-works/pi-agent-core";
2
+ import { type SpawnClaudeCliArgs } from "./spawn.js";
3
+ interface CtxMessage {
4
+ role: string;
5
+ content: unknown;
6
+ toolName?: unknown;
7
+ }
8
+ /**
9
+ * Serialize the conversation into a single stdin prompt. The current (final)
10
+ * user message is the live request; everything before it is rendered as a
11
+ * labelled transcript so the CLI has the multi-turn context Brigade would
12
+ * otherwise pass as a messages array. The system prompt travels separately via
13
+ * `--append-system-prompt`, so it's not duplicated here.
14
+ */
15
+ export declare function serializeConversationPrompt(messages: CtxMessage[]): string;
16
+ export interface CreateClaudeCliStreamFnOpts {
17
+ /** Injectable spawn for tests. */
18
+ spawnFn?: SpawnClaudeCliArgs["spawnFn"];
19
+ }
20
+ /**
21
+ * Build the Pi `StreamFn` for claude-cli models. Reads `model`, `context`
22
+ * (systemPrompt + messages), and `options` (signal), spawns the CLI, and fills
23
+ * the returned event stream from the parsed frames.
24
+ */
25
+ export declare function createClaudeCliStreamFn(opts?: CreateClaudeCliStreamFnOpts): StreamFn;
26
+ export {};
27
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../src/agents/claude-cli/stream.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAG9D,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAWrE,UAAU,UAAU;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAqBD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,MAAM,CA2B1E;AA8DD,MAAM,WAAW,2BAA2B;IAC3C,kCAAkC;IAClC,OAAO,CAAC,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;CACxC;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,GAAE,2BAAgC,GAAG,QAAQ,CAiPxF"}