pi-openai-toolkit 0.7.0 → 0.7.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/README.md +1 -0
- package/README_zh.md +1 -0
- package/package.json +1 -1
- package/src/debug.ts +58 -0
- package/src/extension-runtime.ts +25 -1
- package/src/payload-rewrite.ts +109 -96
package/README.md
CHANGED
|
@@ -163,6 +163,7 @@ Content-Type: application/json
|
|
|
163
163
|
- **Validation Criteria**: Must return a valid SSE stream with `response.completed` (status `completed`) containing exactly one output item of `type: "compaction"` with non-empty `encrypted_content`.
|
|
164
164
|
- **Remote Model Override**: `compaction.remoteCompactModel` can assign a same-gateway Responses model as the checkpoint producer while the active model remains the consumer. Its registered model metadata, authentication, headers, and effective base URL are used only for the synthetic compact request. Existing and recursive checkpoints remain replayable by the active model.
|
|
165
165
|
- **Zero Loss Replay**: The opaque encrypted item is stored in `CompactionEntry.details.compactedWindow`. On subsequent requests, it is transparently prepended after fresh preamble instructions alongside live turns, eliminating lossy text re-summaries.
|
|
166
|
+
- **Fail-Closed Replay Guard**: Replay verifies only the compaction summary sentinel plus the kept pre-compaction window derived from session entries; leading/trailing prompts and the post-compaction live tail are preserved verbatim, so transient messages injected by other extensions no longer block replay. If the kept region cannot be verified (e.g. `expected-pi-replay-mismatch` or `compaction-summary-not-found`), the toolkit aborts the provider request, shows an error notification, and always writes a content-free `*-replay-failure.json` artifact even when `logProviderPayloads` is disabled. The sentinel-only payload is never sent to the model.
|
|
166
167
|
- **Graceful Multi-Tier Fallback**:
|
|
167
168
|
1. Compatible Responses endpoints → Remote v2 Opaque Compaction.
|
|
168
169
|
2. Gateway failure or non-Responses model → Configured `compaction.model` fallback.
|
package/README_zh.md
CHANGED
|
@@ -163,6 +163,7 @@ Content-Type: application/json
|
|
|
163
163
|
- **校验标准**:接收 SSE 流并验证 `response.completed` 事件(状态为 `completed`),且返回且仅返回一个包含非空 `encrypted_content` 密文的 `type: "compaction"` 数据块。
|
|
164
164
|
- **远端压缩模型覆盖**:`compaction.remoteCompactModel` 可以让同一网关上的另一个 Responses 模型负责生成 checkpoint,而当前活动模型仍是 checkpoint 的消费者。synthetic 压缩请求会使用覆盖模型在注册表中的模型元数据、认证、请求头和实际 Base URL;旧 checkpoint、递归压缩 checkpoint 都仍由当前活动模型重放。
|
|
165
165
|
- **无损重放机制**:密文数据完整存入 `CompactionEntry.details.compactedWindow`。在后续对话中,插件会自动将密文插入在最新的系统指令之后与最新几轮消息之前,实现完全服务端无损上下文还原。
|
|
166
|
+
- **重放失败保护(Fail-Closed)**:重放只校验 compaction summary 哨兵项以及由 session entries 推导出的压缩前保留窗口;前置/后置 prompt 与压缩后的实时尾部消息一律原样保留,因此其他扩展注入的临时消息不再阻断重放。若保留窗口无法与 session entries 对齐(如 `expected-pi-replay-mismatch`、`compaction-summary-not-found`),插件会中止 provider 请求、弹出错误通知,并且即使 `logProviderPayloads` 关闭也会写入一份不含内容的安全失败记录 `*-replay-failure.json`。只含哨兵占位符的请求体永远不会发送给模型。
|
|
166
167
|
- **三级优雅降级**:
|
|
167
168
|
1. 支持的 Responses 端点 → 触发 Remote v2 密文无损压缩;
|
|
168
169
|
2. 网关不支持或报错 → 降级调用用户指定的 `compaction.model`;
|
package/package.json
CHANGED
package/src/debug.ts
CHANGED
|
@@ -177,3 +177,61 @@ export function writeDebugArtifact(
|
|
|
177
177
|
fs.writeFileSync(filePath, `${JSON.stringify(envelope, null, 2)}\n`, "utf8");
|
|
178
178
|
return filePath;
|
|
179
179
|
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Always-written, content-free failure record for an opaque replay failure.
|
|
183
|
+
*
|
|
184
|
+
* Unlike regular payload artifacts this is not gated by logProviderPayloads:
|
|
185
|
+
* a replay failure is a continuity-loss incident, not optional payload logging.
|
|
186
|
+
* It contains only structural parity signatures and identity metadata; prompt
|
|
187
|
+
* text, user content, credentials, and encrypted_content are never included
|
|
188
|
+
* (critical redaction is applied unconditionally).
|
|
189
|
+
*/
|
|
190
|
+
export function writeReplayFailureArtifact(
|
|
191
|
+
details: {
|
|
192
|
+
reason: string;
|
|
193
|
+
parity?: { actual: string[]; expected: string[]; mismatches: string[] };
|
|
194
|
+
compactionEntryId?: string;
|
|
195
|
+
provider?: string;
|
|
196
|
+
api?: string;
|
|
197
|
+
model?: string;
|
|
198
|
+
},
|
|
199
|
+
settings: CompactionConfig,
|
|
200
|
+
context: ArtifactContext,
|
|
201
|
+
): string | undefined {
|
|
202
|
+
try {
|
|
203
|
+
const sessionInfo = toSessionInfo(context);
|
|
204
|
+
const paths = resolveArtifactPaths(settings, context);
|
|
205
|
+
const targetDir = selectArtifactDirectory(paths, "provider-request");
|
|
206
|
+
ensureDir(targetDir);
|
|
207
|
+
|
|
208
|
+
const timestamp = new Date().toISOString();
|
|
209
|
+
const filePath = path.join(targetDir, `${timestamp.replace(/[.:]/g, "-")}-replay-failure.json`);
|
|
210
|
+
const data = redactCriticalValue({
|
|
211
|
+
event: "before_provider_request.rewrite-failed",
|
|
212
|
+
reason: details.reason,
|
|
213
|
+
parity: details.parity,
|
|
214
|
+
compactionEntryId: details.compactionEntryId,
|
|
215
|
+
provider: details.provider,
|
|
216
|
+
api: details.api,
|
|
217
|
+
model: details.model,
|
|
218
|
+
});
|
|
219
|
+
const envelope: DebugArtifactEnvelope = {
|
|
220
|
+
extension: COMPACTION_EXTENSION_ID,
|
|
221
|
+
kind: "provider-request",
|
|
222
|
+
timestamp,
|
|
223
|
+
cwd: sessionInfo.cwd,
|
|
224
|
+
sessionId: sessionInfo.sessionId,
|
|
225
|
+
sessionFile: sessionInfo.sessionFile,
|
|
226
|
+
sessionDir: sessionInfo.sessionDir,
|
|
227
|
+
redaction: { enabled: true },
|
|
228
|
+
data,
|
|
229
|
+
};
|
|
230
|
+
fs.writeFileSync(filePath, `${JSON.stringify(envelope, null, 2)}\n`, "utf8");
|
|
231
|
+
return filePath;
|
|
232
|
+
} catch {
|
|
233
|
+
// The replay failure path itself must never be masked by an artifact write
|
|
234
|
+
// error; the caller still aborts the provider request.
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
}
|
package/src/extension-runtime.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
SessionBeforeCompactEvent,
|
|
7
7
|
} from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { loadToolkitConfig } from "./config";
|
|
9
|
-
import { writeDebugArtifact } from "./debug";
|
|
9
|
+
import { writeDebugArtifact, writeReplayFailureArtifact } from "./debug";
|
|
10
10
|
import { resolveLatestNativeCompactionEntry } from "./details-store";
|
|
11
11
|
import { runNativeFallbackCompaction } from "./native-fallback";
|
|
12
12
|
import {
|
|
@@ -508,6 +508,30 @@ async function handleBeforeProviderRequest(event: BeforeProviderRequestEvent, ct
|
|
|
508
508
|
config,
|
|
509
509
|
ctx,
|
|
510
510
|
);
|
|
511
|
+
|
|
512
|
+
// Fail loud instead of letting Pi send the sentinel-only payload: the
|
|
513
|
+
// compacted history would be silently lost while the request still succeeds.
|
|
514
|
+
// A forced redacted failure record is written even when logProviderPayloads
|
|
515
|
+
// is disabled so the incident is diagnosable without leaking content.
|
|
516
|
+
writeReplayFailureArtifact(
|
|
517
|
+
{
|
|
518
|
+
reason: rewrite.reason,
|
|
519
|
+
parity: rewrite.parity,
|
|
520
|
+
compactionEntryId: latestNativeCompactionEntry.id,
|
|
521
|
+
provider: runtime.provider,
|
|
522
|
+
api: runtime.api,
|
|
523
|
+
model: runtime.model,
|
|
524
|
+
},
|
|
525
|
+
config,
|
|
526
|
+
ctx,
|
|
527
|
+
);
|
|
528
|
+
if (ctx.hasUI) {
|
|
529
|
+
ctx.ui.notify(
|
|
530
|
+
`${COMPACTION_EXTENSION_ID}: native compaction replay failed (${rewrite.reason}); provider request aborted`,
|
|
531
|
+
"error",
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
ctx.abort();
|
|
511
535
|
return undefined;
|
|
512
536
|
}
|
|
513
537
|
|
package/src/payload-rewrite.ts
CHANGED
|
@@ -17,12 +17,6 @@ import {
|
|
|
17
17
|
type ResponsesInputMessageItem,
|
|
18
18
|
} from "./serializer";
|
|
19
19
|
|
|
20
|
-
export type FreshAuthoritativePreamble = {
|
|
21
|
-
instructions?: string;
|
|
22
|
-
leadingInput: ResponsesInputMessageItem[];
|
|
23
|
-
trailingInput: ResponsesInputMessageItem[];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
20
|
export type SerializedReplaySlice = {
|
|
27
21
|
entries: SessionEntry[];
|
|
28
22
|
messages: AgentMessage[];
|
|
@@ -52,10 +46,10 @@ export type NativeReplayPayloadRewrite = {
|
|
|
52
46
|
export type NativeReplayPayloadRewriteFailureReason =
|
|
53
47
|
| "compaction-boundary-not-found"
|
|
54
48
|
| "first-kept-entry-not-found"
|
|
55
|
-
| "unsupported-instructions"
|
|
56
49
|
| "invalid-compacted-window"
|
|
57
50
|
| "unexpected-compaction-after-boundary"
|
|
58
|
-
| "expected-pi-replay-mismatch"
|
|
51
|
+
| "expected-pi-replay-mismatch"
|
|
52
|
+
| "compaction-summary-not-found";
|
|
59
53
|
|
|
60
54
|
export type NativeReplayPayloadRewriteFailure = {
|
|
61
55
|
ok: false;
|
|
@@ -268,44 +262,50 @@ function toReplayAgentMessage(entry: SessionEntry): AgentMessage | undefined {
|
|
|
268
262
|
return undefined;
|
|
269
263
|
}
|
|
270
264
|
|
|
271
|
-
function
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
export function extractFreshAuthoritativePreamble(
|
|
276
|
-
payload: ResponsesCompatibleRequestPayload,
|
|
277
|
-
): FreshAuthoritativePreamble | undefined {
|
|
278
|
-
if (payload.instructions !== undefined && typeof payload.instructions !== "string") {
|
|
279
|
-
return undefined;
|
|
265
|
+
function extractUserItemText(item: Record<string, unknown>): unknown {
|
|
266
|
+
const { content } = item;
|
|
267
|
+
if (typeof content === "string") {
|
|
268
|
+
return content;
|
|
280
269
|
}
|
|
281
270
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
while (leadingBoundary < payload.input.length && isPromptEnvelopeItem(payload.input[leadingBoundary])) {
|
|
288
|
-
leadingBoundary += 1;
|
|
271
|
+
if (Array.isArray(content)) {
|
|
272
|
+
const first = content[0];
|
|
273
|
+
if (isRecord(first) && first.type === "input_text" && typeof first.text === "string") {
|
|
274
|
+
return first.text;
|
|
275
|
+
}
|
|
289
276
|
}
|
|
290
277
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
trailingBoundary -= 1;
|
|
294
|
-
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
295
280
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Locate the Pi-authored compaction summary item. The marker text is unique to
|
|
283
|
+
* this compaction entry, so a content match on the user item is a reliable
|
|
284
|
+
* single anchor; nothing else in the payload needs to be prefixed or counted.
|
|
285
|
+
*/
|
|
286
|
+
function findCompactionSummaryIndex(input: readonly unknown[], summaryMarker: string): number {
|
|
287
|
+
return input.findIndex((item) => {
|
|
288
|
+
if (!isRecord(item) || item.role !== "user") {
|
|
289
|
+
return false;
|
|
299
290
|
}
|
|
300
|
-
}
|
|
301
291
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
292
|
+
const text = extractUserItemText(item);
|
|
293
|
+
return typeof text === "string" && text.includes("<summary>") && text.includes(summaryMarker);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Count consecutive provider-authored developer/system items at the end of the input. */
|
|
298
|
+
function countTrailingPreambleItems(items: readonly unknown[]): number {
|
|
299
|
+
let count = 0;
|
|
300
|
+
for (let index = items.length - 1; index >= 0; index--) {
|
|
301
|
+
const item = items[index];
|
|
302
|
+
if (isResponsesInputMessageItem(item) && isPreambleRole(item.role)) {
|
|
303
|
+
count += 1;
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
return count;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
function collectReplayMessages(entries: readonly SessionEntry[]): AgentMessage[] {
|
|
@@ -408,14 +408,6 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
|
|
|
408
408
|
};
|
|
409
409
|
}
|
|
410
410
|
|
|
411
|
-
const freshPreamble = extractFreshAuthoritativePreamble(args.payload);
|
|
412
|
-
if (!freshPreamble) {
|
|
413
|
-
return {
|
|
414
|
-
ok: false,
|
|
415
|
-
reason: "unsupported-instructions",
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
|
|
419
411
|
const newerCompactionEntry = args.branchEntries
|
|
420
412
|
.slice(boundaryIndex + 1)
|
|
421
413
|
.some((entry) => entry.type === "compaction");
|
|
@@ -447,19 +439,38 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
|
|
|
447
439
|
const preCompactionKeptMessages = collectReplayMessages(preCompactionEntries);
|
|
448
440
|
const postCompactionTailMessages = collectReplayMessages(postCompactionEntries);
|
|
449
441
|
const compactionSummaryMessage = createCompactionSummaryAgentMessage(args.compactionEntry);
|
|
450
|
-
const
|
|
451
|
-
compactionSummaryMessage,
|
|
452
|
-
...preCompactionKeptMessages,
|
|
453
|
-
...postCompactionTailMessages,
|
|
454
|
-
]);
|
|
455
|
-
const originalPiReplayInput: ResponsesInputItem[] = [
|
|
456
|
-
...freshPreamble.leadingInput,
|
|
457
|
-
...serializedPiHistoryInput,
|
|
458
|
-
...freshPreamble.trailingInput,
|
|
459
|
-
];
|
|
442
|
+
const keptWindowSerialized = serializeMessagesToResponsesInput(args.model, preCompactionKeptMessages);
|
|
460
443
|
|
|
461
|
-
|
|
462
|
-
|
|
444
|
+
// Pi-authored compaction summary item. Everything outside the replaced region
|
|
445
|
+
// is preserved verbatim, so this content anchor is the only location probe
|
|
446
|
+
// needed; the marker text is unique to this compaction entry.
|
|
447
|
+
const summaryIndex = findCompactionSummaryIndex(args.payload.input, args.compactionEntry.summary);
|
|
448
|
+
if (summaryIndex < 0) {
|
|
449
|
+
return {
|
|
450
|
+
ok: false,
|
|
451
|
+
reason: "compaction-summary-not-found",
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const keptStart = summaryIndex + 1;
|
|
456
|
+
const keptEnd = keptStart + keptWindowSerialized.length;
|
|
457
|
+
const actualKeptWindow = cloneResponsesInputSlice(args.payload.input.slice(keptStart, keptEnd));
|
|
458
|
+
if (!actualKeptWindow) {
|
|
459
|
+
return {
|
|
460
|
+
ok: false,
|
|
461
|
+
reason: "expected-pi-replay-mismatch",
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// The only hard verification: the region immediately after the sentinel must be
|
|
466
|
+
// exactly the kept pre-compaction window derived from session entries. Content
|
|
467
|
+
// after this region is preserved as-is, so transient messages injected by other
|
|
468
|
+
// extensions (which have no session entries) no longer break replay.
|
|
469
|
+
if (!areEquivalentValues(actualKeptWindow, keptWindowSerialized)) {
|
|
470
|
+
const parity = compareResponsesInputParity(
|
|
471
|
+
args.payload.input.slice(keptStart, keptEnd),
|
|
472
|
+
keptWindowSerialized,
|
|
473
|
+
);
|
|
463
474
|
return {
|
|
464
475
|
ok: false,
|
|
465
476
|
reason: "expected-pi-replay-mismatch",
|
|
@@ -471,71 +482,73 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
|
|
|
471
482
|
};
|
|
472
483
|
}
|
|
473
484
|
|
|
474
|
-
const
|
|
475
|
-
const
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
);
|
|
489
|
-
|
|
490
|
-
if (!actualCompactionSummary || !actualPreCompactionKeptWindow || !actualPostCompactionTail) {
|
|
485
|
+
const actualCompactionSummary = cloneResponsesInputSlice([args.payload.input[summaryIndex]]);
|
|
486
|
+
const actualPostCompactionTail = cloneResponsesInputSlice(args.payload.input.slice(keptEnd));
|
|
487
|
+
if (!actualCompactionSummary || !actualPostCompactionTail) {
|
|
488
|
+
return {
|
|
489
|
+
ok: false,
|
|
490
|
+
reason: "expected-pi-replay-mismatch",
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Keep provider-authored trailing developer/system prompts in place.
|
|
495
|
+
const trailingCount = countTrailingPreambleItems(actualPostCompactionTail);
|
|
496
|
+
const trailingPreamble = actualPostCompactionTail.slice(actualPostCompactionTail.length - trailingCount);
|
|
497
|
+
const liveTail = actualPostCompactionTail.slice(0, actualPostCompactionTail.length - trailingCount);
|
|
498
|
+
|
|
499
|
+
const leadingInput = cloneResponsesInputSlice(args.payload.input.slice(0, summaryIndex));
|
|
500
|
+
if (!leadingInput) {
|
|
491
501
|
return {
|
|
492
502
|
ok: false,
|
|
493
503
|
reason: "expected-pi-replay-mismatch",
|
|
494
504
|
};
|
|
495
505
|
}
|
|
496
506
|
|
|
497
|
-
const preCompactionKeptWindow = createReplaySlice(
|
|
498
|
-
preCompactionEntries,
|
|
499
|
-
preCompactionKeptMessages,
|
|
500
|
-
actualPreCompactionKeptWindow,
|
|
501
|
-
);
|
|
502
|
-
const postCompactionTail = createReplaySlice(
|
|
503
|
-
postCompactionEntries,
|
|
504
|
-
postCompactionTailMessages,
|
|
505
|
-
actualPostCompactionTail,
|
|
506
|
-
);
|
|
507
507
|
const baseRewrittenPayload: ResponsesCompatibleRequestPayload = {
|
|
508
508
|
...args.payload,
|
|
509
|
-
...(freshPreamble.instructions !== undefined ? { instructions: freshPreamble.instructions } : {}),
|
|
510
509
|
input: [
|
|
511
|
-
...
|
|
510
|
+
...leadingInput,
|
|
512
511
|
...compactedWindow,
|
|
513
|
-
...
|
|
514
|
-
...
|
|
512
|
+
...liveTail,
|
|
513
|
+
...trailingPreamble,
|
|
515
514
|
],
|
|
516
515
|
};
|
|
517
516
|
const carryoverRewrite = rewritePayloadWithDeferredToolCarryover({
|
|
518
517
|
payload: baseRewrittenPayload,
|
|
519
518
|
carryover: details.deferredToolCarryover,
|
|
520
519
|
compactionEntryId: args.compactionEntry.id,
|
|
521
|
-
checkpointEndIndex:
|
|
520
|
+
checkpointEndIndex: leadingInput.length + compactedWindow.length,
|
|
522
521
|
compat: args.model.compat as
|
|
523
522
|
| { supportsAdditionalTools?: boolean; supportsToolSearch?: boolean }
|
|
524
523
|
| undefined,
|
|
525
524
|
});
|
|
526
525
|
|
|
526
|
+
const sentinelSerialized = serializeMessagesToResponsesInput(args.model, [compactionSummaryMessage]);
|
|
527
|
+
const serializedTailInput = serializeMessagesToResponsesInput(args.model, postCompactionTailMessages);
|
|
528
|
+
const originalPiReplayInput: ResponsesInputItem[] = [
|
|
529
|
+
...leadingInput,
|
|
530
|
+
...sentinelSerialized,
|
|
531
|
+
...keptWindowSerialized,
|
|
532
|
+
...serializedTailInput,
|
|
533
|
+
...trailingPreamble,
|
|
534
|
+
];
|
|
535
|
+
|
|
527
536
|
return {
|
|
528
537
|
ok: true,
|
|
529
538
|
segments: {
|
|
530
539
|
boundaryIndex,
|
|
531
540
|
firstKeptEntryIndex,
|
|
532
|
-
instructions:
|
|
533
|
-
freshPreamble:
|
|
534
|
-
trailingPreamble:
|
|
541
|
+
instructions: typeof args.payload.instructions === "string" ? args.payload.instructions : undefined,
|
|
542
|
+
freshPreamble: leadingInput as ResponsesInputMessageItem[],
|
|
543
|
+
trailingPreamble: trailingPreamble as ResponsesInputMessageItem[],
|
|
535
544
|
compactionSummary: actualCompactionSummary,
|
|
536
|
-
preCompactionKeptWindow
|
|
545
|
+
preCompactionKeptWindow: createReplaySlice(
|
|
546
|
+
preCompactionEntries,
|
|
547
|
+
preCompactionKeptMessages,
|
|
548
|
+
actualKeptWindow,
|
|
549
|
+
),
|
|
537
550
|
compactedWindow,
|
|
538
|
-
postCompactionTail,
|
|
551
|
+
postCompactionTail: createReplaySlice(postCompactionEntries, postCompactionTailMessages, liveTail),
|
|
539
552
|
originalPiReplayInput,
|
|
540
553
|
replayInput: carryoverRewrite.payload.input,
|
|
541
554
|
},
|