pi-background-tasks 1.0.4 → 1.0.6

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.
@@ -6,17 +6,39 @@ import { createRequire } from 'node:module';
6
6
  import { dirname, resolve } from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
8
  import {
9
+ FUSION_CHILD_MAX_PROVIDER_REQUESTS,
10
+ FUSION_CHILD_MAX_TOOL_CALLS,
11
+ FUSION_CHILD_MIN_OUTPUT_RESERVE_TOKENS,
9
12
  FUSION_CHILD_RESULT_PREFIX,
10
13
  FUSION_CHILD_RESULT_SCHEMA_VERSION,
14
+ FUSION_CHILD_SAFETY_RESERVE_TOKENS,
15
+ FUSION_CHILD_SETTLEMENT_PREFIX,
16
+ FUSION_CHILD_SETTLEMENT_SCHEMA_VERSION,
11
17
  FUSION_RESEARCH_ENABLED_ENV,
18
+ FUSION_RUNTIME_GUARD_PREFIX,
19
+ FUSION_RUNTIME_GUARD_SCHEMA_VERSION,
12
20
  FUSION_SOURCE_POLICY_PATH_ENV,
13
21
  FUSION_SOURCE_POLICY_SHA256_ENV,
14
22
  FUSION_TOOL_CALL_LOG_PATH_ENV,
15
23
  FUSION_TOOL_CALL_SEAL_SCHEMA_VERSION,
16
24
  FUSION_TOOL_CALL_SEAL_SUFFIX,
17
25
  FUSION_CHILD_MAX_TOTAL_TOOL_RESULT_BYTES,
26
+ buildFusionChildSettlement,
27
+ isRecoverableFusionChildErrorRecord,
28
+ serializeFusionChildResultRecords,
18
29
  type FusionChildResultMetadata,
30
+ type FusionChildSettlementFailureReason,
31
+ type FusionChildSettlementRecord,
32
+ type FusionRuntimeGuardCode,
33
+ type FusionRuntimeGuardRecord,
19
34
  } from './child-protocol.js';
