blazen 0.5.3 → 0.5.4

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/blazen.workers.js CHANGED
@@ -81,6 +81,8 @@ export const AnthropicProvider = __napiModule.exports.AnthropicProvider
81
81
  export const JsAnthropicProvider = __napiModule.exports.JsAnthropicProvider
82
82
  export const ApiProtocol = __napiModule.exports.ApiProtocol
83
83
  export const JsApiProtocol = __napiModule.exports.JsApiProtocol
84
+ export const AssignmentContext = __napiModule.exports.AssignmentContext
85
+ export const JsAssignmentContext = __napiModule.exports.JsAssignmentContext
84
86
  export const AudioMusicProviderDefaults = __napiModule.exports.AudioMusicProviderDefaults
85
87
  export const JsAudioMusicProviderDefaults = __napiModule.exports.JsAudioMusicProviderDefaults
86
88
  export const AudioSpeechProviderDefaults = __napiModule.exports.AudioSpeechProviderDefaults
@@ -137,6 +139,12 @@ export const ContentStore = __napiModule.exports.ContentStore
137
139
  export const JsContentStore = __napiModule.exports.JsContentStore
138
140
  export const Context = __napiModule.exports.Context
139
141
  export const JsContext = __napiModule.exports.JsContext
142
+ export const ControlPlaneClient = __napiModule.exports.ControlPlaneClient
143
+ export const JsControlPlaneClient = __napiModule.exports.JsControlPlaneClient
144
+ export const ControlPlaneWorker = __napiModule.exports.ControlPlaneWorker
145
+ export const JsControlPlaneWorker = __napiModule.exports.JsControlPlaneWorker
146
+ export const ControlPlaneWorkerConfig = __napiModule.exports.ControlPlaneWorkerConfig
147
+ export const JsControlPlaneWorkerConfig = __napiModule.exports.JsControlPlaneWorkerConfig
140
148
  export const CustomProvider = __napiModule.exports.CustomProvider
141
149
  export const JsCustomProvider = __napiModule.exports.JsCustomProvider
142
150
  export const DeepSeekProvider = __napiModule.exports.DeepSeekProvider
@@ -422,6 +430,7 @@ export const initPrometheus = __napiModule.exports.initPrometheus
422
430
  export const internEventType = __napiModule.exports.internEventType
423
431
  export const JoinStrategy = __napiModule.exports.JoinStrategy
424
432
  export const JsJoinStrategy = __napiModule.exports.JsJoinStrategy
433
+ export const JsAdmissionModeTag = __napiModule.exports.JsAdmissionModeTag
425
434
  export const JsAuthMethod = __napiModule.exports.JsAuthMethod
426
435
  export const JsCacheStrategy = __napiModule.exports.JsCacheStrategy
427
436
  export const JsContentKind = __napiModule.exports.JsContentKind
@@ -429,6 +438,7 @@ export const JsDiffusionScheduler = __napiModule.exports.JsDiffusionScheduler
429
438
  export const JsFalLlmEndpointKind = __napiModule.exports.JsFalLlmEndpointKind
430
439
  export const JsJobStatus = __napiModule.exports.JsJobStatus
431
440
  export const JsRole = __napiModule.exports.JsRole
441
+ export const JsRunStatus = __napiModule.exports.JsRunStatus
432
442
  export const JsWhisperModel = __napiModule.exports.JsWhisperModel
433
443
  export const LlamaCppChatRole = __napiModule.exports.LlamaCppChatRole
434
444
  export const JsLlamaCppChatRole = __napiModule.exports.JsLlamaCppChatRole
package/error-classes.js CHANGED
@@ -23,6 +23,77 @@
23
23
 
24
24
  const PROVIDER_ERROR_SENTINEL = '__BLAZEN_PROVIDER_ERROR__'
25
25
 
