@axlsdk/axl 0.19.0 → 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 +31 -10
- package/dist/{chunk-JBLQKU6X.js → chunk-NVPA4RY6.js} +6 -4
- package/dist/chunk-NVPA4RY6.js.map +1 -0
- package/dist/index.cjs +2005 -791
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +518 -251
- package/dist/index.d.ts +518 -251
- package/dist/index.js +1988 -784
- 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
|
@@ -162,9 +162,23 @@ const calculator = tool({
|
|
|
162
162
|
// handler also accepts (input, ctx) for nested agent invocations — see below
|
|
163
163
|
retry: { attempts: 3, backoff: 'exponential' },
|
|
164
164
|
sensitive: false,
|
|
165
|
+
// Keep the complete handler result for the host while sending only this
|
|
166
|
+
// allowlisted projection back to the model:
|
|
167
|
+
toModelOutput: (result) => ({ answer: result.result }),
|
|
165
168
|
});
|
|
166
169
|
```
|
|
167
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 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).
|
|
181
|
+
|
|
168
182
|
Tool handlers receive a second parameter `ctx: WorkflowContext` (a child context), enabling the "agent-as-tool" composition pattern:
|
|
169
183
|
|
|
170
184
|
```typescript
|
|
@@ -396,6 +410,11 @@ const wf = workflow({
|
|
|
396
410
|
|
|
397
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.
|
|
398
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
|
+
|
|
399
418
|
### Context Primitives
|
|
400
419
|
|
|
401
420
|
All available on `ctx` inside workflow handlers. See the [API Reference](../../docs/api-reference.md) for complete option types, valid values, and defaults.
|
|
@@ -447,11 +466,10 @@ const decision = await ctx.awaitHuman({
|
|
|
447
466
|
prompt: 'Approve this action?',
|
|
448
467
|
});
|
|
449
468
|
|
|
450
|
-
//
|
|
451
|
-
// the
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
// 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.
|
|
455
473
|
const checkpointed = await ctx.checkpoint('expensive-op', async () => expensiveOperation());
|
|
456
474
|
```
|
|
457
475
|
|
|
@@ -605,7 +623,7 @@ const result = await ctx.ask(extractAgent, 'Extract user from this text', {
|
|
|
605
623
|
|
|
606
624
|
### State Stores
|
|
607
625
|
|
|
608
|
-
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.
|
|
609
627
|
|
|
610
628
|
**Memory** (default) — in-process, no persistence. Use for development and stateless workflows.
|
|
611
629
|
|
|
@@ -684,7 +702,7 @@ app.listen(3000);
|
|
|
684
702
|
|
|
685
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).
|
|
686
704
|
|
|
687
|
-
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.
|
|
688
706
|
|
|
689
707
|
See [docs/migration/state-store-durability.md](../../docs/migration/state-store-durability.md) for the full design.
|
|
690
708
|
|
|
@@ -699,11 +717,15 @@ runtime.on('execution_deleted', (e) => {
|
|
|
699
717
|
// e: { executionId, wasActive, hadPendingDecision, removed }
|
|
700
718
|
auditLog.write({ event: 'execution.deleted', ...e });
|
|
701
719
|
});
|
|
720
|
+
|
|
721
|
+
runtime.on('decision_cleanup_failed', (e) => {
|
|
722
|
+
opsAlerts.write({ event: 'approval.cleanup_failed', ...e });
|
|
723
|
+
});
|
|
702
724
|
```
|
|
703
725
|
|
|
704
|
-
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`.
|
|
705
727
|
|
|
706
|
-
`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.
|
|
707
729
|
|
|
708
730
|
### Session Options
|
|
709
731
|
|
|
@@ -743,7 +765,6 @@ import {
|
|
|
743
765
|
BudgetExceededError, // Budget limit exceeded
|
|
744
766
|
GuardrailError, // Guardrail blocked input or output
|
|
745
767
|
ValidationError, // Post-schema business rule validation failed after retries
|
|
746
|
-
ToolDenied, // Agent tried to call unauthorized tool
|
|
747
768
|
} from '@axlsdk/axl';
|
|
748
769
|
```
|
|
749
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":[]}
|