@prestyj/cli 5.19.0 → 5.21.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.
- package/README.md +1 -1
- package/dist/app-sidecar.js +120 -0
- package/dist/app-sidecar.js.map +1 -1
- package/dist/core/agent-session.d.ts +29 -0
- package/dist/core/agent-session.js +139 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/thinking-level.d.ts +1 -1
- package/dist/core/thinking-level.js +1 -1
- package/dist/core/thinking-level.js.map +1 -1
- package/dist/core/worktree.d.ts +161 -0
- package/dist/core/worktree.js +574 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/tools/subagent.d.ts +1 -0
- package/dist/tools/subagent.js +66 -2
- package/dist/tools/subagent.js.map +1 -1
- package/package.json +5 -5
|
@@ -260,6 +260,15 @@ export declare class AgentSession {
|
|
|
260
260
|
* only on a real edge. */
|
|
261
261
|
private verificationArmed;
|
|
262
262
|
private compactionOccurred;
|
|
263
|
+
/**
|
|
264
|
+
* Re-grounding carry-over for post-turn compaction. `resetHookState` clears
|
|
265
|
+
* `compactionOccurred` at run start — before the injection point — so a
|
|
266
|
+
* background compaction that finished after the final response parks its
|
|
267
|
+
* "re-ground next turn" signal here for pickup at the next run start.
|
|
268
|
+
*/
|
|
269
|
+
private compactionArmedForNextRun;
|
|
270
|
+
/** In-flight post-turn (background) compaction, if any. */
|
|
271
|
+
private postTurnCompaction?;
|
|
263
272
|
private lastCompactionCompacted;
|
|
264
273
|
private compactionRetryAfter;
|
|
265
274
|
/** A restored oversized checkpoint must be canonicalized before its first prompt is persisted. */
|
|
@@ -574,6 +583,26 @@ export declare class AgentSession {
|
|
|
574
583
|
/** Canonicalize a deferred restore before a new prompt can fork stale history. */
|
|
575
584
|
private adoptDeferredCheckpointBeforePrompt;
|
|
576
585
|
private persistCompactionCheckpoint;
|
|
586
|
+
/**
|
|
587
|
+
* Wait out any in-flight post-turn background compaction before a new
|
|
588
|
+
* entry point mutates `this.messages` (prompt, attachments, model switch).
|
|
589
|
+
* The background compact() snapshots and then REPLACES the array, so a
|
|
590
|
+
* message pushed while it runs would be silently dropped from the live
|
|
591
|
+
* history — the pre-run await in runLoop() sits after the push and cannot
|
|
592
|
+
* protect it.
|
|
593
|
+
*/
|
|
594
|
+
private settlePostTurnCompaction;
|
|
595
|
+
/**
|
|
596
|
+
* Post-turn compaction: once the final response has been delivered, compact
|
|
597
|
+
* in the background while the user reads the answer, instead of making the
|
|
598
|
+
* next prompt pay the summarizer latency up front (the pre-run path stays as
|
|
599
|
+
* the backstop, so a skipped or failed attempt costs nothing). Mirrors the
|
|
600
|
+
* Codex `model_post_turn_compact_threshold_percent` guards: skip when user
|
|
601
|
+
* input is already queued (it would race the next turn), when the run was
|
|
602
|
+
* aborted, or during the failure cooldown — and never let a compaction
|
|
603
|
+
* error surface in the completed turn.
|
|
604
|
+
*/
|
|
605
|
+
private maybeCompactPostTurn;
|
|
577
606
|
compact(existingCredentials?: {
|
|
578
607
|
accessToken: string;
|
|
579
608
|
accountId?: string;
|
|
@@ -37,6 +37,7 @@ import { log } from "./logger.js";
|
|
|
37
37
|
import { setEstimatorModel, calibrateEstimatorFromUsage } from "./compaction/token-estimator.js";
|
|
38
38
|
import { calculateActiveContextTokens } from "./compaction/active-context.js";
|
|
39
39
|
import { resolveCompactionPolicy } from "./compaction/policy.js";
|
|
40
|
+
import { clampThinkingForPlanMode } from "./thinking-level.js";
|
|
40
41
|
import { pruneStaleToolResults } from "./compaction/tool-result-pruner.js";
|
|
41
42
|
import { discoverAgents } from "./agents.js";
|
|
42
43
|
import { enhancePrompt } from "../utils/prompt-enhancer.js";
|
|
@@ -199,6 +200,15 @@ export class AgentSession {
|
|
|
199
200
|
* only on a real edge. */
|
|
200
201
|
verificationArmed = false;
|
|
201
202
|
compactionOccurred = false;
|
|
203
|
+
/**
|
|
204
|
+
* Re-grounding carry-over for post-turn compaction. `resetHookState` clears
|
|
205
|
+
* `compactionOccurred` at run start — before the injection point — so a
|
|
206
|
+
* background compaction that finished after the final response parks its
|
|
207
|
+
* "re-ground next turn" signal here for pickup at the next run start.
|
|
208
|
+
*/
|
|
209
|
+
compactionArmedForNextRun = false;
|
|
210
|
+
/** In-flight post-turn (background) compaction, if any. */
|
|
211
|
+
postTurnCompaction;
|
|
202
212
|
lastCompactionCompacted = false;
|
|
203
213
|
compactionRetryAfter = 0;
|
|
204
214
|
/** A restored oversized checkpoint must be canonicalized before its first prompt is persisted. */
|
|
@@ -889,6 +899,7 @@ export class AgentSession {
|
|
|
889
899
|
kind: "prompt",
|
|
890
900
|
visibility: "transcript",
|
|
891
901
|
}, options = {}) {
|
|
902
|
+
await this.settlePostTurnCompaction();
|
|
892
903
|
await this.adoptDeferredCheckpointBeforePrompt();
|
|
893
904
|
const slash = await this.resolveSlashInput(content);
|
|
894
905
|
if (slash?.kind === "template") {
|
|
@@ -923,6 +934,7 @@ export class AgentSession {
|
|
|
923
934
|
* attachments are always a direct conversational turn.
|
|
924
935
|
*/
|
|
925
936
|
async promptWithAttachments(text, attachments) {
|
|
937
|
+
await this.settlePostTurnCompaction();
|
|
926
938
|
await this.adoptDeferredCheckpointBeforePrompt();
|
|
927
939
|
const parts = this.buildAttachmentParts(text, attachments);
|
|
928
940
|
if (parts.length === 0)
|
|
@@ -1063,6 +1075,13 @@ export class AgentSession {
|
|
|
1063
1075
|
this.backgroundVerification.delete(id);
|
|
1064
1076
|
}
|
|
1065
1077
|
this.compactionOccurred = false;
|
|
1078
|
+
// Post-turn compaction may have landed between runs — adopt its armed
|
|
1079
|
+
// signal so the re-grounding hook fires for this run exactly as it would
|
|
1080
|
+
// after a pre-run compaction.
|
|
1081
|
+
if (this.compactionArmedForNextRun) {
|
|
1082
|
+
this.compactionArmedForNextRun = false;
|
|
1083
|
+
this.compactionOccurred = true;
|
|
1084
|
+
}
|
|
1066
1085
|
this.originalRequest = originalRequest;
|
|
1067
1086
|
}
|
|
1068
1087
|
/**
|
|
@@ -1930,6 +1949,11 @@ export class AgentSession {
|
|
|
1930
1949
|
// of the public API model window. Failed/no-op attempts cool down across
|
|
1931
1950
|
// prompts; provider overflow recovery still bypasses this path entirely.
|
|
1932
1951
|
if (this.settingsManager.get("autoCompact") && Date.now() >= this.compactionRetryAfter) {
|
|
1952
|
+
// A post-turn compaction may still be running in the background. Let it
|
|
1953
|
+
// settle first: it usually already compacted this history, so the
|
|
1954
|
+
// decision below turns into a no-op instead of duplicate work.
|
|
1955
|
+
if (this.postTurnCompaction)
|
|
1956
|
+
await this.postTurnCompaction;
|
|
1933
1957
|
const contextWindow = getContextWindow(this.model, {
|
|
1934
1958
|
provider: this.provider,
|
|
1935
1959
|
accountId: creds.accountId,
|
|
@@ -1997,7 +2021,13 @@ export class AgentSession {
|
|
|
1997
2021
|
maxTokens: this.maxTokens,
|
|
1998
2022
|
maxTurns: this.opts.maxTurns,
|
|
1999
2023
|
maxTurnExtensions: this.opts.maxTurnExtensions,
|
|
2000
|
-
|
|
2024
|
+
// Plan mode caps effort at medium (Codex `plan_mode_reasoning_effort`
|
|
2025
|
+
// preset): read-only exploration doesn't need xhigh/max reasoning, and
|
|
2026
|
+
// deep-reasoning models left at the ceiling burn enormous thinking
|
|
2027
|
+
// budgets re-deriving context they cannot act on.
|
|
2028
|
+
thinking: this.planModeRef.current
|
|
2029
|
+
? clampThinkingForPlanMode(this.thinkingLevel)
|
|
2030
|
+
: this.thinkingLevel,
|
|
2001
2031
|
apiKey,
|
|
2002
2032
|
// Per-turn credential resolution. A run can span many minutes; if any
|
|
2003
2033
|
// process sharing auth.json refreshes this grant meanwhile, the token
|
|
@@ -2263,8 +2293,15 @@ export class AgentSession {
|
|
|
2263
2293
|
await this.persistMessage(this.messages[i]);
|
|
2264
2294
|
}
|
|
2265
2295
|
this.lastPersistedIndex = this.messages.length;
|
|
2296
|
+
// Final response is delivered — compact in the background while the user
|
|
2297
|
+
// reads it, instead of charging the summarizer latency to their next
|
|
2298
|
+
// prompt. Detached by design; the pre-run path above remains the backstop.
|
|
2299
|
+
this.maybeCompactPostTurn(creds);
|
|
2266
2300
|
}
|
|
2267
2301
|
async switchModel(provider, model) {
|
|
2302
|
+
// The model-switch note is appended to `this.messages`; settle any
|
|
2303
|
+
// background compaction first so the note cannot be swapped out.
|
|
2304
|
+
await this.settlePostTurnCompaction();
|
|
2268
2305
|
const prevProvider = this.provider;
|
|
2269
2306
|
const prevModel = this.model;
|
|
2270
2307
|
// Diff gate: a "switch" to the model already in use is not state change.
|
|
@@ -2465,6 +2502,101 @@ export class AgentSession {
|
|
|
2465
2502
|
newCount: result.newCount,
|
|
2466
2503
|
});
|
|
2467
2504
|
}
|
|
2505
|
+
/**
|
|
2506
|
+
* Wait out any in-flight post-turn background compaction before a new
|
|
2507
|
+
* entry point mutates `this.messages` (prompt, attachments, model switch).
|
|
2508
|
+
* The background compact() snapshots and then REPLACES the array, so a
|
|
2509
|
+
* message pushed while it runs would be silently dropped from the live
|
|
2510
|
+
* history — the pre-run await in runLoop() sits after the push and cannot
|
|
2511
|
+
* protect it.
|
|
2512
|
+
*/
|
|
2513
|
+
async settlePostTurnCompaction() {
|
|
2514
|
+
if (this.postTurnCompaction)
|
|
2515
|
+
await this.postTurnCompaction;
|
|
2516
|
+
}
|
|
2517
|
+
/**
|
|
2518
|
+
* Post-turn compaction: once the final response has been delivered, compact
|
|
2519
|
+
* in the background while the user reads the answer, instead of making the
|
|
2520
|
+
* next prompt pay the summarizer latency up front (the pre-run path stays as
|
|
2521
|
+
* the backstop, so a skipped or failed attempt costs nothing). Mirrors the
|
|
2522
|
+
* Codex `model_post_turn_compact_threshold_percent` guards: skip when user
|
|
2523
|
+
* input is already queued (it would race the next turn), when the run was
|
|
2524
|
+
* aborted, or during the failure cooldown — and never let a compaction
|
|
2525
|
+
* error surface in the completed turn.
|
|
2526
|
+
*/
|
|
2527
|
+
maybeCompactPostTurn(creds) {
|
|
2528
|
+
if (!this.settingsManager.get("autoCompact"))
|
|
2529
|
+
return;
|
|
2530
|
+
if (this.opts.signal?.aborted)
|
|
2531
|
+
return;
|
|
2532
|
+
if (this.userQueue.length > 0)
|
|
2533
|
+
return;
|
|
2534
|
+
if (this.postTurnCompaction)
|
|
2535
|
+
return;
|
|
2536
|
+
if (Date.now() < this.compactionRetryAfter)
|
|
2537
|
+
return;
|
|
2538
|
+
// One compaction per turn boundary: a pre-run or overflow-recovery
|
|
2539
|
+
// compaction already shrank this run's history — re-probing right after
|
|
2540
|
+
// the final response would only re-derive that decision.
|
|
2541
|
+
if (this.compactionOccurred)
|
|
2542
|
+
return;
|
|
2543
|
+
const contextWindow = getContextWindow(this.model, {
|
|
2544
|
+
provider: this.provider,
|
|
2545
|
+
accountId: creds.accountId,
|
|
2546
|
+
});
|
|
2547
|
+
const policy = resolveCompactionPolicy({
|
|
2548
|
+
provider: this.provider,
|
|
2549
|
+
model: this.model,
|
|
2550
|
+
contextWindow,
|
|
2551
|
+
threshold: this.settingsManager.get("compactThreshold"),
|
|
2552
|
+
accountId: creds.accountId,
|
|
2553
|
+
approvedPlanPath: this.approvedPlanPath,
|
|
2554
|
+
});
|
|
2555
|
+
let activeTokens;
|
|
2556
|
+
if (this.providerContext) {
|
|
2557
|
+
const anchorIndex = this.messages.lastIndexOf(this.providerContext.anchor);
|
|
2558
|
+
if (anchorIndex >= 0) {
|
|
2559
|
+
activeTokens = calculateActiveContextTokens(this.messages, {
|
|
2560
|
+
usage: this.providerContext.usage,
|
|
2561
|
+
pendingMessages: this.messages.slice(anchorIndex + 1),
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
if (!shouldCompact(this.messages, contextWindow, policy.threshold, activeTokens))
|
|
2566
|
+
return;
|
|
2567
|
+
log("INFO", "compaction", "Post-turn compaction decision — compacting in background", {
|
|
2568
|
+
provider: this.provider,
|
|
2569
|
+
model: this.model,
|
|
2570
|
+
transport: this.provider === "openai" && creds.accountId ? "codex_oauth" : "public_api",
|
|
2571
|
+
contextWindow: String(contextWindow),
|
|
2572
|
+
activeTokens: activeTokens === undefined ? "estimated" : String(activeTokens),
|
|
2573
|
+
triggerLimit: String(policy.targetTokens),
|
|
2574
|
+
});
|
|
2575
|
+
this.postTurnCompaction = (async () => {
|
|
2576
|
+
try {
|
|
2577
|
+
await this.compact(creds, "automatic");
|
|
2578
|
+
if (this.lastCompactionCompacted) {
|
|
2579
|
+
// Arm re-grounding for the next turn. resetHookState clears
|
|
2580
|
+
// compactionOccurred at run start, before the injection point, so
|
|
2581
|
+
// park the signal for pickup there.
|
|
2582
|
+
this.compactionArmedForNextRun = true;
|
|
2583
|
+
this.compactionRetryAfter = 0;
|
|
2584
|
+
}
|
|
2585
|
+
else {
|
|
2586
|
+
this.compactionRetryAfter = Date.now() + 30_000;
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2589
|
+
catch (error) {
|
|
2590
|
+
this.compactionRetryAfter = Date.now() + 30_000;
|
|
2591
|
+
if (isAbortError(error) || this.opts.signal?.aborted)
|
|
2592
|
+
return;
|
|
2593
|
+
log("WARN", "compaction", `Post-turn compaction failed; cooling down for 30s: ${error instanceof Error ? error.message : String(error)}`);
|
|
2594
|
+
}
|
|
2595
|
+
finally {
|
|
2596
|
+
this.postTurnCompaction = undefined;
|
|
2597
|
+
}
|
|
2598
|
+
})();
|
|
2599
|
+
}
|
|
2468
2600
|
async compact(existingCredentials, mode = "manual",
|
|
2469
2601
|
/** User-stated focus (`/compact <focus>`): what must survive verbatim. */
|
|
2470
2602
|
focus) {
|
|
@@ -3439,6 +3571,12 @@ export class AgentSession {
|
|
|
3439
3571
|
return this.getPromptCacheKey();
|
|
3440
3572
|
}
|
|
3441
3573
|
async dispose() {
|
|
3574
|
+
// Quiesce any in-flight post-turn compaction BEFORE tearing down state:
|
|
3575
|
+
// the background compact() snapshots and replaces `this.messages`, so
|
|
3576
|
+
// letting it run past this point would checkpoint a near-empty history
|
|
3577
|
+
// and leak a junk session file after teardown.
|
|
3578
|
+
if (this.postTurnCompaction)
|
|
3579
|
+
await this.postTurnCompaction;
|
|
3442
3580
|
this.diagnosticsRecorder?.finalize();
|
|
3443
3581
|
this.managerAbortSignal?.removeEventListener("abort", this.managerAbortHandler);
|
|
3444
3582
|
this.processManager?.shutdownAll();
|