@synergenius/flow-weaver 0.22.6 → 0.22.8

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.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Provider-agnostic agent loop — streams LLM responses, collects tool calls,
3
+ * executes them via a caller-provided executor, and iterates.
4
+ *
5
+ * The loop never touches provider internals. Adding a new provider (Codex,
6
+ * Gemini, etc.) requires zero loop changes — just implement AgentProvider.
7
+ */
8
+ import type { AgentProvider, AgentMessage, ToolDefinition, ToolExecutor, AgentLoopOptions, AgentLoopResult } from './types.js';
9
+ export declare function runAgentLoop(provider: AgentProvider, tools: ToolDefinition[], executor: ToolExecutor, messages: AgentMessage[], options?: AgentLoopOptions): Promise<AgentLoopResult>;
10
+ //# sourceMappingURL=agent-loop.d.ts.map
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Provider-agnostic agent loop — streams LLM responses, collects tool calls,
3
+ * executes them via a caller-provided executor, and iterates.
4
+ *
5
+ * The loop never touches provider internals. Adding a new provider (Codex,
6
+ * Gemini, etc.) requires zero loop changes — just implement AgentProvider.
7
+ */
8
+ const DEFAULT_MAX_ITERATIONS = 15;
9
+ const TOOL_RESULT_CAP = 10_000; // bytes
10
+ export async function runAgentLoop(provider, tools, executor, messages, options) {
11
+ const maxIterations = options?.maxIterations ?? DEFAULT_MAX_ITERATIONS;
12
+ const signal = options?.signal;
13
+ const onStreamEvent = options?.onStreamEvent;
14
+ const onToolEvent = options?.onToolEvent;
15
+ const conversation = [...messages];
16
+ let totalPromptTokens = 0;
17
+ let totalCompletionTokens = 0;
18
+ let toolCallCount = 0;
19
+ for (let iteration = 0; iteration < maxIterations; iteration++) {
20
+ if (signal?.aborted) {
21
+ return buildResult(false, 'Aborted', conversation, toolCallCount, totalPromptTokens, totalCompletionTokens);
22
+ }
23
+ // Stream from provider
24
+ let text = '';
25
+ let finishReason = 'stop';
26
+ const collectedToolCalls = [];
27
+ const activeToolNames = new Map();
28
+ const stream = provider.stream(conversation, tools, {
29
+ systemPrompt: options?.systemPrompt,
30
+ model: options?.model,
31
+ maxTokens: options?.maxTokens,
32
+ signal,
33
+ executor,
34
+ onToolEvent,
35
+ });
36
+ for await (const event of stream) {
37
+ onStreamEvent?.(event);
38
+ switch (event.type) {
39
+ case 'text_delta':
40
+ text += event.text;
41
+ break;
42
+ case 'tool_use_start':
43
+ activeToolNames.set(event.id, event.name);
44
+ break;
45
+ case 'tool_use_delta':
46
+ // Partial JSON — tracked by provider, nothing to do here
47
+ break;
48
+ case 'tool_use_end':
49
+ collectedToolCalls.push({
50
+ id: event.id,
51
+ name: activeToolNames.get(event.id) ?? 'unknown',
52
+ arguments: event.arguments,
53
+ });
54
+ activeToolNames.delete(event.id);
55
+ break;
56
+ case 'tool_result':
57
+ // CLI handled tool internally via MCP — count it
58
+ toolCallCount++;
59
+ break;
60
+ case 'usage':
61
+ totalPromptTokens += event.promptTokens;
62
+ totalCompletionTokens += event.completionTokens;
63
+ break;
64
+ case 'message_stop':
65
+ finishReason = event.finishReason;
66
+ break;
67
+ }
68
+ }
69
+ // Add assistant message to conversation
70
+ if (collectedToolCalls.length > 0) {
71
+ conversation.push({
72
+ role: 'assistant',
73
+ content: text || '',
74
+ toolCalls: collectedToolCalls,
75
+ });
76
+ }
77
+ else if (text) {
78
+ conversation.push({ role: 'assistant', content: text });
79
+ }
80
+ // If no tool calls, we're done
81
+ if (finishReason !== 'tool_calls' || collectedToolCalls.length === 0) {
82
+ return buildResult(finishReason !== 'error', text || 'Task completed', conversation, toolCallCount, totalPromptTokens, totalCompletionTokens);
83
+ }
84
+ // Execute tool calls and add results to conversation
85
+ for (const tc of collectedToolCalls) {
86
+ if (signal?.aborted)
87
+ break;
88
+ toolCallCount++;
89
+ onToolEvent?.({ type: 'tool_call_start', name: tc.name, args: tc.arguments });
90
+ let result;
91
+ let isError;
92
+ try {
93
+ const res = await executor(tc.name, tc.arguments);
94
+ result = res.result;
95
+ isError = res.isError;
96
+ }
97
+ catch (err) {
98
+ result = err instanceof Error ? err.message : String(err);
99
+ isError = true;
100
+ }
101
+ onToolEvent?.({ type: 'tool_call_result', name: tc.name, result: result.slice(0, 200), isError });
102
+ // Add tool result to conversation (cap size to prevent context overflow)
103
+ conversation.push({
104
+ role: 'tool',
105
+ content: result.slice(0, TOOL_RESULT_CAP),
106
+ toolCallId: tc.id,
107
+ });
108
+ }
109
+ }
110
+ return buildResult(false, `Reached max iterations (${maxIterations})`, conversation, toolCallCount, totalPromptTokens, totalCompletionTokens);
111
+ }
112
+ function buildResult(success, summary, messages, toolCallCount, promptTokens, completionTokens) {
113
+ return {
114
+ success,
115
+ summary,
116
+ messages,
117
+ toolCallCount,
118
+ usage: { promptTokens, completionTokens },
119
+ };
120
+ }
121
+ //# sourceMappingURL=agent-loop.js.map
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Persistent CLI session manager — eliminates cold-start delay by keeping
3
+ * the Claude CLI process alive between messages.
4
+ *
5
+ * Instead of spawning a new CLI process per message (~5s cold start + MCP
6
+ * handshake), we spawn once with `--input-format stream-json` and feed
7
+ * messages via stdin. The CLI maintains conversation context internally.
8
+ *
9
+ * KEY LEARNINGS (Claude Code CLI v2.1.76):
10
+ *
11
+ * 1. Stdin message format (NDJSON):
12
+ * {"type":"user","message":{"role":"user","content":"..."},"parent_tool_use_id":null}
13
+ *
14
+ * 2. The CLI stays alive between messages when stdin is kept open.
15
+ * Each message gets its own system/init → stream_events → result cycle.
16
+ *
17
+ * 3. Turn boundary: the `result` event marks the end of a CLI turn, NOT
18
+ * `message_stop` from stream_event.
19
+ *
20
+ * 4. The CLI uses NDJSON for MCP stdio transport (not Content-Length framing).
21
+ */
22
+ import { type ChildProcess } from 'node:child_process';
23
+ import type { StreamEvent, CliSessionOptions } from './types.js';
24
+ export declare class CliSession {
25
+ private readonly options;
26
+ readonly sessionId: `${string}-${string}-${string}-${string}-${string}`;
27
+ private child;
28
+ private alive;
29
+ private stderrBuf;
30
+ private stdoutBuffer;
31
+ private activeTurn;
32
+ private idleTimer;
33
+ private cleanupFn;
34
+ private parser;
35
+ private readonly log;
36
+ private readonly spawnFn;
37
+ private readonly idleTimeout;
38
+ constructor(options: CliSessionOptions);
39
+ get ready(): boolean;
40
+ /**
41
+ * Inject a mock child process for testing.
42
+ * @internal — test only
43
+ */
44
+ _injectForTest(child: ChildProcess): void;
45
+ /**
46
+ * Spawn the CLI process. Must be called before send().
47
+ */
48
+ spawn(): Promise<void>;
49
+ /**
50
+ * Send a user message and stream back events.
51
+ * Auto-respawns if the process has died.
52
+ */
53
+ send(userMessage: string, systemPromptPrefix?: string): AsyncGenerator<StreamEvent>;
54
+ /**
55
+ * Kill the CLI process.
56
+ */
57
+ kill(): void;
58
+ private pushEvent;
59
+ private completeTurn;
60
+ private markDead;
61
+ private onStdoutData;
62
+ private resetIdleTimer;
63
+ private clearIdleTimer;
64
+ }
65
+ /**
66
+ * Get an existing session or create a new one.
67
+ */
68
+ export declare function getOrCreateCliSession(key: string, options: CliSessionOptions): CliSession;
69
+ /**
70
+ * Kill a specific session.
71
+ */
72
+ export declare function killCliSession(key: string): void;
73
+ /**
74
+ * Kill all CLI sessions (for shutdown).
75
+ */
76
+ export declare function killAllCliSessions(): void;
77
+ //# sourceMappingURL=cli-session.d.ts.map
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Persistent CLI session manager — eliminates cold-start delay by keeping
3
+ * the Claude CLI process alive between messages.
4
+ *
5
+ * Instead of spawning a new CLI process per message (~5s cold start + MCP
6
+ * handshake), we spawn once with `--input-format stream-json` and feed
7
+ * messages via stdin. The CLI maintains conversation context internally.
8
+ *
9
+ * KEY LEARNINGS (Claude Code CLI v2.1.76):
10
+ *
11
+ * 1. Stdin message format (NDJSON):
12
+ * {"type":"user","message":{"role":"user","content":"..."},"parent_tool_use_id":null}
13
+ *
14
+ * 2. The CLI stays alive between messages when stdin is kept open.
15
+ * Each message gets its own system/init → stream_events → result cycle.
16
+ *
17
+ * 3. Turn boundary: the `result` event marks the end of a CLI turn, NOT
18
+ * `message_stop` from stream_event.
19
+ *
20
+ * 4. The CLI uses NDJSON for MCP stdio transport (not Content-Length framing).
21
+ */
22
+ import { randomUUID } from 'node:crypto';
23
+ import { spawn as nodeSpawn } from 'node:child_process';
24
+ import { StreamJsonParser } from './streaming.js';
25
+ const DEFAULT_IDLE_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
26
+ export class CliSession {
27
+ options;
28
+ sessionId = randomUUID();
29
+ child = null;
30
+ alive = false;
31
+ stderrBuf = '';
32
+ stdoutBuffer = '';
33
+ activeTurn = null;
34
+ idleTimer = null;
35
+ cleanupFn = null;
36
+ parser;
37
+ log;
38
+ spawnFn;
39
+ idleTimeout;
40
+ constructor(options) {
41
+ this.options = options;
42
+ this.log = options.logger;
43
+ this.spawnFn = options.spawnFn ?? ((cmd, args, opts) => nodeSpawn(cmd, args, { ...opts, stdio: opts.stdio }));
44
+ this.idleTimeout = options.idleTimeout ?? DEFAULT_IDLE_TIMEOUT_MS;
45
+ // Parser delegates to pushEvent which routes to activeTurn
46
+ this.parser = new StreamJsonParser((event) => this.pushEvent(event));
47
+ }
48
+ get ready() {
49
+ return this.alive;
50
+ }
51
+ /**
52
+ * Inject a mock child process for testing.
53
+ * @internal — test only
54
+ */
55
+ _injectForTest(child) {
56
+ this.child = child;
57
+ this.alive = true;
58
+ this.stderrBuf = '';
59
+ this.stdoutBuffer = '';
60
+ child.stdout.on('data', (chunk) => {
61
+ this.onStdoutData(chunk.toString());
62
+ });
63
+ child.on('exit', () => this.markDead());
64
+ child.on('error', () => this.markDead());
65
+ }
66
+ /**
67
+ * Spawn the CLI process. Must be called before send().
68
+ */
69
+ async spawn() {
70
+ const { binPath, cwd, env, model, mcpConfigPath } = this.options;
71
+ const args = [
72
+ '-p',
73
+ '--input-format',
74
+ 'stream-json',
75
+ '--output-format',
76
+ 'stream-json',
77
+ '--include-partial-messages',
78
+ '--verbose',
79
+ '--dangerously-skip-permissions',
80
+ '--permission-mode',
81
+ 'bypassPermissions',
82
+ '--setting-sources',
83
+ 'user,local',
84
+ '--model',
85
+ model,
86
+ ];
87
+ if (mcpConfigPath) {
88
+ args.push('--mcp-config', mcpConfigPath, '--strict-mcp-config');
89
+ }
90
+ const spawnResult = this.spawnFn(binPath, args, { cwd, stdio: ['pipe', 'pipe', 'pipe'], env: env ?? process.env });
91
+ const child = 'child' in spawnResult ? spawnResult.child : spawnResult;
92
+ const cleanup = 'cleanup' in spawnResult ? spawnResult.cleanup : undefined;
93
+ this.child = child;
94
+ this.cleanupFn = cleanup ?? null;
95
+ this.alive = true;
96
+ this.stderrBuf = '';
97
+ this.stdoutBuffer = '';
98
+ child.stderr.on('data', (chunk) => {
99
+ this.stderrBuf += chunk.toString();
100
+ });
101
+ child.stdout.on('data', (chunk) => {
102
+ this.onStdoutData(chunk.toString());
103
+ });
104
+ child.on('exit', (code) => {
105
+ this.log?.info('CLI session process exited', { sessionId: this.sessionId, exitCode: code });
106
+ this.markDead();
107
+ });
108
+ child.on('error', (err) => {
109
+ this.log?.error('CLI session process error', { sessionId: this.sessionId, err });
110
+ this.markDead();
111
+ });
112
+ this.resetIdleTimer();
113
+ this.log?.info('CLI session spawned', { sessionId: this.sessionId, cwd, model });
114
+ }
115
+ /**
116
+ * Send a user message and stream back events.
117
+ * Auto-respawns if the process has died.
118
+ */
119
+ async *send(userMessage, systemPromptPrefix) {
120
+ if (!this.alive) {
121
+ this.log?.info('CLI session dead, respawning', { sessionId: this.sessionId });
122
+ await this.spawn();
123
+ }
124
+ this.resetIdleTimer();
125
+ this.parser.reset();
126
+ const content = systemPromptPrefix ? `${systemPromptPrefix}\n\n${userMessage}` : userMessage;
127
+ // Write NDJSON message to stdin
128
+ const ndjsonMessage = JSON.stringify({
129
+ type: 'user',
130
+ message: { role: 'user', content },
131
+ parent_tool_use_id: null,
132
+ }) + '\n';
133
+ const turn = { resolve: () => { }, events: [], done: false };
134
+ this.activeTurn = turn;
135
+ // Track whether this turn saw a 'result' event (definitive turn end)
136
+ let sawResult = false;
137
+ // Wrap pushEvent to detect result-driven message_stop as turn end
138
+ const originalPush = this.pushEvent.bind(this);
139
+ this.parser = new StreamJsonParser((event) => {
140
+ // The result event emits message_stop — but in session mode,
141
+ // we need to detect it as the turn boundary
142
+ if (event.type === 'message_stop' && !sawResult) {
143
+ // This is a stream_event message_stop (API turn), not CLI turn end.
144
+ // Push it but don't complete the turn.
145
+ originalPush(event);
146
+ return;
147
+ }
148
+ originalPush(event);
149
+ });
150
+ // Override parser feed to detect result events for turn completion
151
+ const baseFeed = this.parser.feed.bind(this.parser);
152
+ this.parser.feed = (line) => {
153
+ // Check if this line is a result event before parsing
154
+ try {
155
+ let parsed = JSON.parse(line);
156
+ if (parsed.type === 'stream_event' && parsed.event)
157
+ parsed = parsed.event;
158
+ if (parsed.type === 'result') {
159
+ sawResult = true;
160
+ }
161
+ }
162
+ catch {
163
+ // Not JSON, let parser handle it
164
+ }
165
+ baseFeed(line);
166
+ if (sawResult) {
167
+ this.completeTurn();
168
+ }
169
+ };
170
+ try {
171
+ this.child.stdin.write(ndjsonMessage, (err) => {
172
+ if (err) {
173
+ this.log?.error('stdin write error', { sessionId: this.sessionId, err });
174
+ this.markDead();
175
+ turn.done = true;
176
+ turn.resolve();
177
+ }
178
+ });
179
+ }
180
+ catch (err) {
181
+ this.log?.error('stdin write exception', { sessionId: this.sessionId, err });
182
+ this.markDead();
183
+ throw new Error('CLI session stdin write failed');
184
+ }
185
+ // Yield events as they arrive
186
+ while (!turn.done || turn.events.length > 0) {
187
+ if (turn.events.length > 0) {
188
+ yield turn.events.shift();
189
+ }
190
+ else {
191
+ await new Promise((r) => {
192
+ turn.resolve = r;
193
+ setTimeout(r, 50);
194
+ });
195
+ }
196
+ }
197
+ // Yield any remaining events
198
+ while (turn.events.length > 0) {
199
+ yield turn.events.shift();
200
+ }
201
+ this.activeTurn = null;
202
+ this.resetIdleTimer();
203
+ }
204
+ /**
205
+ * Kill the CLI process.
206
+ */
207
+ kill() {
208
+ this.clearIdleTimer();
209
+ if (this.child && this.alive) {
210
+ this.log?.info('Killing CLI session', { sessionId: this.sessionId });
211
+ this.child.kill('SIGTERM');
212
+ setTimeout(() => {
213
+ if (this.child && !this.child.killed) {
214
+ this.child.kill('SIGKILL');
215
+ }
216
+ }, 2000);
217
+ }
218
+ this.markDead();
219
+ }
220
+ // ---------------------------------------------------------------------------
221
+ // Private
222
+ // ---------------------------------------------------------------------------
223
+ pushEvent(event) {
224
+ if (!this.activeTurn)
225
+ return;
226
+ this.activeTurn.events.push(event);
227
+ this.activeTurn.resolve();
228
+ }
229
+ completeTurn() {
230
+ if (!this.activeTurn)
231
+ return;
232
+ this.activeTurn.done = true;
233
+ this.activeTurn.resolve();
234
+ }
235
+ markDead() {
236
+ this.alive = false;
237
+ this.cleanupFn?.();
238
+ this.cleanupFn = null;
239
+ if (this.activeTurn && !this.activeTurn.done) {
240
+ if (!this.activeTurn.events.some((e) => e.type === 'message_stop')) {
241
+ this.activeTurn.events.push({ type: 'message_stop', finishReason: 'error' });
242
+ }
243
+ this.activeTurn.done = true;
244
+ this.activeTurn.resolve();
245
+ }
246
+ }
247
+ onStdoutData(data) {
248
+ this.stdoutBuffer += data;
249
+ const lines = this.stdoutBuffer.split('\n');
250
+ this.stdoutBuffer = lines.pop() || '';
251
+ for (const line of lines) {
252
+ this.parser.feed(line);
253
+ }
254
+ }
255
+ resetIdleTimer() {
256
+ this.clearIdleTimer();
257
+ this.idleTimer = setTimeout(() => {
258
+ this.log?.info('CLI session idle timeout, killing', { sessionId: this.sessionId });
259
+ this.kill();
260
+ }, this.idleTimeout);
261
+ }
262
+ clearIdleTimer() {
263
+ if (this.idleTimer) {
264
+ clearTimeout(this.idleTimer);
265
+ this.idleTimer = null;
266
+ }
267
+ }
268
+ }
269
+ // ---------------------------------------------------------------------------
270
+ // Session manager — cache keyed by identifier
271
+ // ---------------------------------------------------------------------------
272
+ const sessions = new Map();
273
+ /**
274
+ * Get an existing session or create a new one.
275
+ */
276
+ export function getOrCreateCliSession(key, options) {
277
+ const existing = sessions.get(key);
278
+ if (existing && existing.ready) {
279
+ return existing;
280
+ }
281
+ // Kill stale session if present
282
+ if (existing) {
283
+ existing.kill();
284
+ sessions.delete(key);
285
+ }
286
+ const session = new CliSession(options);
287
+ sessions.set(key, session);
288
+ return session;
289
+ }
290
+ /**
291
+ * Kill a specific session.
292
+ */
293
+ export function killCliSession(key) {
294
+ const session = sessions.get(key);
295
+ if (session) {
296
+ session.kill();
297
+ sessions.delete(key);
298
+ }
299
+ }
300
+ /**
301
+ * Kill all CLI sessions (for shutdown).
302
+ */
303
+ export function killAllCliSessions() {
304
+ for (const [, session] of sessions) {
305
+ session.kill();
306
+ }
307
+ sessions.clear();
308
+ }
309
+ //# sourceMappingURL=cli-session.js.map
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Allowlisted environment variables for spawned CLI processes.
3
+ *
4
+ * Only these variables are forwarded from the host process to prevent
5
+ * leaking secrets, user identity, SSH agent sockets, workspace paths,
6
+ * and other sensitive host data to AI CLI tools.
7
+ */
8
+ /**
9
+ * Minimal PATH for sandboxed AI CLI processes. Contains only standard
10
+ * system directories. CLI binaries are invoked via `process.execPath`
11
+ * (node) directly, bypassing shebang resolution, so the node binary
12
+ * directory is NOT needed here.
13
+ */
14
+ export declare const MINIMAL_PATH = "/usr/local/bin:/usr/bin:/bin";
15
+ export declare const ENV_ALLOWLIST: readonly string[];
16
+ /**
17
+ * Build a safe environment object from the current process, forwarding
18
+ * only allowlisted keys. Callers can spread additional overrides on top.
19
+ */
20
+ export declare function buildSafeEnv(overrides?: Record<string, string | undefined>): NodeJS.ProcessEnv;
21
+ /**
22
+ * Convenience helper that returns both `cwd` and `env` in one object,
23
+ * making it harder to forget either when spawning a child process.
24
+ */
25
+ export declare function buildSafeSpawnOpts(cwd: string, envOverrides?: Record<string, string | undefined>): {
26
+ cwd: string;
27
+ env: NodeJS.ProcessEnv;
28
+ };
29
+ //# sourceMappingURL=env-allowlist.d.ts.map
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Allowlisted environment variables for spawned CLI processes.
3
+ *
4
+ * Only these variables are forwarded from the host process to prevent
5
+ * leaking secrets, user identity, SSH agent sockets, workspace paths,
6
+ * and other sensitive host data to AI CLI tools.
7
+ */
8
+ /**
9
+ * Minimal PATH for sandboxed AI CLI processes. Contains only standard
10
+ * system directories. CLI binaries are invoked via `process.execPath`
11
+ * (node) directly, bypassing shebang resolution, so the node binary
12
+ * directory is NOT needed here.
13
+ */
14
+ export const MINIMAL_PATH = '/usr/local/bin:/usr/bin:/bin';
15
+ export const ENV_ALLOWLIST = [
16
+ // System basics
17
+ 'PATH',
18
+ 'LANG',
19
+ 'LC_ALL',
20
+ 'LC_CTYPE',
21
+ 'TERM',
22
+ 'TMPDIR',
23
+ 'TZ',
24
+ // Node/runtime
25
+ 'NODE_ENV',
26
+ 'NO_COLOR',
27
+ 'FORCE_COLOR',
28
+ // TLS/certificates
29
+ 'NODE_EXTRA_CA_CERTS',
30
+ 'SSL_CERT_FILE',
31
+ 'SSL_CERT_DIR',
32
+ // Network proxy (needed behind corporate proxies)
33
+ 'HTTP_PROXY',
34
+ 'HTTPS_PROXY',
35
+ 'NO_PROXY',
36
+ ];
37
+ /**
38
+ * Build a safe environment object from the current process, forwarding
39
+ * only allowlisted keys. Callers can spread additional overrides on top.
40
+ */
41
+ export function buildSafeEnv(overrides) {
42
+ const env = {};
43
+ for (const key of ENV_ALLOWLIST) {
44
+ if (process.env[key] !== undefined) {
45
+ env[key] = process.env[key];
46
+ }
47
+ }
48
+ if (overrides) {
49
+ Object.assign(env, overrides);
50
+ }
51
+ return env;
52
+ }
53
+ /**
54
+ * Convenience helper that returns both `cwd` and `env` in one object,
55
+ * making it harder to forget either when spawning a child process.
56
+ */
57
+ export function buildSafeSpawnOpts(cwd, envOverrides) {
58
+ return { cwd, env: buildSafeEnv(envOverrides) };
59
+ }
60
+ //# sourceMappingURL=env-allowlist.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @synergenius/flow-weaver/agent
3
+ *
4
+ * Provider-agnostic agent loop with MCP bridge for tool execution.
5
+ * Two built-in providers: Anthropic API (raw fetch) and Claude CLI.
6
+ */
7
+ export type { StreamEvent, AgentMessage, AgentProvider, ToolDefinition, ToolExecutor, ToolEvent, McpBridge, AgentLoopOptions, AgentLoopResult, StreamOptions, SpawnFn, ClaudeCliProviderOptions, CliSessionOptions, Logger, } from './types.js';
8
+ export { runAgentLoop } from './agent-loop.js';
9
+ export { AnthropicProvider, createAnthropicProvider } from './providers/anthropic.js';
10
+ export type { AnthropicProviderOptions } from './providers/anthropic.js';
11
+ export { ClaudeCliProvider, createClaudeCliProvider } from './providers/claude-cli.js';
12
+ export { createMcpBridge } from './mcp-bridge.js';
13
+ export { CliSession, getOrCreateCliSession, killCliSession, killAllCliSessions, } from './cli-session.js';
14
+ export { buildSafeEnv, buildSafeSpawnOpts, MINIMAL_PATH, ENV_ALLOWLIST } from './env-allowlist.js';
15
+ export { StreamJsonParser } from './streaming.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @synergenius/flow-weaver/agent
3
+ *
4
+ * Provider-agnostic agent loop with MCP bridge for tool execution.
5
+ * Two built-in providers: Anthropic API (raw fetch) and Claude CLI.
6
+ */
7
+ // Agent loop
8
+ export { runAgentLoop } from './agent-loop.js';
9
+ // Providers
10
+ export { AnthropicProvider, createAnthropicProvider } from './providers/anthropic.js';
11
+ export { ClaudeCliProvider, createClaudeCliProvider } from './providers/claude-cli.js';
12
+ // MCP bridge
13
+ export { createMcpBridge } from './mcp-bridge.js';
14
+ // CLI session (warm persistent sessions)
15
+ export { CliSession, getOrCreateCliSession, killCliSession, killAllCliSessions, } from './cli-session.js';
16
+ // Env utilities
17
+ export { buildSafeEnv, buildSafeSpawnOpts, MINIMAL_PATH, ENV_ALLOWLIST } from './env-allowlist.js';
18
+ // Stream parser (for custom providers)
19
+ export { StreamJsonParser } from './streaming.js';
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * MCP bridge — creates a Unix domain socket server that the MCP tool server
3
+ * connects to for executing tools. Also generates the temporary MCP config
4
+ * and tool definition files needed by the Claude CLI.
5
+ *
6
+ * Usage:
7
+ * const bridge = await createMcpBridge(tools, executor, onToolEvent);
8
+ * // pass bridge.configPath to --mcp-config
9
+ * // ...run CLI...
10
+ * bridge.cleanup();
11
+ */
12
+ import type { ToolDefinition, ToolExecutor, ToolEvent, McpBridge, Logger } from './types.js';
13
+ /**
14
+ * Create an MCP bridge that the Claude CLI can connect to for tool execution.
15
+ *
16
+ * @param tools Tool definitions to advertise to the CLI
17
+ * @param executor Function that executes a tool call
18
+ * @param onToolEvent Optional callback for relaying tool events
19
+ * @param logger Optional logger
20
+ */
21
+ export declare function createMcpBridge(tools: ToolDefinition[], executor: ToolExecutor, onToolEvent?: (event: ToolEvent) => void, logger?: Logger): Promise<McpBridge>;
22
+ //# sourceMappingURL=mcp-bridge.d.ts.map