@boardwalk-labs/runner 0.2.12 → 0.2.14

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.
@@ -23,7 +23,7 @@ export interface DirectInferenceDeps {
23
23
  }
24
24
  /** One model turn, straight to the org's endpoint. Returns the ChatTurn + canonical model ref;
25
25
  * BYO carries no platform cost (costMicros stays 0 — BYO is never metered). */
26
- export declare function streamDirectTurn(deps: DirectInferenceDeps, entry: ByoInferenceProvider, req: DirectTurnRequest, onDelta: ((text: string) => void) | undefined,
26
+ export declare function streamDirectTurn(deps: DirectInferenceDeps, entry: ByoInferenceProvider, req: DirectTurnRequest, onDelta: ((text: string) => void) | undefined, onReasoningDelta: ((text: string) => void) | undefined,
27
27
  /** Register the resolved key with the CURRENT leaf's engine redactor, before the model call.
28
28
  * The key resolves mid-leaf (after the leaf's redactor snapshot is seeded), so without this a
29
29
  * provider that echoes the Authorization header in an error body would leak it into that
@@ -53,7 +53,7 @@ export function directProviderFor(registry, provider) {
53
53
  }
54
54
  /** One model turn, straight to the org's endpoint. Returns the ChatTurn + canonical model ref;
55
55
  * BYO carries no platform cost (costMicros stays 0 — BYO is never metered). */
56
- export async function streamDirectTurn(deps, entry, req, onDelta,
56
+ export async function streamDirectTurn(deps, entry, req, onDelta, onReasoningDelta,
57
57
  /** Register the resolved key with the CURRENT leaf's engine redactor, before the model call.
58
58
  * The key resolves mid-leaf (after the leaf's redactor snapshot is seeded), so without this a
59
59
  * provider that echoes the Authorization header in an error body would leak it into that
@@ -77,6 +77,7 @@ registerSecret) {
77
77
  const io = {
78
78
  ...(deps.fetchImpl !== undefined ? { fetchImpl: deps.fetchImpl } : {}),
79
79
  ...(onDelta !== undefined ? { onDelta } : {}),
80
+ ...(onReasoningDelta !== undefined ? { onReasoningDelta } : {}),
80
81
  };
81
82
  log.debug("direct_turn", { provider: entry.name, source: entry.source, model: req.model });
82
83
  const turn = entry.source === "anthropic"
@@ -218,7 +218,7 @@ export class EngineLeafExecutor {
218
218
  messages: req.messages,
219
219
  tools: req.tools,
220
220
  ...(req.reasoning !== undefined ? { reasoning: req.reasoning } : {}),
221
- }, providerIo.onDelta,
221
+ }, providerIo.onDelta, providerIo.onReasoningDelta,
222
222
  // Register the org's key with THIS leaf's redactor before the model call, so an error
223
223
  // body echoing it is scrubbed from the leaf's run events (not just the terminal error).
224
224
  (value) => redactor.add("byo-key", value));
@@ -237,6 +237,9 @@ export class EngineLeafExecutor {
237
237
  if (frame.kind === "delta") {
238
238
  providerIo.onDelta?.(frame.text);
239
239
  }
240
+ else if (frame.kind === "reasoning") {
241
+ providerIo.onReasoningDelta?.(frame.text);
242
+ }
240
243
  else if (frame.kind === "result") {
241
244
  // contextTokens (when the broker knows the served model's window) lets the engine's loop
242
245
  // size compaction against the real window instead of a conservative default.
@@ -331,6 +334,8 @@ export function toRunEventBody(body, identity) {
331
334
  return { kind: "text_delta", blockId: body.blockId, text: body.text };
332
335
  case "text_end":
333
336
  return { kind: "text_end", blockId: body.blockId };
337
+ case "reasoning_delta":
338
+ return { kind: "reasoning_delta", text: body.text };
334
339
  case "tool_call_start":
335
340
  return { kind: "tool_call_start", toolCallId: body.toolCallId, toolName: body.toolName };
336
341
  case "tool_call_input_complete":
@@ -36,6 +36,9 @@ export interface ProxyError {
36
36
  export type InferenceFrame = {
37
37
  kind: "delta";
38
38
  text: string;
39
+ } | {
40
+ kind: "reasoning";
41
+ text: string;
39
42
  } | {
40
43
  kind: "result";
41
44
  turn: ChatTurn;
@@ -58,6 +61,9 @@ export declare function serializeInferenceRequest(req: InferenceProxyRequest): s
58
61
  export declare function parseInferenceRequest(body: string): InferenceProxyRequest;
59
62
  /** Serialize one streamed text delta as a single NDJSON line (trailing "\n" included). */
60
63
  export declare function serializeDeltaFrame(text: string): string;
64
+ /** Serialize one streamed reasoning/thinking delta as a single NDJSON line. Separate from a `delta`
65
+ * frame so the worker routes it to the thinking trace, never into the assistant answer. */
66
+ export declare function serializeReasoningFrame(text: string): string;
61
67
  /** Serialize the single terminal turn result as one NDJSON line. `costMicros` is the turn's EXACT
62
68
  * upstream cost (the managed provider's per-request cost × 1e6) on the managed lane — 0 for BYO or when unavailable.
63
69
  * The worker feeds it to the budget guardrail so `max_usd` tracks real spend, not a token estimate. */
@@ -78,6 +78,11 @@ export function parseInferenceRequest(body) {
78
78
  export function serializeDeltaFrame(text) {
79
79
  return `${JSON.stringify({ t: "delta", text })}\n`;
80
80
  }
81
+ /** Serialize one streamed reasoning/thinking delta as a single NDJSON line. Separate from a `delta`
82
+ * frame so the worker routes it to the thinking trace, never into the assistant answer. */
83
+ export function serializeReasoningFrame(text) {
84
+ return `${JSON.stringify({ t: "reasoning", text })}\n`;
85
+ }
81
86
  /** Serialize the single terminal turn result as one NDJSON line. `costMicros` is the turn's EXACT
82
87
  * upstream cost (the managed provider's per-request cost × 1e6) on the managed lane — 0 for BYO or when unavailable.
83
88
  * The worker feeds it to the budget guardrail so `max_usd` tracks real spend, not a token estimate. */
@@ -110,6 +115,8 @@ export function parseInferenceFrame(line) {
110
115
  switch (rec.t) {
111
116
  case "delta":
112
117
  return { kind: "delta", text: typeof rec.text === "string" ? rec.text : "" };
118
+ case "reasoning":
119
+ return { kind: "reasoning", text: typeof rec.text === "string" ? rec.text : "" };
113
120
  case "result":
114
121
  return {
115
122
  kind: "result",
@@ -129,7 +136,11 @@ export function parseInferenceFrame(line) {
129
136
  case "ping":
130
137
  return { kind: "ping" };
131
138
  default:
132
- throw new Error(`Unknown inference frame kind: ${String(rec.t)}`);
139
+ // Lenient consumer: a well-formed frame whose kind this runner predates must not crash the
140
+ // stream — a newer broker may emit frames an older runner can't name (this is how `reasoning`
141
+ // itself reaches a fleet mid-rollout). Treat it as a no-op heartbeat, which the leaf loop
142
+ // already ignores. Only a genuinely malformed line (caught above) is fatal.
143
+ return { kind: "ping" };
133
144
  }
134
145
  }
135
146
  /** Narrow a parsed `result` frame's turn back to the engine's `ChatTurn`, failing closed on shape —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boardwalk-labs/runner",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "Boardwalk self-hosted runner: execute cloud-scheduled runs on your own machines. Currently: the canonical runner contract types.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -53,8 +53,8 @@
53
53
  "prebuild": "prebuildify --napi --strip -t 24.0.0"
54
54
  },
55
55
  "dependencies": {
56
- "@boardwalk-labs/engine": "0.2.10",
57
- "@boardwalk-labs/workflow": "0.2.2",
56
+ "@boardwalk-labs/engine": "0.2.12",
57
+ "@boardwalk-labs/workflow": "0.2.5",
58
58
  "@modelcontextprotocol/sdk": "^1.29.0",
59
59
  "node-gyp-build": "^4.8.4",
60
60
  "tar": "^7.5.16",