@iowarp/clio-coder 0.3.2 → 0.3.4
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/CHANGELOG.md +269 -458
- package/CONTRIBUTING.md +1 -1
- package/README.md +3 -3
- package/dist/{acp-BIYHVZIM.js → acp-S5R4RR5B.js} +7 -6
- package/dist/{agents-YT6SSRIT.js → agents-P6DMMVZY.js} +24 -21
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-5TWEIYDN.js → auth-2XCZLPKS.js} +12 -8
- package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
- package/dist/{chunk-WMSVI4G2.js → chunk-2LZI5CAG.js} +133 -13
- package/dist/{chunk-OAO4GE4M.js → chunk-2TZWSW76.js} +2 -2
- package/dist/{chunk-OOJYHWRB.js → chunk-34475P3I.js} +2 -2
- package/dist/{chunk-WVO7V2QY.js → chunk-35MKKU5R.js} +4 -4
- package/dist/{chunk-LBNRH5WM.js → chunk-3HZ5RWN2.js} +5 -5
- package/dist/{chunk-AGYYIBLL.js → chunk-3JLKSKD7.js} +2 -2
- package/dist/{chunk-MBS4V7ZP.js → chunk-4JUF2NNX.js} +7 -7
- package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
- package/dist/chunk-5M54SPOL.js +926 -0
- package/dist/{chunk-STBPMHSX.js → chunk-7RXG6QRZ.js} +51 -11
- package/dist/{chunk-77VKQEHF.js → chunk-A2GZF7DC.js} +5 -5
- package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
- package/dist/chunk-AOCYTWAV.js +449 -0
- package/dist/chunk-BEY543CS.js +258 -0
- package/dist/{chunk-6N5PTWMY.js → chunk-BP4OYD6A.js} +32 -13
- package/dist/chunk-BPGS2WCQ.js +612 -0
- package/dist/{chunk-J5HN4RYU.js → chunk-BRXQQJFP.js} +8 -8
- package/dist/chunk-CFGTUFWB.js +67 -0
- package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
- package/dist/{chunk-G2DE3C7R.js → chunk-EDRHSCIE.js} +4 -4
- package/dist/{chunk-4KLWL3UC.js → chunk-EFADSJET.js} +2 -2
- package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
- package/dist/chunk-FYYLNIL5.js +313 -0
- package/dist/{chunk-EPVUXGXG.js → chunk-HV5X7OR2.js} +14 -12
- package/dist/{chunk-TZTZS7QK.js → chunk-HXG4IURW.js} +5 -3
- package/dist/{chunk-IGLFWIYI.js → chunk-K6WL7QZT.js} +3 -3
- package/dist/chunk-K7VKOLQQ.js +15 -0
- package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
- package/dist/{chunk-V4RXGQ5Q.js → chunk-KRPY7NTG.js} +10 -7
- package/dist/chunk-LL4KHSZI.js +22 -0
- package/dist/{chunk-KJ5LWLOE.js → chunk-MEQ45TQ4.js} +15 -9
- package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
- package/dist/{chunk-AO4RKG4M.js → chunk-N4CZJQRK.js} +5 -5
- package/dist/{chunk-ARBGF5F7.js → chunk-NILBFAPG.js} +14 -8
- package/dist/chunk-OZNBF4L3.js +23 -0
- package/dist/{verify-G6V4D2G7.js → chunk-PCZJO5TI.js} +127 -42
- package/dist/chunk-QQK64KLB.js +1360 -0
- package/dist/{chunk-6EJV5X2W.js → chunk-QQL5RT5M.js} +979 -1619
- package/dist/{chunk-LZSJBIVT.js → chunk-QWU7ZBO7.js} +70 -720
- package/dist/{chunk-2EHAIA3X.js → chunk-RD5U66HV.js} +3 -3
- package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
- package/dist/{chunk-OQ33BKR3.js → chunk-TTNYS3EA.js} +3 -60
- package/dist/chunk-TW3WDMVS.js +677 -0
- package/dist/chunk-TZSKNMZG.js +434 -0
- package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
- package/dist/{chunk-7EYHLWU7.js → chunk-UZHIZC5S.js} +7 -7
- package/dist/{chunk-QTYWRVRA.js → chunk-VAWWTKDP.js} +8 -8
- package/dist/{chunk-X75S7HFS.js → chunk-VEZEGCGW.js} +214 -20
- package/dist/{chunk-OHHN2SO4.js → chunk-VMNQ6OZA.js} +98 -202
- package/dist/chunk-VSNATDE6.js +122 -0
- package/dist/chunk-W6GROXXM.js +69 -0
- package/dist/chunk-WPQLXFOZ.js +375 -0
- package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
- package/dist/{chunk-3ZXDFGR5.js → chunk-X6COSD2O.js} +5 -5
- package/dist/chunk-ZGVHUX3M.js +66 -0
- package/dist/{chunk-MAW544W2.js → chunk-ZWMF7253.js} +4 -4
- package/dist/{chunk-MQSRRFWA.js → chunk-ZYKPLLNQ.js} +563 -546
- package/dist/cli/index.js +27 -23
- package/dist/{clio-4LY5K2AC.js → clio-J5JIOIDS.js} +7 -6
- package/dist/{code-nav-7AX6FYE6.js → code-nav-AXCXSBHX.js} +5 -3
- package/dist/{config-GTLUW2PR.js → config-OEBMIN2U.js} +37 -27
- package/dist/{configure-R6A64DHX.js → configure-PUQOSIXQ.js} +16 -13
- package/dist/{context-5VKGUVJJ.js → context-EKDCKUUZ.js} +82 -7
- package/dist/{context-RW5HC47S.js → context-MGSE4Z2T.js} +33 -23
- package/dist/{context-JFZEJ7W5.js → context-URSXPBCK.js} +17 -9
- package/dist/{context-clear-6ZHBAZZT.js → context-clear-KDAJRNUK.js} +33 -23
- package/dist/context-working-set-SBKMPPI2.js +1552 -0
- package/dist/{dispatch-runner-VKBRCWQC.js → dispatch-runner-MSWN72NK.js} +43 -29
- package/dist/{doctor-KI767GSN.js → doctor-7BSE27PJ.js} +10 -10
- package/dist/{eval-XSSNATB4.js → eval-IZGDOO4H.js} +9 -8
- package/dist/{evidence-UA6AWDQQ.js → evidence-SR7WXB5B.js} +51 -23
- package/dist/{evolve-QNTFGV6Z.js → evolve-K7VE2CBX.js} +30 -20
- package/dist/{fleet-Q7UOMUSG.js → fleet-7XMJNQNF.js} +48 -38
- package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-AQNAH644.js} +3 -3
- package/dist/{init-WBB65ZHQ.js → init-JGNPAYXT.js} +41 -31
- package/dist/{memory-MD3O64RI.js → memory-4ALKDJ4Q.js} +32 -22
- package/dist/{models-BZU34YWD.js → models-ZMMLFJNN.js} +22 -19
- package/dist/{monitor-MEQA5C3I.js → monitor-2F3T5KHP.js} +55 -43
- package/dist/{orchestrator-CGFKEP27.js → orchestrator-ORHT43JB.js} +2507 -1896
- package/dist/{reset-L2FQEE3E.js → reset-NXGTYNUO.js} +4 -3
- package/dist/{run-IV4Q6RLN.js → run-RF4WJGMT.js} +51 -41
- package/dist/{share-S5BZQC5I.js → share-UT3W6E4M.js} +5 -4
- package/dist/{skills-LQEKRDTN.js → skills-PSACKC5Q.js} +2 -2
- package/dist/{skills-eval-3DC4HEWS.js → skills-eval-WJSI55RZ.js} +34 -24
- package/dist/{targets-C4SSGQOB.js → targets-PIIRAOYS.js} +23 -20
- package/dist/{terminal-lease-IT5JW2NR.js → terminal-lease-ULWXWNVY.js} +5 -4
- package/dist/{upgrade-7TT7SQ3G.js → upgrade-346TZ6AV.js} +18 -17
- package/dist/{usage-GV4PKT3M.js → usage-6KKXR32N.js} +34 -24
- package/dist/verifiers-4UUM6TEE.js +1214 -0
- package/dist/verify-X5HDROLA.js +25 -0
- package/dist/{wiki-generate-DQF6Z66B.js → wiki-generate-7STOCIFZ.js} +42 -31
- package/dist/worker/entry.js +33 -24
- package/docs/README.md +8 -7
- package/docs/acp.md +1 -1
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +2 -2
- package/docs/artifact-versions.md +1 -1
- package/docs/built-in-agents.md +1 -1
- package/docs/capacity-and-scheduling.md +1 -1
- package/docs/commands-and-modes.md +53 -21
- package/docs/config-knobs-audit.md +1 -2
- package/docs/configuration-and-targets.md +15 -1
- package/docs/context-engine.md +64 -12
- package/docs/context-working-set.md +194 -0
- package/docs/development-pipeline.md +1 -1
- package/docs/documentation-coverage.md +5 -5
- package/docs/documentation-guide.md +6 -5
- package/docs/environment-variables.md +2 -1
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +14 -1
- package/docs/evidence-and-memory.md +74 -2
- package/docs/evolution.md +1 -1
- package/docs/exit-codes-and-output.md +1 -1
- package/docs/extensions-and-sharing.md +2 -2
- package/docs/fleet-dispatch.md +22 -7
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +6 -6
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +7 -9
- package/docs/observability.md +4 -4
- package/docs/performance-methodology.md +2 -2
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +4 -4
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +35 -35
- package/docs/safety-model.md +23 -4
- package/docs/scientific-validation.md +21 -3
- package/docs/session-lifecycle.md +3 -3
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +79 -12
- package/docs/trace-store.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/docs/tui-design.md +2 -2
- package/docs/worker-dispatch-mechanics.md +11 -1
- package/package.json +8 -11
- package/skills/meta/clio-test/SKILL.md +20 -17
- package/skills/meta/clio-test/evals.md +3 -3
- package/skills/meta/clio-test/references/harness.md +35 -6
- package/skills/meta/clio-test/references/test-map.md +20 -10
- package/skills/registry.yaml +2 -2
- package/skills/skill-marketplace.json +1 -1
- package/src/cli/context-working-set.ts +513 -0
- package/src/cli/context.ts +8 -0
- package/src/cli/evidence.ts +20 -2
- package/src/cli/index.ts +4 -0
- package/src/cli/verifiers.ts +325 -0
- package/src/core/bash-exec.ts +39 -14
- package/src/core/bus-events.ts +19 -4
- package/src/core/config.ts +54 -0
- package/src/core/defaults.ts +50 -3
- package/src/core/git-commit-attribution.ts +46 -21
- package/src/core/verification-scripts.ts +6 -0
- package/src/domains/agents/builtins/verifier.md +3 -0
- package/src/domains/config/classify.ts +1 -0
- package/src/domains/config/keybindings.ts +3 -3
- package/src/domains/context/working-set/contract.ts +161 -0
- package/src/domains/context/working-set/defaults.ts +28 -0
- package/src/domains/context/working-set/engine.ts +203 -0
- package/src/domains/context/working-set/fold.ts +62 -0
- package/src/domains/context/working-set/horizon.ts +38 -0
- package/src/domains/context/working-set/marker.ts +103 -0
- package/src/domains/context/working-set/path-index.ts +436 -0
- package/src/domains/context/working-set/payload.ts +152 -0
- package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
- package/src/domains/context/working-set/policies/index.ts +21 -0
- package/src/domains/context/working-set/policies/structural.ts +160 -0
- package/src/domains/context/working-set/project.ts +132 -0
- package/src/domains/context/working-set/protect.ts +109 -0
- package/src/domains/context/working-set/recall.ts +177 -0
- package/src/domains/context/working-set/replay/controls.ts +112 -0
- package/src/domains/context/working-set/replay/load-clio.ts +199 -0
- package/src/domains/context/working-set/replay/metrics.ts +185 -0
- package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
- package/src/domains/context/working-set/replay/report.ts +139 -0
- package/src/domains/context/working-set/replay/runner.ts +325 -0
- package/src/domains/context/working-set/replay/synthetic.ts +422 -0
- package/src/domains/context/working-set/replay/trace.ts +21 -0
- package/src/domains/context/working-set/visible.ts +54 -0
- package/src/domains/evidence/build.ts +112 -45
- package/src/domains/evidence/eval.ts +24 -7
- package/src/domains/evidence/index.ts +53 -0
- package/src/domains/evidence/ordering.ts +12 -0
- package/src/domains/evidence/run-trust.ts +221 -0
- package/src/domains/evidence/store.ts +46 -6
- package/src/domains/evidence/trust-status.ts +854 -0
- package/src/domains/evidence/types.ts +26 -0
- package/src/domains/middleware/memory-intervention.ts +3 -0
- package/src/domains/middleware/stalled-turn.ts +165 -4
- package/src/domains/safety/autonomy.ts +1 -1
- package/src/domains/safety/default-path-policy.ts +8 -0
- package/src/domains/safety/finish-contract.ts +4 -3
- package/src/domains/safety/policy-engine.ts +48 -6
- package/src/domains/session/compaction/compact.ts +23 -1
- package/src/domains/session/compaction/cut-point.ts +2 -0
- package/src/domains/session/compaction/tokens.ts +16 -1
- package/src/domains/session/context-ledger.ts +2 -0
- package/src/domains/session/entries.ts +107 -1
- package/src/domains/session/manager.ts +9 -2
- package/src/domains/session/migrations/index.ts +22 -3
- package/src/engine/acp/server.ts +3 -0
- package/src/engine/agent.ts +18 -1
- package/src/engine/session.ts +9 -3
- package/src/entry/orchestrator.ts +16 -4
- package/src/interactive/chat-loop-messages.ts +18 -6
- package/src/interactive/chat-panel.ts +571 -244
- package/src/interactive/chat-renderer.ts +79 -39
- package/src/interactive/context-meter.ts +10 -0
- package/src/interactive/context-overlay.ts +81 -6
- package/src/interactive/context-recall-command.ts +110 -0
- package/src/interactive/editor-submit.ts +26 -1
- package/src/interactive/footer/widgets.ts +22 -20
- package/src/interactive/footer-panel.ts +6 -1
- package/src/interactive/interactive-application.ts +2 -0
- package/src/interactive/interactive-event-projection.ts +12 -0
- package/src/interactive/interactive-slash-runtime.ts +49 -8
- package/src/interactive/model-session-replay.ts +21 -0
- package/src/interactive/overlay-general-openers.ts +6 -0
- package/src/interactive/overlay-session-lifecycle.ts +8 -4
- package/src/interactive/overlays/ask-user.ts +146 -24
- package/src/interactive/renderers/tool-execution.ts +167 -56
- package/src/interactive/session-transcript.ts +2 -2
- package/src/interactive/slash-commands.ts +29 -2
- package/src/interactive/status/index.ts +12 -1
- package/src/interactive/status/reasoning.ts +87 -0
- package/src/interactive/status/summary.ts +13 -2
- package/src/interactive/transcript-detail.ts +120 -0
- package/src/interactive/turn-context.ts +238 -88
- package/src/interactive/turn-middleware.ts +6 -6
- package/src/tools/agent-tools.ts +11 -4
- package/src/tools/bash.ts +144 -82
- package/src/tools/builtin-tool-catalog.ts +18 -6
- package/src/tools/context/index.ts +105 -3
- package/src/tools/context/surface.ts +3 -2
- package/src/tools/core-bootstrap.ts +21 -0
- package/src/tools/dispatch-runner.ts +9 -7
- package/src/tools/monitor.ts +28 -20
- package/src/tools/presentation.ts +107 -0
- package/src/tools/registry.ts +65 -7
- package/src/tools/result-disposition.ts +550 -0
- package/src/tools/result-shaping.ts +262 -19
- package/src/tools/safe-exec.ts +2 -0
- package/src/tools/verify/authoring.ts +1119 -0
- package/src/tools/verify/catalog.ts +346 -0
- package/src/tools/verify/index.ts +13 -3
- package/src/tools/verify/scripts.ts +135 -37
- package/src/tools/verify/surface.ts +9 -5
- package/src/tools/worker-evidence.ts +35 -12
- package/dist/chunk-MNA4JGU4.js +0 -255
- package/dist/chunk-SRF2PJNW.js +0 -184
- package/dist/chunk-T6YILFSB.js +0 -80
- package/dist/chunk-VAKQQHWR.js +0 -434
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transcript detail policy: the one place `/output minimal|default|verbose`
|
|
3
|
+
* becomes a decision about how much of each foldable transcript block is open
|
|
4
|
+
* before the operator touches it.
|
|
5
|
+
*
|
|
6
|
+
* The chat panel used to consult the verbosity string in four places, each
|
|
7
|
+
* spelling the rule a little differently, and nothing else (running rows, live
|
|
8
|
+
* tool output, worker cards in replay) consulted it at all. The panel now reads
|
|
9
|
+
* this policy once per frame and threads it down; the verbosity string never
|
|
10
|
+
* reaches a renderer.
|
|
11
|
+
*
|
|
12
|
+
* Operator toggles (`Alt+O`, `Alt+R`, expand-all, collapse-all) are overrides
|
|
13
|
+
* layered on top of this policy, not fights with it: the effective state of a
|
|
14
|
+
* block is `override ?? policy`, and `resolveFold` is that one rule.
|
|
15
|
+
*
|
|
16
|
+
* Pure module: no I/O, no UI imports.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { OutputVerbosity } from "../core/defaults.js";
|
|
20
|
+
import type { ToolFoldDefault, ToolPresentationPolicy } from "../tools/presentation.js";
|
|
21
|
+
|
|
22
|
+
/** A block's fold state. The same vocabulary serves tools, workers, thinking, and local bash. */
|
|
23
|
+
export type Fold = ToolFoldDefault;
|
|
24
|
+
|
|
25
|
+
/** An operator's explicit choice for one block, or none. */
|
|
26
|
+
export type FoldOverride = Fold | undefined;
|
|
27
|
+
|
|
28
|
+
export interface TranscriptDetailPolicy {
|
|
29
|
+
/** Finished tool call body: folded subline, the tool's own default, or open. */
|
|
30
|
+
toolBody: "folded" | "per-tool" | "expanded";
|
|
31
|
+
/** In-flight tool call: one-line row with elapsed, or the header plus streaming body. */
|
|
32
|
+
runningTool: "row" | "body";
|
|
33
|
+
/** Reasoning stretch: bare marker, folded marker with live progress, or the open rail. */
|
|
34
|
+
thinking: "marker" | "folded" | "rail";
|
|
35
|
+
/** Worker block: folded card, the origin default, or open. */
|
|
36
|
+
worker: "folded" | "origin" | "expanded";
|
|
37
|
+
/** Settled turn receipt: none, `turn · in N · out M`, or the full receipt. */
|
|
38
|
+
receipt: "none" | "compact" | "full";
|
|
39
|
+
/** Failed tool under its folded row: the bounded excerpt, or the full body. */
|
|
40
|
+
errors: "excerpt" | "body";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const MINIMAL: TranscriptDetailPolicy = {
|
|
44
|
+
toolBody: "folded",
|
|
45
|
+
runningTool: "row",
|
|
46
|
+
thinking: "marker",
|
|
47
|
+
worker: "folded",
|
|
48
|
+
receipt: "none",
|
|
49
|
+
errors: "excerpt",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const DEFAULT: TranscriptDetailPolicy = {
|
|
53
|
+
toolBody: "per-tool",
|
|
54
|
+
runningTool: "row",
|
|
55
|
+
thinking: "folded",
|
|
56
|
+
worker: "origin",
|
|
57
|
+
receipt: "compact",
|
|
58
|
+
errors: "excerpt",
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const VERBOSE: TranscriptDetailPolicy = {
|
|
62
|
+
toolBody: "expanded",
|
|
63
|
+
runningTool: "body",
|
|
64
|
+
thinking: "rail",
|
|
65
|
+
worker: "expanded",
|
|
66
|
+
receipt: "full",
|
|
67
|
+
errors: "body",
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Map a verbosity to the full policy table. An absent verbosity (a panel built
|
|
72
|
+
* without settings) is the balanced default, so the literal never has to be
|
|
73
|
+
* spelled by the caller.
|
|
74
|
+
*/
|
|
75
|
+
export function transcriptDetail(verbosity: OutputVerbosity | undefined): TranscriptDetailPolicy {
|
|
76
|
+
switch (verbosity) {
|
|
77
|
+
case "minimal":
|
|
78
|
+
return MINIMAL;
|
|
79
|
+
case "verbose":
|
|
80
|
+
return VERBOSE;
|
|
81
|
+
default:
|
|
82
|
+
return DEFAULT;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Effective state of one block: the operator's override when set, else the policy's answer. */
|
|
87
|
+
export function resolveFold(override: FoldOverride, policyFold: Fold): Fold {
|
|
88
|
+
return override ?? policyFold;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** The fold the policy gives a finished tool call, through the tool's own presentation when asked to. */
|
|
92
|
+
export function policyToolFold(detail: TranscriptDetailPolicy, presentation: ToolPresentationPolicy): Fold {
|
|
93
|
+
if (detail.toolBody === "per-tool") return presentation.foldDefault;
|
|
94
|
+
return detail.toolBody;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The fold the policy gives an in-flight tool call. */
|
|
98
|
+
export function policyRunningToolFold(detail: TranscriptDetailPolicy): Fold {
|
|
99
|
+
return detail.runningTool === "body" ? "expanded" : "folded";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The fold the policy gives a reasoning stretch. */
|
|
103
|
+
export function policyThinkingFold(detail: TranscriptDetailPolicy): Fold {
|
|
104
|
+
return detail.thinking === "rail" ? "expanded" : "folded";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The fold the policy gives a worker block. `askedByModel` is the origin rule
|
|
109
|
+
* the caller already knows: a run the model asked for folds, the operator's own
|
|
110
|
+
* run opens.
|
|
111
|
+
*/
|
|
112
|
+
export function policyWorkerFold(detail: TranscriptDetailPolicy, askedByModel: boolean): Fold {
|
|
113
|
+
if (detail.worker === "origin") return askedByModel ? "folded" : "expanded";
|
|
114
|
+
return detail.worker;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The override that flips a block away from its current effective state. */
|
|
118
|
+
export function toggledFold(effective: Fold): Fold {
|
|
119
|
+
return effective === "expanded" ? "folded" : "expanded";
|
|
120
|
+
}
|
|
@@ -10,11 +10,16 @@ import {
|
|
|
10
10
|
BusChannels,
|
|
11
11
|
type ContextActivityStatus,
|
|
12
12
|
type ContextPrunedPayload,
|
|
13
|
+
type ContextRecalledPayload,
|
|
13
14
|
type ContextWarningPayload,
|
|
14
15
|
} from "../core/bus-events.js";
|
|
15
16
|
import type { ClioSettings } from "../core/config.js";
|
|
16
17
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
17
18
|
import type { ToolName } from "../core/tool-names.js";
|
|
19
|
+
import { buildEvictionFields, planEviction } from "../domains/context/working-set/engine.js";
|
|
20
|
+
import { foldWorkingSet } from "../domains/context/working-set/fold.js";
|
|
21
|
+
import { resolveWorkingSetPolicy } from "../domains/context/working-set/policies/index.js";
|
|
22
|
+
import { selectVisibleEntries } from "../domains/context/working-set/visible.js";
|
|
18
23
|
import type { ObservabilityContract } from "../domains/observability/contract.js";
|
|
19
24
|
import type { CompiledSessionPrompt, SessionPromptInputs } from "../domains/prompts/compiler.js";
|
|
20
25
|
import type { PromptsContract } from "../domains/prompts/contract.js";
|
|
@@ -27,6 +32,7 @@ import {
|
|
|
27
32
|
} from "../domains/session/compaction/auto.js";
|
|
28
33
|
import type { CompactResult } from "../domains/session/compaction/compact.js";
|
|
29
34
|
import { maskStaleObservations } from "../domains/session/compaction/mask-observations.js";
|
|
35
|
+
import { estimateTokens } from "../domains/session/compaction/tokens.js";
|
|
30
36
|
import {
|
|
31
37
|
appendContextSnapshot,
|
|
32
38
|
type CaptureContextSnapshotInput,
|
|
@@ -57,7 +63,7 @@ import {
|
|
|
57
63
|
sumRunUsage,
|
|
58
64
|
toolNamesFromAgentState,
|
|
59
65
|
} from "./chat-loop-messages.js";
|
|
60
|
-
import {
|
|
66
|
+
import { buildModelReplayAgentMessagesFromTurns } from "./model-session-replay.js";
|
|
61
67
|
import { renderCompactionSummaryLine } from "./renderers/compaction-summary.js";
|
|
62
68
|
import type { TurnMiddleware } from "./turn-middleware.js";
|
|
63
69
|
import type { AgentRuntime, ChatTurnState } from "./turn-state.js";
|
|
@@ -73,6 +79,8 @@ export interface TurnContextDeps {
|
|
|
73
79
|
bus?: SafeEventBus | undefined;
|
|
74
80
|
readSessionEntries?: (() => ReadonlyArray<SessionEntry>) | undefined;
|
|
75
81
|
autoCompact?: ((instructions?: string, trigger?: CompactionTrigger) => Promise<CompactResult | null>) | undefined;
|
|
82
|
+
/** Test seam for the eviction planner; production uses `planEviction` from the working-set engine. */
|
|
83
|
+
planEviction?: typeof planEviction;
|
|
76
84
|
getMemorySection?: (() => string) | undefined;
|
|
77
85
|
middleware: TurnMiddleware;
|
|
78
86
|
emitNotice: (text: string) => void;
|
|
@@ -159,6 +167,11 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
159
167
|
let sessionPromptKey: string | null = null;
|
|
160
168
|
const sessionWorkingContextPaths = new Set<string>();
|
|
161
169
|
let pendingPromptLogEntry: SessionPromptCompileRecord | null = null;
|
|
170
|
+
// A post-tool guard can run after every tool result in one model turn. Once
|
|
171
|
+
// both automatic stages report that they have nothing to do, remember that
|
|
172
|
+
// stable user-turn id so the remaining results do not repeat the same plan
|
|
173
|
+
// and summary probes. A new submitted user turn gets a new id naturally.
|
|
174
|
+
let emptyAutoCompactTurnId: string | null = null;
|
|
162
175
|
|
|
163
176
|
// Cache-disturbance honesty (T3.3). Dispatch traffic and history
|
|
164
177
|
// compaction invalidate a single-slot local backend's prefix cache.
|
|
@@ -167,20 +180,41 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
167
180
|
// its first assistant entry, and shows one dim notice.
|
|
168
181
|
const pendingColdReasons = new Set<string>();
|
|
169
182
|
let runExpectedColdReasons: string[] = [];
|
|
170
|
-
let
|
|
183
|
+
let nextAssistantColdReasons: string[] = [];
|
|
184
|
+
// A working-set eviction changes the prefix itself, so it cools every
|
|
185
|
+
// tier's cache, not only a single-slot local one. Dispatch and compaction
|
|
186
|
+
// disturbances keep the local-native gate below.
|
|
187
|
+
const TIER_INDEPENDENT_COLD_REASONS: ReadonlySet<string> = new Set(["working_set_evict"]);
|
|
188
|
+
const stampsOnTier = (reason: string, runtimeId: string | undefined): boolean =>
|
|
189
|
+
TIER_INDEPENDENT_COLD_REASONS.has(reason) ||
|
|
190
|
+
(runtimeId !== undefined && deps.providers.getRuntime(runtimeId)?.tier === "local-native");
|
|
191
|
+
const noteColdReason = (reason: string): void => {
|
|
192
|
+
if (!state.streaming) {
|
|
193
|
+
pendingColdReasons.add(reason);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!stampsOnTier(reason, state.runtime?.runtimeId)) return;
|
|
197
|
+
if (!runExpectedColdReasons.includes(reason)) runExpectedColdReasons.push(reason);
|
|
198
|
+
if (nextAssistantColdReasons.includes(reason)) return;
|
|
199
|
+
nextAssistantColdReasons.push(reason);
|
|
200
|
+
deps.emitNotice(`[context engine] backend prefix cache likely cold this turn: ${reason}`);
|
|
201
|
+
};
|
|
171
202
|
const unsubscribeColdReasonSources = [
|
|
172
203
|
...[BusChannels.DispatchStarted, BusChannels.DispatchCompleted, BusChannels.DispatchFailed].map(
|
|
173
204
|
(channel) =>
|
|
174
205
|
deps.bus?.on(channel, () => {
|
|
175
|
-
|
|
206
|
+
noteColdReason("dispatch");
|
|
176
207
|
}) ?? null,
|
|
177
208
|
),
|
|
178
209
|
...[BusChannels.CompactionBegin, BusChannels.CompactionEnd].map(
|
|
179
210
|
(channel) =>
|
|
180
211
|
deps.bus?.on(channel, () => {
|
|
181
|
-
|
|
212
|
+
noteColdReason("compaction");
|
|
182
213
|
}) ?? null,
|
|
183
214
|
),
|
|
215
|
+
deps.bus?.on(BusChannels.ContextRecalled, (payload: ContextRecalledPayload) => {
|
|
216
|
+
middleware.fireCompactionHook("working_set_recall", payload.trigger);
|
|
217
|
+
}) ?? null,
|
|
184
218
|
];
|
|
185
219
|
|
|
186
220
|
/**
|
|
@@ -292,7 +326,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
292
326
|
|
|
293
327
|
const refreshAgentMessagesFromSession = (agentRuntime: AgentRuntime): ReadonlyArray<SessionEntry> => {
|
|
294
328
|
const refreshedEntries = deps.readSessionEntries?.() ?? [];
|
|
295
|
-
agentRuntime.agent.state.messages =
|
|
329
|
+
agentRuntime.agent.state.messages = buildModelReplayAgentMessagesFromTurns(refreshedEntries, {
|
|
296
330
|
...(state.lastTurnId ? { activeLeafTurnId: state.lastTurnId } : {}),
|
|
297
331
|
});
|
|
298
332
|
state.replayedContextMessages = [];
|
|
@@ -339,12 +373,12 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
339
373
|
|
|
340
374
|
/**
|
|
341
375
|
* Two-mechanism context protection. When pressure crosses the single
|
|
342
|
-
* threshold, first
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
*
|
|
376
|
+
* threshold, first apply a non-destructive working-set eviction. If pressure
|
|
377
|
+
* stays above the threshold, delegate to the pi-style LLM compaction path:
|
|
378
|
+
* append a compaction summary entry, then replay from the session view. The
|
|
379
|
+
* destructive observation mask remains only as a one-release escape hatch.
|
|
346
380
|
*
|
|
347
|
-
* `force = true` skips the pressure check and
|
|
381
|
+
* `force = true` skips the pressure check and every pre-stage and runs
|
|
348
382
|
* the LLM summary directly. Used for `/context compact`, CLIO_CODER_FORCE_COMPACT=1,
|
|
349
383
|
* and overflow recovery.
|
|
350
384
|
*/
|
|
@@ -355,11 +389,17 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
355
389
|
triggerOverride?: CompactionTrigger,
|
|
356
390
|
pendingUserText?: string,
|
|
357
391
|
): Promise<boolean> => {
|
|
358
|
-
if (!deps.
|
|
392
|
+
if (!deps.readSessionEntries) return false;
|
|
393
|
+
const activeAutoTurnId = force ? null : state.activeUserTurnId;
|
|
394
|
+
if (activeAutoTurnId && emptyAutoCompactTurnId === activeAutoTurnId) return false;
|
|
359
395
|
const settings = deps.getSettings();
|
|
360
396
|
const cfg = settings.compaction;
|
|
361
397
|
const autoEnabled = cfg?.auto !== false;
|
|
362
398
|
if (!force && !autoEnabled) return false;
|
|
399
|
+
let preSummaryStageActed = false;
|
|
400
|
+
const rememberEmptyAutomaticAttempt = (): void => {
|
|
401
|
+
if (!force && !preSummaryStageActed && activeAutoTurnId) emptyAutoCompactTurnId = activeAutoTurnId;
|
|
402
|
+
};
|
|
363
403
|
|
|
364
404
|
const compactionThreshold = cfg?.threshold ?? DEFAULT_COMPACTION_THRESHOLD;
|
|
365
405
|
const trigger: CompactionTrigger = triggerOverride ?? (force ? "force" : "auto");
|
|
@@ -369,91 +409,198 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
369
409
|
const verdict = shouldCompact(estimate.tokens, compactionThreshold, estimate.contextWindow);
|
|
370
410
|
if (!verdict.shouldCompact) return false;
|
|
371
411
|
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
// CompactionBegin/End subscription explains the next cold turn.
|
|
412
|
+
// One-release compatibility escape hatch. This is the destructive
|
|
413
|
+
// pre-stage that working-set eviction replaces; keep it byte-for-byte
|
|
414
|
+
// reachable only when explicitly requested.
|
|
376
415
|
if (deps.session?.current()) {
|
|
377
416
|
const beforeSnapshotId = currentContextSnapshot?.snapshotId ?? null;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
masked.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
417
|
+
if (process.env.CLIO_CODER_LEGACY_MASK === "1") {
|
|
418
|
+
let masked: ReturnType<typeof maskStaleObservations>;
|
|
419
|
+
try {
|
|
420
|
+
masked = maskStaleObservations(deps.readSessionEntries() ?? [], cfg?.excludeLastTurns ?? 6);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
middleware.fireCompactionHook("mask_observations", trigger, estimate.tokens);
|
|
423
|
+
deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
|
|
424
|
+
emitCompactionActivity("started", "compacting context (mask stage)");
|
|
425
|
+
emitCompactionActivity("failed", compactionFailureMessage(error));
|
|
426
|
+
deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
if (masked.changed) {
|
|
430
|
+
preSummaryStageActed = true;
|
|
431
|
+
middleware.fireCompactionHook("mask_observations", trigger, estimate.tokens);
|
|
432
|
+
deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
|
|
433
|
+
emitCompactionActivity("started", "compacting context (mask stage)");
|
|
434
|
+
deps.session.replaceEntries(masked.entries);
|
|
435
|
+
refreshAgentMessagesFromSession(agentRuntime);
|
|
436
|
+
deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
|
|
437
|
+
|
|
438
|
+
const postMaskSnapshot = captureRuntimeContextSnapshot(
|
|
439
|
+
agentRuntime,
|
|
440
|
+
state.activeUserTurnId || "compaction",
|
|
441
|
+
compactionThreshold,
|
|
442
|
+
);
|
|
443
|
+
currentContextSnapshot = postMaskSnapshot;
|
|
444
|
+
persistContextSnapshot(postMaskSnapshot);
|
|
445
|
+
|
|
446
|
+
const tokensAfterMask = snapshotInputTokens(postMaskSnapshot);
|
|
447
|
+
lastCompactionEvent = {
|
|
448
|
+
stage: "mask_observations",
|
|
449
|
+
tokensBefore: estimate.tokens,
|
|
450
|
+
tokensAfter: tokensAfterMask,
|
|
451
|
+
trigger,
|
|
452
|
+
};
|
|
453
|
+
deps.bus?.emit(BusChannels.ContextPruned, {
|
|
454
|
+
stage: "mask_observations",
|
|
455
|
+
pressure: verdict.pressure,
|
|
456
|
+
tokensBefore: estimate.tokens,
|
|
457
|
+
tokensAfter: tokensAfterMask,
|
|
458
|
+
maskedObservations: masked.maskedObservations,
|
|
459
|
+
maskedThinkingBlocks: masked.maskedThinkingBlocks,
|
|
460
|
+
maskedThinkingChars: masked.maskedThinkingChars,
|
|
461
|
+
trigger,
|
|
462
|
+
snapshotIdBefore: beforeSnapshotId,
|
|
463
|
+
snapshotIdAfter: postMaskSnapshot.snapshotId,
|
|
464
|
+
at: Date.now(),
|
|
465
|
+
} satisfies ContextPrunedPayload);
|
|
466
|
+
emitCompactionActivity("completed", `${masked.maskedObservations} observations masked`);
|
|
467
|
+
const thinkingNote =
|
|
468
|
+
masked.maskedThinkingBlocks > 0
|
|
469
|
+
? `, ${masked.maskedThinkingBlocks} thinking blocks dropped (~${masked.maskedThinkingChars} chars)`
|
|
470
|
+
: "";
|
|
471
|
+
deps.emitNotice(
|
|
472
|
+
`[context engine] mask_observations: ${masked.maskedObservations} observations masked${thinkingNote}; ~${estimate.tokens} tokens -> ~${tokensAfterMask} tokens`,
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
const after = liveContextEstimate(agentRuntime, pendingUserText);
|
|
476
|
+
if (!shouldCompact(after.tokens, compactionThreshold, after.contextWindow).shouldCompact) return true;
|
|
477
|
+
}
|
|
478
|
+
} else if (settings.context.workingSet.enabled) {
|
|
479
|
+
let planned: ReturnType<typeof planEviction>;
|
|
480
|
+
try {
|
|
481
|
+
const entries = deps.readSessionEntries() ?? [];
|
|
482
|
+
const view = foldWorkingSet(entries, state.lastTurnId ?? undefined);
|
|
483
|
+
const policy = resolveWorkingSetPolicy(settings.context.workingSet.policy);
|
|
484
|
+
planned = (deps.planEviction ?? planEviction)(policy, {
|
|
485
|
+
entries: selectVisibleEntries(entries, state.lastTurnId ?? undefined),
|
|
486
|
+
view,
|
|
487
|
+
cwd: deps.session.current()?.cwd ?? null,
|
|
488
|
+
settings: settings.context.workingSet,
|
|
489
|
+
pressure: {
|
|
490
|
+
tokens: estimate.tokens,
|
|
491
|
+
contextWindow: estimate.contextWindow,
|
|
492
|
+
threshold: compactionThreshold,
|
|
493
|
+
target: settings.context.workingSet.target,
|
|
494
|
+
},
|
|
495
|
+
estimateTokens,
|
|
496
|
+
});
|
|
497
|
+
} catch (error) {
|
|
498
|
+
middleware.fireCompactionHook("working_set_evict", "pressure", estimate.tokens);
|
|
499
|
+
emitCompactionActivity("started", "compacting context (working-set eviction)");
|
|
500
|
+
emitCompactionActivity("failed", compactionFailureMessage(error));
|
|
501
|
+
throw error;
|
|
502
|
+
}
|
|
503
|
+
if (planned) {
|
|
504
|
+
preSummaryStageActed = true;
|
|
505
|
+
middleware.fireCompactionHook("working_set_evict", "pressure", estimate.tokens);
|
|
506
|
+
emitCompactionActivity("started", "compacting context (working-set eviction)");
|
|
507
|
+
try {
|
|
508
|
+
deps.session.appendEntry({
|
|
509
|
+
...buildEvictionFields(planned, {
|
|
510
|
+
trigger: "pressure",
|
|
511
|
+
pressureBefore: verdict.pressure,
|
|
512
|
+
snapshotIdBefore: beforeSnapshotId,
|
|
513
|
+
}),
|
|
514
|
+
// appendEntry does not infer this anchor; the interactive
|
|
515
|
+
// cursor is the leaf the next message will extend.
|
|
516
|
+
parentTurnId: state.lastTurnId,
|
|
517
|
+
});
|
|
518
|
+
noteColdReason("working_set_evict");
|
|
519
|
+
refreshAgentMessagesFromSession(agentRuntime);
|
|
520
|
+
|
|
521
|
+
const postEvictionSnapshot = captureRuntimeContextSnapshot(
|
|
522
|
+
agentRuntime,
|
|
523
|
+
state.activeUserTurnId || "compaction",
|
|
524
|
+
compactionThreshold,
|
|
525
|
+
);
|
|
526
|
+
currentContextSnapshot = postEvictionSnapshot;
|
|
527
|
+
persistContextSnapshot(postEvictionSnapshot);
|
|
528
|
+
|
|
529
|
+
// Every surface that describes this event (the notice, the toast
|
|
530
|
+
// ContextPruned feeds, the overlay's last-compaction line, the
|
|
531
|
+
// ledger entry) quotes the plan: the same chars/4 pricing over the
|
|
532
|
+
// same visible slice. The live estimate prices the agent message
|
|
533
|
+
// list and differs by the tool schemas and replay text; it stays
|
|
534
|
+
// what the meter and the re-check below read, not what the event
|
|
535
|
+
// reports about itself.
|
|
536
|
+
lastCompactionEvent = {
|
|
537
|
+
stage: "working_set",
|
|
538
|
+
tokensBefore: planned.tokensBefore,
|
|
539
|
+
tokensAfter: planned.tokensAfter,
|
|
540
|
+
trigger,
|
|
541
|
+
};
|
|
542
|
+
deps.bus?.emit(BusChannels.ContextPruned, {
|
|
543
|
+
stage: "working_set",
|
|
544
|
+
pressure: verdict.pressure,
|
|
545
|
+
tokensBefore: planned.tokensBefore,
|
|
546
|
+
tokensAfter: planned.tokensAfter,
|
|
547
|
+
trigger,
|
|
548
|
+
snapshotIdBefore: beforeSnapshotId,
|
|
549
|
+
snapshotIdAfter: postEvictionSnapshot.snapshotId,
|
|
550
|
+
policyId: planned.policyId,
|
|
551
|
+
evictedItems: planned.items.length,
|
|
552
|
+
at: Date.now(),
|
|
553
|
+
} satisfies ContextPrunedPayload);
|
|
554
|
+
const itemsWord = planned.items.length === 1 ? "item" : "items";
|
|
555
|
+
emitCompactionActivity("completed", `${planned.items.length} working-set ${itemsWord} evicted`);
|
|
556
|
+
deps.emitNotice(
|
|
557
|
+
`[context engine] working set: ${planned.items.length} ${itemsWord} evicted by ${planned.policyId}; ~${planned.tokensBefore} -> ~${planned.tokensAfter} tokens, recall by ref with context(scope="recall")`,
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
const after = liveContextEstimate(agentRuntime, pendingUserText);
|
|
561
|
+
if (!shouldCompact(after.tokens, compactionThreshold, after.contextWindow).shouldCompact) return true;
|
|
562
|
+
} catch (error) {
|
|
563
|
+
emitCompactionActivity("failed", compactionFailureMessage(error));
|
|
564
|
+
throw error;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
436
567
|
}
|
|
437
568
|
}
|
|
438
569
|
}
|
|
439
570
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
571
|
+
if (!deps.autoCompact) {
|
|
572
|
+
rememberEmptyAutomaticAttempt();
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
let summaryLifecycleStarted = false;
|
|
576
|
+
const startSummaryLifecycle = (): void => {
|
|
577
|
+
middleware.fireCompactionHook("llm_summary", trigger);
|
|
578
|
+
deps.bus?.emit(BusChannels.CompactionBegin, { trigger, at: Date.now() });
|
|
579
|
+
emitCompactionActivity("started", "compacting context (summary)");
|
|
580
|
+
summaryLifecycleStarted = true;
|
|
581
|
+
};
|
|
582
|
+
if (force) startSummaryLifecycle();
|
|
443
583
|
let result: CompactResult | null = null;
|
|
444
584
|
const beforeSnapshotId = currentContextSnapshot?.snapshotId ?? null;
|
|
445
585
|
try {
|
|
446
586
|
result = await compactionTrigger.fire(() => (deps.autoCompact ?? (async () => null))(instructions, trigger));
|
|
447
587
|
} catch (error) {
|
|
588
|
+
if (!summaryLifecycleStarted) startSummaryLifecycle();
|
|
448
589
|
emitCompactionActivity("failed", compactionFailureMessage(error));
|
|
449
|
-
throw error;
|
|
450
|
-
} finally {
|
|
451
590
|
deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
|
|
591
|
+
throw error;
|
|
452
592
|
}
|
|
453
593
|
if (!result || result.summary.length === 0) {
|
|
454
|
-
|
|
594
|
+
if (summaryLifecycleStarted) {
|
|
595
|
+
deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
|
|
596
|
+
emitCompactionActivity("completed", "nothing to compact");
|
|
597
|
+
} else {
|
|
598
|
+
rememberEmptyAutomaticAttempt();
|
|
599
|
+
}
|
|
455
600
|
return false;
|
|
456
601
|
}
|
|
602
|
+
if (!summaryLifecycleStarted) startSummaryLifecycle();
|
|
603
|
+
deps.bus?.emit(BusChannels.CompactionEnd, { trigger, at: Date.now() });
|
|
457
604
|
|
|
458
605
|
// The summarization call spends tokens on the same target the turn would
|
|
459
606
|
// have. The ledger entry carries its usage for a later reseed; this is the
|
|
@@ -790,16 +937,17 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
790
937
|
consumeExpectedColdReasons(runtimeId: string): void {
|
|
791
938
|
// Cache-disturbance honesty (T3.3): consume disturbances since
|
|
792
939
|
// the last settled run. Only single-slot local backends lose their
|
|
793
|
-
// prefix cache to interleaved work, so
|
|
794
|
-
// stamp
|
|
940
|
+
// prefix cache to interleaved work, so dispatch and compaction
|
|
941
|
+
// stamp only on local-native targets; a working-set eviction moved
|
|
942
|
+
// the prefix itself and stamps on every tier (see stampsOnTier).
|
|
795
943
|
runExpectedColdReasons = [];
|
|
796
|
-
|
|
944
|
+
nextAssistantColdReasons = [];
|
|
797
945
|
if (pendingColdReasons.size > 0) {
|
|
798
|
-
const reasons = [...pendingColdReasons];
|
|
946
|
+
const reasons = [...pendingColdReasons].filter((reason) => stampsOnTier(reason, runtimeId));
|
|
799
947
|
pendingColdReasons.clear();
|
|
800
|
-
if (
|
|
948
|
+
if (reasons.length > 0) {
|
|
801
949
|
runExpectedColdReasons = reasons;
|
|
802
|
-
|
|
950
|
+
nextAssistantColdReasons = reasons;
|
|
803
951
|
deps.emitNotice(`[context engine] backend prefix cache likely cold this turn: ${reasons.join(", ")}`);
|
|
804
952
|
}
|
|
805
953
|
}
|
|
@@ -818,9 +966,9 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
818
966
|
cacheWrite,
|
|
819
967
|
backendVerdict: backendCacheVerdict(input, cacheRead),
|
|
820
968
|
};
|
|
821
|
-
if (
|
|
822
|
-
promptCache.expectedColdReasons = [...
|
|
823
|
-
|
|
969
|
+
if (nextAssistantColdReasons.length > 0) {
|
|
970
|
+
promptCache.expectedColdReasons = [...nextAssistantColdReasons];
|
|
971
|
+
nextAssistantColdReasons = [];
|
|
824
972
|
}
|
|
825
973
|
return promptCache;
|
|
826
974
|
},
|
|
@@ -834,6 +982,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
834
982
|
cacheWriteTokens: cacheSummary.cacheRead > 0 || cacheSummary.cacheWrite > 0 ? cacheSummary.cacheWrite : null,
|
|
835
983
|
uncachedInputTokens: cacheSummary.input,
|
|
836
984
|
backendVerdict: runFirstCallVerdict,
|
|
985
|
+
...(runExpectedColdReasons.length > 0 ? { expectedColdReasons: [...runExpectedColdReasons] } : {}),
|
|
837
986
|
};
|
|
838
987
|
}
|
|
839
988
|
},
|
|
@@ -844,6 +993,7 @@ export function createTurnContext(deps: TurnContextDeps): TurnContext {
|
|
|
844
993
|
sessionPromptKey = null;
|
|
845
994
|
sessionWorkingContextPaths.clear();
|
|
846
995
|
pendingPromptLogEntry = null;
|
|
996
|
+
emptyAutoCompactTurnId = null;
|
|
847
997
|
const session = deps.session?.current();
|
|
848
998
|
currentContextSnapshot = session ? getLatestContextSnapshot(session) : null;
|
|
849
999
|
},
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
type MiddlewareToolChoiceControl,
|
|
16
16
|
} from "../domains/middleware/index.js";
|
|
17
17
|
import type { SessionContract } from "../domains/session/contract.js";
|
|
18
|
-
import type { CompactionTrigger } from "../domains/session/entries.js";
|
|
18
|
+
import type { CompactionTrigger, EvictionTrigger, RecallTrigger } from "../domains/session/entries.js";
|
|
19
19
|
import type { AgentMessage } from "../engine/types.js";
|
|
20
20
|
import { extractText, hasStructuredToolCall, toolNamesFromAgentState } from "./chat-loop-messages.js";
|
|
21
21
|
import type { AgentRuntime, ChatTurnState } from "./turn-state.js";
|
|
@@ -42,8 +42,8 @@ export interface TurnMiddleware {
|
|
|
42
42
|
terminalToolResult?: { toolCallId: string; toolName: string },
|
|
43
43
|
): Promise<void>;
|
|
44
44
|
fireCompactionHook(
|
|
45
|
-
stage: "mask_observations" | "llm_summary",
|
|
46
|
-
trigger: CompactionTrigger,
|
|
45
|
+
stage: "mask_observations" | "working_set_evict" | "working_set_recall" | "llm_summary",
|
|
46
|
+
trigger: CompactionTrigger | EvictionTrigger | RecallTrigger,
|
|
47
47
|
tokensBefore?: number,
|
|
48
48
|
): void;
|
|
49
49
|
flushPendingReminders(): string;
|
|
@@ -240,9 +240,9 @@ export function createTurnMiddleware(deps: TurnMiddlewareDeps): TurnMiddleware {
|
|
|
240
240
|
},
|
|
241
241
|
|
|
242
242
|
/**
|
|
243
|
-
* Observe-only lifecycle point fired before each compaction
|
|
244
|
-
*
|
|
245
|
-
*
|
|
243
|
+
* Observe-only lifecycle point fired before each compaction or working-set
|
|
244
|
+
* stage. Consumers record telemetry or state around projected context
|
|
245
|
+
* changes; returned effects are discarded by design.
|
|
246
246
|
*/
|
|
247
247
|
fireCompactionHook(stage, trigger, tokensBefore): void {
|
|
248
248
|
if (!deps.middleware) return;
|