26
+ // Sentinel + stash for caller-error preservation. The napi side
27
+ // formats `__BLAZEN_CALLER_ERROR__ {json} \n [CallerError] msg` when a
28
+ // user's tool handler threw. The JSON `ref` is a UUID that maps to an
29
+ // entry in `callerErrorStash` (populated by `wrapToolHandlerForCallerErrors`
30
+ // below); the stashed value is the ORIGINAL JS Error instance -- so
31
+ // `enrichError` can re-throw it verbatim, preserving `instanceof MyError`
32
+ // and all custom properties. See `crates/blazen-node/src/error.rs`
33
+ // CALLER_ERROR_SENTINEL.
34
+ const CALLER_ERROR_SENTINEL = '__BLAZEN_CALLER_ERROR__'
35
+
36
+ // Module-level Map keyed by UUID strings. Values are the original
37
+ // thrown JS Error instances. Each entry is added by
38
+ // `wrapToolHandlerForCallerErrors` and deleted in `enrichError` after
39
+ // re-throw (or fall-back). Entries that escape the stash because the
40
+ // agent loop produced a non-CallerError outcome are cleaned up by the
41
+ // `runAgent` wrapper's `.finally(...)` (see `wrapRunAgentForCallerErrors`
42
+ // in index.js).
43
+ const callerErrorStash = new Map()
44
+
45
+ // Build a fresh UUID string suitable for Map keys. Uses the built-in
46
+ // crypto.randomUUID() when available (Node 14.17+); falls back to a
47
+ // simple time+random pattern otherwise.
48
+ function freshUuid() {
49
+ try {
50
+ // eslint-disable-next-line global-require
51
+ const { randomUUID } = require('crypto')
52
+ if (typeof randomUUID === 'function') {
53
+ return randomUUID()
54
+ }
55
+ } catch {
56
+ // Fall through.
57
+ }
58
+ return `caller-${Date.now()}-${Math.random().toString(36).slice(2)}`
59
+ }
60
+
61
+ // Wrap a user-supplied tool handler so any thrown / rejected error is
62
+ // captured as an envelope `{__blazenOk: false, errorRef: uuid, errorName,
63
+ // errorMessage}` and stashed for `enrichError` to re-throw. Success
64
+ // returns become `{__blazenOk: true, value: <user's return>}`.
65
+ //
66
+ // Returns a freshly-wrapped function (does NOT mutate the user's
67
+ // handler). The wrapped function is what the napi `runAgent` /
68
+ // `runAgentWithCallback` Rust side sees.
69
+ //
70
+ // The wrapped function returns a Promise (always) so the napi
71
+ // `Promise<serde_json::Value>` resolution path is uniform regardless of
72
+ // whether the user's handler is sync or async.
73
+ function wrapToolHandlerForCallerErrors(handler) {
74
+ if (typeof handler !== 'function') {
75
+ return handler
76
+ }
77
+ return async function blazenEnvelopeToolHandler(...args) {
78
+ try {
79
+ const value = await handler.apply(this, args)
80
+ return { __blazenOk: true, value }
81
+ } catch (error) {
82
+ const ref = freshUuid()
83
+ callerErrorStash.set(ref, error)
84
+ const errorName =
85
+ (error && typeof error === 'object' && typeof error.name === 'string')
86
+ ? error.name
87
+ : undefined
88
+ const errorMessage =
89
+ (error && typeof error === 'object' && typeof error.message === 'string')
90
+ ? error.message
91
+ : String(error)
92
+ return { __blazenOk: false, errorRef: ref, errorName, errorMessage }
93
+ }
94
+ }
95
+ }
96
+
26
97
  // ---------------------------------------------------------------------------
27
98
  // Class hierarchy
28
99
  // ---------------------------------------------------------------------------
@@ -702,6 +773,41 @@ function enrichError(err) {
702
773
  }
703
774
  }
704
775
 
776
+ // Detect and strip the caller-error sentinel. If the stash has the
777
+ // original error, re-throw it verbatim (preserves `instanceof MyError`
778
+ // and all custom properties). Otherwise fall back to a generic Error
779
+ // carrying the name+message from the sentinel JSON.
780
+ if (message.startsWith(CALLER_ERROR_SENTINEL)) {
781
+ const newlineIdx = message.indexOf('\n')
782
+ let payload = null
783
+ if (newlineIdx !== -1) {
784
+ const jsonPart = message
785
+ .slice(CALLER_ERROR_SENTINEL.length, newlineIdx)
786
+ .trim()
787
+ try {
788
+ payload = JSON.parse(jsonPart)
789
+ } catch {
790
+ payload = null
791
+ }
792
+ }
793
+ if (payload && typeof payload.ref === 'string') {
794
+ const original = callerErrorStash.get(payload.ref)
795
+ callerErrorStash.delete(payload.ref)
796
+ if (original !== undefined) {
797
+ return original
798
+ }
799
+ }
800
+ // Fallback: construct a generic Error with the sentinel-embedded
801
+ // name and message. instanceof won't match, but `.name` / `.message`
802
+ // still let consumers branch on the error type.
803
+ const fallback = new Error((payload && payload.message) || 'caller error')
804
+ if (payload && typeof payload.name === 'string') {
805
+ fallback.name = payload.name
806
+ }
807
+ fallback.stack = err.stack
808
+ return fallback
809
+ }
810
+
705
811
  const match = TAG_RE.exec(message)
