@axlsdk/axl 0.19.1 → 0.20.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 +27 -11
- package/dist/{chunk-JBLQKU6X.js → chunk-NVPA4RY6.js} +6 -4
- package/dist/chunk-NVPA4RY6.js.map +1 -0
- package/dist/index.cjs +1838 -857
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +510 -257
- package/dist/index.d.ts +510 -257
- package/dist/index.js +1752 -780
- package/dist/index.js.map +1 -1
- package/dist/span-manager-74ZCGELO.js +7 -0
- package/package.json +2 -2
- package/dist/chunk-JBLQKU6X.js.map +0 -1
- package/dist/span-manager-3IKXXUTZ.js +0 -7
- /package/dist/{span-manager-3IKXXUTZ.js.map → span-manager-74ZCGELO.js.map} +0 -0
package/README.md
CHANGED
|
@@ -168,7 +168,16 @@ const calculator = tool({
|
|
|
168
168
|
});
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
`toModelOutput` is an opt-in, synchronous mapper for successful agent-invoked local tools. Strings are sent verbatim; JSON-compatible values are strictly validated and serialized once. The full post-hook result stays on `tool_call_end.data.result` for host rendering (subject to `trace.redact`). `sensitive: true` and thrown handler/hook/mock failures skip projection, and projection errors
|
|
171
|
+
`toModelOutput` is an opt-in, synchronous mapper for successful agent-invoked local tools. Strings are sent verbatim; JSON-compatible values are strictly validated and serialized once. The full post-hook result stays on the succeeded `tool_call_end.data.outcome.result` for host rendering (subject to `trace.redact`). `sensitive: true` and thrown handler/hook/mock failures skip projection, and projection errors close the call as `failed` / `projection` with no raw fallback. Direct `tool.run()`/`_execute()`, MCP tools, and handoffs are unchanged. See the [API reference](../../docs/api-reference.md#model-facing-tool-output) for the exact type, validation, mock, and delivery contract.
|
|
172
|
+
|
|
173
|
+
A normally returned value succeeds even when it contains an `error` property,
|
|
174
|
+
and `hooks.after` runs after every normal handler return. Throw `ToolFailure`
|
|
175
|
+
when a known failure has an author-declared message that is safe for model
|
|
176
|
+
recovery; ordinary throws abort the ask without provider tool content. Live
|
|
177
|
+
events use schema v2: pre-start rejection is `tool_call_rejected`, while every
|
|
178
|
+
accepted call closes with a `succeeded`, `failed`, `denied`, or `cancelled`
|
|
179
|
+
outcome. See the
|
|
180
|
+
[migration guide](../../docs/migration/stream-first-observation.md).
|
|
172
181
|
|
|
173
182
|
Tool handlers receive a second parameter `ctx: WorkflowContext` (a child context), enabling the "agent-as-tool" composition pattern:
|
|
174
183
|
|
|
@@ -401,6 +410,11 @@ const wf = workflow({
|
|
|
401
410
|
|
|
402
411
|
Subscribe before the first `ctx.ask()` — the streaming code path inside `ctx.ask()` only activates when an observer is present at the time the ask starts. The bus auto-terminates on `workflow_end` / `error` (and on signal abort, for ad-hoc `runtime.createContext({ signal })` flows). Configure the iterator-queue cap and overflow policy via `runtime.execute(..., { events: { maxQueued, onOverflow } })` — defaults (`maxQueued: 10_000`, `onOverflow: 'drop-oldest-non-terminal'`) are a default-on safety net against slow consumers. See [`docs/observability.md`](../../docs/observability.md#observation-paths) for the full Observation paths comparison, [`docs/api-reference.md`](../../docs/api-reference.md#ctxevents) for the type table, and [`docs/migration/stream-first-observation.md`](../../docs/migration/stream-first-observation.md) for upgrade notes.
|
|
403
412
|
|
|
413
|
+
The former `onToken`, `onToolCall`, and `onAgentStart` options on
|
|
414
|
+
`runtime.createContext()` are removed. Use `ctx.events`, `runtime.stream()`, or
|
|
415
|
+
the runtime trace listener according to observation scope; untyped callers that
|
|
416
|
+
still pass a removed key receive a targeted migration error.
|
|
417
|
+
|
|
404
418
|
### Context Primitives
|
|
405
419
|
|
|
406
420
|
All available on `ctx` inside workflow handlers. See the [API Reference](../../docs/api-reference.md) for complete option types, valid values, and defaults.
|
|
@@ -452,11 +466,10 @@ const decision = await ctx.awaitHuman({
|
|
|
452
466
|
prompt: 'Approve this action?',
|
|
453
467
|
});
|
|
454
468
|
|
|
455
|
-
//
|
|
456
|
-
// the
|
|
457
|
-
//
|
|
458
|
-
//
|
|
459
|
-
// match across runs for replay to work.
|
|
469
|
+
// Scoped checkpoint — on first evaluation, executes and saves the result
|
|
470
|
+
// under the current execution ID. A later evaluation in that same explicit
|
|
471
|
+
// execution scope can replay it. Axl does not automatically restart or
|
|
472
|
+
// resume workflows across processes.
|
|
460
473
|
const checkpointed = await ctx.checkpoint('expensive-op', async () => expensiveOperation());
|
|
461
474
|
```
|
|
462
475
|
|
|
@@ -610,7 +623,7 @@ const result = await ctx.ask(extractAgent, 'Extract user from this text', {
|
|
|
610
623
|
|
|
611
624
|
### State Stores
|
|
612
625
|
|
|
613
|
-
Three built-in implementations
|
|
626
|
+
Three built-in implementations persist workflow checkpoints, pending `awaitHuman` requests, session history, memory entries, execution history, and eval history. Pending approval requests remain visible after process loss, but their workflow continuation is in-process only; use an application-owned durable command/idempotency protocol for cross-process approval. Cancellation compensation is serialized with public resolution. If compensation fails, the request stays visible and the runtime emits `decision_cleanup_failed` for operator retry or total deletion.
|
|
614
627
|
|
|
615
628
|
**Memory** (default) — in-process, no persistence. Use for development and stateless workflows.
|
|
616
629
|
|
|
@@ -689,7 +702,7 @@ app.listen(3000);
|
|
|
689
702
|
|
|
690
703
|
Excluded events (never flushed): `token`, `partial_object`, `string_delta` — reconstructable from `agent_call_end.data.response`. Scope: `runtime.execute()` and `runtime.stream()` only — `createContext()` flows are deliberately excluded (no terminal finalize path).
|
|
691
704
|
|
|
692
|
-
Synthesized recovered executions carry `status: 'failed'`, `error: 'process terminated (recovered from streaming buffer)'`, and `
|
|
705
|
+
Synthesized recovered executions carry `status: 'failed'`, `error: 'process terminated (recovered from streaming buffer)'`, and `observation: { complete: false, reason: 'process_interrupted' }`; `workflow` is `__axl/recovered` when no `workflow_start` was captured. Events are bounded by `state.maxEventsPerExecution`. SQLite does not implement streaming methods — the runtime warns and falls back to terminal mode.
|
|
693
706
|
|
|
694
707
|
See [docs/migration/state-store-durability.md](../../docs/migration/state-store-durability.md) for the full design.
|
|
695
708
|
|
|
@@ -704,11 +717,15 @@ runtime.on('execution_deleted', (e) => {
|
|
|
704
717
|
// e: { executionId, wasActive, hadPendingDecision, removed }
|
|
705
718
|
auditLog.write({ event: 'execution.deleted', ...e });
|
|
706
719
|
});
|
|
720
|
+
|
|
721
|
+
runtime.on('decision_cleanup_failed', (e) => {
|
|
722
|
+
opsAlerts.write({ event: 'approval.cleanup_failed', ...e });
|
|
723
|
+
});
|
|
707
724
|
```
|
|
708
725
|
|
|
709
|
-
If the execution is still running, the workflow is aborted (and a paused `ctx.awaitHuman()` correctly wakes with `AbortError` — fixed in 0.17.7). The resurrection guard ensures the workflow's eventual `workflow_end` doesn't re-create the row.
|
|
726
|
+
If the execution is still running, the workflow is aborted (and a paused `ctx.awaitHuman()` correctly wakes with `AbortError` — fixed in 0.17.7). The resurrection guard ensures the workflow's eventual `workflow_end` doesn't re-create the row. Deletion waits for any in-process approval resolution or cancellation compensation before the store sweep. Custom stores must make `resolveDecision` idempotent and implement the complete execution-scoped sweep in `deleteExecution`.
|
|
710
727
|
|
|
711
|
-
`ExecutionInfo.metadata` round-trips from `ExecuteOptions.metadata` (`userId`, `tenantId`, etc.) — queryable via `runtime.getExecutions().filter(...)`. Internal control-plane keys (`sessionHistory`, `sessionId
|
|
728
|
+
`ExecutionInfo.metadata` round-trips from `ExecuteOptions.metadata` (`userId`, `tenantId`, etc.) — queryable via `runtime.getExecutions().filter(...)`. Internal session control-plane keys (`sessionHistory`, `sessionId`) are stripped before persistence; they remain available via `ctx.metadata` for dynamic selectors.
|
|
712
729
|
|
|
713
730
|
### Session Options
|
|
714
731
|
|
|
@@ -748,7 +765,6 @@ import {
|
|
|
748
765
|
BudgetExceededError, // Budget limit exceeded
|
|
749
766
|
GuardrailError, // Guardrail blocked input or output
|
|
750
767
|
ValidationError, // Post-schema business rule validation failed after retries
|
|
751
|
-
ToolDenied, // Agent tried to call unauthorized tool
|
|
752
768
|
} from '@axlsdk/axl';
|
|
753
769
|
```
|
|
754
770
|
|
|
@@ -32,6 +32,7 @@ var OTelSpanManager = class _OTelSpanManager {
|
|
|
32
32
|
async withSpanAsync(name, attributes, fn) {
|
|
33
33
|
const otelApi = this.otelApi;
|
|
34
34
|
return this.tracer.startActiveSpan(name, { attributes }, async (otelSpan) => {
|
|
35
|
+
let statusWasSet = false;
|
|
35
36
|
const handle = {
|
|
36
37
|
setAttribute(key, value) {
|
|
37
38
|
otelSpan.setAttribute(key, value);
|
|
@@ -40,7 +41,8 @@ var OTelSpanManager = class _OTelSpanManager {
|
|
|
40
41
|
otelSpan.addEvent(eventName, attrs);
|
|
41
42
|
},
|
|
42
43
|
setStatus(code, message) {
|
|
43
|
-
|
|
44
|
+
statusWasSet = true;
|
|
45
|
+
const statusCode = code === "ok" ? otelApi.SpanStatusCode.OK : code === "error" ? otelApi.SpanStatusCode.ERROR : otelApi.SpanStatusCode.UNSET;
|
|
44
46
|
otelSpan.setStatus({ code: statusCode, message });
|
|
45
47
|
},
|
|
46
48
|
end() {
|
|
@@ -49,10 +51,10 @@ var OTelSpanManager = class _OTelSpanManager {
|
|
|
49
51
|
};
|
|
50
52
|
try {
|
|
51
53
|
const result = await fn(handle);
|
|
52
|
-
handle.setStatus("ok");
|
|
54
|
+
if (!statusWasSet) handle.setStatus("ok");
|
|
53
55
|
return result;
|
|
54
56
|
} catch (err) {
|
|
55
|
-
handle.setStatus("error"
|
|
57
|
+
handle.setStatus("error");
|
|
56
58
|
throw err;
|
|
57
59
|
} finally {
|
|
58
60
|
handle.end();
|
|
@@ -76,4 +78,4 @@ export {
|
|
|
76
78
|
__require,
|
|
77
79
|
OTelSpanManager
|
|
78
80
|
};
|
|
79
|
-
//# sourceMappingURL=chunk-
|
|
81
|
+
//# sourceMappingURL=chunk-NVPA4RY6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/telemetry/span-manager.ts"],"sourcesContent":["import type { SpanHandle, SpanManager, TelemetryConfig } from './types.js';\n\n/** Minimal interface for an OTel span. */\ninterface OTelSpan {\n setAttribute(key: string, value: string | number | boolean): void;\n addEvent(name: string, attributes?: Record<string, string | number | boolean>): void;\n setStatus(status: { code: unknown; message?: string }): void;\n end(): void;\n}\n\n/** Minimal interface for an OTel tracer. */\ninterface OTelTracer {\n startActiveSpan<T>(\n name: string,\n options: { attributes: Record<string, string | number | boolean> },\n fn: (span: OTelSpan) => Promise<T>,\n ): Promise<T>;\n}\n\n/** Minimal interface for an OTel tracer provider. */\ninterface OTelTracerProvider {\n getTracer(name: string): OTelTracer;\n shutdown?(): Promise<void>;\n}\n\n/** Minimal interface for the @opentelemetry/api module. */\ninterface OTelApi {\n SpanStatusCode: { UNSET: unknown; OK: unknown; ERROR: unknown };\n trace: {\n getTracerProvider(): OTelTracerProvider;\n getActiveSpan?(): OTelSpan | undefined;\n };\n}\n\n/**\n * OpenTelemetry-backed SpanManager.\n * Dynamically imports @opentelemetry/api to avoid hard dependency.\n */\nexport class OTelSpanManager implements SpanManager {\n private tracer: OTelTracer;\n private otelApi: OTelApi;\n private tracerProvider: OTelTracerProvider;\n\n private constructor(otelApi: OTelApi, tracer: OTelTracer, tracerProvider: OTelTracerProvider) {\n this.otelApi = otelApi;\n this.tracer = tracer;\n this.tracerProvider = tracerProvider;\n }\n\n static async create(config: TelemetryConfig): Promise<OTelSpanManager> {\n let otelApi: OTelApi;\n try {\n otelApi = (await import('@opentelemetry/api')) as unknown as OTelApi;\n } catch {\n throw new Error(\n '@opentelemetry/api is required for telemetry. Install it with: npm install @opentelemetry/api',\n );\n }\n\n const tracerProvider =\n (config.tracerProvider as OTelTracerProvider | undefined) ??\n otelApi.trace.getTracerProvider();\n const serviceName = config.serviceName ?? 'axl';\n const tracer = tracerProvider.getTracer(serviceName);\n\n return new OTelSpanManager(otelApi, tracer, tracerProvider);\n }\n\n async withSpanAsync<T>(\n name: string,\n attributes: Record<string, string | number | boolean>,\n fn: (span: SpanHandle) => Promise<T>,\n ): Promise<T> {\n const otelApi = this.otelApi;\n\n return this.tracer.startActiveSpan(name, { attributes }, async (otelSpan: OTelSpan) => {\n let statusWasSet = false;\n const handle: SpanHandle = {\n setAttribute(key: string, value: string | number | boolean) {\n otelSpan.setAttribute(key, value);\n },\n addEvent(eventName: string, attrs?: Record<string, string | number | boolean>) {\n otelSpan.addEvent(eventName, attrs);\n },\n setStatus(code: 'unset' | 'ok' | 'error', message?: string) {\n statusWasSet = true;\n const statusCode =\n code === 'ok'\n ? otelApi.SpanStatusCode.OK\n : code === 'error'\n ? otelApi.SpanStatusCode.ERROR\n : otelApi.SpanStatusCode.UNSET;\n otelSpan.setStatus({ code: statusCode, message });\n },\n end() {\n otelSpan.end();\n },\n };\n\n try {\n const result = await fn(handle);\n if (!statusWasSet) handle.setStatus('ok');\n return result;\n } catch (err) {\n // Error text can contain provider, prompt, or sensitive tool data.\n // Keep exported status structural; host diagnostics remain in Axl's\n // own error and redacted event channels.\n handle.setStatus('error');\n throw err;\n } finally {\n handle.end();\n }\n });\n }\n\n addEventToActiveSpan(name: string, attributes?: Record<string, string | number | boolean>): void {\n const activeSpan = this.otelApi.trace.getActiveSpan?.();\n if (activeSpan) {\n activeSpan.addEvent(name, attributes);\n }\n }\n\n async shutdown(): Promise<void> {\n if (this.tracerProvider && typeof this.tracerProvider.shutdown === 'function') {\n await this.tracerProvider.shutdown();\n }\n }\n}\n"],"mappings":";;;;;;;;AAsCO,IAAM,kBAAN,MAAM,iBAAuC;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YAAY,SAAkB,QAAoB,gBAAoC;AAC5F,SAAK,UAAU;AACf,SAAK,SAAS;AACd,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAEA,aAAa,OAAO,QAAmD;AACrE,QAAI;AACJ,QAAI;AACF,gBAAW,MAAM,OAAO,oBAAoB;AAAA,IAC9C,QAAQ;AACN,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,UAAM,iBACH,OAAO,kBACR,QAAQ,MAAM,kBAAkB;AAClC,UAAM,cAAc,OAAO,eAAe;AAC1C,UAAM,SAAS,eAAe,UAAU,WAAW;AAEnD,WAAO,IAAI,iBAAgB,SAAS,QAAQ,cAAc;AAAA,EAC5D;AAAA,EAEA,MAAM,cACJ,MACA,YACA,IACY;AACZ,UAAM,UAAU,KAAK;AAErB,WAAO,KAAK,OAAO,gBAAgB,MAAM,EAAE,WAAW,GAAG,OAAO,aAAuB;AACrF,UAAI,eAAe;AACnB,YAAM,SAAqB;AAAA,QACzB,aAAa,KAAa,OAAkC;AAC1D,mBAAS,aAAa,KAAK,KAAK;AAAA,QAClC;AAAA,QACA,SAAS,WAAmB,OAAmD;AAC7E,mBAAS,SAAS,WAAW,KAAK;AAAA,QACpC;AAAA,QACA,UAAU,MAAgC,SAAkB;AAC1D,yBAAe;AACf,gBAAM,aACJ,SAAS,OACL,QAAQ,eAAe,KACvB,SAAS,UACP,QAAQ,eAAe,QACvB,QAAQ,eAAe;AAC/B,mBAAS,UAAU,EAAE,MAAM,YAAY,QAAQ,CAAC;AAAA,QAClD;AAAA,QACA,MAAM;AACJ,mBAAS,IAAI;AAAA,QACf;AAAA,MACF;AAEA,UAAI;AACF,cAAM,SAAS,MAAM,GAAG,MAAM;AAC9B,YAAI,CAAC,aAAc,QAAO,UAAU,IAAI;AACxC,eAAO;AAAA,MACT,SAAS,KAAK;AAIZ,eAAO,UAAU,OAAO;AACxB,cAAM;AAAA,MACR,UAAE;AACA,eAAO,IAAI;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,qBAAqB,MAAc,YAA8D;AAC/F,UAAM,aAAa,KAAK,QAAQ,MAAM,gBAAgB;AACtD,QAAI,YAAY;AACd,iBAAW,SAAS,MAAM,UAAU;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAC9B,QAAI,KAAK,kBAAkB,OAAO,KAAK,eAAe,aAAa,YAAY;AAC7E,YAAM,KAAK,eAAe,SAAS;AAAA,IACrC;AAAA,EACF;AACF;","names":[]}
|