@howaboua/pi-codex-conversion 1.5.8 → 1.5.9-dev.38.dc4aa12

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.9
4
+
5
+ - Fixed native Responses compaction replay when provider payloads include in-flight tail items that are not yet persisted in the session branch.
6
+
3
7
  ## 1.5.8
4
8
 
5
9
  - Fixed native Responses compaction replay after compaction display messages so requests replace Pi placeholder compaction context with the native compacted window instead of failing parity checks.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howaboua/pi-codex-conversion",
3
- "version": "1.5.8",
3
+ "version": "1.5.9-dev.38.dc4aa12",
4
4
  "description": "Codex-oriented tool and prompt adapter for pi coding agent",
5
5
  "type": "module",
6
6
  "repository": {
@@ -71,6 +71,19 @@ export type NativeReplayPayloadRewriteResult =
71
71
  | NativeReplayPayloadRewrite
72
72
  | NativeReplayPayloadRewriteFailure;
73
73
 
74
+ type ReplayMessageSet = {
75
+ messages: AgentMessage[];
76
+ input: ResponsesInputItem[];
77
+ };
78
+
79
+ type ReplayMatch = {
80
+ originalPiReplayInput: ResponsesInputItem[];
81
+ preCompactionKept: ReplayMessageSet;
82
+ postCompactionTail: ReplayMessageSet;
83
+ actualPostCompactionTail: ResponsesInputItem[];
84
+ extraPostCompactionTail: ResponsesInputItem[];
85
+ };
86
+
74
87
  function isRecord(value: unknown): value is Record<string, unknown> {
75
88
  return !!value && typeof value === "object" && !Array.isArray(value);
76
89
  }
@@ -359,6 +372,66 @@ function createReplaySlice(
359
372
  };
360
373
  }
361
374
 
