@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,325 @@
|
|
|
1
|
+
import { SAFE_EXEC_DEFAULT_TIMEOUT_MS } from "../core/safe-exec.js";
|
|
2
|
+
import {
|
|
3
|
+
createVerifierDraft,
|
|
4
|
+
discoverVerifierAuthoring,
|
|
5
|
+
previewVerifierDraft,
|
|
6
|
+
runVerifierAuthoringWorkflow,
|
|
7
|
+
type VerifierRevision,
|
|
8
|
+
} from "../tools/verify/authoring.js";
|
|
9
|
+
import { loadProjectVerifierCatalog, PROJECT_VERIFIER_CATALOG_RELATIVE_PATH } from "../tools/verify/catalog.js";
|
|
10
|
+
import { verifyTool } from "../tools/verify/index.js";
|
|
11
|
+
import { printError, printOk } from "./argv.js";
|
|
12
|
+
|
|
13
|
+
const HELP = `Project verifier authoring
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
clio-coder verifiers discover
|
|
17
|
+
clio-coder verifiers author [--exclude <id>] [--rename <old>=<new>] [--dry-run <id>] [--yes]
|
|
18
|
+
clio-coder verifiers validate
|
|
19
|
+
clio-coder verifiers dry-run <id>
|
|
20
|
+
clio-coder verifiers add --id <id> --description <text> --command <json-argv> [fields] [--yes]
|
|
21
|
+
clio-coder verifiers edit <id> [--description <text>] [--command <json-argv>] [fields] [--yes]
|
|
22
|
+
clio-coder verifiers rename <old> <new> [--yes]
|
|
23
|
+
clio-coder verifiers remove <id> [--yes]
|
|
24
|
+
|
|
25
|
+
Fields:
|
|
26
|
+
--cwd <path> repository-relative working directory (default .)
|
|
27
|
+
--timeout-ms <number> bounded timeout in milliseconds (default 120000)
|
|
28
|
+
--tags <a,b> comma-separated catalog tags
|
|
29
|
+
|
|
30
|
+
Discovery and every preview are read-only. Mutating commands write only with
|
|
31
|
+
--yes after printing the exact argv, cwd, timeout, tags, source provenance, and
|
|
32
|
+
effective execution authority. dry-run invokes the production verify path.
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const VALUE_OPTIONS = new Set([
|
|
36
|
+
"--id",
|
|
37
|
+
"--description",
|
|
38
|
+
"--command",
|
|
39
|
+
"--cwd",
|
|
40
|
+
"--timeout-ms",
|
|
41
|
+
"--tags",
|
|
42
|
+
"--exclude",
|
|
43
|
+
"--rename",
|
|
44
|
+
"--dry-run",
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
interface ParsedAuthoringArgs {
|
|
48
|
+
yes: boolean;
|
|
49
|
+
help: boolean;
|
|
50
|
+
positional: string[];
|
|
51
|
+
values: Map<string, string[]>;
|
|
52
|
+
error?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseAuthoringArgs(argv: ReadonlyArray<string>): ParsedAuthoringArgs {
|
|
56
|
+
const positional: string[] = [];
|
|
57
|
+
const values = new Map<string, string[]>();
|
|
58
|
+
let yes = false;
|
|
59
|
+
let help = false;
|
|
60
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
61
|
+
const arg = argv[index];
|
|
62
|
+
if (arg === "--yes") {
|
|
63
|
+
yes = true;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (arg === "--help" || arg === "-h") {
|
|
67
|
+
help = true;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (arg?.startsWith("--") === true) {
|
|
71
|
+
if (!VALUE_OPTIONS.has(arg)) return { yes, help, positional, values, error: `unknown option: ${arg}` };
|
|
72
|
+
const value = argv[index + 1];
|
|
73
|
+
if (value === undefined || value.startsWith("--")) {
|
|
74
|
+
return { yes, help, positional, values, error: `${arg} requires a value` };
|
|
75
|
+
}
|
|
76
|
+
const prior = values.get(arg) ?? [];
|
|
77
|
+
prior.push(value);
|
|
78
|
+
values.set(arg, prior);
|
|
79
|
+
index += 1;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (arg !== undefined) positional.push(arg);
|
|
83
|
+
}
|
|
84
|
+
return { yes, help, positional, values };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function oneValue(parsed: ParsedAuthoringArgs, option: string): string | undefined {
|
|
88
|
+
return parsed.values.get(option)?.at(-1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function parseCommand(value: string | undefined): string[] | Error {
|
|
92
|
+
if (value === undefined) return new Error("--command is required and must be a JSON argv array");
|
|
93
|
+
let parsed: unknown;
|
|
94
|
+
try {
|
|
95
|
+
parsed = JSON.parse(value) as unknown;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return new Error(`--command must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
98
|
+
}
|
|
99
|
+
if (!Array.isArray(parsed) || parsed.length === 0 || parsed.some((entry) => typeof entry !== "string")) {
|
|
100
|
+
return new Error("--command must be a non-empty JSON string array");
|
|
101
|
+
}
|
|
102
|
+
return parsed as string[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function parseTimeout(value: string | undefined): number | Error {
|
|
106
|
+
if (value === undefined) return SAFE_EXEC_DEFAULT_TIMEOUT_MS;
|
|
107
|
+
const parsed = Number(value);
|
|
108
|
+
if (!Number.isInteger(parsed) || parsed <= 0) return new Error("--timeout-ms must be a positive integer");
|
|
109
|
+
return parsed;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function tags(value: string | undefined): string[] {
|
|
113
|
+
return value === undefined
|
|
114
|
+
? []
|
|
115
|
+
: value
|
|
116
|
+
.split(",")
|
|
117
|
+
.map((tag) => tag.trim())
|
|
118
|
+
.filter((tag) => tag.length > 0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function requiredValue(parsed: ParsedAuthoringArgs, option: string): string | Error {
|
|
122
|
+
const value = oneValue(parsed, option);
|
|
123
|
+
return value === undefined || value.length === 0 ? new Error(`${option} is required`) : value;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function authorRevisions(parsed: ParsedAuthoringArgs): VerifierRevision[] | Error {
|
|
127
|
+
const revisions: VerifierRevision[] = [];
|
|
128
|
+
for (const id of parsed.values.get("--exclude") ?? []) revisions.push({ kind: "remove", id });
|
|
129
|
+
for (const rename of parsed.values.get("--rename") ?? []) {
|
|
130
|
+
const separator = rename.indexOf("=");
|
|
131
|
+
if (separator <= 0 || separator === rename.length - 1) {
|
|
132
|
+
return new Error("--rename must use <old>=<new>");
|
|
133
|
+
}
|
|
134
|
+
revisions.push({ kind: "rename", id: rename.slice(0, separator), newId: rename.slice(separator + 1) });
|
|
135
|
+
}
|
|
136
|
+
return revisions;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function addRevision(parsed: ParsedAuthoringArgs): VerifierRevision | Error {
|
|
140
|
+
const id = requiredValue(parsed, "--id");
|
|
141
|
+
if (id instanceof Error) return id;
|
|
142
|
+
const description = requiredValue(parsed, "--description");
|
|
143
|
+
if (description instanceof Error) return description;
|
|
144
|
+
const command = parseCommand(oneValue(parsed, "--command"));
|
|
145
|
+
if (command instanceof Error) return command;
|
|
146
|
+
const timeoutMs = parseTimeout(oneValue(parsed, "--timeout-ms"));
|
|
147
|
+
if (timeoutMs instanceof Error) return timeoutMs;
|
|
148
|
+
return {
|
|
149
|
+
kind: "add",
|
|
150
|
+
check: {
|
|
151
|
+
id,
|
|
152
|
+
description,
|
|
153
|
+
command,
|
|
154
|
+
cwd: oneValue(parsed, "--cwd") ?? ".",
|
|
155
|
+
timeoutMs,
|
|
156
|
+
tags: tags(oneValue(parsed, "--tags")),
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function editRevision(parsed: ParsedAuthoringArgs): VerifierRevision | Error {
|
|
162
|
+
const id = parsed.positional[1];
|
|
163
|
+
if (id === undefined) return new Error("edit requires a check ID");
|
|
164
|
+
const changes: Extract<VerifierRevision, { kind: "edit" }>["changes"] = {};
|
|
165
|
+
const description = oneValue(parsed, "--description");
|
|
166
|
+
if (description !== undefined) changes.description = description;
|
|
167
|
+
const commandValue = oneValue(parsed, "--command");
|
|
168
|
+
if (commandValue !== undefined) {
|
|
169
|
+
const command = parseCommand(commandValue);
|
|
170
|
+
if (command instanceof Error) return command;
|
|
171
|
+
changes.command = command;
|
|
172
|
+
}
|
|
173
|
+
const cwd = oneValue(parsed, "--cwd");
|
|
174
|
+
if (cwd !== undefined) changes.cwd = cwd;
|
|
175
|
+
const timeoutValue = oneValue(parsed, "--timeout-ms");
|
|
176
|
+
if (timeoutValue !== undefined) {
|
|
177
|
+
const timeoutMs = parseTimeout(timeoutValue);
|
|
178
|
+
if (timeoutMs instanceof Error) return timeoutMs;
|
|
179
|
+
changes.timeoutMs = timeoutMs;
|
|
180
|
+
}
|
|
181
|
+
const tagValue = oneValue(parsed, "--tags");
|
|
182
|
+
if (tagValue !== undefined) changes.tags = tags(tagValue);
|
|
183
|
+
if (Object.keys(changes).length === 0) return new Error("edit requires at least one changed field");
|
|
184
|
+
return { kind: "edit", id, changes };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function mutationRevision(parsed: ParsedAuthoringArgs): VerifierRevision | Error {
|
|
188
|
+
const command = parsed.positional[0];
|
|
189
|
+
if (command === "add") return addRevision(parsed);
|
|
190
|
+
if (command === "edit") return editRevision(parsed);
|
|
191
|
+
if (command === "rename") {
|
|
192
|
+
const id = parsed.positional[1];
|
|
193
|
+
const newId = parsed.positional[2];
|
|
194
|
+
if (id === undefined || newId === undefined) return new Error("rename requires <old> and <new> IDs");
|
|
195
|
+
return { kind: "rename", id, newId };
|
|
196
|
+
}
|
|
197
|
+
if (command === "remove") {
|
|
198
|
+
const id = parsed.positional[1];
|
|
199
|
+
if (id === undefined) return new Error("remove requires a check ID");
|
|
200
|
+
return { kind: "remove", id };
|
|
201
|
+
}
|
|
202
|
+
return new Error(`unknown verifiers command: ${command ?? "(none)"}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function printDryRun(id: string, result: Awaited<ReturnType<typeof verifyTool.run>>): number {
|
|
206
|
+
if (result.kind === "error") {
|
|
207
|
+
printError(`dry-run '${id}' failed: ${result.message}`);
|
|
208
|
+
return 1;
|
|
209
|
+
}
|
|
210
|
+
process.stdout.write(`Dry run '${id}':\n${result.output}${result.output.endsWith("\n") ? "" : "\n"}`);
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async function executeWorkflow(
|
|
215
|
+
parsed: ParsedAuthoringArgs,
|
|
216
|
+
options: { includeProposals: boolean; revisions: VerifierRevision[] },
|
|
217
|
+
): Promise<number> {
|
|
218
|
+
let printedPreview = false;
|
|
219
|
+
const dryRunCheckIds = parsed.values.get("--dry-run") ?? [];
|
|
220
|
+
const result = await runVerifierAuthoringWorkflow({
|
|
221
|
+
includeProposals: options.includeProposals,
|
|
222
|
+
initialRevisions: options.revisions,
|
|
223
|
+
confirmed: parsed.yes,
|
|
224
|
+
decide(context) {
|
|
225
|
+
process.stdout.write(`${context.preview}\n`);
|
|
226
|
+
printedPreview = true;
|
|
227
|
+
if (!parsed.yes || (context.draft.checks.length === 0 && options.revisions.length === 0)) {
|
|
228
|
+
return { kind: "reject" };
|
|
229
|
+
}
|
|
230
|
+
return { kind: "confirm", dryRunCheckIds };
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
if (result.status === "invalid") {
|
|
234
|
+
if (!printedPreview && result.preview !== undefined) process.stdout.write(`${result.preview}\n`);
|
|
235
|
+
printError(result.reason);
|
|
236
|
+
if (result.wrote && result.path !== undefined) {
|
|
237
|
+
process.stderr.write(` ${result.path} was written; repair or remove it before running a check.\n`);
|
|
238
|
+
}
|
|
239
|
+
return 1;
|
|
240
|
+
}
|
|
241
|
+
if (result.status === "rejected") {
|
|
242
|
+
if (!parsed.yes) {
|
|
243
|
+
process.stdout.write("Review complete. Nothing changed; rerun the same command with --yes to write this preview.\n");
|
|
244
|
+
} else {
|
|
245
|
+
process.stdout.write(
|
|
246
|
+
"Nothing changed because the preview contains no catalog checks. Package checks remain active; unsupported commands can be added with `clio-coder verifiers add`.\n",
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
printOk(`wrote ${result.path}; production parser accepted the catalog`);
|
|
252
|
+
let code = 0;
|
|
253
|
+
for (const dryRun of result.dryRuns) code = Math.max(code, printDryRun(dryRun.id, dryRun.result));
|
|
254
|
+
return code;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function discoverCommand(): Promise<number> {
|
|
258
|
+
const discovery = discoverVerifierAuthoring();
|
|
259
|
+
if (!discovery.ok) {
|
|
260
|
+
printError(discovery.reason);
|
|
261
|
+
process.stderr.write(`${discovery.manualEntry}\n`);
|
|
262
|
+
return 1;
|
|
263
|
+
}
|
|
264
|
+
process.stdout.write(`${previewVerifierDraft(createVerifierDraft(discovery))}\n`);
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function validateCommand(): number {
|
|
269
|
+
const loaded = loadProjectVerifierCatalog(process.cwd());
|
|
270
|
+
if (!loaded.ok) {
|
|
271
|
+
printError(`production catalog parser rejected ${loaded.reason}`);
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
if (loaded.source === null) {
|
|
275
|
+
process.stdout.write(`No ${PROJECT_VERIFIER_CATALOG_RELATIVE_PATH} exists. Run \`clio-coder verifiers author\`.\n`);
|
|
276
|
+
return 0;
|
|
277
|
+
}
|
|
278
|
+
printOk(
|
|
279
|
+
`production catalog parser accepted ${PROJECT_VERIFIER_CATALOG_RELATIVE_PATH} (${loaded.source.checks.length} check${loaded.source.checks.length === 1 ? "" : "s"})`,
|
|
280
|
+
);
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export async function runVerifiersCommand(argv: string[]): Promise<number> {
|
|
285
|
+
const parsed = parseAuthoringArgs(argv);
|
|
286
|
+
if (parsed.error !== undefined) {
|
|
287
|
+
printError(parsed.error);
|
|
288
|
+
process.stderr.write(HELP);
|
|
289
|
+
return 2;
|
|
290
|
+
}
|
|
291
|
+
if (parsed.help || parsed.positional.length === 0) {
|
|
292
|
+
process.stdout.write(HELP);
|
|
293
|
+
return 0;
|
|
294
|
+
}
|
|
295
|
+
const command = parsed.positional[0];
|
|
296
|
+
if (command === "discover") return discoverCommand();
|
|
297
|
+
if (command === "validate") return validateCommand();
|
|
298
|
+
if (command === "dry-run") {
|
|
299
|
+
const id = parsed.positional[1];
|
|
300
|
+
if (id === undefined) {
|
|
301
|
+
printError("dry-run requires a check ID");
|
|
302
|
+
return 2;
|
|
303
|
+
}
|
|
304
|
+
return printDryRun(id, await verifyTool.run({ check: id }));
|
|
305
|
+
}
|
|
306
|
+
if (command === "author") {
|
|
307
|
+
const revisions = authorRevisions(parsed);
|
|
308
|
+
if (revisions instanceof Error) {
|
|
309
|
+
printError(revisions.message);
|
|
310
|
+
return 2;
|
|
311
|
+
}
|
|
312
|
+
return executeWorkflow(parsed, { includeProposals: true, revisions });
|
|
313
|
+
}
|
|
314
|
+
if (["add", "edit", "rename", "remove"].includes(command ?? "")) {
|
|
315
|
+
const revision = mutationRevision(parsed);
|
|
316
|
+
if (revision instanceof Error) {
|
|
317
|
+
printError(revision.message);
|
|
318
|
+
return 2;
|
|
319
|
+
}
|
|
320
|
+
return executeWorkflow(parsed, { includeProposals: false, revisions: [revision] });
|
|
321
|
+
}
|
|
322
|
+
printError(`unknown verifiers command: ${command}`);
|
|
323
|
+
process.stderr.write(HELP);
|
|
324
|
+
return 2;
|
|
325
|
+
}
|
package/src/core/bash-exec.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { StringDecoder } from "node:string_decoder";
|
|
2
3
|
import { AI_AGENT_NAME } from "./agent-environment.js";
|
|
3
4
|
import {
|
|
4
5
|
gitCommitAttributionEnabled,
|
|
@@ -28,6 +29,8 @@ export interface BashCommandResult {
|
|
|
28
29
|
aborted: boolean;
|
|
29
30
|
timedOut: boolean;
|
|
30
31
|
outputCapped: boolean;
|
|
32
|
+
/** Exact bytes retained from stdout and stderr, never above the hard cap. */
|
|
33
|
+
outputBytes: number;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
export interface BashCommandProgress {
|
|
@@ -151,6 +154,9 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
|
|
|
151
154
|
let killSent = false;
|
|
152
155
|
let stdout = "";
|
|
153
156
|
let stderr = "";
|
|
157
|
+
const stdoutDecoder = new StringDecoder("utf8");
|
|
158
|
+
const stderrDecoder = new StringDecoder("utf8");
|
|
159
|
+
let decodersFinished = false;
|
|
154
160
|
let outputBytes = 0;
|
|
155
161
|
let outputCapped = false;
|
|
156
162
|
let updateTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -164,6 +170,16 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
|
|
|
164
170
|
stdio: ["ignore", "pipe", "pipe"],
|
|
165
171
|
});
|
|
166
172
|
|
|
173
|
+
const finishDecoders = (): void => {
|
|
174
|
+
if (decodersFinished) return;
|
|
175
|
+
decodersFinished = true;
|
|
176
|
+
// A hard byte cap may bisect a multibyte code point. Discard the
|
|
177
|
+
// decoder's incomplete suffix instead of manufacturing U+FFFD.
|
|
178
|
+
if (outputCapped) return;
|
|
179
|
+
stdout += stdoutDecoder.end();
|
|
180
|
+
stderr += stderrDecoder.end();
|
|
181
|
+
};
|
|
182
|
+
|
|
167
183
|
const emitUpdate = (): void => {
|
|
168
184
|
if (options.onUpdate === undefined || !updateDirty) return;
|
|
169
185
|
updateDirty = false;
|
|
@@ -250,24 +266,20 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
|
|
|
250
266
|
|
|
251
267
|
const appendChunk = (target: "stdout" | "stderr", chunk: Buffer): void => {
|
|
252
268
|
if (outputCapped) return;
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
else stderr += slice;
|
|
263
|
-
}
|
|
269
|
+
const remaining = BASH_HARD_CAP_BYTES - outputBytes;
|
|
270
|
+
const accepted = chunk.byteLength <= remaining ? chunk : chunk.subarray(0, Math.max(0, remaining));
|
|
271
|
+
outputBytes += accepted.byteLength;
|
|
272
|
+
if (accepted.byteLength > 0) {
|
|
273
|
+
const decoded = target === "stdout" ? stdoutDecoder.write(accepted) : stderrDecoder.write(accepted);
|
|
274
|
+
if (target === "stdout") stdout += decoded;
|
|
275
|
+
else stderr += decoded;
|
|
276
|
+
}
|
|
277
|
+
if (accepted.byteLength < chunk.byteLength) {
|
|
264
278
|
outputCapped = true;
|
|
265
279
|
scheduleUpdate();
|
|
266
280
|
killChild();
|
|
267
281
|
return;
|
|
268
282
|
}
|
|
269
|
-
if (target === "stdout") stdout += chunk.toString("utf8");
|
|
270
|
-
else stderr += chunk.toString("utf8");
|
|
271
283
|
scheduleUpdate();
|
|
272
284
|
};
|
|
273
285
|
|
|
@@ -278,6 +290,7 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
|
|
|
278
290
|
settled = true;
|
|
279
291
|
if (timeoutId) clearTimeout(timeoutId);
|
|
280
292
|
clearKillGraceTimer();
|
|
293
|
+
finishDecoders();
|
|
281
294
|
finishUpdates();
|
|
282
295
|
options.signal?.removeEventListener("abort", onAbort);
|
|
283
296
|
resolve({
|
|
@@ -289,6 +302,7 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
|
|
|
289
302
|
aborted,
|
|
290
303
|
timedOut,
|
|
291
304
|
outputCapped,
|
|
305
|
+
outputBytes,
|
|
292
306
|
});
|
|
293
307
|
});
|
|
294
308
|
child.on("close", (code, signalName) => {
|
|
@@ -296,6 +310,7 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
|
|
|
296
310
|
settled = true;
|
|
297
311
|
if (timeoutId) clearTimeout(timeoutId);
|
|
298
312
|
clearKillGraceTimer();
|
|
313
|
+
finishDecoders();
|
|
299
314
|
finishUpdates();
|
|
300
315
|
options.signal?.removeEventListener("abort", onAbort);
|
|
301
316
|
const error =
|
|
@@ -307,7 +322,17 @@ export async function runBashCommand(command: string, options: RunBashCommandOpt
|
|
|
307
322
|
code: code ?? undefined,
|
|
308
323
|
signal: signalName ?? undefined,
|
|
309
324
|
} as NodeJS.ErrnoException);
|
|
310
|
-
resolve({
|
|
325
|
+
resolve({
|
|
326
|
+
error,
|
|
327
|
+
stdout,
|
|
328
|
+
stderr,
|
|
329
|
+
exitCode: code,
|
|
330
|
+
signal: signalName,
|
|
331
|
+
aborted,
|
|
332
|
+
timedOut,
|
|
333
|
+
outputCapped,
|
|
334
|
+
outputBytes,
|
|
335
|
+
});
|
|
311
336
|
});
|
|
312
337
|
});
|
|
313
338
|
}
|
package/src/core/bus-events.ts
CHANGED
|
@@ -61,6 +61,7 @@ export const BusChannels = {
|
|
|
61
61
|
ContextActivity: "context.activity",
|
|
62
62
|
ContextWarning: "context.warning",
|
|
63
63
|
ContextPruned: "context.pruned",
|
|
64
|
+
ContextRecalled: "context.recalled",
|
|
64
65
|
AgentStatusChanged: "agent.status.changed",
|
|
65
66
|
RunAborted: "run.aborted",
|
|
66
67
|
BudgetAlert: "budget.alert",
|
|
@@ -253,21 +254,34 @@ export function isRunAbortedPayload(value: unknown): value is RunAbortedPayload
|
|
|
253
254
|
return true;
|
|
254
255
|
}
|
|
255
256
|
|
|
256
|
-
/** Payload published on {@link BusChannels.ContextPruned} after
|
|
257
|
+
/** Payload published on {@link BusChannels.ContextPruned} after projected or summarized context shrinks. */
|
|
257
258
|
export interface ContextPrunedPayload {
|
|
258
|
-
stage: "mask_observations" | "llm_summary";
|
|
259
|
+
stage: "mask_observations" | "working_set" | "llm_summary";
|
|
259
260
|
tokensBefore: number;
|
|
260
261
|
tokensAfter: number;
|
|
261
262
|
trigger: string;
|
|
262
263
|
snapshotIdBefore: string | null;
|
|
263
264
|
snapshotIdAfter: string;
|
|
264
265
|
at: number;
|
|
265
|
-
/** Used/window ratio at trigger time; mask
|
|
266
|
+
/** Used/window ratio at trigger time; working-set and legacy-mask stages only. */
|
|
266
267
|
pressure?: number | null;
|
|
268
|
+
/** Legacy destructive-mask count. */
|
|
267
269
|
maskedObservations?: number;
|
|
268
|
-
/** Thinking blocks stripped from stale assistant messages; mask
|
|
270
|
+
/** Thinking blocks stripped from stale assistant messages; legacy mask only. */
|
|
269
271
|
maskedThinkingBlocks?: number;
|
|
270
272
|
maskedThinkingChars?: number;
|
|
273
|
+
/** Working-set policy that selected an applied eviction event. */
|
|
274
|
+
policyId?: string;
|
|
275
|
+
/** Number of working-set units evicted by the event. */
|
|
276
|
+
evictedItems?: number;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Payload published on {@link BusChannels.ContextRecalled} after an exact working-set recall. */
|
|
280
|
+
export interface ContextRecalledPayload {
|
|
281
|
+
ref: string;
|
|
282
|
+
trigger: "tool" | "operator";
|
|
283
|
+
tokensReadmitted: number;
|
|
284
|
+
at: number;
|
|
271
285
|
}
|
|
272
286
|
|
|
273
287
|
// ---------------------------------------------------------------------------
|
|
@@ -727,6 +741,7 @@ export type BusPayloadMap = {
|
|
|
727
741
|
[BusChannels.ContextActivity]: ContextActivityPayload;
|
|
728
742
|
[BusChannels.ContextWarning]: ContextWarningPayload;
|
|
729
743
|
[BusChannels.ContextPruned]: ContextPrunedPayload;
|
|
744
|
+
[BusChannels.ContextRecalled]: ContextRecalledPayload;
|
|
730
745
|
[BusChannels.AgentStatusChanged]: AgentStatusChangedPayload;
|
|
731
746
|
[BusChannels.RunAborted]: RunAbortedPayload;
|
|
732
747
|
[BusChannels.BudgetAlert]: BudgetAlertPayload;
|
package/src/core/config.ts
CHANGED
|
@@ -968,6 +968,7 @@ const TOP_LEVEL_KEYS = [
|
|
|
968
968
|
"delegation",
|
|
969
969
|
"keybindings",
|
|
970
970
|
"compaction",
|
|
971
|
+
"context",
|
|
971
972
|
"retry",
|
|
972
973
|
"guardrails",
|
|
973
974
|
] as const;
|
|
@@ -1405,6 +1406,59 @@ export function validateSettings(raw: unknown): SettingsValidationResult {
|
|
|
1405
1406
|
}
|
|
1406
1407
|
}
|
|
1407
1408
|
|
|
1409
|
+
if ("context" in raw) {
|
|
1410
|
+
if (!isPlainObject(raw.context)) {
|
|
1411
|
+
issues.add("context", `expected a map, got ${describe(raw.context)}`);
|
|
1412
|
+
} else {
|
|
1413
|
+
issues.unknownKeys("context", raw.context, ["workingSet"]);
|
|
1414
|
+
if ("workingSet" in raw.context) {
|
|
1415
|
+
if (!isPlainObject(raw.context.workingSet)) {
|
|
1416
|
+
issues.add("context.workingSet", `expected a map, got ${describe(raw.context.workingSet)}`);
|
|
1417
|
+
} else {
|
|
1418
|
+
const workingSet = raw.context.workingSet;
|
|
1419
|
+
issues.unknownKeys("context.workingSet", workingSet, [
|
|
1420
|
+
"enabled",
|
|
1421
|
+
"policy",
|
|
1422
|
+
"target",
|
|
1423
|
+
"protectLastTurns",
|
|
1424
|
+
"minEvictableTokens",
|
|
1425
|
+
]);
|
|
1426
|
+
if ("enabled" in workingSet) {
|
|
1427
|
+
const v = expectBoolean(issues, "context.workingSet.enabled", workingSet.enabled);
|
|
1428
|
+
if (v !== undefined) settings.context.workingSet.enabled = v;
|
|
1429
|
+
}
|
|
1430
|
+
if ("policy" in workingSet) {
|
|
1431
|
+
const v = expectEnum(issues, "context.workingSet.policy", workingSet.policy, [
|
|
1432
|
+
"age-horizon",
|
|
1433
|
+
"structural-v1",
|
|
1434
|
+
] as const);
|
|
1435
|
+
if (v !== undefined) settings.context.workingSet.policy = v;
|
|
1436
|
+
}
|
|
1437
|
+
if ("target" in workingSet) {
|
|
1438
|
+
const v = expectNumber(issues, "context.workingSet.target", workingSet.target);
|
|
1439
|
+
if (v !== undefined && (v <= 0 || v >= 1)) {
|
|
1440
|
+
issues.add("context.workingSet.target", `expected a number > 0 and < 1, got ${v}`);
|
|
1441
|
+
} else if (v !== undefined) {
|
|
1442
|
+
settings.context.workingSet.target = v;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
if ("protectLastTurns" in workingSet) {
|
|
1446
|
+
const v = expectInteger(issues, "context.workingSet.protectLastTurns", workingSet.protectLastTurns, {
|
|
1447
|
+
min: 1,
|
|
1448
|
+
});
|
|
1449
|
+
if (v !== undefined) settings.context.workingSet.protectLastTurns = v;
|
|
1450
|
+
}
|
|
1451
|
+
if ("minEvictableTokens" in workingSet) {
|
|
1452
|
+
const v = expectInteger(issues, "context.workingSet.minEvictableTokens", workingSet.minEvictableTokens, {
|
|
1453
|
+
min: 0,
|
|
1454
|
+
});
|
|
1455
|
+
if (v !== undefined) settings.context.workingSet.minEvictableTokens = v;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1408
1462
|
if ("retry" in raw) {
|
|
1409
1463
|
if (!isPlainObject(raw.retry)) {
|
|
1410
1464
|
issues.add("retry", `expected a map, got ${describe(raw.retry)}`);
|
package/src/core/defaults.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* exist. Users edit the file directly or through TUI overlays.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { DEFAULT_WORKING_SET_SETTINGS } from "../domains/context/working-set/defaults.js";
|
|
7
8
|
import type { TargetDescriptor } from "../domains/providers/types/target-descriptor.js";
|
|
8
9
|
import type { AutonomyLevel } from "../domains/safety/autonomy.js";
|
|
9
10
|
import { GUARDRAIL_DEFAULTS, type GuardrailValues } from "./guardrails.js";
|
|
@@ -98,6 +99,32 @@ export interface CompactionSettings {
|
|
|
98
99
|
systemPrompt?: string;
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Working-set layer settings (`context.workingSet`). The layer decides which
|
|
104
|
+
* tool-result bodies and thinking blocks leave the model's working set when
|
|
105
|
+
* pressure crosses `compaction.threshold`; it records evictions as ledger
|
|
106
|
+
* entries and never rewrites history. Defaults and prose live in
|
|
107
|
+
* src/domains/context/working-set/defaults.ts.
|
|
108
|
+
*
|
|
109
|
+
* - enabled: master switch. Off skips eviction and goes straight to summary
|
|
110
|
+
* compaction (the legacy destructive mask is only reachable through
|
|
111
|
+
* CLIO_CODER_LEGACY_MASK=1).
|
|
112
|
+
* - policy: candidate selection rule set.
|
|
113
|
+
* - target: used/window ratio an applied event batches down to.
|
|
114
|
+
* - protectLastTurns: recent user turns whose observations are never evicted.
|
|
115
|
+
* - minEvictableTokens: results below this estimate are never evicted; the
|
|
116
|
+
* marker would cost more than it saves.
|
|
117
|
+
*/
|
|
118
|
+
export type WorkingSetPolicyId = "age-horizon" | "structural-v1";
|
|
119
|
+
|
|
120
|
+
export interface WorkingSetSettings {
|
|
121
|
+
enabled: boolean;
|
|
122
|
+
policy: WorkingSetPolicyId;
|
|
123
|
+
target: number;
|
|
124
|
+
protectLastTurns: number;
|
|
125
|
+
minEvictableTokens: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
101
128
|
/**
|
|
102
129
|
* Transient provider retry controls for the interactive chat loop. These are
|
|
103
130
|
* intentionally small and mirror the session retry helper defaults. Dispatched
|
|
@@ -357,6 +384,9 @@ export const DEFAULT_SETTINGS = {
|
|
|
357
384
|
threshold: 0.8,
|
|
358
385
|
excludeLastTurns: 6,
|
|
359
386
|
} as CompactionSettings,
|
|
387
|
+
context: {
|
|
388
|
+
workingSet: DEFAULT_WORKING_SET_SETTINGS,
|
|
389
|
+
},
|
|
360
390
|
retry: {
|
|
361
391
|
enabled: true,
|
|
362
392
|
maxRetries: 3,
|
|
@@ -610,9 +640,9 @@ keybindings: {}
|
|
|
610
640
|
# auto master switch for the pre-request compaction trigger.
|
|
611
641
|
# Manual /context compact always runs the LLM summary.
|
|
612
642
|
# threshold pressure = estimated_tokens / context_window. Crossing
|
|
613
|
-
# it
|
|
614
|
-
#
|
|
615
|
-
# excludeLastTurns recent
|
|
643
|
+
# it evicts from the working set first, then runs a full
|
|
644
|
+
# LLM summary if pressure stays above the threshold.
|
|
645
|
+
# excludeLastTurns recent turns protected only by the temporary legacy mask.
|
|
616
646
|
# model optional pattern (e.g. provider/summary-model-id) for a
|
|
617
647
|
# dedicated summarization model. Absent ⇒ orchestrator target.
|
|
618
648
|
# systemPrompt optional path to a prompt-override file.
|
|
@@ -623,6 +653,23 @@ compaction:
|
|
|
623
653
|
# model: provider/summary-model-id
|
|
624
654
|
# systemPrompt: ~/.config/clio-coder/prompts/compaction.md
|
|
625
655
|
|
|
656
|
+
# Non-destructive working-set eviction before summary compaction.
|
|
657
|
+
# enabled false skips eviction and goes directly to the summary stage.
|
|
658
|
+
# policy structural-v1 evicts by what the session did since
|
|
659
|
+
# (re-reads, edits, resolved failures, consumed listings)
|
|
660
|
+
# and falls back to age only under pressure;
|
|
661
|
+
# age-horizon is the previous age-based selection.
|
|
662
|
+
# target pressure ratio an applied eviction batches down to.
|
|
663
|
+
# protectLastTurns recent user turns whose observations remain in the working set.
|
|
664
|
+
# minEvictableTokens entries below this estimate remain in the working set.
|
|
665
|
+
context:
|
|
666
|
+
workingSet:
|
|
667
|
+
enabled: true
|
|
668
|
+
policy: structural-v1
|
|
669
|
+
target: 0.6
|
|
670
|
+
protectLastTurns: 6
|
|
671
|
+
minEvictableTokens: 200
|
|
672
|
+
|
|
626
673
|
# Transient provider/stream retry controls for interactive chat.
|
|
627
674
|
# Retryable errors include overloads, rate limits, 5xx responses, network
|
|
628
675
|
# resets, and timeouts. Context overflow uses compaction recovery instead.
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
export const VERIFICATION_SCRIPT_FAMILY_HINT = "test*/lint*/build*/typecheck*/check*/format*/ci*";
|
|
2
2
|
|
|
3
3
|
const VERIFICATION_SCRIPT_PATTERN = /^(?:test|lint|build|typecheck|check|format|ci)(?:[:.-].*)?$/;
|
|
4
|
+
const PROJECT_VERIFIER_CHECK_ID_PATTERN = /^[a-z0-9][a-z0-9._:-]*$/;
|
|
4
5
|
|
|
5
6
|
export function isVerificationScriptName(name: string): boolean {
|
|
6
7
|
return VERIFICATION_SCRIPT_PATTERN.test(name);
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
/** Stable identifier grammar for project-declared verifier catalog checks. */
|
|
11
|
+
export function isProjectVerifierCheckId(name: string): boolean {
|
|
12
|
+
return PROJECT_VERIFIER_CHECK_ID_PATTERN.test(name);
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
export function declaredVerificationScripts(scripts: Record<string, unknown>): string[] {
|
|
10
16
|
return Object.keys(scripts)
|
|
11
17
|
.filter((name) => isVerificationScriptName(name))
|
|
@@ -24,6 +24,9 @@ Inspect scripts, docs, recent diffs, and touched files before choosing commands.
|
|
|
24
24
|
When a codewiki exists, prefer `code_nav` (symbol, dependents) over broad reads to scope what a diff touches.
|
|
25
25
|
Run only the checks required for the requested confidence level.
|
|
26
26
|
Prefer typed validation tools over arbitrary shell execution.
|
|
27
|
+
Call `verify()` before choosing an executable check. It lists package scripts and strict project entries from `.clio-coder/verifiers.yaml` through the same metadata shape.
|
|
28
|
+
Run a project entry only by its listed ID. Do not add args, cwd, timeout, environment, or shell composition; the catalog's exact argv, cwd, and timeout are authoritative.
|
|
29
|
+
Treat scientific validation contracts and generated handbook expectations as advisory evidence requirements. They do not become executable until the project declares a corresponding verifier-catalog entry.
|
|
27
30
|
Do not edit source files, tests, docs, configs, or generated artifacts from this role.
|
|
28
31
|
When a gate fails, report the exact command, exit status, relevant error lines, and likely owner.
|
|
29
32
|
Distinguish pre-existing failures from introduced failures when the evidence allows.
|