pi-background-tasks 2.5.0 → 2.6.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.
- package/BACKGROUND-TASKS-INSTRUCTIONS.md +1 -1
- package/PUBLISHING.md +1 -1
- package/README.md +70 -25
- package/TESTING.md +44 -2
- package/TEST_PLAN.md +9 -5
- package/dist/extensions/anthropic-attribution-child.js +8 -0
- package/dist/extensions/anthropic-attribution-child.js.map +1 -0
- package/dist/extensions/anthropic-attribution.js +102 -0
- package/dist/extensions/anthropic-attribution.js.map +1 -0
- package/dist/extensions/background-tasks.js +2 -0
- package/dist/extensions/background-tasks.js.map +1 -0
- package/dist/extensions/delegate-child.js +2 -0
- package/dist/extensions/delegate-child.js.map +1 -0
- package/dist/extensions/fusion-child.js +2 -0
- package/dist/extensions/fusion-child.js.map +1 -0
- package/dist/package.json +5 -0
- package/dist/src/core/anthropic-attribution-path.js +24 -0
- package/dist/src/core/anthropic-attribution-path.js.map +1 -0
- package/dist/src/core/anthropic-attribution.js +2253 -0
- package/dist/src/core/anthropic-attribution.js.map +1 -0
- package/dist/src/core/attested-pi-contract.js +5 -0
- package/dist/src/core/attested-pi-contract.js.map +1 -0
- package/dist/src/core/attested-pi-run.js +749 -0
- package/dist/src/core/attested-pi-run.js.map +1 -0
- package/dist/src/core/canonical-json.js +19 -0
- package/dist/src/core/canonical-json.js.map +1 -0
- package/dist/src/core/common.js +787 -0
- package/dist/src/core/common.js.map +1 -0
- package/dist/src/core/config.js +78 -0
- package/dist/src/core/config.js.map +1 -0
- package/dist/src/core/context/parent-snapshot.js +75 -0
- package/dist/src/core/context/parent-snapshot.js.map +1 -0
- package/dist/src/core/context/token-budget.js +631 -0
- package/dist/src/core/context/token-budget.js.map +1 -0
- package/dist/src/core/context/visible-conversation-v2.js +390 -0
- package/dist/src/core/context/visible-conversation-v2.js.map +1 -0
- package/dist/src/core/delegate/artifacts.js +357 -0
- package/dist/src/core/delegate/artifacts.js.map +1 -0
- package/dist/src/core/delegate/budget.js +266 -0
- package/dist/src/core/delegate/budget.js.map +1 -0
- package/dist/src/core/delegate/facade-contract.js +7 -0
- package/dist/src/core/delegate/facade-contract.js.map +1 -0
- package/dist/src/core/delegate/hook-contract-evidence.json +18 -0
- package/dist/src/core/delegate/hook-contract.js +124 -0
- package/dist/src/core/delegate/hook-contract.js.map +1 -0
- package/dist/src/core/delegate/launch.js +366 -0
- package/dist/src/core/delegate/launch.js.map +1 -0
- package/dist/src/core/delegate/result-package.js +293 -0
- package/dist/src/core/delegate/result-package.js.map +1 -0
- package/dist/src/core/delegate/runner.js +398 -0
- package/dist/src/core/delegate/runner.js.map +1 -0
- package/dist/src/core/delegate/seed.js +334 -0
- package/dist/src/core/delegate/seed.js.map +1 -0
- package/dist/src/core/delegate/types.js +103 -0
- package/dist/src/core/delegate/types.js.map +1 -0
- package/dist/src/core/durable-fs.js +372 -0
- package/dist/src/core/durable-fs.js.map +1 -0
- package/dist/src/core/extension-api.js +382 -0
- package/dist/src/core/extension-api.js.map +1 -0
- package/dist/src/core/fusion/artifacts.js +654 -0
- package/dist/src/core/fusion/artifacts.js.map +1 -0
- package/dist/src/core/fusion/budget.js +949 -0
- package/dist/src/core/fusion/budget.js.map +1 -0
- package/dist/src/core/fusion/child-protocol.js +176 -0
- package/dist/src/core/fusion/child-protocol.js.map +1 -0
- package/dist/src/core/fusion/claude-cache.js +156 -0
- package/dist/src/core/fusion/claude-cache.js.map +1 -0
- package/dist/src/core/fusion/clean-context.js +64 -0
- package/dist/src/core/fusion/clean-context.js.map +1 -0
- package/dist/src/core/fusion/config.js +349 -0
- package/dist/src/core/fusion/config.js.map +1 -0
- package/dist/src/core/fusion/context.js +184 -0
- package/dist/src/core/fusion/context.js.map +1 -0
- package/dist/src/core/fusion/evaluation.js +696 -0
- package/dist/src/core/fusion/evaluation.js.map +1 -0
- package/dist/src/core/fusion/facade-contract.js +3 -0
- package/dist/src/core/fusion/facade-contract.js.map +1 -0
- package/dist/src/core/fusion/orchestrator.js +867 -0
- package/dist/src/core/fusion/orchestrator.js.map +1 -0
- package/dist/src/core/fusion/output-contract.js +27 -0
- package/dist/src/core/fusion/output-contract.js.map +1 -0
- package/dist/src/core/fusion/pi-child.js +1741 -0
- package/dist/src/core/fusion/pi-child.js.map +1 -0
- package/dist/src/core/fusion/prompts.js +260 -0
- package/dist/src/core/fusion/prompts.js.map +1 -0
- package/dist/src/core/fusion/result-package.js +857 -0
- package/dist/src/core/fusion/result-package.js.map +1 -0
- package/dist/src/core/fusion/source-policy.js +259 -0
- package/dist/src/core/fusion/source-policy.js.map +1 -0
- package/dist/src/core/fusion/types.js +207 -0
- package/dist/src/core/fusion/types.js.map +1 -0
- package/dist/src/core/fusion/web-fetch.js +777 -0
- package/dist/src/core/fusion/web-fetch.js.map +1 -0
- package/dist/src/core/fusion/workflows.js +126 -0
- package/dist/src/core/fusion/workflows.js.map +1 -0
- package/dist/src/core/lazy-module.js +181 -0
- package/dist/src/core/lazy-module.js.map +1 -0
- package/dist/src/core/pi-launch.js +431 -0
- package/dist/src/core/pi-launch.js.map +1 -0
- package/dist/src/core/registry.js +3331 -0
- package/dist/src/core/registry.js.map +1 -0
- package/dist/src/core/reload-shell-owner.js +1396 -0
- package/dist/src/core/reload-shell-owner.js.map +1 -0
- package/dist/src/core/shell-policy.js +80 -0
- package/dist/src/core/shell-policy.js.map +1 -0
- package/dist/src/core/task-durable.js +51 -0
- package/dist/src/core/task-durable.js.map +1 -0
- package/dist/src/core/update-check.js +92 -0
- package/dist/src/core/update-check.js.map +1 -0
- package/dist/src/core/windows-taskkill.js +185 -0
- package/dist/src/core/windows-taskkill.js.map +1 -0
- package/dist/src/delegate-child-extension.js +776 -0
- package/dist/src/delegate-child-extension.js.map +1 -0
- package/dist/src/delegate-extension.js +746 -0
- package/dist/src/delegate-extension.js.map +1 -0
- package/dist/src/extension.js +953 -0
- package/dist/src/extension.js.map +1 -0
- package/dist/src/fusion-child-extension.js +760 -0
- package/dist/src/fusion-child-extension.js.map +1 -0
- package/dist/src/fusion-extension.js +1030 -0
- package/dist/src/fusion-extension.js.map +1 -0
- package/dist/src/ui/background-tasks-manager.js +689 -0
- package/dist/src/ui/background-tasks-manager.js.map +1 -0
- package/dist/src/ui/fusion-model-selector.js +277 -0
- package/dist/src/ui/fusion-model-selector.js.map +1 -0
- package/docs/INDEX.md +35 -33
- package/docs/api/eventbus-v1.md +21 -5
- package/docs/choose-a-workflow.md +2 -0
- package/docs/commands/bg-clear.md +3 -3
- package/docs/commands/bg-update.md +3 -3
- package/docs/commands/bg.md +18 -6
- package/docs/commands/claude-cache.md +4 -4
- package/docs/commands/fusion-models.md +49 -4
- package/docs/commands/fusion.md +3 -3
- package/docs/commands/jobs.md +3 -3
- package/docs/commands/kill.md +3 -3
- package/docs/commands/logs.md +3 -3
- package/docs/commands/task-manager.md +5 -5
- package/docs/concepts/completion-delivery.md +6 -2
- package/docs/getting-started.md +7 -1
- package/docs/manifest.json +228 -42
- package/docs/operations/configuration.md +58 -5
- package/docs/operations/releasing.md +5 -3
- package/docs/operations/testing.md +22 -0
- package/docs/operations/troubleshooting.md +7 -2
- package/docs/read-before-edit.md +44 -31
- package/docs/reference/runtime-contracts.md +87 -51
- package/docs/reference/shortcuts-and-dock.md +28 -14
- package/docs/subsystems/anthropic-attribution.md +23 -11
- package/docs/subsystems/attested-pi-runs.md +12 -7
- package/docs/subsystems/background-task-runtime.md +53 -11
- package/docs/subsystems/child-launch-durability-and-safety.md +28 -9
- package/docs/subsystems/delegation.md +16 -3
- package/docs/subsystems/docs-freshness-gate.md +14 -7
- package/docs/subsystems/fusion.md +36 -11
- package/docs/subsystems/host-ui-and-telemetry.md +22 -9
- package/docs/tools/bg_delegate.md +3 -1
- package/docs/tools/bg_kill.md +3 -1
- package/docs/tools/bg_logs.md +3 -1
- package/docs/tools/bg_result.md +5 -3
- package/docs/tools/bg_run.md +31 -5
- package/docs/tools/bg_run_pi_attested.md +3 -1
- package/docs/tools/bg_status.md +3 -1
- package/docs/tools/fusion_investigate.md +3 -1
- package/docs/tools/fusion_reason.md +3 -1
- package/docs/tools/fusion_research.md +3 -1
- package/docs/tools/fusion_validate.md +3 -1
- package/extensions/anthropic-attribution-child.ts +10 -0
- package/extensions/anthropic-attribution.ts +157 -1
- package/package.json +13 -10
- package/src/core/anthropic-attribution-path.ts +5 -4
- package/src/core/anthropic-attribution.ts +306 -112
- package/src/core/attested-pi-contract.ts +4 -0
- package/src/core/attested-pi-run.ts +468 -81
- package/src/core/canonical-json.ts +21 -0
- package/src/core/common.ts +450 -40
- package/src/core/config.ts +121 -0
- package/src/core/context/visible-conversation-v2.ts +3 -6
- package/src/core/delegate/artifacts.ts +13 -9
- package/src/core/delegate/budget.ts +13 -6
- package/src/core/delegate/facade-contract.ts +6 -0
- package/src/core/delegate/result-package.ts +32 -14
- package/src/core/delegate/runner.ts +129 -58
- package/src/core/delegate/seed.ts +5 -7
- package/src/core/durable-fs.ts +178 -30
- package/src/core/extension-api.ts +36 -8
- package/src/core/fusion/artifacts.ts +17 -16
- package/src/core/fusion/clean-context.ts +8 -5
- package/src/core/fusion/config.ts +2 -1
- package/src/core/fusion/context.ts +2 -6
- package/src/core/fusion/facade-contract.ts +2 -0
- package/src/core/fusion/orchestrator.ts +3 -3
- package/src/core/fusion/prompts.ts +1 -1
- package/src/core/fusion/result-package.ts +259 -72
- package/src/core/fusion/source-policy.ts +43 -17
- package/src/core/lazy-module.ts +215 -0
- package/src/core/pi-launch.ts +390 -65
- package/src/core/registry.ts +2010 -381
- package/src/core/reload-shell-owner.ts +1662 -0
- package/src/core/shell-policy.ts +134 -0
- package/src/core/task-durable.ts +67 -0
- package/src/delegate-extension.ts +404 -144
- package/src/extension.ts +387 -114
- package/src/fusion-extension.ts +202 -96
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { appendFileSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
4
|
+
import { isAbsolute, join } from 'node:path';
|
|
5
|
+
import type {
|
|
6
|
+
AssistantMessageEventStream as HostAssistantMessageEventStream,
|
|
7
|
+
Context as HostContext,
|
|
8
|
+
Model as HostModel,
|
|
9
|
+
SimpleStreamOptions as HostSimpleStreamOptions,
|
|
10
|
+
} from '@earendil-works/pi-ai';
|
|
5
11
|
|
|
6
12
|
export const CLAUDE_CODE_SESSION_HEADER = 'X-Claude-Code-Session-Id';
|
|
7
13
|
|
|
@@ -72,6 +78,7 @@ const CLAUDE_AGENT_SDK_SYSTEM_TEXT =
|
|
|
72
78
|
const FINGERPRINT_SALT = '59cf53e54c78';
|
|
73
79
|
const AUDIT_ENV = 'PIPELINE_ANTHROPIC_ATTRIBUTION_AUDIT_PATH';
|
|
74
80
|
const CACHE_RETENTION_ENV = 'PI_CACHE_RETENTION';
|
|
81
|
+
export const ANTHROPIC_ACCOUNT_CONFIG_PATH_ENV = 'PI_ANTHROPIC_ACCOUNT_CONFIG_PATH';
|
|
75
82
|
export const ANTHROPIC_CACHE_RETENTION_ENTRY = 'pipeline-anthropic-cache-retention';
|
|
76
83
|
const ANTHROPIC_CACHE_RETENTION_SCHEMA = 'pipeline.anthropic_cache_retention.v1';
|
|
77
84
|
export const ANTHROPIC_ATTRIBUTION_CLAIM_CHANNEL = 'pi-anthropic-attribution:claim:v1';
|
|
@@ -146,8 +153,11 @@ interface PiModelCostLike extends PiCostRatesLike {
|
|
|
146
153
|
export interface PiModelLike {
|
|
147
154
|
readonly provider?: string;
|
|
148
155
|
readonly id?: string;
|
|
156
|
+
readonly name?: string;
|
|
149
157
|
readonly api?: string;
|
|
150
158
|
readonly baseUrl?: string;
|
|
159
|
+
readonly input?: readonly ('text' | 'image')[];
|
|
160
|
+
readonly contextWindow?: number;
|
|
151
161
|
readonly maxTokens?: number;
|
|
152
162
|
readonly reasoning?: boolean;
|
|
153
163
|
readonly compat?: {
|
|
@@ -394,7 +404,11 @@ interface PiAssistantDiagnosticLike {
|
|
|
394
404
|
}
|
|
395
405
|
|
|
396
406
|
type PiMessage =
|
|
397
|
-
| {
|
|
407
|
+
| {
|
|
408
|
+
readonly role: 'user';
|
|
409
|
+
readonly content: string | readonly PiContentBlock[];
|
|
410
|
+
readonly timestamp?: number;
|
|
411
|
+
}
|
|
398
412
|
| {
|
|
399
413
|
readonly role: 'assistant';
|
|
400
414
|
readonly content: readonly JsonObject[];
|
|
@@ -404,6 +418,7 @@ type PiMessage =
|
|
|
404
418
|
readonly responseId?: string;
|
|
405
419
|
readonly stopReason?: string;
|
|
406
420
|
readonly diagnostics?: readonly PiAssistantDiagnosticLike[];
|
|
421
|
+
readonly timestamp?: number;
|
|
407
422
|
}
|
|
408
423
|
| {
|
|
409
424
|
readonly role: 'toolResult';
|
|
@@ -411,6 +426,7 @@ type PiMessage =
|
|
|
411
426
|
readonly toolName?: string;
|
|
412
427
|
readonly content: readonly PiContentBlock[];
|
|
413
428
|
readonly isError?: boolean;
|
|
429
|
+
readonly timestamp?: number;
|
|
414
430
|
};
|
|
415
431
|
|
|
416
432
|
export interface PiStreamContext {
|
|
@@ -449,8 +465,12 @@ export interface PiSimpleStreamOptions {
|
|
|
449
465
|
) => Promise<void> | void;
|
|
450
466
|
}
|
|
451
467
|
|
|
468
|
+
export type HostAnthropicMessagesApiFactory =
|
|
469
|
+
typeof import('@earendil-works/pi-ai/compat').anthropicMessagesApi;
|
|
470
|
+
|
|
452
471
|
export interface AnthropicTransportDependencies {
|
|
453
472
|
readonly loadAccount?: () => ClaudeAttributionAccount;
|
|
473
|
+
readonly hostAnthropicMessagesApi?: HostAnthropicMessagesApiFactory;
|
|
454
474
|
}
|
|
455
475
|
|
|
456
476
|
export interface AssistantMessageLike {
|
|
@@ -465,14 +485,20 @@ export interface AssistantMessageLike {
|
|
|
465
485
|
cacheRead: number;
|
|
466
486
|
cacheWrite: number;
|
|
467
487
|
cacheWrite1h?: number;
|
|
488
|
+
reasoning?: number;
|
|
468
489
|
totalTokens: number;
|
|
469
490
|
cost: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number };
|
|
470
491
|
};
|
|
471
|
-
stopReason: 'stop' | 'length' | 'toolUse' | 'aborted' | 'error';
|
|
492
|
+
stopReason: 'pending' | 'stop' | 'length' | 'toolUse' | 'aborted' | 'error' | 'deferred';
|
|
472
493
|
timestamp: number;
|
|
494
|
+
responseModel?: string;
|
|
473
495
|
responseId?: string;
|
|
496
|
+
providerThinkingLevel?: string;
|
|
497
|
+
endTurn?: boolean;
|
|
474
498
|
diagnostics?: PiAssistantDiagnosticLike[];
|
|
499
|
+
deferred?: unknown;
|
|
475
500
|
errorMessage?: string;
|
|
501
|
+
rawStopReason?: string;
|
|
476
502
|
}
|
|
477
503
|
|
|
478
504
|
type AssistantMessageEvent =
|
|
@@ -862,20 +888,43 @@ export function extractClaudeAttributionAccount(
|
|
|
862
888
|
};
|
|
863
889
|
}
|
|
864
890
|
|
|
891
|
+
export function resolveClaudeAttributionConfigPath(
|
|
892
|
+
explicitConfigPath?: string,
|
|
893
|
+
env?: ProviderEnv,
|
|
894
|
+
homeDirectory = homedir(),
|
|
895
|
+
): string {
|
|
896
|
+
const configuredPath = providerEnvValue(ANTHROPIC_ACCOUNT_CONFIG_PATH_ENV, env);
|
|
897
|
+
const selectedPath = explicitConfigPath ?? configuredPath ?? join(homeDirectory, '.claude.json');
|
|
898
|
+
const source =
|
|
899
|
+
explicitConfigPath !== undefined
|
|
900
|
+
? 'explicit configPath'
|
|
901
|
+
: configuredPath !== undefined
|
|
902
|
+
? ANTHROPIC_ACCOUNT_CONFIG_PATH_ENV
|
|
903
|
+
: 'default ~/.claude.json path';
|
|
904
|
+
if (selectedPath.trim().length === 0 || !isAbsolute(selectedPath)) {
|
|
905
|
+
throw new Error(
|
|
906
|
+
`Anthropic attribution ${source} must be a non-empty absolute file path; got ${JSON.stringify(selectedPath)}`,
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
return selectedPath;
|
|
910
|
+
}
|
|
911
|
+
|
|
865
912
|
export function loadClaudeAttributionAccount(
|
|
866
|
-
configPath
|
|
913
|
+
configPath?: string,
|
|
914
|
+
env?: ProviderEnv,
|
|
867
915
|
): ClaudeAttributionAccount {
|
|
916
|
+
const selectedPath = resolveClaudeAttributionConfigPath(configPath, env);
|
|
868
917
|
let configText: string;
|
|
869
918
|
try {
|
|
870
|
-
configText = readFileSync(
|
|
919
|
+
configText = readFileSync(selectedPath, 'utf8');
|
|
871
920
|
} catch (error) {
|
|
872
921
|
throw new Error(
|
|
873
|
-
`Anthropic attribution config ${
|
|
922
|
+
`Anthropic attribution config ${selectedPath} could not be read: ${error instanceof Error ? error.message : String(error)}`,
|
|
874
923
|
);
|
|
875
924
|
}
|
|
876
925
|
return extractClaudeAttributionAccount(
|
|
877
|
-
parseJsonValue(configText, `Anthropic attribution config ${
|
|
878
|
-
|
|
926
|
+
parseJsonValue(configText, `Anthropic attribution config ${selectedPath}`),
|
|
927
|
+
selectedPath,
|
|
879
928
|
);
|
|
880
929
|
}
|
|
881
930
|
|
|
@@ -1414,6 +1463,10 @@ function isSha256(value: unknown): value is string {
|
|
|
1414
1463
|
return typeof value === 'string' && /^[a-f0-9]{64}$/u.test(value);
|
|
1415
1464
|
}
|
|
1416
1465
|
|
|
1466
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
1467
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1417
1470
|
function blockWithoutCacheControl(block: unknown): unknown {
|
|
1418
1471
|
if (!isPlainObject(block)) return block;
|
|
1419
1472
|
const output = { ...block };
|
|
@@ -1466,31 +1519,82 @@ function parseAnthropicLineageDetails(
|
|
|
1466
1519
|
.find((candidate) => candidate.type === ANTHROPIC_LINEAGE_DIAGNOSTIC_TYPE);
|
|
1467
1520
|
const details = diagnostic?.details;
|
|
1468
1521
|
if (!isPlainObject(details)) return undefined;
|
|
1522
|
+
|
|
1523
|
+
const sourceModel = details['source_model'];
|
|
1524
|
+
const responseId = details['response_id'];
|
|
1525
|
+
const assistantContentSha256 = details['assistant_content_sha256'];
|
|
1526
|
+
const conversationStaticSha256 = details['conversation_static_sha256'];
|
|
1527
|
+
const requestMessageCount = details['request_message_count'];
|
|
1528
|
+
const requestMessagesSha256 = details['request_messages_sha256'];
|
|
1529
|
+
const cacheProfileSha256 = details['cache_profile_sha256'];
|
|
1530
|
+
const cacheRetention = details['cache_retention'];
|
|
1531
|
+
const compactionBoundarySha256 = details['compaction_boundary_sha256'];
|
|
1532
|
+
const signatureEpochSha256 = details['signature_epoch_sha256'];
|
|
1533
|
+
const signatureEpochInheritsPrior = details['signature_epoch_inherits_prior'];
|
|
1534
|
+
const previousMessageId = details['previous_message_id'];
|
|
1469
1535
|
if (
|
|
1470
1536
|
details['schema_version'] !== ANTHROPIC_LINEAGE_SCHEMA ||
|
|
1471
1537
|
details['projection_version'] !== ANTHROPIC_PROJECTION_VERSION ||
|
|
1472
1538
|
details['source_provider'] !== 'anthropic' ||
|
|
1473
1539
|
details['source_api'] !== 'anthropic-messages' ||
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
!isSha256(
|
|
1477
|
-
!isSha256(
|
|
1478
|
-
|
|
1479
|
-
(
|
|
1480
|
-
|
|
1481
|
-
!isSha256(
|
|
1482
|
-
(
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
(
|
|
1486
|
-
|
|
1487
|
-
!
|
|
1488
|
-
typeof details['signature_epoch_inherits_prior'] !== 'boolean' ||
|
|
1489
|
-
(details['previous_message_id'] !== null && typeof details['previous_message_id'] !== 'string')
|
|
1540
|
+
!isNonEmptyString(sourceModel) ||
|
|
1541
|
+
!isNonEmptyString(responseId) ||
|
|
1542
|
+
!isSha256(assistantContentSha256) ||
|
|
1543
|
+
!isSha256(conversationStaticSha256) ||
|
|
1544
|
+
typeof requestMessageCount !== 'number' ||
|
|
1545
|
+
!Number.isSafeInteger(requestMessageCount) ||
|
|
1546
|
+
requestMessageCount < 0 ||
|
|
1547
|
+
!isSha256(requestMessagesSha256) ||
|
|
1548
|
+
!isSha256(cacheProfileSha256) ||
|
|
1549
|
+
(cacheRetention !== 'none' && cacheRetention !== 'short' && cacheRetention !== 'long') ||
|
|
1550
|
+
(compactionBoundarySha256 !== null && !isSha256(compactionBoundarySha256)) ||
|
|
1551
|
+
!isSha256(signatureEpochSha256) ||
|
|
1552
|
+
typeof signatureEpochInheritsPrior !== 'boolean' ||
|
|
1553
|
+
(previousMessageId !== null && !isNonEmptyString(previousMessageId))
|
|
1490
1554
|
) {
|
|
1491
1555
|
return undefined;
|
|
1492
1556
|
}
|
|
1493
|
-
|
|
1557
|
+
|
|
1558
|
+
return {
|
|
1559
|
+
schema_version: ANTHROPIC_LINEAGE_SCHEMA,
|
|
1560
|
+
projection_version: ANTHROPIC_PROJECTION_VERSION,
|
|
1561
|
+
source_provider: 'anthropic',
|
|
1562
|
+
source_api: 'anthropic-messages',
|
|
1563
|
+
source_model: sourceModel,
|
|
1564
|
+
response_id: responseId,
|
|
1565
|
+
assistant_content_sha256: assistantContentSha256,
|
|
1566
|
+
conversation_static_sha256: conversationStaticSha256,
|
|
1567
|
+
request_message_count: requestMessageCount,
|
|
1568
|
+
request_messages_sha256: requestMessagesSha256,
|
|
1569
|
+
cache_profile_sha256: cacheProfileSha256,
|
|
1570
|
+
cache_retention: cacheRetention,
|
|
1571
|
+
compaction_boundary_sha256: compactionBoundarySha256,
|
|
1572
|
+
signature_epoch_sha256: signatureEpochSha256,
|
|
1573
|
+
signature_epoch_inherits_prior: signatureEpochInheritsPrior,
|
|
1574
|
+
previous_message_id: previousMessageId,
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
function isSuccessfulLineageStopReason(
|
|
1579
|
+
stopReason: string | undefined,
|
|
1580
|
+
): stopReason is 'stop' | 'length' | 'toolUse' {
|
|
1581
|
+
return stopReason === 'stop' || stopReason === 'length' || stopReason === 'toolUse';
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
function lineageBindsContainingAssistant(
|
|
1585
|
+
message: Extract<PiMessage, { role: 'assistant' }>,
|
|
1586
|
+
lineage: AnthropicLineageDetails,
|
|
1587
|
+
): boolean {
|
|
1588
|
+
return (
|
|
1589
|
+
message.provider === lineage.source_provider &&
|
|
1590
|
+
message.api === lineage.source_api &&
|
|
1591
|
+
isNonEmptyString(message.model) &&
|
|
1592
|
+
message.model === lineage.source_model &&
|
|
1593
|
+
isSuccessfulLineageStopReason(message.stopReason) &&
|
|
1594
|
+
isNonEmptyString(message.responseId) &&
|
|
1595
|
+
message.responseId === lineage.response_id &&
|
|
1596
|
+
lineage.assistant_content_sha256 === assistantContentHash(message.content)
|
|
1597
|
+
);
|
|
1494
1598
|
}
|
|
1495
1599
|
|
|
1496
1600
|
function canTargetReadAnthropicThinking(sourceModel: string, targetModel: string): boolean {
|
|
@@ -1529,11 +1633,25 @@ function resolveSignatureEpochPolicy(
|
|
|
1529
1633
|
compactionBoundarySha256: string | null,
|
|
1530
1634
|
): SignatureEpochPolicy {
|
|
1531
1635
|
const targetModelId = normalizedAnthropicModelId(model);
|
|
1532
|
-
const
|
|
1533
|
-
if (
|
|
1636
|
+
const latestState = targetLineageState({ messages }, targetModelId);
|
|
1637
|
+
if (latestState.kind === 'none') {
|
|
1534
1638
|
return initialSignatureEpochPolicy(targetModelId, cacheRetention, compactionBoundarySha256);
|
|
1535
1639
|
}
|
|
1640
|
+
if (latestState.kind === 'untrusted') {
|
|
1641
|
+
return {
|
|
1642
|
+
sha256: sha256Canonical({
|
|
1643
|
+
projection_version: ANTHROPIC_PROJECTION_VERSION,
|
|
1644
|
+
kind: 'untrusted-lineage-reset',
|
|
1645
|
+
target_model: targetModelId,
|
|
1646
|
+
cache_retention: cacheRetention,
|
|
1647
|
+
compaction_boundary_sha256: compactionBoundarySha256,
|
|
1648
|
+
latest_assistant_sha256: latestState.assistantSha256,
|
|
1649
|
+
}),
|
|
1650
|
+
inheritsPrior: false,
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1536
1653
|
|
|
1654
|
+
const latest = latestState.lineage;
|
|
1537
1655
|
const retentionChanged = latest.cache_retention !== cacheRetention;
|
|
1538
1656
|
const declaredNewCompaction =
|
|
1539
1657
|
compactionBoundarySha256 !== null &&
|
|
@@ -1565,22 +1683,12 @@ function isTrustedReplayableAnthropicAssistant(
|
|
|
1565
1683
|
conversationStaticSha256: string,
|
|
1566
1684
|
wireMessagesBeforeAssistant: readonly JsonObject[],
|
|
1567
1685
|
): boolean {
|
|
1568
|
-
if (
|
|
1569
|
-
message.provider !== 'anthropic' ||
|
|
1570
|
-
message.api !== 'anthropic-messages' ||
|
|
1571
|
-
typeof message.model !== 'string' ||
|
|
1572
|
-
message.stopReason === 'error' ||
|
|
1573
|
-
message.stopReason === 'aborted' ||
|
|
1574
|
-
!canTargetReadAnthropicThinking(message.model, normalizedAnthropicModelId(targetModel))
|
|
1575
|
-
) {
|
|
1576
|
-
return false;
|
|
1577
|
-
}
|
|
1686
|
+
if (typeof message.model !== 'string') return false;
|
|
1578
1687
|
const lineage = parseAnthropicLineageDetails(message);
|
|
1579
1688
|
if (
|
|
1580
1689
|
lineage === undefined ||
|
|
1581
|
-
lineage
|
|
1582
|
-
|
|
1583
|
-
lineage.assistant_content_sha256 !== assistantContentHash(message.content) ||
|
|
1690
|
+
!lineageBindsContainingAssistant(message, lineage) ||
|
|
1691
|
+
!canTargetReadAnthropicThinking(message.model, normalizedAnthropicModelId(targetModel)) ||
|
|
1584
1692
|
lineage.cache_retention !== targetCacheRetention ||
|
|
1585
1693
|
lineage.conversation_static_sha256 !== conversationStaticSha256 ||
|
|
1586
1694
|
lineage.request_message_count !== wireMessagesBeforeAssistant.length ||
|
|
@@ -1858,12 +1966,20 @@ export function buildAnthropicRequestParams(
|
|
|
1858
1966
|
context: PiStreamContext,
|
|
1859
1967
|
options?: PiSimpleStreamOptions,
|
|
1860
1968
|
): JsonObject {
|
|
1969
|
+
return buildAnthropicRequest(model, context, options).params;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
function buildAnthropicRequest(
|
|
1973
|
+
model: PiModelLike,
|
|
1974
|
+
context: PiStreamContext,
|
|
1975
|
+
options?: PiSimpleStreamOptions,
|
|
1976
|
+
): { readonly params: JsonObject; readonly signatureEpoch: SignatureEpochPolicy } {
|
|
1861
1977
|
const policy = resolveClaudeCodeModelPolicy(model);
|
|
1862
1978
|
const maxTokens = resolveAnthropicMaxTokens(model);
|
|
1863
1979
|
const cacheControl = resolveAnthropicCacheControl(model, options);
|
|
1864
1980
|
const cacheRetention: CacheRetention =
|
|
1865
1981
|
cacheControl === undefined ? 'none' : cacheControl.ttl === '1h' ? 'long' : 'short';
|
|
1866
|
-
|
|
1982
|
+
let signatureEpoch = resolveSignatureEpochPolicy(
|
|
1867
1983
|
model,
|
|
1868
1984
|
context.messages,
|
|
1869
1985
|
cacheRetention,
|
|
@@ -1934,8 +2050,37 @@ export function buildAnthropicRequestParams(
|
|
|
1934
2050
|
params['thinking'] = { type: 'disabled' };
|
|
1935
2051
|
params['temperature'] = options?.temperature ?? 1;
|
|
1936
2052
|
}
|
|
2053
|
+
const previous = latestLineageForTarget(context, normalizedAnthropicModelId(model));
|
|
2054
|
+
if (
|
|
2055
|
+
previous !== undefined &&
|
|
2056
|
+
signatureEpoch.sha256 === previous.signature_epoch_sha256 &&
|
|
2057
|
+
(previous.cache_profile_sha256 !== cacheProfileHash(model, policy, params, staticSha256) ||
|
|
2058
|
+
!hasLineageRequestPrefix(requestMessagesFromPayload(params), previous))
|
|
2059
|
+
) {
|
|
2060
|
+
// Resume/reload and profile edits can invalidate a lane. Re-project before transport,
|
|
2061
|
+
// rather than just clearing the response ID while retaining prefix-bound signatures.
|
|
2062
|
+
signatureEpoch = {
|
|
2063
|
+
sha256: sha256Canonical({
|
|
2064
|
+
projection_version: ANTHROPIC_PROJECTION_VERSION,
|
|
2065
|
+
kind: 'lineage-reset',
|
|
2066
|
+
previous_signature_epoch_sha256: previous.signature_epoch_sha256,
|
|
2067
|
+
previous_response_id: previous.response_id,
|
|
2068
|
+
cache_profile_sha256: cacheProfileHash(model, policy, params, staticSha256),
|
|
2069
|
+
request_messages_sha256: promptMessagesHash(requestMessagesFromPayload(params)),
|
|
2070
|
+
}),
|
|
2071
|
+
inheritsPrior: false,
|
|
2072
|
+
};
|
|
2073
|
+
params['messages'] = convertMessages(
|
|
2074
|
+
model,
|
|
2075
|
+
context.messages,
|
|
2076
|
+
staticSha256,
|
|
2077
|
+
cacheRetention,
|
|
2078
|
+
signatureEpoch,
|
|
2079
|
+
cacheControl,
|
|
2080
|
+
);
|
|
2081
|
+
}
|
|
1937
2082
|
assertCacheControlBreakpointLimit(params);
|
|
1938
|
-
return params;
|
|
2083
|
+
return { params, signatureEpoch };
|
|
1939
2084
|
}
|
|
1940
2085
|
|
|
1941
2086
|
interface PreparedAnthropicLineage {
|
|
@@ -2001,24 +2146,68 @@ function cacheRetentionFromPayload(payload: JsonObject): CacheRetention {
|
|
|
2001
2146
|
return inspectCacheControls(payload).retention ?? 'none';
|
|
2002
2147
|
}
|
|
2003
2148
|
|
|
2004
|
-
|
|
2149
|
+
type TargetLineageState =
|
|
2150
|
+
| { readonly kind: 'none' }
|
|
2151
|
+
| { readonly kind: 'trusted'; readonly lineage: AnthropicLineageDetails }
|
|
2152
|
+
| { readonly kind: 'untrusted'; readonly assistantSha256: string };
|
|
2153
|
+
|
|
2154
|
+
function untrustedAssistantSha256(
|
|
2155
|
+
message: Extract<PiMessage, { role: 'assistant' }>,
|
|
2156
|
+
): string {
|
|
2157
|
+
return sha256Canonical({
|
|
2158
|
+
provider: message.provider ?? null,
|
|
2159
|
+
api: message.api ?? null,
|
|
2160
|
+
model: message.model ?? null,
|
|
2161
|
+
response_id: message.responseId ?? null,
|
|
2162
|
+
stop_reason: message.stopReason ?? null,
|
|
2163
|
+
assistant_content_sha256: assistantContentHash(message.content),
|
|
2164
|
+
lineage_diagnostics: (message.diagnostics ?? []).filter(
|
|
2165
|
+
(diagnostic) => diagnostic.type === ANTHROPIC_LINEAGE_DIAGNOSTIC_TYPE,
|
|
2166
|
+
),
|
|
2167
|
+
});
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
function targetLineageState(
|
|
2005
2171
|
context: PiStreamContext,
|
|
2006
2172
|
targetModelId: string,
|
|
2007
|
-
):
|
|
2173
|
+
): TargetLineageState {
|
|
2008
2174
|
for (let index = context.messages.length - 1; index >= 0; index -= 1) {
|
|
2009
2175
|
const message = context.messages[index];
|
|
2176
|
+
if (message?.role !== 'assistant') continue;
|
|
2177
|
+
|
|
2178
|
+
const lineageDiagnostics = (message.diagnostics ?? []).filter(
|
|
2179
|
+
(diagnostic) => diagnostic.type === ANTHROPIC_LINEAGE_DIAGNOSTIC_TYPE,
|
|
2180
|
+
);
|
|
2181
|
+
const latestRawDetails = lineageDiagnostics.at(-1)?.details;
|
|
2182
|
+
const receiptClaimsTarget = latestRawDetails?.['source_model'] === targetModelId;
|
|
2183
|
+
const assistantClaimsTarget = message.model === targetModelId;
|
|
2184
|
+
if (!assistantClaimsTarget && !receiptClaimsTarget) continue;
|
|
2185
|
+
|
|
2010
2186
|
if (
|
|
2011
|
-
message
|
|
2012
|
-
|
|
2013
|
-
message.stopReason === 'error' ||
|
|
2014
|
-
message.stopReason === 'aborted'
|
|
2187
|
+
(message.stopReason === 'error' || message.stopReason === 'aborted') &&
|
|
2188
|
+
lineageDiagnostics.length === 0
|
|
2015
2189
|
) {
|
|
2016
2190
|
continue;
|
|
2017
2191
|
}
|
|
2192
|
+
|
|
2018
2193
|
const lineage = parseAnthropicLineageDetails(message);
|
|
2019
|
-
|
|
2194
|
+
if (
|
|
2195
|
+
lineage?.source_model === targetModelId &&
|
|
2196
|
+
lineageBindsContainingAssistant(message, lineage)
|
|
2197
|
+
) {
|
|
2198
|
+
return { kind: 'trusted', lineage };
|
|
2199
|
+
}
|
|
2200
|
+
return { kind: 'untrusted', assistantSha256: untrustedAssistantSha256(message) };
|
|
2020
2201
|
}
|
|
2021
|
-
return
|
|
2202
|
+
return { kind: 'none' };
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
function latestLineageForTarget(
|
|
2206
|
+
context: PiStreamContext,
|
|
2207
|
+
targetModelId: string,
|
|
2208
|
+
): AnthropicLineageDetails | undefined {
|
|
2209
|
+
const state = targetLineageState(context, targetModelId);
|
|
2210
|
+
return state.kind === 'trusted' ? state.lineage : undefined;
|
|
2022
2211
|
}
|
|
2023
2212
|
|
|
2024
2213
|
function declaredCompactionBoundarySha256(messages: readonly JsonObject[]): string | null {
|
|
@@ -2035,11 +2224,23 @@ function declaredCompactionBoundarySha256(messages: readonly JsonObject[]): stri
|
|
|
2035
2224
|
return sha256Canonical(promptMessagesWithoutCacheControls([firstMessage])[0]);
|
|
2036
2225
|
}
|
|
2037
2226
|
|
|
2227
|
+
function hasLineageRequestPrefix(
|
|
2228
|
+
messages: readonly JsonObject[],
|
|
2229
|
+
previous: AnthropicLineageDetails,
|
|
2230
|
+
): boolean {
|
|
2231
|
+
return (
|
|
2232
|
+
messages.length >= previous.request_message_count &&
|
|
2233
|
+
promptMessagesHash(messages.slice(0, previous.request_message_count)) ===
|
|
2234
|
+
previous.request_messages_sha256
|
|
2235
|
+
);
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2038
2238
|
function prepareAnthropicLineageDetails(args: {
|
|
2039
2239
|
readonly model: PiModelLike;
|
|
2040
2240
|
readonly policy: ClaudeCodeModelPolicy;
|
|
2041
2241
|
readonly context: PiStreamContext;
|
|
2042
2242
|
readonly payload: JsonObject;
|
|
2243
|
+
readonly signatureEpoch: SignatureEpochPolicy;
|
|
2043
2244
|
}): PreparedAnthropicLineage['details'] {
|
|
2044
2245
|
const targetModelId = normalizedAnthropicModelId(args.model);
|
|
2045
2246
|
const messages = requestMessagesFromPayload(args.payload);
|
|
@@ -2050,37 +2251,16 @@ function prepareAnthropicLineageDetails(args: {
|
|
|
2050
2251
|
const profileSha256 = cacheProfileHash(args.model, args.policy, args.payload, staticSha256);
|
|
2051
2252
|
const cacheRetention = cacheRetentionFromPayload(args.payload);
|
|
2052
2253
|
const compactionBoundarySha256 = declaredCompactionBoundarySha256(messages);
|
|
2053
|
-
const signatureEpoch =
|
|
2054
|
-
args.model,
|
|
2055
|
-
args.context.messages,
|
|
2056
|
-
cacheRetention,
|
|
2057
|
-
compactionBoundarySha256,
|
|
2058
|
-
);
|
|
2254
|
+
const signatureEpoch = args.signatureEpoch;
|
|
2059
2255
|
let previous = latestLineageForTarget(args.context, targetModelId);
|
|
2060
|
-
if (
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
compactionBoundarySha256 !== previous.compaction_boundary_sha256
|
|
2069
|
-
) {
|
|
2070
|
-
previous = undefined;
|
|
2071
|
-
} else {
|
|
2072
|
-
if (!prefixStillExists) {
|
|
2073
|
-
throw new Error(
|
|
2074
|
-
'Anthropic cache lineage diverged before transport: message history is not append-only',
|
|
2075
|
-
);
|
|
2076
|
-
}
|
|
2077
|
-
if (previous.cache_profile_sha256 !== profileSha256) {
|
|
2078
|
-
throw new Error(
|
|
2079
|
-
'Anthropic cache lineage diverged before transport: model/system/tools/thinking/beta profile changed',
|
|
2080
|
-
);
|
|
2081
|
-
}
|
|
2082
|
-
if (previous.cache_retention !== cacheRetention) previous = undefined;
|
|
2083
|
-
}
|
|
2256
|
+
if (
|
|
2257
|
+
previous !== undefined &&
|
|
2258
|
+
(signatureEpoch.sha256 !== previous.signature_epoch_sha256 ||
|
|
2259
|
+
!hasLineageRequestPrefix(messages, previous) ||
|
|
2260
|
+
previous.cache_profile_sha256 !== profileSha256 ||
|
|
2261
|
+
previous.cache_retention !== cacheRetention)
|
|
2262
|
+
) {
|
|
2263
|
+
previous = undefined;
|
|
2084
2264
|
}
|
|
2085
2265
|
return {
|
|
2086
2266
|
schema_version: ANTHROPIC_LINEAGE_SCHEMA,
|
|
@@ -2109,6 +2289,7 @@ class AnthropicLineageCoordinator {
|
|
|
2109
2289
|
readonly policy: ClaudeCodeModelPolicy;
|
|
2110
2290
|
readonly context: PiStreamContext;
|
|
2111
2291
|
readonly payload: JsonObject;
|
|
2292
|
+
readonly signatureEpoch: SignatureEpochPolicy;
|
|
2112
2293
|
}): PreparedAnthropicLineage {
|
|
2113
2294
|
const targetModelId = normalizedAnthropicModelId(args.model);
|
|
2114
2295
|
const key = `${args.sessionId}\u0000${targetModelId}`;
|
|
@@ -2136,6 +2317,14 @@ export function createAnthropicLineageDiagnostic(args: {
|
|
|
2136
2317
|
readonly requestPayload: JsonObject;
|
|
2137
2318
|
readonly previousMessageId?: string | null;
|
|
2138
2319
|
}): PiAssistantDiagnosticLike {
|
|
2320
|
+
if (!isNonEmptyString(args.responseId)) {
|
|
2321
|
+
throw new Error('Anthropic lineage diagnostic requires a non-empty response ID');
|
|
2322
|
+
}
|
|
2323
|
+
if (args.previousMessageId !== undefined && args.previousMessageId !== null) {
|
|
2324
|
+
if (!isNonEmptyString(args.previousMessageId)) {
|
|
2325
|
+
throw new Error('Anthropic lineage diagnostic requires a non-empty previous response ID');
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2139
2328
|
const policy = resolveClaudeCodeModelPolicy(args.model);
|
|
2140
2329
|
const messages = requestMessagesFromPayload(args.requestPayload);
|
|
2141
2330
|
const staticSha256 = conversationStaticHash(
|
|
@@ -2177,7 +2366,7 @@ function appendLineageDiagnostic(
|
|
|
2177
2366
|
output: AssistantMessageLike,
|
|
2178
2367
|
prepared: PreparedAnthropicLineage,
|
|
2179
2368
|
): void {
|
|
2180
|
-
if (
|
|
2369
|
+
if (!isNonEmptyString(output.responseId)) {
|
|
2181
2370
|
throw new Error(
|
|
2182
2371
|
'Anthropic attribution successful response is missing responseId lineage proof',
|
|
2183
2372
|
);
|
|
@@ -2488,33 +2677,37 @@ function createOutput(model: PiModelLike): AssistantMessageLike {
|
|
|
2488
2677
|
};
|
|
2489
2678
|
}
|
|
2490
2679
|
|
|
2491
|
-
|
|
2680
|
+
type HostForwardingModel = PiModelLike & HostModel<'anthropic-messages'>;
|
|
2681
|
+
type HostForwardingContext = PiStreamContext & HostContext;
|
|
2682
|
+
type HostForwardingOptions = PiSimpleStreamOptions & HostSimpleStreamOptions;
|
|
2683
|
+
type HostForwardingStream = AssistantMessageEventStreamLike & HostAssistantMessageEventStream;
|
|
2684
|
+
|
|
2685
|
+
function forwardToBuiltInAnthropic(
|
|
2492
2686
|
model: PiModelLike,
|
|
2493
2687
|
context: PiStreamContext,
|
|
2494
2688
|
options: PiSimpleStreamOptions | undefined,
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
) => AssistantMessageEventStreamLike;
|
|
2507
|
-
}>;
|
|
2508
|
-
const mod = await dynamicImport('@earendil-works/pi-ai/anthropic');
|
|
2509
|
-
const delegated = mod.streamSimpleAnthropic(model, context, options);
|
|
2510
|
-
for await (const event of delegated) stream.push(event);
|
|
2511
|
-
stream.end(await delegated.result());
|
|
2512
|
-
} catch (error) {
|
|
2513
|
-
output.stopReason = 'error';
|
|
2514
|
-
output.errorMessage = `Anthropic attribution could not delegate non-target provider ${JSON.stringify(model.provider)}: ${error instanceof Error ? error.message : String(error)}`;
|
|
2515
|
-
stream.push({ type: 'error', reason: 'error', error: output });
|
|
2516
|
-
stream.end();
|
|
2689
|
+
dependencies: AnthropicTransportDependencies,
|
|
2690
|
+
): AssistantMessageEventStreamLike {
|
|
2691
|
+
// The compiled gateway resolves this host-owned adapter through Pi's alias-aware
|
|
2692
|
+
// extension loader and injects it across the lazy native-import boundary. Keeping
|
|
2693
|
+
// the deferred core free of runtime Pi package imports prevents Node from trying to
|
|
2694
|
+
// resolve a private @earendil-works/pi-ai installation beside a managed package.
|
|
2695
|
+
const hostAnthropicMessagesApi = dependencies.hostAnthropicMessagesApi;
|
|
2696
|
+
if (hostAnthropicMessagesApi === undefined) {
|
|
2697
|
+
throw new Error(
|
|
2698
|
+
"pi_anthropic_attribution_host_adapter_missing: the extension gateway did not inject Pi's anthropic-messages adapter",
|
|
2699
|
+
);
|
|
2517
2700
|
}
|
|
2701
|
+
|
|
2702
|
+
// The host owns both its legacy Context or normalized TranscriptContext input and
|
|
2703
|
+
// the complete stream/event/result shape. These intersections only mark that
|
|
2704
|
+
// host-owned boundary; no request, callback, event, or result is reconstructed.
|
|
2705
|
+
const delegated = hostAnthropicMessagesApi().streamSimple(
|
|
2706
|
+
model as HostForwardingModel,
|
|
2707
|
+
context as HostForwardingContext,
|
|
2708
|
+
options as HostForwardingOptions | undefined,
|
|
2709
|
+
);
|
|
2710
|
+
return delegated as HostForwardingStream;
|
|
2518
2711
|
}
|
|
2519
2712
|
|
|
2520
2713
|
export function streamAnthropicViaBetaMessages(
|
|
@@ -2523,14 +2716,12 @@ export function streamAnthropicViaBetaMessages(
|
|
|
2523
2716
|
options?: PiSimpleStreamOptions,
|
|
2524
2717
|
dependencies: AnthropicTransportDependencies = {},
|
|
2525
2718
|
): AssistantMessageEventStreamLike {
|
|
2526
|
-
const stream = createAssistantMessageEventStream();
|
|
2527
|
-
const output = createOutput(model);
|
|
2528
|
-
|
|
2529
2719
|
if (model.provider !== 'anthropic') {
|
|
2530
|
-
|
|
2531
|
-
return stream;
|
|
2720
|
+
return forwardToBuiltInAnthropic(model, context, options, dependencies);
|
|
2532
2721
|
}
|
|
2533
2722
|
|
|
2723
|
+
const stream = createAssistantMessageEventStream();
|
|
2724
|
+
const output = createOutput(model);
|
|
2534
2725
|
void (async () => {
|
|
2535
2726
|
let preparedLineage: PreparedAnthropicLineage | undefined;
|
|
2536
2727
|
try {
|
|
@@ -2548,11 +2739,12 @@ export function streamAnthropicViaBetaMessages(
|
|
|
2548
2739
|
|
|
2549
2740
|
const sessionId = requireSessionId(options?.sessionId, 'options.sessionId');
|
|
2550
2741
|
const account = requireAttributionAccount(
|
|
2551
|
-
|
|
2742
|
+
dependencies.loadAccount?.() ?? loadClaudeAttributionAccount(undefined, options?.env),
|
|
2552
2743
|
);
|
|
2553
2744
|
const url = resolveAnthropicBetaMessagesUrl(model);
|
|
2554
2745
|
const policy = resolveClaudeCodeModelPolicy(model);
|
|
2555
|
-
|
|
2746
|
+
const request = buildAnthropicRequest(model, context, options);
|
|
2747
|
+
let params = request.params;
|
|
2556
2748
|
const billingSystemText = buildClaudeCodeBillingSystemText(
|
|
2557
2749
|
firstUserMessageTextFromPayload(params),
|
|
2558
2750
|
);
|
|
@@ -2561,6 +2753,7 @@ export function streamAnthropicViaBetaMessages(
|
|
|
2561
2753
|
policy,
|
|
2562
2754
|
context,
|
|
2563
2755
|
payload: params,
|
|
2756
|
+
signatureEpoch: request.signatureEpoch,
|
|
2564
2757
|
});
|
|
2565
2758
|
if (policy.supportsCacheDiagnostics) {
|
|
2566
2759
|
if (!policy.beta.split(',').includes(ANTHROPIC_CACHE_DIAGNOSTICS_BETA)) {
|
|
@@ -2608,6 +2801,7 @@ export function streamAnthropicViaBetaMessages(
|
|
|
2608
2801
|
policy,
|
|
2609
2802
|
context,
|
|
2610
2803
|
payload: params,
|
|
2804
|
+
signatureEpoch: request.signatureEpoch,
|
|
2611
2805
|
});
|
|
2612
2806
|
if (
|
|
2613
2807
|
preparedLineage.details.previous_message_id !== provisionalLineage.previous_message_id ||
|
|
@@ -2693,7 +2887,7 @@ export function streamAnthropicViaBetaMessages(
|
|
|
2693
2887
|
'Anthropic beta messages stream emitted malformed/duplicate message_start',
|
|
2694
2888
|
);
|
|
2695
2889
|
}
|
|
2696
|
-
if (
|
|
2890
|
+
if (!isNonEmptyString(event['message']['id'])) {
|
|
2697
2891
|
throw new Error('Anthropic beta messages message_start is missing a response id');
|
|
2698
2892
|
}
|
|
2699
2893
|
sawMessageStart = true;
|