@iowarp/clio-coder 0.3.3 → 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 +39 -0
- package/CONTRIBUTING.md +1 -1
- package/README.md +3 -3
- package/dist/{acp-P2AQILE2.js → acp-S5R4RR5B.js} +7 -6
- package/dist/{agents-72W3BI7I.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-2DJ2KNFG.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-4XUGQOHA.js → chunk-35MKKU5R.js} +4 -4
- package/dist/{chunk-LM5TQCJZ.js → chunk-3HZ5RWN2.js} +5 -5
- package/dist/{chunk-AGYYIBLL.js → chunk-3JLKSKD7.js} +2 -2
- package/dist/{chunk-KZWTDYJF.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-X6IAEBZR.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-V6RTAOC2.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-DUYJ5IO6.js → chunk-EDRHSCIE.js} +4 -4
- package/dist/{chunk-XBXAASKX.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-FNTMWMX5.js → chunk-HV5X7OR2.js} +14 -12
- package/dist/{chunk-TZTZS7QK.js → chunk-HXG4IURW.js} +5 -3
- package/dist/{chunk-5UFT4SUX.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-OC7FIQPC.js → chunk-KRPY7NTG.js} +10 -7
- package/dist/chunk-LL4KHSZI.js +22 -0
- package/dist/{chunk-DSELYM6W.js → chunk-MEQ45TQ4.js} +15 -9
- package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
- package/dist/{chunk-6SGHMWE3.js → chunk-N4CZJQRK.js} +5 -5
- package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
- package/dist/chunk-OZNBF4L3.js +23 -0
- package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
- package/dist/chunk-QQK64KLB.js +1360 -0
- package/dist/{chunk-SRDMMSEP.js → chunk-QQL5RT5M.js} +979 -1619
- package/dist/{chunk-LZSJBIVT.js → chunk-QWU7ZBO7.js} +70 -720
- package/dist/{chunk-2TLUCQVG.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-PIWWS5BL.js → chunk-UZHIZC5S.js} +7 -7
- package/dist/{chunk-UFIIWP2H.js → chunk-VAWWTKDP.js} +8 -8
- package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
- package/dist/{chunk-LW6DSM3M.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-PAJK6MAQ.js → chunk-X6COSD2O.js} +5 -5
- package/dist/chunk-ZGVHUX3M.js +66 -0
- package/dist/{chunk-LWLEKMDQ.js → chunk-ZYKPLLNQ.js} +510 -547
- package/dist/cli/index.js +27 -23
- package/dist/{clio-JOU4FXVA.js → clio-J5JIOIDS.js} +7 -6
- package/dist/{code-nav-7AX6FYE6.js → code-nav-AXCXSBHX.js} +5 -3
- package/dist/{config-XCDVKR23.js → config-OEBMIN2U.js} +37 -27
- package/dist/{configure-4GAP54ZW.js → configure-PUQOSIXQ.js} +16 -13
- package/dist/{context-5VKGUVJJ.js → context-EKDCKUUZ.js} +82 -7
- package/dist/{context-4UOGGLQ5.js → context-MGSE4Z2T.js} +33 -23
- package/dist/{context-77FM5DV5.js → context-URSXPBCK.js} +17 -9
- package/dist/{context-clear-XXJRLCJJ.js → context-clear-KDAJRNUK.js} +33 -23
- package/dist/context-working-set-SBKMPPI2.js +1552 -0
- package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-MSWN72NK.js} +43 -29
- package/dist/{doctor-HR46URBJ.js → doctor-7BSE27PJ.js} +10 -10
- package/dist/{eval-XSSNATB4.js → eval-IZGDOO4H.js} +9 -8
- package/dist/{evidence-6HG2PY2B.js → evidence-SR7WXB5B.js} +51 -23
- package/dist/{evolve-K7YU3NCY.js → evolve-K7VE2CBX.js} +30 -20
- package/dist/{fleet-VY3HHKN6.js → fleet-7XMJNQNF.js} +48 -38
- package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-AQNAH644.js} +3 -3
- package/dist/{init-JYGXI3FK.js → init-JGNPAYXT.js} +41 -31
- package/dist/{memory-WFZMGYHX.js → memory-4ALKDJ4Q.js} +32 -22
- package/dist/{models-I5QWSEOM.js → models-ZMMLFJNN.js} +22 -19
- package/dist/{monitor-GE4ID3IA.js → monitor-2F3T5KHP.js} +55 -43
- package/dist/{orchestrator-EM5MC3HM.js → orchestrator-ORHT43JB.js} +572 -388
- package/dist/{reset-L2FQEE3E.js → reset-NXGTYNUO.js} +4 -3
- package/dist/{run-ZU3QMZPZ.js → run-RF4WJGMT.js} +51 -41
- package/dist/{share-S5BZQC5I.js → share-UT3W6E4M.js} +5 -4
- package/dist/{skills-X5VXCRNQ.js → skills-PSACKC5Q.js} +2 -2
- package/dist/{skills-eval-WKIHWTHR.js → skills-eval-WJSI55RZ.js} +34 -24
- package/dist/{targets-SNCPI2NR.js → targets-PIIRAOYS.js} +23 -20
- package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-ULWXWNVY.js} +4 -3
- package/dist/{upgrade-JQHHPQ4K.js → upgrade-346TZ6AV.js} +18 -17
- package/dist/{usage-OR4O5SMZ.js → usage-6KKXR32N.js} +34 -24
- package/dist/verifiers-4UUM6TEE.js +1214 -0
- package/dist/verify-X5HDROLA.js +25 -0
- package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-7STOCIFZ.js} +42 -31
- package/dist/worker/entry.js +33 -24
- package/docs/README.md +7 -6
- 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 +2 -2
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +7 -9
- package/docs/observability.md +4 -4
- 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 +31 -31
- 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 +1 -1
- 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/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/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 +17 -1
- package/src/interactive/chat-renderer.ts +30 -21
- 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/interactive-slash-runtime.ts +37 -1
- 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/renderers/tool-execution.ts +18 -2
- package/src/interactive/session-transcript.ts +2 -2
- package/src/interactive/slash-commands.ts +29 -2
- 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 +11 -5
- 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/registry.ts +59 -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-J7CWMCQD.js +0 -255
- package/dist/chunk-T6YILFSB.js +0 -80
- package/dist/chunk-VAKQQHWR.js +0 -434
- package/dist/chunk-VPAYEGVX.js +0 -184
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/context recall <ref>`: the operator's half of working-set recall.
|
|
3
|
+
*
|
|
4
|
+
* The model recalls through `context(scope="recall", ref=...)`, which puts the
|
|
5
|
+
* body back in front of the model at the tail of the working set. This command
|
|
6
|
+
* answers a different question, asked by a person: what was in the result the
|
|
7
|
+
* marker replaced. So the body goes to the transcript and nowhere else. It is
|
|
8
|
+
* never submitted as a turn, never replayed, and never counted against the
|
|
9
|
+
* context window, which is why an operator can read a 40k-line build log back
|
|
10
|
+
* without paying for it.
|
|
11
|
+
*
|
|
12
|
+
* Everything else is identical to the tool path, deliberately: the same
|
|
13
|
+
* `resolveRecall` over the same fold at the same live leaf, the same
|
|
14
|
+
* `contextRecall` ledger entry (with `trigger: "operator"`), and the same
|
|
15
|
+
* `BusChannels.ContextRecalled` publication. A recall is a churn signal
|
|
16
|
+
* whoever asked for it, and a policy that keeps evicting something a human
|
|
17
|
+
* keeps reading back is a policy worth changing.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { ContextRecalledPayload } from "../core/bus-events.js";
|
|
21
|
+
import type { EvictedState } from "../domains/context/working-set/contract.js";
|
|
22
|
+
import { foldWorkingSet } from "../domains/context/working-set/fold.js";
|
|
23
|
+
import {
|
|
24
|
+
buildRecallFields,
|
|
25
|
+
recallErrorMessage,
|
|
26
|
+
recallParentTurnId,
|
|
27
|
+
resolveRecall,
|
|
28
|
+
} from "../domains/context/working-set/recall.js";
|
|
29
|
+
import type { SessionEntryInput } from "../domains/session/contract.js";
|
|
30
|
+
import type { SessionEntry } from "../domains/session/entries.js";
|
|
31
|
+
|
|
32
|
+
/** Ledger access the command needs, mirroring the context tool's `ContextSessionDeps`. */
|
|
33
|
+
export interface OperatorRecallDeps {
|
|
34
|
+
hasSession(): boolean;
|
|
35
|
+
readEntries(): ReadonlyArray<SessionEntry>;
|
|
36
|
+
/** The live append point (`/tree` pin or tree leaf); undefined lets the fold infer it. */
|
|
37
|
+
activeLeafTurnId(): string | undefined;
|
|
38
|
+
appendEntry(entry: SessionEntryInput): SessionEntry;
|
|
39
|
+
/** Publisher for `BusChannels.ContextRecalled`; the runtime supplies the bus. */
|
|
40
|
+
onRecalled?: (payload: ContextRecalledPayload) => void;
|
|
41
|
+
now?: () => number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type OperatorRecallOutcome =
|
|
45
|
+
| {
|
|
46
|
+
ok: true;
|
|
47
|
+
/** One-line summary for the notice bar. */
|
|
48
|
+
headline: string;
|
|
49
|
+
/** The original body, byte-exact, for the transcript. */
|
|
50
|
+
body: string;
|
|
51
|
+
}
|
|
52
|
+
| { ok: false; message: string };
|
|
53
|
+
|
|
54
|
+
function formatTokens(tokens: number): string {
|
|
55
|
+
return tokens.toLocaleString("en-US");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Ref, why it left, what it costs to read, and where the full artifact lives
|
|
60
|
+
* when the original result was offloaded. Nothing else: the body is on the next
|
|
61
|
+
* line and the operator is already looking at it.
|
|
62
|
+
*/
|
|
63
|
+
function headlineFor(ref: string, tokens: number, state: EvictedState | undefined, offloadPath?: string): string {
|
|
64
|
+
const parts = [ref];
|
|
65
|
+
if (state !== undefined) {
|
|
66
|
+
parts.push(state.by === undefined ? `evicted: ${state.reason}` : `evicted: ${state.reason} by ${state.by}`);
|
|
67
|
+
}
|
|
68
|
+
parts.push(`${formatTokens(tokens)} tokens`);
|
|
69
|
+
if (offloadPath !== undefined) parts.push(`offload: ${offloadPath}`);
|
|
70
|
+
return `[/context recall] ${parts.join(" · ")}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function runOperatorRecall(ref: string, deps: OperatorRecallDeps): OperatorRecallOutcome {
|
|
74
|
+
if (!deps.hasSession()) {
|
|
75
|
+
return { ok: false, message: "[/context recall] no active session; start one with /new or /resume first" };
|
|
76
|
+
}
|
|
77
|
+
const trimmed = ref.trim();
|
|
78
|
+
if (trimmed.length === 0) {
|
|
79
|
+
return { ok: false, message: "[/context recall] needs a ref: the turnId named in an [evicted ...] marker" };
|
|
80
|
+
}
|
|
81
|
+
const entries = deps.readEntries();
|
|
82
|
+
const leaf = deps.activeLeafTurnId();
|
|
83
|
+
const view = foldWorkingSet(entries, leaf);
|
|
84
|
+
const resolved = resolveRecall(entries, view, trimmed, leaf);
|
|
85
|
+
// A ref that resolves to nothing is usually a typo or a stale marker; the
|
|
86
|
+
// shared message ends with the refs the operator could have typed instead.
|
|
87
|
+
if (!resolved.ok)
|
|
88
|
+
return { ok: false, message: `[/context recall] ${recallErrorMessage(resolved.error, entries, view)}` };
|
|
89
|
+
const { result } = resolved;
|
|
90
|
+
const fields = buildRecallFields(result, { trigger: "operator" });
|
|
91
|
+
try {
|
|
92
|
+
deps.appendEntry({ ...fields, parentTurnId: recallParentTurnId(entries, leaf) });
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
message: `[/context recall] recall of ${result.ref.entry} could not be recorded: ${err instanceof Error ? err.message : String(err)}`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
deps.onRecalled?.({
|
|
100
|
+
ref: result.ref.entry,
|
|
101
|
+
trigger: "operator",
|
|
102
|
+
tokensReadmitted: result.tokens,
|
|
103
|
+
at: deps.now?.() ?? Date.now(),
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
headline: headlineFor(result.ref.entry, result.tokens, view.evicted.get(result.ref.entry), result.offloadPath),
|
|
108
|
+
body: result.body,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { BusChannels } from "../core/bus-events.js";
|
|
3
4
|
import type { ClioSettings } from "../core/config.js";
|
|
4
5
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
5
6
|
import type { PendingSkillRequest } from "../core/skill-activation.js";
|
|
@@ -28,6 +29,7 @@ import { type ChatPanel, createChatPanel } from "./chat-panel.js";
|
|
|
28
29
|
import { rehydrateChatPanelFromTurns } from "./chat-renderer.js";
|
|
29
30
|
import { runCompactWithNotice } from "./command-fallbacks.js";
|
|
30
31
|
import { appendNotice, appendOperatorCommand } from "./command-output.js";
|
|
32
|
+
import { runOperatorRecall } from "./context-recall-command.js";
|
|
31
33
|
import { renderSessionHtml } from "./export-html/index.js";
|
|
32
34
|
import { dateLocal } from "./format-time.js";
|
|
33
35
|
import type { SettingsCenterRowId, SettingsSectionId } from "./overlays/settings.js";
|
|
@@ -100,8 +102,10 @@ export interface InteractiveSlashRuntimeDeps {
|
|
|
100
102
|
* Leaf lookup for `/export`, so the export follows the branch the session
|
|
101
103
|
* is actually on. current.jsonl still holds the abandoned turns after a
|
|
102
104
|
* `/tree` pin, and an unscoped rehydrate reproduced them (issue #109).
|
|
105
|
+
* `/context recall` uses the same leaf for its fold, plus `appendEntry` for
|
|
106
|
+
* the `contextRecall` record.
|
|
103
107
|
*/
|
|
104
|
-
session?: Pick<SessionContract, "tree">;
|
|
108
|
+
session?: Pick<SessionContract, "tree" | "current" | "appendEntry">;
|
|
105
109
|
expandSubmit: (text: string) => Promise<InteractiveSlashSubmitExpansion>;
|
|
106
110
|
openAskUser: AskUserHandler;
|
|
107
111
|
openSkillsHub: () => void;
|
|
@@ -512,6 +516,38 @@ export function createInteractiveSlashRuntime(deps: InteractiveSlashRuntimeDeps)
|
|
|
512
516
|
}
|
|
513
517
|
deps.openContextReset();
|
|
514
518
|
},
|
|
519
|
+
...(deps.session
|
|
520
|
+
? {
|
|
521
|
+
runContextRecall: (ref: string) => {
|
|
522
|
+
const session = deps.session;
|
|
523
|
+
if (!session) return;
|
|
524
|
+
const outcome = runOperatorRecall(ref, {
|
|
525
|
+
hasSession: () => session.current() !== null,
|
|
526
|
+
readEntries: () => {
|
|
527
|
+
const sessionId = deps.chat.getSessionId();
|
|
528
|
+
return sessionId ? deps.readStructuredEntries(sessionId) : [];
|
|
529
|
+
},
|
|
530
|
+
activeLeafTurnId: () => {
|
|
531
|
+
const meta = session.current();
|
|
532
|
+
return meta ? (session.tree(meta.id).leafId ?? undefined) : undefined;
|
|
533
|
+
},
|
|
534
|
+
appendEntry: (entry) => session.appendEntry(entry),
|
|
535
|
+
onRecalled: (payload) => deps.bus.emit(BusChannels.ContextRecalled, payload),
|
|
536
|
+
...(deps.now ? { now: () => (deps.now?.() ?? new Date()).getTime() } : {}),
|
|
537
|
+
});
|
|
538
|
+
if (!outcome.ok) {
|
|
539
|
+
appendCommandNotice("error", outcome.message);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
appendCommandNotice("success", outcome.headline);
|
|
543
|
+
// The body is transcript output, not a notice: notices collapse
|
|
544
|
+
// newlines, and a recalled build log is worth nothing on one line.
|
|
545
|
+
// It is a replay block either way, so it never enters model context.
|
|
546
|
+
deps.io.stdout(`${outcome.body}\n`);
|
|
547
|
+
deps.requestRender();
|
|
548
|
+
},
|
|
549
|
+
}
|
|
550
|
+
: {}),
|
|
515
551
|
runContextRefresh: () => {
|
|
516
552
|
if (!deps.onContextRefresh) {
|
|
517
553
|
deps.io.stderr("[/context refresh] context refresh not wired; pass onContextRefresh to startInteractive\n");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { foldWorkingSet } from "../domains/context/working-set/fold.js";
|
|
2
|
+
import { projectWorkingSet } from "../domains/context/working-set/project.js";
|
|
3
|
+
import type { SessionEntry } from "../domains/session/entries.js";
|
|
4
|
+
import type { AgentMessage } from "../engine/types.js";
|
|
5
|
+
import { buildReplayAgentMessagesFromTurns, type RehydrateChatPanelOptions } from "./chat-renderer.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Build provider-facing replay messages from the durable session ledger.
|
|
9
|
+
* Projection always honors existing eviction and recall entries; the enabled
|
|
10
|
+
* setting gates creation of new evictions, not replay of durable state.
|
|
11
|
+
* Visible transcript and export callers intentionally keep using the raw
|
|
12
|
+
* rehydration helpers so eviction remains a model projection, not data loss.
|
|
13
|
+
*/
|
|
14
|
+
export function buildModelReplayAgentMessagesFromTurns(
|
|
15
|
+
entries: ReadonlyArray<SessionEntry>,
|
|
16
|
+
options: RehydrateChatPanelOptions = {},
|
|
17
|
+
): AgentMessage[] {
|
|
18
|
+
const activeLeafTurnId = options.activeLeafTurnId ?? options.uptoTurnId;
|
|
19
|
+
const projected = projectWorkingSet(entries, foldWorkingSet(entries, activeLeafTurnId));
|
|
20
|
+
return buildReplayAgentMessagesFromTurns(projected, options);
|
|
21
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SafeEventBus } from "../core/event-bus.js";
|
|
2
|
+
import { foldWorkingSet } from "../domains/context/working-set/fold.js";
|
|
2
3
|
import type { DispatchContract } from "../domains/dispatch/index.js";
|
|
3
4
|
import { loadMemoryRecordsSync, type MemoryRecord } from "../domains/memory/index.js";
|
|
4
5
|
import type { ObservabilityContract } from "../domains/observability/index.js";
|
|
@@ -107,6 +108,11 @@ export function createOverlayGeneralOpeners(deps: OverlayGeneralOpenersDeps): Ov
|
|
|
107
108
|
deps.transitions.handle = openContextOverlayFactory(deps.tui, deps.getContextLedger, {
|
|
108
109
|
bus: deps.bus,
|
|
109
110
|
chat: deps.contextChat,
|
|
111
|
+
getWorkingSet: () => {
|
|
112
|
+
const readSessionEntries = deps.readSessionEntries;
|
|
113
|
+
if (!readSessionEntries) return null;
|
|
114
|
+
return foldWorkingSet(readSessionEntries(), deps.getSessionMeta()?.pinnedLeafTurnId ?? undefined);
|
|
115
|
+
},
|
|
110
116
|
});
|
|
111
117
|
deps.requestRender();
|
|
112
118
|
};
|
|
@@ -3,9 +3,10 @@ import type { SessionContract, SessionEntry } from "../domains/session/index.js"
|
|
|
3
3
|
import type { TUI } from "../engine/tui.js";
|
|
4
4
|
import type { ChatLoop } from "./chat-loop.js";
|
|
5
5
|
import type { ChatPanel } from "./chat-panel.js";
|
|
6
|
-
import {
|
|
6
|
+
import { rehydrateChatPanelFromTurns } from "./chat-renderer.js";
|
|
7
7
|
import { emitCommandNotice } from "./command-fallbacks.js";
|
|
8
8
|
import type { InteractiveNoticeLevel } from "./interactive-subscriptions.js";
|
|
9
|
+
import { buildModelReplayAgentMessagesFromTurns } from "./model-session-replay.js";
|
|
9
10
|
import type { OverlayTransitions } from "./overlay-transitions.js";
|
|
10
11
|
import { openCwdFallbackOverlay } from "./overlays/cwd-fallback.js";
|
|
11
12
|
import { openMessagePickerOverlay } from "./overlays/message-picker.js";
|
|
@@ -159,7 +160,7 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
159
160
|
const replayOptions = leafTurnId ? { activeLeafTurnId: leafTurnId } : {};
|
|
160
161
|
deps.resetTranscript();
|
|
161
162
|
rehydrateChatPanelFromTurns(deps.chatPanel, turns, replayOptions);
|
|
162
|
-
const replayMessages =
|
|
163
|
+
const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, replayOptions);
|
|
163
164
|
deps.chat.resetForSession(leafTurnId, replayMessages);
|
|
164
165
|
rescopeToBranch(session, turns, leafTurnId);
|
|
165
166
|
} catch (error) {
|
|
@@ -209,7 +210,7 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
209
210
|
const turns = deps.readStructuredEntries(sessionId);
|
|
210
211
|
deps.resetTranscript();
|
|
211
212
|
rehydrateChatPanelFromTurns(deps.chatPanel, turns, { uptoTurnId: turnId });
|
|
212
|
-
const replayMessages =
|
|
213
|
+
const replayMessages = buildModelReplayAgentMessagesFromTurns(turns, { uptoTurnId: turnId });
|
|
213
214
|
deps.chat.resetForSession(turnId, replayMessages);
|
|
214
215
|
// The same branch the transcript above was just scoped to. Without the
|
|
215
216
|
// leaf, /cost, the footer Σ, and the last-turn line kept reporting the
|
|
@@ -287,8 +288,11 @@ export function createOverlaySessionLifecycle(deps: OverlaySessionLifecycleDeps)
|
|
|
287
288
|
try {
|
|
288
289
|
const turns = deps.readStructuredEntries(forkedSessionId);
|
|
289
290
|
rehydrateChatPanelFromTurns(deps.chatPanel, turns);
|
|
290
|
-
const replayMessages = buildReplayAgentMessagesFromTurns(turns);
|
|
291
291
|
const leafTurnId = session.tree(forkedSessionId).leafId ?? parentTurnId;
|
|
292
|
+
const replayMessages = buildModelReplayAgentMessagesFromTurns(
|
|
293
|
+
turns,
|
|
294
|
+
leafTurnId ? { activeLeafTurnId: leafTurnId } : {},
|
|
295
|
+
);
|
|
292
296
|
deps.chat.resetForSession(leafTurnId, replayMessages);
|
|
293
297
|
rescopeToBranch(session, turns, leafTurnId);
|
|
294
298
|
} catch (error) {
|
|
@@ -16,6 +16,7 @@ import { sanitizeCallTargetText } from "../../domains/safety/call-target.js";
|
|
|
16
16
|
import { formatSize } from "../../engine/truncate.js";
|
|
17
17
|
import { visibleWidth, wrapTextWithAnsi } from "../../engine/tui.js";
|
|
18
18
|
import { classifyResourceRead, toolPresentationPolicy } from "../../tools/presentation.js";
|
|
19
|
+
import { toolResultPresentationPolicy, toolResultPresentationText } from "../../tools/result-disposition.js";
|
|
19
20
|
import type { ApprovalRequestView } from "../permission-overlay.js";
|
|
20
21
|
import { clioTheme, formatCompactMs, GLYPH } from "../theme/index.js";
|
|
21
22
|
import { renderDiffLines } from "./diff.js";
|
|
@@ -81,6 +82,13 @@ export interface ToolExecutionFinished {
|
|
|
81
82
|
* model reading the same transcript, to guess at the rule.
|
|
82
83
|
*/
|
|
83
84
|
blockReason?: string | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Working-set eviction reason, when the projection has replaced this
|
|
87
|
+
* result's body for the model. The transcript still renders the full body:
|
|
88
|
+
* the ledger is what the operator scrolls, the projection is only what the
|
|
89
|
+
* next request carries. The tag says the two now differ here.
|
|
90
|
+
*/
|
|
91
|
+
evictedReason?: string | undefined;
|
|
84
92
|
/** Structured exit status when the caller has one; text parsing is legacy fallback only. */
|
|
85
93
|
exitCode?: number | string | null | undefined;
|
|
86
94
|
/** Local `!!` bash output is visible to the operator but excluded from model context. */
|
|
@@ -456,6 +464,7 @@ function ledgerTail(finished: ToolExecutionFinished): { facts: string; offload:
|
|
|
456
464
|
if (details?.outputCapped === true) parts.push("output capped");
|
|
457
465
|
}
|
|
458
466
|
if (finished.excludeFromContext === true) parts.push("excluded from context");
|
|
467
|
+
if (finished.evictedReason !== undefined) parts.push("evicted", finished.evictedReason);
|
|
459
468
|
const offloadPath = executed ? offloadPathOf(finished) : null;
|
|
460
469
|
return {
|
|
461
470
|
facts: parts.length > 0 ? dim(` · ${parts.join(" · ")}`) : "",
|
|
@@ -473,7 +482,9 @@ const FAILURE_EXCERPT_LIMIT = 80;
|
|
|
473
482
|
* its failures carry one; the renderer never names a tool here.
|
|
474
483
|
*/
|
|
475
484
|
function failureExcerpt(finished: ToolExecutionFinished, width: number): string {
|
|
476
|
-
|
|
485
|
+
const presentation =
|
|
486
|
+
toolResultPresentationPolicy(finished.result) ?? toolPresentationPolicy(finished.toolName, finished.args);
|
|
487
|
+
if (!finished.isError || !presentation.failureExcerpt) return "";
|
|
477
488
|
if (isNonExecutedOutcome(finished.outcome)) return "";
|
|
478
489
|
const text = unwrapResultEnvelope(finished.result);
|
|
479
490
|
if (typeof text !== "string") return "";
|
|
@@ -871,6 +882,8 @@ function renderArgsBody(toolName: string, args: unknown, width: number, isError:
|
|
|
871
882
|
*/
|
|
872
883
|
function unwrapResultEnvelope(result: unknown): unknown {
|
|
873
884
|
if (typeof result === "string" || result === null || result === undefined) return result;
|
|
885
|
+
const presentationText = toolResultPresentationText(result);
|
|
886
|
+
if (presentationText !== null) return presentationText;
|
|
874
887
|
const blocks = Array.isArray(result)
|
|
875
888
|
? result
|
|
876
889
|
: isPlainObject(result) && Array.isArray(result.content)
|
|
@@ -1041,6 +1054,7 @@ function outputFacts(finished: ToolExecutionFinished): string[] {
|
|
|
1041
1054
|
}
|
|
1042
1055
|
if (isPlainObject(finished.result) && finished.result.terminate === true) parts.push("terminal result");
|
|
1043
1056
|
if (finished.excludeFromContext === true) parts.push("excluded from context");
|
|
1057
|
+
if (finished.evictedReason !== undefined) parts.push("evicted", finished.evictedReason);
|
|
1044
1058
|
return parts;
|
|
1045
1059
|
}
|
|
1046
1060
|
|
|
@@ -1174,7 +1188,9 @@ export interface ToolSublineRenderOptions {
|
|
|
1174
1188
|
function foldedDiffRows(finished: ToolExecutionFinished, width: number, opts: ToolSublineRenderOptions): string[] {
|
|
1175
1189
|
if (opts.foldedExtras === "none") return [];
|
|
1176
1190
|
if (finished.isError || isNonExecutedOutcome(finished.outcome)) return [];
|
|
1177
|
-
|
|
1191
|
+
const presentation =
|
|
1192
|
+
toolResultPresentationPolicy(finished.result) ?? toolPresentationPolicy(finished.toolName, finished.args);
|
|
1193
|
+
if (!presentation.showDiffWhenFolded) return [];
|
|
1178
1194
|
const diff = resultDiff(finished.result);
|
|
1179
1195
|
if (diff === null) return [];
|
|
1180
1196
|
return truncateRowsMiddle(
|
|
@@ -3,7 +3,7 @@ import { collectSessionEntries } from "../domains/session/compaction/session-ent
|
|
|
3
3
|
import type { SessionContract, SessionEntry } from "../domains/session/index.js";
|
|
4
4
|
import { openSession, sessionPaths } from "../engine/session.js";
|
|
5
5
|
import type { ChatLoop } from "./chat-loop.js";
|
|
6
|
-
import {
|
|
6
|
+
import { buildModelReplayAgentMessagesFromTurns } from "./model-session-replay.js";
|
|
7
7
|
|
|
8
8
|
type SessionOwner = Pick<SessionContract, "create" | "current">;
|
|
9
9
|
type SessionChat = Pick<ChatLoop, "resetForSession">;
|
|
@@ -87,7 +87,7 @@ export function createSessionTranscript(deps: SessionTranscriptDeps): SessionTra
|
|
|
87
87
|
const turns = deps.readSessionEntries();
|
|
88
88
|
deps.chat.resetForSession(
|
|
89
89
|
leafTurnId,
|
|
90
|
-
|
|
90
|
+
buildModelReplayAgentMessagesFromTurns(turns, {
|
|
91
91
|
...(leafTurnId ? { activeLeafTurnId: leafTurnId } : {}),
|
|
92
92
|
}),
|
|
93
93
|
);
|
|
@@ -44,6 +44,8 @@ type SlashCommandVariant =
|
|
|
44
44
|
| { kind: "init"; options: InitCommandOptions }
|
|
45
45
|
| { kind: "context-clear"; options: ContextClearCommandOptions }
|
|
46
46
|
| { kind: "context-refresh" }
|
|
47
|
+
/** `ref` is the turnId an `[evicted ...]` marker names. */
|
|
48
|
+
| { kind: "context-recall"; ref: string }
|
|
47
49
|
| { kind: "skill-selector" }
|
|
48
50
|
| { kind: "skill-invocation"; text: string }
|
|
49
51
|
| { kind: "prompts" }
|
|
@@ -343,6 +345,13 @@ export interface SlashCommandContext {
|
|
|
343
345
|
* Optional until the host wires onContextRefresh.
|
|
344
346
|
*/
|
|
345
347
|
runContextRefresh?: () => void;
|
|
348
|
+
/**
|
|
349
|
+
* Read an evicted tool-result body back into the transcript by ref, and
|
|
350
|
+
* record the `contextRecall` entry. Transcript-only: an operator recall
|
|
351
|
+
* answers the person, so the body never becomes model context. Optional
|
|
352
|
+
* until the host wires a session.
|
|
353
|
+
*/
|
|
354
|
+
runContextRecall?: (ref: string) => void;
|
|
346
355
|
openSkillsHub?: () => void;
|
|
347
356
|
listPrompts: () => ResourceList<PromptTemplate>;
|
|
348
357
|
/**
|
|
@@ -481,6 +490,13 @@ const COMPACT_POSITIONALS: ReadonlyArray<CommandPositionalSpec> = [
|
|
|
481
490
|
{ name: "instructions", required: false, rest: true },
|
|
482
491
|
];
|
|
483
492
|
|
|
493
|
+
/**
|
|
494
|
+
* `/context recall <ref>` takes exactly one ref, not a rest tail: a ref is a
|
|
495
|
+
* single turnId, and a second token is a mistake worth reporting rather than
|
|
496
|
+
* text to fold into the first.
|
|
497
|
+
*/
|
|
498
|
+
const RECALL_POSITIONALS: ReadonlyArray<CommandPositionalSpec> = [{ name: "ref", required: true }];
|
|
499
|
+
|
|
484
500
|
/**
|
|
485
501
|
* A registered command whose arguments do not parse is a mistake to report,
|
|
486
502
|
* not a chat message to send. Falling through to `unknown` submitted the raw
|
|
@@ -859,11 +875,12 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
859
875
|
},
|
|
860
876
|
{
|
|
861
877
|
name: "context",
|
|
862
|
-
description: "Context hub: window overlay plus compact, init, refresh, and reset",
|
|
878
|
+
description: "Context hub: window overlay plus compact, recall, init, refresh, and reset",
|
|
863
879
|
group: "Inspect",
|
|
864
|
-
kinds: ["context-view", "compact", "init", "context-clear", "context-refresh"],
|
|
880
|
+
kinds: ["context-view", "compact", "context-recall", "init", "context-clear", "context-refresh"],
|
|
865
881
|
subcommandDescriptions: {
|
|
866
882
|
compact: "Compact session context",
|
|
883
|
+
recall: "Recall an evicted result",
|
|
867
884
|
init: "Initialize project context",
|
|
868
885
|
refresh: "Refresh project context",
|
|
869
886
|
reset: "Reset project context",
|
|
@@ -871,6 +888,7 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
871
888
|
args: {
|
|
872
889
|
subcommands: {
|
|
873
890
|
compact: { positionals: [...COMPACT_POSITIONALS] },
|
|
891
|
+
recall: { positionals: [...RECALL_POSITIONALS] },
|
|
874
892
|
init: {},
|
|
875
893
|
refresh: {},
|
|
876
894
|
reset: {},
|
|
@@ -881,6 +899,8 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
881
899
|
switch (parsed.subcommand) {
|
|
882
900
|
case "compact":
|
|
883
901
|
return { kind: "compact", instructions: parsed.rest };
|
|
902
|
+
case "recall":
|
|
903
|
+
return { kind: "context-recall", ref: parsed.positionals[0] ?? "" };
|
|
884
904
|
case "init":
|
|
885
905
|
return { kind: "init", options: {} };
|
|
886
906
|
case "refresh":
|
|
@@ -899,6 +919,13 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
|
|
|
899
919
|
case "compact":
|
|
900
920
|
ctx.runCompact(command.instructions);
|
|
901
921
|
return;
|
|
922
|
+
case "context-recall":
|
|
923
|
+
if (ctx.runContextRecall) {
|
|
924
|
+
ctx.runContextRecall(command.ref);
|
|
925
|
+
} else {
|
|
926
|
+
ctx.notice("error", "context recall is not wired; no session is bound to this process");
|
|
927
|
+
}
|
|
928
|
+
return;
|
|
902
929
|
case "init":
|
|
903
930
|
ctx.runInit(command.options);
|
|
904
931
|
return;
|