706
812
  if (!match) {
707
813
  return err
@@ -852,4 +958,9 @@ module.exports = {
852
958
 
853
959
  // Helper
854
960
  enrichError,
961
+
962
+ // Caller-error preservation
963
+ CALLER_ERROR_SENTINEL,
964
+ wrapToolHandlerForCallerErrors,
965
+ callerErrorStash,
855
966
  }
package/index.d.ts CHANGED
@@ -93,6 +93,26 @@ export declare class ApiProtocol {
93
93
  }
94
94
  export type JsApiProtocol = ApiProtocol
95
95
 
96
+ /**
97
+ * Per-assignment context handed to the JS handler. Mirrors
98
+ * [`blazen_controlplane::AssignmentContext`].
99
+ *
100
+ * Carries the run id and a sink for emitting non-terminal events back
101
+ * to the control plane.
102
+ */
103
+ export declare class AssignmentContext {
104
+ /** Run identifier this context belongs to (UUID string). */
105
+ get runId(): string
106
+ /**
107
+ * Emit a non-terminal event back to the control plane.
108
+ *
109
+ * `data` must be a JSON-serializable JS value (object, array,
110
+ * string, number, boolean, null).
111
+ */
112
+ emitEvent(eventType: string, data: any): Promise<void>
113
+ }
114
+ export type JsAssignmentContext = AssignmentContext
115
+
96
116
  export declare class AudioMusicProviderDefaults {
97
117
  /** Construct role-specific defaults. */
98
118
  constructor(base?: BaseProviderDefaults | undefined | null, before?: BeforeRoleTsfn | undefined | null)
@@ -1439,6 +1459,131 @@ export declare class Context {
1439
1459
  }
1440
1460
  export type JsContext = Context
1441
1461
 
1462
+ /**
1463
+ * Orchestrator-side client for the control plane. Submit / cancel /
1464
+ * describe runs, list workers, drain workers, subscribe to events.
1465
+ *
1466
+ * ```typescript
1467
+ * const client = await ControlPlaneClient.connect("http://cp:7445");
1468
+ * const snap = await client.submitWorkflow({
1469
+ * workflowName: "summarize",
1470
+ * input: { url: "https://example.com" },
1471
+ * waitForWorker: true,
1472
+ * });
1473
+ * for await (const event of client.subscribeRunEvents(snap.runId)) {
1474
+ * console.log(event.eventType, event.data);
1475
+ * }
1476
+ * ```
1477
+ */
1478
+ export declare class ControlPlaneClient {
1479
+ /**
1480
+ * Open a connection to the control plane at `endpoint`. Pass
1481
+ * `{ mtls: { cert, key, ca } }` to use mTLS.
1482
+ */
1483
+ static connect(endpoint: string, opts?: JsClientConnectOptions | undefined | null): Promise<ControlPlaneClient>
1484
+ /** Submit a workflow run. */
1485
+ submitWorkflow(opts: JsSubmitWorkflowOptions): Promise<JsRunStateSnapshot>
1486
+ /** Cancel an in-flight workflow run. */
1487
+ cancelWorkflow(runId: string): Promise<JsRunStateSnapshot>
1488
+ /** Describe the current state of a workflow run. */
1489
+ describeWorkflow(runId: string): Promise<JsRunStateSnapshot>
1490
+ /** List currently-connected workers. */
1491
+ listWorkers(): Promise<Array<JsWorkerInfo>>
1492
+ /**
1493
+ * Drain a worker. Pass `immediate = true` to refuse new work right
1494
+ * away; otherwise let in-flight runs finish first.
1495
+ */
1496
+ drainWorker(nodeId: string, immediate: boolean): Promise<void>
1497
+ /**
1498
+ * Subscribe to events for a specific run. The returned stream is
1499
+ * a JS `AsyncIterableIterator<RunEvent>` (object with both `next`
1500
+ * and `[Symbol.asyncIterator]`).
1501
+ */
1502
+ subscribeRunEvents(runId: string): AsyncIterableIterator<RunEvent>
1503
+ /**
1504
+ * Subscribe to events across all runs, optionally filtered by tag
1505
+ * predicates. Returns an `AsyncIterableIterator<RunEvent>`.
1506
+ */
1507
+ subscribeAll(opts?: JsSubscribeAllOptions | undefined | null): AsyncIterableIterator<RunEvent>
1508
+ }
1509
+ export type JsControlPlaneClient = ControlPlaneClient
1510
+
1511
+ /**
1512
+ * Worker connection to a control-plane server. Construct via
1513
+ * [`Self::connect`] (which validates the endpoint URI), then call
1514
+ * [`Self::run`] with a JS handler to drive the worker forever.
1515
+ *
1516
+ * ```typescript
1517
+ * const config = new ControlPlaneWorkerConfig("http://cp:7445", "node-a")
1518
+ * .withCapability({ kind: "workflow:summarize", version: 1 });
1519
+ * const worker = ControlPlaneWorker.connect(config);
1520
+ * await worker.run(async (assignment, ctx) => {
1521
+ * await ctx.emitEvent("started", { runId: assignment.runId });
1522
+ * return { ok: true };
1523
+ * });
1524
+ * ```
1525
+ */
1526
+ export declare class ControlPlaneWorker {
1527
+ /**
1528
+ * Validate the configured endpoint URI and prepare a worker that's
1529
+ * ready to call [`Self::run`]. Does NOT open a network connection
1530
+ * yet — that happens on the first iteration of `run`.
1531
+ */
1532
+ static connect(config: ControlPlaneWorkerConfig): ControlPlaneWorker
1533
+ /**
1534
+ * Drive the worker forever, dispatching each assignment to the JS
1535
+ * handler. Resolves on graceful drain or [`Self::shutdown`].
1536
+ *
1537
+ * The JS handler is invoked with two arguments: the assignment
1538
+ * itself and a [`JsAssignmentContext`] that exposes the run id and
1539
+ * an `emitEvent` method. Its return value (JSON-serialized) becomes
1540
+ * the assignment output reported back to the server.
1541
+ */
1542
+ run(handler: (assignment: Assignment, ctx: AssignmentContext) => Promise<unknown>): Promise<void>
1543
+ /**
1544
+ * Signal the worker to stop. Idempotent.
1545
+ *
1546
+ * This drops the cancellation token tied to [`Self::run`]; the
1547
+ * `run` future resolves at the next await point.
1548
+ */
1549
+ shutdown(): void
1550
+ }
1551
+ export type JsControlPlaneWorker = ControlPlaneWorker
1552
+
1553
+ /**
1554
+ * Fluent builder for a worker connection. Mirrors
1555
+ * [`blazen_controlplane::WorkerConfig`].
1556
+ *
1557
+ * ```typescript
1558
+ * const config = new ControlPlaneWorkerConfig("http://cp:7445", "node-a")
1559
+ * .withCapability({ kind: "workflow:summarize", version: 1 })
1560
+ * .withTag("region", "us-west")
1561
+ * .withAdmission({ type: "Fixed", maxInFlight: 4 });
1562
+ * ```
1563
+ */
1564
+ export declare class ControlPlaneWorkerConfig {
1565
+ /**
1566
+ * Build a config with the required `endpoint` URI and stable
1567
+ * `nodeId`. Defaults: no capabilities, no tags, `Fixed { maxInFlight: 1 }`,
1568
+ * 5s heartbeat, plaintext transport.
1569
+ */
1570
+ constructor(endpoint: string, nodeId: string)
1571
+ /** Append a capability advertised at handshake. */
1572
+ withCapability(cap: JsWorkerCapability): this
1573
+ /**
1574
+ * Insert a free-form `key=value` tag used by submission-time tag
1575
+ * predicates.
1576
+ */
1577
+ withTag(key: string, value: string): this
1578
+ /** Override the admission mode declared at handshake. */
1579
+ withAdmission(mode: JsAdmissionMode): this
1580
+ /** Override the heartbeat cadence (milliseconds). */
1581
+ withHeartbeatIntervalMs(ms: number): this
1582
+ /** Load a client identity + CA from PEM files and use them for mTLS. */
1583
+ withMtls(certPath: string, keyPath: string, caPath: string): this
1584
+ }
1585
+ export type JsControlPlaneWorkerConfig = ControlPlaneWorkerConfig
1586
+
1442
1587
  /**
1443
1588
  * A user-defined Blazen provider exposed to JavaScript.
1444
1589
  *
@@ -6543,6 +6688,35 @@ export interface JsAddEntry {
6543
6688
  metadata?: any
6544
6689
  }
6545
6690
 
6691
+ /**
6692
+ * JS-facing admission mode. The `type` field discriminates the variant:
6693
+ * `'Fixed'` requires `maxInFlight`, `'VramBudget'` requires `totalMb`,
6694
+ * and `'Reactive'` has no payload fields.
6695
+ */
6696
+ export interface JsAdmissionMode {
6697
+ /** Discriminator: `'Fixed'`, `'Reactive'`, or `'VramBudget'`. */
6698
+ type: JsAdmissionModeTag
6699
+ /** In-flight cap for `Fixed`. `None` for the other variants. */
6700
+ maxInFlight?: number
6701
+ /**
6702
+ * VRAM budget in megabytes for `VramBudget`. `None` for the other
6703
+ * variants.
6704
+ */
6705
+ totalMb?: bigint
6706
+ }
6707
+
6708
+ /**
6709
+ * JS-facing label that drives [`JsAdmissionMode::r#type`]. Carries the
6710
+ * same three variants as [`AdmissionMode`] but as a plain string union
6711
+ * rather than a tagged enum (napi-rs `#[napi(object)]` does not yet
6712
+ * support discriminated-union codegen for enums with associated data).
6713
+ */
6714
+ export declare const enum JsAdmissionModeTag {
6715
+ Fixed = 'Fixed',
6716
+ Reactive = 'Reactive',
6717
+ VramBudget = 'VramBudget'
6718
+ }
6719
+
6546
6720
  /** Options for configuring an agent run. */
6547
6721
  export interface JsAgentRunOptions {
6548
6722
  /**
@@ -6625,6 +6799,29 @@ export interface JsArtifact {
6625
6799
  customKind?: string
6626
6800
  }
6627
6801
 
6802
+ /**
6803
+ * Worker-facing view of an assignment dispatched by the control plane.
6804
+ * JSON input is surfaced as a [`Buffer`] so handlers can decide whether
6805
+ * to decode (single-shot) or stream the bytes onward.
6806
+ */
6807
+ export interface JsAssignment {
6808
+ /** Run identifier, rendered as a UUID string. */
6809
+ runId: string
6810
+ /** Symbolic workflow name. */
6811
+ workflowName: string
6812
+ /**
6813
+ * Optional workflow version. `None` lets the worker use whichever
6814
+ * version it has registered.
6815
+ */
6816
+ workflowVersion?: number
6817
+ /** JSON-encoded initial input as raw bytes. */
6818
+ inputJson: Buffer
6819
+ /** Optional deadline in milliseconds. `None` = no timeout. */
6820
+ deadlineMs?: bigint
6821
+ /** 1-indexed attempt counter. Incremented on re-dispatch. */
6822
+ attempt: number
6823
+ }
6824
+
6628
6825
  /** Audio content for multimodal messages. */
6629
6826
  export interface JsAudioContent {
6630
6827
  source: JsImageSource
@@ -6767,6 +6964,15 @@ export interface JsCitation {
6767
6964
  metadata: any
6768
6965
  }
6769
6966
 
6967
+ /**
6968
+ * Options bag passed to `ControlPlaneClient.connect`. The `mtls`
6969
+ * field, when present, swaps the connection into mTLS mode.
6970
+ */
6971
+ export interface JsClientConnectOptions {
6972
+ /** mTLS configuration. `None` = plaintext. */
6973
+ mtls?: JsMtlsOptions
6974
+ }
6975
+
6770
6976
  /** Options for a chat completion request. */
6771
6977
  export interface JsCompletionOptions {
6772
6978
  temperature?: number
@@ -6866,8 +7072,9 @@ export interface JsContentMetadata {
6866
7072
  /**
6867
7073
  * A single part in a multi-part message.
6868
7074
  *
6869
- * `partType` is one of `"text"`, `"image"`, `"audio"`, `"video"`. Set the
6870
- * matching field (`text`, `image`, `audio`, `video`) accordingly.
7075
+ * `partType` is one of `"text"`, `"image"`, `"audio"`, `"video"`, `"file"`.
7076
+ * Set the matching field (`text`, `image`, `audio`, `video`, `file`)
7077
+ * accordingly.
6871
7078
  */
6872
7079
  export interface JsContentPart {
6873
7080
  partType: string
@@ -6875,6 +7082,7 @@ export interface JsContentPart {
6875
7082
  image?: JsImageContent
6876
7083
  audio?: JsAudioContent
6877
7084
  video?: JsVideoContent
7085
+ file?: FileContent
6878
7086
  }
6879
7087
 
6880
7088
  /** Request to dereference a remote session ref. */
@@ -7331,6 +7539,19 @@ export interface JsModelStatus {
7331
7539
  pool: string
7332
7540
  }
7333
7541
 
7542
+ /**
7543
+ * PEM file paths for mTLS configuration. Used by
7544
+ * [`crate::controlplane::client::JsControlPlaneClient::connect`].
7545
+ */
7546
+ export interface JsMtlsOptions {
7547
+ /** Path to the client certificate PEM file. */
7548
+ cert: string
7549
+ /** Path to the client private-key PEM file. */
7550
+ key: string
7551
+ /** Path to the CA PEM file used to authenticate the server. */
7552
+ ca: string
7553
+ }
7554
+
7334
7555
  export interface JsMusicRequest {
7335
7556
  prompt: string
7336
7557
  durationSeconds?: number
@@ -7564,6 +7785,47 @@ export declare const enum JsRole {
7564
7785
  Tool = 'tool'
7565
7786
  }
7566
7787
 
7788
+ /** Event emitted during a run. Mirrors [`RunEvent`]. */
7789
+ export interface JsRunEvent {
7790
+ /** Run identifier, rendered as a UUID string. */
7791
+ runId: string
7792
+ /** Caller-supplied event type tag (e.g. `"step.completed"`). */
7793
+ eventType: string
7794
+ /** Free-form JSON payload. */
7795
+ data: any
7796
+ /** Wall-clock emission time in epoch milliseconds. */
7797
+ timestampMs: bigint
7798
+ }
7799
+
7800
+ /** Snapshot of a workflow run's state. Mirrors [`RunStateSnapshot`]. */
7801
+ export interface JsRunStateSnapshot {
7802
+ /** Run identifier, rendered as a UUID string. */
7803
+ runId: string
7804
+ /** Current status. */
7805
+ status: JsRunStatus
7806
+ /** Wall-clock submission time in epoch milliseconds. */
7807
+ startedAtMs: bigint
7808
+ /** Wall-clock completion time, `None` until terminal. */
7809
+ completedAtMs?: bigint
7810
+ /** `Some(node_id)` once the run has been routed to a worker. */
7811
+ assignedTo?: string
7812
+ /** Wall-clock of the most recent event for this run, if any. */
7813
+ lastEventAtMs?: bigint
7814
+ /** Terminal output JSON value if `status == 'Completed'`. */
7815
+ output?: any
7816
+ /** Error message if `status == 'Failed'`. */
7817
+ error?: string
7818
+ }
7819
+
7820
+ /** JS-facing run status. Mirrors [`RunStatus`]. */
7821
+ export declare const enum JsRunStatus {
7822
+ Pending = 'Pending',
7823
+ Running = 'Running',
7824
+ Completed = 'Completed',
7825
+ Failed = 'Failed',
7826
+ Cancelled = 'Cancelled'
7827
+ }
7828
+
7567
7829
  export interface JsSpeechRequest {
7568
7830
  text: string
7569
7831
  voice?: string
@@ -7642,6 +7904,42 @@ export interface JsStreamChunk {
7642
7904
  artifacts: Array<JsArtifact>
7643
7905
  }
7644
7906
 
7907
+ /**
7908
+ * Options bag for
7909
+ * [`crate::controlplane::client::JsControlPlaneClient::submit_workflow`].
7910
+ */
7911
+ export interface JsSubmitWorkflowOptions {
7912
+ /** Symbolic name of the workflow to run. */
7913
+ workflowName: string
7914
+ /** JSON-serializable initial input. */
7915
+ input: any
7916
+ /** Optional workflow version. `None` = latest. */
7917
+ workflowVersion?: number
7918
+ /**
7919
+ * Required tags. Each `key=value` (or `key=*`) is AND'd. `None`
7920
+ * means no tag predicate.
7921
+ */
7922
+ requiredTags?: Array<string>
7923
+ /** Optional dedupe key for at-most-one-run-per-window semantics. */
7924
+ idempotencyKey?: string
7925
+ /** Optional deadline in milliseconds from submission. */
7926
+ deadlineMs?: bigint
7927
+ /**
7928
+ * If `true`, queue the submission until a matching worker appears.
7929
+ * Defaults to `false`.
7930
+ */
7931
+ waitForWorker?: boolean
7932
+ }
7933
+
7934
+ /**
7935
+ * Options bag for
7936
+ * [`crate::controlplane::client::JsControlPlaneClient::subscribe_all`].
7937
+ */
7938
+ export interface JsSubscribeAllOptions {
7939
+ /** Tag predicates the events must match. */
7940
+ requiredTags?: Array<string>
7941
+ }
7942
+
7645
7943
  /** Request to invoke a sub-workflow on a remote peer. */
7646
7944
  export interface JsSubWorkflowRequest {
7647
7945
  /** Symbolic name of the workflow to invoke on the remote peer. */
@@ -7918,6 +8216,37 @@ export interface JsWhisperOptions {
7918
8216
  cacheDir?: string
7919
8217
  }
7920
8218
 
8219
+ /**
8220
+ * Capability advertised by a worker at handshake time. Mirrors
8221
+ * [`blazen_core::distributed::WorkerCapability`].
8222
+ */
8223
+ export interface JsWorkerCapability {
8224
+ /** Capability tag, e.g. `"workflow:summarize"`. */
8225
+ kind: string
8226
+ /**
8227
+ * Capability version. Workers and orchestrators only match when both
8228
+ * values agree.
8229
+ */
8230
+ version: number
8231
+ }
8232
+
8233
+ /**
8234
+ * Summary of a connected worker returned by
8235
+ * [`crate::controlplane::client::JsControlPlaneClient::list_workers`].
8236
+ */
8237
+ export interface JsWorkerInfo {
8238
+ /** Stable identifier of the worker. */
8239
+ nodeId: string
8240
+ /** Capabilities advertised by this worker at handshake. */
8241
+ capabilities: Array<JsWorkerCapability>
8242
+ /** Free-form `key=value` tags this worker advertised. */
8243
+ tags: Record<string, string>
8244
+ /** Last reported in-flight assignment count. */
8245
+ inFlight: number
8246
+ /** Wall-clock connection time in epoch milliseconds. */
8247
+ connectedAtMs: bigint
8248
+ }
8249
+
7921
8250
  /**
7922
8251
  * Plain-object payload accepted by `WorkflowCheckpoint.create()` for
7923
8252
  * constructing a checkpoint from JS.
@@ -7994,11 +8323,8 @@ export interface LlmPayload {
7994
8323
  * `kind: "provider_raw"`.
7995
8324
  */
7996
8325
  value?: any
7997
- /**
7998
- * Multimodal content parts (serialized `ContentPart[]`).
7999
- * Required for `kind: "parts"`.
8000
- */
8001
- parts?: any
8326
+ /** Multimodal content parts. Required for `kind: "parts"`. */
8327
+ parts?: Array<JsContentPart>
8002
8328
  /** Provider id string. Required for `kind: "provider_raw"`. */
8003
8329
  provider?: string
8004
8330
  }
@@ -9055,6 +9381,17 @@ export type ImageSource = JsImageSource
9055
9381
  export type ContentHandle = JsContentHandle
9056
9382
  export type ContentMetadata = JsContentMetadata
9057
9383
  export type ContentKind = JsContentKind
9384
+ export type WorkerCapability = JsWorkerCapability
9385
+ export type AdmissionMode = JsAdmissionMode
9386
+ export type Assignment = JsAssignment
9387
+ export type RunStatus = JsRunStatus
9388
+ export type RunStateSnapshot = JsRunStateSnapshot
9389
+ export type RunEvent = JsRunEvent
9390
+ export type WorkerInfo = JsWorkerInfo
9391
+ export type MtlsOptions = JsMtlsOptions
9392
+ export type ClientConnectOptions = JsClientConnectOptions
9393
+ export type SubmitWorkflowOptions = JsSubmitWorkflowOptions
9394
+ export type SubscribeAllOptions = JsSubscribeAllOptions
9058
9395
 
9059
9396
  // --- post-build: ContentBody / ContentHint helper types ---
9060
9397
  /**
package/index.js CHANGED
@@ -584,6 +584,8 @@ module.exports.AnthropicProvider = nativeBinding.AnthropicProvider
584
584
  module.exports.JsAnthropicProvider = nativeBinding.JsAnthropicProvider
585
585
  module.exports.ApiProtocol = nativeBinding.ApiProtocol
586
586
  module.exports.JsApiProtocol = nativeBinding.JsApiProtocol
587
+ module.exports.AssignmentContext = nativeBinding.AssignmentContext
588
+ module.exports.JsAssignmentContext = nativeBinding.JsAssignmentContext
587
589
  module.exports.AudioMusicProviderDefaults = nativeBinding.AudioMusicProviderDefaults
588
590
  module.exports.JsAudioMusicProviderDefaults = nativeBinding.JsAudioMusicProviderDefaults
589
591
  module.exports.AudioSpeechProviderDefaults = nativeBinding.AudioSpeechProviderDefaults
@@ -640,6 +642,12 @@ module.exports.ContentStore = nativeBinding.ContentStore
640
642
  module.exports.JsContentStore = nativeBinding.JsContentStore
641
643
  module.exports.Context = nativeBinding.Context
642
644
  module.exports.JsContext = nativeBinding.JsContext
645
+ module.exports.ControlPlaneClient = nativeBinding.ControlPlaneClient
646
+ module.exports.JsControlPlaneClient = nativeBinding.JsControlPlaneClient
647
+ module.exports.ControlPlaneWorker = nativeBinding.ControlPlaneWorker
648
+ module.exports.JsControlPlaneWorker = nativeBinding.JsControlPlaneWorker
649
+ module.exports.ControlPlaneWorkerConfig = nativeBinding.ControlPlaneWorkerConfig
650
+ module.exports.JsControlPlaneWorkerConfig = nativeBinding.JsControlPlaneWorkerConfig
643
651
  module.exports.CustomProvider = nativeBinding.CustomProvider
644
652
  module.exports.JsCustomProvider = nativeBinding.JsCustomProvider
645
653
  module.exports.DeepSeekProvider = nativeBinding.DeepSeekProvider
@@ -925,6 +933,7 @@ module.exports.initPrometheus = nativeBinding.initPrometheus
925
933
  module.exports.internEventType = nativeBinding.internEventType
926
934
  module.exports.JoinStrategy = nativeBinding.JoinStrategy
927
935
  module.exports.JsJoinStrategy = nativeBinding.JsJoinStrategy
936
+ module.exports.JsAdmissionModeTag = nativeBinding.JsAdmissionModeTag
928
937
  module.exports.JsAuthMethod = nativeBinding.JsAuthMethod
929
938
  module.exports.JsCacheStrategy = nativeBinding.JsCacheStrategy
930
939
  module.exports.JsContentKind = nativeBinding.JsContentKind
@@ -932,6 +941,7 @@ module.exports.JsDiffusionScheduler = nativeBinding.JsDiffusionScheduler
932
941
  module.exports.JsFalLlmEndpointKind = nativeBinding.JsFalLlmEndpointKind
933
942
  module.exports.JsJobStatus = nativeBinding.JsJobStatus
934
943
  module.exports.JsRole = nativeBinding.JsRole
944
+ module.exports.JsRunStatus = nativeBinding.JsRunStatus
935
945
  module.exports.JsWhisperModel = nativeBinding.JsWhisperModel
936
946
  module.exports.LlamaCppChatRole = nativeBinding.LlamaCppChatRole
937
947
  module.exports.JsLlamaCppChatRole = nativeBinding.JsLlamaCppChatRole
@@ -1032,6 +1042,67 @@ module.exports.videoInput = nativeBinding.videoInput
1032
1042
  }
1033
1043
  }
1034
1044
 
1045
+ // Specialised handling for the agent entrypoints. The user's
1046
+ // `toolHandler` argument must be wrapped to envelope-format thrown
1047
+ // errors (see `wrapToolHandlerForCallerErrors` in error-classes.js);
1048
+ // `enrichError` then re-throws the original instance after the agent
1049
+ // loop rejects with the `__BLAZEN_CALLER_ERROR__` sentinel. The
1050
+ // generic `wrap()` loop below is skipped for these via the
1051
+ // `__blazenCallerErrorWrapped` marker.
1052
+ const { wrapToolHandlerForCallerErrors, callerErrorStash } = errorClasses
1053
+ const AGENT_ENTRYPOINTS = ['runAgent', 'runAgentWithCallback']
1054
+ for (const fnName of AGENT_ENTRYPOINTS) {
1055
+ const orig = module.exports[fnName]
1056
+ if (typeof orig !== 'function') continue
1057
+ // toolHandler is positional arg index 3 for both runAgent and
1058
+ // runAgentWithCallback in the current TS signatures.
1059
+ const TOOL_HANDLER_ARG_INDEX = 3
1060
+ const wrapped = function blazenAgentEntrypoint(...args) {
1061
+ if (args.length > TOOL_HANDLER_ARG_INDEX) {
1062
+ const userHandler = args[TOOL_HANDLER_ARG_INDEX]
1063
+ if (typeof userHandler === 'function') {
1064
+ args[TOOL_HANDLER_ARG_INDEX] = wrapToolHandlerForCallerErrors(userHandler)
1065
+ }
1066
+ }
1067
+ // Track stash entries created during this run so we can
1068
+ // garbage-collect any that the agent loop swallowed (e.g.
1069
+ // succeeded after a handler threw, or the loop ended before the
1070
+ // napi side surfaced the caller error).
1071
+ const stashSnapshot = new Set(callerErrorStash.keys())
1072
+ const cleanupNewStash = () => {
1073
+ for (const key of callerErrorStash.keys()) {
1074
+ if (!stashSnapshot.has(key)) {
1075
+ callerErrorStash.delete(key)
1076
+ }
1077
+ }
1078
+ }
1079
+ try {
1080
+ const result = orig.apply(this, args)
1081
+ if (result && typeof result.then === 'function') {
1082
+ return result.then(
1083
+ (v) => {
1084
+ cleanupNewStash()
1085
+ return v
1086
+ },
1087
+ (e) => {
1088
+ const enriched = enrichError(e)
1089
+ cleanupNewStash()
1090
+ throw enriched
1091
+ },
1092
+ )
1093
+ }
1094
+ cleanupNewStash()
1095
+ return result
1096
+ } catch (e) {
1097
+ const enriched = enrichError(e)
1098
+ cleanupNewStash()
1099
+ throw enriched
1100
+ }
1101
+ }
1102
+ wrapped.__blazenCallerErrorWrapped = true
1103
+ module.exports[fnName] = wrapped
1104
+ }
1105
+
1035
1106
  // Wrap every top-level export. Distinguish functions (call wrap) from
1036
1107
  // constructors (call patchPrototype). Heuristic: a constructor has a
1037
1108
  // `prototype` object with own properties beyond `constructor`. When
@@ -1045,6 +1116,8 @@ module.exports.videoInput = nativeBinding.videoInput
1045
1116
  if (typeof orig !== 'function') continue
1046
1117
  // Skip the typed-error classes we are about to install.
1047
1118
  if (Object.prototype.hasOwnProperty.call(errorClasses, key)) continue
1119
+ // Skip the agent entrypoints we already specialised above.
1120
+ if (orig.__blazenCallerErrorWrapped) continue
1048
1121
  if (orig.prototype && typeof orig.prototype === 'object') {
1049
1122
  patchPrototype(orig)
1050
1123
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blazen",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
4
4
  "description": "Blazen - Event-driven AI workflow framework for Node.js/TypeScript",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -88,13 +88,13 @@
88
88
  "verbose": true
89
89
  },
90
90
  "optionalDependencies": {
91
- "@blazen-dev/blazen-linux-x64-gnu": "0.5.3",
92
- "@blazen-dev/blazen-linux-x64-musl": "0.5.3",
93
- "@blazen-dev/blazen-linux-arm64-gnu": "0.5.3",
94
- "@blazen-dev/blazen-linux-arm64-musl": "0.5.3",
95
- "@blazen-dev/blazen-darwin-arm64": "0.5.3",
96
- "@blazen-dev/blazen-win32-x64-msvc": "0.5.3",
97
- "@blazen-dev/blazen-wasm32-wasi": "0.5.3"
91
+ "@blazen-dev/blazen-linux-x64-gnu": "0.5.4",
92
+ "@blazen-dev/blazen-linux-x64-musl": "0.5.4",
93
+ "@blazen-dev/blazen-linux-arm64-gnu": "0.5.4",
94
+ "@blazen-dev/blazen-linux-arm64-musl": "0.5.4",
95
+ "@blazen-dev/blazen-darwin-arm64": "0.5.4",
96
+ "@blazen-dev/blazen-win32-x64-msvc": "0.5.4",
97
+ "@blazen-dev/blazen-wasm32-wasi": "0.5.4"
98
98
  },
99
99
  "scripts": {
100
100
  "build": "napi build --release --platform --features local-all,langfuse --js index.js && node scripts/post-build.mjs",