@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.
Files changed (244) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +3 -3
  4. package/dist/{acp-P2AQILE2.js → acp-S5R4RR5B.js} +7 -6
  5. package/dist/{agents-72W3BI7I.js → agents-P6DMMVZY.js} +24 -21
  6. package/dist/assets/codewiki.json +1 -1
  7. package/dist/{auth-5TWEIYDN.js → auth-2XCZLPKS.js} +12 -8
  8. package/dist/{chunk-GGXXDWE4.js → chunk-22NAGB7X.js} +2 -2
  9. package/dist/{chunk-2DJ2KNFG.js → chunk-2LZI5CAG.js} +133 -13
  10. package/dist/{chunk-OAO4GE4M.js → chunk-2TZWSW76.js} +2 -2
  11. package/dist/{chunk-OOJYHWRB.js → chunk-34475P3I.js} +2 -2
  12. package/dist/{chunk-4XUGQOHA.js → chunk-35MKKU5R.js} +4 -4
  13. package/dist/{chunk-LM5TQCJZ.js → chunk-3HZ5RWN2.js} +5 -5
  14. package/dist/{chunk-AGYYIBLL.js → chunk-3JLKSKD7.js} +2 -2
  15. package/dist/{chunk-KZWTDYJF.js → chunk-4JUF2NNX.js} +7 -7
  16. package/dist/{chunk-ZDOOVTXZ.js → chunk-4OC57DA6.js} +27 -4
  17. package/dist/chunk-5M54SPOL.js +926 -0
  18. package/dist/{chunk-STBPMHSX.js → chunk-7RXG6QRZ.js} +51 -11
  19. package/dist/{chunk-X6IAEBZR.js → chunk-A2GZF7DC.js} +5 -5
  20. package/dist/{chunk-A3CYT5EX.js → chunk-AD2SYQYC.js} +55 -2
  21. package/dist/chunk-AOCYTWAV.js +449 -0
  22. package/dist/chunk-BEY543CS.js +258 -0
  23. package/dist/{chunk-6N5PTWMY.js → chunk-BP4OYD6A.js} +32 -13
  24. package/dist/chunk-BPGS2WCQ.js +612 -0
  25. package/dist/{chunk-V6RTAOC2.js → chunk-BRXQQJFP.js} +8 -8
  26. package/dist/chunk-CFGTUFWB.js +67 -0
  27. package/dist/{chunk-CBCAPZAA.js → chunk-E25LMLRW.js} +2 -2
  28. package/dist/{chunk-DUYJ5IO6.js → chunk-EDRHSCIE.js} +4 -4
  29. package/dist/{chunk-XBXAASKX.js → chunk-EFADSJET.js} +2 -2
  30. package/dist/{chunk-M6SHUN7Q.js → chunk-FO5ZOVUY.js} +2 -2
  31. package/dist/chunk-FYYLNIL5.js +313 -0
  32. package/dist/{chunk-FNTMWMX5.js → chunk-HV5X7OR2.js} +14 -12
  33. package/dist/{chunk-TZTZS7QK.js → chunk-HXG4IURW.js} +5 -3
  34. package/dist/{chunk-5UFT4SUX.js → chunk-K6WL7QZT.js} +3 -3
  35. package/dist/chunk-K7VKOLQQ.js +15 -0
  36. package/dist/{chunk-BMEMKKIT.js → chunk-KOHPCX4K.js} +2 -2
  37. package/dist/{chunk-OC7FIQPC.js → chunk-KRPY7NTG.js} +10 -7
  38. package/dist/chunk-LL4KHSZI.js +22 -0
  39. package/dist/{chunk-DSELYM6W.js → chunk-MEQ45TQ4.js} +15 -9
  40. package/dist/{chunk-5UUP6MWO.js → chunk-MV3K5QF2.js} +5 -436
  41. package/dist/{chunk-6SGHMWE3.js → chunk-N4CZJQRK.js} +5 -5
  42. package/dist/{chunk-TZK7PACC.js → chunk-NILBFAPG.js} +14 -8
  43. package/dist/chunk-OZNBF4L3.js +23 -0
  44. package/dist/{verify-375KUB3Y.js → chunk-PCZJO5TI.js} +127 -42
  45. package/dist/chunk-QQK64KLB.js +1360 -0
  46. package/dist/{chunk-SRDMMSEP.js → chunk-QQL5RT5M.js} +979 -1619
  47. package/dist/{chunk-LZSJBIVT.js → chunk-QWU7ZBO7.js} +70 -720
  48. package/dist/{chunk-2TLUCQVG.js → chunk-RD5U66HV.js} +3 -3
  49. package/dist/{chunk-OKGUZO2U.js → chunk-SPULKLCF.js} +4 -3
  50. package/dist/{chunk-OQ33BKR3.js → chunk-TTNYS3EA.js} +3 -60
  51. package/dist/chunk-TW3WDMVS.js +677 -0
  52. package/dist/chunk-TZSKNMZG.js +434 -0
  53. package/dist/{chunk-7MNJORFF.js → chunk-UL3WSD3F.js} +6 -1
  54. package/dist/{chunk-PIWWS5BL.js → chunk-UZHIZC5S.js} +7 -7
  55. package/dist/{chunk-UFIIWP2H.js → chunk-VAWWTKDP.js} +8 -8
  56. package/dist/{chunk-COU2UHX6.js → chunk-VEZEGCGW.js} +170 -2
  57. package/dist/{chunk-LW6DSM3M.js → chunk-VMNQ6OZA.js} +98 -202
  58. package/dist/chunk-VSNATDE6.js +122 -0
  59. package/dist/chunk-W6GROXXM.js +69 -0
  60. package/dist/chunk-WPQLXFOZ.js +375 -0
  61. package/dist/{chunk-ORBHGJC5.js → chunk-WR67VIZY.js} +3 -3
  62. package/dist/{chunk-PAJK6MAQ.js → chunk-X6COSD2O.js} +5 -5
  63. package/dist/chunk-ZGVHUX3M.js +66 -0
  64. package/dist/{chunk-LWLEKMDQ.js → chunk-ZYKPLLNQ.js} +510 -547
  65. package/dist/cli/index.js +27 -23
  66. package/dist/{clio-JOU4FXVA.js → clio-J5JIOIDS.js} +7 -6
  67. package/dist/{code-nav-7AX6FYE6.js → code-nav-AXCXSBHX.js} +5 -3
  68. package/dist/{config-XCDVKR23.js → config-OEBMIN2U.js} +37 -27
  69. package/dist/{configure-4GAP54ZW.js → configure-PUQOSIXQ.js} +16 -13
  70. package/dist/{context-5VKGUVJJ.js → context-EKDCKUUZ.js} +82 -7
  71. package/dist/{context-4UOGGLQ5.js → context-MGSE4Z2T.js} +33 -23
  72. package/dist/{context-77FM5DV5.js → context-URSXPBCK.js} +17 -9
  73. package/dist/{context-clear-XXJRLCJJ.js → context-clear-KDAJRNUK.js} +33 -23
  74. package/dist/context-working-set-SBKMPPI2.js +1552 -0
  75. package/dist/{dispatch-runner-QPRDDBDX.js → dispatch-runner-MSWN72NK.js} +43 -29
  76. package/dist/{doctor-HR46URBJ.js → doctor-7BSE27PJ.js} +10 -10
  77. package/dist/{eval-XSSNATB4.js → eval-IZGDOO4H.js} +9 -8
  78. package/dist/{evidence-6HG2PY2B.js → evidence-SR7WXB5B.js} +51 -23
  79. package/dist/{evolve-K7YU3NCY.js → evolve-K7VE2CBX.js} +30 -20
  80. package/dist/{fleet-VY3HHKN6.js → fleet-7XMJNQNF.js} +48 -38
  81. package/dist/{fleet-preflight-DDN536IT.js → fleet-preflight-AQNAH644.js} +3 -3
  82. package/dist/{init-JYGXI3FK.js → init-JGNPAYXT.js} +41 -31
  83. package/dist/{memory-WFZMGYHX.js → memory-4ALKDJ4Q.js} +32 -22
  84. package/dist/{models-I5QWSEOM.js → models-ZMMLFJNN.js} +22 -19
  85. package/dist/{monitor-GE4ID3IA.js → monitor-2F3T5KHP.js} +55 -43
  86. package/dist/{orchestrator-EM5MC3HM.js → orchestrator-ORHT43JB.js} +572 -388
  87. package/dist/{reset-L2FQEE3E.js → reset-NXGTYNUO.js} +4 -3
  88. package/dist/{run-ZU3QMZPZ.js → run-RF4WJGMT.js} +51 -41
  89. package/dist/{share-S5BZQC5I.js → share-UT3W6E4M.js} +5 -4
  90. package/dist/{skills-X5VXCRNQ.js → skills-PSACKC5Q.js} +2 -2
  91. package/dist/{skills-eval-WKIHWTHR.js → skills-eval-WJSI55RZ.js} +34 -24
  92. package/dist/{targets-SNCPI2NR.js → targets-PIIRAOYS.js} +23 -20
  93. package/dist/{terminal-lease-BNAHVHBS.js → terminal-lease-ULWXWNVY.js} +4 -3
  94. package/dist/{upgrade-JQHHPQ4K.js → upgrade-346TZ6AV.js} +18 -17
  95. package/dist/{usage-OR4O5SMZ.js → usage-6KKXR32N.js} +34 -24
  96. package/dist/verifiers-4UUM6TEE.js +1214 -0
  97. package/dist/verify-X5HDROLA.js +25 -0
  98. package/dist/{wiki-generate-UEXP2ARI.js → wiki-generate-7STOCIFZ.js} +42 -31
  99. package/dist/worker/entry.js +33 -24
  100. package/docs/README.md +7 -6
  101. package/docs/acp.md +1 -1
  102. package/docs/alcf-provider.md +1 -1
  103. package/docs/architecture.md +2 -2
  104. package/docs/artifact-versions.md +1 -1
  105. package/docs/built-in-agents.md +1 -1
  106. package/docs/capacity-and-scheduling.md +1 -1
  107. package/docs/commands-and-modes.md +53 -21
  108. package/docs/config-knobs-audit.md +1 -2
  109. package/docs/configuration-and-targets.md +15 -1
  110. package/docs/context-engine.md +64 -12
  111. package/docs/context-working-set.md +194 -0
  112. package/docs/development-pipeline.md +1 -1
  113. package/docs/documentation-coverage.md +5 -5
  114. package/docs/documentation-guide.md +6 -5
  115. package/docs/environment-variables.md +2 -1
  116. package/docs/eval-runner.md +1 -1
  117. package/docs/evals-internal.md +14 -1
  118. package/docs/evidence-and-memory.md +74 -2
  119. package/docs/evolution.md +1 -1
  120. package/docs/exit-codes-and-output.md +1 -1
  121. package/docs/extensions-and-sharing.md +2 -2
  122. package/docs/fleet-dispatch.md +22 -7
  123. package/docs/glossary.md +21 -1
  124. package/docs/installation-and-lifecycle.md +2 -2
  125. package/docs/middleware-and-components.md +1 -1
  126. package/docs/model-catalog.md +7 -9
  127. package/docs/observability.md +4 -4
  128. package/docs/proactive-memory.md +1 -1
  129. package/docs/prompt-envelope-and-tools.md +4 -4
  130. package/docs/provider-adapter-cookbook.md +1 -1
  131. package/docs/release-cut-checklist.md +31 -31
  132. package/docs/safety-model.md +23 -4
  133. package/docs/scientific-validation.md +21 -3
  134. package/docs/session-lifecycle.md +3 -3
  135. package/docs/skills-marketplace.md +1 -1
  136. package/docs/tool-usage.md +79 -12
  137. package/docs/trace-store.md +1 -1
  138. package/docs/troubleshooting.md +1 -1
  139. package/docs/tui-design.md +1 -1
  140. package/docs/worker-dispatch-mechanics.md +11 -1
  141. package/package.json +8 -11
  142. package/skills/meta/clio-test/SKILL.md +20 -17
  143. package/skills/meta/clio-test/evals.md +3 -3
  144. package/skills/meta/clio-test/references/harness.md +35 -6
  145. package/skills/meta/clio-test/references/test-map.md +20 -10
  146. package/skills/registry.yaml +2 -2
  147. package/skills/skill-marketplace.json +1 -1
  148. package/src/cli/context-working-set.ts +513 -0
  149. package/src/cli/context.ts +8 -0
  150. package/src/cli/evidence.ts +20 -2
  151. package/src/cli/index.ts +4 -0
  152. package/src/cli/verifiers.ts +325 -0
  153. package/src/core/bash-exec.ts +39 -14
  154. package/src/core/bus-events.ts +19 -4
  155. package/src/core/config.ts +54 -0
  156. package/src/core/defaults.ts +50 -3
  157. package/src/core/verification-scripts.ts +6 -0
  158. package/src/domains/agents/builtins/verifier.md +3 -0
  159. package/src/domains/config/classify.ts +1 -0
  160. package/src/domains/context/working-set/contract.ts +161 -0
  161. package/src/domains/context/working-set/defaults.ts +28 -0
  162. package/src/domains/context/working-set/engine.ts +203 -0
  163. package/src/domains/context/working-set/fold.ts +62 -0
  164. package/src/domains/context/working-set/horizon.ts +38 -0
  165. package/src/domains/context/working-set/marker.ts +103 -0
  166. package/src/domains/context/working-set/path-index.ts +436 -0
  167. package/src/domains/context/working-set/payload.ts +152 -0
  168. package/src/domains/context/working-set/policies/age-horizon.ts +55 -0
  169. package/src/domains/context/working-set/policies/index.ts +21 -0
  170. package/src/domains/context/working-set/policies/structural.ts +160 -0
  171. package/src/domains/context/working-set/project.ts +132 -0
  172. package/src/domains/context/working-set/protect.ts +109 -0
  173. package/src/domains/context/working-set/recall.ts +177 -0
  174. package/src/domains/context/working-set/replay/controls.ts +112 -0
  175. package/src/domains/context/working-set/replay/load-clio.ts +199 -0
  176. package/src/domains/context/working-set/replay/metrics.ts +185 -0
  177. package/src/domains/context/working-set/replay/reference-graph.ts +79 -0
  178. package/src/domains/context/working-set/replay/report.ts +139 -0
  179. package/src/domains/context/working-set/replay/runner.ts +325 -0
  180. package/src/domains/context/working-set/replay/synthetic.ts +422 -0
  181. package/src/domains/context/working-set/replay/trace.ts +21 -0
  182. package/src/domains/context/working-set/visible.ts +54 -0
  183. package/src/domains/evidence/build.ts +112 -45
  184. package/src/domains/evidence/eval.ts +24 -7
  185. package/src/domains/evidence/index.ts +53 -0
  186. package/src/domains/evidence/ordering.ts +12 -0
  187. package/src/domains/evidence/run-trust.ts +221 -0
  188. package/src/domains/evidence/store.ts +46 -6
  189. package/src/domains/evidence/trust-status.ts +854 -0
  190. package/src/domains/evidence/types.ts +26 -0
  191. package/src/domains/middleware/memory-intervention.ts +3 -0
  192. package/src/domains/middleware/stalled-turn.ts +165 -4
  193. package/src/domains/safety/autonomy.ts +1 -1
  194. package/src/domains/safety/default-path-policy.ts +8 -0
  195. package/src/domains/safety/finish-contract.ts +4 -3
  196. package/src/domains/safety/policy-engine.ts +48 -6
  197. package/src/domains/session/compaction/compact.ts +23 -1
  198. package/src/domains/session/compaction/cut-point.ts +2 -0
  199. package/src/domains/session/compaction/tokens.ts +16 -1
  200. package/src/domains/session/context-ledger.ts +2 -0
  201. package/src/domains/session/entries.ts +107 -1
  202. package/src/domains/session/manager.ts +9 -2
  203. package/src/domains/session/migrations/index.ts +22 -3
  204. package/src/engine/acp/server.ts +3 -0
  205. package/src/engine/agent.ts +18 -1
  206. package/src/engine/session.ts +9 -3
  207. package/src/entry/orchestrator.ts +16 -4
  208. package/src/interactive/chat-loop-messages.ts +18 -6
  209. package/src/interactive/chat-panel.ts +17 -1
  210. package/src/interactive/chat-renderer.ts +30 -21
  211. package/src/interactive/context-meter.ts +10 -0
  212. package/src/interactive/context-overlay.ts +81 -6
  213. package/src/interactive/context-recall-command.ts +110 -0
  214. package/src/interactive/interactive-slash-runtime.ts +37 -1
  215. package/src/interactive/model-session-replay.ts +21 -0
  216. package/src/interactive/overlay-general-openers.ts +6 -0
  217. package/src/interactive/overlay-session-lifecycle.ts +8 -4
  218. package/src/interactive/renderers/tool-execution.ts +18 -2
  219. package/src/interactive/session-transcript.ts +2 -2
  220. package/src/interactive/slash-commands.ts +29 -2
  221. package/src/interactive/turn-context.ts +238 -88
  222. package/src/interactive/turn-middleware.ts +6 -6
  223. package/src/tools/agent-tools.ts +11 -4
  224. package/src/tools/bash.ts +144 -82
  225. package/src/tools/builtin-tool-catalog.ts +11 -5
  226. package/src/tools/context/index.ts +105 -3
  227. package/src/tools/context/surface.ts +3 -2
  228. package/src/tools/core-bootstrap.ts +21 -0
  229. package/src/tools/dispatch-runner.ts +9 -7
  230. package/src/tools/monitor.ts +28 -20
  231. package/src/tools/registry.ts +59 -7
  232. package/src/tools/result-disposition.ts +550 -0
  233. package/src/tools/result-shaping.ts +262 -19
  234. package/src/tools/safe-exec.ts +2 -0
  235. package/src/tools/verify/authoring.ts +1119 -0
  236. package/src/tools/verify/catalog.ts +346 -0
  237. package/src/tools/verify/index.ts +13 -3
  238. package/src/tools/verify/scripts.ts +135 -37
  239. package/src/tools/verify/surface.ts +9 -5
  240. package/src/tools/worker-evidence.ts +35 -12
  241. package/dist/chunk-J7CWMCQD.js +0 -255
  242. package/dist/chunk-T6YILFSB.js +0 -80
  243. package/dist/chunk-VAKQQHWR.js +0 -434
  244. package/dist/chunk-VPAYEGVX.js +0 -184
