@bastani/atomic 0.9.63-alpha.1 → 0.9.64-alpha.1

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.
@@ -72171,32 +72171,36 @@ function workflowChildFromDraft(draft) {
72171
72171
  var init_completed_catalog_stage_groups = () => {};
72172
72172
 
72173
72173
  // dist/builtin/workflows/src/engine/run-tool-control-registry.ts
72174
+ import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
72174
72175
  function createToolControlRegistry() {
72175
72176
  const byRun = new Map;
72176
72177
  const admissionByRun = new Map;
72177
72178
  const runControls = new Map;
72178
- const makeHandle = (registration) => ({
72179
- runId: registration.runId,
72180
- nodeId: registration.nodeId,
72181
- name: registration.name,
72182
- settled: registration.settled,
72183
- get aborted() {
72184
- return registration.controller.signal.aborted;
72185
- },
72186
- abort(scope) {
72187
- const existing = registration.controller.signal.reason;
72188
- if (registration.controller.signal.aborted && existing instanceof WorkflowToolAbortError)
72189
- return existing;
72190
- const reason = new WorkflowToolAbortError({
72191
- runId: registration.runId,
72192
- nodeId: registration.nodeId,
72193
- toolName: registration.name,
72194
- scope
72195
- });
72196
- registration.controller.abort(reason);
72197
- return reason;
72198
- }
72199
- });
72179
+ const makeHandle = (registration) => {
72180
+ const inRegisteringContext = AsyncLocalStorage4.snapshot();
72181
+ return {
72182
+ runId: registration.runId,
72183
+ nodeId: registration.nodeId,
72184
+ name: registration.name,
72185
+ settled: registration.settled,
72186
+ get aborted() {
72187
+ return registration.controller.signal.aborted;
72188
+ },
72189
+ abort(scope) {
72190
+ const existing = registration.controller.signal.reason;
72191
+ if (registration.controller.signal.aborted && existing instanceof WorkflowToolAbortError)
72192
+ return existing;
72193
+ const reason = new WorkflowToolAbortError({
72194
+ runId: registration.runId,
72195
+ nodeId: registration.nodeId,
72196
+ toolName: registration.name,
72197
+ scope
72198
+ });
72199
+ inRegisteringContext(() => registration.controller.abort(reason));
72200
+ return reason;
72201
+ }
72202
+ };
72203
+ };
72200
72204
  return {
72201
72205
  register(registration) {
72202
72206
  let runMap = byRun.get(registration.runId);
@@ -73076,7 +73080,7 @@ function deferStageUntilRunRelease(input) {
73076
73080
  }
73077
73081
 
73078
73082
  // dist/builtin/workflows/src/engine/run-rejection-boundary.ts
73079
- import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
73083
+ import { AsyncLocalStorage as AsyncLocalStorage5 } from "node:async_hooks";
73080
73084
  import { inspect } from "node:util";
73081
73085
  import { isStaleExtensionContextError as isStaleExtensionContextError3 } from "@bastani/atomic";
73082
73086
  function boundaryBag() {
@@ -73089,7 +73093,7 @@ function sharedBoundary() {
73089
73093
  return existing;
73090
73094
  const created = {
73091
73095
  version: 5,
73092
- store: new AsyncLocalStorage4,
73096
+ store: new AsyncLocalStorage5,
73093
73097
  owners: new WeakMap,
73094
73098
  promises: new WeakMap,
73095
73099
  scopes: new Set,
@@ -73380,16 +73384,18 @@ function openRunRejectionScope(options) {
73380
73384
  ensureInstalled(boundary);
73381
73385
  pruneEndedSources(boundary);
73382
73386
  const parent = options.parentRunId === undefined ? undefined : boundary.runs.get(options.parentRunId);
73387
+ const rootRunId = parent?.rootRunId ?? options.runId;
73383
73388
  const scope = {
73384
73389
  runId: options.runId,
73385
- rootRunId: parent?.rootRunId ?? options.runId,
73390
+ rootRunId,
73386
73391
  source: options.source === undefined ? undefined : normalizeStackPath(options.source),
73387
73392
  parent,
73388
73393
  fail: options.fail,
73389
73394
  primitives: new Map,
73390
73395
  generation: 0,
73391
73396
  live: true,
73392
- sharedSource: false
73397
+ sharedSource: false,
73398
+ console: parent !== undefined ? parent.console : options.consoleLog === true ? { rootRunId, bytes: 0, capped: false, announced: false } : undefined
73393
73399
  };
73394
73400
  boundary.scopes.add(scope);
73395
73401
  boundary.runs.set(scope.runId, scope);
@@ -80010,6 +80016,7 @@ async function executeRun(def, resolvedInputs, opts) {
80010
80016
  runId,
80011
80017
  ...opts.parentRun !== undefined ? { parentRunId: opts.parentRun.runId } : {},
80012
80018
  ...authoredIn !== undefined ? { source: authoredIn } : {},
80019
+ ...opts.routeConsoleToLog === true ? { consoleLog: true } : {},
80013
80020
  fail: (error) => {
80014
80021
  if (findWorkflowGracefulQuit(error) !== undefined)
80015
80022
  return;
@@ -80018,6 +80025,8 @@ async function executeRun(def, resolvedInputs, opts) {
80018
80025
  ownController.abort(selected.error);
80019
80026
  }
80020
80027
  });
80028
+ const abortOutsideRunScope = ownController.abort.bind(ownController);
80029
+ ownController.abort = (reason) => rejectionScope.run(() => abortOutsideRunScope(reason));
80021
80030
  ownController.signal.addEventListener("abort", () => rejectionScope.ignore(ownController.signal.reason), {
80022
80031
  once: true
80023
80032
  });
@@ -72256,32 +72256,36 @@ function workflowChildFromDraft(draft) {
72256
72256
  var init_completed_catalog_stage_groups = () => {};
72257
72257
 
72258
72258
  // dist/builtin/workflows/src/engine/run-tool-control-registry.ts
72259
+ import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
72259
72260
  function createToolControlRegistry() {
72260
72261
  const byRun = new Map;
72261
72262
  const admissionByRun = new Map;
72262
72263
  const runControls = new Map;
72263
- const makeHandle = (registration) => ({
72264
- runId: registration.runId,
72265
- nodeId: registration.nodeId,
72266
- name: registration.name,
72267
- settled: registration.settled,
72268
- get aborted() {
72269
- return registration.controller.signal.aborted;
72270
- },
72271
- abort(scope) {
72272
- const existing = registration.controller.signal.reason;
72273
- if (registration.controller.signal.aborted && existing instanceof WorkflowToolAbortError)
72274
- return existing;
72275
- const reason = new WorkflowToolAbortError({
72276
- runId: registration.runId,
72277
- nodeId: registration.nodeId,
72278
- toolName: registration.name,
72279
- scope
72280
- });
72281
- registration.controller.abort(reason);
72282
- return reason;
72283
- }
72284
- });
72264
+ const makeHandle = (registration) => {
72265
+ const inRegisteringContext = AsyncLocalStorage4.snapshot();
72266
+ return {
72267
+ runId: registration.runId,
72268
+ nodeId: registration.nodeId,
72269
+ name: registration.name,
72270
+ settled: registration.settled,
72271
+ get aborted() {
72272
+ return registration.controller.signal.aborted;
72273
+ },
72274
+ abort(scope) {
72275
+ const existing = registration.controller.signal.reason;
72276
+ if (registration.controller.signal.aborted && existing instanceof WorkflowToolAbortError)
72277
+ return existing;
72278
+ const reason = new WorkflowToolAbortError({
72279
+ runId: registration.runId,
72280
+ nodeId: registration.nodeId,
72281
+ toolName: registration.name,
72282
+ scope
72283
+ });
72284
+ inRegisteringContext(() => registration.controller.abort(reason));
72285
+ return reason;
72286
+ }
72287
+ };
72288
+ };
72285
72289
  return {
72286
72290
  register(registration) {
72287
72291
  let runMap = byRun.get(registration.runId);
@@ -73161,7 +73165,7 @@ function deferStageUntilRunRelease(input) {
73161
73165
  }
73162
73166
 
73163
73167
  // dist/builtin/workflows/src/engine/run-rejection-boundary.ts
73164
- import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
73168
+ import { AsyncLocalStorage as AsyncLocalStorage5 } from "node:async_hooks";
73165
73169
  import { inspect } from "node:util";
73166
73170
  import { isStaleExtensionContextError as isStaleExtensionContextError3 } from "@bastani/atomic";
73167
73171
  function boundaryBag() {
@@ -73174,7 +73178,7 @@ function sharedBoundary() {
73174
73178
  return existing;
73175
73179
  const created = {
73176
73180
  version: 5,
73177
- store: new AsyncLocalStorage4,
73181
+ store: new AsyncLocalStorage5,
73178
73182
  owners: new WeakMap,
73179
73183
  promises: new WeakMap,
73180
73184
  scopes: new Set,
@@ -73465,16 +73469,18 @@ function openRunRejectionScope(options) {
73465
73469
  ensureInstalled(boundary);
73466
73470
  pruneEndedSources(boundary);
73467
73471
  const parent = options.parentRunId === undefined ? undefined : boundary.runs.get(options.parentRunId);
73472
+ const rootRunId = parent?.rootRunId ?? options.runId;
73468
73473
  const scope = {
73469
73474
  runId: options.runId,
73470
- rootRunId: parent?.rootRunId ?? options.runId,
73475
+ rootRunId,
73471
73476
  source: options.source === undefined ? undefined : normalizeStackPath(options.source),
73472
73477
  parent,
73473
73478
  fail: options.fail,
73474
73479
  primitives: new Map,
73475
73480
  generation: 0,
73476
73481
  live: true,
73477
- sharedSource: false
73482
+ sharedSource: false,
73483
+ console: parent !== undefined ? parent.console : options.consoleLog === true ? { rootRunId, bytes: 0, capped: false, announced: false } : undefined
73478
73484
  };
73479
73485
  boundary.scopes.add(scope);
73480
73486
  boundary.runs.set(scope.runId, scope);
@@ -80095,6 +80101,7 @@ async function executeRun(def, resolvedInputs, opts) {
80095
80101
  runId,
80096
80102
  ...opts.parentRun !== undefined ? { parentRunId: opts.parentRun.runId } : {},
80097
80103
  ...authoredIn !== undefined ? { source: authoredIn } : {},
80104
+ ...opts.routeConsoleToLog === true ? { consoleLog: true } : {},
80098
80105
  fail: (error) => {
80099
80106
  if (findWorkflowGracefulQuit(error) !== undefined)
80100
80107
  return;
@@ -80103,6 +80110,8 @@ async function executeRun(def, resolvedInputs, opts) {
80103
80110
  ownController.abort(selected.error);
80104
80111
  }
80105
80112
  });
80113
+ const abortOutsideRunScope = ownController.abort.bind(ownController);
80114
+ ownController.abort = (reason) => rejectionScope.run(() => abortOutsideRunScope(reason));
80106
80115
  ownController.signal.addEventListener("abort", () => rejectionScope.ignore(ownController.signal.reason), {
80107
80116
  once: true
80108
80117
  });
@@ -72171,32 +72171,36 @@ function workflowChildFromDraft(draft) {
72171
72171
  var init_completed_catalog_stage_groups = () => {};
72172
72172
 
72173
72173
  // dist/builtin/workflows/src/engine/run-tool-control-registry.ts
72174
+ import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
72174
72175
  function createToolControlRegistry() {
72175
72176
  const byRun = new Map;
72176
72177
  const admissionByRun = new Map;
72177
72178
  const runControls = new Map;
72178
- const makeHandle = (registration) => ({
72179
- runId: registration.runId,
72180
- nodeId: registration.nodeId,
72181
- name: registration.name,
72182
- settled: registration.settled,
72183
- get aborted() {
72184
- return registration.controller.signal.aborted;
72185
- },
72186
- abort(scope) {
72187
- const existing = registration.controller.signal.reason;
72188
- if (registration.controller.signal.aborted && existing instanceof WorkflowToolAbortError)
72189
- return existing;
72190
- const reason = new WorkflowToolAbortError({
72191
- runId: registration.runId,
72192
- nodeId: registration.nodeId,
72193
- toolName: registration.name,
72194
- scope
72195
- });
72196
- registration.controller.abort(reason);
72197
- return reason;
72198
- }
72199
- });
72179
+ const makeHandle = (registration) => {
72180
+ const inRegisteringContext = AsyncLocalStorage4.snapshot();
72181
+ return {
72182
+ runId: registration.runId,
72183
+ nodeId: registration.nodeId,
72184
+ name: registration.name,
72185
+ settled: registration.settled,
72186
+ get aborted() {
72187
+ return registration.controller.signal.aborted;
72188
+ },
72189
+ abort(scope) {
72190
+ const existing = registration.controller.signal.reason;
72191
+ if (registration.controller.signal.aborted && existing instanceof WorkflowToolAbortError)
72192
+ return existing;
72193
+ const reason = new WorkflowToolAbortError({
72194
+ runId: registration.runId,
72195
+ nodeId: registration.nodeId,
72196
+ toolName: registration.name,
72197
+ scope
72198
+ });
72199
+ inRegisteringContext(() => registration.controller.abort(reason));
72200
+ return reason;
72201
+ }
72202
+ };
72203
+ };
72200
72204
  return {
72201
72205
  register(registration) {
72202
72206
  let runMap = byRun.get(registration.runId);
@@ -73076,7 +73080,7 @@ function deferStageUntilRunRelease(input) {
73076
73080
  }
73077
73081
 
73078
73082
  // dist/builtin/workflows/src/engine/run-rejection-boundary.ts
73079
- import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
73083
+ import { AsyncLocalStorage as AsyncLocalStorage5 } from "node:async_hooks";
73080
73084
  import { inspect } from "node:util";
73081
73085
  import { isStaleExtensionContextError as isStaleExtensionContextError3 } from "@bastani/atomic";
73082
73086
  function boundaryBag() {
@@ -73089,7 +73093,7 @@ function sharedBoundary() {
73089
73093
  return existing;
73090
73094
  const created = {
73091
73095
  version: 5,
73092
- store: new AsyncLocalStorage4,
73096
+ store: new AsyncLocalStorage5,
73093
73097
  owners: new WeakMap,
73094
73098
  promises: new WeakMap,
73095
73099
  scopes: new Set,
@@ -73380,16 +73384,18 @@ function openRunRejectionScope(options) {
73380
73384
  ensureInstalled(boundary);
73381
73385
  pruneEndedSources(boundary);
73382
73386
  const parent = options.parentRunId === undefined ? undefined : boundary.runs.get(options.parentRunId);
73387
+ const rootRunId = parent?.rootRunId ?? options.runId;
73383
73388
  const scope = {
73384
73389
  runId: options.runId,
73385
- rootRunId: parent?.rootRunId ?? options.runId,
73390
+ rootRunId,
73386
73391
  source: options.source === undefined ? undefined : normalizeStackPath(options.source),
73387
73392
  parent,
73388
73393
  fail: options.fail,
73389
73394
  primitives: new Map,
73390
73395
  generation: 0,
73391
73396
  live: true,
73392
- sharedSource: false
73397
+ sharedSource: false,
73398
+ console: parent !== undefined ? parent.console : options.consoleLog === true ? { rootRunId, bytes: 0, capped: false, announced: false } : undefined
73393
73399
  };
73394
73400
  boundary.scopes.add(scope);
73395
73401
  boundary.runs.set(scope.runId, scope);
@@ -80010,6 +80016,7 @@ async function executeRun(def, resolvedInputs, opts) {
80010
80016
  runId,
80011
80017
  ...opts.parentRun !== undefined ? { parentRunId: opts.parentRun.runId } : {},
80012
80018
  ...authoredIn !== undefined ? { source: authoredIn } : {},
80019
+ ...opts.routeConsoleToLog === true ? { consoleLog: true } : {},
80013
80020
  fail: (error) => {
80014
80021
  if (findWorkflowGracefulQuit(error) !== undefined)
80015
80022
  return;
@@ -80018,6 +80025,8 @@ async function executeRun(def, resolvedInputs, opts) {
80018
80025
  ownController.abort(selected.error);
80019
80026
  }
80020
80027
  });
80028
+ const abortOutsideRunScope = ownController.abort.bind(ownController);
80029
+ ownController.abort = (reason) => rejectionScope.run(() => abortOutsideRunScope(reason));
80021
80030
  ownController.signal.addEventListener("abort", () => rejectionScope.ignore(ownController.signal.reason), {
80022
80031
  once: true
80023
80032
  });
@@ -4,10 +4,11 @@ import type { Api, Message, Model } from "@bastani/pi-ai/compat";
4
4
  * construction and extension payload hooks.
5
5
  *
6
6
  * Anthropic requires thinking/redacted_thinking blocks from replayed assistant
7
- * messages to remain byte-for-byte identical to the original response. The
8
- * upstream pi-ai Anthropic converter can sanitize thinking text and drop signed
9
- * empty thinking. convertToLlm normalizes raw redacted_thinking blocks in its
10
- * transient, non-mutating LLM-compatible messages before this hook, then this guard
7
+ * messages to remain byte-for-byte identical to the original response.
8
+ * Extension `before_provider_request` hooks can rewrite them, and older pi-ai
9
+ * converters sanitized thinking text and dropped signed empty thinking.
10
+ * convertToLlm normalizes raw redacted_thinking blocks in its transient,
11
+ * non-mutating LLM-compatible messages before this hook, then this guard
11
12
  * restores exact same-model payloads while leaving non-Anthropic and cross-model
12
13
  * payloads unchanged.
13
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-thinking-guard.d.ts","sourceRoot":"","sources":["../../src/core/anthropic-thinking-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAoB,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAoQnF;;;;;;;;;;;GAWG;AACH,wBAAgB,oCAAoC,CACnD,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,SAAS,OAAO,EAAE,EAClC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GACf,OAAO,CA+CT"}
1
+ {"version":3,"file":"anthropic-thinking-guard.d.ts","sourceRoot":"","sources":["../../src/core/anthropic-thinking-guard.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAoB,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AA2MnF;;;;;;;;;;;;GAYG;AACH,wBAAgB,oCAAoC,CACnD,OAAO,EAAE,OAAO,EAChB,cAAc,EAAE,SAAS,OAAO,EAAE,EAClC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GACf,OAAO,CA+CT"}
@@ -1,3 +1,5 @@
1
+ import { anthropicAssistantReplayContent } from "@bastani/pi-ai/api/anthropic-assistant-content";
2
+ import { isProducedByModel } from "@bastani/pi-ai/api/transform-messages";
1
3
  function isObject(value) {
2
4
  return value !== null && typeof value === "object" && !Array.isArray(value);
3
5
  }
@@ -9,24 +11,6 @@ function isThinkingLikeAnthropicBlock(value) {
9
11
  return false;
10
12
  return value.type === "thinking" || value.type === "redacted_thinking";
11
13
  }
12
- function isSameModelAssistant(message, model) {
13
- return message.provider === model.provider && message.api === model.api && message.model === model.id;
14
- }
15
- /**
16
- * Mirrors `canReplayForeignThinking` in @bastani/pi-ai's `transformMessages`: a *different* model
17
- * on the same provider and API whose API adjudicates thinking model binding itself. Its signed
18
- * blocks replay unchanged — including redacted ones and signed blocks whose text is empty, which
19
- * is Claude Fable 5.1's default shape because `thinking.display` defaults to `"omitted"`.
20
- *
21
- * This must stay conditional. For a model that does not delegate binding, pi-ai still drops or
22
- * flattens foreign thinking, so accepting signature-only blocks unconditionally would misalign
23
- * the ordinals in the opposite direction.
24
- */
25
- function delegatesForeignThinkingBinding(message, model) {
26
- if (message.provider !== model.provider || message.api !== model.api)
27
- return false;
28
- return isObject(model.compat) && model.compat.delegatesThinkingModelBinding === true;
29
- }
30
14
  /**
31
15
  * Index of the last server-side fallback marker, or -1.
32
16
  *
@@ -66,53 +50,15 @@ function readReplayThinkingBlock(block, allowEmptySignature = false) {
66
50
  function hasReplayThinkingBlock(message, allowEmptySignature) {
67
51
  return message.content.some((block) => readReplayThinkingBlock(block, allowEmptySignature) !== undefined);
68
52
  }
69
- function legacyProviderWouldEmitAssistantBlock(block) {
70
- if (!isObject(block) || typeof block.type !== "string")
71
- return false;
72
- if (block.type === "text") {
73
- return typeof block.text === "string" && block.text.trim().length > 0;
74
- }
75
- if (block.type === "toolCall") {
76
- return true;
77
- }
78
- if (block.type !== "thinking") {
79
- return false;
80
- }
81
- if (block.redacted === true) {
82
- return true;
83
- }
84
- if (typeof block.thinking !== "string")
85
- return false;
86
- const signature = typeof block.thinkingSignature === "string" ? block.thinkingSignature : undefined;
87
- return block.thinking.trim().length > 0 || (signature !== undefined && signature.trim().length > 0);
88
- }
89
- function legacyProviderWouldEmitAssistant(message, model) {
90
- if (message.stopReason === "error" || message.stopReason === "aborted")
91
- return false;
92
- // This mirrors the relevant same-model branch in @bastani/pi-ai's
93
- // transformMessages() + Anthropic convertMessages() pipeline closely enough
94
- // to keep assistant ordinal mapping aligned with the provider payload.
95
- //
96
- // A cross-model turn on a model that delegates binding follows the *same* rule: pi-ai replays
97
- // its foreign signed thinking unchanged, so a redacted or empty-text signed block is emitted
98
- // there too. Without this branch, a Fable 5.1 turn whose thinking display is `"omitted"` looks
99
- // non-emitting here, the ordinals desynchronise, and repair is abandoned for the whole request.
100
- if (isSameModelAssistant(message, model) || delegatesForeignThinkingBinding(message, model)) {
101
- return message.content.some((block) => legacyProviderWouldEmitAssistantBlock(block));
102
- }
103
- return message.content.some((block) => {
104
- if (!isObject(block) || typeof block.type !== "string")
105
- return false;
106
- if (block.type === "text")
107
- return typeof block.text === "string" && block.text.trim().length > 0;
108
- if (block.type === "toolCall")
109
- return true;
110
- if (block.type !== "thinking")
111
- return false;
112
- if (block.redacted === true)
113
- return false;
114
- return typeof block.thinking === "string" && block.thinking.trim().length > 0;
115
- });
53
+ /**
54
+ * Whether the provider payload holds an assistant message for this turn, answered by pi-ai's own
55
+ * `transformMessages` pass and Anthropic block conversion so the ordinal mapping cannot drift from
56
+ * the payload. A turn left with nothing to send is omitted, such as a GPT turn that held only
57
+ * reasoning. A retained server-side `fallback` marker is always sent, even when it is the only
58
+ * block left because the turn's reasoning belongs to other models.
59
+ */
60
+ function providerEmitsAssistant(message, model) {
61
+ return anthropicAssistantReplayContent(message, model).length > 0;
116
62
  }
117
63
  function fallbackTextBlock(block) {
118
64
  return typeof block.text === "string" && block.text.trim().length > 0
@@ -234,10 +180,11 @@ function getAnthropicAssistantContent(message) {
234
180
  * construction and extension payload hooks.
235
181
  *
236
182
  * Anthropic requires thinking/redacted_thinking blocks from replayed assistant
237
- * messages to remain byte-for-byte identical to the original response. The
238
- * upstream pi-ai Anthropic converter can sanitize thinking text and drop signed
239
- * empty thinking. convertToLlm normalizes raw redacted_thinking blocks in its
240
- * transient, non-mutating LLM-compatible messages before this hook, then this guard
183
+ * messages to remain byte-for-byte identical to the original response.
184
+ * Extension `before_provider_request` hooks can rewrite them, and older pi-ai
185
+ * converters sanitized thinking text and dropped signed empty thinking.
186
+ * convertToLlm normalizes raw redacted_thinking blocks in its transient,
187
+ * non-mutating LLM-compatible messages before this hook, then this guard
241
188
  * restores exact same-model payloads while leaving non-Anthropic and cross-model
242
189
  * payloads unchanged.
243
190
  */
@@ -252,16 +199,17 @@ export function restoreAnthropicReplayThinkingBlocks(payload, sourceMessages, mo
252
199
  .filter((entry) => entry.content !== undefined && isObject(entry.message));
253
200
  if (assistantPayloads.length === 0)
254
201
  return payload;
255
- const emittingSourceAssistants = sourceMessages.filter((message) => message.role === "assistant" && legacyProviderWouldEmitAssistant(message, model));
256
- // Fail closed if pi-ai's converter emission behavior drifts from the model above;
257
- // ordinal restoration must never splice thinking into a different assistant.
202
+ const emittingSourceAssistants = sourceMessages.filter((message) => message.role === "assistant" && providerEmitsAssistant(message, model));
203
+ // Fail closed when the payload no longer has one assistant per emitting turn, for example
204
+ // because a `before_provider_request` hook added or removed one: ordinal restoration must
205
+ // never splice thinking into a different assistant.
258
206
  if (emittingSourceAssistants.length !== assistantPayloads.length)
259
207
  return payload;
260
208
  const allowEmptySignature = isObject(model.compat) && model.compat.allowEmptySignature === true;
261
209
  let nextPayloadMessages;
262
210
  for (const [assistantPayloadOrdinal, sourceMessage] of emittingSourceAssistants.entries()) {
263
211
  const payloadAssistant = assistantPayloads[assistantPayloadOrdinal];
264
- if (!isSameModelAssistant(sourceMessage, model) || !hasReplayThinkingBlock(sourceMessage, allowEmptySignature))
212
+ if (!isProducedByModel(sourceMessage, model) || !hasReplayThinkingBlock(sourceMessage, allowEmptySignature))
265
213
  continue;
266
214
  // A second fail-closed guard verifies the non-thinking shape before replacing
267
215
  // signed blocks, protecting against equal-count ordinal misalignment.
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-thinking-guard.js","sourceRoot":"","sources":["../../src/core/anthropic-thinking-guard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, AssistantMessage, Message, Model } from \"@bastani/pi-ai/compat\";\n\ntype JsonObject = Record<string, unknown>;\n\ntype AnthropicContentBlock = JsonObject & { type: string };\n\ntype ReplayThinkingBlock =\n\t| { type: \"thinking\"; thinking: string; signature: string }\n\t| { type: \"redacted_thinking\"; data: string };\n\nfunction isObject(value: unknown): value is JsonObject {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isAnthropicContentBlock(value: unknown): value is AnthropicContentBlock {\n\treturn isObject(value) && typeof value.type === \"string\";\n}\n\nfunction isThinkingLikeAnthropicBlock(value: unknown): boolean {\n\tif (!isAnthropicContentBlock(value)) return false;\n\treturn value.type === \"thinking\" || value.type === \"redacted_thinking\";\n}\n\nfunction isSameModelAssistant(message: AssistantMessage, model: Model<Api>): boolean {\n\treturn message.provider === model.provider && message.api === model.api && message.model === model.id;\n}\n\n/**\n * Mirrors `canReplayForeignThinking` in @bastani/pi-ai's `transformMessages`: a *different* model\n * on the same provider and API whose API adjudicates thinking model binding itself. Its signed\n * blocks replay unchanged — including redacted ones and signed blocks whose text is empty, which\n * is Claude Fable 5.1's default shape because `thinking.display` defaults to `\"omitted\"`.\n *\n * This must stay conditional. For a model that does not delegate binding, pi-ai still drops or\n * flattens foreign thinking, so accepting signature-only blocks unconditionally would misalign\n * the ordinals in the opposite direction.\n */\nfunction delegatesForeignThinkingBinding(message: AssistantMessage, model: Model<Api>): boolean {\n\tif (message.provider !== model.provider || message.api !== model.api) return false;\n\treturn isObject(model.compat) && (model.compat as JsonObject).delegatesThinkingModelBinding === true;\n}\n\n/**\n * Index of the last server-side fallback marker, or -1.\n *\n * pi-ai drops `thinking`, `redacted_thinking`, and client-side `tool_use` that precede the final\n * marker, because \"a request that echoes thinking blocks from both sides of the boundary is\n * rejected\". Both the alignment check and the repair pass must apply the same rule, or repair\n * would splice pre-boundary thinking back into a request the API rejects.\n * https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback\n */\nfunction finalFallbackBoundaryIndex(content: readonly unknown[]): number {\n\tfor (let index = content.length - 1; index >= 0; index -= 1) {\n\t\tconst block = content[index];\n\t\tif (isObject(block) && block.type === \"fallback\") return index;\n\t}\n\treturn -1;\n}\n\nfunction readReplayThinkingBlock(block: unknown, allowEmptySignature = false): ReplayThinkingBlock | undefined {\n\tif (!isObject(block) || typeof block.type !== \"string\") return undefined;\n\n\tif (block.type === \"redacted_thinking\") {\n\t\treturn typeof block.data === \"string\" ? { type: \"redacted_thinking\", data: block.data } : undefined;\n\t}\n\n\tif (block.type !== \"thinking\") return undefined;\n\n\tif (block.redacted === true) {\n\t\treturn typeof block.thinkingSignature === \"string\"\n\t\t\t? { type: \"redacted_thinking\", data: block.thinkingSignature }\n\t\t\t: undefined;\n\t}\n\n\tif (typeof block.thinking !== \"string\" || typeof block.thinkingSignature !== \"string\") return undefined;\n\tif (!allowEmptySignature && block.thinkingSignature.trim().length === 0) return undefined;\n\treturn { type: \"thinking\", thinking: block.thinking, signature: block.thinkingSignature };\n}\n\nfunction hasReplayThinkingBlock(message: AssistantMessage, allowEmptySignature: boolean): boolean {\n\treturn message.content.some((block) => readReplayThinkingBlock(block, allowEmptySignature) !== undefined);\n}\n\nfunction legacyProviderWouldEmitAssistantBlock(block: unknown): boolean {\n\tif (!isObject(block) || typeof block.type !== \"string\") return false;\n\n\tif (block.type === \"text\") {\n\t\treturn typeof block.text === \"string\" && block.text.trim().length > 0;\n\t}\n\n\tif (block.type === \"toolCall\") {\n\t\treturn true;\n\t}\n\n\tif (block.type !== \"thinking\") {\n\t\treturn false;\n\t}\n\n\tif (block.redacted === true) {\n\t\treturn true;\n\t}\n\n\tif (typeof block.thinking !== \"string\") return false;\n\tconst signature = typeof block.thinkingSignature === \"string\" ? block.thinkingSignature : undefined;\n\treturn block.thinking.trim().length > 0 || (signature !== undefined && signature.trim().length > 0);\n}\n\nfunction legacyProviderWouldEmitAssistant(message: AssistantMessage, model: Model<Api>): boolean {\n\tif (message.stopReason === \"error\" || message.stopReason === \"aborted\") return false;\n\n\t// This mirrors the relevant same-model branch in @bastani/pi-ai's\n\t// transformMessages() + Anthropic convertMessages() pipeline closely enough\n\t// to keep assistant ordinal mapping aligned with the provider payload.\n\t//\n\t// A cross-model turn on a model that delegates binding follows the *same* rule: pi-ai replays\n\t// its foreign signed thinking unchanged, so a redacted or empty-text signed block is emitted\n\t// there too. Without this branch, a Fable 5.1 turn whose thinking display is `\"omitted\"` looks\n\t// non-emitting here, the ordinals desynchronise, and repair is abandoned for the whole request.\n\tif (isSameModelAssistant(message, model) || delegatesForeignThinkingBinding(message, model)) {\n\t\treturn message.content.some((block) => legacyProviderWouldEmitAssistantBlock(block));\n\t}\n\n\treturn message.content.some((block) => {\n\t\tif (!isObject(block) || typeof block.type !== \"string\") return false;\n\t\tif (block.type === \"text\") return typeof block.text === \"string\" && block.text.trim().length > 0;\n\t\tif (block.type === \"toolCall\") return true;\n\t\tif (block.type !== \"thinking\") return false;\n\t\tif (block.redacted === true) return false;\n\t\treturn typeof block.thinking === \"string\" && block.thinking.trim().length > 0;\n\t});\n}\n\nfunction fallbackTextBlock(block: JsonObject): AnthropicContentBlock | undefined {\n\treturn typeof block.text === \"string\" && block.text.trim().length > 0\n\t\t? { type: \"text\", text: block.text }\n\t\t: undefined;\n}\n\nfunction fallbackToolUseBlock(block: JsonObject): AnthropicContentBlock | undefined {\n\tif (typeof block.id !== \"string\" || typeof block.name !== \"string\") return undefined;\n\treturn { type: \"tool_use\", id: block.id, name: block.name, input: block.arguments ?? {} };\n}\n\nfunction takeProviderBlock(\n\tproviderBlocks: readonly AnthropicContentBlock[],\n\tproviderIndex: number,\n\texpectedType: string,\n): { block: AnthropicContentBlock | undefined; nextProviderIndex: number } {\n\tconst providerBlock = providerBlocks[providerIndex];\n\tif (providerBlock?.type === expectedType) {\n\t\treturn { block: providerBlock, nextProviderIndex: providerIndex + 1 };\n\t}\n\treturn { block: undefined, nextProviderIndex: providerIndex };\n}\n\nfunction nonThinkingBlocksAlign(message: AssistantMessage, providerBlocks: readonly AnthropicContentBlock[]): boolean {\n\tconst boundaryIndex = finalFallbackBoundaryIndex(message.content);\n\tconst sourceTypes = message.content.flatMap((block, index) => {\n\t\tif (!isObject(block) || typeof block.type !== \"string\") return [];\n\t\tif (block.type === \"text\") return typeof block.text === \"string\" && block.text.trim().length > 0 ? [\"text\"] : [];\n\t\t// The marker is replayed in place and is not thinking-like, so it survives the provider-side\n\t\t// filter below and both sides must count it.\n\t\tif (block.type === \"fallback\") return [\"fallback\"];\n\t\tif (block.type !== \"toolCall\") return [];\n\t\t// A client-side tool call before the final marker was never executed by the model that\n\t\t// continued the turn, so pi-ai drops it along with its tool result.\n\t\treturn boundaryIndex >= 0 && index < boundaryIndex ? [] : [\"tool_use\"];\n\t});\n\tconst providerTypes = providerBlocks\n\t\t.filter((block) => !isThinkingLikeAnthropicBlock(block))\n\t\t.map((block) => block.type);\n\treturn (\n\t\tsourceTypes.length === providerTypes.length && sourceTypes.every((type, index) => type === providerTypes[index])\n\t);\n}\n\nfunction repairAssistantContent(\n\toriginalMessage: AssistantMessage,\n\tproviderBlocks: readonly AnthropicContentBlock[],\n\tallowEmptySignature: boolean,\n): AnthropicContentBlock[] {\n\tconst repaired: AnthropicContentBlock[] = [];\n\tlet providerIndex = 0;\n\n\t// `fallbackTextBlock`/`fallbackToolUseBlock` below mean \"fallback value\"; `boundaryIndex` is the\n\t// unrelated Anthropic server-side fallback marker.\n\tconst boundaryIndex = finalFallbackBoundaryIndex(originalMessage.content);\n\n\tfor (const [index, originalBlock] of originalMessage.content.entries()) {\n\t\tconst precedesBoundary = boundaryIndex >= 0 && index < boundaryIndex;\n\t\t// Thinking before the final marker is not in the payload and must not be restored into it:\n\t\t// echoing thinking from both sides of the boundary is rejected outright.\n\t\tconst replayBlock = precedesBoundary ? undefined : readReplayThinkingBlock(originalBlock, allowEmptySignature);\n\t\tif (replayBlock) {\n\t\t\tif (isThinkingLikeAnthropicBlock(providerBlocks[providerIndex])) {\n\t\t\t\tproviderIndex += 1;\n\t\t\t}\n\t\t\trepaired.push({ ...replayBlock });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!isObject(originalBlock) || typeof originalBlock.type !== \"string\") continue;\n\n\t\tif (originalBlock.type === \"fallback\") {\n\t\t\t// Replayed in place, straight from the payload: this guard only restores thinking, and\n\t\t\t// the marker's position is what the API validates the surrounding thinking against.\n\t\t\tconst providerMarker = takeProviderBlock(providerBlocks, providerIndex, \"fallback\");\n\t\t\tif (providerMarker.block) {\n\t\t\t\trepaired.push(providerMarker.block);\n\t\t\t\tproviderIndex = providerMarker.nextProviderIndex;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (originalBlock.type === \"text\") {\n\t\t\tconst providerText = takeProviderBlock(providerBlocks, providerIndex, \"text\");\n\t\t\tif (providerText.block) {\n\t\t\t\trepaired.push(providerText.block);\n\t\t\t\tproviderIndex = providerText.nextProviderIndex;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst fallback = fallbackTextBlock(originalBlock);\n\t\t\tif (fallback) repaired.push(fallback);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (originalBlock.type === \"toolCall\") {\n\t\t\t// Dropped by pi-ai alongside its tool result, because the declining model's call was\n\t\t\t// never executed by the model that continued.\n\t\t\tif (precedesBoundary) continue;\n\t\t\tconst providerToolUse = takeProviderBlock(providerBlocks, providerIndex, \"tool_use\");\n\t\t\tif (providerToolUse.block) {\n\t\t\t\trepaired.push(providerToolUse.block);\n\t\t\t\tproviderIndex = providerToolUse.nextProviderIndex;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst fallback = fallbackToolUseBlock(originalBlock);\n\t\t\tif (fallback) repaired.push(fallback);\n\t\t}\n\t}\n\n\tfor (; providerIndex < providerBlocks.length; providerIndex += 1) {\n\t\trepaired.push(providerBlocks[providerIndex]);\n\t}\n\n\treturn repaired;\n}\n\nfunction getAnthropicMessages(payload: unknown): unknown[] | undefined {\n\tif (!isObject(payload)) return undefined;\n\treturn Array.isArray(payload.messages) ? payload.messages : undefined;\n}\n\nfunction getAnthropicAssistantContent(message: unknown): AnthropicContentBlock[] | undefined {\n\tif (!isObject(message) || message.role !== \"assistant\") return undefined;\n\tif (!Array.isArray(message.content)) return undefined;\n\tconst content = message.content.filter(isAnthropicContentBlock);\n\treturn content.length === message.content.length ? content : undefined;\n}\n\n/**\n * Restore same-model Anthropic replay thinking blocks after provider payload\n * construction and extension payload hooks.\n *\n * Anthropic requires thinking/redacted_thinking blocks from replayed assistant\n * messages to remain byte-for-byte identical to the original response. The\n * upstream pi-ai Anthropic converter can sanitize thinking text and drop signed\n * empty thinking. convertToLlm normalizes raw redacted_thinking blocks in its\n * transient, non-mutating LLM-compatible messages before this hook, then this guard\n * restores exact same-model payloads while leaving non-Anthropic and cross-model\n * payloads unchanged.\n */\nexport function restoreAnthropicReplayThinkingBlocks(\n\tpayload: unknown,\n\tsourceMessages: readonly Message[],\n\tmodel: Model<Api>,\n): unknown {\n\tif (model.api !== \"anthropic-messages\") return payload;\n\n\tconst payloadMessages = getAnthropicMessages(payload);\n\tif (!payloadMessages) return payload;\n\n\tconst assistantPayloads = payloadMessages\n\t\t.map((message, index) => ({ message, index, content: getAnthropicAssistantContent(message) }))\n\t\t.filter(\n\t\t\t(entry): entry is { message: JsonObject; index: number; content: AnthropicContentBlock[] } =>\n\t\t\t\tentry.content !== undefined && isObject(entry.message),\n\t\t);\n\n\tif (assistantPayloads.length === 0) return payload;\n\n\tconst emittingSourceAssistants = sourceMessages.filter(\n\t\t(message): message is AssistantMessage =>\n\t\t\tmessage.role === \"assistant\" && legacyProviderWouldEmitAssistant(message, model),\n\t);\n\t// Fail closed if pi-ai's converter emission behavior drifts from the model above;\n\t// ordinal restoration must never splice thinking into a different assistant.\n\tif (emittingSourceAssistants.length !== assistantPayloads.length) return payload;\n\n\tconst allowEmptySignature = isObject(model.compat) && (model.compat as JsonObject).allowEmptySignature === true;\n\tlet nextPayloadMessages: unknown[] | undefined;\n\n\tfor (const [assistantPayloadOrdinal, sourceMessage] of emittingSourceAssistants.entries()) {\n\t\tconst payloadAssistant = assistantPayloads[assistantPayloadOrdinal];\n\n\t\tif (!isSameModelAssistant(sourceMessage, model) || !hasReplayThinkingBlock(sourceMessage, allowEmptySignature))\n\t\t\tcontinue;\n\t\t// A second fail-closed guard verifies the non-thinking shape before replacing\n\t\t// signed blocks, protecting against equal-count ordinal misalignment.\n\t\tif (!nonThinkingBlocksAlign(sourceMessage, payloadAssistant.content)) continue;\n\n\t\tconst repairedContent = repairAssistantContent(sourceMessage, payloadAssistant.content, allowEmptySignature);\n\t\tif (JSON.stringify(repairedContent) === JSON.stringify(payloadAssistant.content)) continue;\n\n\t\tnextPayloadMessages ??= [...payloadMessages];\n\t\tnextPayloadMessages[payloadAssistant.index] = {\n\t\t\t...payloadAssistant.message,\n\t\t\tcontent: repairedContent,\n\t\t};\n\t}\n\n\tif (!nextPayloadMessages || !isObject(payload)) return payload;\n\treturn { ...payload, messages: nextPayloadMessages };\n}\n"]}
1
+ {"version":3,"file":"anthropic-thinking-guard.js","sourceRoot":"","sources":["../../src/core/anthropic-thinking-guard.ts"],"names":[],"mappings":"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","sourcesContent":["import { anthropicAssistantReplayContent } from \"@bastani/pi-ai/api/anthropic-assistant-content\";\nimport { isProducedByModel } from \"@bastani/pi-ai/api/transform-messages\";\nimport type { Api, AssistantMessage, Message, Model } from \"@bastani/pi-ai/compat\";\n\ntype JsonObject = Record<string, unknown>;\n\ntype AnthropicContentBlock = JsonObject & { type: string };\n\ntype ReplayThinkingBlock =\n\t| { type: \"thinking\"; thinking: string; signature: string }\n\t| { type: \"redacted_thinking\"; data: string };\n\nfunction isObject(value: unknown): value is JsonObject {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction isAnthropicContentBlock(value: unknown): value is AnthropicContentBlock {\n\treturn isObject(value) && typeof value.type === \"string\";\n}\n\nfunction isThinkingLikeAnthropicBlock(value: unknown): boolean {\n\tif (!isAnthropicContentBlock(value)) return false;\n\treturn value.type === \"thinking\" || value.type === \"redacted_thinking\";\n}\n\n/**\n * Index of the last server-side fallback marker, or -1.\n *\n * pi-ai drops `thinking`, `redacted_thinking`, and client-side `tool_use` that precede the final\n * marker, because \"a request that echoes thinking blocks from both sides of the boundary is\n * rejected\". Both the alignment check and the repair pass must apply the same rule, or repair\n * would splice pre-boundary thinking back into a request the API rejects.\n * https://platform.claude.com/docs/en/build-with-claude/refusals-and-fallback\n */\nfunction finalFallbackBoundaryIndex(content: readonly unknown[]): number {\n\tfor (let index = content.length - 1; index >= 0; index -= 1) {\n\t\tconst block = content[index];\n\t\tif (isObject(block) && block.type === \"fallback\") return index;\n\t}\n\treturn -1;\n}\n\nfunction readReplayThinkingBlock(block: unknown, allowEmptySignature = false): ReplayThinkingBlock | undefined {\n\tif (!isObject(block) || typeof block.type !== \"string\") return undefined;\n\n\tif (block.type === \"redacted_thinking\") {\n\t\treturn typeof block.data === \"string\" ? { type: \"redacted_thinking\", data: block.data } : undefined;\n\t}\n\n\tif (block.type !== \"thinking\") return undefined;\n\n\tif (block.redacted === true) {\n\t\treturn typeof block.thinkingSignature === \"string\"\n\t\t\t? { type: \"redacted_thinking\", data: block.thinkingSignature }\n\t\t\t: undefined;\n\t}\n\n\tif (typeof block.thinking !== \"string\" || typeof block.thinkingSignature !== \"string\") return undefined;\n\tif (!allowEmptySignature && block.thinkingSignature.trim().length === 0) return undefined;\n\treturn { type: \"thinking\", thinking: block.thinking, signature: block.thinkingSignature };\n}\n\nfunction hasReplayThinkingBlock(message: AssistantMessage, allowEmptySignature: boolean): boolean {\n\treturn message.content.some((block) => readReplayThinkingBlock(block, allowEmptySignature) !== undefined);\n}\n\n/**\n * Whether the provider payload holds an assistant message for this turn, answered by pi-ai's own\n * `transformMessages` pass and Anthropic block conversion so the ordinal mapping cannot drift from\n * the payload. A turn left with nothing to send is omitted, such as a GPT turn that held only\n * reasoning. A retained server-side `fallback` marker is always sent, even when it is the only\n * block left because the turn's reasoning belongs to other models.\n */\nfunction providerEmitsAssistant(message: AssistantMessage, model: Model<Api>): boolean {\n\treturn anthropicAssistantReplayContent(message, model).length > 0;\n}\n\nfunction fallbackTextBlock(block: JsonObject): AnthropicContentBlock | undefined {\n\treturn typeof block.text === \"string\" && block.text.trim().length > 0\n\t\t? { type: \"text\", text: block.text }\n\t\t: undefined;\n}\n\nfunction fallbackToolUseBlock(block: JsonObject): AnthropicContentBlock | undefined {\n\tif (typeof block.id !== \"string\" || typeof block.name !== \"string\") return undefined;\n\treturn { type: \"tool_use\", id: block.id, name: block.name, input: block.arguments ?? {} };\n}\n\nfunction takeProviderBlock(\n\tproviderBlocks: readonly AnthropicContentBlock[],\n\tproviderIndex: number,\n\texpectedType: string,\n): { block: AnthropicContentBlock | undefined; nextProviderIndex: number } {\n\tconst providerBlock = providerBlocks[providerIndex];\n\tif (providerBlock?.type === expectedType) {\n\t\treturn { block: providerBlock, nextProviderIndex: providerIndex + 1 };\n\t}\n\treturn { block: undefined, nextProviderIndex: providerIndex };\n}\n\nfunction nonThinkingBlocksAlign(message: AssistantMessage, providerBlocks: readonly AnthropicContentBlock[]): boolean {\n\tconst boundaryIndex = finalFallbackBoundaryIndex(message.content);\n\tconst sourceTypes = message.content.flatMap((block, index) => {\n\t\tif (!isObject(block) || typeof block.type !== \"string\") return [];\n\t\tif (block.type === \"text\") return typeof block.text === \"string\" && block.text.trim().length > 0 ? [\"text\"] : [];\n\t\t// The marker is replayed in place and is not thinking-like, so it survives the provider-side\n\t\t// filter below and both sides must count it.\n\t\tif (block.type === \"fallback\") return [\"fallback\"];\n\t\tif (block.type !== \"toolCall\") return [];\n\t\t// A client-side tool call before the final marker was never executed by the model that\n\t\t// continued the turn, so pi-ai drops it along with its tool result.\n\t\treturn boundaryIndex >= 0 && index < boundaryIndex ? [] : [\"tool_use\"];\n\t});\n\tconst providerTypes = providerBlocks\n\t\t.filter((block) => !isThinkingLikeAnthropicBlock(block))\n\t\t.map((block) => block.type);\n\treturn (\n\t\tsourceTypes.length === providerTypes.length && sourceTypes.every((type, index) => type === providerTypes[index])\n\t);\n}\n\nfunction repairAssistantContent(\n\toriginalMessage: AssistantMessage,\n\tproviderBlocks: readonly AnthropicContentBlock[],\n\tallowEmptySignature: boolean,\n): AnthropicContentBlock[] {\n\tconst repaired: AnthropicContentBlock[] = [];\n\tlet providerIndex = 0;\n\n\t// `fallbackTextBlock`/`fallbackToolUseBlock` below mean \"fallback value\"; `boundaryIndex` is the\n\t// unrelated Anthropic server-side fallback marker.\n\tconst boundaryIndex = finalFallbackBoundaryIndex(originalMessage.content);\n\n\tfor (const [index, originalBlock] of originalMessage.content.entries()) {\n\t\tconst precedesBoundary = boundaryIndex >= 0 && index < boundaryIndex;\n\t\t// Thinking before the final marker is not in the payload and must not be restored into it:\n\t\t// echoing thinking from both sides of the boundary is rejected outright.\n\t\tconst replayBlock = precedesBoundary ? undefined : readReplayThinkingBlock(originalBlock, allowEmptySignature);\n\t\tif (replayBlock) {\n\t\t\tif (isThinkingLikeAnthropicBlock(providerBlocks[providerIndex])) {\n\t\t\t\tproviderIndex += 1;\n\t\t\t}\n\t\t\trepaired.push({ ...replayBlock });\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!isObject(originalBlock) || typeof originalBlock.type !== \"string\") continue;\n\n\t\tif (originalBlock.type === \"fallback\") {\n\t\t\t// Replayed in place, straight from the payload: this guard only restores thinking, and\n\t\t\t// the marker's position is what the API validates the surrounding thinking against.\n\t\t\tconst providerMarker = takeProviderBlock(providerBlocks, providerIndex, \"fallback\");\n\t\t\tif (providerMarker.block) {\n\t\t\t\trepaired.push(providerMarker.block);\n\t\t\t\tproviderIndex = providerMarker.nextProviderIndex;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (originalBlock.type === \"text\") {\n\t\t\tconst providerText = takeProviderBlock(providerBlocks, providerIndex, \"text\");\n\t\t\tif (providerText.block) {\n\t\t\t\trepaired.push(providerText.block);\n\t\t\t\tproviderIndex = providerText.nextProviderIndex;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst fallback = fallbackTextBlock(originalBlock);\n\t\t\tif (fallback) repaired.push(fallback);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (originalBlock.type === \"toolCall\") {\n\t\t\t// Dropped by pi-ai alongside its tool result, because the declining model's call was\n\t\t\t// never executed by the model that continued.\n\t\t\tif (precedesBoundary) continue;\n\t\t\tconst providerToolUse = takeProviderBlock(providerBlocks, providerIndex, \"tool_use\");\n\t\t\tif (providerToolUse.block) {\n\t\t\t\trepaired.push(providerToolUse.block);\n\t\t\t\tproviderIndex = providerToolUse.nextProviderIndex;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst fallback = fallbackToolUseBlock(originalBlock);\n\t\t\tif (fallback) repaired.push(fallback);\n\t\t}\n\t}\n\n\tfor (; providerIndex < providerBlocks.length; providerIndex += 1) {\n\t\trepaired.push(providerBlocks[providerIndex]);\n\t}\n\n\treturn repaired;\n}\n\nfunction getAnthropicMessages(payload: unknown): unknown[] | undefined {\n\tif (!isObject(payload)) return undefined;\n\treturn Array.isArray(payload.messages) ? payload.messages : undefined;\n}\n\nfunction getAnthropicAssistantContent(message: unknown): AnthropicContentBlock[] | undefined {\n\tif (!isObject(message) || message.role !== \"assistant\") return undefined;\n\tif (!Array.isArray(message.content)) return undefined;\n\tconst content = message.content.filter(isAnthropicContentBlock);\n\treturn content.length === message.content.length ? content : undefined;\n}\n\n/**\n * Restore same-model Anthropic replay thinking blocks after provider payload\n * construction and extension payload hooks.\n *\n * Anthropic requires thinking/redacted_thinking blocks from replayed assistant\n * messages to remain byte-for-byte identical to the original response.\n * Extension `before_provider_request` hooks can rewrite them, and older pi-ai\n * converters sanitized thinking text and dropped signed empty thinking.\n * convertToLlm normalizes raw redacted_thinking blocks in its transient,\n * non-mutating LLM-compatible messages before this hook, then this guard\n * restores exact same-model payloads while leaving non-Anthropic and cross-model\n * payloads unchanged.\n */\nexport function restoreAnthropicReplayThinkingBlocks(\n\tpayload: unknown,\n\tsourceMessages: readonly Message[],\n\tmodel: Model<Api>,\n): unknown {\n\tif (model.api !== \"anthropic-messages\") return payload;\n\n\tconst payloadMessages = getAnthropicMessages(payload);\n\tif (!payloadMessages) return payload;\n\n\tconst assistantPayloads = payloadMessages\n\t\t.map((message, index) => ({ message, index, content: getAnthropicAssistantContent(message) }))\n\t\t.filter(\n\t\t\t(entry): entry is { message: JsonObject; index: number; content: AnthropicContentBlock[] } =>\n\t\t\t\tentry.content !== undefined && isObject(entry.message),\n\t\t);\n\n\tif (assistantPayloads.length === 0) return payload;\n\n\tconst emittingSourceAssistants = sourceMessages.filter(\n\t\t(message): message is AssistantMessage => message.role === \"assistant\" && providerEmitsAssistant(message, model),\n\t);\n\t// Fail closed when the payload no longer has one assistant per emitting turn, for example\n\t// because a `before_provider_request` hook added or removed one: ordinal restoration must\n\t// never splice thinking into a different assistant.\n\tif (emittingSourceAssistants.length !== assistantPayloads.length) return payload;\n\n\tconst allowEmptySignature = isObject(model.compat) && (model.compat as JsonObject).allowEmptySignature === true;\n\tlet nextPayloadMessages: unknown[] | undefined;\n\n\tfor (const [assistantPayloadOrdinal, sourceMessage] of emittingSourceAssistants.entries()) {\n\t\tconst payloadAssistant = assistantPayloads[assistantPayloadOrdinal];\n\n\t\tif (!isProducedByModel(sourceMessage, model) || !hasReplayThinkingBlock(sourceMessage, allowEmptySignature))\n\t\t\tcontinue;\n\t\t// A second fail-closed guard verifies the non-thinking shape before replacing\n\t\t// signed blocks, protecting against equal-count ordinal misalignment.\n\t\tif (!nonThinkingBlocksAlign(sourceMessage, payloadAssistant.content)) continue;\n\n\t\tconst repairedContent = repairAssistantContent(sourceMessage, payloadAssistant.content, allowEmptySignature);\n\t\tif (JSON.stringify(repairedContent) === JSON.stringify(payloadAssistant.content)) continue;\n\n\t\tnextPayloadMessages ??= [...payloadMessages];\n\t\tnextPayloadMessages[payloadAssistant.index] = {\n\t\t\t...payloadAssistant.message,\n\t\t\tcontent: repairedContent,\n\t\t};\n\t}\n\n\tif (!nextPayloadMessages || !isObject(payload)) return payload;\n\treturn { ...payload, messages: nextPayloadMessages };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"loader-virtual-modules.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader-virtual-modules.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAMvC,OAAO,EAAqB,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AA0B7D;;;;GAIG;AACH,iBAAS,oBAAoB,IAAI,MAAM,CAMtC;AAiBD;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC,OAAO,EAAE,CAAC,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAWD,iBAAS,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGxD;AAgFD,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAYpG;AAmHD,iBAAe,gCAAgC,CAC9C,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,mBAAmB,GAAG,SAAS,GACzC,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAyBvC;AAED,MAAM,WAAW,mBAAmB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAOrE;AAiBD;;;;;;;;GAQG;AACH,iBAAS,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAQ5E;AAKD;;;GAGG;AACH,iBAAS,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA4G5C;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACzC,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,OAAO,GAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAM,GAC5C,OAAO,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CASxC;AAED,eAAO,MAAM,wBAAwB;;;;;;;oDAOY,MAAM,eAAe,mBAAmB;oDAExC,MAAM,eAAe,mBAAmB;;6CAG/C,MAAM,KAAG,OAAO;CACzD,CAAC;AAkFF,wBAAsB,mBAAmB,CACxC,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAiCvC"}
1
+ {"version":3,"file":"loader-virtual-modules.d.ts","sourceRoot":"","sources":["../../../src/core/extensions/loader-virtual-modules.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAMvC,OAAO,EAAqB,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AA0B7D;;;;GAIG;AACH,iBAAS,oBAAoB,IAAI,MAAM,CAMtC;AAiBD;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC,OAAO,EAAE,CAAC,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAWD,iBAAS,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGxD;AAgFD,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS,CAYpG;AAmHD,iBAAe,gCAAgC,CAC9C,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,mBAAmB,GAAG,SAAS,GACzC,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAyBvC;AAED,MAAM,WAAW,mBAAmB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAI1C;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAOrE;AAiBD;;;;;;;;GAQG;AACH,iBAAS,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAQ5E;AAKD;;;GAGG;AACH,iBAAS,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoH5C;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACzC,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACtC,OAAO,GAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAM,GAC5C,OAAO,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CASxC;AAED,eAAO,MAAM,wBAAwB;;;;;;;oDAOY,MAAM,eAAe,mBAAmB;oDAExC,MAAM,eAAe,mBAAmB;;6CAG/C,MAAM,KAAG,OAAO;CACzD,CAAC;AAkFF,wBAAsB,mBAAmB,CACxC,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAiCvC"}