375
+ function createReplayMessageSet<TApi extends Api>(model: Model<TApi>, messages: AgentMessage[]): ReplayMessageSet {
376
+ return {
377
+ messages,
378
+ input: serializeMessagesToResponsesInput(model, messages),
379
+ };
380
+ }
381
+
382
+ function createReplayVariants<TApi extends Api>(args: {
383
+ model: Model<TApi>;
384
+ entries: readonly SessionEntry[];
385
+ }): ReplayMessageSet[] {
386
+ const contextMessages = collectReplayMessages(args.entries);
387
+ const piMessages = collectPiReplayMessages(args.entries);
388
+ const contextSet = createReplayMessageSet(args.model, contextMessages);
389
+ if (areEquivalentValues(contextMessages, piMessages)) return [contextSet];
390
+ return [contextSet, createReplayMessageSet(args.model, piMessages)];
391
+ }
392
+
393
+ function findReplayMatch<TApi extends Api>(args: {
394
+ model: Model<TApi>;
395
+ payloadInput: readonly unknown[];
396
+ freshPreamble: FreshAuthoritativePreamble;
397
+ compactionSummaryMessage: AgentMessage;
398
+ preCompactionEntries: readonly SessionEntry[];
399
+ postCompactionEntries: readonly SessionEntry[];
400
+ }): ReplayMatch | undefined {
401
+ const compactionSummaryInput = serializeMessagesToResponsesInput(args.model, [args.compactionSummaryMessage]);
402
+ const preCompactionVariants = createReplayVariants({ model: args.model, entries: args.preCompactionEntries });
403
+ const postCompactionVariants = createReplayVariants({ model: args.model, entries: args.postCompactionEntries });
404
+
405
+ for (const preCompactionKept of preCompactionVariants) {
406
+ for (const postCompactionTail of postCompactionVariants) {
407
+ const expectedBeforeTrailing: ResponsesInputItem[] = [
408
+ ...args.freshPreamble.leadingInput,
409
+ ...compactionSummaryInput,
410
+ ...preCompactionKept.input,
411
+ ...postCompactionTail.input,
412
+ ];
413
+ const originalPiReplayInput: ResponsesInputItem[] = [...expectedBeforeTrailing, ...args.freshPreamble.trailingInput];
414
+ const tailEndIndex = args.payloadInput.length - args.freshPreamble.trailingInput.length;
415
+ const prefixMatches = areEquivalentValues(args.payloadInput.slice(0, expectedBeforeTrailing.length), expectedBeforeTrailing);
416
+ const trailingMatches = areEquivalentValues(args.payloadInput.slice(tailEndIndex), args.freshPreamble.trailingInput);
417
+
418
+ if (prefixMatches && trailingMatches && tailEndIndex >= expectedBeforeTrailing.length) {
419
+ const actualPostCompactionTail = cloneResponsesInputSlice(
420
+ args.payloadInput.slice(
421
+ args.freshPreamble.leadingInput.length + compactionSummaryInput.length + preCompactionKept.input.length,
422
+ tailEndIndex,
423
+ ),
424
+ );
425
+ const extraPostCompactionTail = cloneResponsesInputSlice(args.payloadInput.slice(expectedBeforeTrailing.length, tailEndIndex));
426
+ if (!actualPostCompactionTail || !extraPostCompactionTail) return undefined;
427
+ return { originalPiReplayInput, preCompactionKept, postCompactionTail, actualPostCompactionTail, extraPostCompactionTail };
428
+ }
429
+ }
430
+ }
431
+
432
+ return undefined;
433
+ }
434
+
362
435
  function findEntryIndexByIdBeforeBoundary(
363
436
  entries: readonly SessionEntry[],
364
437
  entryId: string,
@@ -453,23 +526,27 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
453
526
 
454
527
  const preCompactionEntries = args.branchEntries.slice(firstKeptEntryIndex, boundaryIndex);
455
528
  const postCompactionEntries = args.branchEntries.slice(boundaryIndex + 1);
456
- const preCompactionKeptMessages = collectPiReplayMessages(preCompactionEntries);
457
- const postCompactionTailMessages = collectPiReplayMessages(postCompactionEntries);
458
529
  const contextPostCompactionTailMessages = collectReplayMessages(postCompactionEntries);
459
530
  const compactionSummaryMessage = createCompactionSummaryAgentMessage(args.compactionEntry);
460
- const serializedPiHistoryInput = serializeMessagesToResponsesInput(args.model, [
531
+ const replayMatch = findReplayMatch({
532
+ model: args.model,
533
+ payloadInput: args.payload.input,
534
+ freshPreamble,
461
535
  compactionSummaryMessage,
462
- ...preCompactionKeptMessages,
463
- ...postCompactionTailMessages,
464
- ]);
465
- const originalPiReplayInput: ResponsesInputItem[] = [
466
- ...freshPreamble.leadingInput,
467
- ...serializedPiHistoryInput,
468
- ...freshPreamble.trailingInput,
469
- ];
470
-
471
- if (!areEquivalentValues(args.payload.input, originalPiReplayInput)) {
472
- const parity = compareResponsesInputParity(args.payload.input, originalPiReplayInput);
536
+ preCompactionEntries,
537
+ postCompactionEntries,
538
+ });
539
+
540
+ if (!replayMatch) {
541
+ const compactionSummaryInput = serializeMessagesToResponsesInput(args.model, [compactionSummaryMessage]);
542
+ const expectedInput = [
543
+ ...freshPreamble.leadingInput,
544
+ ...compactionSummaryInput,
545
+ ...serializeMessagesToResponsesInput(args.model, collectReplayMessages(preCompactionEntries)),
546
+ ...serializeMessagesToResponsesInput(args.model, collectReplayMessages(postCompactionEntries)),
547
+ ...freshPreamble.trailingInput,
548
+ ];
549
+ const parity = compareResponsesInputParity(args.payload.input, expectedInput);
473
550
  return {
474
551
  ok: false,
475
552
  reason: "expected-pi-replay-mismatch",
@@ -484,8 +561,7 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
484
561
  const freshPreambleCount = freshPreamble.leadingInput.length;
485
562
  const trailingPreambleCount = freshPreamble.trailingInput.length;
486
563
  const compactionSummaryCount = serializeMessagesToResponsesInput(args.model, [compactionSummaryMessage]).length;
487
- const preCompactionKeptCount = serializeMessagesToResponsesInput(args.model, preCompactionKeptMessages).length;
488
- const tailStartIndex = freshPreambleCount + compactionSummaryCount + preCompactionKeptCount;
564
+ const preCompactionKeptCount = replayMatch.preCompactionKept.input.length;
489
565
  const tailEndIndex = args.payload.input.length - trailingPreambleCount;
490
566
  const actualCompactionSummary = cloneResponsesInputSlice(
491
567
  args.payload.input.slice(freshPreambleCount, freshPreambleCount + compactionSummaryCount),
@@ -496,8 +572,11 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
496
572
  freshPreambleCount + compactionSummaryCount + preCompactionKeptCount,
497
573
  ),
498
574
  );
499
- const actualPostCompactionTail = cloneResponsesInputSlice(args.payload.input.slice(tailStartIndex, tailEndIndex));
500
- const contextPostCompactionTail = serializeMessagesToResponsesInput(args.model, contextPostCompactionTailMessages);
575
+ const actualPostCompactionTail = replayMatch.actualPostCompactionTail;
576
+ const contextPostCompactionTail = [
577
+ ...serializeMessagesToResponsesInput(args.model, contextPostCompactionTailMessages),
578
+ ...replayMatch.extraPostCompactionTail,
579
+ ];
501
580
  if (!actualCompactionSummary || !actualPreCompactionKeptWindow || !actualPostCompactionTail) {
502
581
  return {
503
582
  ok: false,
@@ -507,7 +586,7 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
507
586
 
508
587
  const preCompactionKeptWindow = createReplaySlice(
509
588
  preCompactionEntries,
510
- preCompactionKeptMessages,
589
+ replayMatch.preCompactionKept.messages,
511
590
  actualPreCompactionKeptWindow,
512
591
  );
513
592
  const postCompactionTail = createReplaySlice(
@@ -528,7 +607,7 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
528
607
  preCompactionKeptWindow,
529
608
  compactedWindow,
530
609
  postCompactionTail,
531
- originalPiReplayInput,
610
+ originalPiReplayInput: replayMatch.originalPiReplayInput,
532
611
  replayInput: [
533
612
  ...freshPreamble.leadingInput,
534
613
  ...compactedWindow,