@@ -0,0 +1,513 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
+ import { assertSafeId } from "../core/safe-id.js";
5
+ import { clioStatePath } from "../core/xdg.js";
6
+ import { DEFAULT_WORKING_SET_SETTINGS } from "../domains/context/working-set/defaults.js";
7
+ import { foldWorkingSet } from "../domains/context/working-set/fold.js";
8
+ import { buildPathIndex } from "../domains/context/working-set/path-index.js";
9
+ import { resolveWorkingSetPolicy } from "../domains/context/working-set/policies/index.js";
10
+ import { makeOraclePolicy, makeRandomPolicy, nonePolicy } from "../domains/context/working-set/replay/controls.js";
11
+ import { loadClioTraces, type ReplayLoadCascade } from "../domains/context/working-set/replay/load-clio.js";
12
+ import { aggregateReplayMetrics, type ReplayMeasurement } from "../domains/context/working-set/replay/metrics.js";
13
+ import { buildReferenceGraph, type ReferenceGraph } from "../domains/context/working-set/replay/reference-graph.js";
14
+ import {
15
+ type ReplayPolicyResult,
16
+ type ReplayReportInput,
17
+ renderReplayJson,
18
+ renderReplayMarkdown,
19
+ } from "../domains/context/working-set/replay/report.js";
20
+ import { replayTrace } from "../domains/context/working-set/replay/runner.js";
21
+ import { generateSyntheticCorpora, SYNTHETIC_CORPORA } from "../domains/context/working-set/replay/synthetic.js";
22
+ import type { Trace } from "../domains/context/working-set/replay/trace.js";
23
+ import { parseSessionEntries } from "../domains/session/archive-readers.js";
24
+ import { DEFAULT_KEEP_RECENT_TOKENS } from "../domains/session/compaction/defaults.js";
25
+ import { filterEntriesToActivePath } from "../domains/session/tree/active-path.js";
26
+
27
+ const REPLAY_HELP = `Usage:
28
+ clio-coder context replay (--sessions <path>... | --synthetic <corpus>...) [options]
29
+
30
+ Options:
31
+ --sessions <path>... Clio session directories, sessions roots, or ledger JSONL files
32
+ --synthetic <ids> comma-separated procedural corpora: ${SYNTHETIC_CORPORA.map((spec) => spec.id).join(",")}
33
+ --policies <ids> comma-separated none,random,age-horizon,structural-v1,oracle
34
+ --budgets <tokens> comma-separated budgets (default: 16000,32000,64000)
35
+ --threshold <ratio> pressure threshold (default: 0.8)
36
+ --target <ratio> post-eviction pressure target (default: 0.6)
37
+ --protect-last-turns <n>
38
+ protected recent turns (default: ${DEFAULT_WORKING_SET_SETTINGS.protectLastTurns})
39
+ --min-evictable-tokens <n>
40
+ minimum tool-result body tokens (default: ${DEFAULT_WORKING_SET_SETTINGS.minEvictableTokens})
41
+ --seed <integer> deterministic random-policy seed (default: 0)
42
+ --no-filter include every readable transcript
43
+ --json <path> write the stable JSON report
44
+ --md <path> write Markdown instead of printing it
45
+ `;
46
+
47
+ const WORKING_SET_HELP = `Usage:
48
+ clio-coder context working-set --session <id|path>
49
+
50
+ Print the durable working-set fold and path-index summary for one Clio session.
51
+ `;
52
+
53
+ /**
54
+ * Conservative stand-in for replay's summary paraphrase. Five recorded local
55
+ * compactions price at 405, 405, 968, 981, and 1,124 tokens; 1,500 sits above
56
+ * that observed range. The replay README records the sample and sensitivity.
57
+ */
58
+ const REPLAY_SUMMARY_TOKENS = 1_500;
59
+
60
+ const POLICY_IDS = ["none", "random", "age-horizon", "structural-v1", "oracle"] as const;
61
+ type ReplayPolicyId = (typeof POLICY_IDS)[number];
62
+
63
+ class CliUsageError extends Error {}
64
+
65
+ interface ReplayArgs {
66
+ sessions: string[];
67
+ policies: ReplayPolicyId[];
68
+ budgets: number[];
69
+ threshold: number;
70
+ target: number;
71
+ protectLastTurns: number;
72
+ minEvictableTokens: number;
73
+ seed: number;
74
+ synthetic: string[];
75
+ noFilter: boolean;
76
+ jsonPath?: string;
77
+ markdownPath?: string;
78
+ }
79
+
80
+ function commaValues(value: string, flag: string): string[] {
81
+ const values = value
82
+ .split(",")
83
+ .map((entry) => entry.trim())
84
+ .filter((entry) => entry.length > 0);
85
+ if (values.length === 0) throw new CliUsageError(`${flag} requires at least one value`);
86
+ return [...new Set(values)];
87
+ }
88
+
89
+ function numberValue(value: string, flag: string): number {
90
+ const parsed = Number(value);
91
+ if (!Number.isFinite(parsed)) throw new CliUsageError(`${flag} requires a finite number`);
92
+ return parsed;
93
+ }
94
+
95
+ function requiredValue(args: ReadonlyArray<string>, index: number, flag: string): string {
96
+ const value = args[index + 1];
97
+ if (value === undefined || value.startsWith("--")) throw new CliUsageError(`${flag} requires a value`);
98
+ return value;
99
+ }
100
+
101
+ function policyResolves(id: ReplayPolicyId): boolean {
102
+ if (id === "none" || id === "random" || id === "oracle") return true;
103
+ try {
104
+ resolveWorkingSetPolicy(id);
105
+ return true;
106
+ } catch {
107
+ return false;
108
+ }
109
+ }
110
+
111
+ function defaultPolicies(): ReplayPolicyId[] {
112
+ return POLICY_IDS.filter(policyResolves);
113
+ }
114
+
115
+ function parseReplayArgs(args: ReadonlyArray<string>): ReplayArgs {
116
+ const parsed: ReplayArgs = {
117
+ sessions: [],
118
+ policies: defaultPolicies(),
119
+ budgets: [16_000, 32_000, 64_000],
120
+ threshold: 0.8,
121
+ target: 0.6,
122
+ protectLastTurns: DEFAULT_WORKING_SET_SETTINGS.protectLastTurns,
123
+ minEvictableTokens: DEFAULT_WORKING_SET_SETTINGS.minEvictableTokens,
124
+ seed: 0,
125
+ synthetic: [],
126
+ noFilter: false,
127
+ };
128
+ let policiesExplicit = false;
129
+ for (let index = 0; index < args.length; index += 1) {
130
+ const arg = args[index];
131
+ if (arg === "--no-filter") {
132
+ parsed.noFilter = true;
133
+ continue;
134
+ }
135
+ if (arg === "--sessions") {
136
+ let consumed = 0;
137
+ while (index + 1 < args.length && !args[index + 1]?.startsWith("--")) {
138
+ parsed.sessions.push(args[index + 1] as string);
139
+ index += 1;
140
+ consumed += 1;
141
+ }
142
+ if (consumed === 0) throw new CliUsageError("--sessions requires at least one path");
143
+ continue;
144
+ }
145
+ if (
146
+ arg === "--policies" ||
147
+ arg === "--budgets" ||
148
+ arg === "--synthetic" ||
149
+ arg === "--threshold" ||
150
+ arg === "--target" ||
151
+ arg === "--protect-last-turns" ||
152
+ arg === "--min-evictable-tokens" ||
153
+ arg === "--seed"
154
+ ) {
155
+ const value = requiredValue(args, index, arg);
156
+ index += 1;
157
+ if (arg === "--policies") {
158
+ const values = commaValues(value, arg);
159
+ const unknown = values.filter((entry) => !(POLICY_IDS as ReadonlyArray<string>).includes(entry));
160
+ if (unknown.length > 0) throw new CliUsageError(`unknown replay policy: ${unknown.join(", ")}`);
161
+ parsed.policies = values as ReplayPolicyId[];
162
+ policiesExplicit = true;
163
+ } else if (arg === "--budgets") {
164
+ parsed.budgets = commaValues(value, arg).map((entry) => {
165
+ const budget = numberValue(entry, arg);
166
+ if (!Number.isInteger(budget) || budget <= 0) {
167
+ throw new CliUsageError("--budgets values must be positive integers");
168
+ }
169
+ return budget;
170
+ });
171
+ } else if (arg === "--synthetic") {
172
+ const values = commaValues(value, arg);
173
+ const known = SYNTHETIC_CORPORA.map((spec) => spec.id);
174
+ const unknown = values.filter((entry) => !known.includes(entry));
175
+ if (unknown.length > 0) throw new CliUsageError(`unknown synthetic corpus: ${unknown.join(", ")}`);
176
+ parsed.synthetic = values;
177
+ } else if (arg === "--threshold") {
178
+ parsed.threshold = numberValue(value, arg);
179
+ if (parsed.threshold <= 0 || parsed.threshold > 1) {
180
+ throw new CliUsageError("--threshold must be greater than 0 and at most 1");
181
+ }
182
+ } else if (arg === "--target") {
183
+ parsed.target = numberValue(value, arg);
184
+ if (parsed.target <= 0 || parsed.target >= 1) {
185
+ throw new CliUsageError("--target must be greater than 0 and less than 1");
186
+ }
187
+ } else if (arg === "--protect-last-turns") {
188
+ parsed.protectLastTurns = numberValue(value, arg);
189
+ if (!Number.isInteger(parsed.protectLastTurns) || parsed.protectLastTurns < 1) {
190
+ throw new CliUsageError("--protect-last-turns must be an integer at least 1");
191
+ }
192
+ } else if (arg === "--min-evictable-tokens") {
193
+ parsed.minEvictableTokens = numberValue(value, arg);
194
+ if (!Number.isInteger(parsed.minEvictableTokens) || parsed.minEvictableTokens < 0) {
195
+ throw new CliUsageError("--min-evictable-tokens must be a non-negative integer");
196
+ }
197
+ } else {
198
+ parsed.seed = numberValue(value, arg);
199
+ if (!Number.isInteger(parsed.seed)) throw new CliUsageError("--seed must be an integer");
200
+ }
201
+ continue;
202
+ }
203
+ if (arg === "--json" || arg === "--md") {
204
+ const value = requiredValue(args, index, arg);
205
+ index += 1;
206
+ if (arg === "--json") parsed.jsonPath = value;
207
+ else parsed.markdownPath = value;
208
+ continue;
209
+ }
210
+ throw new CliUsageError(`unknown flag ${arg}`);
211
+ }
212
+ if (parsed.sessions.length === 0 && parsed.synthetic.length === 0) {
213
+ throw new CliUsageError("--sessions or --synthetic is required");
214
+ }
215
+ if (parsed.target >= parsed.threshold) throw new CliUsageError("--target must be less than --threshold");
216
+ if (policiesExplicit) {
217
+ const unavailable = parsed.policies.filter((id) => !policyResolves(id));
218
+ if (unavailable.length > 0) {
219
+ throw new CliUsageError(`replay policy is not available in this build: ${unavailable.join(", ")}`);
220
+ }
221
+ }
222
+ return parsed;
223
+ }
224
+
225
+ function policyForTrace(id: ReplayPolicyId, graph: ReferenceGraph, seed: number) {
226
+ if (id === "none") return nonePolicy;
227
+ if (id === "random") return makeRandomPolicy(seed);
228
+ if (id === "oracle") return makeOraclePolicy(graph);
229
+ return resolveWorkingSetPolicy(id);
230
+ }
231
+
232
+ function gitSha(): string | null {
233
+ try {
234
+ return execFileSync("git", ["rev-parse", "HEAD"], {
235
+ cwd: process.cwd(),
236
+ encoding: "utf8",
237
+ stdio: ["ignore", "pipe", "ignore"],
238
+ }).trim();
239
+ } catch {
240
+ return null;
241
+ }
242
+ }
243
+
244
+ function exactCommandLine(): string[] {
245
+ return [process.execPath, ...process.execArgv, ...process.argv.slice(1)];
246
+ }
247
+
248
+ async function writeOutput(path: string, contents: string): Promise<string> {
249
+ const output = resolve(path);
250
+ await mkdir(dirname(output), { recursive: true });
251
+ await writeFile(output, contents, "utf8");
252
+ return output;
253
+ }
254
+
255
+ function cascadeLine(cascade: ReplayLoadCascade): string {
256
+ const filtered = Object.entries(cascade.filtered)
257
+ .map(([stage, count]) => `${stage}=${count}`)
258
+ .join(" ");
259
+ return `cascade found=${cascade.found} unreadable=${cascade.unreadable} ${filtered} kept=${cascade.kept}`;
260
+ }
261
+
262
+ export async function runContextReplayCommand(args: string[]): Promise<number> {
263
+ if (args.includes("--help") || args.includes("-h")) {
264
+ process.stdout.write(REPLAY_HELP);
265
+ return 0;
266
+ }
267
+ let parsed: ReplayArgs;
268
+ try {
269
+ parsed = parseReplayArgs(args);
270
+ } catch (error) {
271
+ process.stderr.write(
272
+ `clio-coder context replay: ${error instanceof Error ? error.message : String(error)}\n${REPLAY_HELP}`,
273
+ );
274
+ return 2;
275
+ }
276
+ try {
277
+ const ledgers = await loadClioTraces(parsed.sessions, { filter: parsed.noFilter ? false : {} });
278
+ const synthetic = generateSyntheticCorpora(parsed.synthetic);
279
+ const loaded = {
280
+ traces: [...ledgers.traces, ...synthetic.traces],
281
+ cascade: {
282
+ found: ledgers.cascade.found + synthetic.cascade.found,
283
+ unreadable: ledgers.cascade.unreadable,
284
+ filtered: ledgers.cascade.filtered,
285
+ kept: ledgers.cascade.kept + synthetic.cascade.kept,
286
+ },
287
+ };
288
+ if (loaded.traces.length === 0) {
289
+ process.stderr.write(`clio-coder context replay: no traces to replay (${cascadeLine(loaded.cascade)})\n`);
290
+ return 1;
291
+ }
292
+ const indexed = loaded.traces.map((trace) => {
293
+ const index = buildPathIndex(trace.entries, { cwd: trace.cwd });
294
+ return { trace, index, graph: buildReferenceGraph(trace, index) };
295
+ });
296
+ const settings = {
297
+ ...DEFAULT_WORKING_SET_SETTINGS,
298
+ target: parsed.target,
299
+ protectLastTurns: parsed.protectLastTurns,
300
+ minEvictableTokens: parsed.minEvictableTokens,
301
+ };
302
+ const results: ReplayPolicyResult[] = [];
303
+ for (const budgetTokens of parsed.budgets) {
304
+ for (const policyId of parsed.policies) {
305
+ const measurements: ReplayMeasurement[] = indexed.map(({ trace, index, graph }) => ({
306
+ trace,
307
+ index,
308
+ graph,
309
+ replay: replayTrace(trace, policyForTrace(policyId, graph, parsed.seed), {
310
+ policyId,
311
+ budgetTokens,
312
+ threshold: parsed.threshold,
313
+ target: parsed.target,
314
+ settings,
315
+ seed: parsed.seed,
316
+ summaries: { keepRecentTokens: DEFAULT_KEEP_RECENT_TOKENS, summaryTokens: REPLAY_SUMMARY_TOKENS },
317
+ }),
318
+ }));
319
+ results.push({ budgetTokens, policyId, metrics: aggregateReplayMetrics(measurements) });
320
+ }
321
+ }
322
+ const report: ReplayReportInput = {
323
+ config: {
324
+ policies: parsed.policies,
325
+ budgets: parsed.budgets,
326
+ threshold: parsed.threshold,
327
+ target: parsed.target,
328
+ seed: parsed.seed,
329
+ corpus: [...(parsed.sessions.length > 0 ? ["ledgers"] : []), ...parsed.synthetic],
330
+ filter: parsed.noFilter ? "none" : "default",
331
+ settings,
332
+ },
333
+ cascade: loaded.cascade,
334
+ results,
335
+ gitSha: gitSha(),
336
+ commandLine: exactCommandLine(),
337
+ };
338
+ const markdown = renderReplayMarkdown(report);
339
+ if (parsed.markdownPath === undefined) process.stdout.write(markdown);
340
+ else {
341
+ const path = await writeOutput(parsed.markdownPath, markdown);
342
+ process.stdout.write(`${cascadeLine(loaded.cascade)}\nmarkdown ${path}\n`);
343
+ }
344
+ if (parsed.jsonPath !== undefined) {
345
+ const path = await writeOutput(parsed.jsonPath, renderReplayJson(report));
346
+ process.stdout.write(`json ${path}\n`);
347
+ }
348
+ return 0;
349
+ } catch (error) {
350
+ process.stderr.write(`clio-coder context replay failed: ${error instanceof Error ? error.message : String(error)}\n`);
351
+ return 1;
352
+ }
353
+ }
354
+
355
+ async function sessionPath(value: string): Promise<string> {
356
+ const candidate = resolve(value);
357
+ try {
358
+ const facts = await stat(candidate);
359
+ if (facts.isDirectory()) return join(candidate, "current.jsonl");
360
+ if (facts.isFile()) return candidate;
361
+ } catch {
362
+ // A non-path value is resolved as a validated session id below.
363
+ }
364
+ try {
365
+ assertSafeId(value, "session");
366
+ } catch (error) {
367
+ throw new CliUsageError(error instanceof Error ? error.message : String(error));
368
+ }
369
+ const sessionsRoot = join(clioStatePath(), "sessions");
370
+ let cwdHashes: string[];
371
+ try {
372
+ cwdHashes = await readdir(sessionsRoot);
373
+ } catch {
374
+ throw new Error(`session not found: ${value}`);
375
+ }
376
+ for (const cwdHash of cwdHashes.sort()) {
377
+ const current = join(sessionsRoot, cwdHash, value, "current.jsonl");
378
+ try {
379
+ if ((await stat(current)).isFile()) return current;
380
+ } catch {
381
+ // Continue across repositories until the id is found.
382
+ }
383
+ }
384
+ throw new Error(`session not found: ${value}`);
385
+ }
386
+
387
+ async function pinnedLeafForSession(source: string): Promise<string | undefined> {
388
+ try {
389
+ const raw = await readFile(join(dirname(source), "meta.json"), "utf8");
390
+ const value = JSON.parse(raw) as unknown;
391
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
392
+ const pinned = (value as Record<string, unknown>).pinnedLeafTurnId;
393
+ return typeof pinned === "string" && pinned.length > 0 ? pinned : undefined;
394
+ } catch {
395
+ return undefined;
396
+ }
397
+ }
398
+
399
+ async function sessionCwdForSession(source: string): Promise<string | null> {
400
+ try {
401
+ const raw = await readFile(join(dirname(source), "meta.json"), "utf8");
402
+ const value = JSON.parse(raw) as unknown;
403
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
404
+ const cwd = (value as Record<string, unknown>).cwd;
405
+ return typeof cwd === "string" && cwd.length > 0 ? cwd : null;
406
+ } catch {
407
+ return null;
408
+ }
409
+ }
410
+
411
+ function formatWorkingSet(trace: Trace): string {
412
+ const view = foldWorkingSet(trace.entries);
413
+ const index = buildPathIndex(trace.entries, { cwd: trace.cwd });
414
+ const graph = buildReferenceGraph(trace, index);
415
+ const evictedTokens = [...view.evicted.values()].reduce((sum, state) => sum + state.tokensFreed, 0);
416
+ const churn = view.itemsEvicted === 0 ? "n/a" : (view.recalls / view.itemsEvicted).toFixed(3);
417
+ const opCounts = new Map<string, number>();
418
+ for (const observation of index.observations) {
419
+ opCounts.set(observation.op, (opCounts.get(observation.op) ?? 0) + 1);
420
+ }
421
+ const rewrittenPaths = new Set<string>();
422
+ for (const edge of graph.edges) {
423
+ if (edge.kind !== "file_rewrite") continue;
424
+ const path = index.byRef.get(edge.from)?.path;
425
+ if (path) rewrittenPaths.add(path);
426
+ }
427
+ const lines = [
428
+ `session: ${trace.id}`,
429
+ `source: ${trace.source}`,
430
+ "working set:",
431
+ ` policy: ${view.lastPolicyId ?? "none"}`,
432
+ ` eviction events: ${view.evictionEvents}`,
433
+ ` evicted refs: ${view.evicted.size}`,
434
+ ` items evicted: ${view.itemsEvicted}`,
435
+ ` evicted tokens: ${evictedTokens}`,
436
+ ` recalls: ${view.recalls}`,
437
+ ` churn: ${churn}`,
438
+ " refs:",
439
+ ...([...view.evicted.entries()].length === 0
440
+ ? [" none"]
441
+ : [...view.evicted.entries()]
442
+ .sort(([a], [b]) => a.localeCompare(b))
443
+ .map(([ref, state]) => ` ${ref} reason=${state.reason} by=${state.by ?? "-"} tokens=${state.tokensFreed}`)),
444
+ "path index:",
445
+ ` observations: ${index.observations.length}`,
446
+ ` ops: ${
447
+ [...opCounts.entries()]
448
+ .sort(([a], [b]) => a.localeCompare(b))
449
+ .map(([op, count]) => `${op}=${count}`)
450
+ .join(", ") || "none"
451
+ }`,
452
+ ` paths with rewrites: ${rewrittenPaths.size}`,
453
+ ...[...rewrittenPaths].sort().map((path) => ` ${path}`),
454
+ "",
455
+ ];
456
+ return lines.join("\n");
457
+ }
458
+
459
+ export async function runContextWorkingSetCommand(args: string[]): Promise<number> {
460
+ if (args.includes("--help") || args.includes("-h")) {
461
+ process.stdout.write(WORKING_SET_HELP);
462
+ return 0;
463
+ }
464
+ let session: string | undefined;
465
+ for (let index = 0; index < args.length; index += 1) {
466
+ const arg = args[index];
467
+ if (arg === "--session") {
468
+ try {
469
+ session = requiredValue(args, index, arg);
470
+ } catch (error) {
471
+ process.stderr.write(
472
+ `clio-coder context working-set: ${error instanceof Error ? error.message : String(error)}\n${WORKING_SET_HELP}`,
473
+ );
474
+ return 2;
475
+ }
476
+ index += 1;
477
+ continue;
478
+ }
479
+ process.stderr.write(`clio-coder context working-set: unknown flag ${arg}\n${WORKING_SET_HELP}`);
480
+ return 2;
481
+ }
482
+ if (session === undefined) {
483
+ process.stderr.write(`clio-coder context working-set: --session is required\n${WORKING_SET_HELP}`);
484
+ return 2;
485
+ }
486
+ try {
487
+ const source = await sessionPath(session);
488
+ const raw = await readFile(source, "utf8");
489
+ const parsed = parseSessionEntries(raw, source);
490
+ if (parsed.errors.length > 0) throw new Error(parsed.errors.join("; "));
491
+ const entries = filterEntriesToActivePath(parsed.entries, await pinnedLeafForSession(source));
492
+ const cwd = await sessionCwdForSession(source);
493
+ process.stdout.write(
494
+ formatWorkingSet({
495
+ id: basename(dirname(source)),
496
+ source,
497
+ cwd,
498
+ entries,
499
+ turnCount: buildPathIndex(entries, { cwd }).turnCount,
500
+ }),
501
+ );
502
+ return 0;
503
+ } catch (error) {
504
+ if (error instanceof CliUsageError) {
505
+ process.stderr.write(`clio-coder context working-set: ${error.message}\n${WORKING_SET_HELP}`);
506
+ return 2;
507
+ }
508
+ process.stderr.write(
509
+ `clio-coder context working-set failed: ${error instanceof Error ? error.message : String(error)}\n`,
510
+ );
511
+ return 1;
512
+ }
513
+ }
@@ -10,6 +10,8 @@ const HELP = `Usage:
10
10
  [--target <id>] [--model <id>] [--thinking off|low|medium|high]
11
11
  clio-coder context reset [--all] [--yes]
12
12
  clio-coder context index [--json]
13
+ clio-coder context replay --sessions <path>... [options]
14
+ clio-coder context working-set --session <id|path>
13
15
 
14
16
  Project context commands:
15
17
  clio-coder context show project context status (CLIO-CODER.md, preload, codewiki)
@@ -18,6 +20,8 @@ Project context commands:
18
20
  clio-coder context wiki generate or inspect the agent-authored Markdown wiki
19
21
  clio-coder context reset clear accumulated project context artifacts
20
22
  clio-coder context index build the codewiki index without model calls
23
+ clio-coder context replay compare working-set policies over Clio ledgers
24
+ clio-coder context working-set inspect one session's working-set fold and path index
21
25
  `;
