@bastani/atomic 0.9.63 → 0.9.64
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/CHANGELOG.md +24 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +910 -722
- package/dist/builtin/workflows/src/extension/local-worker-entry.mjs +35 -26
- package/dist/builtin/workflows/src/index.js +35 -26
- package/dist/builtin/workflows/src/local-worker-entry.mjs +35 -26
- package/dist/core/anthropic-thinking-guard.d.ts +5 -4
- package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
- package/dist/core/anthropic-thinking-guard.js +21 -73
- package/dist/core/anthropic-thinking-guard.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +5 -0
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/npm-shrinkwrap.json +32 -32
- package/package.json +3 -3
|
@@ -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
|
-
|
|
72180
|
-
|
|
72181
|
-
|
|
72182
|
-
|
|
72183
|
-
|
|
72184
|
-
|
|
72185
|
-
|
|
72186
|
-
|
|
72187
|
-
|
|
72188
|
-
|
|
72189
|
-
|
|
72190
|
-
|
|
72191
|
-
|
|
72192
|
-
|
|
72193
|
-
|
|
72194
|
-
|
|
72195
|
-
|
|
72196
|
-
|
|
72197
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
72265
|
-
|
|
72266
|
-
|
|
72267
|
-
|
|
72268
|
-
|
|
72269
|
-
|
|
72270
|
-
|
|
72271
|
-
|
|
72272
|
-
|
|
72273
|
-
|
|
72274
|
-
|
|
72275
|
-
|
|
72276
|
-
|
|
72277
|
-
|
|
72278
|
-
|
|
72279
|
-
|
|
72280
|
-
|
|
72281
|
-
|
|
72282
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
72180
|
-
|
|
72181
|
-
|
|
72182
|
-
|
|
72183
|
-
|
|
72184
|
-
|
|
72185
|
-
|
|
72186
|
-
|
|
72187
|
-
|
|
72188
|
-
|
|
72189
|
-
|
|
72190
|
-
|
|
72191
|
-
|
|
72192
|
-
|
|
72193
|
-
|
|
72194
|
-
|
|
72195
|
-
|
|
72196
|
-
|
|
72197
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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":"
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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.
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
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" &&
|
|
256
|
-
// Fail closed
|
|
257
|
-
//
|
|
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 (!
|
|
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,
|
|
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"}
|