pi-openai-toolkit 0.6.0 → 0.7.0

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.
@@ -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,
@@ -503,6 +504,25 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
503
504
  postCompactionTailMessages,
504
505
  actualPostCompactionTail,
505
506
  );
507
+ const baseRewrittenPayload: ResponsesCompatibleRequestPayload = {
508
+ ...args.payload,
509
+ ...(freshPreamble.instructions !== undefined ? { instructions: freshPreamble.instructions } : {}),
510
+ input: [
511
+ ...freshPreamble.leadingInput,
512
+ ...compactedWindow,
513
+ ...actualPostCompactionTail,
514
+ ...freshPreamble.trailingInput,
515
+ ],
516
+ };
517
+ const carryoverRewrite = rewritePayloadWithDeferredToolCarryover({
518
+ payload: baseRewrittenPayload,
519
+ carryover: details.deferredToolCarryover,
520
+ compactionEntryId: args.compactionEntry.id,
521
+ checkpointEndIndex: freshPreamble.leadingInput.length + compactedWindow.length,
522
+ compat: args.model.compat as
523
+ | { supportsAdditionalTools?: boolean; supportsToolSearch?: boolean }
524
+ | undefined,
525
+ });
506
526
 
507
527
  return {
508
528
  ok: true,
@@ -517,23 +537,9 @@ function buildNativeReplaySegmentsInternal<TApi extends Api>(args: {
517
537
  compactedWindow,
518
538
  postCompactionTail,
519
539
  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
- ],
540
+ replayInput: carryoverRewrite.payload.input,
536
541
  },
542
+ rewrittenPayload: carryoverRewrite.payload,
537
543
  };
538
544
  }
539
545
 
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,
@@ -18,6 +18,8 @@
18
18
  "src/config.ts",
19
19
  "src/debug.ts",
20
20
  "src/details-store.ts",
21
+ "src/deferred-tool-carryover.ts",
22
+ "src/inline-compaction.ts",
21
23
  "src/extension-runtime.ts",
22
24
  "src/native-fallback.ts",
23
25
  "src/payload-rewrite.ts",