pi-openai-toolkit 0.6.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 +22 -3
- package/README_zh.md +22 -3
- package/package.json +6 -1
- package/src/config.ts +69 -0
- package/src/debug.ts +58 -0
- package/src/deferred-tool-carryover.ts +280 -0
- package/src/extension-runtime.ts +34 -2
- package/src/inline-compaction.ts +1264 -0
- package/src/payload-rewrite.ts +126 -107
- package/src/types.ts +94 -1
- package/tsconfig.check.json +2 -0
package/src/payload-rewrite.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
SessionMessageEntry,
|
|
8
8
|
} from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import type { ResponsesCompatibleRequestPayload } from "./runtime";
|
|
10
|
+
import { rewritePayloadWithDeferredToolCarryover } from "./deferred-tool-carryover";
|
|
10
11
|
import type { NativeCompactionEntry } from "./types";
|
|
11
12
|
import {
|
|
12
13
|
compareResponsesInputParity,
|
|
@@ -16,12 +17,6 @@ import {
|
|
|
16
17
|
type ResponsesInputMessageItem,
|
|
17
18
|
} from "./serializer";
|
|
18
19
|
|
|
19
|
-
export type FreshAuthoritativePreamble = {
|
|
20
|
-
instructions?: string;
|
|
21
|
-
leadingInput: ResponsesInputMessageItem[];
|
|
22
|
-
trailingInput: ResponsesInputMessageItem[];
|
|
23
|
-
};
|
|
24
|
-
|
|
25
20
|
export type SerializedReplaySlice = {
|
|
26
21
|
entries: SessionEntry[];
|
|
27
22
|
messages: AgentMessage[];
|
|
@@ -51,10 +46,10 @@ export type NativeReplayPayloadRewrite = {
|
|
|
51
46
|
export type NativeReplayPayloadRewriteFailureReason =
|
|
52
47
|
| "compaction-boundary-not-found"
|
|
53
48
|
| "first-kept-entry-not-found"
|
|
54
|
-
| "unsupported-instructions"
|
|
55
49
|
| "invalid-compacted-window"
|
|
56
50
|
| "unexpected-compaction-after-boundary"
|
|
57
|
-
| "expected-pi-replay-mismatch"
|
|
51
|
+
| "expected-pi-replay-mismatch"
|
|
52
|
+
| "compaction-summary-not-found";
|
|
58
53
|
|
|
59
54
|
export type NativeReplayPayloadRewriteFailure = {
|
|
60
55
|
ok: false;
|
|
@@ -267,44 +262,50 @@ function toReplayAgentMessage(entry: SessionEntry): AgentMessage | undefined {
|
|
|
267
262
|
return undefined;
|
|
268
263
|
}
|
|
269
264
|
|
|
270
|
-
function
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
export function extractFreshAuthoritativePreamble(
|
|
275
|
-
payload: ResponsesCompatibleRequestPayload,
|
|
276
|
-
): FreshAuthoritativePreamble | undefined {
|
|
277
|
-
if (payload.instructions !== undefined && typeof payload.instructions !== "string") {
|
|
278
|
-
return undefined;
|
|
265
|
+
function extractUserItemText(item: Record<string, unknown>): unknown {
|
|
266
|
+
const { content } = item;
|
|
267
|
+
if (typeof content === "string") {
|
|
268
|
+
return content;
|
|
279
269
|
}
|
|
280
270
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
while (leadingBoundary < payload.input.length && isPromptEnvelopeItem(payload.input[leadingBoundary])) {
|
|
287
|
-
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
|
+
}
|
|
288
276
|
}
|
|
289
277
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
trailingBoundary -= 1;
|
|
293
|
-
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
294
280
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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;
|
|
298
290
|
}
|
|
299
|
-
}
|
|
300
291
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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;
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
function collectReplayMessages(entries: readonly SessionEntry[]): AgentMessage[] {
|
|
@@ -407,14 +408,6 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
|
|
|
407
408
|
};
|
|
408
409
|
}
|
|
409
410
|
|
|
410
|
-
const freshPreamble = extractFreshAuthoritativePreamble(args.payload);
|
|
411
|
-
if (!freshPreamble) {
|
|
412
|
-
return {
|
|
413
|
-
ok: false,
|
|
414
|
-
reason: "unsupported-instructions",
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
|
|
418
411
|
const newerCompactionEntry = args.branchEntries
|
|
419
412
|
.slice(boundaryIndex + 1)
|
|
420
413
|
.some((entry) => entry.type === "compaction");
|
|
@@ -446,19 +439,38 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
|
|
|
446
439
|
const preCompactionKeptMessages = collectReplayMessages(preCompactionEntries);
|
|
447
440
|
const postCompactionTailMessages = collectReplayMessages(postCompactionEntries);
|
|
448
441
|
const compactionSummaryMessage = createCompactionSummaryAgentMessage(args.compactionEntry);
|
|
449
|
-
const
|
|
450
|
-
compactionSummaryMessage,
|
|
451
|
-
...preCompactionKeptMessages,
|
|
452
|
-
...postCompactionTailMessages,
|
|
453
|
-
]);
|
|
454
|
-
const originalPiReplayInput: ResponsesInputItem[] = [
|
|
455
|
-
...freshPreamble.leadingInput,
|
|
456
|
-
...serializedPiHistoryInput,
|
|
457
|
-
...freshPreamble.trailingInput,
|
|
458
|
-
];
|
|
442
|
+
const keptWindowSerialized = serializeMessagesToResponsesInput(args.model, preCompactionKeptMessages);
|
|
459
443
|
|
|
460
|
-
|
|
461
|
-
|
|
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
|
+
);
|
|
462
474
|
return {
|
|
463
475
|
ok: false,
|
|
464
476
|
reason: "expected-pi-replay-mismatch",
|
|
@@ -470,70 +482,77 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
|
|
|
470
482
|
};
|
|
471
483
|
}
|
|
472
484
|
|
|
473
|
-
const
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
const preCompactionKeptCount = serializeMessagesToResponsesInput(args.model, preCompactionKeptMessages).length;
|
|
477
|
-
const tailStartIndex = freshPreambleCount + compactionSummaryCount + preCompactionKeptCount;
|
|
478
|
-
const tailEndIndex = args.payload.input.length - trailingPreambleCount;
|
|
479
|
-
const actualCompactionSummary = cloneResponsesInputSlice(
|
|
480
|
-
args.payload.input.slice(freshPreambleCount, freshPreambleCount + compactionSummaryCount),
|
|
481
|
-
);
|
|
482
|
-
const actualPreCompactionKeptWindow = cloneResponsesInputSlice(
|
|
483
|
-
args.payload.input.slice(
|
|
484
|
-
freshPreambleCount + compactionSummaryCount,
|
|
485
|
-
freshPreambleCount + compactionSummaryCount + preCompactionKeptCount,
|
|
486
|
-
),
|
|
487
|
-
);
|
|
488
|
-
const actualPostCompactionTail = cloneResponsesInputSlice(args.payload.input.slice(tailStartIndex, tailEndIndex));
|
|
489
|
-
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) {
|
|
490
488
|
return {
|
|
491
489
|
ok: false,
|
|
492
490
|
reason: "expected-pi-replay-mismatch",
|
|
493
491
|
};
|
|
494
492
|
}
|
|
495
493
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
const
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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) {
|
|
501
|
+
return {
|
|
502
|
+
ok: false,
|
|
503
|
+
reason: "expected-pi-replay-mismatch",
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const baseRewrittenPayload: ResponsesCompatibleRequestPayload = {
|
|
508
|
+
...args.payload,
|
|
509
|
+
input: [
|
|
510
|
+
...leadingInput,
|
|
511
|
+
...compactedWindow,
|
|
512
|
+
...liveTail,
|
|
513
|
+
...trailingPreamble,
|
|
514
|
+
],
|
|
515
|
+
};
|
|
516
|
+
const carryoverRewrite = rewritePayloadWithDeferredToolCarryover({
|
|
517
|
+
payload: baseRewrittenPayload,
|
|
518
|
+
carryover: details.deferredToolCarryover,
|
|
519
|
+
compactionEntryId: args.compactionEntry.id,
|
|
520
|
+
checkpointEndIndex: leadingInput.length + compactedWindow.length,
|
|
521
|
+
compat: args.model.compat as
|
|
522
|
+
| { supportsAdditionalTools?: boolean; supportsToolSearch?: boolean }
|
|
523
|
+
| undefined,
|
|
524
|
+
});
|
|
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
|
+
];
|
|
506
535
|
|
|
507
536
|
return {
|
|
508
537
|
ok: true,
|
|
509
538
|
segments: {
|
|
510
539
|
boundaryIndex,
|
|
511
540
|
firstKeptEntryIndex,
|
|
512
|
-
instructions:
|
|
513
|
-
freshPreamble:
|
|
514
|
-
trailingPreamble:
|
|
541
|
+
instructions: typeof args.payload.instructions === "string" ? args.payload.instructions : undefined,
|
|
542
|
+
freshPreamble: leadingInput as ResponsesInputMessageItem[],
|
|
543
|
+
trailingPreamble: trailingPreamble as ResponsesInputMessageItem[],
|
|
515
544
|
compactionSummary: actualCompactionSummary,
|
|
516
|
-
preCompactionKeptWindow
|
|
545
|
+
preCompactionKeptWindow: createReplaySlice(
|
|
546
|
+
preCompactionEntries,
|
|
547
|
+
preCompactionKeptMessages,
|
|
548
|
+
actualKeptWindow,
|
|
549
|
+
),
|
|
517
550
|
compactedWindow,
|
|
518
|
-
postCompactionTail,
|
|
551
|
+
postCompactionTail: createReplaySlice(postCompactionEntries, postCompactionTailMessages, liveTail),
|
|
519
552
|
originalPiReplayInput,
|
|
520
|
-
replayInput:
|
|
521
|
-
...freshPreamble.leadingInput,
|
|
522
|
-
...compactedWindow,
|
|
523
|
-
...actualPostCompactionTail,
|
|
524
|
-
...freshPreamble.trailingInput,
|
|
525
|
-
],
|
|
526
|
-
},
|
|
527
|
-
rewrittenPayload: {
|
|
528
|
-
...args.payload,
|
|
529
|
-
...(freshPreamble.instructions !== undefined ? { instructions: freshPreamble.instructions } : {}),
|
|
530
|
-
input: [
|
|
531
|
-
...freshPreamble.leadingInput,
|
|
532
|
-
...compactedWindow,
|
|
533
|
-
...actualPostCompactionTail,
|
|
534
|
-
...freshPreamble.trailingInput,
|
|
535
|
-
],
|
|
553
|
+
replayInput: carryoverRewrite.payload.input,
|
|
536
554
|
},
|
|
555
|
+
rewrittenPayload: carryoverRewrite.payload,
|
|
537
556
|
};
|
|
538
557
|
}
|
|
539
558
|
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
|
-
import type { CompactionEntry, CompactionResult, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { CompactionEntry, CompactionResult, ExtensionContext, SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
|
|
4
4
|
export const TOOLKIT_ID = "pi-openai-toolkit";
|
|
5
5
|
export const COMPACTION_EXTENSION_ID = `${TOOLKIT_ID}:compaction`;
|
|
@@ -36,6 +36,15 @@ export type DebugArtifactKind =
|
|
|
36
36
|
| "compaction-event"
|
|
37
37
|
| "lifecycle";
|
|
38
38
|
|
|
39
|
+
export type CompactionContinuationMode = "inline" | "followUp" | "off";
|
|
40
|
+
|
|
41
|
+
export type AutoCompactionConfig = {
|
|
42
|
+
enabled: boolean;
|
|
43
|
+
continuation: CompactionContinuationMode;
|
|
44
|
+
unsupportedFallback: "followUp" | "off";
|
|
45
|
+
reserveTokens?: number;
|
|
46
|
+
};
|
|
47
|
+
|
|
39
48
|
export type CompactionConfig = {
|
|
40
49
|
enabled: boolean;
|
|
41
50
|
/**
|
|
@@ -55,6 +64,8 @@ export type CompactionConfig = {
|
|
|
55
64
|
model?: string;
|
|
56
65
|
/** Thinking level passed to pi's native compact() when the fallback model runs. */
|
|
57
66
|
thinkingLevel: ThinkingLevel;
|
|
67
|
+
/** Threshold-triggered compaction and same-tool-loop continuation policy. */
|
|
68
|
+
autoCompaction: AutoCompactionConfig;
|
|
58
69
|
/** Subset of RESPONSES_COMPACT_CAPABLE_APIS that should use remote compaction. */
|
|
59
70
|
responsesApis: string[];
|
|
60
71
|
notifyOnLoad: boolean;
|
|
@@ -135,10 +146,21 @@ export type NativeCompactionIdentity = {
|
|
|
135
146
|
baseUrl: string;
|
|
136
147
|
};
|
|
137
148
|
|
|
149
|
+
export const CACHE_STACK_ACTIVATION_ENTRY_TYPE = "pi-cache-stack.activation-state.v1" as const;
|
|
150
|
+
|
|
151
|
+
export type DeferredToolCarryoverV1 = {
|
|
152
|
+
version: 1;
|
|
153
|
+
source: typeof CACHE_STACK_ACTIVATION_ENTRY_TYPE;
|
|
154
|
+
toolNames: string[];
|
|
155
|
+
catalogHash?: string;
|
|
156
|
+
};
|
|
157
|
+
|
|
138
158
|
export type NativeCompactionDetails = NativeCompactionIdentity & {
|
|
139
159
|
strategy: NativeCompactionStrategy;
|
|
140
160
|
/** Actual producer of the opaque checkpoint; absent on legacy same-model entries. */
|
|
141
161
|
compactionModel?: NativeCompactionIdentity;
|
|
162
|
+
/** Cache-stack activation state captured at this opaque checkpoint. */
|
|
163
|
+
deferredToolCarryover?: DeferredToolCarryoverV1;
|
|
142
164
|
compactedWindow: unknown[];
|
|
143
165
|
compactResponseId?: string;
|
|
144
166
|
createdAt: string;
|
|
@@ -149,6 +171,7 @@ export type NativeCompactionEntry = CompactionEntry<NativeCompactionDetails>;
|
|
|
149
171
|
|
|
150
172
|
export type CreateNativeCompactionDetailsInput = NativeCompactionIdentity & {
|
|
151
173
|
compactionModel?: NativeCompactionIdentity;
|
|
174
|
+
deferredToolCarryover?: DeferredToolCarryoverV1;
|
|
152
175
|
compactedWindow: unknown[];
|
|
153
176
|
compactResponseId?: string;
|
|
154
177
|
createdAt?: string;
|
|
@@ -262,6 +285,64 @@ export function isNativeCompactionIdentity(value: unknown): value is NativeCompa
|
|
|
262
285
|
);
|
|
263
286
|
}
|
|
264
287
|
|
|
288
|
+
export function isDeferredToolCarryover(value: unknown): value is DeferredToolCarryoverV1 {
|
|
289
|
+
if (!isRecord(value) || value.version !== 1 || value.source !== CACHE_STACK_ACTIVATION_ENTRY_TYPE) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
Array.isArray(value.toolNames) &&
|
|
295
|
+
value.toolNames.every((name) => isNonEmptyString(name)) &&
|
|
296
|
+
(value.catalogHash === undefined || typeof value.catalogHash === "string")
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function normalizeDeferredToolCarryover(value: DeferredToolCarryoverV1): DeferredToolCarryoverV1 {
|
|
301
|
+
if (!isDeferredToolCarryover(value)) {
|
|
302
|
+
throw new Error("Invalid deferred tool carryover");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
version: 1,
|
|
307
|
+
source: CACHE_STACK_ACTIVATION_ENTRY_TYPE,
|
|
308
|
+
toolNames: [...new Set(value.toolNames.map((name) => name.trim()).filter(Boolean))].sort(),
|
|
309
|
+
...(typeof value.catalogHash === "string" ? { catalogHash: value.catalogHash } : {}),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function decodeCacheStackActivationState(value: unknown): DeferredToolCarryoverV1 | undefined {
|
|
314
|
+
if (!isRecord(value) || value.version !== 1 || typeof value.catalogHash !== "string") {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
if (!Array.isArray(value.activatedTools) || !value.activatedTools.every((name) => typeof name === "string")) {
|
|
318
|
+
return undefined;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return normalizeDeferredToolCarryover({
|
|
322
|
+
version: 1,
|
|
323
|
+
source: CACHE_STACK_ACTIVATION_ENTRY_TYPE,
|
|
324
|
+
toolNames: value.activatedTools,
|
|
325
|
+
catalogHash: value.catalogHash,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Read only the latest valid cache-stack activation snapshot on this branch. */
|
|
330
|
+
export function getLatestDeferredToolCarryover(
|
|
331
|
+
entries: readonly SessionEntry[],
|
|
332
|
+
): DeferredToolCarryoverV1 | undefined {
|
|
333
|
+
for (let index = entries.length - 1; index >= 0; index -= 1) {
|
|
334
|
+
const entry = entries[index];
|
|
335
|
+
if (entry?.type !== "custom" || entry.customType !== CACHE_STACK_ACTIVATION_ENTRY_TYPE) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const decoded = decodeCacheStackActivationState(entry.data);
|
|
340
|
+
if (decoded) return decoded;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
|
|
265
346
|
export function isNativeCompactionDetails(value: unknown): value is NativeCompactionDetails {
|
|
266
347
|
if (!isRecord(value) || !isNativeCompactionIdentity(value)) {
|
|
267
348
|
return false;
|
|
@@ -272,6 +353,7 @@ export function isNativeCompactionDetails(value: unknown): value is NativeCompac
|
|
|
272
353
|
(candidate.strategy === LEGACY_NATIVE_COMPACTION_STRATEGY ||
|
|
273
354
|
candidate.strategy === REMOTE_V2_COMPACTION_STRATEGY) &&
|
|
274
355
|
(candidate.compactionModel === undefined || isNativeCompactionIdentity(candidate.compactionModel)) &&
|
|
356
|
+
(candidate.deferredToolCarryover === undefined || isDeferredToolCarryover(candidate.deferredToolCarryover)) &&
|
|
275
357
|
Array.isArray(candidate.compactedWindow) &&
|
|
276
358
|
candidate.compactedWindow.every(isCompactedWindowItem) &&
|
|
277
359
|
isNonEmptyString(candidate.createdAt) &&
|
|
@@ -299,6 +381,9 @@ export function createNativeCompactionDetails(input: CreateNativeCompactionDetai
|
|
|
299
381
|
baseUrl: normalizeString(input.compactionModel.baseUrl),
|
|
300
382
|
}
|
|
301
383
|
: undefined,
|
|
384
|
+
deferredToolCarryover: input.deferredToolCarryover
|
|
385
|
+
? normalizeDeferredToolCarryover(input.deferredToolCarryover)
|
|
386
|
+
: undefined,
|
|
302
387
|
compactedWindow: input.compactedWindow.map((item) => cloneStructuredValue(item)),
|
|
303
388
|
compactResponseId: isNonEmptyString(input.compactResponseId) ? normalizeString(input.compactResponseId) : undefined,
|
|
304
389
|
createdAt: isNonEmptyString(input.createdAt) ? normalizeString(input.createdAt) : new Date().toISOString(),
|
|
@@ -325,12 +410,20 @@ export function createNativeCompactionResult(
|
|
|
325
410
|
};
|
|
326
411
|
}
|
|
327
412
|
|
|
413
|
+
export const DEFAULT_AUTO_COMPACTION_CONFIG: AutoCompactionConfig = {
|
|
414
|
+
enabled: true,
|
|
415
|
+
continuation: "inline",
|
|
416
|
+
unsupportedFallback: "followUp",
|
|
417
|
+
reserveTokens: undefined,
|
|
418
|
+
};
|
|
419
|
+
|
|
328
420
|
export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = {
|
|
329
421
|
enabled: true,
|
|
330
422
|
allowCompactionContinuityBreak: false,
|
|
331
423
|
remoteCompactModel: undefined,
|
|
332
424
|
model: undefined,
|
|
333
425
|
thinkingLevel: "off",
|
|
426
|
+
autoCompaction: { ...DEFAULT_AUTO_COMPACTION_CONFIG },
|
|
334
427
|
responsesApis: [...RESPONSES_COMPACT_CAPABLE_APIS],
|
|
335
428
|
notifyOnLoad: false,
|
|
336
429
|
debug: false,
|