@superatomai/sdk-node 0.0.14-s → 0.0.15-dsp

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.
package/dist/index.d.mts CHANGED
@@ -1406,6 +1406,10 @@ interface ExecutedToolInfo {
1406
1406
  _recordsShown: number;
1407
1407
  _metadata?: any;
1408
1408
  _sampleData: any[];
1409
+ /** Bounded summary over the FULL fetched result (complete structure). */
1410
+ _summary?: any;
1411
+ /** Up to MAIN_AGENT_COMPLETE_ROWS rows — the complete result when small. */
1412
+ _mainAgentRows?: any[];
1409
1413
  };
1410
1414
  outputSchema?: any;
1411
1415
  sourceSchema?: string;
@@ -1619,8 +1623,16 @@ interface AgentWrittenScript {
1619
1623
  * Controls limits, models, and behavior.
1620
1624
  */
1621
1625
  interface AgentConfig {
1622
- /** Max rows a source agent can return (default: 50) */
1626
+ /** Max rows shown to the UI preview / inlined per source (default: 10) */
1623
1627
  maxRowsPerSource: number;
1628
+ /**
1629
+ * Max rows a source query may FETCH from the DB server-side (default: 2000).
1630
+ * Decoupled from what the main agent is shown: the full result is fetched and
1631
+ * summarized (bounded), but only a small/complete slice enters LLM context.
1632
+ * This lets small lookups (benchmark maps) arrive COMPLETE without letting
1633
+ * large results blow up context.
1634
+ */
1635
+ maxRowsFetched: number;
1624
1636
  /** Model for the main agent (routing + analysis in one LLM call) */
1625
1637
  mainAgentModel: string;
1626
1638
  /** Model for source agent query generation */
@@ -2117,7 +2129,22 @@ declare class MainAgent {
2117
2129
  private turnId;
2118
2130
  private createdFromPrompt;
2119
2131
  private scriptState;
2120
- constructor(externalTools: ExternalTool[], config: AgentConfig, scriptStore?: ScriptStore, turnId?: string, streamBuffer?: StreamBuffer, workflows?: WorkflowDescriptor[]);
2132
+ /**
2133
+ * Fork mode — set when this turn is adapting a near-matching parent script.
2134
+ * In fork mode there is no legitimate "answer with bare text" outcome: the
2135
+ * only correct first move is a tool call (write_script, or a source tool for
2136
+ * schema discovery). We therefore force tool use on the first LLM iteration
2137
+ * so the model can't end its turn with a bare "I'll adapt…" preamble and zero
2138
+ * tool calls. Never set on the fresh-authoring / general-question path.
2139
+ */
2140
+ private forkMode;
2141
+ /**
2142
+ * Per-turn cancellation signal (user hit "Stop"). Set at the top of
2143
+ * handleQuestion and read by the tool handler, the SourceAgent dispatch, and
2144
+ * the script subprocess so an abort tears down every layer of the turn.
2145
+ */
2146
+ private abortSignal?;
2147
+ constructor(externalTools: ExternalTool[], config: AgentConfig, scriptStore?: ScriptStore, turnId?: string, streamBuffer?: StreamBuffer, workflows?: WorkflowDescriptor[], forkMode?: boolean);
2121
2148
  private get scriptingEnabled();
2122
2149
  /**
2123
2150
  * Handle a user question using the multi-agent system.
@@ -2129,7 +2156,7 @@ declare class MainAgent {
2129
2156
  * 4. Direct tools → fn() called directly with LLM params → returns data
2130
2157
  * 5. Generates final analysis text
2131
2158
  */
2132
- handleQuestion(userPrompt: string, apiKey?: string, conversationHistory?: string, streamCallback?: (chunk: string) => void): Promise<AgentResponse>;
2159
+ handleQuestion(userPrompt: string, apiKey?: string, conversationHistory?: string, streamCallback?: (chunk: string) => void, signal?: AbortSignal): Promise<AgentResponse>;
2133
2160
  private handleWriteScript;
2134
2161
  private handleExecuteScript;
2135
2162
  /**
@@ -2214,7 +2241,45 @@ interface LLMOptions {
2214
2241
  temperature?: number;
2215
2242
  topP?: number;
2216
2243
  apiKey?: string;
2244
+ baseURL?: string;
2217
2245
  partial?: (chunk: string) => void;
2246
+ /**
2247
+ * Per-request cancellation. When the caller aborts this signal (user hit
2248
+ * "Stop"), the underlying provider request is cancelled and the call throws
2249
+ * a RequestAbortedError. Threaded into the provider `messages.create` request
2250
+ * options and checked between tool-loop iterations. Currently honored on the
2251
+ * Anthropic path (the agent flow's default provider).
2252
+ */
2253
+ signal?: AbortSignal;
2254
+ /**
2255
+ * Forces a tool call on the FIRST iteration of streamWithTools only
2256
+ * (subsequent iterations revert to auto). Used by fork mode to stop the
2257
+ * model from ending its turn with a bare "I'll adapt the script…" preamble
2258
+ * and zero tool calls. `{ type: 'any' }` lets the model pick which tool
2259
+ * (write_script in the common case, a source tool for schema discovery);
2260
+ * `{ type: 'tool', name }` pins a specific tool. Honored on both the
2261
+ * Anthropic path and the OpenAI/OpenRouter path (mapped to OpenAI's
2262
+ * tool_choice: 'required' / a named function).
2263
+ */
2264
+ firstIterationToolChoice?: {
2265
+ type: 'any';
2266
+ } | {
2267
+ type: 'tool';
2268
+ name: string;
2269
+ };
2270
+ /**
2271
+ * Internal — set only by the OpenRouter wrappers when the target is a Claude
2272
+ * model. Tells the OpenAI-wire path to emit Anthropic `cache_control`
2273
+ * breakpoints (OpenRouter forwards them to Anthropic for prompt caching).
2274
+ * Never set for direct OpenAI/Groq calls, so their requests are unchanged.
2275
+ */
2276
+ _openrouterClaudeCaching?: boolean;
2277
+ /**
2278
+ * Internal — OpenRouter provider-routing preferences (forwarded as the
2279
+ * `provider` body field). Set by the OpenRouter wrappers to steer routing to
2280
+ * a fast backend (e.g. {sort:'throughput'}). Never set for direct OpenAI/Groq.
2281
+ */
2282
+ _openrouterProvider?: Record<string, unknown>;
2218
2283
  }
2219
2284
  interface Tool {
2220
2285
  name: string;
@@ -2262,11 +2327,44 @@ declare class LLM {
2262
2327
  * "claude-sonnet-4-5" → ["anthropic", "claude-sonnet-4-5"] (default)
2263
2328
  */
2264
2329
  private static _parseModel;
2330
+ /**
2331
+ * Map an Anthropic model id (e.g. "claude-sonnet-4-5-20250929") to the OpenRouter slug
2332
+ * (e.g. "claude-sonnet-4.5"). OpenRouter slugs drop the date suffix and use dotted versions.
2333
+ */
2334
+ private static _toOpenRouterSlug;
2335
+ /**
2336
+ * Per-provider proxy base URL. Returns `${SUPERATOM_LLM_PROXY_URL}/<provider>`
2337
+ * when our Cloudflare LLM proxy is configured, else undefined (→ talk to the
2338
+ * provider directly, legacy behaviour). An explicit options.baseURL (e.g.
2339
+ * OpenRouter) always wins and is never overridden. See backend/docs/llm-proxy.md.
2340
+ */
2341
+ private static _proxyBaseURL;
2342
+ private static _openrouterOptions;
2343
+ private static _openrouterText;
2344
+ private static _openrouterStream;
2345
+ private static _openrouterStreamWithTools;
2346
+ /**
2347
+ * Build an Anthropic client. Routes through our Cloudflare LLM proxy when
2348
+ * SUPERATOM_LLM_PROXY_URL is set (each client ships a per-client proxy key as
2349
+ * ANTHROPIC_API_KEY and never holds the real key); otherwise talks to
2350
+ * api.anthropic.com directly. See backend/docs/llm-proxy.md.
2351
+ */
2352
+ private static _anthropicClient;
2353
+ /** True when OpenRouter is configured as a fail-open fallback for Claude. */
2354
+ private static _openrouterAvailable;
2355
+ /** Remap an Anthropic model id to the OpenRouter model path for fail-open. */
2356
+ private static _anthropicFallbackModel;
2265
2357
  private static _anthropicText;
2266
2358
  private static _anthropicStream;
2267
2359
  private static _anthropicStreamWithTools;
2268
2360
  private static _groqText;
2269
2361
  private static _groqStream;
2362
+ /**
2363
+ * Gemini request options carrying the proxy base URL, or undefined → talk to
2364
+ * generativelanguage.googleapis.com directly. The Google SDK takes baseUrl as a
2365
+ * per-model request option, not a constructor arg. See backend/docs/llm-proxy.md.
2366
+ */
2367
+ private static _geminiRequestOptions;
2270
2368
  private static _geminiText;
2271
2369
  private static _geminiStream;
2272
2370
  /**
@@ -2275,8 +2373,28 @@ declare class LLM {
2275
2373
  */
2276
2374
  private static _cleanSchemaForGemini;
2277
2375
  private static _geminiStreamWithTools;
2376
+ /** True for Anthropic/Claude model ids — gates OpenRouter prompt caching. */
2377
+ private static _isClaudeModel;
2378
+ /**
2379
+ * Build the OpenAI-wire system message. For OpenRouter + Claude
2380
+ * (cacheClaude=true) it emits content parts carrying Anthropic
2381
+ * `cache_control` breakpoints (preserving any the caller set, else marking
2382
+ * the last block), so OpenRouter forwards them to Anthropic for prompt
2383
+ * caching. Otherwise it returns a plain flattened string — unchanged for
2384
+ * direct OpenAI/Groq.
2385
+ */
2386
+ private static _openaiSystemMessage;
2387
+ /**
2388
+ * Split an OpenAI-wire usage object. `prompt_tokens` INCLUDES cached tokens,
2389
+ * so we subtract them out (Anthropic-style: input excludes cache reads) and
2390
+ * report cached separately — this makes calculateCost price cache reads at
2391
+ * the discounted rate and reflects OpenRouter prompt-cache savings in logs.
2392
+ */
2393
+ private static _openaiUsage;
2278
2394
  private static _openaiText;
2279
2395
  private static _openaiStream;
2396
+ /** Map the Anthropic-style firstIterationToolChoice to OpenAI's tool_choice. */
2397
+ private static _openaiToolChoice;
2280
2398
  private static _openaiStreamWithTools;
2281
2399
  /**
2282
2400
  * Parse JSON string, handling markdown code blocks and surrounding text
@@ -3201,7 +3319,7 @@ declare abstract class BaseLLM {
3201
3319
  * This helps provide intelligent suggestions for follow-up queries
3202
3320
  * For general/conversational questions without components, pass textResponse instead
3203
3321
  */
3204
- generateNextQuestions(originalUserPrompt: string, component?: Component | null, componentData?: Record<string, unknown>, apiKey?: string, conversationHistory?: string, textResponse?: string): Promise<string[]>;
3322
+ generateNextQuestions(originalUserPrompt: string, component?: Component | null, componentData?: Record<string, unknown>, apiKey?: string, conversationHistory?: string, textResponse?: string, signal?: AbortSignal): Promise<string[]>;
3205
3323
  }
3206
3324
 
3207
3325
  interface AnthropicLLMConfig extends BaseLLMConfig {
@@ -3433,7 +3551,7 @@ declare class ScriptMatcher {
3433
3551
  * Uses ONE LLM call that picks the script AND extracts parameters.
3434
3552
  * Returns null if no script matches.
3435
3553
  */
3436
- match(userPrompt: string, apiKey?: string, model?: string): Promise<ScriptMatch | null>;
3554
+ match(userPrompt: string, apiKey?: string, model?: string, signal?: AbortSignal): Promise<ScriptMatch | null>;
3437
3555
  /**
3438
3556
  * Build the script catalog string for the LLM prompt.
3439
3557
  * Each script gets: index, ID, name, description, and parameter definitions.
@@ -3459,6 +3577,12 @@ interface RunScriptOptions {
3459
3577
  streamBuffer?: StreamBuffer;
3460
3578
  /** Override the wall-clock timeout (default `SCRIPT_TIMEOUT_MS`, 60s). */
3461
3579
  timeoutMs?: number;
3580
+ /**
3581
+ * Per-turn cancellation signal. When the user hits "Stop" mid-run, the child
3582
+ * process group is SIGKILLed and the run resolves as an aborted failure (the
3583
+ * caller is already unwinding, so the result is discarded).
3584
+ */
3585
+ signal?: AbortSignal;
3462
3586
  }
3463
3587
  /**
3464
3588
  * Execute a recipe by spawning a tsx child on the script's .ts file.
package/dist/index.d.ts CHANGED
@@ -1406,6 +1406,10 @@ interface ExecutedToolInfo {
1406
1406
  _recordsShown: number;
1407
1407
  _metadata?: any;
1408
1408
  _sampleData: any[];
1409
+ /** Bounded summary over the FULL fetched result (complete structure). */
1410
+ _summary?: any;
1411
+ /** Up to MAIN_AGENT_COMPLETE_ROWS rows — the complete result when small. */
1412
+ _mainAgentRows?: any[];
1409
1413
  };
1410
1414
  outputSchema?: any;
1411
1415
  sourceSchema?: string;
@@ -1619,8 +1623,16 @@ interface AgentWrittenScript {
1619
1623
  * Controls limits, models, and behavior.
1620
1624
  */
1621
1625
  interface AgentConfig {
1622
- /** Max rows a source agent can return (default: 50) */
1626
+ /** Max rows shown to the UI preview / inlined per source (default: 10) */
1623
1627
  maxRowsPerSource: number;
1628
+ /**
1629
+ * Max rows a source query may FETCH from the DB server-side (default: 2000).
1630
+ * Decoupled from what the main agent is shown: the full result is fetched and
1631
+ * summarized (bounded), but only a small/complete slice enters LLM context.
1632
+ * This lets small lookups (benchmark maps) arrive COMPLETE without letting
1633
+ * large results blow up context.
1634
+ */
1635
+ maxRowsFetched: number;
1624
1636
  /** Model for the main agent (routing + analysis in one LLM call) */
1625
1637
  mainAgentModel: string;
1626
1638
  /** Model for source agent query generation */
@@ -2117,7 +2129,22 @@ declare class MainAgent {
2117
2129
  private turnId;
2118
2130
  private createdFromPrompt;
2119
2131
  private scriptState;
2120
- constructor(externalTools: ExternalTool[], config: AgentConfig, scriptStore?: ScriptStore, turnId?: string, streamBuffer?: StreamBuffer, workflows?: WorkflowDescriptor[]);
2132
+ /**
2133
+ * Fork mode — set when this turn is adapting a near-matching parent script.
2134
+ * In fork mode there is no legitimate "answer with bare text" outcome: the
2135
+ * only correct first move is a tool call (write_script, or a source tool for
2136
+ * schema discovery). We therefore force tool use on the first LLM iteration
2137
+ * so the model can't end its turn with a bare "I'll adapt…" preamble and zero
2138
+ * tool calls. Never set on the fresh-authoring / general-question path.
2139
+ */
2140
+ private forkMode;
2141
+ /**
2142
+ * Per-turn cancellation signal (user hit "Stop"). Set at the top of
2143
+ * handleQuestion and read by the tool handler, the SourceAgent dispatch, and
2144
+ * the script subprocess so an abort tears down every layer of the turn.
2145
+ */
2146
+ private abortSignal?;
2147
+ constructor(externalTools: ExternalTool[], config: AgentConfig, scriptStore?: ScriptStore, turnId?: string, streamBuffer?: StreamBuffer, workflows?: WorkflowDescriptor[], forkMode?: boolean);
2121
2148
  private get scriptingEnabled();
2122
2149
  /**
2123
2150
  * Handle a user question using the multi-agent system.
@@ -2129,7 +2156,7 @@ declare class MainAgent {
2129
2156
  * 4. Direct tools → fn() called directly with LLM params → returns data
2130
2157
  * 5. Generates final analysis text
2131
2158
  */
2132
- handleQuestion(userPrompt: string, apiKey?: string, conversationHistory?: string, streamCallback?: (chunk: string) => void): Promise<AgentResponse>;
2159
+ handleQuestion(userPrompt: string, apiKey?: string, conversationHistory?: string, streamCallback?: (chunk: string) => void, signal?: AbortSignal): Promise<AgentResponse>;
2133
2160
  private handleWriteScript;
2134
2161
  private handleExecuteScript;
2135
2162
  /**
@@ -2214,7 +2241,45 @@ interface LLMOptions {
2214
2241
  temperature?: number;
2215
2242
  topP?: number;
2216
2243
  apiKey?: string;
2244
+ baseURL?: string;
2217
2245
  partial?: (chunk: string) => void;
2246
+ /**
2247
+ * Per-request cancellation. When the caller aborts this signal (user hit
2248
+ * "Stop"), the underlying provider request is cancelled and the call throws
2249
+ * a RequestAbortedError. Threaded into the provider `messages.create` request
2250
+ * options and checked between tool-loop iterations. Currently honored on the
2251
+ * Anthropic path (the agent flow's default provider).
2252
+ */
2253
+ signal?: AbortSignal;
2254
+ /**
2255
+ * Forces a tool call on the FIRST iteration of streamWithTools only
2256
+ * (subsequent iterations revert to auto). Used by fork mode to stop the
2257
+ * model from ending its turn with a bare "I'll adapt the script…" preamble
2258
+ * and zero tool calls. `{ type: 'any' }` lets the model pick which tool
2259
+ * (write_script in the common case, a source tool for schema discovery);
2260
+ * `{ type: 'tool', name }` pins a specific tool. Honored on both the
2261
+ * Anthropic path and the OpenAI/OpenRouter path (mapped to OpenAI's
2262
+ * tool_choice: 'required' / a named function).
2263
+ */
2264
+ firstIterationToolChoice?: {
2265
+ type: 'any';
2266
+ } | {
2267
+ type: 'tool';
2268
+ name: string;
2269
+ };
2270
+ /**
2271
+ * Internal — set only by the OpenRouter wrappers when the target is a Claude
2272
+ * model. Tells the OpenAI-wire path to emit Anthropic `cache_control`
2273
+ * breakpoints (OpenRouter forwards them to Anthropic for prompt caching).
2274
+ * Never set for direct OpenAI/Groq calls, so their requests are unchanged.
2275
+ */
2276
+ _openrouterClaudeCaching?: boolean;
2277
+ /**
2278
+ * Internal — OpenRouter provider-routing preferences (forwarded as the
2279
+ * `provider` body field). Set by the OpenRouter wrappers to steer routing to
2280
+ * a fast backend (e.g. {sort:'throughput'}). Never set for direct OpenAI/Groq.
2281
+ */
2282
+ _openrouterProvider?: Record<string, unknown>;
2218
2283
  }
2219
2284
  interface Tool {
2220
2285
  name: string;
@@ -2262,11 +2327,44 @@ declare class LLM {
2262
2327
  * "claude-sonnet-4-5" → ["anthropic", "claude-sonnet-4-5"] (default)
2263
2328
  */
2264
2329
  private static _parseModel;
2330
+ /**
2331
+ * Map an Anthropic model id (e.g. "claude-sonnet-4-5-20250929") to the OpenRouter slug
2332
+ * (e.g. "claude-sonnet-4.5"). OpenRouter slugs drop the date suffix and use dotted versions.
2333
+ */
2334
+ private static _toOpenRouterSlug;
2335
+ /**
2336
+ * Per-provider proxy base URL. Returns `${SUPERATOM_LLM_PROXY_URL}/<provider>`
2337
+ * when our Cloudflare LLM proxy is configured, else undefined (→ talk to the
2338
+ * provider directly, legacy behaviour). An explicit options.baseURL (e.g.
2339
+ * OpenRouter) always wins and is never overridden. See backend/docs/llm-proxy.md.
2340
+ */
2341
+ private static _proxyBaseURL;
2342
+ private static _openrouterOptions;
2343
+ private static _openrouterText;
2344
+ private static _openrouterStream;
2345
+ private static _openrouterStreamWithTools;
2346
+ /**
2347
+ * Build an Anthropic client. Routes through our Cloudflare LLM proxy when
2348
+ * SUPERATOM_LLM_PROXY_URL is set (each client ships a per-client proxy key as
2349
+ * ANTHROPIC_API_KEY and never holds the real key); otherwise talks to
2350
+ * api.anthropic.com directly. See backend/docs/llm-proxy.md.
2351
+ */
2352
+ private static _anthropicClient;
2353
+ /** True when OpenRouter is configured as a fail-open fallback for Claude. */
2354
+ private static _openrouterAvailable;
2355
+ /** Remap an Anthropic model id to the OpenRouter model path for fail-open. */
2356
+ private static _anthropicFallbackModel;
2265
2357
  private static _anthropicText;
2266
2358
  private static _anthropicStream;
2267
2359
  private static _anthropicStreamWithTools;
2268
2360
  private static _groqText;
2269
2361
  private static _groqStream;
2362
+ /**
2363
+ * Gemini request options carrying the proxy base URL, or undefined → talk to
2364
+ * generativelanguage.googleapis.com directly. The Google SDK takes baseUrl as a
2365
+ * per-model request option, not a constructor arg. See backend/docs/llm-proxy.md.
2366
+ */
2367
+ private static _geminiRequestOptions;
2270
2368
  private static _geminiText;
2271
2369
  private static _geminiStream;
2272
2370
  /**
@@ -2275,8 +2373,28 @@ declare class LLM {
2275
2373
  */
2276
2374
  private static _cleanSchemaForGemini;
2277
2375
  private static _geminiStreamWithTools;
2376
+ /** True for Anthropic/Claude model ids — gates OpenRouter prompt caching. */
2377
+ private static _isClaudeModel;
2378
+ /**
2379
+ * Build the OpenAI-wire system message. For OpenRouter + Claude
2380
+ * (cacheClaude=true) it emits content parts carrying Anthropic
2381
+ * `cache_control` breakpoints (preserving any the caller set, else marking
2382
+ * the last block), so OpenRouter forwards them to Anthropic for prompt
2383
+ * caching. Otherwise it returns a plain flattened string — unchanged for
2384
+ * direct OpenAI/Groq.
2385
+ */
2386
+ private static _openaiSystemMessage;
2387
+ /**
2388
+ * Split an OpenAI-wire usage object. `prompt_tokens` INCLUDES cached tokens,
2389
+ * so we subtract them out (Anthropic-style: input excludes cache reads) and
2390
+ * report cached separately — this makes calculateCost price cache reads at
2391
+ * the discounted rate and reflects OpenRouter prompt-cache savings in logs.
2392
+ */
2393
+ private static _openaiUsage;
2278
2394
  private static _openaiText;
2279
2395
  private static _openaiStream;
2396
+ /** Map the Anthropic-style firstIterationToolChoice to OpenAI's tool_choice. */
2397
+ private static _openaiToolChoice;
2280
2398
  private static _openaiStreamWithTools;
2281
2399
  /**
2282
2400
  * Parse JSON string, handling markdown code blocks and surrounding text
@@ -3201,7 +3319,7 @@ declare abstract class BaseLLM {
3201
3319
  * This helps provide intelligent suggestions for follow-up queries
3202
3320
  * For general/conversational questions without components, pass textResponse instead
3203
3321
  */
3204
- generateNextQuestions(originalUserPrompt: string, component?: Component | null, componentData?: Record<string, unknown>, apiKey?: string, conversationHistory?: string, textResponse?: string): Promise<string[]>;
3322
+ generateNextQuestions(originalUserPrompt: string, component?: Component | null, componentData?: Record<string, unknown>, apiKey?: string, conversationHistory?: string, textResponse?: string, signal?: AbortSignal): Promise<string[]>;
3205
3323
  }
3206
3324
 
3207
3325
  interface AnthropicLLMConfig extends BaseLLMConfig {
@@ -3433,7 +3551,7 @@ declare class ScriptMatcher {
3433
3551
  * Uses ONE LLM call that picks the script AND extracts parameters.
3434
3552
  * Returns null if no script matches.
3435
3553
  */
3436
- match(userPrompt: string, apiKey?: string, model?: string): Promise<ScriptMatch | null>;
3554
+ match(userPrompt: string, apiKey?: string, model?: string, signal?: AbortSignal): Promise<ScriptMatch | null>;
3437
3555
  /**
3438
3556
  * Build the script catalog string for the LLM prompt.
3439
3557
  * Each script gets: index, ID, name, description, and parameter definitions.
@@ -3459,6 +3577,12 @@ interface RunScriptOptions {
3459
3577
  streamBuffer?: StreamBuffer;
3460
3578
  /** Override the wall-clock timeout (default `SCRIPT_TIMEOUT_MS`, 60s). */
3461
3579
  timeoutMs?: number;
3580
+ /**
3581
+ * Per-turn cancellation signal. When the user hits "Stop" mid-run, the child
3582
+ * process group is SIGKILLed and the run resolves as an aborted failure (the
3583
+ * caller is already unwinding, so the result is discarded).
3584
+ */
3585
+ signal?: AbortSignal;
3462
3586
  }
3463
3587
  /**
3464
3588
  * Execute a recipe by spawning a tsx child on the script's .ts file.