@prestyj/cli 5.28.1 → 5.29.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/assets/motion/bin/contact-sheet.mjs +9 -3
- package/assets/motion/bin/cues.mjs +337 -0
- package/assets/motion/bin/library.mjs +53 -10
- package/assets/motion/bin/motion-blur.mjs +943 -0
- package/assets/motion/bin/motion-check.mjs +354 -3
- package/assets/motion/bin/music-fit.mjs +436 -0
- package/assets/motion/bin/pdf-extract.mjs +22 -10
- package/assets/motion/bin/reference-study.mjs +346 -0
- package/assets/motion/bin/score-synth.mjs +1052 -93
- package/assets/motion/library/README.md +50 -14
- package/assets/motion/library/kit/moves.js +1981 -0
- package/assets/motion/library/library.json +232 -0
- package/assets/motion/library/pieces/camera-rig/meta.json +13 -0
- package/assets/motion/library/pieces/camera-rig/piece.html +153 -0
- package/assets/motion/library/pieces/camera-rig/preview.jpg +0 -0
- package/assets/motion/library/pieces/chain-knock/meta.json +13 -0
- package/assets/motion/library/pieces/chain-knock/piece.html +195 -0
- package/assets/motion/library/pieces/chain-knock/preview.jpg +0 -0
- package/assets/motion/library/pieces/gather-to-logo/meta.json +13 -0
- package/assets/motion/library/pieces/gather-to-logo/piece.html +159 -0
- package/assets/motion/library/pieces/gather-to-logo/preview.jpg +0 -0
- package/assets/motion/library/pieces/morph-carry/meta.json +13 -0
- package/assets/motion/library/pieces/morph-carry/piece.html +173 -0
- package/assets/motion/library/pieces/morph-carry/preview.jpg +0 -0
- package/assets/motion/library/pieces/one-shape-journey/meta.json +13 -0
- package/assets/motion/library/pieces/one-shape-journey/piece.html +195 -0
- package/assets/motion/library/pieces/one-shape-journey/preview.jpg +0 -0
- package/assets/motion/library/pieces/open-from-subject/meta.json +13 -0
- package/assets/motion/library/pieces/open-from-subject/piece.html +168 -0
- package/assets/motion/library/pieces/open-from-subject/preview.jpg +0 -0
- package/assets/motion/library/pieces/request-to-result/meta.json +13 -0
- package/assets/motion/library/pieces/request-to-result/piece.html +212 -0
- package/assets/motion/library/pieces/request-to-result/preview.jpg +0 -0
- package/assets/motion/library/pieces/scale-dive/meta.json +13 -0
- package/assets/motion/library/pieces/scale-dive/piece.html +321 -0
- package/assets/motion/library/pieces/scale-dive/preview.jpg +0 -0
- package/assets/motion/library/pieces/screen-replica-steps/meta.json +13 -0
- package/assets/motion/library/pieces/screen-replica-steps/piece.html +366 -0
- package/assets/motion/library/pieces/screen-replica-steps/preview.jpg +0 -0
- package/assets/motion/library/pieces/zoom-into-card/meta.json +13 -0
- package/assets/motion/library/pieces/zoom-into-card/piece.html +179 -0
- package/assets/motion/library/pieces/zoom-into-card/preview.jpg +0 -0
- package/assets/motion/library/sheets/diagram.jpg +0 -0
- package/assets/motion/library/sheets/frame.jpg +0 -0
- package/assets/motion/library/sheets/transition.jpg +0 -0
- package/assets/motion/library/sheets/ui.jpg +0 -0
- package/assets/motion/references/build-sheet.md +206 -0
- package/assets/motion/references/runtime/determinism-rules.md +1 -1
- package/assets/motion/references/runtime/gsap-easing-and-stagger.md +29 -29
- package/assets/motion/references/runtime/inputs-and-assets.md +7 -12
- package/assets/motion/references/runtime/lint-validate-inspect.md +3 -3
- package/assets/motion/references/runtime/minimal-composition.md +1 -1
- package/assets/motion/references/runtime/preview-render.md +3 -3
- package/assets/motion/skills/app-walkthrough/SKILL.md +70 -0
- package/assets/motion/skills/before-after/SKILL.md +57 -0
- package/assets/motion/skills/brand-kit/SKILL.md +3 -3
- package/assets/motion/skills/dev-tool-video/SKILL.md +61 -0
- package/assets/motion/skills/launch-video/SKILL.md +66 -0
- package/assets/motion/skills/match-reference/SKILL.md +62 -0
- package/assets/motion/skills/motion/SKILL.md +148 -98
- package/assets/motion/skills/source-ingest/SKILL.md +17 -6
- package/assets/motion/skills/website-video/SKILL.md +76 -0
- package/assets/skills/bulletproof/SKILL.md +36 -11
- package/assets/skills/bulletproof/references/agent-surface.md +19 -9
- package/assets/skills/bulletproof/references/audit-protocol.md +20 -5
- package/assets/skills/bulletproof/references/platform-playbooks.md +5 -4
- package/assets/skills/bulletproof/references/provenance.md +26 -1
- package/assets/skills/bulletproof/references/secure-defaults.md +6 -5
- package/assets/skills/bulletproof/references/supply-chain.md +21 -17
- package/assets/skills/bulletproof/references/threat-landscape.md +28 -26
- package/assets/skills/bulletproof/references/verification.md +2 -0
- package/assets/skills/clarify/SKILL.md +25 -16
- package/assets/skills/code-review/SKILL.md +71 -13
- package/assets/skills/code-review/references/agent-diffs.md +27 -0
- package/assets/skills/code-review/references/tests.md +19 -0
- package/assets/skills/compliance-guard/SKILL.md +20 -5
- package/assets/skills/compliance-guard/references/artifacts.md +1 -1
- package/assets/skills/compliance-guard/references/eu-uk.md +16 -16
- package/assets/skills/compliance-guard/references/lawsuit-vectors.md +5 -5
- package/assets/skills/compliance-guard/references/provenance.md +41 -2
- package/assets/skills/compliance-guard/references/sector-gates.md +3 -3
- package/assets/skills/compliance-guard/references/security-baseline.md +2 -2
- package/assets/skills/compliance-guard/references/trigger-map.md +5 -5
- package/assets/skills/compliance-guard/references/us.md +27 -21
- package/assets/skills/durable/SKILL.md +87 -79
- package/assets/skills/durable/references/agent-db-safety.md +69 -0
- package/assets/skills/durable/references/backups-and-runtime.md +19 -12
- package/assets/skills/durable/references/migrations-and-schema.md +13 -6
- package/assets/skills/evidence-led-ui/SKILL.md +69 -127
- package/assets/skills/evidence-led-ui/references/anti-defaults.md +107 -208
- package/assets/skills/evidence-led-ui/references/direction.md +124 -0
- package/assets/skills/evidence-led-ui/references/production-contract.md +8 -0
- package/assets/skills/evidence-led-ui/references/provenance.md +24 -1
- package/assets/skills/lean/SKILL.md +90 -71
- package/assets/skills/lean/references/memory-and-processes.md +3 -2
- package/assets/skills/lean/references/playbooks.md +37 -12
- package/assets/skills/refactoring/SKILL.md +24 -3
- package/assets/skills/refactoring/references/agent-pitfalls.md +4 -1
- package/assets/skills/refactoring/references/legacy.md +21 -0
- package/assets/skills/root-cause/SKILL.md +20 -10
- package/assets/skills/shared-language/SKILL.md +16 -14
- package/assets/skills/tdd/SKILL.md +27 -15
- package/dist/app-sidecar.js +206 -47
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli/thinking-arg.d.ts +6 -0
- package/dist/cli/thinking-arg.js +13 -0
- package/dist/cli/thinking-arg.js.map +1 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +18 -34
- package/dist/cli.js.map +1 -1
- package/dist/core/acceptance-checks.d.ts +48 -0
- package/dist/core/acceptance-checks.js +144 -0
- package/dist/core/acceptance-checks.js.map +1 -0
- package/dist/core/agent-session.d.ts +107 -72
- package/dist/core/agent-session.js +562 -407
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agents.d.ts +6 -5
- package/dist/core/agents.js.map +1 -1
- package/dist/core/ask-user.d.ts +90 -8
- package/dist/core/ask-user.js +124 -13
- package/dist/core/ask-user.js.map +1 -1
- package/dist/core/bundled-agents.js +1 -3
- package/dist/core/bundled-agents.js.map +1 -1
- package/dist/core/cache-diagnostics.d.ts +68 -0
- package/dist/core/cache-diagnostics.js +196 -0
- package/dist/core/cache-diagnostics.js.map +1 -0
- package/dist/core/cache-expiry.d.ts +87 -0
- package/dist/core/cache-expiry.js +111 -0
- package/dist/core/cache-expiry.js.map +1 -0
- package/dist/core/codex-request-shape.d.ts +12 -0
- package/dist/core/codex-request-shape.js +31 -0
- package/dist/core/codex-request-shape.js.map +1 -0
- package/dist/core/compaction/compactor.js +84 -48
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/plan-step-policy.d.ts +46 -0
- package/dist/core/compaction/plan-step-policy.js +57 -0
- package/dist/core/compaction/plan-step-policy.js.map +1 -0
- package/dist/core/destructive-git-guard.d.ts +90 -0
- package/dist/core/destructive-git-guard.js +871 -0
- package/dist/core/destructive-git-guard.js.map +1 -0
- package/dist/core/event-bus.d.ts +3 -0
- package/dist/core/event-bus.js +5 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/injection-detect.d.ts +38 -0
- package/dist/core/injection-detect.js +232 -0
- package/dist/core/injection-detect.js.map +1 -0
- package/dist/core/keep-awake.d.ts +88 -0
- package/dist/core/keep-awake.js +251 -0
- package/dist/core/keep-awake.js.map +1 -0
- package/dist/core/mcp/client.d.ts +72 -0
- package/dist/core/mcp/client.js +264 -41
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/content.js +6 -2
- package/dist/core/mcp/content.js.map +1 -1
- package/dist/core/mcp/store.d.ts +6 -1
- package/dist/core/mcp/store.js +12 -1
- package/dist/core/mcp/store.js.map +1 -1
- package/dist/core/mcp/types.d.ts +18 -0
- package/dist/core/model-unavailable.d.ts +14 -0
- package/dist/core/model-unavailable.js +23 -0
- package/dist/core/model-unavailable.js.map +1 -0
- package/dist/core/node-debugger.d.ts +148 -0
- package/dist/core/node-debugger.js +642 -0
- package/dist/core/node-debugger.js.map +1 -0
- package/dist/core/nolan-context.js +7 -3
- package/dist/core/nolan-context.js.map +1 -1
- package/dist/core/package-threats.d.ts +18 -0
- package/dist/core/package-threats.js +168 -0
- package/dist/core/package-threats.js.map +1 -0
- package/dist/core/persistent-shell.d.ts +58 -6
- package/dist/core/persistent-shell.js +331 -49
- package/dist/core/persistent-shell.js.map +1 -1
- package/dist/core/process-manager.d.ts +14 -0
- package/dist/core/process-manager.js +61 -0
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/progress/git-xp.js +8 -14
- package/dist/core/progress/git-xp.js.map +1 -1
- package/dist/core/session-history.d.ts +12 -0
- package/dist/core/session-history.js +27 -0
- package/dist/core/session-history.js.map +1 -1
- package/dist/core/session-manager.d.ts +13 -1
- package/dist/core/session-manager.js +38 -18
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-summary-index.d.ts +37 -0
- package/dist/core/session-summary-index.js +172 -0
- package/dist/core/session-summary-index.js.map +1 -0
- package/dist/core/settings-manager.d.ts +12 -0
- package/dist/core/settings-manager.js +20 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/shell-threats-popular-packages.d.ts +11 -0
- package/dist/core/shell-threats-popular-packages.js +675 -0
- package/dist/core/shell-threats-popular-packages.js.map +1 -0
- package/dist/core/shell-threats.d.ts +8 -0
- package/dist/core/shell-threats.js +186 -0
- package/dist/core/shell-threats.js.map +1 -0
- package/dist/core/skills.js +3 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/stream-rules.d.ts +30 -0
- package/dist/core/stream-rules.js +151 -0
- package/dist/core/stream-rules.js.map +1 -0
- package/dist/core/subagent-manager.d.ts +20 -5
- package/dist/core/subagent-manager.js +22 -8
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-receipt.d.ts +54 -0
- package/dist/core/subagent-receipt.js +278 -0
- package/dist/core/subagent-receipt.js.map +1 -0
- package/dist/core/subagent-turn-record.d.ts +2 -0
- package/dist/core/subagent-turn-record.js.map +1 -1
- package/dist/core/test-impact.d.ts +73 -0
- package/dist/core/test-impact.js +467 -0
- package/dist/core/test-impact.js.map +1 -0
- 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/verification-gate.d.ts +2 -0
- package/dist/core/verification-gate.js +4 -0
- package/dist/core/verification-gate.js.map +1 -1
- package/dist/core/verification-snapshot.js +3 -5
- package/dist/core/verification-snapshot.js.map +1 -1
- package/dist/core/workspace-guard.d.ts +18 -7
- package/dist/core/workspace-guard.js +227 -60
- package/dist/core/workspace-guard.js.map +1 -1
- package/dist/interactive.js +2 -1
- package/dist/interactive.js.map +1 -1
- package/dist/modes/subagent-worker-mode.js +34 -6
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/motion-agent/motion-agent.d.ts +6 -2
- package/dist/motion-agent/motion-agent.js +5 -7
- package/dist/motion-agent/motion-agent.js.map +1 -1
- package/dist/motion-agent/motion-prompt.d.ts +1 -1
- package/dist/motion-agent/motion-prompt.js +16 -20
- package/dist/motion-agent/motion-prompt.js.map +1 -1
- package/dist/motion-agent/motion-review-session.js +7 -3
- package/dist/motion-agent/motion-review-session.js.map +1 -1
- package/dist/motion-agent/motion-review.d.ts +10 -3
- package/dist/motion-agent/motion-review.js +15 -7
- package/dist/motion-agent/motion-review.js.map +1 -1
- package/dist/motion-agent/motion-studio-context.js +1 -1
- package/dist/motion-agent/motion-studio-context.js.map +1 -1
- package/dist/system-prompt.js +15 -11
- package/dist/system-prompt.js.map +1 -1
- package/dist/test-support/keep-alive.d.ts +14 -0
- package/dist/test-support/keep-alive.js +17 -0
- package/dist/test-support/keep-alive.js.map +1 -0
- package/dist/tools/ask-user.js +3 -3
- package/dist/tools/ask-user.js.map +1 -1
- package/dist/tools/bash-read-evidence.d.ts +10 -0
- package/dist/tools/bash-read-evidence.js +133 -0
- package/dist/tools/bash-read-evidence.js.map +1 -0
- package/dist/tools/bash.d.ts +10 -1
- package/dist/tools/bash.js +115 -7
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/debug.d.ts +54 -0
- package/dist/tools/debug.js +233 -0
- package/dist/tools/debug.js.map +1 -0
- package/dist/tools/edit-targets.d.ts +10 -0
- package/dist/tools/edit-targets.js +31 -0
- package/dist/tools/edit-targets.js.map +1 -0
- package/dist/tools/edit.d.ts +11 -3
- package/dist/tools/edit.js +387 -305
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/goals.d.ts +1 -1
- package/dist/tools/index.d.ts +19 -2
- package/dist/tools/index.js +47 -7
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/prompt-hints.js +6 -1
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/read-tracker.d.ts +5 -0
- package/dist/tools/read-tracker.js +19 -9
- package/dist/tools/read-tracker.js.map +1 -1
- package/dist/tools/read.js +3 -2
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/skill.js +11 -3
- package/dist/tools/skill.js.map +1 -1
- package/dist/tools/subagent-control.js +44 -8
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/subagent-shared.d.ts +29 -8
- package/dist/tools/subagent-shared.js +45 -14
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/subagent.d.ts +14 -2
- package/dist/tools/subagent.js +43 -13
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/task-output.js +3 -2
- package/dist/tools/task-output.js.map +1 -1
- package/dist/tools/task-send.d.ts +1 -1
- package/dist/tools/task-send.js +15 -1
- package/dist/tools/task-send.js.map +1 -1
- package/dist/tools/tool-tiers.d.ts +8 -2
- package/dist/tools/tool-tiers.js +10 -3
- package/dist/tools/tool-tiers.js.map +1 -1
- package/dist/tools/truncate.d.ts +21 -0
- package/dist/tools/truncate.js +187 -0
- package/dist/tools/truncate.js.map +1 -1
- package/dist/tools/ui-adopt.js +2 -0
- package/dist/tools/ui-adopt.js.map +1 -1
- package/dist/ui/App.d.ts +0 -4
- package/dist/ui/App.js +12 -31
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.js +1 -0
- package/dist/ui/components/ActivityIndicator.js.map +1 -1
- package/dist/ui/components/ToolExecution.js +3 -2
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +1 -8
- package/dist/ui/hooks/useAgentLoop.js +1 -119
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/render.d.ts +0 -4
- package/dist/ui/render.js +0 -2
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/terminal-history.js +3 -1
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/tool-line-summary.js +3 -1
- package/dist/ui/tool-line-summary.js.map +1 -1
- package/dist/utils/format.js +5 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/git.d.ts +77 -0
- package/dist/utils/git.js +285 -21
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/github-ci.js +2 -1
- package/dist/utils/github-ci.js.map +1 -1
- package/dist/utils/github.js +11 -9
- package/dist/utils/github.js.map +1 -1
- package/dist/utils/image.d.ts +14 -0
- package/dist/utils/image.js +16 -0
- package/dist/utils/image.js.map +1 -1
- package/dist/utils/process.d.ts +20 -0
- package/dist/utils/process.js +98 -0
- package/dist/utils/process.js.map +1 -1
- package/package.json +4 -4
- package/assets/motion/references/motion-language.md +0 -128
- package/assets/motion/skills/video-qa/SKILL.md +0 -89
- package/dist/core/ideal-review-subagent.d.ts +0 -56
- package/dist/core/ideal-review-subagent.js +0 -112
- package/dist/core/ideal-review-subagent.js.map +0 -1
- package/dist/core/ideal-review.d.ts +0 -82
- package/dist/core/ideal-review.js +0 -242
- package/dist/core/ideal-review.js.map +0 -1
- package/dist/motion-agent/motion-check-tool.d.ts +0 -35
- package/dist/motion-agent/motion-check-tool.js +0 -514
- package/dist/motion-agent/motion-check-tool.js.map +0 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { CacheRetention } from "@prestyj/ai";
|
|
2
|
+
/**
|
|
3
|
+
* Cold-prompt-cache detection (mirrors langchain-ai/deepagents#6477's
|
|
4
|
+
* `cold_cache.py`): providers keep the conversation prefix cached only for a
|
|
5
|
+
* TTL, so after a break the next message re-reads the whole context uncached.
|
|
6
|
+
*
|
|
7
|
+
* Every TTL below is derived from what @prestyj/ai actually puts on the wire — never
|
|
8
|
+
* from a provider's marketing maximum we did not request. A provider/route we
|
|
9
|
+
* cannot pin to a request-side TTL returns `null` (no warning) rather than a
|
|
10
|
+
* guess.
|
|
11
|
+
*/
|
|
12
|
+
export interface CacheTtlPolicy {
|
|
13
|
+
ttlMs: number;
|
|
14
|
+
/**
|
|
15
|
+
* `expired`: the TTL is a documented/requested *maximum* — past it the entry
|
|
16
|
+
* is gone. `may_be_cold`: the TTL is a guaranteed *minimum* (GPT-5.6+ `30m`),
|
|
17
|
+
* so past it the prefix may still be warm.
|
|
18
|
+
*/
|
|
19
|
+
confidence: "expired" | "may_be_cold";
|
|
20
|
+
/** Where the number comes from, for logs/diagnostics. */
|
|
21
|
+
source: string;
|
|
22
|
+
}
|
|
23
|
+
export interface CacheTtlInput {
|
|
24
|
+
provider: string;
|
|
25
|
+
model: string;
|
|
26
|
+
cacheRetention: CacheRetention;
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
/** OpenAI OAuth (ChatGPT/Codex) routes carry an account id. */
|
|
29
|
+
accountId?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Resolve the cache TTL our provider code requests for this route. */
|
|
32
|
+
export declare function resolveCacheTtl(input: CacheTtlInput): CacheTtlPolicy | null;
|
|
33
|
+
/**
|
|
34
|
+
* Prefix size below which the notice is not worth an interruption. At 40k
|
|
35
|
+
* tokens a cold Anthropic re-read (1.25x write vs 0.1x cached read) costs ~12x
|
|
36
|
+
* the warm request, roughly a dime on Sonnet-class pricing; below that the delta
|
|
37
|
+
* is pennies — and compacting first itself re-reads the whole context once, so
|
|
38
|
+
* on small chats "Compact first" saves nothing.
|
|
39
|
+
*/
|
|
40
|
+
export declare const CACHE_EXPIRY_NOTICE_MIN_TOKENS = 40000;
|
|
41
|
+
export interface CacheTouch {
|
|
42
|
+
at: number;
|
|
43
|
+
provider: string;
|
|
44
|
+
model: string;
|
|
45
|
+
policy: CacheTtlPolicy;
|
|
46
|
+
}
|
|
47
|
+
export interface CacheExpiryStatus {
|
|
48
|
+
/** Owning session, so clients can key "dismissed once per expiry per chat". */
|
|
49
|
+
sessionId?: string;
|
|
50
|
+
provider: string;
|
|
51
|
+
ttlMs: number;
|
|
52
|
+
confidence: CacheTtlPolicy["confidence"];
|
|
53
|
+
ttlSource: string;
|
|
54
|
+
/** Last successful cache-using request (real turn or prewarm), epoch ms. */
|
|
55
|
+
lastRequestAt: number | null;
|
|
56
|
+
/** When the cached prefix lapses/lapsed, epoch ms; null when age is unknown. */
|
|
57
|
+
expiresAt: number | null;
|
|
58
|
+
expired: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* `idle`: TTL passed. `age_unknown`: resumed/reset session with no request in
|
|
61
|
+
* this process. `identity_changed`: provider/model switched since the last
|
|
62
|
+
* request, so the old prefix cannot be reused.
|
|
63
|
+
*/
|
|
64
|
+
reason: "idle" | "age_unknown" | "identity_changed" | null;
|
|
65
|
+
/** Tokens the next request would re-read (the current context). */
|
|
66
|
+
prefixTokens: number;
|
|
67
|
+
minTokens: number;
|
|
68
|
+
/** expired AND prefixTokens >= minTokens: worth showing the notice. */
|
|
69
|
+
notable: boolean;
|
|
70
|
+
/** No authoritative pricing exists in GG (turn metric cost is "unavailable"). */
|
|
71
|
+
estimatedExtraCostUsd: null;
|
|
72
|
+
}
|
|
73
|
+
export interface AssessCacheExpiryInput {
|
|
74
|
+
/** Policy for the route the NEXT request will take. */
|
|
75
|
+
current: {
|
|
76
|
+
provider: string;
|
|
77
|
+
model: string;
|
|
78
|
+
policy: CacheTtlPolicy | null;
|
|
79
|
+
};
|
|
80
|
+
lastTouch: CacheTouch | null;
|
|
81
|
+
now: number;
|
|
82
|
+
prefixTokens: number;
|
|
83
|
+
/** False for an empty chat — nothing to re-read. */
|
|
84
|
+
hasHistory: boolean;
|
|
85
|
+
minTokens?: number;
|
|
86
|
+
}
|
|
87
|
+
export declare function assessCacheExpiry(input: AssessCacheExpiryInput): CacheExpiryStatus | null;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const MINUTE = 60_000;
|
|
2
|
+
/** Resolve the cache TTL our provider code requests for this route. */
|
|
3
|
+
export function resolveCacheTtl(input) {
|
|
4
|
+
if (input.cacheRetention === "none")
|
|
5
|
+
return null;
|
|
6
|
+
if (input.provider === "anthropic") {
|
|
7
|
+
// @prestyj/ai/src/providers/transform.ts `toAnthropicCacheControl`: "long" sends
|
|
8
|
+
// `ttl: "1h"` only on the first-party endpoint; everything else sends a bare
|
|
9
|
+
// `{type: "ephemeral"}`, which is Anthropic's 5-minute default.
|
|
10
|
+
const firstParty = !input.baseUrl || input.baseUrl.includes("api.anthropic.com");
|
|
11
|
+
if (input.cacheRetention === "long" && firstParty) {
|
|
12
|
+
return {
|
|
13
|
+
ttlMs: 60 * MINUTE,
|
|
14
|
+
confidence: "expired",
|
|
15
|
+
source: "anthropic cache_control ttl=1h (transform.ts toAnthropicCacheControl)",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
ttlMs: 5 * MINUTE,
|
|
20
|
+
confidence: "expired",
|
|
21
|
+
source: "anthropic cache_control ephemeral default 5m (transform.ts toAnthropicCacheControl)",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (input.provider === "openai") {
|
|
25
|
+
if (input.accountId) {
|
|
26
|
+
// @prestyj/ai/src/providers/openai-codex.ts: the Codex backend rejects
|
|
27
|
+
// prompt_cache_retention; "Cache TTL on Codex is controlled server-side
|
|
28
|
+
// (~5-10 min in-memory)". Use the upper bound so we only warn once the
|
|
29
|
+
// entry is surely gone.
|
|
30
|
+
return {
|
|
31
|
+
ttlMs: 10 * MINUTE,
|
|
32
|
+
confidence: "expired",
|
|
33
|
+
source: "codex server-side in-memory cache ~5-10m (openai-codex.ts)",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (input.model.startsWith("gpt-5.6") || input.model.startsWith("gpt-6")) {
|
|
37
|
+
// @prestyj/ai/src/providers/openai.ts sends prompt_cache_options.ttl = "30m";
|
|
38
|
+
// OpenAI documents 30 minutes as a guaranteed minimum.
|
|
39
|
+
return {
|
|
40
|
+
ttlMs: 30 * MINUTE,
|
|
41
|
+
confidence: "may_be_cold",
|
|
42
|
+
source: "openai prompt_cache_options ttl=30m (openai.ts)",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (input.cacheRetention === "long") {
|
|
46
|
+
// @prestyj/ai/src/providers/openai.ts sends prompt_cache_retention = "24h".
|
|
47
|
+
return {
|
|
48
|
+
ttlMs: 24 * 60 * MINUTE,
|
|
49
|
+
confidence: "expired",
|
|
50
|
+
source: "openai prompt_cache_retention=24h (openai.ts)",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// No retention param sent → OpenAI's default in-memory retention, which
|
|
54
|
+
// its prompt-caching guide documents as up to one hour (a maximum).
|
|
55
|
+
return {
|
|
56
|
+
ttlMs: 60 * MINUTE,
|
|
57
|
+
confidence: "expired",
|
|
58
|
+
source: "openai default in_memory retention, max 1h (no param sent by openai.ts)",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Other providers (Gemini, GLM, Moonshot, OpenRouter, local, …): we request
|
|
62
|
+
// no TTL and have no documented window to anchor a warning on.
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Prefix size below which the notice is not worth an interruption. At 40k
|
|
67
|
+
* tokens a cold Anthropic re-read (1.25x write vs 0.1x cached read) costs ~12x
|
|
68
|
+
* the warm request, roughly a dime on Sonnet-class pricing; below that the delta
|
|
69
|
+
* is pennies — and compacting first itself re-reads the whole context once, so
|
|
70
|
+
* on small chats "Compact first" saves nothing.
|
|
71
|
+
*/
|
|
72
|
+
export const CACHE_EXPIRY_NOTICE_MIN_TOKENS = 40_000;
|
|
73
|
+
export function assessCacheExpiry(input) {
|
|
74
|
+
const { current, lastTouch, now } = input;
|
|
75
|
+
if (!input.hasHistory || !current.policy)
|
|
76
|
+
return null;
|
|
77
|
+
const minTokens = input.minTokens ?? CACHE_EXPIRY_NOTICE_MIN_TOKENS;
|
|
78
|
+
const prefixTokens = Math.max(0, Math.round(input.prefixTokens));
|
|
79
|
+
let reason = null;
|
|
80
|
+
let expiresAt = null;
|
|
81
|
+
// The entry lives by the TTL it was WRITTEN with, not the current setting.
|
|
82
|
+
let policy = current.policy;
|
|
83
|
+
if (!lastTouch) {
|
|
84
|
+
reason = "age_unknown";
|
|
85
|
+
}
|
|
86
|
+
else if (lastTouch.provider !== current.provider || lastTouch.model !== current.model) {
|
|
87
|
+
reason = "identity_changed";
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
policy = lastTouch.policy;
|
|
91
|
+
expiresAt = lastTouch.at + policy.ttlMs;
|
|
92
|
+
if (now >= expiresAt)
|
|
93
|
+
reason = "idle";
|
|
94
|
+
}
|
|
95
|
+
const expired = reason !== null;
|
|
96
|
+
return {
|
|
97
|
+
provider: current.provider,
|
|
98
|
+
ttlMs: policy.ttlMs,
|
|
99
|
+
confidence: policy.confidence,
|
|
100
|
+
ttlSource: policy.source,
|
|
101
|
+
lastRequestAt: lastTouch?.at ?? null,
|
|
102
|
+
expiresAt,
|
|
103
|
+
expired,
|
|
104
|
+
reason,
|
|
105
|
+
prefixTokens,
|
|
106
|
+
minTokens,
|
|
107
|
+
notable: expired && prefixTokens >= minTokens,
|
|
108
|
+
estimatedExtraCostUsd: null,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=cache-expiry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-expiry.js","sourceRoot":"","sources":["../../src/core/cache-expiry.ts"],"names":[],"mappings":"AAkCA,MAAM,MAAM,GAAG,MAAM,CAAC;AAEtB,uEAAuE;AACvE,MAAM,UAAU,eAAe,CAAC,KAAoB;IAClD,IAAI,KAAK,CAAC,cAAc,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IAEjD,IAAI,KAAK,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QACnC,iFAAiF;QACjF,6EAA6E;QAC7E,gEAAgE;QAChE,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;QACjF,IAAI,KAAK,CAAC,cAAc,KAAK,MAAM,IAAI,UAAU,EAAE,CAAC;YAClD,OAAO;gBACL,KAAK,EAAE,EAAE,GAAG,MAAM;gBAClB,UAAU,EAAE,SAAS;gBACrB,MAAM,EAAE,uEAAuE;aAChF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,KAAK,EAAE,CAAC,GAAG,MAAM;YACjB,UAAU,EAAE,SAAS;YACrB,MAAM,EAAE,qFAAqF;SAC9F,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAChC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,uEAAuE;YACvE,wEAAwE;YACxE,uEAAuE;YACvE,wBAAwB;YACxB,OAAO;gBACL,KAAK,EAAE,EAAE,GAAG,MAAM;gBAClB,UAAU,EAAE,SAAS;gBACrB,MAAM,EAAE,4DAA4D;aACrE,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzE,8EAA8E;YAC9E,uDAAuD;YACvD,OAAO;gBACL,KAAK,EAAE,EAAE,GAAG,MAAM;gBAClB,UAAU,EAAE,aAAa;gBACzB,MAAM,EAAE,iDAAiD;aAC1D,CAAC;QACJ,CAAC;QACD,IAAI,KAAK,CAAC,cAAc,KAAK,MAAM,EAAE,CAAC;YACpC,4EAA4E;YAC5E,OAAO;gBACL,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,MAAM;gBACvB,UAAU,EAAE,SAAS;gBACrB,MAAM,EAAE,+CAA+C;aACxD,CAAC;QACJ,CAAC;QACD,wEAAwE;QACxE,oEAAoE;QACpE,OAAO;YACL,KAAK,EAAE,EAAE,GAAG,MAAM;YAClB,UAAU,EAAE,SAAS;YACrB,MAAM,EAAE,yEAAyE;SAClF,CAAC;IACJ,CAAC;IAED,4EAA4E;IAC5E,+DAA+D;IAC/D,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,MAAM,CAAC;AA+CrD,MAAM,UAAU,iBAAiB,CAAC,KAA6B;IAC7D,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;IAC1C,IAAI,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,OAAO,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACtD,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,8BAA8B,CAAC;IACpE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;IAEjE,IAAI,MAAM,GAAgC,IAAI,CAAC;IAC/C,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,2EAA2E;IAC3E,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC5B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,GAAG,aAAa,CAAC;IACzB,CAAC;SAAM,IAAI,SAAS,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,IAAI,SAAS,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC;QACxF,MAAM,GAAG,kBAAkB,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QAC1B,SAAS,GAAG,SAAS,CAAC,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC;QACxC,IAAI,GAAG,IAAI,SAAS;YAAE,MAAM,GAAG,MAAM,CAAC;IACxC,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,SAAS,EAAE,MAAM,CAAC,MAAM;QACxB,aAAa,EAAE,SAAS,EAAE,EAAE,IAAI,IAAI;QACpC,SAAS;QACT,OAAO;QACP,MAAM;QACN,YAAY;QACZ,SAAS;QACT,OAAO,EAAE,OAAO,IAAI,YAAY,IAAI,SAAS;QAC7C,qBAAqB,EAAE,IAAI;KAC5B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** User setting for an OpenAI request-shape option: `auto` (fast) or forced on/off. */
|
|
2
|
+
export type CodexShapeSetting = "auto" | "on" | "off";
|
|
3
|
+
/**
|
|
4
|
+
* The `responsesLite` stream option for this route, or undefined where the
|
|
5
|
+
* model never uses lite (the provider ignores it there).
|
|
6
|
+
*/
|
|
7
|
+
export declare function resolveResponsesLite(setting: CodexShapeSetting, provider: string, model: string): boolean | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* The `strictTools` stream option for this route, or undefined off OpenAI
|
|
10
|
+
* (other providers never send strict schemas).
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveStrictTools(setting: CodexShapeSetting, provider: string): boolean | undefined;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { usesResponsesLite } from "@prestyj/ai";
|
|
2
|
+
/**
|
|
3
|
+
* `auto` picks the fast shape on every OpenAI model: no Responses-Lite and no
|
|
4
|
+
* strict tool schemas (measured in a head-to-head against the pi agent).
|
|
5
|
+
* - Lite caps a response at one tool call. Without it, gpt-6-astra,
|
|
6
|
+
* gpt-6.1-sol and gpt-6-luna each put 3 independent reads in one response
|
|
7
|
+
* instead of 1, and the Astra h2h suite finished 8% faster, no lost passes.
|
|
8
|
+
* - Strict schemas make every call carry each optional field as null; without
|
|
9
|
+
* them requests finished ~0.6–1s sooner on all three models.
|
|
10
|
+
* `on` restores the previous (Codex CLI) behaviour.
|
|
11
|
+
*/
|
|
12
|
+
function resolve(setting, applies) {
|
|
13
|
+
if (!applies)
|
|
14
|
+
return undefined;
|
|
15
|
+
return setting === "on";
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The `responsesLite` stream option for this route, or undefined where the
|
|
19
|
+
* model never uses lite (the provider ignores it there).
|
|
20
|
+
*/
|
|
21
|
+
export function resolveResponsesLite(setting, provider, model) {
|
|
22
|
+
return resolve(setting, provider === "openai" && usesResponsesLite(model));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The `strictTools` stream option for this route, or undefined off OpenAI
|
|
26
|
+
* (other providers never send strict schemas).
|
|
27
|
+
*/
|
|
28
|
+
export function resolveStrictTools(setting, provider) {
|
|
29
|
+
return resolve(setting, provider === "openai");
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=codex-request-shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-request-shape.js","sourceRoot":"","sources":["../../src/core/codex-request-shape.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKhD;;;;;;;;;GASG;AACH,SAAS,OAAO,CAAC,OAA0B,EAAE,OAAgB;IAC3D,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,OAAO,OAAO,KAAK,IAAI,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,OAA0B,EAC1B,QAAgB,EAChB,KAAa;IAEb,OAAO,OAAO,CAAC,OAAO,EAAE,QAAQ,KAAK,QAAQ,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAA0B,EAC1B,QAAgB;IAEhB,OAAO,OAAO,CAAC,OAAO,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC;AACjD,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { stream, } from "@prestyj/ai";
|
|
2
2
|
import { estimateConversationTokens, estimateMessageTokens } from "./token-estimator.js";
|
|
3
3
|
import { findLatestHumanQuery, selectQueryAwareContext } from "./query-aware-selector.js";
|
|
4
|
-
import { getSummaryModel, getContextWindow } from "../model-registry.js";
|
|
4
|
+
import { getSummaryModel, getContextWindow, getModel } from "../model-registry.js";
|
|
5
|
+
import { isModelUnavailableFailure } from "../model-unavailable.js";
|
|
5
6
|
import { kimiCodingHeaders, isKimiCodingEndpoint } from "../oauth/kimi.js";
|
|
6
7
|
import { log } from "../logger.js";
|
|
8
|
+
import { editTargetPaths } from "../../tools/edit-targets.js";
|
|
7
9
|
/**
|
|
8
10
|
* Per-message-part char caps when preparing messages for the summarizer.
|
|
9
11
|
* Verbose tool output is capped aggressively; user messages are the highest-
|
|
@@ -332,6 +334,11 @@ export function extractFileOperations(messages) {
|
|
|
332
334
|
if (!("type" in part) || part.type !== "tool_call")
|
|
333
335
|
continue;
|
|
334
336
|
const tc = part;
|
|
337
|
+
if (tc.name === "edit") {
|
|
338
|
+
for (const target of editTargetPaths(tc.args))
|
|
339
|
+
modified.add(target);
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
335
342
|
const filePath = tc.args.file_path ?? tc.args.path ?? tc.args.file;
|
|
336
343
|
if (typeof filePath !== "string")
|
|
337
344
|
continue;
|
|
@@ -955,13 +962,10 @@ export async function compact(messages, options) {
|
|
|
955
962
|
// after generation, so every message that could be removed must be represented.
|
|
956
963
|
const summarizationSource = messages.slice(1);
|
|
957
964
|
const fileOps = extractFileOperations(summarizationSource);
|
|
958
|
-
//
|
|
965
|
+
// The cheaper summary model goes first (see getSummaryModel). The ACTIVE
|
|
966
|
+
// model is the fallback when the provider rejects it — retired upstream, or
|
|
967
|
+
// not on the user's plan — which the registry cannot know in advance.
|
|
959
968
|
const summaryModel = getSummaryModel(options.provider, options.model);
|
|
960
|
-
const summaryContextWindow = getContextWindow(summaryModel.id, {
|
|
961
|
-
provider: options.provider,
|
|
962
|
-
accountId: options.accountId,
|
|
963
|
-
});
|
|
964
|
-
const summaryOutputTokens = Math.min(summaryModel.maxOutputTokens, resolveSummaryOutputTokens(summaryContextWindow));
|
|
965
969
|
const previousSummary = findLatestPreviousSummary(summarizationSource);
|
|
966
970
|
// Carry the prior tracked edits forward as DATA, not as prose the summarizer
|
|
967
971
|
// has to re-transcribe. Their originating tool calls were collapsed by an
|
|
@@ -981,10 +985,8 @@ export async function compact(messages, options) {
|
|
|
981
985
|
...extractFailingTests(summarizationSource),
|
|
982
986
|
], carriedTests.omitted);
|
|
983
987
|
const classifiedMessages = classifyMessagesForSummary(summarizationSource);
|
|
984
|
-
// Budget: summary model context - output tokens - system/user prompt overhead (~1K).
|
|
985
988
|
// Prior compacted memory is pinned separately, never presented as a fresh human turn.
|
|
986
989
|
const promptOverhead = 1000;
|
|
987
|
-
const tokenBudget = summaryContextWindow - summaryOutputTokens - promptOverhead;
|
|
988
990
|
const previousSummaryMessage = previousSummary
|
|
989
991
|
? {
|
|
990
992
|
role: "user",
|
|
@@ -995,27 +997,6 @@ export async function compact(messages, options) {
|
|
|
995
997
|
? estimateMessageTokens(previousSummaryMessage)
|
|
996
998
|
: 0;
|
|
997
999
|
const query = findLatestHumanQuery(summarizationSource);
|
|
998
|
-
const contextSelection = selectQueryAwareContext(classifiedMessages, query, Math.max(0, tokenBudget - previousSummaryTokens), { fallback: selectMessagesInBudget });
|
|
999
|
-
const selectedMessages = contextSelection.messages;
|
|
1000
|
-
log("INFO", "compaction", `Summarizing ${middleMessages.length} messages`, {
|
|
1001
|
-
summaryModel: summaryModel.id,
|
|
1002
|
-
summaryContextWindow: String(summaryContextWindow),
|
|
1003
|
-
tokenBudget: String(tokenBudget),
|
|
1004
|
-
preparedMessages: String(classifiedMessages.length),
|
|
1005
|
-
selectedMessages: String(selectedMessages.length + (previousSummaryMessage ? 1 : 0)),
|
|
1006
|
-
droppedMessages: String(contextSelection.droppedMessages),
|
|
1007
|
-
selectedTokens: String(contextSelection.selectedTokens),
|
|
1008
|
-
selectionStrategy: contextSelection.strategy,
|
|
1009
|
-
queryTerms: String(contextSelection.queryTerms),
|
|
1010
|
-
...(contextSelection.fallbackReason
|
|
1011
|
-
? { selectionFallback: contextSelection.fallbackReason }
|
|
1012
|
-
: {}),
|
|
1013
|
-
previousSummary: String(!!previousSummaryMessage),
|
|
1014
|
-
summaryOutputTokens: String(summaryOutputTokens),
|
|
1015
|
-
filesModified: String(fileOps.modified.size),
|
|
1016
|
-
filesModifiedCarried: String(carriedSummary.files.length),
|
|
1017
|
-
recentKept: String(recentMessages.length),
|
|
1018
|
-
});
|
|
1019
1000
|
// Add plan preservation and summary-update instructions when applicable.
|
|
1020
1001
|
const planPreservation = options.approvedPlanPath
|
|
1021
1002
|
? `\n\n### APPROVED PLAN PRESERVATION\n` +
|
|
@@ -1039,21 +1020,59 @@ export async function compact(messages, options) {
|
|
|
1039
1020
|
"evidence completed, reversed, or made irrelevant. Never concatenate, never emit an 'update since " +
|
|
1040
1021
|
"the previous summary' section, and never restate a fact in both old and new wording."
|
|
1041
1022
|
: "";
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1023
|
+
/**
|
|
1024
|
+
* Size and assemble the summary request for one model. Re-run when the
|
|
1025
|
+
* request moves to the active model, whose context window may be smaller.
|
|
1026
|
+
*/
|
|
1027
|
+
const prepareSummaryRequest = (modelId) => {
|
|
1028
|
+
const contextWindow = getContextWindow(modelId, {
|
|
1029
|
+
provider: options.provider,
|
|
1030
|
+
accountId: options.accountId,
|
|
1031
|
+
});
|
|
1032
|
+
const outputTokens = Math.min(getModel(modelId)?.maxOutputTokens ?? Number.POSITIVE_INFINITY, resolveSummaryOutputTokens(contextWindow));
|
|
1033
|
+
// Budget: summary model context - output tokens - system/user prompt overhead (~1K).
|
|
1034
|
+
const tokenBudget = contextWindow - outputTokens - promptOverhead;
|
|
1035
|
+
const contextSelection = selectQueryAwareContext(classifiedMessages, query, Math.max(0, tokenBudget - previousSummaryTokens), { fallback: selectMessagesInBudget });
|
|
1036
|
+
const selectedMessages = contextSelection.messages;
|
|
1037
|
+
log("INFO", "compaction", `Summarizing ${middleMessages.length} messages`, {
|
|
1038
|
+
summaryModel: modelId,
|
|
1039
|
+
summaryContextWindow: String(contextWindow),
|
|
1040
|
+
tokenBudget: String(tokenBudget),
|
|
1041
|
+
preparedMessages: String(classifiedMessages.length),
|
|
1042
|
+
selectedMessages: String(selectedMessages.length + (previousSummaryMessage ? 1 : 0)),
|
|
1043
|
+
droppedMessages: String(contextSelection.droppedMessages),
|
|
1044
|
+
selectedTokens: String(contextSelection.selectedTokens),
|
|
1045
|
+
selectionStrategy: contextSelection.strategy,
|
|
1046
|
+
queryTerms: String(contextSelection.queryTerms),
|
|
1047
|
+
...(contextSelection.fallbackReason
|
|
1048
|
+
? { selectionFallback: contextSelection.fallbackReason }
|
|
1049
|
+
: {}),
|
|
1050
|
+
previousSummary: String(!!previousSummaryMessage),
|
|
1051
|
+
summaryOutputTokens: String(outputTokens),
|
|
1052
|
+
filesModified: String(fileOps.modified.size),
|
|
1053
|
+
filesModifiedCarried: String(carriedSummary.files.length),
|
|
1054
|
+
recentKept: String(recentMessages.length),
|
|
1055
|
+
});
|
|
1056
|
+
const summaryMessages = [
|
|
1057
|
+
{
|
|
1058
|
+
role: "system",
|
|
1059
|
+
content: COMPACTION_SYSTEM_PROMPT + planPreservation + focusDirective + updateInstruction,
|
|
1060
|
+
},
|
|
1061
|
+
...(previousSummaryMessage ? [previousSummaryMessage] : []),
|
|
1062
|
+
...selectedMessages,
|
|
1063
|
+
{ role: "user", content: COMPACTION_USER_PROMPT },
|
|
1064
|
+
];
|
|
1065
|
+
log("INFO", "compaction", `Calling summary LLM`, {
|
|
1066
|
+
provider: options.provider,
|
|
1067
|
+
model: modelId,
|
|
1068
|
+
messageCount: String(summaryMessages.length),
|
|
1069
|
+
hasApiKey: String(!!options.apiKey),
|
|
1070
|
+
});
|
|
1071
|
+
return { modelId, outputTokens, contextSelection, selectedMessages, summaryMessages };
|
|
1072
|
+
};
|
|
1073
|
+
let request = prepareSummaryRequest(summaryModel.id);
|
|
1074
|
+
// Only a DIFFERENT model can rescue a rejected one.
|
|
1075
|
+
let canFallBackToActiveModel = summaryModel.id !== options.model;
|
|
1057
1076
|
// Retry empty successful responses only. Transport failures and timeouts use
|
|
1058
1077
|
// the deterministic fallback immediately; replaying the same large request
|
|
1059
1078
|
// adds long UI stalls and can leave several expensive requests in flight.
|
|
@@ -1066,9 +1085,9 @@ export async function compact(messages, options) {
|
|
|
1066
1085
|
try {
|
|
1067
1086
|
const result = stream({
|
|
1068
1087
|
provider: options.provider,
|
|
1069
|
-
model:
|
|
1070
|
-
messages: summaryMessages,
|
|
1071
|
-
maxTokens:
|
|
1088
|
+
model: request.modelId,
|
|
1089
|
+
messages: request.summaryMessages,
|
|
1090
|
+
maxTokens: request.outputTokens,
|
|
1072
1091
|
apiKey: options.apiKey,
|
|
1073
1092
|
accountId: options.accountId,
|
|
1074
1093
|
projectId: options.projectId,
|
|
@@ -1112,6 +1131,20 @@ export async function compact(messages, options) {
|
|
|
1112
1131
|
if (options.signal?.aborted || (err instanceof Error && err.name === "AbortError")) {
|
|
1113
1132
|
throw err;
|
|
1114
1133
|
}
|
|
1134
|
+
if (canFallBackToActiveModel && isModelUnavailableFailure(err)) {
|
|
1135
|
+
// The cheaper model was rejected outright: summarize on the model the
|
|
1136
|
+
// session is already using, instead of dropping to the much weaker
|
|
1137
|
+
// extractive fallback.
|
|
1138
|
+
canFallBackToActiveModel = false;
|
|
1139
|
+
log("WARN", "compaction", `Summary model unavailable — retrying on the active model`, {
|
|
1140
|
+
summaryModel: request.modelId,
|
|
1141
|
+
activeModel: options.model,
|
|
1142
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1143
|
+
});
|
|
1144
|
+
request = prepareSummaryRequest(options.model);
|
|
1145
|
+
attempt--; // A rejected model is not one of the empty-response retries.
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1115
1148
|
log("WARN", "compaction", err instanceof SummaryTimeoutError
|
|
1116
1149
|
? `Summary LLM call timed out after ${SUMMARY_ATTEMPT_TIMEOUT_MS}ms — using fallback`
|
|
1117
1150
|
: `Summary LLM call failed: ${err instanceof Error ? err.message : String(err)}`, { attempt: String(attempt), timeoutMs: String(SUMMARY_ATTEMPT_TIMEOUT_MS) });
|
|
@@ -1122,6 +1155,9 @@ export async function compact(messages, options) {
|
|
|
1122
1155
|
attemptController.abort();
|
|
1123
1156
|
}
|
|
1124
1157
|
}
|
|
1158
|
+
// Report the selection of the request that actually ran (the active model's
|
|
1159
|
+
// after a fallback).
|
|
1160
|
+
const { contextSelection, selectedMessages } = request;
|
|
1125
1161
|
// Fallback: preserve prior compacted memory and append a fresh extractive update.
|
|
1126
1162
|
if (summaryText.length === 0) {
|
|
1127
1163
|
log("WARN", "compaction", `All summary attempts failed — using fallback extractive summary`);
|