@axlsdk/axl 0.15.0 → 0.16.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.
- package/README.md +11 -7
- package/dist/index.cjs +1734 -627
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +941 -236
- package/dist/index.d.ts +941 -236
- package/dist/index.js +1725 -627
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -274,96 +274,114 @@ type RaceOptions<T = unknown> = {
|
|
|
274
274
|
};
|
|
275
275
|
/** Execution status */
|
|
276
276
|
type ExecutionStatus = 'running' | 'completed' | 'failed' | 'waiting';
|
|
277
|
-
/**
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
277
|
+
/**
|
|
278
|
+
* Canonical list of `AxlEvent.type` discriminators. Single source of truth —
|
|
279
|
+
* derive `AxlEventType` from this tuple and use it to validate stream filters,
|
|
280
|
+
* redaction tables, and exhaustiveness assertions.
|
|
281
|
+
*/
|
|
282
|
+
declare const AXL_EVENT_TYPES: readonly ["workflow_start", "workflow_end", "ask_start", "ask_end", "agent_call_start", "agent_call_end", "token", "tool_call_start", "tool_call_end", "tool_approval", "tool_denied", "delegate", "handoff_start", "handoff_return", "pipeline", "partial_object", "verify", "log", "memory_remember", "memory_recall", "memory_forget", "checkpoint_save", "checkpoint_replay", "await_human", "await_human_resolved", "guardrail", "schema_check", "validate", "done", "error"];
|
|
283
|
+
/** Discriminator union derived from `AXL_EVENT_TYPES`. */
|
|
284
|
+
type AxlEventType = (typeof AXL_EVENT_TYPES)[number];
|
|
285
|
+
/** Resolved model parameters sent to the provider for an LLM call. */
|
|
286
|
+
type AgentCallParams = {
|
|
287
|
+
temperature?: number;
|
|
288
|
+
maxTokens?: number;
|
|
289
|
+
effort?: Effort;
|
|
290
|
+
thinkingBudget?: number;
|
|
291
|
+
includeThoughts?: boolean;
|
|
292
|
+
toolChoice?: ToolChoice;
|
|
293
|
+
stop?: string[];
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* Data shape for `agent_call_start` events — the **request** side of the call.
|
|
297
|
+
* Everything here is known at the moment the call is dispatched, before the
|
|
298
|
+
* provider responds. Consumers can render "what's being asked" without waiting
|
|
299
|
+
* for completion.
|
|
300
|
+
*/
|
|
301
|
+
type AgentCallStartData = {
|
|
281
302
|
/** Original user prompt passed to `ctx.ask()`. Does not include retry feedback or tool results. */
|
|
282
303
|
prompt: string;
|
|
283
|
-
/** Final LLM response content for this turn. */
|
|
284
|
-
response: string;
|
|
285
304
|
/** Resolved system prompt (after evaluating dynamic system selectors). */
|
|
286
305
|
system?: string;
|
|
287
|
-
/** Reasoning/thinking content returned by the provider, when available. */
|
|
288
|
-
thinking?: string;
|
|
289
306
|
/** Resolved model parameters sent to the provider for this call. */
|
|
290
|
-
params?:
|
|
291
|
-
temperature?: number;
|
|
292
|
-
maxTokens?: number;
|
|
293
|
-
effort?: Effort;
|
|
294
|
-
thinkingBudget?: number;
|
|
295
|
-
includeThoughts?: boolean;
|
|
296
|
-
toolChoice?: ToolChoice;
|
|
297
|
-
stop?: string[];
|
|
298
|
-
};
|
|
307
|
+
params?: AgentCallParams;
|
|
299
308
|
/** 1-indexed iteration of the tool-calling loop for this `ctx.ask()` call. */
|
|
300
|
-
turn
|
|
309
|
+
turn: number;
|
|
301
310
|
/** When set, this call is a retry triggered by a failed gate check on the previous turn. */
|
|
302
311
|
retryReason?: 'schema' | 'validate' | 'guardrail';
|
|
312
|
+
/** Names of tools exposed to the model on this call. Empty/omitted when no tools are bound. */
|
|
313
|
+
toolNames?: string[];
|
|
303
314
|
/** Full ChatMessage[] sent to the provider this turn. Only populated when `trace.level === 'full'`. */
|
|
304
315
|
messages?: ChatMessage[];
|
|
305
316
|
};
|
|
306
|
-
/**
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
317
|
+
/**
|
|
318
|
+
* Data shape for `agent_call_end` events — the **response** side of the call.
|
|
319
|
+
* Populated when the provider returns (success or recoverable failure). The
|
|
320
|
+
* companion request-side payload lives on the matching `agent_call_start`.
|
|
321
|
+
*
|
|
322
|
+
* Pair invariant: every `agent_call_start` is followed by exactly one
|
|
323
|
+
* `agent_call_end`, even on provider error. On the error path `response` is
|
|
324
|
+
* empty and `error` carries the provider's message; cost/tokens/duration are
|
|
325
|
+
* still emitted (top-level on the event) when partial usage is available.
|
|
326
|
+
*/
|
|
327
|
+
type AgentCallEndData = {
|
|
328
|
+
/** Final LLM response content for this turn. Empty string on error. */
|
|
329
|
+
response: string;
|
|
330
|
+
/** Reasoning/thinking content returned by the provider, when available. */
|
|
331
|
+
thinking?: string;
|
|
332
|
+
/** 1-indexed iteration of the tool-calling loop. Mirrors the matching `agent_call_start.data.turn`. */
|
|
333
|
+
turn: number;
|
|
334
|
+
/** Mirrors `agent_call_start.data.retryReason` so cost-attribution consumers
|
|
335
|
+
* reading `agent_call_end` (cost lives here) can bucket without joining. */
|
|
336
|
+
retryReason?: 'schema' | 'validate' | 'guardrail';
|
|
337
|
+
/** Provider error message when the call threw (network failure, 4xx/5xx,
|
|
338
|
+
* abort, etc). Mutually exclusive with `response` content. Subject to
|
|
339
|
+
* `config.trace.redact` (vendor errors can echo prompt text). */
|
|
340
|
+
error?: string;
|
|
319
341
|
};
|
|
320
|
-
/** Data shape for `
|
|
321
|
-
type
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
maxAttempts: number;
|
|
326
|
-
feedbackMessage?: string;
|
|
342
|
+
/** Data shape for `tool_call_end` events. */
|
|
343
|
+
type ToolCallData = {
|
|
344
|
+
args: unknown;
|
|
345
|
+
result: unknown;
|
|
346
|
+
callId?: string;
|
|
327
347
|
};
|
|
328
|
-
/** Data shape for `
|
|
329
|
-
type
|
|
330
|
-
|
|
331
|
-
reason?: string;
|
|
332
|
-
attempt: number;
|
|
333
|
-
maxAttempts: number;
|
|
334
|
-
feedbackMessage?: string;
|
|
348
|
+
/** Data shape for `tool_call_start` events. */
|
|
349
|
+
type ToolCallStartData = {
|
|
350
|
+
args: unknown;
|
|
335
351
|
};
|
|
336
|
-
/** Data shape for `tool_approval`
|
|
337
|
-
type
|
|
352
|
+
/** Data shape for `tool_approval` events. Emitted by the approval gate on both outcomes. */
|
|
353
|
+
type ToolApprovalData = {
|
|
338
354
|
approved: boolean;
|
|
339
355
|
args: unknown;
|
|
340
356
|
reason?: string;
|
|
341
357
|
};
|
|
342
|
-
/** Data shape for `
|
|
343
|
-
type
|
|
344
|
-
args
|
|
345
|
-
|
|
358
|
+
/** Data shape for `tool_denied` events. Emitted when the LLM names a tool the agent doesn't expose. */
|
|
359
|
+
type ToolDeniedData = {
|
|
360
|
+
args?: unknown;
|
|
361
|
+
reason?: string;
|
|
346
362
|
callId?: string;
|
|
347
363
|
};
|
|
348
|
-
/** Data shape for `
|
|
349
|
-
type
|
|
364
|
+
/** Data shape for `handoff_start` events (always emitted, pre-transition). */
|
|
365
|
+
type HandoffStartData = {
|
|
366
|
+
source: string;
|
|
350
367
|
target: string;
|
|
351
368
|
mode: 'oneway' | 'roundtrip';
|
|
352
|
-
/** Wall-clock ms from handoff_to_X tool call to target agent completion.
|
|
353
|
-
* Always emitted — the event only fires at the terminal point of the
|
|
354
|
-
* handoff, so we always have a measurement. */
|
|
355
|
-
duration: number;
|
|
356
|
-
/** Source agent that initiated the handoff (mirrors the event's `agent` field
|
|
357
|
-
* for convenience — lets consumers query the handoff chain without
|
|
358
|
-
* stitching together event.agent and data.target). */
|
|
359
|
-
source?: string;
|
|
360
369
|
/** The `message` arg the source agent passed when invoking `handoff_to_X`
|
|
361
|
-
* (roundtrip mode only).
|
|
362
|
-
* to delegate. Subject to `config.trace.redact`. */
|
|
370
|
+
* (roundtrip mode only). Subject to `config.trace.redact`. */
|
|
363
371
|
message?: string;
|
|
364
372
|
};
|
|
365
|
-
/** Data shape for `
|
|
366
|
-
|
|
373
|
+
/** Data shape for `handoff_return` events (roundtrip-only, post-return).
|
|
374
|
+
* The returned value itself is observable via the target ask's
|
|
375
|
+
* `ask_end.outcome`; this event marks the control transfer back to the
|
|
376
|
+
* source agent and carries the round-trip duration. */
|
|
377
|
+
type HandoffReturnData = {
|
|
378
|
+
source: string;
|
|
379
|
+
target: string;
|
|
380
|
+
/** Wall-clock ms from `handoff_start` emission to control returning. */
|
|
381
|
+
duration: number;
|
|
382
|
+
};
|
|
383
|
+
/** Data shape for `delegate` events. */
|
|
384
|
+
type DelegateData = {
|
|
367
385
|
candidates: string[];
|
|
368
386
|
/** Set when the decision is known at emission time (single-agent short-circuit). */
|
|
369
387
|
selected?: string;
|
|
@@ -372,20 +390,45 @@ type DelegateTraceData = {
|
|
|
372
390
|
/** Why this delegate was emitted: 'routed' (multi-agent) or 'single_candidate'. */
|
|
373
391
|
reason: 'routed' | 'single_candidate';
|
|
374
392
|
};
|
|
375
|
-
/** Data shape for `verify`
|
|
376
|
-
type
|
|
393
|
+
/** Data shape for `verify` events. */
|
|
394
|
+
type VerifyData = {
|
|
377
395
|
attempts: number;
|
|
378
396
|
passed: boolean;
|
|
379
397
|
lastError?: string;
|
|
380
398
|
};
|
|
381
|
-
/** Data shape for `
|
|
382
|
-
type
|
|
399
|
+
/** Data shape for legacy `guardrail` events. Replaced by `pipeline` in PR 2. */
|
|
400
|
+
type GuardrailData = {
|
|
401
|
+
guardrailType: 'input' | 'output';
|
|
402
|
+
blocked: boolean;
|
|
403
|
+
reason?: string;
|
|
404
|
+
attempt?: number;
|
|
405
|
+
maxAttempts?: number;
|
|
406
|
+
feedbackMessage?: string;
|
|
407
|
+
};
|
|
408
|
+
/** Data shape for legacy `schema_check` events. Replaced by `pipeline` in PR 2. */
|
|
409
|
+
type SchemaCheckData = {
|
|
410
|
+
valid: boolean;
|
|
411
|
+
reason?: string;
|
|
412
|
+
attempt: number;
|
|
413
|
+
maxAttempts: number;
|
|
414
|
+
feedbackMessage?: string;
|
|
415
|
+
};
|
|
416
|
+
/** Data shape for legacy `validate` events. Replaced by `pipeline` in PR 2. */
|
|
417
|
+
type ValidateData = {
|
|
418
|
+
valid: boolean;
|
|
419
|
+
reason?: string;
|
|
420
|
+
attempt: number;
|
|
421
|
+
maxAttempts: number;
|
|
422
|
+
feedbackMessage?: string;
|
|
423
|
+
};
|
|
424
|
+
/** Data shape for `workflow_start` events. Emitted once per workflow execution. */
|
|
425
|
+
type WorkflowStartData = {
|
|
383
426
|
/** The validated input passed to the workflow handler. */
|
|
384
427
|
input: unknown;
|
|
385
428
|
};
|
|
386
|
-
/** Data shape for `workflow_end`
|
|
429
|
+
/** Data shape for `workflow_end` events. Emitted once per workflow execution
|
|
387
430
|
* on completion, failure, or cancellation. Distinguish cancellation via `aborted`. */
|
|
388
|
-
type
|
|
431
|
+
type WorkflowEndData = {
|
|
389
432
|
status: 'completed' | 'failed';
|
|
390
433
|
duration: number;
|
|
391
434
|
/** Workflow return value. Present on `status: 'completed'`. */
|
|
@@ -396,78 +439,328 @@ type WorkflowEndTraceData = {
|
|
|
396
439
|
* or consumer disconnect on streaming workflows). */
|
|
397
440
|
aborted?: boolean;
|
|
398
441
|
};
|
|
399
|
-
/**
|
|
400
|
-
|
|
442
|
+
/** Data shape for `checkpoint_save` / `checkpoint_replay` events.
|
|
443
|
+
* Emitted by `ctx.checkpoint(name, fn)` — `save` on first execution,
|
|
444
|
+
* `replay` when a saved value short-circuits the function call. */
|
|
445
|
+
type CheckpointEventData = {
|
|
446
|
+
/** Stable, caller-supplied identifier under which the checkpoint is
|
|
447
|
+
* stored. Internal auto-checkpoints from ask/spawn/race/parallel/map
|
|
448
|
+
* use names prefixed with `__auto/<primitive>/`. */
|
|
449
|
+
name: string;
|
|
450
|
+
};
|
|
451
|
+
/** Data shape for `await_human` events — emitted when execution suspends
|
|
452
|
+
* for a human decision via `ctx.awaitHuman()`. The pending side of the
|
|
453
|
+
* pair; `await_human_resolved` follows when the decision arrives. */
|
|
454
|
+
type AwaitHumanData = {
|
|
455
|
+
/** Optional human-facing prompt describing the decision needed. */
|
|
456
|
+
prompt?: string;
|
|
457
|
+
/** Channel routing the decision (e.g., 'slack', 'email', custom). */
|
|
458
|
+
channel?: string;
|
|
459
|
+
};
|
|
460
|
+
/** Data shape for `await_human_resolved` events — paired terminal of an
|
|
461
|
+
* `await_human` request. Carries the `HumanDecision` returned to the workflow. */
|
|
462
|
+
type AwaitHumanResolvedData = {
|
|
463
|
+
channel?: string;
|
|
464
|
+
decision: HumanDecision;
|
|
465
|
+
};
|
|
466
|
+
/** Data shape for `memory_remember` / `memory_recall` / `memory_forget` events. */
|
|
467
|
+
type MemoryEventData = {
|
|
468
|
+
scope: string;
|
|
469
|
+
key?: string;
|
|
470
|
+
/** Result count for `recall` (number of vectors returned). */
|
|
471
|
+
count?: number;
|
|
472
|
+
/** Embedder cost for semantic recall/remember. Mirrored at the top-level
|
|
473
|
+
* `cost` on the event so cost rails (`trackExecution`) pick it up. */
|
|
474
|
+
cost?: number;
|
|
475
|
+
/** Embedder usage detail (tokens / model). */
|
|
476
|
+
usage?: {
|
|
477
|
+
tokens?: number;
|
|
478
|
+
cost?: number;
|
|
479
|
+
model?: string;
|
|
480
|
+
};
|
|
481
|
+
/** True when this memory op called the semantic recall path (vs. key-only). */
|
|
482
|
+
embed?: boolean;
|
|
483
|
+
/** True when a key-only recall returned a value / semantic call path. */
|
|
484
|
+
semantic?: boolean;
|
|
485
|
+
/** True when a key-only recall returned a value. */
|
|
486
|
+
hit?: boolean;
|
|
487
|
+
/** Result count for `recall` (alias for `count` retained for back-compat). */
|
|
488
|
+
resultCount?: number;
|
|
489
|
+
/** Error message when the operation failed on the partial-failure path. */
|
|
490
|
+
error?: string;
|
|
491
|
+
};
|
|
492
|
+
/** Common fields carried by every `AxlEvent` regardless of `type`. */
|
|
493
|
+
type AxlEventBase = {
|
|
401
494
|
executionId: string;
|
|
495
|
+
/** Monotonic per-execution step counter, shared across nested asks via ALS. */
|
|
402
496
|
step: number;
|
|
497
|
+
/** Wall-clock ms. */
|
|
403
498
|
timestamp: number;
|
|
499
|
+
/** Workflow this event belongs to. Auto-stamped by `emitEvent` from
|
|
500
|
+
* `this.workflowName` when defined; callers may override. */
|
|
404
501
|
workflow?: string;
|
|
502
|
+
/** Optional emitting-agent name. Variants that always have an agent
|
|
503
|
+
* (e.g., `agent_call_start/end`) redeclare it as required so consumers
|
|
504
|
+
* narrowing on those variants get a non-optional `agent`. Single-point
|
|
505
|
+
* events that may or may not have an agent (`handoff`, gate events,
|
|
506
|
+
* `log`) keep the optional. */
|
|
405
507
|
agent?: string;
|
|
406
|
-
|
|
508
|
+
/** Optional model URI — set on agent-related events; ignored on others. */
|
|
407
509
|
model?: string;
|
|
510
|
+
/** Optional prompt version stamped from `agent._config.version`. */
|
|
511
|
+
promptVersion?: string;
|
|
512
|
+
/**
|
|
513
|
+
* Cost (USD) contributed by this event.
|
|
514
|
+
*
|
|
515
|
+
* Two DIFFERENT semantics ship on this field and consumers must know
|
|
516
|
+
* which they're reading:
|
|
517
|
+
*
|
|
518
|
+
* - **Leaf cost** (`agent_call_end`, `tool_call_end`, `memory_remember`,
|
|
519
|
+
* `memory_recall`): the authoritative charge for this single
|
|
520
|
+
* provider call / tool invocation / embedder call. Summing these
|
|
521
|
+
* across an execution gives the true spend.
|
|
522
|
+
*
|
|
523
|
+
* - **Per-ask rollup** (`ask_end`): the SUM of leaf costs emitted
|
|
524
|
+
* within this ask's frame, EXCLUDING nested asks (which roll up
|
|
525
|
+
* into their own `ask_end`). Spec/16 decision 10.
|
|
526
|
+
*
|
|
527
|
+
* **If you write your own accumulator, DO NOT do `total += event.cost`
|
|
528
|
+
* across all event types — you'll double-count every ask** because the
|
|
529
|
+
* leaves AND the rollup both carry `cost`. Use the exported
|
|
530
|
+
* `eventCostContribution(event)` helper from `@axlsdk/axl` instead:
|
|
531
|
+
* it encapsulates the "skip ask_end, finite-check, leaf-only" rule in
|
|
532
|
+
* one place so your accumulator stays in lockstep with the built-in
|
|
533
|
+
* `runtime.trackExecution`, `ExecutionInfo.totalCost`, and Studio's
|
|
534
|
+
* cost aggregator.
|
|
535
|
+
*
|
|
536
|
+
* Other variants may stamp `cost` optionally to flow into cost rails
|
|
537
|
+
* (e.g., memory ops mirror `usage.cost` here). Review UX-8.
|
|
538
|
+
*/
|
|
408
539
|
cost?: number;
|
|
540
|
+
/** Token counts. Required-by-narrowing on `agent_call_end`; optional on
|
|
541
|
+
* any event that wishes to mirror an aggregate. Scope is agent prompt /
|
|
542
|
+
* completion / reasoning tokens ONLY — embedder tokens live in
|
|
543
|
+
* `memory_*.data.usage.tokens` and are deliberately NOT summed into
|
|
544
|
+
* this field (different pricing, different model, different category). */
|
|
409
545
|
tokens?: {
|
|
410
546
|
input?: number;
|
|
411
547
|
output?: number;
|
|
412
548
|
reasoning?: number;
|
|
413
549
|
};
|
|
550
|
+
/** Duration in ms (set on `_end` variants and a few single-point events). */
|
|
414
551
|
duration?: number;
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
552
|
+
};
|
|
553
|
+
/** Fields on every event that originates within a specific `ctx.ask()` call. */
|
|
554
|
+
type AskScoped = {
|
|
555
|
+
askId: string;
|
|
556
|
+
/** Absent on root ask. */
|
|
557
|
+
parentAskId?: string;
|
|
558
|
+
/** 0 = root ask; +1 per nested ask. */
|
|
559
|
+
depth: number;
|
|
560
|
+
/** Emitting agent's name. Absent on `ask_start` (pre-resolution) and on
|
|
561
|
+
* events that predate agent resolution. */
|
|
562
|
+
agent?: string;
|
|
563
|
+
};
|
|
564
|
+
/**
|
|
565
|
+
* Meta carried alongside callback invocations (`onToken`, `onAgentStart`,
|
|
566
|
+
* `onToolCall`) so consumers can group/route by ask.
|
|
567
|
+
*
|
|
568
|
+
* Note: `agent` is **required** here (the callback is always invoked
|
|
569
|
+
* inside an ALS frame that has the agent name already resolved). On the
|
|
570
|
+
* event side (`AskScoped.agent`) the field is optional — events emitted
|
|
571
|
+
* before agent resolution (e.g., `ask_start` is fired before the
|
|
572
|
+
* dynamic agent selector runs) can land without it.
|
|
573
|
+
*/
|
|
574
|
+
type CallbackMeta = {
|
|
575
|
+
askId: string;
|
|
576
|
+
parentAskId?: string;
|
|
577
|
+
depth: number;
|
|
578
|
+
agent: string;
|
|
421
579
|
};
|
|
422
580
|
/**
|
|
423
|
-
*
|
|
424
|
-
* `
|
|
425
|
-
*
|
|
426
|
-
*
|
|
581
|
+
* Unified event union. Replaces the old `TraceEvent` (rich, persisted) and
|
|
582
|
+
* `StreamEvent` (lean, wire) by emitting a single rich event from one site
|
|
583
|
+
* and consuming the same shape on both rails.
|
|
584
|
+
*
|
|
585
|
+
* - Streaming consumers iterate `AxlStream` (an `AsyncIterable<AxlEvent>`).
|
|
586
|
+
* - Non-streaming consumers read `ExecutionInfo.events: AxlEvent[]`.
|
|
587
|
+
*
|
|
588
|
+
* Tree reconstruction: group ask-scoped events by `askId`, parent-link via
|
|
589
|
+
* `parentAskId`, sort by `step`, render by `depth`. Tokens (high-volume) and
|
|
590
|
+
* `partial_object` events are stream-only — never persisted to
|
|
591
|
+
* `ExecutionInfo.events`.
|
|
592
|
+
*
|
|
593
|
+
* When adding a new variant, extend `AXL_EVENT_TYPES` AND the emitter in
|
|
594
|
+
* `WorkflowContext.emitEvent()` together so the compiler catches drift; the
|
|
595
|
+
* exhaustiveness fixture in `__tests__/axl-event-exhaustive.test-d.ts` will
|
|
596
|
+
* also fail until the new case is handled.
|
|
427
597
|
*/
|
|
428
|
-
type
|
|
429
|
-
type: '
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
598
|
+
type AxlEvent = (AxlEventBase & {
|
|
599
|
+
type: 'workflow_start';
|
|
600
|
+
workflow: string;
|
|
601
|
+
data: WorkflowStartData;
|
|
602
|
+
}) | (AxlEventBase & {
|
|
603
|
+
type: 'workflow_end';
|
|
604
|
+
workflow: string;
|
|
605
|
+
data: WorkflowEndData;
|
|
606
|
+
}) | (AxlEventBase & AskScoped & {
|
|
607
|
+
type: 'ask_start';
|
|
608
|
+
prompt: string;
|
|
609
|
+
}) | (AxlEventBase & AskScoped & {
|
|
610
|
+
type: 'ask_end';
|
|
611
|
+
/** Discriminated outcome — narrow on `outcome.ok`. Ask-internal throws
|
|
612
|
+
* surface here, NOT via the workflow-level `error` event. */
|
|
613
|
+
outcome: {
|
|
614
|
+
ok: true;
|
|
615
|
+
result: unknown;
|
|
616
|
+
} | {
|
|
617
|
+
ok: false;
|
|
618
|
+
error: string;
|
|
619
|
+
};
|
|
620
|
+
/** Sum of `agent_call_end.cost` + `tool_call_end.cost` WITHIN THIS ASK,
|
|
621
|
+
* excluding nested asks. Nested asks contribute to their own ask_end. */
|
|
622
|
+
cost: number;
|
|
623
|
+
duration: number;
|
|
624
|
+
}) | (AxlEventBase & AskScoped & {
|
|
625
|
+
type: 'agent_call_start';
|
|
626
|
+
agent: string;
|
|
627
|
+
model: string;
|
|
628
|
+
/** 1-indexed tool-calling loop iteration within the ask. */
|
|
629
|
+
turn: number;
|
|
630
|
+
/** Request-side payload — prompt, system, params, messages, retry context. */
|
|
631
|
+
data: AgentCallStartData;
|
|
632
|
+
}) | (AxlEventBase & AskScoped & {
|
|
633
|
+
type: 'agent_call_end';
|
|
634
|
+
agent: string;
|
|
635
|
+
model: string;
|
|
636
|
+
/** Authoritative turn-level cost. */
|
|
637
|
+
cost: number;
|
|
638
|
+
duration: number;
|
|
639
|
+
tokens?: {
|
|
640
|
+
input?: number;
|
|
641
|
+
output?: number;
|
|
642
|
+
reasoning?: number;
|
|
643
|
+
};
|
|
644
|
+
/** Response-side payload — response text, thinking. */
|
|
645
|
+
data: AgentCallEndData;
|
|
646
|
+
}) | (AxlEventBase & AskScoped & {
|
|
647
|
+
type: 'token';
|
|
648
|
+
data: string;
|
|
649
|
+
}) | (AxlEventBase & AskScoped & {
|
|
650
|
+
type: 'tool_call_start';
|
|
433
651
|
tool: string;
|
|
434
|
-
|
|
435
|
-
|
|
652
|
+
callId: string;
|
|
653
|
+
data: ToolCallStartData;
|
|
654
|
+
}) | (AxlEventBase & AskScoped & {
|
|
655
|
+
type: 'tool_call_end';
|
|
656
|
+
tool: string;
|
|
657
|
+
callId: string;
|
|
658
|
+
duration: number;
|
|
659
|
+
cost?: number;
|
|
660
|
+
data: ToolCallData;
|
|
661
|
+
}) | (AxlEventBase & AskScoped & {
|
|
436
662
|
type: 'tool_approval';
|
|
437
663
|
tool: string;
|
|
438
|
-
|
|
439
|
-
|
|
664
|
+
callId?: string;
|
|
665
|
+
data: ToolApprovalData;
|
|
666
|
+
}) | (AxlEventBase & AskScoped & {
|
|
440
667
|
type: 'tool_denied';
|
|
441
668
|
tool: string;
|
|
442
|
-
|
|
443
|
-
|
|
669
|
+
callId?: string;
|
|
670
|
+
data?: ToolDeniedData;
|
|
671
|
+
}) | (AxlEventBase & AskScoped & {
|
|
672
|
+
type: 'delegate';
|
|
673
|
+
data: DelegateData;
|
|
674
|
+
}) | (AxlEventBase & {
|
|
675
|
+
type: 'handoff_start';
|
|
676
|
+
fromAskId: string;
|
|
677
|
+
toAskId: string;
|
|
678
|
+
sourceDepth: number;
|
|
679
|
+
targetDepth: number;
|
|
680
|
+
data: HandoffStartData;
|
|
681
|
+
}) | (AxlEventBase & {
|
|
682
|
+
type: 'handoff_return';
|
|
683
|
+
fromAskId: string;
|
|
684
|
+
toAskId: string;
|
|
685
|
+
sourceDepth: number;
|
|
686
|
+
targetDepth: number;
|
|
687
|
+
data: HandoffReturnData;
|
|
688
|
+
}) | (AxlEventBase & AskScoped & {
|
|
689
|
+
type: 'pipeline';
|
|
690
|
+
status: 'start';
|
|
691
|
+
stage: 'initial' | 'schema' | 'validate' | 'guardrail';
|
|
692
|
+
attempt: number;
|
|
693
|
+
maxAttempts: number;
|
|
694
|
+
}) | (AxlEventBase & AskScoped & {
|
|
695
|
+
type: 'pipeline';
|
|
696
|
+
status: 'failed';
|
|
697
|
+
stage: 'schema' | 'validate' | 'guardrail';
|
|
698
|
+
attempt: number;
|
|
699
|
+
maxAttempts: number;
|
|
700
|
+
/** Feedback message about to be injected into the conversation. */
|
|
701
|
+
reason: string;
|
|
702
|
+
}) | (AxlEventBase & AskScoped & {
|
|
703
|
+
type: 'pipeline';
|
|
704
|
+
status: 'committed';
|
|
705
|
+
/** The stage of the most recent `pipeline(start)` — `'initial'` when
|
|
706
|
+
* the ask committed on the first pass, otherwise the gate that last
|
|
707
|
+
* retried before commit. Lets consumers tell "committed cleanly"
|
|
708
|
+
* from "committed after a schema/validate/guardrail retry". */
|
|
709
|
+
stage: 'initial' | 'schema' | 'validate' | 'guardrail';
|
|
710
|
+
/** The final successful attempt. */
|
|
711
|
+
attempt: number;
|
|
712
|
+
maxAttempts: number;
|
|
713
|
+
}) | (AxlEventBase & AskScoped & {
|
|
714
|
+
type: 'partial_object';
|
|
715
|
+
attempt: number;
|
|
716
|
+
/** DeepPartial<T>; consumers cast at the render site. */
|
|
717
|
+
data: {
|
|
718
|
+
object: unknown;
|
|
719
|
+
};
|
|
720
|
+
}) | (AxlEventBase & AskScoped & {
|
|
721
|
+
type: 'verify';
|
|
722
|
+
data: VerifyData;
|
|
723
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
444
724
|
type: 'guardrail';
|
|
445
|
-
data?:
|
|
446
|
-
}) | (
|
|
725
|
+
data?: GuardrailData;
|
|
726
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
447
727
|
type: 'schema_check';
|
|
448
|
-
data?:
|
|
449
|
-
}) | (
|
|
728
|
+
data?: SchemaCheckData;
|
|
729
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
450
730
|
type: 'validate';
|
|
451
|
-
data?:
|
|
452
|
-
}) | (
|
|
453
|
-
type: 'delegate';
|
|
454
|
-
data?: DelegateTraceData;
|
|
455
|
-
}) | (TraceEventBase & {
|
|
456
|
-
type: 'handoff';
|
|
457
|
-
data?: HandoffTraceData;
|
|
458
|
-
}) | (TraceEventBase & {
|
|
459
|
-
type: 'verify';
|
|
460
|
-
data?: VerifyTraceData;
|
|
461
|
-
}) | (TraceEventBase & {
|
|
731
|
+
data?: ValidateData;
|
|
732
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
462
733
|
type: 'log';
|
|
463
|
-
data
|
|
464
|
-
}) | (
|
|
465
|
-
type: '
|
|
466
|
-
data
|
|
467
|
-
}) | (
|
|
468
|
-
type: '
|
|
469
|
-
data
|
|
734
|
+
data: unknown;
|
|
735
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
736
|
+
type: 'memory_remember' | 'memory_recall' | 'memory_forget';
|
|
737
|
+
data: MemoryEventData;
|
|
738
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
739
|
+
type: 'checkpoint_save' | 'checkpoint_replay';
|
|
740
|
+
data: CheckpointEventData;
|
|
741
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
742
|
+
type: 'await_human';
|
|
743
|
+
data: AwaitHumanData;
|
|
744
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
745
|
+
type: 'await_human_resolved';
|
|
746
|
+
data: AwaitHumanResolvedData;
|
|
747
|
+
}) | (AxlEventBase & {
|
|
748
|
+
type: 'done';
|
|
749
|
+
data: {
|
|
750
|
+
result: unknown;
|
|
751
|
+
};
|
|
752
|
+
}) | (AxlEventBase & Partial<AskScoped> & {
|
|
753
|
+
type: 'error';
|
|
754
|
+
data: {
|
|
755
|
+
message: string;
|
|
756
|
+
name?: string;
|
|
757
|
+
code?: string;
|
|
758
|
+
};
|
|
470
759
|
});
|
|
760
|
+
/** Convenience: extract the union member matching a given `type` discriminator. */
|
|
761
|
+
type AxlEventOf<T extends AxlEventType> = Extract<AxlEvent, {
|
|
762
|
+
type: T;
|
|
763
|
+
}>;
|
|
471
764
|
/** Result of a guardrail check. */
|
|
472
765
|
type GuardrailResult = {
|
|
473
766
|
block: boolean;
|
|
@@ -509,7 +802,10 @@ type ExecutionInfo = {
|
|
|
509
802
|
executionId: string;
|
|
510
803
|
workflow: string;
|
|
511
804
|
status: ExecutionStatus;
|
|
512
|
-
|
|
805
|
+
/** Full event timeline. Tokens and `partial_object` events are NOT persisted
|
|
806
|
+
* here (stream-only); aggregate `tokens: { input, output, reasoning? }`
|
|
807
|
+
* on `agent_call_end` is the persisted token representation. */
|
|
808
|
+
events: AxlEvent[];
|
|
513
809
|
totalCost: number;
|
|
514
810
|
startedAt: number;
|
|
515
811
|
completedAt?: number;
|
|
@@ -517,57 +813,26 @@ type ExecutionInfo = {
|
|
|
517
813
|
result?: unknown;
|
|
518
814
|
error?: string;
|
|
519
815
|
};
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
} | {
|
|
534
|
-
type: 'tool_approval';
|
|
535
|
-
name: string;
|
|
536
|
-
args: unknown;
|
|
537
|
-
approved: boolean;
|
|
538
|
-
reason?: string;
|
|
539
|
-
} | {
|
|
540
|
-
type: 'agent_start';
|
|
541
|
-
agent: string;
|
|
542
|
-
model?: string;
|
|
543
|
-
} | {
|
|
544
|
-
type: 'agent_end';
|
|
545
|
-
agent: string;
|
|
546
|
-
cost?: number;
|
|
547
|
-
duration?: number;
|
|
548
|
-
} | {
|
|
549
|
-
type: 'handoff';
|
|
550
|
-
source: string;
|
|
551
|
-
target: string;
|
|
552
|
-
mode?: 'oneway' | 'roundtrip';
|
|
553
|
-
} | {
|
|
554
|
-
type: 'step';
|
|
555
|
-
step: number;
|
|
556
|
-
data: unknown;
|
|
557
|
-
} | {
|
|
558
|
-
type: 'done';
|
|
559
|
-
data: unknown;
|
|
560
|
-
} | {
|
|
561
|
-
type: 'error';
|
|
562
|
-
message: string;
|
|
563
|
-
};
|
|
564
|
-
/** Record of an agent handoff event (persisted in session metadata). */
|
|
816
|
+
/** Record of an agent handoff event (persisted in session metadata).
|
|
817
|
+
*
|
|
818
|
+
* `duration` semantics:
|
|
819
|
+
* - `oneway`: target's full ask duration (start-to-completion).
|
|
820
|
+
* - `roundtrip`: full round-trip wall-clock (handoff_start → handoff_return),
|
|
821
|
+
* includes the time to push the result back into the source's
|
|
822
|
+
* conversation. Both measurements are populated by the
|
|
823
|
+
* runtime once the corresponding event fires; if the target
|
|
824
|
+
* never completes (workflow aborted mid-handoff), `duration`
|
|
825
|
+
* stays undefined.
|
|
826
|
+
*
|
|
827
|
+
* `toAskId` is the askId of the target frame — lets consumers correlate
|
|
828
|
+
* the record to the target's `ask_end` event in the trace stream. */
|
|
565
829
|
type HandoffRecord = {
|
|
566
830
|
source: string;
|
|
567
831
|
target: string;
|
|
568
832
|
mode: 'oneway' | 'roundtrip';
|
|
569
833
|
timestamp: number;
|
|
570
834
|
duration?: number;
|
|
835
|
+
toAskId?: string;
|
|
571
836
|
};
|
|
572
837
|
/** Information about a completed agent call, emitted via onAgentCallComplete. */
|
|
573
838
|
type AgentCallInfo = {
|
|
@@ -707,12 +972,8 @@ type ExecutionState = {
|
|
|
707
972
|
* Redis (production).
|
|
708
973
|
*/
|
|
709
974
|
interface StateStore {
|
|
710
|
-
saveCheckpoint(executionId: string,
|
|
711
|
-
getCheckpoint(executionId: string,
|
|
712
|
-
getLatestCheckpoint(executionId: string): Promise<{
|
|
713
|
-
step: number;
|
|
714
|
-
data: unknown;
|
|
715
|
-
} | null>;
|
|
975
|
+
saveCheckpoint(executionId: string, name: string, data: unknown): Promise<void>;
|
|
976
|
+
getCheckpoint(executionId: string, name: string): Promise<unknown | null>;
|
|
716
977
|
saveSession(sessionId: string, history: ChatMessage[]): Promise<void>;
|
|
717
978
|
getSession(sessionId: string): Promise<ChatMessage[]>;
|
|
718
979
|
deleteSession(sessionId: string): Promise<void>;
|
|
@@ -905,6 +1166,27 @@ type StateConfig = {
|
|
|
905
1166
|
sqlite?: {
|
|
906
1167
|
path: string;
|
|
907
1168
|
};
|
|
1169
|
+
/**
|
|
1170
|
+
* Maximum number of events retained in `ExecutionInfo.events` per
|
|
1171
|
+
* execution. Token and partial_object events are already excluded
|
|
1172
|
+
* from the array (high-volume, stream-only); this cap bounds the
|
|
1173
|
+
* remaining structural events (`agent_call_*`, `tool_call_*`, gate
|
|
1174
|
+
* events, pipeline, etc).
|
|
1175
|
+
*
|
|
1176
|
+
* Pathological workloads (e.g., 50 nested asks × 20-turn tool loops)
|
|
1177
|
+
* can otherwise accumulate tens of thousands of events totalling
|
|
1178
|
+
* hundreds of MB before the terminal `done` event fires. When the
|
|
1179
|
+
* cap is hit, further events are dropped from the array and a single
|
|
1180
|
+
* `log` event with `data.event === 'events_truncated'` is appended
|
|
1181
|
+
* recording the truncation. The trace channel (`runtime.on('trace')`)
|
|
1182
|
+
* and WS broadcast continue to receive every event — only the
|
|
1183
|
+
* in-memory `ExecutionInfo.events` array is bounded.
|
|
1184
|
+
*
|
|
1185
|
+
* Default: 50_000. Set to `Infinity` to disable the cap (legacy
|
|
1186
|
+
* behavior; only safe for short-lived executions). Must be a positive
|
|
1187
|
+
* integer or `Infinity`.
|
|
1188
|
+
*/
|
|
1189
|
+
maxEventsPerExecution?: number;
|
|
908
1190
|
};
|
|
909
1191
|
/** Global defaults */
|
|
910
1192
|
type DefaultsConfig = {
|
|
@@ -1200,13 +1482,17 @@ type WorkflowContextInit = {
|
|
|
1200
1482
|
config: AxlConfig;
|
|
1201
1483
|
providerRegistry: ProviderRegistry;
|
|
1202
1484
|
sessionHistory?: ChatMessage[];
|
|
1203
|
-
onTrace?: (event:
|
|
1204
|
-
|
|
1485
|
+
onTrace?: (event: AxlEvent) => void;
|
|
1486
|
+
/** Per-token streaming callback. `meta` carries `askId`/`parentAskId`/
|
|
1487
|
+
* `depth`/`agent` so consumers can route or filter (e.g., `meta.depth === 0`
|
|
1488
|
+
* for root-only chat UIs). */
|
|
1489
|
+
onToken?: (token: string, meta: CallbackMeta) => void;
|
|
1490
|
+
/** Pre-execution tool-call callback. `meta` carries the ask correlation. */
|
|
1205
1491
|
onToolCall?: (call: {
|
|
1206
1492
|
name: string;
|
|
1207
1493
|
args: unknown;
|
|
1208
1494
|
callId?: string;
|
|
1209
|
-
}) => void;
|
|
1495
|
+
}, meta: CallbackMeta) => void;
|
|
1210
1496
|
pendingDecisions?: Map<string, (d: HumanDecision) => void>;
|
|
1211
1497
|
budgetContext?: {
|
|
1212
1498
|
totalCost: number;
|
|
@@ -1233,13 +1519,26 @@ type WorkflowContextInit = {
|
|
|
1233
1519
|
onAgentStart?: (info: {
|
|
1234
1520
|
agent: string;
|
|
1235
1521
|
model: string;
|
|
1236
|
-
}) => void;
|
|
1522
|
+
}, meta: CallbackMeta) => void;
|
|
1237
1523
|
/** Callback fired after each ctx.ask() completes (once per ask invocation). */
|
|
1238
1524
|
onAgentCallComplete?: (call: AgentCallInfo) => void;
|
|
1239
|
-
/** Set by `createChildContext
|
|
1240
|
-
*
|
|
1241
|
-
*
|
|
1242
|
-
|
|
1525
|
+
/** Internal: shared auto-checkpoint counters. Set by `createChildContext`
|
|
1526
|
+
* so parent + child share the same number space, preventing checkpoint
|
|
1527
|
+
* store key collisions across nested `WorkflowContext` instances.
|
|
1528
|
+
*
|
|
1529
|
+
* - `byAgent` — per-agent counter for `ctx.ask` auto-checkpoints; each
|
|
1530
|
+
* agent's first ask gets `0`, second gets `1`, etc. Composed name:
|
|
1531
|
+
* `__auto/<agent>/ask/<n>`. Reads naturally as "orchestrator's first
|
|
1532
|
+
* ask, second ask…" instead of a global ordinal.
|
|
1533
|
+
* - `root` — counter for `ctx.spawn` / `race` / `parallel` / `map`
|
|
1534
|
+
* wrappers, which don't have an agent context (they wrap arbitrary
|
|
1535
|
+
* fn callbacks). Composed name: `__auto/<primitive>/<n>`.
|
|
1536
|
+
*
|
|
1537
|
+
* Root contexts default to a fresh `{ byAgent: new Map(), root: 0 }`. */
|
|
1538
|
+
autoCheckpointCounters?: {
|
|
1539
|
+
byAgent: Map<string, number>;
|
|
1540
|
+
root: number;
|
|
1541
|
+
};
|
|
1243
1542
|
};
|
|
1244
1543
|
/**
|
|
1245
1544
|
* The central coordination object for all Axl primitives.
|
|
@@ -1264,8 +1563,34 @@ declare class WorkflowContext<TInput = unknown> {
|
|
|
1264
1563
|
private pendingDecisions?;
|
|
1265
1564
|
private budgetContext?;
|
|
1266
1565
|
private stateStore?;
|
|
1267
|
-
|
|
1268
|
-
|
|
1566
|
+
/** Root step counter for this execution. Inherited by every ctx.ask()
|
|
1567
|
+
* frame so all events from this WorkflowContext (root + nested) share
|
|
1568
|
+
* a single monotonic counter, even when concurrent branch primitives
|
|
1569
|
+
* fire asks before any single parent ask exists. Per-instance so
|
|
1570
|
+
* separate executions don't cross-talk. */
|
|
1571
|
+
private stepRefRoot;
|
|
1572
|
+
/**
|
|
1573
|
+
* Auto-checkpoint counters for internal `_checkpoint` callers. Held by
|
|
1574
|
+
* reference so child contexts created via `createChildContext` share
|
|
1575
|
+
* the same number space — otherwise a tool handler's nested `ctx.ask()`
|
|
1576
|
+
* (which auto-checkpoints) would collide with the parent's
|
|
1577
|
+
* auto-checkpoints in the state store. See `WorkflowContextInit
|
|
1578
|
+
* .autoCheckpointCounters` for the per-agent vs root split.
|
|
1579
|
+
* User-facing `ctx.checkpoint(name, fn)` requires an explicit name and
|
|
1580
|
+
* does NOT touch these counters — it goes straight to the store under
|
|
1581
|
+
* the caller-supplied key.
|
|
1582
|
+
*/
|
|
1583
|
+
private autoCheckpointCounters;
|
|
1584
|
+
/** Idempotency guards for `workflow_start` / `workflow_end` emission.
|
|
1585
|
+
* Both `runtime.execute()` and `runtime.stream()` have paths where a
|
|
1586
|
+
* post-emit side-effect (checkpoint deletion, state-store persistence)
|
|
1587
|
+
* can throw AFTER `_emitWorkflowEnd({status: 'completed'})` has already
|
|
1588
|
+
* fired — the outer catch would then fire `_emitWorkflowEnd({status:
|
|
1589
|
+
* 'failed'})` for a second time. These flags make both emitters
|
|
1590
|
+
* single-fire so consumers never see paired-then-conflicting terminal
|
|
1591
|
+
* events for one execution. */
|
|
1592
|
+
private _workflowStartEmitted;
|
|
1593
|
+
private _workflowEndEmitted;
|
|
1269
1594
|
private signal?;
|
|
1270
1595
|
private summaryCache?;
|
|
1271
1596
|
private workflowName?;
|
|
@@ -1277,27 +1602,46 @@ declare class WorkflowContext<TInput = unknown> {
|
|
|
1277
1602
|
private awaitHumanHandler?;
|
|
1278
1603
|
private onAgentStart?;
|
|
1279
1604
|
private onAgentCallComplete?;
|
|
1280
|
-
private parentToolCallId?;
|
|
1281
1605
|
constructor(init: WorkflowContextInit);
|
|
1282
1606
|
/**
|
|
1283
1607
|
* Create a child context for nested agent invocations (e.g., agent-as-tool).
|
|
1284
1608
|
* Shares: budget tracking, abort signals, trace emission, provider registry,
|
|
1285
1609
|
* state store, span manager, memory manager, MCP manager, config,
|
|
1286
|
-
* awaitHuman handler, pending decisions, tool overrides
|
|
1287
|
-
*
|
|
1610
|
+
* awaitHuman handler, pending decisions, tool overrides, AND the
|
|
1611
|
+
* streaming callbacks (onToken / onAgentStart / onToolCall).
|
|
1612
|
+
* Isolates: session history.
|
|
1613
|
+
*
|
|
1614
|
+
* Streaming callbacks now propagate into nested asks because every callback
|
|
1615
|
+
* invocation carries `meta.askId`/`meta.parentAskId`/`meta.depth` so
|
|
1616
|
+
* consumers that want root-only behavior can filter on `meta.depth === 0`
|
|
1617
|
+
* instead of relying on the runtime to drop nested events. This is the
|
|
1618
|
+
* "nested ask visibility" fix from spec §3.2.
|
|
1288
1619
|
*
|
|
1289
|
-
*
|
|
1290
|
-
*
|
|
1291
|
-
*
|
|
1292
|
-
*
|
|
1620
|
+
* The shared step counter lives in `askStorage` (ALS), so there is no
|
|
1621
|
+
* per-context counter to pass through anymore. Nested events are
|
|
1622
|
+
* correlated to their parent via `parentAskId` (set on every nested ask
|
|
1623
|
+
* by ALS frame allocation) — the deprecated `parentToolCallId` field
|
|
1624
|
+
* was removed in 0.16.0.
|
|
1293
1625
|
*/
|
|
1294
|
-
createChildContext(
|
|
1626
|
+
createChildContext(): WorkflowContext;
|
|
1295
1627
|
/**
|
|
1296
1628
|
* Resolve the current abort signal.
|
|
1297
1629
|
* Branch-scoped signals (from race/spawn/map/budget) in AsyncLocalStorage
|
|
1298
1630
|
* take priority over the instance-level signal.
|
|
1299
1631
|
*/
|
|
1300
1632
|
private get currentSignal();
|
|
1633
|
+
/**
|
|
1634
|
+
* Build a `CallbackMeta` for the current ask frame. Used at every
|
|
1635
|
+
* `onToken`/`onToolCall`/`onAgentStart` call site so consumers can
|
|
1636
|
+
* route or filter by ask correlation (`meta.depth === 0` for root
|
|
1637
|
+
* chat UIs, etc.).
|
|
1638
|
+
*
|
|
1639
|
+
* If the call is somehow outside an ask frame (e.g., a programmatic
|
|
1640
|
+
* `tool.run()` test harness), falls back to a synthetic meta keyed off
|
|
1641
|
+
* `executionId` so the type contract holds — consumers see `depth: 0`
|
|
1642
|
+
* and can ignore those events.
|
|
1643
|
+
*/
|
|
1644
|
+
private currentCallbackMeta;
|
|
1301
1645
|
ask<T = string>(agent: Agent, prompt: string, options?: AskOptions<T>): Promise<T>;
|
|
1302
1646
|
private executeAgentCall;
|
|
1303
1647
|
/**
|
|
@@ -1314,16 +1658,49 @@ declare class WorkflowContext<TInput = unknown> {
|
|
|
1314
1658
|
/**
|
|
1315
1659
|
* Execute a function with checkpoint-replay semantics.
|
|
1316
1660
|
*
|
|
1317
|
-
* On first execution, runs `fn()`, saves the result
|
|
1318
|
-
* On replay (resume after restart), returns the saved result
|
|
1319
|
-
* This prevents duplicate side effects (double API
|
|
1661
|
+
* On first execution, runs `fn()`, saves the result under `name`, and
|
|
1662
|
+
* returns it. On replay (resume after restart), returns the saved result
|
|
1663
|
+
* without re-executing. This prevents duplicate side effects (double API
|
|
1664
|
+
* calls, double refunds, etc.).
|
|
1665
|
+
*
|
|
1666
|
+
* `name` must be a stable, caller-supplied identifier — the same call
|
|
1667
|
+
* site MUST pass the same name across runs of the same execution for
|
|
1668
|
+
* replay to work. Names are scoped to a single execution and must be
|
|
1669
|
+
* unique within it; reusing a name (e.g. inside a loop) gives
|
|
1670
|
+
* last-write-wins behavior. For loops, compose names from the iterator:
|
|
1671
|
+
* `ctx.checkpoint(`process-${id}`, fn)`.
|
|
1672
|
+
*
|
|
1673
|
+
* Names starting with `__auto/` are reserved for runtime-internal
|
|
1674
|
+
* auto-checkpointing of `ctx.ask` / spawn / race / parallel / map.
|
|
1320
1675
|
*/
|
|
1321
|
-
checkpoint<T>(fn: () => Promise<T>): Promise<T>;
|
|
1676
|
+
checkpoint<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
|
1322
1677
|
/**
|
|
1323
|
-
* Internal checkpoint implementation shared by both the public
|
|
1324
|
-
* and the automatic checkpointing in
|
|
1678
|
+
* Internal checkpoint implementation shared by both the public
|
|
1679
|
+
* `checkpoint(name, fn)` and the automatic checkpointing in
|
|
1680
|
+
* ask/spawn/race/parallel/map. Internal callers pass an `__auto/...`
|
|
1681
|
+
* name composed by `_autoCheckpointName`.
|
|
1682
|
+
*
|
|
1683
|
+
* `agent` is the originating agent for ask auto-checkpoints — passing
|
|
1684
|
+
* it explicitly stamps `event.agent` on the trace events even though
|
|
1685
|
+
* the emit fires before/after `fn()` (where the ask's ALS frame
|
|
1686
|
+
* either doesn't exist yet or has already torn down). Without this,
|
|
1687
|
+
* post-fn `checkpoint_save` rows show no agent in the trace UI.
|
|
1325
1688
|
*/
|
|
1326
1689
|
private _checkpoint;
|
|
1690
|
+
/** Generate a stable auto-checkpoint name for an internal primitive.
|
|
1691
|
+
*
|
|
1692
|
+
* For `ctx.ask`, the originating agent is known and gets a per-agent
|
|
1693
|
+
* counter — the resulting name reads as "orchestrator's first ask"
|
|
1694
|
+
* (`__auto/orchestrator-agent/ask/0`) instead of a global ordinal.
|
|
1695
|
+
* For `spawn` / `race` / `parallel` / `map`, no agent context is
|
|
1696
|
+
* available (those wrap arbitrary fn callbacks), so the name uses
|
|
1697
|
+
* the root counter: `__auto/spawn/0`, `__auto/race/3`, etc.
|
|
1698
|
+
*
|
|
1699
|
+
* All counters live on a shared ref passed through `createChildContext`,
|
|
1700
|
+
* so a tool handler's nested `ctx.ask()` (which runs in a child
|
|
1701
|
+
* WorkflowContext) can never collide with the parent's auto-names in
|
|
1702
|
+
* the state store. */
|
|
1703
|
+
private _autoCheckpointName;
|
|
1327
1704
|
spawn<T>(n: number, fn: (index: number) => Promise<T>, options?: SpawnOptions): Promise<Result<T>[]>;
|
|
1328
1705
|
private _spawnImpl;
|
|
1329
1706
|
vote<T>(results: Result<T>[], options: VoteOptions<T>): T | Promise<T>;
|
|
@@ -1377,9 +1754,16 @@ declare class WorkflowContext<TInput = unknown> {
|
|
|
1377
1754
|
* @internal
|
|
1378
1755
|
*/
|
|
1379
1756
|
private _accumulateBudgetCost;
|
|
1380
|
-
/** @internal */
|
|
1757
|
+
/** @internal — idempotent; no-ops on second+ call within one ctx. */
|
|
1381
1758
|
_emitWorkflowStart(input: unknown): void;
|
|
1382
|
-
/** @internal
|
|
1759
|
+
/** @internal — idempotent; no-ops on second+ call within one ctx.
|
|
1760
|
+
* Protects against the post-emit-side-effect double-fire (reviewer
|
|
1761
|
+
* bug B1): `runtime.execute()` / `runtime.stream()` emit
|
|
1762
|
+
* `workflow_end(completed)`, then call `deleteCheckpoints` /
|
|
1763
|
+
* `persistExecution`. If either throws, the outer catch would
|
|
1764
|
+
* otherwise fire a second `workflow_end(failed)` with conflicting
|
|
1765
|
+
* status. First-wins semantics: the completed event stands, the
|
|
1766
|
+
* inner side-effect failure propagates as a normal thrown error. */
|
|
1383
1767
|
_emitWorkflowEnd(info: {
|
|
1384
1768
|
status: 'completed' | 'failed';
|
|
1385
1769
|
duration: number;
|
|
@@ -1416,10 +1800,10 @@ declare class WorkflowContext<TInput = unknown> {
|
|
|
1416
1800
|
/**
|
|
1417
1801
|
* Internal emitter input — intentionally loose so call sites don't need to
|
|
1418
1802
|
* build a perfectly-narrowed discriminated-union member. The resulting
|
|
1419
|
-
* `
|
|
1803
|
+
* `AxlEvent` (exported type) remains strict, and TypeScript narrows it at
|
|
1420
1804
|
* consumer sites via the `type` discriminator.
|
|
1421
1805
|
*/
|
|
1422
|
-
private
|
|
1806
|
+
private emitEvent;
|
|
1423
1807
|
}
|
|
1424
1808
|
|
|
1425
1809
|
/** Retry policy for tool handlers */
|
|
@@ -1487,6 +1871,15 @@ type Workflow<TInput extends z.ZodType = z.ZodType, TOutput extends z.ZodType =
|
|
|
1487
1871
|
readonly outputSchema: TOutput | undefined;
|
|
1488
1872
|
readonly handler: (ctx: WorkflowContext<z.infer<TInput>>) => Promise<z.infer<TOutput>>;
|
|
1489
1873
|
};
|
|
1874
|
+
/**
|
|
1875
|
+
* Bivariant-parameter alias for `Workflow<any, any>`. Function parameters
|
|
1876
|
+
* are contravariant under strict types, so `Workflow<TInput=MsgType>` won't
|
|
1877
|
+
* satisfy a `Workflow<unknown, unknown>` slot. The standard escape hatch is
|
|
1878
|
+
* to widen to `any`. This alias gives that workaround a name so call sites
|
|
1879
|
+
* (e.g. `AxlRuntime.register`, `AxlTestRuntime.WorkflowLike.handler`) don't
|
|
1880
|
+
* each independently re-derive it.
|
|
1881
|
+
*/
|
|
1882
|
+
type AnyWorkflow = Workflow<any, any>;
|
|
1490
1883
|
/**
|
|
1491
1884
|
* Define a named workflow with Zod-validated input/output and an async handler.
|
|
1492
1885
|
* Register workflows with `AxlRuntime.register()` to execute them.
|
|
@@ -1498,37 +1891,112 @@ declare function workflow<TInput extends z.ZodType, TOutput extends z.ZodType =
|
|
|
1498
1891
|
/**
|
|
1499
1892
|
* A streamable workflow execution.
|
|
1500
1893
|
*
|
|
1501
|
-
* Extends Node's Readable and implements AsyncIterable<
|
|
1502
|
-
*
|
|
1894
|
+
* Extends Node's Readable and implements `AsyncIterable<AxlEvent>`. Supports
|
|
1895
|
+
* `.on()` events, `for-await-of`, the `.text` iterator (root-only tokens),
|
|
1896
|
+
* the `.lifecycle` iterator (structural events only), and `.pipe()`.
|
|
1897
|
+
*
|
|
1898
|
+
* The wire carries `AxlEvent` directly — there is no per-stream synthesized
|
|
1899
|
+
* shape. Consumers narrow on `event.type` and use `AskScoped` fields
|
|
1900
|
+
* (`askId`, `parentAskId`, `depth`) for routing/filtering.
|
|
1503
1901
|
*/
|
|
1504
1902
|
declare class AxlStream extends Readable {
|
|
1505
1903
|
private bus;
|
|
1506
|
-
|
|
1904
|
+
/**
|
|
1905
|
+
* Per-ask token buffers split into "in-progress" and "committed"
|
|
1906
|
+
* halves so `fullText` only includes tokens from attempts that
|
|
1907
|
+
* actually won. Scoping is per-`askId` so concurrent root-level asks
|
|
1908
|
+
* (`ctx.parallel`, `ctx.spawn`, `ctx.race`, `ctx.map`) don't
|
|
1909
|
+
* interleave each other's tokens — each branch's chunks stay
|
|
1910
|
+
* contiguous, and a `pipeline(failed)` on one branch only discards
|
|
1911
|
+
* THAT branch's in-progress buffer (previously: shared buffer caused
|
|
1912
|
+
* a failure on one branch to discard a peer's in-flight successful
|
|
1913
|
+
* tokens). Insertion order in the Maps reflects which ask emitted
|
|
1914
|
+
* its first token first, which is what `fullText` joins on.
|
|
1915
|
+
*
|
|
1916
|
+
* On `pipeline(status: 'committed')` for an ask, that ask's
|
|
1917
|
+
* in-progress entry flushes to its committed entry. On
|
|
1918
|
+
* `pipeline(status: 'failed')` or `ask_end({ok:false})` for an ask,
|
|
1919
|
+
* that ask's in-progress entry is discarded. Spec/16 §4.3.
|
|
1920
|
+
*
|
|
1921
|
+
* Tokens emitted outside any ask (synthesized test fixtures with no
|
|
1922
|
+
* `askId`) fall back to an empty-string sentinel key — preserves the
|
|
1923
|
+
* single-buffer behavior for that legacy case.
|
|
1924
|
+
*/
|
|
1925
|
+
private attemptByAsk;
|
|
1926
|
+
private committedByAsk;
|
|
1507
1927
|
private result;
|
|
1508
1928
|
private finished;
|
|
1509
1929
|
readonly promise: Promise<unknown>;
|
|
1510
1930
|
private eventQueue;
|
|
1511
1931
|
private waiters;
|
|
1512
1932
|
constructor();
|
|
1933
|
+
/** Wire-format event names callers can subscribe to via `.on(name, fn)`.
|
|
1934
|
+
* Derived from the canonical `AXL_EVENT_TYPES` tuple — adding a new
|
|
1935
|
+
* variant in `types.ts` automatically extends the subscribable set. */
|
|
1513
1936
|
private static readonly STREAM_EVENTS;
|
|
1514
1937
|
on(event: string, handler: (...args: unknown[]) => void): this;
|
|
1515
1938
|
off(event: string, handler: (...args: unknown[]) => void): this;
|
|
1516
1939
|
[Symbol.asyncIterator](): {
|
|
1517
|
-
next: () => Promise<IteratorResult<
|
|
1940
|
+
next: () => Promise<IteratorResult<AxlEvent>>;
|
|
1518
1941
|
[Symbol.asyncIterator](): /*elided*/ any;
|
|
1519
1942
|
[Symbol.asyncDispose](): Promise<void>;
|
|
1520
1943
|
};
|
|
1521
1944
|
get text(): AsyncIterable<string>;
|
|
1522
|
-
|
|
1945
|
+
/**
|
|
1946
|
+
* Iterator over `{ askId, text }` pairs — one emission per token chunk,
|
|
1947
|
+
* tagged with the ask frame that produced it. Complements `.text`
|
|
1948
|
+
* (root-only stream): consumers building a split-pane UI that shows
|
|
1949
|
+
* each sub-agent's output in its own lane can group by `askId`
|
|
1950
|
+
* without iterating the raw stream and hand-filtering on `event.type`.
|
|
1951
|
+
*
|
|
1952
|
+
* Nested and root tokens both flow through. The `agent` field names
|
|
1953
|
+
* the producing agent for UI labelling; it's undefined when the token
|
|
1954
|
+
* was emitted outside any ask (rare — synthesized test fixtures).
|
|
1955
|
+
*/
|
|
1956
|
+
get textByAsk(): AsyncIterable<{
|
|
1957
|
+
askId: string;
|
|
1958
|
+
agent?: string;
|
|
1959
|
+
text: string;
|
|
1960
|
+
}>;
|
|
1961
|
+
/**
|
|
1962
|
+
* Iterator over structural lifecycle events only — skips per-token chatter
|
|
1963
|
+
* and progressive partial_object emissions. Useful for waterfall UIs and
|
|
1964
|
+
* any consumer that wants the "what happened" timeline without per-chunk
|
|
1965
|
+
* noise. Renamed from `.steps` in the unified-event-model migration
|
|
1966
|
+
* because these are events, not pipeline steps.
|
|
1967
|
+
*/
|
|
1968
|
+
get lifecycle(): AsyncIterable<AxlEvent>;
|
|
1523
1969
|
pipe<T extends NodeJS.WritableStream>(destination: T, options?: {
|
|
1524
1970
|
end?: boolean;
|
|
1525
1971
|
}): T;
|
|
1526
|
-
/** Push
|
|
1527
|
-
_push(event:
|
|
1528
|
-
/**
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1972
|
+
/** Push an event onto the stream. Called by the runtime. */
|
|
1973
|
+
_push(event: AxlEvent): void;
|
|
1974
|
+
/**
|
|
1975
|
+
* Signal successful completion.
|
|
1976
|
+
*
|
|
1977
|
+
* `executionId` is required — the runtime must allocate it before
|
|
1978
|
+
* calling `stream()` so terminal events always carry a real id.
|
|
1979
|
+
* Previously the default-empty parameter surfaced blank executionIds
|
|
1980
|
+
* on error paths that threw before `execInfo` was assigned (review S4).
|
|
1981
|
+
*/
|
|
1982
|
+
_done(result: unknown, executionId: string): void;
|
|
1983
|
+
/** Signal an error. `executionId` is required for the same reason as
|
|
1984
|
+
* `_done`: terminal events must carry a real id even when the
|
|
1985
|
+
* failure happens before any real trace event fires (review S4). */
|
|
1986
|
+
_error(error: Error, executionId: string): void;
|
|
1987
|
+
/** Concatenated root-only text from committed attempts plus the
|
|
1988
|
+
* current in-flight attempt(s). Retried (gate-rejected) attempts
|
|
1989
|
+
* are excluded — see spec/16 §4.3. Reading mid-attempt returns the
|
|
1990
|
+
* in-progress text; reading after `pipeline(committed)` (which
|
|
1991
|
+
* fires before `done`) returns the canonical winning text.
|
|
1992
|
+
*
|
|
1993
|
+
* With concurrent root-level asks (`ctx.parallel`, `ctx.spawn`,
|
|
1994
|
+
* `ctx.race`, `ctx.map`), each branch's tokens are scoped per-`askId`
|
|
1995
|
+
* and emitted contiguously in the order each ask first started
|
|
1996
|
+
* emitting tokens. A `pipeline(failed)` or `ask_end({ok:false})` on
|
|
1997
|
+
* one branch only discards THAT branch's in-progress buffer —
|
|
1998
|
+
* sibling branches are unaffected. For UIs that want each sub-agent
|
|
1999
|
+
* in its own lane, prefer `textByAsk`. */
|
|
1532
2000
|
get fullText(): string;
|
|
1533
2001
|
}
|
|
1534
2002
|
|
|
@@ -1597,8 +2065,23 @@ type CreateContextOptions = {
|
|
|
1597
2065
|
signal?: AbortSignal;
|
|
1598
2066
|
/** Prior conversation history for multi-turn eval testing. */
|
|
1599
2067
|
sessionHistory?: ChatMessage[];
|
|
1600
|
-
/** Token streaming callback.
|
|
1601
|
-
|
|
2068
|
+
/** Token streaming callback. `meta` carries `askId`/`parentAskId`/`depth`/`agent`
|
|
2069
|
+
* so consumers can route or filter by ask correlation (e.g.,
|
|
2070
|
+
* `meta.depth === 0` for root-only chat UIs). */
|
|
2071
|
+
onToken?: (token: string, meta: CallbackMeta) => void;
|
|
2072
|
+
/** Fires when an agent invokes a tool. `meta` carries the same ask
|
|
2073
|
+
* correlation shape as `onToken`. */
|
|
2074
|
+
onToolCall?: (call: {
|
|
2075
|
+
name: string;
|
|
2076
|
+
args: unknown;
|
|
2077
|
+
callId?: string;
|
|
2078
|
+
}, meta: CallbackMeta) => void;
|
|
2079
|
+
/** Fires when an agent begins processing a turn. `meta` carries the
|
|
2080
|
+
* same ask correlation shape as `onToken`. */
|
|
2081
|
+
onAgentStart?: (info: {
|
|
2082
|
+
agent: string;
|
|
2083
|
+
model?: string;
|
|
2084
|
+
}, meta: CallbackMeta) => void;
|
|
1602
2085
|
/** Handler for tool approval requests. Called when an agent invokes a tool with requireApproval. */
|
|
1603
2086
|
awaitHumanHandler?: (options: AwaitHumanOptions) => Promise<HumanDecision>;
|
|
1604
2087
|
};
|
|
@@ -1626,6 +2109,10 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
1626
2109
|
private historicalExecutionsLoadPromise;
|
|
1627
2110
|
private evalHistory;
|
|
1628
2111
|
private evalHistoryLoadPromise;
|
|
2112
|
+
/** Resolved cap on `ExecutionInfo.events` per execution. Cached on
|
|
2113
|
+
* the instance so per-event onTrace handlers don't reach into
|
|
2114
|
+
* `this.config.state` on every emission. */
|
|
2115
|
+
private readonly maxEventsPerExecution;
|
|
1629
2116
|
constructor(config?: AxlConfig);
|
|
1630
2117
|
/**
|
|
1631
2118
|
* Whether `config.trace.redact` is enabled on this runtime. A narrow
|
|
@@ -1654,13 +2141,39 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
1654
2141
|
/** Get the MCP manager (if initialized). */
|
|
1655
2142
|
getMcpManager(): McpManager | undefined;
|
|
1656
2143
|
private createStateStore;
|
|
2144
|
+
/**
|
|
2145
|
+
* Run a workflow body inside an ALREADY-CONSTRUCTED `WorkflowContext`
|
|
2146
|
+
* with full lifecycle invariants: emit `workflow_start` → run handler
|
|
2147
|
+
* → parse output → emit `workflow_end` → delete checkpoints → persist.
|
|
2148
|
+
*
|
|
2149
|
+
* Shared between `execute()` and `stream()`. Centralizes the
|
|
2150
|
+
* "start iff end" pairing invariant so a future emit path
|
|
2151
|
+
* (e.g., `resumeExecution`) can inherit correct behavior by routing
|
|
2152
|
+
* through this helper. Also factors out the AbortError detection.
|
|
2153
|
+
*
|
|
2154
|
+
* Side effects on `execInfo`: sets `status` / `completedAt` /
|
|
2155
|
+
* `duration` / `result` (or `error`) before emitting `workflow_end`.
|
|
2156
|
+
* On throw: emits `workflow_end({status: 'failed', aborted?})` and
|
|
2157
|
+
* persists before rethrowing. `abortControllers.delete` runs in
|
|
2158
|
+
* `finally` regardless of outcome.
|
|
2159
|
+
*
|
|
2160
|
+
* Caller responsibilities:
|
|
2161
|
+
* - Allocate `executionId` and register it in `abortControllers` map
|
|
2162
|
+
* BEFORE calling this helper (so early-throw paths still have a
|
|
2163
|
+
* correlation id available for terminal events).
|
|
2164
|
+
* - Construct `execInfo` and `ctx` before calling.
|
|
2165
|
+
* - Pass `span` if the call site is inside a tracer span — this
|
|
2166
|
+
* helper sets `axl.workflow.cost` / `.duration` attributes on
|
|
2167
|
+
* success.
|
|
2168
|
+
*/
|
|
2169
|
+
private runWorkflowBody;
|
|
1657
2170
|
/**
|
|
1658
2171
|
* Persist a completed/failed execution to the state store (fire-and-forget)
|
|
1659
2172
|
* and move it from the active executions map to the historical cache.
|
|
1660
2173
|
*/
|
|
1661
2174
|
private persistExecution;
|
|
1662
2175
|
/** Register a workflow with the runtime. */
|
|
1663
|
-
register(workflow:
|
|
2176
|
+
register(workflow: AnyWorkflow): void;
|
|
1664
2177
|
/** Register standalone tools for Studio introspection and direct testing. */
|
|
1665
2178
|
registerTool(...tools: Tool<any, any>[]): void;
|
|
1666
2179
|
/** Register standalone agents for Studio playground and introspection. */
|
|
@@ -1716,7 +2229,7 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
1716
2229
|
* When `true`, populate `EvalItem.traces` on every item (success + failure
|
|
1717
2230
|
* paths). Forwards to `runEval({ captureTraces: true })`, which wraps each
|
|
1718
2231
|
* item's execution in `runtime.trackExecution({ captureTraces: true })`.
|
|
1719
|
-
* Verbose-mode `
|
|
2232
|
+
* Verbose-mode `agent_call_start.data.messages` snapshots are stripped from
|
|
1720
2233
|
* captured traces to keep memory bounded.
|
|
1721
2234
|
*/
|
|
1722
2235
|
captureTraces?: boolean;
|
|
@@ -1841,11 +2354,11 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
1841
2354
|
* need embedder token counts, subscribe to `runtime.on('trace', ...)` and
|
|
1842
2355
|
* read `data.usage.tokens` on `memory_remember` / `memory_recall` events.
|
|
1843
2356
|
*
|
|
1844
|
-
* Pass `{ captureTraces: true }` to also collect the raw `
|
|
2357
|
+
* Pass `{ captureTraces: true }` to also collect the raw `AxlEvent[]` observed
|
|
1845
2358
|
* during `fn()`. This is opt-in because it keeps every event in memory for the
|
|
1846
2359
|
* duration of the call — useful for eval per-item capture, debugging, and test
|
|
1847
2360
|
* assertions, but overhead grows with trace volume. When enabled, verbose-mode
|
|
1848
|
-
* `
|
|
2361
|
+
* `agent_call_start.data.messages` snapshots are omitted from captured events (still
|
|
1849
2362
|
* broadcast via onTrace) to keep memory bounded — callers who need the full
|
|
1850
2363
|
* verbose snapshot should subscribe to `runtime.on('trace', ...)` directly.
|
|
1851
2364
|
*
|
|
@@ -1856,7 +2369,7 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
1856
2369
|
}): Promise<{
|
|
1857
2370
|
result: T;
|
|
1858
2371
|
cost: number;
|
|
1859
|
-
traces?:
|
|
2372
|
+
traces?: AxlEvent[];
|
|
1860
2373
|
metadata: {
|
|
1861
2374
|
models: string[];
|
|
1862
2375
|
modelCallCounts?: Record<string, number>;
|
|
@@ -1891,8 +2404,8 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
1891
2404
|
* The emit('trace', event) call happens before this method is called, so
|
|
1892
2405
|
* programmatic subscribers always receive events regardless of trace config.
|
|
1893
2406
|
*/
|
|
1894
|
-
private
|
|
1895
|
-
private
|
|
2407
|
+
private outputAxlEvent;
|
|
2408
|
+
private logAxlEvent;
|
|
1896
2409
|
/**
|
|
1897
2410
|
* Append a handoff record to session metadata.
|
|
1898
2411
|
* Note: The read-modify-write is not atomic. Concurrent handoffs in the same
|
|
@@ -1901,8 +2414,177 @@ declare class AxlRuntime extends EventEmitter {
|
|
|
1901
2414
|
* cross-execution concurrency on the same session, which is unlikely.
|
|
1902
2415
|
*/
|
|
1903
2416
|
private appendHandoffRecord;
|
|
2417
|
+
/**
|
|
2418
|
+
* Patch `duration` on the handoff record matching `toAskId` (the target
|
|
2419
|
+
* frame's askId, which the runtime stamps on `handoff_start` and which
|
|
2420
|
+
* appears as `askId` on the target's `ask_end`).
|
|
2421
|
+
*
|
|
2422
|
+
* Called from two places per handoff:
|
|
2423
|
+
* - target's `ask_end` — first-write source for ONEWAY (no return event).
|
|
2424
|
+
* - `handoff_return` — overwrites for ROUNDTRIP with the round-trip
|
|
2425
|
+
* wall-clock figure (intentional: round-trip
|
|
2426
|
+
* duration is the more useful metric for the
|
|
2427
|
+
* source-paused-for-N-ms semantic).
|
|
2428
|
+
*
|
|
2429
|
+
* No-op if no record matches `toAskId` (e.g., stream subscribed mid-
|
|
2430
|
+
* handoff, or session metadata got wiped) — `duration` stays undefined,
|
|
2431
|
+
* which is the documented default for `HandoffRecord`.
|
|
2432
|
+
*/
|
|
2433
|
+
private updateHandoffDuration;
|
|
1904
2434
|
}
|
|
1905
2435
|
|
|
2436
|
+
/**
|
|
2437
|
+
* Shared helpers for working with `AxlEvent` streams.
|
|
2438
|
+
*
|
|
2439
|
+
* Consumers writing their own accumulators / reducers reach for these so
|
|
2440
|
+
* they don't have to re-derive spec invariants at every call site. The
|
|
2441
|
+
* core runtime, AxlTestRuntime, and Studio's cost reducer ALL use these —
|
|
2442
|
+
* keeping one source of truth for each invariant.
|
|
2443
|
+
*/
|
|
2444
|
+
|
|
2445
|
+
/**
|
|
2446
|
+
* Variants that directly carry an authoritative cost charge from the
|
|
2447
|
+
* provider (or embedder). `ask_end` is a per-ask ROLLUP of these leaves
|
|
2448
|
+
* — counting it alongside them would double-charge. Any future
|
|
2449
|
+
* cost-emitting variant MUST be added here AND NOT be a rollup.
|
|
2450
|
+
*
|
|
2451
|
+
* Treated as a `Set` for O(1) membership checks; exported `as const`
|
|
2452
|
+
* tuple so exhaustiveness fixtures can cross-check against
|
|
2453
|
+
* `AXL_EVENT_TYPES`.
|
|
2454
|
+
*/
|
|
2455
|
+
declare const COST_BEARING_LEAF_TYPES: readonly ["agent_call_end", "tool_call_end", "memory_remember", "memory_recall"];
|
|
2456
|
+
/**
|
|
2457
|
+
* Cost contribution of `event` to a running total.
|
|
2458
|
+
*
|
|
2459
|
+
* Returns the event's `cost` if it's a cost-bearing leaf; 0 otherwise.
|
|
2460
|
+
* The key invariant (spec/16 decision 10): `ask_end` events carry a
|
|
2461
|
+
* per-ask ROLLUP of leaf costs that already passed through consumer
|
|
2462
|
+
* accumulators, so they contribute 0 here to avoid double-counting.
|
|
2463
|
+
*
|
|
2464
|
+
* Use this at every place you'd write `total += event.cost`:
|
|
2465
|
+
*
|
|
2466
|
+
* for (const ev of info.events) total += eventCostContribution(ev);
|
|
2467
|
+
*
|
|
2468
|
+
* Consumers who want the authoritative per-ask rollup read
|
|
2469
|
+
* `ask_end.cost` directly — that field is populated whether or not
|
|
2470
|
+
* the leaf events were summed separately. Spec decision 10.
|
|
2471
|
+
*/
|
|
2472
|
+
declare function eventCostContribution(event: AxlEvent): number;
|
|
2473
|
+
/**
|
|
2474
|
+
* True when the event is a cost-bearing leaf (contributes directly to
|
|
2475
|
+
* totals) — used by internal emitters that need to know whether to
|
|
2476
|
+
* bump per-frame askCost rollups.
|
|
2477
|
+
*
|
|
2478
|
+
* Distinct from `eventCostContribution(e) > 0`: an `agent_call_end`
|
|
2479
|
+
* with `cost: 0` is still a leaf (produced by a free / cached turn),
|
|
2480
|
+
* just one that happens to contribute zero.
|
|
2481
|
+
*/
|
|
2482
|
+
declare function isCostBearingLeaf(event: AxlEvent): boolean;
|
|
2483
|
+
/**
|
|
2484
|
+
* True when the event originates from the root ask (`depth === 0`)
|
|
2485
|
+
* or has no ask correlation at all (workflow / done / error / log).
|
|
2486
|
+
*
|
|
2487
|
+
* Used by consumers that want "just the chat bubble" tokens or
|
|
2488
|
+
* "just the root ask" visuals. Nested-ask UIs drop this filter.
|
|
2489
|
+
*
|
|
2490
|
+
* `depth` is coerced through `?? 0` so events without the field
|
|
2491
|
+
* (out-of-ask lifecycle events, synthesized terminals) are treated
|
|
2492
|
+
* as root-level — they're never at depth ≥ 1.
|
|
2493
|
+
*/
|
|
2494
|
+
declare function isRootLevel(event: AxlEvent): boolean;
|
|
2495
|
+
|
|
2496
|
+
/**
|
|
2497
|
+
* Tolerant JSON parser for progressive structured-output streaming.
|
|
2498
|
+
*
|
|
2499
|
+
* Accepts partial / truncated JSON and returns the longest parseable prefix
|
|
2500
|
+
* as a structured value. Used by `ctx.ask({ schema })` while streaming so
|
|
2501
|
+
* consumers can render fields as they arrive — see `partial_object` AxlEvent.
|
|
2502
|
+
*
|
|
2503
|
+
* Scope (intentional minimum):
|
|
2504
|
+
* - Single-pass tokenizer + recursive-descent state machine.
|
|
2505
|
+
* - Only TRAILING truncation is tolerated. Mid-document malformed input
|
|
2506
|
+
* throws. (A consumer rendering progressive output sees one well-formed
|
|
2507
|
+
* subtree at a time, never bogus shape changes.)
|
|
2508
|
+
* - Supports objects, arrays, strings (with `\"` `\\` `\n` `\t` `\r` `\b`
|
|
2509
|
+
* `\f` `\/` `\uXXXX`), numbers (int/float/exp/negative), `true` /
|
|
2510
|
+
* `false` / `null`.
|
|
2511
|
+
* - Rejects: trailing commas, comments, unquoted keys.
|
|
2512
|
+
*
|
|
2513
|
+
* Truncation recovery:
|
|
2514
|
+
* - Unterminated string → return content parsed so far.
|
|
2515
|
+
* - Unterminated number → return valid prefix if it's a complete number;
|
|
2516
|
+
* drop the key/value if the prefix is malformed
|
|
2517
|
+
* (`-`, `1.`, `1e` → drop).
|
|
2518
|
+
* - Unclosed `[` → array with parsed elements.
|
|
2519
|
+
* - Unclosed `{` → object with parsed pairs. A key without a value
|
|
2520
|
+
* is dropped.
|
|
2521
|
+
* - `,` or `:` at EOF → ignored; the last complete value wins.
|
|
2522
|
+
*
|
|
2523
|
+
* Zero runtime deps. ~250 LOC.
|
|
2524
|
+
*/
|
|
2525
|
+
/** Parse a JSON-shaped string that may be truncated mid-stream. */
|
|
2526
|
+
declare function parsePartialJson(input: string): unknown;
|
|
2527
|
+
|
|
2528
|
+
/**
|
|
2529
|
+
* Per-variant redaction rules for `AxlEvent`.
|
|
2530
|
+
*
|
|
2531
|
+
* `config.trace.redact` is an observability-boundary filter that scrubs
|
|
2532
|
+
* user/LLM content from events before they reach consumers. Two layers
|
|
2533
|
+
* historically applied this filter independently:
|
|
2534
|
+
*
|
|
2535
|
+
* 1. Core: `WorkflowContext.emitEvent()` — emit-time scrub before `onTrace`.
|
|
2536
|
+
* 2. Studio: `redactStreamEvent()` — wire-boundary scrub before WS broadcast
|
|
2537
|
+
* and again at REST serialization (`redactExecutionInfo`).
|
|
2538
|
+
*
|
|
2539
|
+
* Both layers walked the same `AxlEvent` discriminated union, with separate
|
|
2540
|
+
* if/else and switch/case ladders. Adding a new variant required updating
|
|
2541
|
+
* both. They had drifted: WS missed `workflow_start`/`workflow_end`,
|
|
2542
|
+
* `log`, and the full `memory_*` walk that core applied. Defense-in-depth
|
|
2543
|
+
* also broke for runtimes that flipped `redact` after emitting events —
|
|
2544
|
+
* REST reads of stored `ExecutionInfo.events` would leak fields the WS
|
|
2545
|
+
* layer didn't know to scrub.
|
|
2546
|
+
*
|
|
2547
|
+
* `REDACTION_RULES` is the single source of truth: a `Record<AxlEventType,
|
|
2548
|
+
* RuleFor<...>>` so adding a new variant to `AXL_EVENT_TYPES` without a
|
|
2549
|
+
* corresponding entry is a typecheck error. Every rule is pure (no
|
|
2550
|
+
* mutation), so the same rule runs at emit time and at the wire boundary
|
|
2551
|
+
* with identical results.
|
|
2552
|
+
*
|
|
2553
|
+
* Numeric/structural fields (`cost`, `tokens`, `duration`, `askId`,
|
|
2554
|
+
* `parentAskId`, `depth`, `agent`, `executionId`, `step`, `timestamp`,
|
|
2555
|
+
* `callId`, `tool`, `model`, `workflow`) are NEVER scrubbed — they're
|
|
2556
|
+
* non-PII observability metadata and load-bearing for cost rails. Rules
|
|
2557
|
+
* preserve these by spreading `{ ...event }` and only overriding the
|
|
2558
|
+
* specific user-content field(s) for that variant.
|
|
2559
|
+
*/
|
|
2560
|
+
|
|
2561
|
+
declare const REDACTED = "[redacted]";
|
|
2562
|
+
/** Type-safe rule signature: takes a specific event variant, returns the same variant. */
|
|
2563
|
+
type RuleFor<T extends AxlEventType> = (event: AxlEventOf<T>) => AxlEventOf<T>;
|
|
2564
|
+
/**
|
|
2565
|
+
* Per-variant rule table. Adding a new variant to `AXL_EVENT_TYPES`
|
|
2566
|
+
* without a corresponding entry here is a typecheck error — the
|
|
2567
|
+
* `Record<AxlEventType, ...>` mapped type forces exhaustiveness.
|
|
2568
|
+
*
|
|
2569
|
+
* For variants that ship no user content today (`delegate`,
|
|
2570
|
+
* `handoff_return`, `checkpoint_*`), the rule is `passthrough` — but
|
|
2571
|
+
* that's still an explicit, reviewed decision rather than a
|
|
2572
|
+
* silent omission.
|
|
2573
|
+
*/
|
|
2574
|
+
declare const REDACTION_RULES: {
|
|
2575
|
+
[K in AxlEventType]: RuleFor<K>;
|
|
2576
|
+
};
|
|
2577
|
+
/**
|
|
2578
|
+
* Apply the redaction rule for `event.type` and return a new event with
|
|
2579
|
+
* user-content fields scrubbed. Pure: never mutates the input.
|
|
2580
|
+
*
|
|
2581
|
+
* Caller responsibility: only invoke when redaction is enabled. The rules
|
|
2582
|
+
* unconditionally scrub — they don't gate on `config.trace.redact`. Both
|
|
2583
|
+
* `WorkflowContext.emitEvent` (core) and `redactStreamEvent` (Studio)
|
|
2584
|
+
* check the flag before calling this function.
|
|
2585
|
+
*/
|
|
2586
|
+
declare function redactEvent(event: AxlEvent): AxlEvent;
|
|
2587
|
+
|
|
1906
2588
|
/** Base error class for all Axl errors */
|
|
1907
2589
|
declare class AxlError extends Error {
|
|
1908
2590
|
readonly code: string;
|
|
@@ -2160,12 +2842,8 @@ declare class MemoryStore implements StateStore {
|
|
|
2160
2842
|
private executionHistory;
|
|
2161
2843
|
private evalHistory;
|
|
2162
2844
|
constructor();
|
|
2163
|
-
saveCheckpoint(executionId: string,
|
|
2164
|
-
getCheckpoint(executionId: string,
|
|
2165
|
-
getLatestCheckpoint(executionId: string): Promise<{
|
|
2166
|
-
step: number;
|
|
2167
|
-
data: unknown;
|
|
2168
|
-
} | null>;
|
|
2845
|
+
saveCheckpoint(executionId: string, name: string, data: unknown): Promise<void>;
|
|
2846
|
+
getCheckpoint(executionId: string, name: string): Promise<unknown | null>;
|
|
2169
2847
|
saveSession(sessionId: string, history: ChatMessage[]): Promise<void>;
|
|
2170
2848
|
getSession(sessionId: string): Promise<ChatMessage[]>;
|
|
2171
2849
|
deleteSession(sessionId: string): Promise<void>;
|
|
@@ -2209,13 +2887,44 @@ declare class MemoryStore implements StateStore {
|
|
|
2209
2887
|
declare class SQLiteStore implements StateStore {
|
|
2210
2888
|
private db;
|
|
2211
2889
|
constructor(filePath: string);
|
|
2890
|
+
/**
|
|
2891
|
+
* Idempotent schema migration. Tracks version via `PRAGMA user_version`
|
|
2892
|
+
* so reopens skip applied steps. Wrapped in `BEGIN IMMEDIATE` so
|
|
2893
|
+
* concurrent `SQLiteStore` constructors serialize at the file level
|
|
2894
|
+
* and rollback on failure leaves the DB in a clean pre-migration
|
|
2895
|
+
* state.
|
|
2896
|
+
*
|
|
2897
|
+
* Migration history:
|
|
2898
|
+
* - v1 (spec/16): rename `execution_history.steps` → `events`. The
|
|
2899
|
+
* JS-level `ExecutionInfo.steps` was renamed to `.events` in PR 1
|
|
2900
|
+
* commit 1; this brings the on-disk schema in line.
|
|
2901
|
+
* - v2 (named checkpoints): rename `checkpoints.step` (INTEGER) →
|
|
2902
|
+
* `checkpoints.name` (TEXT). The runtime now requires
|
|
2903
|
+
* `ctx.checkpoint(name, fn)` so the per-context numeric counter is
|
|
2904
|
+
* gone — names are caller-supplied stable identifiers. Existing
|
|
2905
|
+
* numeric step values become stringified names ("0", "1", …) to
|
|
2906
|
+
* preserve replay continuity for in-flight executions across the
|
|
2907
|
+
* upgrade boundary.
|
|
2908
|
+
*
|
|
2909
|
+
* Applied BEFORE `initTables()` so the subsequent `CREATE TABLE
|
|
2910
|
+
* IF NOT EXISTS` runs against the post-migration column name. Fresh
|
|
2911
|
+
* installs just create the table with `events` directly and bump
|
|
2912
|
+
* `user_version` to the current version.
|
|
2913
|
+
*
|
|
2914
|
+
* TOCTOU guard (review B-3): the initial `user_version` read is
|
|
2915
|
+
* informational only — a concurrent process can complete the
|
|
2916
|
+
* migration between that read and our `BEGIN IMMEDIATE`. We
|
|
2917
|
+
* re-read `user_version` inside the transaction and short-circuit
|
|
2918
|
+
* if it's already at target, so the non-idempotent half (writes
|
|
2919
|
+
* that went beyond a simple ALTER) never double-applies under a
|
|
2920
|
+
* race. The current v0→v1 step IS idempotent via the column-
|
|
2921
|
+
* presence check, but future migrations may not be, and the
|
|
2922
|
+
* transactional re-read costs nothing.
|
|
2923
|
+
*/
|
|
2924
|
+
private migrate;
|
|
2212
2925
|
private initTables;
|
|
2213
|
-
saveCheckpoint(executionId: string,
|
|
2214
|
-
getCheckpoint(executionId: string,
|
|
2215
|
-
getLatestCheckpoint(executionId: string): Promise<{
|
|
2216
|
-
step: number;
|
|
2217
|
-
data: unknown;
|
|
2218
|
-
} | null>;
|
|
2926
|
+
saveCheckpoint(executionId: string, name: string, data: unknown): Promise<void>;
|
|
2927
|
+
getCheckpoint(executionId: string, name: string): Promise<unknown | null>;
|
|
2219
2928
|
saveSession(sessionId: string, history: ChatMessage[]): Promise<void>;
|
|
2220
2929
|
getSession(sessionId: string): Promise<ChatMessage[]>;
|
|
2221
2930
|
deleteSession(sessionId: string): Promise<void>;
|
|
@@ -2274,12 +2983,8 @@ declare class RedisStore implements StateStore {
|
|
|
2274
2983
|
private execHistorySetKey;
|
|
2275
2984
|
private evalHistoryKey;
|
|
2276
2985
|
private evalHistorySetKey;
|
|
2277
|
-
saveCheckpoint(executionId: string,
|
|
2278
|
-
getCheckpoint(executionId: string,
|
|
2279
|
-
getLatestCheckpoint(executionId: string): Promise<{
|
|
2280
|
-
step: number;
|
|
2281
|
-
data: unknown;
|
|
2282
|
-
} | null>;
|
|
2986
|
+
saveCheckpoint(executionId: string, name: string, data: unknown): Promise<void>;
|
|
2987
|
+
getCheckpoint(executionId: string, name: string): Promise<unknown | null>;
|
|
2283
2988
|
saveSession(sessionId: string, history: ChatMessage[]): Promise<void>;
|
|
2284
2989
|
getSession(sessionId: string): Promise<ChatMessage[]>;
|
|
2285
2990
|
deleteSession(sessionId: string): Promise<void>;
|
|
@@ -2369,4 +3074,4 @@ declare class NoopSpanManager implements SpanManager {
|
|
|
2369
3074
|
*/
|
|
2370
3075
|
declare function createSpanManager(config?: TelemetryConfig): Promise<SpanManager>;
|
|
2371
3076
|
|
|
2372
|
-
export { type Agent, type AgentCallInfo, type
|
|
3077
|
+
export { AXL_EVENT_TYPES, type Agent, type AgentCallEndData, type AgentCallInfo, type AgentCallParams, type AgentCallStartData, type AgentConfig, AnthropicProvider, type AnyWorkflow, type AskOptions, type AskScoped, type AwaitHumanData, type AwaitHumanOptions, type AwaitHumanResolvedData, type AxlConfig, AxlError, type AxlEvent, type AxlEventBase, type AxlEventOf, type AxlEventType, AxlRuntime, AxlStream, BudgetExceededError, type BudgetOptions, type BudgetResult, COST_BEARING_LEAF_TYPES, type CallbackMeta, type ChatMessage, type ChatOptions, type CheckpointEventData, type CreateContextOptions, type DelegateData, type DelegateOptions, type Effort, type EmbedResult, type EmbedUsage, type Embedder, type EvalHistoryEntry, type ExecuteOptions, type ExecutionInfo, type ExecutionState, GeminiProvider, type GuardrailBlockHandler, type GuardrailData, GuardrailError, type GuardrailResult, type GuardrailsConfig, type HandoffDescriptor, type HandoffRecord, type HandoffReturnData, type HandoffStartData, type HumanDecision, InMemoryVectorStore, type InputGuardrail, type MapOptions, MaxTurnsError, McpManager, type McpServer, type McpServerConfig, type McpToolDefinition, type McpToolResult, type MemoryConfig, type MemoryEventData, MemoryManager, MemoryStore, NoConsensus, NoopSpanManager, OpenAIEmbedder, OpenAIProvider, OpenAIResponsesProvider, type OutputGuardrail, type OutputValidator, type PendingDecision, type Provider, type ProviderAdapter, ProviderRegistry, type ProviderResponse, QuorumNotMet, REDACTED, REDACTION_RULES, type RaceOptions, type RecallOptions, type RecallResult, RedisStore, type RememberOptions, type RememberResult, type ResolvedThinkingOptions, type Result, type RetryPolicy, SQLiteStore, type SchemaCheckData, Session, type SessionOptions, type SpanHandle, type SpanManager, type SpawnOptions, SqliteVectorStore, type StateStore, type StreamChunk, type TelemetryConfig, TimeoutError, type Tool, type ToolApprovalData, type ToolCallData, type ToolCallMessage, type ToolCallStartData, type ToolChoice, type ToolConfig, ToolDenied, type ToolDeniedData, type ToolHooks, type ValidateData, type ValidateResult, ValidationError, type VectorEntry, type VectorResult, type VectorStore, type VerifyData, VerifyError, type VerifyOptions, type VerifyRetry, type VoteOptions, type Workflow, type WorkflowConfig, WorkflowContext, type WorkflowContextInit, type WorkflowEndData, type WorkflowStartData, agent, createSpanManager, defineConfig, eventCostContribution, extractJson, isCostBearingLeaf, isRootLevel, parsePartialJson, redactEvent, resolveThinkingOptions, tool, workflow, zodToJsonSchema };
|