35
+ import {
36
+ FUSION_CLAUDE_CACHE_BREAKPOINT_LIMIT,
37
+ FUSION_CLAUDE_CACHE_OBSERVATION_SCHEMA_VERSION,
38
+ FUSION_CLAUDE_CACHE_RETENTION_ENV,
39
+ type FusionClaudeCacheObservation,
40
+ type FusionClaudeCacheRetention,
41
+ } from './claude-cache.js';
20
42
  import {
21
43
  FUSION_FORBIDDEN_TOOLS,
22
44
  FUSION_NO_TOOLS_CAPABILITY,
@@ -405,8 +427,9 @@ function fusionToolArgv(capability: FusionCapability): string[] {
405
427
  *
406
428
  * `--no-extensions` disables discovery but still honours explicit `--extension`
407
429
  * paths, so this list is the complete set a child receives. The metadata
408
- * extension is always present; the Anthropic sanitizer is appended only for
409
- * Claude routes, keeping non-Anthropic child argv byte-identical to before.
430
+ * extension is always present. For Claude routes the sanitizer loads first so
431
+ * the private runtime governor observes the final post-sanitizer payload; non-
432
+ * Anthropic child argv remains unchanged.
410
433
  */
411
434
  export function fusionChildExtensionPaths(
412
435
  model: ResolvedFusionModel,
@@ -414,7 +437,7 @@ export function fusionChildExtensionPaths(
414
437
  resolveSanitizer: () => string = resolveAnthropicSanitizerExtensionPath,
415
438
  ): readonly string[] {
416
439
  if (model.provider !== FUSION_SANITIZED_PROVIDER) return [childExtensionPath];
417
- return [childExtensionPath, resolveSanitizer()];
440
+ return [resolveSanitizer(), childExtensionPath];
418
441
  }
419
442
 
420
443
  export function buildFusionPiChildArgv(
@@ -453,6 +476,8 @@ export function buildFusionPiChildArgv(
453
476
 
454
477
  const SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/;
455
478
  const FUSION_CHILD_RESULT_PREFIX_BYTES = Buffer.from(FUSION_CHILD_RESULT_PREFIX, 'utf8');
479
+ const FUSION_CHILD_SETTLEMENT_PREFIX_BYTES = Buffer.from(FUSION_CHILD_SETTLEMENT_PREFIX, 'utf8');
480
+ const FUSION_RUNTIME_GUARD_PREFIX_BYTES = Buffer.from(FUSION_RUNTIME_GUARD_PREFIX, 'utf8');
456
481
  const PI_EXTENSION_ERROR_PREFIX_BYTES = Buffer.from('Extension error (', 'utf8');
457
482
 
458
483
  interface ParsedFusionChildStderr {
@@ -527,6 +552,16 @@ function requireUsageInteger(
527
552
  return value;
528
553
  }
529
554
 
555
+ function requirePositiveSafeInteger(
556
+ record: Record<PropertyKey, unknown>,
557
+ key: string,
558
+ label: string,
559
+ ): number {
560
+ const value = requireUsageInteger(record, key, label);
561
+ if (value === 0) throw new Error(`${label}.${key} must be a positive safe integer`);
562
+ return value;
563
+ }
564
+
530
565
  function requireCostNumber(
531
566
  record: Record<PropertyKey, unknown>,
532
567
  key: string,
@@ -565,10 +600,124 @@ function parseCompactUsage(value: unknown): FusionUsage {
565
600
  };
566
601
  }
567
602
 
603
+ const FUSION_CLAUDE_CACHE_RETENTIONS = new Set<FusionClaudeCacheRetention>([
604
+ 'none',
605
+ 'short',
606
+ 'long',
607
+ ]);
608
+
609
+ function parseFusionClaudeCacheObservation(
610
+ value: unknown,
611
+ provider: string,
612
+ ): FusionClaudeCacheObservation {
613
+ const record = assertClosedRecord(
614
+ value,
615
+ [
616
+ 'schema_version',
617
+ 'applicability',
618
+ 'source',
619
+ 'requested_retention',
620
+ 'effective_retention',
621
+ 'breakpoint_count',
622
+ 'request_ordinal',
623
+ ],
624
+ 'fusion child cache observation',
625
+ );
626
+ if (record['schema_version'] !== FUSION_CLAUDE_CACHE_OBSERVATION_SCHEMA_VERSION) {
627
+ throw new Error('fusion child cache observation schema_version mismatch');
628
+ }
629
+ const applicability = record['applicability'];
630
+ const source = record['source'];
631
+ const requested = record['requested_retention'];
632
+ const effective = record['effective_retention'];
633
+ const breakpointCount = requireUsageInteger(
634
+ record,
635
+ 'breakpoint_count',
636
+ 'fusion child cache observation',
637
+ );
638
+ const requestOrdinal = requirePositiveSafeInteger(
639
+ record,
640
+ 'request_ordinal',
641
+ 'fusion child cache observation',
642
+ );
643
+ if (requestOrdinal > FUSION_CHILD_MAX_PROVIDER_REQUESTS) {
644
+ throw new Error('fusion child cache observation exceeds the provider request limit');
645
+ }
646
+ if (breakpointCount > FUSION_CLAUDE_CACHE_BREAKPOINT_LIMIT) {
647
+ throw new Error('fusion child cache observation exceeds the Anthropic breakpoint limit');
648
+ }
649
+ if (provider !== 'anthropic') {
650
+ if (
651
+ applicability !== 'not_applicable' ||
652
+ source !== 'not_applicable' ||
653
+ requested !== null ||
654
+ effective !== null ||
655
+ breakpointCount !== 0
656
+ ) {
657
+ throw new Error('non-Anthropic fusion child has contradictory cache observation');
658
+ }
659
+ return {
660
+ schema_version: FUSION_CLAUDE_CACHE_OBSERVATION_SCHEMA_VERSION,
661
+ applicability: 'not_applicable',
662
+ source: 'not_applicable',
663
+ requested_retention: null,
664
+ effective_retention: null,
665
+ breakpoint_count: 0,
666
+ request_ordinal: requestOrdinal,
667
+ };
668
+ }
669
+ if (applicability !== 'anthropic') {
670
+ throw new Error('Anthropic fusion child cache observation is not applicable');
671
+ }
672
+ if (source !== 'default' && source !== FUSION_CLAUDE_CACHE_RETENTION_ENV) {
673
+ throw new Error('Anthropic fusion child cache observation source is invalid');
674
+ }
675
+ if (
676
+ typeof requested !== 'string' ||
677
+ !FUSION_CLAUDE_CACHE_RETENTIONS.has(requested as FusionClaudeCacheRetention) ||
678
+ typeof effective !== 'string' ||
679
+ !FUSION_CLAUDE_CACHE_RETENTIONS.has(effective as FusionClaudeCacheRetention)
680
+ ) {
681
+ throw new Error('Anthropic fusion child cache retention is invalid');
682
+ }
683
+ const requestedRetention = requested as FusionClaudeCacheRetention;
684
+ const effectiveRetention = effective as FusionClaudeCacheRetention;
685
+ if (source === 'default' && requestedRetention !== 'long') {
686
+ throw new Error('default Anthropic cache policy did not request long retention');
687
+ }
688
+ if ((effectiveRetention === 'none') !== (breakpointCount === 0)) {
689
+ throw new Error('Anthropic fusion child cache retention/breakpoint evidence mismatch');
690
+ }
691
+ if (requestedRetention === 'none' && effectiveRetention !== 'none') {
692
+ throw new Error('disabled Anthropic cache policy reported active breakpoints');
693
+ }
694
+ if (effectiveRetention === 'long' && requestedRetention !== 'long') {
695
+ throw new Error('Anthropic long cache retention was not requested');
696
+ }
697
+ return {
698
+ schema_version: FUSION_CLAUDE_CACHE_OBSERVATION_SCHEMA_VERSION,
699
+ applicability: 'anthropic',
700
+ source,
701
+ requested_retention: requestedRetention,
702
+ effective_retention: effectiveRetention,
703
+ breakpoint_count: breakpointCount,
704
+ request_ordinal: requestOrdinal,
705
+ };
706
+ }
707
+
568
708
  function parseChildResultMetadata(value: unknown): FusionChildResultMetadata {
569
709
  const record = assertClosedRecord(
570
710
  value,
571
- ['schema_version', 'provider', 'model', 'stop_reason', 'text_blocks', 'text_sha256', 'usage'],
711
+ [
712
+ 'schema_version',
713
+ 'provider',
714
+ 'model',
715
+ 'stop_reason',
716
+ 'text_blocks',
717
+ 'text_sha256',
718
+ 'usage',
719
+ 'cache_observation',
720
+ ],
572
721
  'fusion child result',
573
722
  );
574
723
  if (record['schema_version'] !== FUSION_CHILD_RESULT_SCHEMA_VERSION)
@@ -585,18 +734,346 @@ function parseChildResultMetadata(value: unknown): FusionChildResultMetadata {
585
734
  };
586
735
  });
587
736
  const usage = parseCompactUsage(record['usage']);
737
+ const provider = requireNonBlankString(record, 'provider', 'fusion child result');
588
738
  return {
589
739
  schema_version: FUSION_CHILD_RESULT_SCHEMA_VERSION,
590
- provider: requireNonBlankString(record, 'provider', 'fusion child result'),
740
+ provider,
591
741
  model: requireNonBlankString(record, 'model', 'fusion child result'),
592
742
  stop_reason: requireNonBlankString(record, 'stop_reason', 'fusion child result'),
593
743
  text_blocks: textBlocks,
594
744
  text_sha256: requireSha256(record, 'text_sha256', 'fusion child result'),
595
745
  usage,
746
+ cache_observation: parseFusionClaudeCacheObservation(record['cache_observation'], provider),
596
747
  };
597
748
  }
598
749
 
750
+ const FUSION_RUNTIME_GUARD_CODES = new Set<FusionRuntimeGuardCode>([
751
+ 'provider_request_limit',
752
+ 'provider_request_budget',
753
+ 'provider_payload_invalid',
754
+ 'claude_cache_policy',
755
+ 'tool_call_limit',
756
+ ]);
757
+
758
+ export function parseFusionRuntimeGuard(stderr: Buffer): FusionRuntimeGuardRecord | undefined {
759
+ const frames: FusionRuntimeGuardRecord[] = [];
760
+ let cursor = 0;
761
+ for (;;) {
762
+ const frameStart = stderr.indexOf(FUSION_RUNTIME_GUARD_PREFIX_BYTES, cursor);
763
+ if (frameStart < 0) break;
764
+ const payloadStart = frameStart + FUSION_RUNTIME_GUARD_PREFIX_BYTES.length;
765
+ const newline = stderr.indexOf(10, payloadStart);
766
+ if (newline < 0) throw new Error('fusion runtime guard frame is not newline-terminated');
767
+ const bytes = stderr.subarray(payloadStart, newline);
768
+ const text = bytes.toString('utf8');
769
+ if (!Buffer.from(text, 'utf8').equals(bytes)) {
770
+ throw new Error('fusion runtime guard frame is not valid UTF-8');
771
+ }
772
+ const record = assertClosedRecord(
773
+ parseJsonText(text),
774
+ [
775
+ 'schema_version',
776
+ 'code',
777
+ 'provider',
778
+ 'model',
779
+ 'request_ordinal',
780
+ 'tool_call_count',
781
+ 'payload_bytes',
782
+ 'payload_sha256',
783
+ 'estimated_input_tokens',
784
+ 'context_window_tokens',
785
+ 'reserved_output_tokens',
786
+ 'safety_reserve_tokens',
787
+ 'allowed_input_tokens',
788
+ 'message',
789
+ ],
790
+ 'fusion runtime guard',
791
+ );
792
+ if (record['schema_version'] !== FUSION_RUNTIME_GUARD_SCHEMA_VERSION) {
793
+ throw new Error('fusion runtime guard schema_version mismatch');
794
+ }
795
+ const code = requireNonBlankString(record, 'code', 'fusion runtime guard');
796
+ if (!FUSION_RUNTIME_GUARD_CODES.has(code as FusionRuntimeGuardCode)) {
797
+ throw new Error(`fusion runtime guard code is unsupported: ${code}`);
798
+ }
799
+ const frame: FusionRuntimeGuardRecord = {
800
+ schema_version: FUSION_RUNTIME_GUARD_SCHEMA_VERSION,
801
+ code: code as FusionRuntimeGuardCode,
802
+ provider: requireNonBlankString(record, 'provider', 'fusion runtime guard'),
803
+ model: requireNonBlankString(record, 'model', 'fusion runtime guard'),
804
+ request_ordinal: requirePositiveSafeInteger(
805
+ record,
806
+ 'request_ordinal',
807
+ 'fusion runtime guard',
808
+ ),
809
+ tool_call_count: requireUsageInteger(record, 'tool_call_count', 'fusion runtime guard'),
810
+ payload_bytes: requireUsageInteger(record, 'payload_bytes', 'fusion runtime guard'),
811
+ payload_sha256: requireSha256(record, 'payload_sha256', 'fusion runtime guard'),
812
+ estimated_input_tokens: requireUsageInteger(
813
+ record,
814
+ 'estimated_input_tokens',
815
+ 'fusion runtime guard',
816
+ ),
817
+ context_window_tokens: requireUsageInteger(
818
+ record,
819
+ 'context_window_tokens',
820
+ 'fusion runtime guard',
821
+ ),
822
+ reserved_output_tokens: requirePositiveSafeInteger(
823
+ record,
824
+ 'reserved_output_tokens',
825
+ 'fusion runtime guard',
826
+ ),
827
+ safety_reserve_tokens: requirePositiveSafeInteger(
828
+ record,
829
+ 'safety_reserve_tokens',
830
+ 'fusion runtime guard',
831
+ ),
832
+ allowed_input_tokens: requireUsageInteger(
833
+ record,
834
+ 'allowed_input_tokens',
835
+ 'fusion runtime guard',
836
+ ),
837
+ message: requireNonBlankString(record, 'message', 'fusion runtime guard'),
838
+ };
839
+ if (frame.code !== 'provider_payload_invalid') {
840
+ if (frame.context_window_tokens === 0) {
841
+ throw new Error('fusion runtime guard.context_window_tokens must be positive');
842
+ }
843
+ const expectedAllowed =
844
+ frame.context_window_tokens - frame.reserved_output_tokens - frame.safety_reserve_tokens;
845
+ if (expectedAllowed < 0 || frame.allowed_input_tokens !== expectedAllowed) {
846
+ throw new Error('fusion runtime guard allowed-input arithmetic mismatch');
847
+ }
848
+ }
849
+ if (frame.code === 'claude_cache_policy') {
850
+ if (
851
+ frame.payload_bytes !== 0 ||
852
+ frame.payload_sha256 !== createHash('sha256').update(Buffer.alloc(0)).digest('hex') ||
853
+ frame.estimated_input_tokens !== 0
854
+ ) {
855
+ throw new Error('fusion runtime guard Claude cache policy payload evidence mismatch');
856
+ }
857
+ }
858
+ if (
859
+ frame.code === 'provider_request_budget' &&
860
+ frame.estimated_input_tokens <= frame.allowed_input_tokens
861
+ ) {
862
+ throw new Error('fusion runtime guard provider budget code has no token overage');
863
+ }
864
+ if (
865
+ frame.code === 'provider_request_limit' &&
866
+ frame.request_ordinal <= FUSION_CHILD_MAX_PROVIDER_REQUESTS
867
+ ) {
868
+ throw new Error('fusion runtime guard provider request limit was not exceeded');
869
+ }
870
+ if (frame.code === 'tool_call_limit') {
871
+ if (frame.tool_call_count <= FUSION_CHILD_MAX_TOOL_CALLS) {
872
+ throw new Error('fusion runtime guard tool call limit was not exceeded');
873
+ }
874
+ if (
875
+ frame.payload_bytes !== 0 ||
876
+ frame.payload_sha256 !== createHash('sha256').update(Buffer.alloc(0)).digest('hex') ||
877
+ frame.estimated_input_tokens !== 0
878
+ ) {
879
+ throw new Error('fusion runtime guard tool call limit payload evidence mismatch');
880
+ }
881
+ }
882
+ frames.push(frame);
883
+ cursor = newline + 1;
884
+ }
885
+ if (frames.length > 1) throw new Error('fusion child emitted multiple runtime guard frames');
886
+ return frames[0];
887
+ }
888
+
889
+ const FUSION_CHILD_SETTLEMENT_FAILURE_REASONS = new Set<FusionChildSettlementFailureReason>([
890
+ 'no_records',
891
+ 'final_not_stop',
892
+ 'invalid_non_final',
893
+ 'runtime_guard',
894
+ 'cache_observation',
895
+ ]);
896
+
897
+ export function parseFusionChildSettlement(
898
+ stderr: Buffer,
899
+ ): FusionChildSettlementRecord | undefined {
900
+ const frames: FusionChildSettlementRecord[] = [];
901
+ let cursor = 0;
902
+ for (;;) {
903
+ const frameStart = stderr.indexOf(FUSION_CHILD_SETTLEMENT_PREFIX_BYTES, cursor);
904
+ if (frameStart < 0) break;
905
+ const payloadStart = frameStart + FUSION_CHILD_SETTLEMENT_PREFIX_BYTES.length;
906
+ const newline = stderr.indexOf(10, payloadStart);
907
+ if (newline < 0) throw new Error('fusion child settlement frame is not newline-terminated');
908
+ const bytes = stderr.subarray(payloadStart, newline);
909
+ const text = bytes.toString('utf8');
910
+ if (!Buffer.from(text, 'utf8').equals(bytes)) {
911
+ throw new Error('fusion child settlement frame is not valid UTF-8');
912
+ }
913
+ const record = assertClosedRecord(
914
+ parseJsonText(text),
915
+ [
916
+ 'schema_version',
917
+ 'status',
918
+ 'record_count',
919
+ 'records_sha256',
920
+ 'final_record_index',
921
+ 'final_text_sha256',
922
+ 'recovered_error_ordinals',
923
+ 'failure_reason',
924
+ ],
925
+ 'fusion child settlement',
926
+ );
927
+ if (record['schema_version'] !== FUSION_CHILD_SETTLEMENT_SCHEMA_VERSION) {
928
+ throw new Error('fusion child settlement schema_version mismatch');
929
+ }
930
+ const status = record['status'];
931
+ if (status !== 'complete' && status !== 'failed') {
932
+ throw new Error('fusion child settlement.status is invalid');
933
+ }
934
+ const recordCount = requireUsageInteger(record, 'record_count', 'fusion child settlement');
935
+ const finalIndexValue = record['final_record_index'];
936
+ const finalRecordIndex =
937
+ finalIndexValue === null
938
+ ? null
939
+ : requireUsageInteger(record, 'final_record_index', 'fusion child settlement');
940
+ const finalHashValue = record['final_text_sha256'];
941
+ const finalTextSha256 =
942
+ finalHashValue === null
943
+ ? null
944
+ : requireSha256(record, 'final_text_sha256', 'fusion child settlement');
945
+ const recoveredValue = record['recovered_error_ordinals'];
946
+ if (!Array.isArray(recoveredValue)) {
947
+ throw new Error('fusion child settlement.recovered_error_ordinals must be an array');
948
+ }
949
+ const recoveredErrorOrdinals = recoveredValue.map((value, index) => {
950
+ if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
951
+ throw new Error(
952
+ `fusion child settlement.recovered_error_ordinals[${String(index)}] must be a non-negative safe integer`,
953
+ );
954
+ }
955
+ return value;
956
+ });
957
+ for (let index = 0; index < recoveredErrorOrdinals.length; index += 1) {
958
+ const ordinal = recoveredErrorOrdinals[index];
959
+ if (
960
+ ordinal === undefined ||
961
+ ordinal >= recordCount - 1 ||
962
+ (index > 0 && ordinal <= (recoveredErrorOrdinals[index - 1] ?? -1))
963
+ ) {
964
+ throw new Error('fusion child settlement recovered-error ordinals are not canonical');
965
+ }
966
+ }
967
+ const failureValue = record['failure_reason'];
968
+ let failureReason: FusionChildSettlementFailureReason | null;
969
+ if (failureValue === null) failureReason = null;
970
+ else if (
971
+ typeof failureValue === 'string' &&
972
+ FUSION_CHILD_SETTLEMENT_FAILURE_REASONS.has(
973
+ failureValue as FusionChildSettlementFailureReason,
974
+ )
975
+ ) {
976
+ failureReason = failureValue as FusionChildSettlementFailureReason;
977
+ } else {
978
+ throw new Error('fusion child settlement.failure_reason is invalid');
979
+ }
980
+ if ((status === 'complete') !== (failureReason === null)) {
981
+ throw new Error('fusion child settlement status/failure_reason mismatch');
982
+ }
983
+ if (recordCount === 0) {
984
+ if (finalRecordIndex !== null || finalTextSha256 !== null) {
985
+ throw new Error('fusion child settlement empty stream has final-record evidence');
986
+ }
987
+ } else if (finalRecordIndex !== recordCount - 1 || finalTextSha256 === null) {
988
+ throw new Error('fusion child settlement final-record evidence mismatch');
989
+ }
990
+ frames.push({
991
+ schema_version: FUSION_CHILD_SETTLEMENT_SCHEMA_VERSION,
992
+ status,
993
+ record_count: recordCount,
994
+ records_sha256: requireSha256(record, 'records_sha256', 'fusion child settlement'),
995
+ final_record_index: finalRecordIndex,
996
+ final_text_sha256: finalTextSha256,
997
+ recovered_error_ordinals: recoveredErrorOrdinals,
998
+ failure_reason: failureReason,
999
+ });
1000
+ cursor = newline + 1;
1001
+ }
1002
+ if (frames.length > 1) throw new Error('fusion child emitted multiple settlement frames');
1003
+ return frames[0];
1004
+ }
1005
+
1006
+ function assertFusionChildSettlementOrdering(stderr: Buffer): void {
1007
+ const settlementStart = stderr.indexOf(FUSION_CHILD_SETTLEMENT_PREFIX_BYTES);
1008
+ if (settlementStart < 0) return;
1009
+ if (stderr.indexOf(FUSION_CHILD_RESULT_PREFIX_BYTES, settlementStart) >= 0) {
1010
+ throw new Error('fusion child emitted result metadata after terminal settlement');
1011
+ }
1012
+ }
1013
+
1014
+ function stripChildControlFrames(stderr: Buffer): Buffer {
1015
+ const prefixes = [FUSION_CHILD_SETTLEMENT_PREFIX_BYTES, FUSION_RUNTIME_GUARD_PREFIX_BYTES];
1016
+ const diagnostics: Buffer[] = [];
1017
+ let cursor = 0;
1018
+ for (;;) {
1019
+ let next = -1;
1020
+ let prefix: Buffer | undefined;
1021
+ for (const candidate of prefixes) {
1022
+ const found = stderr.indexOf(candidate, cursor);
1023
+ if (found >= 0 && (next < 0 || found < next)) {
1024
+ next = found;
1025
+ prefix = candidate;
1026
+ }
1027
+ }
1028
+ if (next < 0 || prefix === undefined) {
1029
+ if (cursor < stderr.length) diagnostics.push(stderr.subarray(cursor));
1030
+ break;
1031
+ }
1032
+ if (next > cursor) diagnostics.push(stderr.subarray(cursor, next));
1033
+ const newline = stderr.indexOf(10, next + prefix.length);
1034
+ if (newline < 0) throw new Error('fusion child control frame is not newline-terminated');
1035
+ cursor = newline + 1;
1036
+ }
1037
+ return Buffer.concat(diagnostics);
1038
+ }
1039
+
1040
+ export function assertFusionRuntimeGuardMatchesModel(
1041
+ guard: FusionRuntimeGuardRecord,
1042
+ model: ResolvedFusionModel,
1043
+ ): void {
1044
+ const routeUnknown = guard.provider === 'unknown' && guard.model === 'unknown';
1045
+ if (!routeUnknown && (guard.provider !== model.provider || guard.model !== model.model)) {
1046
+ throw new Error(
1047
+ `fusion runtime guard route mismatch: expected ${model.qualifiedId}, observed ${guard.provider}/${guard.model}`,
1048
+ );
1049
+ }
1050
+ if (routeUnknown && guard.code !== 'provider_payload_invalid') {
1051
+ throw new Error('fusion runtime guard omitted the route for a capacity-backed refusal');
1052
+ }
1053
+ if (guard.code === 'provider_payload_invalid') return;
1054
+ const expectedReservedOutput = Math.max(
1055
+ FUSION_CHILD_MIN_OUTPUT_RESERVE_TOKENS,
1056
+ model.maxOutputTokens,
1057
+ );
1058
+ const expectedAllowedInput =
1059
+ model.contextWindow - expectedReservedOutput - FUSION_CHILD_SAFETY_RESERVE_TOKENS;
1060
+ if (
1061
+ guard.context_window_tokens !== model.contextWindow ||
1062
+ guard.reserved_output_tokens !== expectedReservedOutput ||
1063
+ guard.safety_reserve_tokens !== FUSION_CHILD_SAFETY_RESERVE_TOKENS ||
1064
+ guard.allowed_input_tokens !== expectedAllowedInput
1065
+ ) {
1066
+ throw new Error('fusion runtime guard capacity evidence does not match the resolved route');
1067
+ }
1068
+ }
1069
+
599
1070
  export function parseFusionChildStderr(stderr: Buffer): ParsedFusionChildStderr {
1071
+ // Validate every package-owned side frame even though result metadata is parsed
1072
+ // independently below. Malformed refusal/settlement evidence must never degrade
1073
+ // into opaque diagnostics.
1074
+ parseFusionRuntimeGuard(stderr);
1075
+ const settlement = parseFusionChildSettlement(stderr);
1076
+ assertFusionChildSettlementOrdering(stderr);
600
1077
  const records: FusionChildResultMetadata[] = [];
601
1078
  const diagnostics: Buffer[] = [];
602
1079
  let cursor = 0;
@@ -625,11 +1102,16 @@ export function parseFusionChildStderr(stderr: Buffer): ParsedFusionChildStderr
625
1102
  records.push(parseChildResultMetadata(parsed));
626
1103
  cursor = newline + 1;
627
1104
  }
628
- const events = Buffer.from(
629
- records.length === 0 ? '' : `${records.map((record) => JSON.stringify(record)).join('\n')}\n`,
630
- 'utf8',
631
- );
632
- return { records, events, diagnostics: Buffer.concat(diagnostics) };
1105
+ const resultEvents = serializeFusionChildResultRecords(records);
1106
+ const events =
1107
+ settlement === undefined
1108
+ ? resultEvents
1109
+ : Buffer.concat([resultEvents, Buffer.from(`${JSON.stringify(settlement)}\n`, 'utf8')]);
1110
+ return {
1111
+ records,
1112
+ events,
1113
+ diagnostics: stripChildControlFrames(Buffer.concat(diagnostics)),
1114
+ };
633
1115
  }
634
1116
 
635
1117
  function parseToolCallLogRecord(value: unknown, label: string): FusionToolCallLogRecord {
@@ -965,13 +1447,29 @@ export class FusionPiCompactResultParser {
965
1447
  diagnostics: Buffer;
966
1448
  } {
967
1449
  const parsed = parseFusionChildStderr(stderr);
1450
+ const runtimeGuard = parseFusionRuntimeGuard(stderr);
1451
+ if (runtimeGuard !== undefined) {
1452
+ throw new Error(`Pi child runtime guard refused the run: ${runtimeGuard.message}`);
1453
+ }
1454
+ const settlement = parseFusionChildSettlement(stderr);
1455
+ if (settlement === undefined) throw new Error('Pi child emitted no terminal result settlement');
1456
+ const expectedSettlement = buildFusionChildSettlement(parsed.records);
1457
+ if (JSON.stringify(settlement) !== JSON.stringify(expectedSettlement)) {
1458
+ throw new Error('Pi child terminal result settlement does not match the metadata stream');
1459
+ }
968
1460
  if (parsed.diagnostics.includes(PI_EXTENSION_ERROR_PREFIX_BYTES)) {
969
1461
  throw new Error('Pi child reported an extension error diagnostic');
970
1462
  }
971
1463
  const final = parsed.records.at(-1);
972
1464
  if (final === undefined) throw new Error('Pi child emitted no compact result metadata');
973
1465
  for (const record of parsed.records) this.assertModel(record);
1466
+ this.assertCacheObservationOrdinals(parsed.records);
974
1467
  this.assertTranscriptStopReasons(parsed.records);
1468
+ if (settlement.status !== 'complete') {
1469
+ throw new Error(
1470
+ `Pi child terminal result settlement failed: ${settlement.failure_reason ?? 'unknown'}`,
1471
+ );
1472
+ }
975
1473
  const observed = this.observedFromRecords(parsed.records);
976
1474
  return {
977
1475
  text: reconstructFinalText(response, final),
@@ -992,6 +1490,19 @@ export class FusionPiCompactResultParser {
992
1490
  }
993
1491
  }
994
1492
 
1493
+ private assertCacheObservationOrdinals(records: readonly FusionChildResultMetadata[]): void {
1494
+ let priorOrdinal = 0;
1495
+ for (const [index, record] of records.entries()) {
1496
+ const ordinal = record.cache_observation.request_ordinal;
1497
+ if (ordinal <= priorOrdinal) {
1498
+ throw new Error(
1499
+ `Pi child cache observation ordinal is not increasing at result ${String(index)}: previous ${String(priorOrdinal)}, observed ${String(ordinal)}`,
1500
+ );
1501
+ }
1502
+ priorOrdinal = ordinal;
1503
+ }
1504
+ }
1505
+
995
1506
  private assertTranscriptStopReasons(records: readonly FusionChildResultMetadata[]): void {
996
1507
  for (const [index, record] of records.entries()) {
997
1508
  const isFinal = index === records.length - 1;
@@ -999,9 +1510,14 @@ export class FusionPiCompactResultParser {
999
1510
  if (record.stop_reason !== 'stop') {
1000
1511
  throw new Error(this.stopReasonError('final', 'stop', record.stop_reason, true));
1001
1512
  }
1002
- } else if (record.stop_reason !== 'toolUse') {
1513
+ } else if (record.stop_reason !== 'toolUse' && !isRecoverableFusionChildErrorRecord(record)) {
1003
1514
  throw new Error(
1004
- this.stopReasonError(`non-final record ${index}`, 'toolUse', record.stop_reason, true),
1515
+ this.stopReasonError(
1516
+ `non-final record ${index}`,
1517
+ 'toolUse or a settled zero-usage retry marker',
1518
+ record.stop_reason,
1519
+ true,
1520
+ ),
1005
1521
  );
1006
1522
  }
1007
1523
  }
@@ -1495,11 +2011,39 @@ export async function runPiChild(options: RunPiChildOptions): Promise<FusionChil
1495
2011
  observed,
1496
2012
  );
1497
2013
  if (close.code !== 0 || close.signal !== null) {
2014
+ let runtimeGuard: FusionRuntimeGuardRecord | undefined;
2015
+ try {
2016
+ runtimeGuard = parseFusionRuntimeGuard(rawStderr);
2017
+ if (runtimeGuard !== undefined) {
2018
+ assertFusionRuntimeGuardMatchesModel(runtimeGuard, options.model);
2019
+ }
2020
+ } catch (error) {
2021
+ throw new FusionChildRunError(
2022
+ withCleanupErrors(
2023
+ childError(
2024
+ `Pi child runtime guard evidence invalid: ${error instanceof Error ? error.message : String(error)}`,
2025
+ 'child_event_invalid',
2026
+ options,
2027
+ ),
2028
+ state.cleanupErrors,
2029
+ ),
2030
+ compactEvents,
2031
+ response,
2032
+ diagnostics,
2033
+ close,
2034
+ observed,
2035
+ );
2036
+ }
1498
2037
  throw new FusionChildRunError(
1499
2038
  withCleanupErrors(
1500
2039
  childError(
1501
- `Pi child exited with code ${close.code === null ? 'null' : String(close.code)}${close.signal === null ? '' : ` (${close.signal})`}`,
1502
- 'child_exit_failed',
2040
+ runtimeGuard?.message ??
2041
+ `Pi child exited with code ${close.code === null ? 'null' : String(close.code)}${close.signal === null ? '' : ` (${close.signal})`}`,
2042
+ runtimeGuard === undefined
2043
+ ? 'child_exit_failed'
2044
+ : runtimeGuard.code === 'claude_cache_policy'
2045
+ ? 'child_cache_policy_invalid'
2046
+ : 'child_runtime_budget_exceeded',
1503
2047
  options,
1504
2048
  ),
1505
2049
  state.cleanupErrors,