@nijaru/pi-provider-compaction 0.0.10 → 0.0.11
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 +2 -2
- package/dist/index.js +16 -63
- package/index.ts +20 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,9 +77,9 @@ Legacy v1/v2 whole-history checkpoints remain inspectable but are never reinterp
|
|
|
77
77
|
|
|
78
78
|
## Diagnose portable fallback
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
Committed compaction entries retain the selected path, elapsed time, reported usage, and estimated cost. Optional outcome notifications show those measurements, not hypothetical savings. Portable fallback appends a `pi-provider-compaction:diagnostic` session entry with a fixed reason. Cancelled attempts with reported usage retain that usage in the same diagnostic record; without a saved checkpoint, Pi's extension API cannot include it in compaction totals. Diagnostics stay out of model context and never include request content, credentials, or backend error text.
|
|
81
81
|
|
|
82
|
-
These
|
|
82
|
+
These records describe attempts made while the extension was loaded; they cannot explain earlier compactions retroactively. A committed native checkpoint does not by itself prove that a later request replayed it.
|
|
83
83
|
|
|
84
84
|
## Generic compaction-model override
|
|
85
85
|
|
package/dist/index.js
CHANGED
|
@@ -2274,30 +2274,10 @@ function pi_provider_compaction_default(pi) {
|
|
|
2274
2274
|
let unregisterCleanup;
|
|
2275
2275
|
let generation = 0;
|
|
2276
2276
|
let requestSequence = 0;
|
|
2277
|
-
let captureStatus = "no request observed";
|
|
2278
|
-
let compactionStatus = "not attempted in this runtime";
|
|
2279
2277
|
let fallbackReason;
|
|
2280
|
-
const captureRejected = (reason) => {
|
|
2281
|
-
captureStatus = reason;
|
|
2282
|
-
};
|
|
2283
2278
|
const fallback = (reason) => {
|
|
2284
2279
|
fallbackReason = reason;
|
|
2285
|
-
compactionStatus = `portable: ${reason}`;
|
|
2286
2280
|
};
|
|
2287
|
-
pi.registerCommand("provider-compaction-status", {
|
|
2288
|
-
description: "Show cache/native eligibility and measured compaction usage",
|
|
2289
|
-
handler: async (_args, context) => {
|
|
2290
|
-
const state = cacheState(context);
|
|
2291
|
-
const remaining = state.expiresAt === undefined ? "unknown lifetime" : `estimated ${Math.max(0, Math.ceil((state.expiresAt - Date.now()) / 1000))}s remaining (model declaration; best effort)`;
|
|
2292
|
-
const usage = lastOutcome?.usage;
|
|
2293
|
-
context.ui.notify(`Provider compaction
|
|
2294
|
-
Observer: ${observer?.isInstalled() ? "installed" : "unavailable for current registration"}
|
|
2295
|
-
Capture: ${captureStatus}
|
|
2296
|
-
Cache: ${state.reason}; ${remaining}
|
|
2297
|
-
Last attempt: ${compactionStatus}${lastOutcome ? `
|
|
2298
|
-
Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usage.input}, cache read ${usage.cacheRead}, write ${usage.cacheWrite}, output ${usage.output}; estimated cost $${usage.cost.total.toFixed(6)}` : "; usage unavailable"}${lastOutcome.reason ? `; ${lastOutcome.reason}` : ""}` : ""}`, "info");
|
|
2299
|
-
}
|
|
2300
|
-
});
|
|
2301
2281
|
const invalidate = () => {
|
|
2302
2282
|
generation++;
|
|
2303
2283
|
runtimeId = randomUUID2();
|
|
@@ -2306,7 +2286,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2306
2286
|
active = undefined;
|
|
2307
2287
|
pending = undefined;
|
|
2308
2288
|
expiryAttempt = undefined;
|
|
2309
|
-
captureStatus = "invalidated by lifecycle/history/policy change";
|
|
2310
2289
|
};
|
|
2311
2290
|
const retire = () => {
|
|
2312
2291
|
usageObservers.clear();
|
|
@@ -2396,23 +2375,12 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2396
2375
|
return;
|
|
2397
2376
|
if (compactionRequest)
|
|
2398
2377
|
return;
|
|
2399
|
-
if (!supportsNativeCompaction(model) && !supportsCachedSummary(model))
|
|
2400
|
-
captureRejected("unsupported API");
|
|
2378
|
+
if (!supportsNativeCompaction(model) && !supportsCachedSummary(model))
|
|
2401
2379
|
return;
|
|
2402
|
-
|
|
2403
|
-
if (!options.onPayload) {
|
|
2404
|
-
if (captureStatus !== "eligible prepared request captured")
|
|
2405
|
-
captureRejected("no payload callback");
|
|
2380
|
+
if (!options.onPayload || generic(owner))
|
|
2406
2381
|
return;
|
|
2407
|
-
|
|
2408
|
-
if (generic(owner)) {
|
|
2409
|
-
captureRejected("generic compaction model takes precedence");
|
|
2410
|
-
return;
|
|
2411
|
-
}
|
|
2412
|
-
if (!supportedRouteOptions(model, options)) {
|
|
2413
|
-
captureRejected("unsupported route overrides");
|
|
2382
|
+
if (!supportedRouteOptions(model, options))
|
|
2414
2383
|
return;
|
|
2415
|
-
}
|
|
2416
2384
|
const ticket = generation;
|
|
2417
2385
|
const startedAt = Date.now();
|
|
2418
2386
|
const sequence = ++requestSequence;
|
|
@@ -2443,14 +2411,11 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2443
2411
|
if (!auth.ok || generic(owner) || auth.apiKey !== options.apiKey || (auth.baseUrl ?? model.baseUrl) !== model.baseUrl || !equal(normalizedHeaders(auth.headers), normalizedHeaders(options.headers)) || !equal(auth.env ?? {}, options.env ?? {})) {
|
|
2444
2412
|
active = undefined;
|
|
2445
2413
|
cache = undefined;
|
|
2446
|
-
captureRejected("resolved auth/route differs from prepared request, or generic policy changed");
|
|
2447
2414
|
return payload;
|
|
2448
2415
|
}
|
|
2449
2416
|
const provider = owner.modelRegistry.getProvider(model.provider);
|
|
2450
|
-
if (!provider)
|
|
2451
|
-
captureRejected("provider unavailable");
|
|
2417
|
+
if (!provider)
|
|
2452
2418
|
return payload;
|
|
2453
|
-
}
|
|
2454
2419
|
const routeHash = route(model, options);
|
|
2455
2420
|
if (config.cachedSummaries && supportsCachedSummary(model)) {
|
|
2456
2421
|
const warming = options.maxTokens === 1 && cache && cache.authHash === routeDigest(auth) && sameCachedPrompt(cache.payload, payload) && equal(cache.context, context);
|
|
@@ -2469,11 +2434,9 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2469
2434
|
if (!warming)
|
|
2470
2435
|
cache = undefined;
|
|
2471
2436
|
}
|
|
2472
|
-
if (!supportsNativeCompaction(model))
|
|
2473
|
-
captureStatus = candidate ? "eligible cached request captured" : "cached request ineligible";
|
|
2437
|
+
if (!supportsNativeCompaction(model))
|
|
2474
2438
|
return payload;
|
|
2475
|
-
|
|
2476
|
-
const prepared = capturePreparedRequest(model, context, canonical, payload, captureRejected);
|
|
2439
|
+
const prepared = capturePreparedRequest(model, context, canonical, payload);
|
|
2477
2440
|
if (!prepared) {
|
|
2478
2441
|
active = undefined;
|
|
2479
2442
|
return payload;
|
|
@@ -2502,7 +2465,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2502
2465
|
active = undefined;
|
|
2503
2466
|
} else
|
|
2504
2467
|
active = undefined;
|
|
2505
|
-
captureStatus = "eligible prepared request captured";
|
|
2506
2468
|
snapshot = {
|
|
2507
2469
|
prepared,
|
|
2508
2470
|
model: structuredClone(model),
|
|
@@ -2540,7 +2502,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2540
2502
|
if (!supportsNativeCompaction(context.model) && !supportsCachedSummary(context.model))
|
|
2541
2503
|
return;
|
|
2542
2504
|
observer = installObserver(pi, context, observe);
|
|
2543
|
-
captureStatus = observer ? "awaiting prepared request" : "unsupported provider registration";
|
|
2544
2505
|
};
|
|
2545
2506
|
pi.on("session_start", (_event, context) => {
|
|
2546
2507
|
retire();
|
|
@@ -2581,8 +2542,8 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2581
2542
|
removeInvalidation();
|
|
2582
2543
|
});
|
|
2583
2544
|
pi.on("session_before_compact", async (event, context) => {
|
|
2584
|
-
const began = Date.now();
|
|
2585
|
-
|
|
2545
|
+
const began = Date.now(), ticket = generation;
|
|
2546
|
+
const sessionId = context.sessionManager.getSessionId();
|
|
2586
2547
|
fallbackReason = undefined;
|
|
2587
2548
|
const attempt = expiryAttempt;
|
|
2588
2549
|
const capture = snapshot;
|
|
@@ -2591,7 +2552,12 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2591
2552
|
cache = undefined;
|
|
2592
2553
|
pending = undefined;
|
|
2593
2554
|
const cancelled = (usage, reason = "compaction ownership or input changed") => {
|
|
2594
|
-
|
|
2555
|
+
if (usage && ticket === generation && ctx) {
|
|
2556
|
+
try {
|
|
2557
|
+
if (ctx.sessionManager.getSessionId() === sessionId)
|
|
2558
|
+
pi.appendEntry("pi-provider-compaction:diagnostic", { status: `${attempt ? "expiry cancelled" : "cancelled"}: ${reason}`, usage });
|
|
2559
|
+
} catch {}
|
|
2560
|
+
}
|
|
2595
2561
|
return { cancel: true };
|
|
2596
2562
|
};
|
|
2597
2563
|
const reject = (reason) => {
|
|
@@ -2605,7 +2571,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2605
2571
|
}
|
|
2606
2572
|
if (!capture && !cached || !context.model || generic(context) || event.signal.aborted)
|
|
2607
2573
|
return reject("no eligible snapshot, explicit model override, or aborted attempt");
|
|
2608
|
-
const ticket = generation;
|
|
2609
2574
|
const branch = context.sessionManager.getBranch();
|
|
2610
2575
|
const branchHash = digest(branch);
|
|
2611
2576
|
const source = cached ?? capture;
|
|
@@ -2630,7 +2595,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2630
2595
|
let summaryPath = "standard";
|
|
2631
2596
|
let summaryReason;
|
|
2632
2597
|
if (cached) {
|
|
2633
|
-
compactionStatus = "generating cached portable summary";
|
|
2634
2598
|
compactionRequest = true;
|
|
2635
2599
|
try {
|
|
2636
2600
|
const result = await cachedSummary(cached, context, event.preparation, event.customInstructions, event.signal, current);
|
|
@@ -2650,7 +2614,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2650
2614
|
return cancelled(attemptedUsage, summaryReason ?? "warm cache unavailable");
|
|
2651
2615
|
if (!input && !attemptedUsage)
|
|
2652
2616
|
return reject(summaryReason ?? fallbackReason ?? "native coverage unavailable");
|
|
2653
|
-
compactionStatus = "generating portable summary";
|
|
2654
2617
|
try {
|
|
2655
2618
|
portable = await compact(event.preparation, source.model, auth.apiKey, auth.headers, event.customInstructions, event.signal, context.thinkingLevel, source.provider.streamSimple.bind(source.provider), auth.env, undefined, undefined, source.sessionId);
|
|
2656
2619
|
portable = withFileTracking(portable, event.preparation);
|
|
@@ -2669,7 +2632,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2669
2632
|
return { compaction: result };
|
|
2670
2633
|
};
|
|
2671
2634
|
if (!capture || !protocol || !input) {
|
|
2672
|
-
compactionStatus = `${summaryPath} portable summary prepared`;
|
|
2673
2635
|
return outcome(summaryPath, portable, summaryReason);
|
|
2674
2636
|
}
|
|
2675
2637
|
try {
|
|
@@ -2680,7 +2642,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2680
2642
|
fallback("auth changed after portable generation");
|
|
2681
2643
|
return outcome(summaryPath, portable, "native auth changed");
|
|
2682
2644
|
}
|
|
2683
|
-
compactionStatus = "requesting native checkpoint";
|
|
2684
2645
|
let native;
|
|
2685
2646
|
compactionRequest = true;
|
|
2686
2647
|
try {
|
|
@@ -2742,19 +2703,12 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2742
2703
|
pi.on("session_compact", (event, context) => {
|
|
2743
2704
|
const recorded = isObject(event.compactionEntry.details) ? event.compactionEntry.details.providerCompaction : undefined;
|
|
2744
2705
|
const owned = isObject(recorded) && equal(recorded, lastOutcome);
|
|
2745
|
-
if (readNativeCompactionDetails(event.compactionEntry.details)?.version
|
|
2746
|
-
compactionStatus = "native checkpoint committed";
|
|
2747
|
-
else if (fallbackReason)
|
|
2748
|
-
compactionStatus = `portable: ${fallbackReason}`;
|
|
2749
|
-
else if (owned)
|
|
2750
|
-
compactionStatus = `${lastOutcome.path} portable summary committed`;
|
|
2751
|
-
else
|
|
2706
|
+
if (!owned && !fallbackReason && readNativeCompactionDetails(event.compactionEntry.details)?.version !== 3)
|
|
2752
2707
|
fallback("another handler supplied the committed result");
|
|
2753
2708
|
if (owned) {
|
|
2754
2709
|
if (configuration().outcomeNotifications && lastOutcome)
|
|
2755
2710
|
context.ui.notify(`Compacted via ${lastOutcome.path} in ${(lastOutcome.elapsedMs / 1000).toFixed(1)}s${lastOutcome.usage ? `; cache read ${lastOutcome.usage.cacheRead}, write ${lastOutcome.usage.cacheWrite}; estimated cost $${lastOutcome.usage.cost.total.toFixed(6)}` : ""}`, "info");
|
|
2756
|
-
}
|
|
2757
|
-
lastOutcome = { path: "Pi/other handler", elapsedMs: 0, usage: event.compactionEntry.usage, reason: compactionStatus };
|
|
2711
|
+
}
|
|
2758
2712
|
if (fallbackReason)
|
|
2759
2713
|
pi.appendEntry("pi-provider-compaction:diagnostic", { status: `portable: ${fallbackReason}` });
|
|
2760
2714
|
active = undefined;
|
|
@@ -2783,7 +2737,6 @@ Outcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usa
|
|
|
2783
2737
|
pending = undefined;
|
|
2784
2738
|
snapshot = undefined;
|
|
2785
2739
|
cache = undefined;
|
|
2786
|
-
compactionStatus = "compaction failed or cancelled";
|
|
2787
2740
|
});
|
|
2788
2741
|
}
|
|
2789
2742
|
export {
|
package/index.ts
CHANGED
|
@@ -174,21 +174,9 @@ export default function (pi: ExtensionAPI): void {
|
|
|
174
174
|
let unregisterCleanup: (() => void) | undefined;
|
|
175
175
|
let generation = 0;
|
|
176
176
|
let requestSequence = 0;
|
|
177
|
-
let captureStatus = "no request observed";
|
|
178
|
-
let compactionStatus = "not attempted in this runtime";
|
|
179
177
|
let fallbackReason: string | undefined;
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
pi.registerCommand("provider-compaction-status", {
|
|
183
|
-
description: "Show cache/native eligibility and measured compaction usage",
|
|
184
|
-
handler: async (_args, context) => {
|
|
185
|
-
const state = cacheState(context);
|
|
186
|
-
const remaining = state.expiresAt === undefined ? "unknown lifetime" : `estimated ${Math.max(0, Math.ceil((state.expiresAt - Date.now()) / 1000))}s remaining (model declaration; best effort)`;
|
|
187
|
-
const usage = lastOutcome?.usage;
|
|
188
|
-
context.ui.notify(`Provider compaction\nObserver: ${observer?.isInstalled() ? "installed" : "unavailable for current registration"}\nCapture: ${captureStatus}\nCache: ${state.reason}; ${remaining}\nLast attempt: ${compactionStatus}${lastOutcome ? `\nOutcome: ${lastOutcome.path}, ${lastOutcome.elapsedMs}ms${usage ? `; input ${usage.input}, cache read ${usage.cacheRead}, write ${usage.cacheWrite}, output ${usage.output}; estimated cost $${usage.cost.total.toFixed(6)}` : "; usage unavailable"}${lastOutcome.reason ? `; ${lastOutcome.reason}` : ""}` : ""}`, "info");
|
|
189
|
-
},
|
|
190
|
-
});
|
|
191
|
-
const invalidate = () => { generation++; runtimeId = randomUUID(); snapshot = undefined; cache = undefined; active = undefined; pending = undefined; expiryAttempt = undefined; captureStatus = "invalidated by lifecycle/history/policy change"; };
|
|
178
|
+
const fallback = (reason: string) => { fallbackReason = reason; };
|
|
179
|
+
const invalidate = () => { generation++; runtimeId = randomUUID(); snapshot = undefined; cache = undefined; active = undefined; pending = undefined; expiryAttempt = undefined; };
|
|
192
180
|
const retire = () => {
|
|
193
181
|
usageObservers.clear();
|
|
194
182
|
invalidate();
|
|
@@ -260,17 +248,11 @@ export default function (pi: ExtensionAPI): void {
|
|
|
260
248
|
}
|
|
261
249
|
if (options.sessionId !== manager.getSessionId()) return;
|
|
262
250
|
if (compactionRequest) return;
|
|
263
|
-
if (!supportsNativeCompaction(model) && !supportsCachedSummary(model))
|
|
264
|
-
if (!options.onPayload)
|
|
265
|
-
// The portable-summary request re-enters this observer without a payload
|
|
266
|
-
// callback. Do not overwrite the main request's capture diagnostic.
|
|
267
|
-
if (captureStatus !== "eligible prepared request captured") captureRejected("no payload callback");
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
if (generic(owner)) { captureRejected("generic compaction model takes precedence"); return; }
|
|
251
|
+
if (!supportsNativeCompaction(model) && !supportsCachedSummary(model)) return;
|
|
252
|
+
if (!options.onPayload || generic(owner)) return;
|
|
271
253
|
// The Azure adapter also accepts API-specific overrides and process-env
|
|
272
254
|
// fallbacks. Those must not silently select a route absent from our evidence.
|
|
273
|
-
if (!supportedRouteOptions(model, options))
|
|
255
|
+
if (!supportedRouteOptions(model, options)) return;
|
|
274
256
|
const ticket = generation;
|
|
275
257
|
const startedAt = Date.now();
|
|
276
258
|
const sequence = ++requestSequence;
|
|
@@ -296,11 +278,10 @@ export default function (pi: ExtensionAPI): void {
|
|
|
296
278
|
const normalizedHeaders = (headers: ProviderHeaders | undefined) => Object.entries(headers ?? {}).map(([name, value]) => [name.toLowerCase(), value]).sort(([a], [b]) => String(a).localeCompare(String(b)));
|
|
297
279
|
if (!auth.ok || generic(owner) || auth.apiKey !== options.apiKey || (auth.baseUrl ?? model.baseUrl) !== model.baseUrl || !equal(normalizedHeaders(auth.headers), normalizedHeaders(options.headers)) || !equal(auth.env ?? {}, options.env ?? {})) {
|
|
298
280
|
active = undefined; cache = undefined;
|
|
299
|
-
captureRejected("resolved auth/route differs from prepared request, or generic policy changed");
|
|
300
281
|
return payload;
|
|
301
282
|
}
|
|
302
283
|
const provider = owner.modelRegistry.getProvider(model.provider);
|
|
303
|
-
if (!provider)
|
|
284
|
+
if (!provider) return payload;
|
|
304
285
|
const routeHash = route(model, options);
|
|
305
286
|
if (config.cachedSummaries && supportsCachedSummary(model)) {
|
|
306
287
|
const warming = options.maxTokens === 1 && cache && cache.authHash === routeDigest(auth) && sameCachedPrompt(cache.payload, payload) && equal(cache.context, context);
|
|
@@ -310,8 +291,8 @@ export default function (pi: ExtensionAPI): void {
|
|
|
310
291
|
}, payload);
|
|
311
292
|
if (!warming) cache = undefined;
|
|
312
293
|
}
|
|
313
|
-
if (!supportsNativeCompaction(model))
|
|
314
|
-
const prepared = capturePreparedRequest(model, context, canonical, payload
|
|
294
|
+
if (!supportsNativeCompaction(model)) return payload;
|
|
295
|
+
const prepared = capturePreparedRequest(model, context, canonical, payload);
|
|
315
296
|
if (!prepared) { active = undefined; return payload; }
|
|
316
297
|
const policyHash = digest(requestPolicy(model, prepared));
|
|
317
298
|
const latest = latestCompaction(branch);
|
|
@@ -334,7 +315,6 @@ export default function (pi: ExtensionAPI): void {
|
|
|
334
315
|
} else active = undefined;
|
|
335
316
|
} else active = undefined;
|
|
336
317
|
} else active = undefined;
|
|
337
|
-
captureStatus = "eligible prepared request captured";
|
|
338
318
|
snapshot = {
|
|
339
319
|
prepared, model: structuredClone(model), provider,
|
|
340
320
|
options: { apiKey: options.apiKey, headers: structuredClone(options.headers), env: structuredClone(options.env), fetch: options.fetch, ...(typeof (options as { serviceTier?: unknown }).serviceTier === "string" ? { serviceTier: (options as { serviceTier: string }).serviceTier } : {}) },
|
|
@@ -359,7 +339,6 @@ export default function (pi: ExtensionAPI): void {
|
|
|
359
339
|
if (observer) { invalidate(); observer.remove(); observer = undefined; }
|
|
360
340
|
if (!supportsNativeCompaction(context.model) && !supportsCachedSummary(context.model)) return;
|
|
361
341
|
observer = installObserver(pi, context, observe);
|
|
362
|
-
captureStatus = observer ? "awaiting prepared request" : "unsupported provider registration";
|
|
363
342
|
};
|
|
364
343
|
pi.on("session_start", (_event, context) => {
|
|
365
344
|
retire();
|
|
@@ -386,15 +365,21 @@ export default function (pi: ExtensionAPI): void {
|
|
|
386
365
|
pi.on("session_shutdown", () => { if (typeof removeInvalidation === "function") removeInvalidation(); });
|
|
387
366
|
|
|
388
367
|
pi.on("session_before_compact", async (event, context) => {
|
|
389
|
-
const began = Date.now();
|
|
390
|
-
|
|
368
|
+
const began = Date.now(), ticket = generation;
|
|
369
|
+
const sessionId = context.sessionManager.getSessionId();
|
|
391
370
|
fallbackReason = undefined;
|
|
392
371
|
const attempt = expiryAttempt;
|
|
393
372
|
const capture = snapshot;
|
|
394
373
|
const cached = cacheState(context).available ? cache : undefined;
|
|
395
374
|
snapshot = undefined; cache = undefined; pending = undefined;
|
|
396
375
|
const cancelled = (usage?: Usage, reason = "compaction ownership or input changed") => {
|
|
397
|
-
|
|
376
|
+
// A cancelled attempt has no checkpoint to carry its known bill. Retain
|
|
377
|
+
// that usage in the existing session diagnostic, not a command-only cache.
|
|
378
|
+
if (usage && ticket === generation && ctx) {
|
|
379
|
+
try {
|
|
380
|
+
if (ctx.sessionManager.getSessionId() === sessionId) pi.appendEntry("pi-provider-compaction:diagnostic", { status: `${attempt ? "expiry cancelled" : "cancelled"}: ${reason}`, usage });
|
|
381
|
+
} catch { /* A disposed session cannot accept a diagnostic. */ }
|
|
382
|
+
}
|
|
398
383
|
return { cancel: true as const };
|
|
399
384
|
};
|
|
400
385
|
const reject = (reason: string) => {
|
|
@@ -408,7 +393,6 @@ export default function (pi: ExtensionAPI): void {
|
|
|
408
393
|
attempt.signal = event.signal;
|
|
409
394
|
}
|
|
410
395
|
if ((!capture && !cached) || !context.model || generic(context) || event.signal.aborted) return reject("no eligible snapshot, explicit model override, or aborted attempt");
|
|
411
|
-
const ticket = generation;
|
|
412
396
|
const branch = context.sessionManager.getBranch();
|
|
413
397
|
const branchHash = digest(branch);
|
|
414
398
|
const source = cached ?? capture!;
|
|
@@ -431,7 +415,6 @@ export default function (pi: ExtensionAPI): void {
|
|
|
431
415
|
let summaryPath = "standard";
|
|
432
416
|
let summaryReason: string | undefined;
|
|
433
417
|
if (cached) {
|
|
434
|
-
compactionStatus = "generating cached portable summary";
|
|
435
418
|
compactionRequest = true;
|
|
436
419
|
try {
|
|
437
420
|
const result = await cachedSummary(cached, context, event.preparation, event.customInstructions, event.signal, current);
|
|
@@ -446,7 +429,6 @@ export default function (pi: ExtensionAPI): void {
|
|
|
446
429
|
if (attempt) return cancelled(attemptedUsage, summaryReason ?? "warm cache unavailable");
|
|
447
430
|
// Without native coverage or a dispatched cached attempt, Pi can own fallback.
|
|
448
431
|
if (!input && !attemptedUsage) return reject(summaryReason ?? fallbackReason ?? "native coverage unavailable");
|
|
449
|
-
compactionStatus = "generating portable summary";
|
|
450
432
|
try {
|
|
451
433
|
portable = await compact(event.preparation, source.model, auth.apiKey, auth.headers as Record<string, string> | undefined, event.customInstructions, event.signal, context.thinkingLevel, source.provider.streamSimple.bind(source.provider), auth.env, undefined, undefined, source.sessionId);
|
|
452
434
|
portable = withFileTracking(portable, event.preparation);
|
|
@@ -463,14 +445,12 @@ export default function (pi: ExtensionAPI): void {
|
|
|
463
445
|
return { compaction: result! };
|
|
464
446
|
};
|
|
465
447
|
if (!capture || !protocol || !input) {
|
|
466
|
-
compactionStatus = `${summaryPath} portable summary prepared`;
|
|
467
448
|
return outcome(summaryPath, portable, summaryReason);
|
|
468
449
|
}
|
|
469
450
|
try {
|
|
470
451
|
const freshAuth = await context.modelRegistry.getApiKeyAndHeaders(context.model);
|
|
471
452
|
if (!current()) return cancelled(portable.usage);
|
|
472
453
|
if (!freshAuth.ok || routeDigest(freshAuth) !== capture.authHash) { fallback("auth changed after portable generation"); return outcome(summaryPath, portable, "native auth changed"); }
|
|
473
|
-
compactionStatus = "requesting native checkpoint";
|
|
474
454
|
let native: Awaited<ReturnType<typeof requestProviderCompaction>>;
|
|
475
455
|
compactionRequest = true;
|
|
476
456
|
try {
|
|
@@ -512,13 +492,10 @@ export default function (pi: ExtensionAPI): void {
|
|
|
512
492
|
pi.on("session_compact", (event, context) => {
|
|
513
493
|
const recorded = isObject(event.compactionEntry.details) ? event.compactionEntry.details.providerCompaction : undefined;
|
|
514
494
|
const owned = isObject(recorded) && equal(recorded, lastOutcome);
|
|
515
|
-
if (readNativeCompactionDetails(event.compactionEntry.details)?.version
|
|
516
|
-
else if (fallbackReason) compactionStatus = `portable: ${fallbackReason}`;
|
|
517
|
-
else if (owned) compactionStatus = `${lastOutcome!.path} portable summary committed`;
|
|
518
|
-
else fallback("another handler supplied the committed result");
|
|
495
|
+
if (!owned && !fallbackReason && readNativeCompactionDetails(event.compactionEntry.details)?.version !== 3) fallback("another handler supplied the committed result");
|
|
519
496
|
if (owned) {
|
|
520
497
|
if (configuration().outcomeNotifications && lastOutcome) context.ui.notify(`Compacted via ${lastOutcome.path} in ${(lastOutcome.elapsedMs / 1000).toFixed(1)}s${lastOutcome.usage ? `; cache read ${lastOutcome.usage.cacheRead}, write ${lastOutcome.usage.cacheWrite}; estimated cost $${lastOutcome.usage.cost.total.toFixed(6)}` : ""}`, "info");
|
|
521
|
-
}
|
|
498
|
+
}
|
|
522
499
|
// Only static diagnostics are persisted, never request/auth data or error text.
|
|
523
500
|
if (fallbackReason) pi.appendEntry("pi-provider-compaction:diagnostic", { status: `portable: ${fallbackReason}` });
|
|
524
501
|
active = undefined; snapshot = undefined; cache = undefined;
|
|
@@ -538,5 +515,5 @@ export default function (pi: ExtensionAPI): void {
|
|
|
538
515
|
...(typeof candidate.capture.prepared.payload.model === "string" ? { payloadModel: candidate.capture.prepared.payload.model } : {}),
|
|
539
516
|
};
|
|
540
517
|
});
|
|
541
|
-
pi.on("session_compact_failed", () => { pending = undefined; snapshot = undefined; cache = undefined;
|
|
518
|
+
pi.on("session_compact_failed", () => { pending = undefined; snapshot = undefined; cache = undefined; });
|
|
542
519
|
}
|