22
26
 
23
27
  function printWikiProgress(event: BootstrapProgressEvent): void {
@@ -313,6 +317,10 @@ export async function runContextCommand(args: string[]): Promise<number> {
313
317
  return (await import("./context-clear.js")).runContextClearCommand(rest);
314
318
  case "index":
315
319
  return (await import("./context-index.js")).runContextIndexCommand(rest);
320
+ case "replay":
321
+ return (await import("./context-working-set.js")).runContextReplayCommand(rest);
322
+ case "working-set":
323
+ return (await import("./context-working-set.js")).runContextWorkingSetCommand(rest);
316
324
  default:
317
325
  process.stderr.write(`clio-coder context: unknown subcommand ${verb}\n`);
318
326
  process.stdout.write(HELP);
@@ -1,6 +1,6 @@
1
1
  import { InvalidIdError } from "../core/safe-id.js";
2
2
  import { clioDataDir, clioStateDir } from "../core/xdg.js";
3
- import type { EvidenceOverview, EvidenceRunProvenance } from "../domains/evidence/index.js";
3
+ import type { CanonicalTrustStatus, EvidenceOverview, EvidenceRunProvenance } from "../domains/evidence/index.js";
4
4
  import {
5
5
  buildEvalEvidence,
6
6
  buildEvidence,
@@ -178,7 +178,7 @@ function renderEvidence(
178
178
  input: Awaited<ReturnType<typeof inspectEvidence>>,
179
179
  provenance: ReadonlyArray<EvidenceRunProvenance> = [],
180
180
  ): void {
181
- const { overview, findings } = input;
181
+ const { overview, findings, trustStatus } = input;
182
182
  process.stdout.write(`evidence: ${overview.evidenceId}\n`);
183
183
  process.stdout.write(`source: ${formatSource(overview)}\n`);
184
184
  process.stdout.write(`generated: ${overview.generatedAt}\n`);
@@ -188,6 +188,13 @@ function renderEvidence(
188
188
  process.stdout.write(`blocked tools: ${overview.totals.blockedToolCalls}\n`);
189
189
  process.stdout.write(`tags: ${formatList(overview.tags)}\n`);
190
190
  process.stdout.write(`findings: ${findings.length}\n`);
191
+ if (trustStatus.projection === "historical_format") {
192
+ process.stdout.write("trust status: unavailable (historical bundle without canonical projection)\n");
193
+ } else {
194
+ for (const run of trustStatus.runs) {
195
+ process.stdout.write(`trust ${run.runId}: ${formatTrustStatus(run.status)}\n`);
196
+ }
197
+ }
191
198
  // Provenance is printed only for runs whose receipts carry it, so a legacy
192
199
  // bundle prints nothing new here.
193
200
  for (const { runId, view } of provenance) {
@@ -197,6 +204,17 @@ function renderEvidence(
197
204
  process.stdout.write(`files: ${formatList(overview.files)}\n`);
198
205
  }
199
206
 
207
+ function formatTrustStatus(status: CanonicalTrustStatus): string {
208
+ return [
209
+ `artifactIntegrity=${status.artifactIntegrity.state}`,
210
+ `validationGrounding=${status.validationGrounding.state}`,
211
+ `independentReview=${status.independentReview.state}`,
212
+ `contextProvenance=${status.contextProvenance.state}`,
213
+ `autonomyEnforcement=${status.autonomyEnforcement.state}`,
214
+ `completionEvidence=${status.completionEvidence.state}`,
215
+ ].join(" ");
216
+ }
217
+
200
218
  function renderEvidenceList(overviews: ReadonlyArray<EvidenceOverview>): void {
201
219
  process.stdout.write(`${overviews.length} evidence artifacts\n`);
202
220
  if (overviews.length === 0) return;
package/src/cli/index.ts CHANGED
@@ -52,6 +52,8 @@ Usage:
52
52
  clio-coder context wiki [--update] [--status] [--depth auto|simple|medium|detailed] [--target <id>] [--model <id>]
53
53
  clio-coder context reset [--all] [--yes] clear accumulated project context artifacts
54
54
  clio-coder context index [--json] build the codewiki index without model calls
55
+ clio-coder context replay --sessions <path>... compare working-set policies over Clio ledgers
56
+ clio-coder context working-set --session <id|path> inspect one session's working-set state
55
57
  clio-coder uninstall remove all Clio Coder state; --remove-binary also unlinks the launcher
56
58
  clio-coder upgrade upgrade Clio Coder and run pending migrations
57
59
  clio-coder agents list discovered agent recipes
@@ -63,6 +65,7 @@ Usage:
63
65
  clio-coder trace query or view the durable dispatch trace mirror
64
66
  clio-coder extensions install, list, enable, disable, or remove extension packages
65
67
  clio-coder skills list, inspect, validate, or install skills
68
+ clio-coder verifiers discover, author, validate, edit, or dry-run project checks
66
69
  clio-coder docs [topic] serve the HTML docs of a source checkout on 127.0.0.1 (--no-open to skip browser)
67
70
  clio-coder dev <command> harness instruments; run 'clio-coder dev' for the list
68
71
  clio-coder --help, -h this message
@@ -243,6 +246,7 @@ const COMMAND_HANDLERS = new Map<string, CommandHandler>([
243
246
  ["ext", extensionsCommand],
244
247
  ["fleet", async (subArgs) => (await import("./fleet.js")).runFleetCommand(subArgs)],
245
248
  ["skills", async (subArgs) => (await import("./skills.js")).runSkillsCommand(subArgs)],
249
+ ["verifiers", async (subArgs) => (await import("./verifiers.js")).runVerifiersCommand(subArgs)],
246
250
  ["docs", async (subArgs) => (await import("./docs.js")).runDocsCommand(subArgs)],
247
251
  ["share", async (subArgs) => (await import("./share.js")).runShareCommand(subArgs)],
248
252
  ["export", async (subArgs) => (await import("./share.js")).runExportCommand(subArgs)],