peaks-loop 4.0.46 → 4.0.48
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 +54 -0
- package/README-en.md +1 -1
- package/README.md +1 -1
- package/agents/karpathy-reviewer.md +11 -10
- package/dist/cli/cli-helpers.d.ts +34 -0
- package/dist/cli/cli-helpers.js +57 -0
- package/dist/cli/commands/code-job-shape-commands.js +8 -0
- package/dist/cli/commands/code-runtime-commands.d.ts +22 -0
- package/dist/cli/commands/code-runtime-commands.js +139 -16
- package/dist/cli/commands/compact-command.js +241 -1
- package/dist/cli/commands/config-commands.js +15 -9
- package/dist/cli/commands/container-commands.js +3 -3
- package/dist/cli/commands/core/skill-command.js +45 -10
- package/dist/cli/commands/cron-commands.js +2 -1
- package/dist/cli/commands/dashboard-long-run.js +6 -0
- package/dist/cli/commands/dispatch-commands.js +11 -1
- package/dist/cli/commands/doctor/invoke-from-code.js +6 -0
- package/dist/cli/commands/e2e-verify.js +3 -3
- package/dist/cli/commands/governance-classify-contract-commands.js +1 -0
- package/dist/cli/commands/hooks-commands.js +14 -5
- package/dist/cli/commands/job-commands.js +8 -0
- package/dist/cli/commands/loop-commands.js +1 -0
- package/dist/cli/commands/loop-eval-commands.js +15 -0
- package/dist/cli/commands/perf-audit-commands.js +2 -0
- package/dist/cli/commands/playwright-commands.js +14 -1
- package/dist/cli/commands/prd-commands.js +1 -1
- package/dist/cli/commands/qa-commands.js +22 -0
- package/dist/cli/commands/reinject-command.d.ts +72 -0
- package/dist/cli/commands/reinject-command.js +174 -0
- package/dist/cli/commands/request-commands.js +14 -3
- package/dist/cli/commands/scan-commands.js +1 -1
- package/dist/cli/commands/security-audit-commands.js +2 -0
- package/dist/cli/commands/shadcn-commands.js +1 -0
- package/dist/cli/commands/slice-integrate-commands.js +5 -0
- package/dist/cli/commands/statusline-commands.js +44 -4
- package/dist/cli/commands/sub-agent/detached.d.ts +14 -1
- package/dist/cli/commands/sub-agent/detached.js +47 -22
- package/dist/cli/commands/sub-agent-shutdown-commands.js +11 -0
- package/dist/cli/commands/test-commands.js +2 -1
- package/dist/cli/commands/verdict-aggregate-command.js +95 -13
- package/dist/cli/commands/vm-commands.js +7 -7
- package/dist/cli/commands/workflow-commands.js +1 -1
- package/dist/cli/commands/workspace/init-command.js +24 -2
- package/dist/cli/commands/worktree-lease-commands.js +4 -4
- package/dist/cli/index.js +10 -3
- package/dist/cli/program.js +5 -0
- package/dist/hooks/pre-tool-use-sub-agent.js +1 -1
- package/dist/services/adapter/adapter-registry.js +1 -1
- package/dist/services/artifacts/artifact-prerequisites.d.ts +38 -7
- package/dist/services/artifacts/artifact-prerequisites.js +130 -65
- package/dist/services/artifacts/artifact-service.js +1 -1
- package/dist/services/artifacts/request-artifact-service.d.ts +8 -0
- package/dist/services/artifacts/request-artifact-service.js +18 -8
- package/dist/services/artifacts/request-artifact-state-helpers.d.ts +57 -0
- package/dist/services/artifacts/request-artifact-state-helpers.js +91 -10
- package/dist/services/audit-independent/perf-audit-service.d.ts +9 -0
- package/dist/services/audit-independent/perf-audit-service.js +27 -5
- package/dist/services/audit-independent/security-audit-service.d.ts +12 -2
- package/dist/services/audit-independent/security-audit-service.js +28 -6
- package/dist/services/capability-guard-runner/contracts/J01.js +2 -1
- package/dist/services/capability-guard-runner/contracts/J02.js +3 -3
- package/dist/services/capability-guard-runner/contracts/J04.js +4 -2
- package/dist/services/capability-guard-runner/contracts/J07.js +2 -1
- package/dist/services/code/auto-compact-lifecycle.d.ts +130 -1
- package/dist/services/code/auto-compact-lifecycle.js +180 -4
- package/dist/services/code/auto-compact-orchestrator.d.ts +53 -9
- package/dist/services/code/auto-compact-orchestrator.js +166 -34
- package/dist/services/code/compact-event-settle.d.ts +122 -0
- package/dist/services/code/compact-event-settle.js +219 -0
- package/dist/services/code/orchestrator-can-do.d.ts +4 -2
- package/dist/services/code/orchestrator-can-do.js +37 -5
- package/dist/services/codegraph/codegraph-exclude-reconciler.js +2 -1
- package/dist/services/codegraph/codegraph-process-runner.js +3 -2
- package/dist/services/compact/request-transition-hook.js +5 -2
- package/dist/services/compact-history/compact-history-service.d.ts +75 -0
- package/dist/services/compact-history/compact-history-service.js +49 -0
- package/dist/services/config/config-restore.d.ts +12 -1
- package/dist/services/config/config-restore.js +35 -4
- package/dist/services/config/config-rollback.js +6 -1
- package/dist/services/config/config-safety.d.ts +52 -0
- package/dist/services/config/config-safety.js +75 -1
- package/dist/services/context/auto-compact-dispatcher.d.ts +7 -37
- package/dist/services/context/auto-compact-dispatcher.js +113 -40
- package/dist/services/context/auto-compact-reader.d.ts +68 -28
- package/dist/services/context/auto-compact-reader.js +155 -1
- package/dist/services/context/auto-compact-types.d.ts +89 -12
- package/dist/services/context/auto-compact-types.js +16 -32
- package/dist/services/context/harness-context-witness.d.ts +310 -0
- package/dist/services/context/harness-context-witness.js +606 -0
- package/dist/services/context/harness-window-config.d.ts +412 -0
- package/dist/services/context/harness-window-config.js +607 -0
- package/dist/services/context/main-session-monitor.d.ts +27 -0
- package/dist/services/context/main-session-monitor.js +32 -1
- package/dist/services/context/post-compact-reinjection.d.ts +221 -0
- package/dist/services/context/post-compact-reinjection.js +491 -0
- package/dist/services/dispatch/merge-back-runner.js +5 -5
- package/dist/services/dispatch/service-shutdown.js +3 -3
- package/dist/services/doc/doc-generator.js +2 -1
- package/dist/services/env/shell-probe.js +1 -1
- package/dist/services/evidence/evidence-generator.js +86 -49
- package/dist/services/final-review/final-review-service.d.ts +9 -0
- package/dist/services/final-review/final-review-service.js +36 -12
- package/dist/services/fuzzy-matching/fzf-pick-service.js +2 -0
- package/dist/services/hooks/auto-compact-hook-install.d.ts +10 -2
- package/dist/services/hooks/auto-compact-hook-install.js +8 -0
- package/dist/services/ide/adapters/claude-code-adapter.d.ts +107 -3
- package/dist/services/ide/adapters/claude-code-adapter.js +154 -7
- package/dist/services/ide/ide-registry.d.ts +31 -0
- package/dist/services/ide/ide-registry.js +35 -0
- package/dist/services/ide/ide-types.d.ts +59 -0
- package/dist/services/job/job-state-store.js +7 -0
- package/dist/services/lint/detect-eslint.js +2 -2
- package/dist/services/lint/eslint-runner.js +3 -1
- package/dist/services/loop/evaluator-dispatcher.js +2 -1
- package/dist/services/memory/project-memory-service/index/kind-dispatch.js +1 -1
- package/dist/services/memory/project-memory-service/store/paths.d.ts +9 -1
- package/dist/services/memory/project-memory-service/store/paths.js +15 -6
- package/dist/services/polyrepo/polyrepo-dispatcher.js +11 -0
- package/dist/services/prd/best-practice-auto-trigger.js +1 -0
- package/dist/services/prd/handoff-auto-regen.js +31 -27
- package/dist/services/prd/handoff-frontmatter.d.ts +44 -0
- package/dist/services/prd/handoff-frontmatter.js +75 -0
- package/dist/services/prd/handoff-service.d.ts +41 -2
- package/dist/services/prd/handoff-service.js +81 -8
- package/dist/services/prd/handoff-types.d.ts +3 -2
- package/dist/services/prd/handoff-types.js +3 -2
- package/dist/services/qa/qa-business-review-state.js +9 -0
- package/dist/services/release/version-precheck-service.d.ts +2 -1
- package/dist/services/release/version-precheck-service.js +82 -12
- package/dist/services/runtime/vendor-adapter.d.ts +29 -4
- package/dist/services/runtime/vendors/claude-code.js +1 -1
- package/dist/services/runtime/vendors/codex.js +1 -1
- package/dist/services/runtime/vendors/copilot.js +1 -1
- package/dist/services/sc/sc-service.js +1 -1
- package/dist/services/scan/diff-scope-service.js +2 -2
- package/dist/services/scan/file-size-scan.js +2 -2
- package/dist/services/scan/karpathy-service.js +2 -2
- package/dist/services/scan/orphan-service.js +2 -1
- package/dist/services/scan/type-sanity-service.js +2 -2
- package/dist/services/session/session-checkpoint-service.js +8 -0
- package/dist/services/skill/resume-detector.js +29 -11
- package/dist/services/skillhub/tar-runtime.js +1 -0
- package/dist/services/skills/hooks-codegate-superpowers.d.ts +14 -0
- package/dist/services/skills/hooks-codegate-superpowers.js +99 -3
- package/dist/services/skills/hooks-settings-service.d.ts +12 -0
- package/dist/services/skills/hooks-settings-service.js +91 -14
- package/dist/services/skills/session-start-hook-constants.d.ts +86 -0
- package/dist/services/skills/session-start-hook-constants.js +86 -0
- package/dist/services/skills/skill-presence-service.js +9 -0
- package/dist/services/skills/skill-statusline-service.d.ts +14 -0
- package/dist/services/slice/slice-check-service.js +31 -12
- package/dist/services/slice/slice-decompose-runners.js +2 -1
- package/dist/services/slice/slice-review-state.js +8 -0
- package/dist/services/upgrade/upgrade-service.js +1 -0
- package/dist/services/workflow/pipeline-verify-gate-support.d.ts +47 -10
- package/dist/services/workflow/pipeline-verify-gate-support.js +212 -93
- package/dist/services/workflow/pipeline-verify-service.js +24 -23
- package/dist/services/workflow/pipeline-verify-types.d.ts +10 -3
- package/dist/services/workflow/workflow-skip-service.js +2 -1
- package/dist/services/workspace/claude-settings-template.d.ts +56 -8
- package/dist/services/workspace/claude-settings-template.js +98 -20
- package/dist/services/workspace/migrate-service.js +1 -1
- package/dist/services/workspace/workspace-claude-settings-materializer.js +124 -9
- package/dist/services/workspace/workspace-service.js +8 -0
- package/dist/services/worktree/host-worktree-reconciler.js +1 -0
- package/dist/services/worktree/long-path-cleanup.js +3 -2
- package/dist/shared/process.js +1 -1
- package/package.json +6 -6
- package/scripts/install-skills.mjs +1 -0
- package/scripts/watch.mjs +3 -1
- package/skills/bee/peaks-perf-audit/SKILL.md +1 -1
- package/skills/bee/peaks-prd/SKILL.md +8 -6
- package/skills/bee/peaks-qa/SKILL.md +7 -7
- package/skills/bee/peaks-qa/references/qa-runbook.md +2 -2
- package/skills/bee/peaks-qa/references/qa-transition-gates.md +7 -7
- package/skills/bee/peaks-rd/SKILL.md +10 -8
- package/skills/bee/peaks-rd/references/artifact-per-request.md +2 -2
- package/skills/bee/peaks-rd/references/parallel-review-fanout.md +7 -5
- package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +13 -13
- package/skills/bee/peaks-rd/references/rd-runbook.md +9 -5
- package/skills/bee/peaks-rd/references/rd-transition-gates.md +9 -7
- package/skills/bee/peaks-rd/references/writing-handoff-frontmatter.md +6 -6
- package/skills/bee/peaks-reviewer/SKILL.md +1 -1
- package/skills/bee/peaks-sc/SKILL.md +1 -1
- package/skills/bee/peaks-security-audit/SKILL.md +1 -1
- package/skills/bee/peaks-txt/SKILL.md +1 -1
- package/skills/bee/peaks-ui/SKILL.md +1 -1
- package/skills/peaks-audit/SKILL.md +1 -1
- package/skills/peaks-code/SKILL.md +3 -3
- package/skills/peaks-code/references/a2a-artifact-mapping.md +3 -3
- package/skills/peaks-code/references/local-artifact-workspace.md +1 -1
- package/skills/peaks-code/references/resume-detection.md +13 -7
- package/skills/peaks-code/references/runbook.md +3 -2
- package/skills/peaks-code/references/session-overload-signal-index.md +2 -1
- package/skills/peaks-code/references/sub-agent-dispatch.md +1 -1
- package/skills/peaks-code/references/workflow-gates-and-types.md +8 -6
- package/skills/peaks-content/SKILL.md +1 -1
- package/skills/peaks-doctor/SKILL.md +1 -1
- package/skills/peaks-final-review/SKILL.md +1 -1
- package/skills/peaks-ide/SKILL.md +1 -1
- package/skills/peaks-issue-fix-orchestrator/SKILL.md +1 -1
- package/skills/peaks-resume/SKILL.md +1 -1
- package/skills/peaks-slice-decompose/SKILL.md +1 -1
- package/skills/peaks-solo/SKILL.md +1 -1
- package/skills/peaks-sop/SKILL.md +1 -1
- package/skills/peaks-status/SKILL.md +1 -1
- package/skills/peaks-test/SKILL.md +1 -1
|
@@ -14,22 +14,28 @@
|
|
|
14
14
|
* toolkit is "ready to use" so the
|
|
15
15
|
* LLM doesn't lose context to a
|
|
16
16
|
* last-second `/compact` panic.
|
|
17
|
-
* - 95% RED LINE — peaks-loop
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* keeps working with context < 95%.
|
|
17
|
+
* - 95% RED LINE — peaks-loop asks the harness to
|
|
18
|
+
* compact and says it is waiting.
|
|
19
|
+
* Nothing is blocked: peaks-loop has
|
|
20
|
+
* no executor for a running session,
|
|
21
|
+
* so it cannot gate dispatch — and
|
|
22
|
+
* claiming to was the deadlock. See
|
|
23
|
+
* the correction note below.
|
|
25
24
|
*
|
|
26
25
|
* Why 0.85 / 0.95 split: the LLM uses the 0.85–0.95 zone to do
|
|
27
26
|
* intelligent convergence — wait for in-flight sub-agents, finish
|
|
28
27
|
* the current todo row, persist a checkpoint, then compact. peaks-loop
|
|
29
|
-
* provides the toolkit; the LLM picks the moment. At 0.95
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* provides the toolkit; the LLM picks the moment. At 0.95 peaks-loop
|
|
29
|
+
* requests the compact outright and says it is waiting.
|
|
30
|
+
*
|
|
31
|
+
* Slice 2026-09-13-auto-compact-trigger-ownership corrected two claims that
|
|
32
|
+
* used to head this file: the red line does NOT "refuse to dispatch any
|
|
33
|
+
* further sub-agent" (peaks-loop has no way to compact a running session, so
|
|
34
|
+
* such a refusal gated nothing and deadlocked the runner), and the window
|
|
35
|
+
* these ratios divide by is now the same one peaks-loop configures for the
|
|
36
|
+
* harness — see `harness-window-config.ts`.
|
|
32
37
|
*/
|
|
38
|
+
import type { HarnessWindowSyncResult } from './harness-window-config.js';
|
|
33
39
|
export declare const AUTO_COMPACT_SOFT_WARN_RATIO = 0.5;
|
|
34
40
|
export declare const AUTO_COMPACT_AUTO_FIRE_RATIO = 0.8;
|
|
35
41
|
export declare const AUTO_COMPACT_PRE_COMPACT_RATIO = 0.85;
|
|
@@ -67,7 +73,10 @@ export type CompactTrigger = {
|
|
|
67
73
|
message: string;
|
|
68
74
|
toolkitReady: true;
|
|
69
75
|
}
|
|
70
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Red line (ratio ≥ 0.95): peaks-loop asks the harness to compact and says
|
|
78
|
+
* it is waiting. Dispatch is NOT blocked — see `redLineRequested`.
|
|
79
|
+
*/
|
|
71
80
|
| {
|
|
72
81
|
kind: 'red-line';
|
|
73
82
|
ratio: number;
|
|
@@ -97,6 +106,20 @@ export interface CompactDispatchResult {
|
|
|
97
106
|
readonly pathway: 'ide-native' | 'llm-self-compress' | 'shell-exec' | 'noop';
|
|
98
107
|
readonly message: string;
|
|
99
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Old → new map of envelope fields that were renamed but are still emitted
|
|
111
|
+
* under their old name as deprecated aliases.
|
|
112
|
+
*
|
|
113
|
+
* One definition, used both as the `deprecatedFields` value the envelopes
|
|
114
|
+
* carry and as the registry the type's `@deprecated` tags describe, so the
|
|
115
|
+
* alias and its record cannot drift apart into two lists.
|
|
116
|
+
*
|
|
117
|
+
* `redLineGated` → `redLineRequested` (slice
|
|
118
|
+
* 2026-09-13-auto-compact-trigger-ownership): nothing was ever gated. See
|
|
119
|
+
* `redLineGated` on the dispatch branches for why the alias is kept rather
|
|
120
|
+
* than deleted.
|
|
121
|
+
*/
|
|
122
|
+
export declare const DEPRECATED_ENVELOPE_FIELDS: Readonly<Record<string, string>>;
|
|
100
123
|
/** Final envelope returned by `runAutoCompact`. */
|
|
101
124
|
export type AutoCompactResult = {
|
|
102
125
|
readonly ok: false;
|
|
@@ -112,6 +135,13 @@ export type AutoCompactResult = {
|
|
|
112
135
|
readonly ratio: number;
|
|
113
136
|
readonly source: string;
|
|
114
137
|
readonly decision: 'below-threshold' | 'in-flight-batch';
|
|
138
|
+
/**
|
|
139
|
+
* Slice 2026-09-13-auto-compact-trigger-ownership: what syncing the
|
|
140
|
+
* harness auto-compact window did on this probe. `null` = the active
|
|
141
|
+
* adapter declares no such knob. Present so the write is VISIBLE —
|
|
142
|
+
* the harness reports an override silently, so peaks-loop must not.
|
|
143
|
+
*/
|
|
144
|
+
readonly harnessWindow?: HarnessWindowSyncResult | null;
|
|
115
145
|
};
|
|
116
146
|
} | {
|
|
117
147
|
readonly ok: boolean;
|
|
@@ -139,7 +169,54 @@ export type AutoCompactResult = {
|
|
|
139
169
|
* caller passes `--mode partial`.
|
|
140
170
|
*/
|
|
141
171
|
readonly mode?: 'standard' | 'partial';
|
|
172
|
+
/**
|
|
173
|
+
* True when the ratio had crossed the red line and peaks-loop asked
|
|
174
|
+
* the harness to compact.
|
|
175
|
+
*
|
|
176
|
+
* Renamed from `redLineGated` in slice
|
|
177
|
+
* 2026-09-13-auto-compact-trigger-ownership: nothing is gated. The
|
|
178
|
+
* old name asserted a block peaks-loop cannot enforce (it has no
|
|
179
|
+
* executor for a running session), and acting on that assertion is
|
|
180
|
+
* what deadlocked the runner.
|
|
181
|
+
*/
|
|
182
|
+
readonly redLineRequested?: boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Deprecated alias of `redLineRequested` — same value, written by the
|
|
185
|
+
* same statement, so the two can never disagree.
|
|
186
|
+
*
|
|
187
|
+
* Kept because the old name SHIPPED: `redLineGated` is in every release
|
|
188
|
+
* from 2.13.0 through 4.0.46, so a script outside this repo that reads
|
|
189
|
+
* it exists in the wild. Dropping the key would hand that script
|
|
190
|
+
* `undefined` instead of the boolean it branches on — no error, no log,
|
|
191
|
+
* just a silently different branch. This is the same call this slice
|
|
192
|
+
* already made for `--bypass-red-line`: stop advertising a
|
|
193
|
+
* wrong-named surface, do not delete it out from under a published
|
|
194
|
+
* caller. That flag is likewise kept with nothing reading it.
|
|
195
|
+
*
|
|
196
|
+
* There is deliberately NO removal date here. A date would be a promise
|
|
197
|
+
* with no mechanism behind it; removing a published field is a MAJOR
|
|
198
|
+
* decision to be taken on purpose, not one this comment can schedule.
|
|
199
|
+
*
|
|
200
|
+
* `deprecatedFields` (below) is the part a runtime consumer can
|
|
201
|
+
* actually see — a `@deprecated` tag is not.
|
|
202
|
+
*/
|
|
142
203
|
readonly redLineGated?: boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Old → new map of the envelope fields still emitted as deprecated
|
|
206
|
+
* aliases, so a consumer that parses JSON can discover the rename
|
|
207
|
+
* without reading this file.
|
|
208
|
+
*
|
|
209
|
+
* Why it has to exist: the consumers a rename breaks are precisely the
|
|
210
|
+
* ones that never read `auto-compact-types.ts`. A type comment reaches
|
|
211
|
+
* the compiler and the next editor, not a script parsing a CLI
|
|
212
|
+
* envelope, which is what made the rename silent in the first place.
|
|
213
|
+
* This puts the fact in the envelope they already read.
|
|
214
|
+
*
|
|
215
|
+
* Absent on the branches that carry no renamed field.
|
|
216
|
+
*/
|
|
217
|
+
readonly deprecatedFields?: Readonly<Record<string, string>>;
|
|
218
|
+
/** See the `AUTO_COMPACT_SKIP` data shape above. */
|
|
219
|
+
readonly harnessWindow?: HarnessWindowSyncResult | null;
|
|
143
220
|
};
|
|
144
221
|
};
|
|
145
222
|
/**
|
|
@@ -1,35 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-compact shared types (v2.13.0 AC-1..AC-4).
|
|
3
|
-
*
|
|
4
|
-
* Two-tier threshold model — peaks-loop is project-aware and the LLM
|
|
5
|
-
* is the decision-maker:
|
|
6
|
-
*
|
|
7
|
-
* - 50% soft warn — log a one-line info row; continue.
|
|
8
|
-
* - 85% pre-compact zone — peaks-loop prepares the convergence
|
|
9
|
-
* toolkit (checkpoint + convergence
|
|
10
|
-
* plan + auto-decisions log +
|
|
11
|
-
* IDE-compact dispatcher). The LLM
|
|
12
|
-
* DECIDES when (or whether) to fire
|
|
13
|
-
* compact during this zone. The
|
|
14
|
-
* toolkit is "ready to use" so the
|
|
15
|
-
* LLM doesn't lose context to a
|
|
16
|
-
* last-second `/compact` panic.
|
|
17
|
-
* - 95% RED LINE — peaks-loop refuses to dispatch any
|
|
18
|
-
* further sub-agent and synchronously
|
|
19
|
-
* triggers IDE-side compact. At 95%+
|
|
20
|
-
* the context window is too tight to
|
|
21
|
-
* continue safely; the LLM cannot
|
|
22
|
-
* opt out. This is the compact red
|
|
23
|
-
* line that guarantees the LLM-runner
|
|
24
|
-
* keeps working with context < 95%.
|
|
25
|
-
*
|
|
26
|
-
* Why 0.85 / 0.95 split: the LLM uses the 0.85–0.95 zone to do
|
|
27
|
-
* intelligent convergence — wait for in-flight sub-agents, finish
|
|
28
|
-
* the current todo row, persist a checkpoint, then compact. peaks-loop
|
|
29
|
-
* provides the toolkit; the LLM picks the moment. At 0.95 the window
|
|
30
|
-
* is gone and peaks-loop takes over synchronously to keep the runner
|
|
31
|
-
* alive.
|
|
32
|
-
*/
|
|
33
1
|
export const AUTO_COMPACT_SOFT_WARN_RATIO = 0.5;
|
|
34
2
|
// Part 22: auto-fire threshold (was 0.85 pre-compact zone where
|
|
35
3
|
// the LLM had to decide; LLM misjudged 0.85–0.95 and only fired
|
|
@@ -41,3 +9,19 @@ export const AUTO_COMPACT_AUTO_FIRE_RATIO = 0.80;
|
|
|
41
9
|
export const AUTO_COMPACT_PRE_COMPACT_RATIO = 0.85;
|
|
42
10
|
export const AUTO_COMPACT_RED_LINE_RATIO = 0.95;
|
|
43
11
|
export const AUTO_COMPACT_THRESHOLD_RATIO = AUTO_COMPACT_AUTO_FIRE_RATIO;
|
|
12
|
+
/**
|
|
13
|
+
* Old → new map of envelope fields that were renamed but are still emitted
|
|
14
|
+
* under their old name as deprecated aliases.
|
|
15
|
+
*
|
|
16
|
+
* One definition, used both as the `deprecatedFields` value the envelopes
|
|
17
|
+
* carry and as the registry the type's `@deprecated` tags describe, so the
|
|
18
|
+
* alias and its record cannot drift apart into two lists.
|
|
19
|
+
*
|
|
20
|
+
* `redLineGated` → `redLineRequested` (slice
|
|
21
|
+
* 2026-09-13-auto-compact-trigger-ownership): nothing was ever gated. See
|
|
22
|
+
* `redLineGated` on the dispatch branches for why the alias is kept rather
|
|
23
|
+
* than deleted.
|
|
24
|
+
*/
|
|
25
|
+
export const DEPRECATED_ENVELOPE_FIELDS = {
|
|
26
|
+
redLineGated: 'redLineRequested'
|
|
27
|
+
};
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import type { StatusLineStdin } from '../skills/skill-statusline-service.js';
|
|
2
|
+
export declare const HARNESS_CONTEXT_WITNESS_FILE = "harness-context-witness.json";
|
|
3
|
+
export declare const WITNESS_SCHEMA_VERSION = 2;
|
|
4
|
+
/**
|
|
5
|
+
* Contributor 1 of the budget: the harness reports a rounded percentage.
|
|
6
|
+
* "Pre-calculated percentage of context window used" is not documented as
|
|
7
|
+
* fractional, so the conservative reading is an integer percent, i.e. up to
|
|
8
|
+
* ±0.5 percentage points = ±0.005 of the window. If the real payload turns out
|
|
9
|
+
* to carry decimals this term shrinks tenfold and the guard gets sharper; the
|
|
10
|
+
* assumption is visible in the first witness file, where
|
|
11
|
+
* `usageTokens / modelWindowTokens` and `usedPercentage` must agree.
|
|
12
|
+
*/
|
|
13
|
+
export declare const WITNESS_PERCENT_ROUNDING_FRACTION = 0.005;
|
|
14
|
+
/**
|
|
15
|
+
* Contributor 2: the two sides do not sum exactly the same token quantities.
|
|
16
|
+
* MEASURED, not guessed — on 2026-09-13 the harness's own pre-compact count
|
|
17
|
+
* (963,306 tokens) exceeded peaks-loop's transcript estimate (961,658) by
|
|
18
|
+
* 1,648 tokens = 0.171%. Used as a fraction of the tokens, not of the window.
|
|
19
|
+
*/
|
|
20
|
+
export declare const WITNESS_NUMERATOR_FRACTION = 0.0017;
|
|
21
|
+
/**
|
|
22
|
+
* The smallest window difference this guard claims to detect, and therefore
|
|
23
|
+
* the yardstick for "is this sample sharp enough to answer at all".
|
|
24
|
+
*
|
|
25
|
+
* 3% is not arbitrary: the harness compacts a native-1M model at ~967,000 by
|
|
26
|
+
* default while peaks-loop writes the model ceiling, so 1,000,000 vs 967,000 —
|
|
27
|
+
* a 3.3% difference — is the smallest real-world disagreement between the two
|
|
28
|
+
* denominators today. Anything the guard reports as "agree" while its own
|
|
29
|
+
* budget is wider than that difference is a claim it cannot support.
|
|
30
|
+
*
|
|
31
|
+
* NOTE (repair cycle 2): the constant is a floor on the SAMPLE, and the sample
|
|
32
|
+
* it floors is the WITNESS's, not peaks-loop's — a gap of this size leaves a
|
|
33
|
+
* residual proportional to the witness's ratio, so a stale (or low) witness
|
|
34
|
+
* shrinks the signal while the budget's rounding term does not shrink with it.
|
|
35
|
+
* See `sampleSupportsAgreement`, which is where this constant is applied. At
|
|
36
|
+
* zero skew it was already correctly calibrated (measured onset 0.176 of the
|
|
37
|
+
* window against a predicted 0.177); the fault was that only zero skew was
|
|
38
|
+
* correctly calibrated.
|
|
39
|
+
*/
|
|
40
|
+
export declare const MIN_DETECTABLE_WINDOW_DIFFERENCE = 0.03;
|
|
41
|
+
/**
|
|
42
|
+
* Which of the two in-range readings of a raw percentage was taken, or that
|
|
43
|
+
* neither could be taken. `unestablished` is not a reading: the payload said a
|
|
44
|
+
* percentage and carried nothing that could settle which scale it is on.
|
|
45
|
+
*/
|
|
46
|
+
export type PercentageUnit = 'fraction' | 'percent' | 'unestablished';
|
|
47
|
+
/**
|
|
48
|
+
* A harness context snapshot, as written to
|
|
49
|
+
* `<projectRoot>/.peaks/_runtime/<sessionId>/harness-context-witness.json`.
|
|
50
|
+
*
|
|
51
|
+
* The path is peaks-loop's own session runtime directory — deliberately NOT
|
|
52
|
+
* the harness's settings file or any other harness-owned location.
|
|
53
|
+
*/
|
|
54
|
+
export interface HarnessContextWitness {
|
|
55
|
+
readonly schemaVersion: number;
|
|
56
|
+
/** When peaks-loop received the render payload. */
|
|
57
|
+
readonly capturedAt: string;
|
|
58
|
+
/**
|
|
59
|
+
* `context_window.used_percentage`, normalised to 0..1. `null` when this
|
|
60
|
+
* render's payload had no percentage this module could read — the record is
|
|
61
|
+
* still written, because "a render happened and carried nothing usable" is
|
|
62
|
+
* a different fact from "nothing rendered", and the two are told apart by
|
|
63
|
+
* exactly this file existing or not.
|
|
64
|
+
*/
|
|
65
|
+
readonly usedPercentage: number | null;
|
|
66
|
+
/**
|
|
67
|
+
* The payload's `used_percentage` VERBATIM, before any normalisation. Kept
|
|
68
|
+
* so a human can tell a unit misread from a real disagreement: the number
|
|
69
|
+
* above is a 0..1 fraction and this one is not, and only this one shows what
|
|
70
|
+
* the harness actually said.
|
|
71
|
+
*/
|
|
72
|
+
readonly usedPercentageRaw: number | null;
|
|
73
|
+
/** Which reading `usedPercentage` was normalised from, or why none was taken. */
|
|
74
|
+
readonly usedPercentageUnit: PercentageUnit | null;
|
|
75
|
+
/** `context_window.context_window_size`. Recorded, never the comparison's denominator. */
|
|
76
|
+
readonly modelWindowTokens: number | null;
|
|
77
|
+
/**
|
|
78
|
+
* Sum of the prompt-side `context_window.current_usage` components. This is
|
|
79
|
+
* the ALIGNMENT KEY: comparing it with a probe's `rawTokens` is how the
|
|
80
|
+
* guard learns whether the two numbers describe the same API response, and
|
|
81
|
+
* therefore how much of any difference is sampling skew rather than a real
|
|
82
|
+
* disagreement. `output_tokens` is excluded so the sum is the same quantity
|
|
83
|
+
* peaks-loop's transcript estimate sums.
|
|
84
|
+
*/
|
|
85
|
+
readonly usageTokens: number | null;
|
|
86
|
+
/** The harness session id from the payload, for the foreign-session check. */
|
|
87
|
+
readonly outerSessionId: string | null;
|
|
88
|
+
}
|
|
89
|
+
export type WitnessVerdict = 'absent' | 'foreign-session' | 'unverifiable' | 'agree' | 'disagree';
|
|
90
|
+
/**
|
|
91
|
+
* Why there is no witness to compare. The first two leave the same file system
|
|
92
|
+
* state — no file — so the caller, which knows the session directory, resolves
|
|
93
|
+
* which applies and this module turns it into a sentence.
|
|
94
|
+
*
|
|
95
|
+
* The third (`unreadable`, repair cycle 3) is decided by the READ, not by the
|
|
96
|
+
* directory, and it is the reason `readHarnessWitness` returns a tagged union
|
|
97
|
+
* rather than `null`: a file that exists but cannot be used is evidence that a
|
|
98
|
+
* render DID happen, so calling it `not-rendered` names the wrong cause — the
|
|
99
|
+
* exact mis-attribution §17-B was written to remove.
|
|
100
|
+
*/
|
|
101
|
+
export type WitnessAbsentCause = 'session-dir-missing' | 'not-rendered' | 'unreadable';
|
|
102
|
+
export interface HarnessWitnessComparison {
|
|
103
|
+
readonly verdict: WitnessVerdict;
|
|
104
|
+
/** Why the verdict is what it is, when it is not `agree` / `disagree`. */
|
|
105
|
+
readonly reason: string | null;
|
|
106
|
+
readonly harnessPct: number | null;
|
|
107
|
+
readonly peaksRatio: number;
|
|
108
|
+
/** `peaksRatio - harnessPct`; `null` when the harness side is unknown. */
|
|
109
|
+
readonly deviation: number | null;
|
|
110
|
+
/**
|
|
111
|
+
* The quantity the verdict is actually decided on: `deviation` minus the
|
|
112
|
+
* sampling skew the two token snapshots measure. Equal denominators put this
|
|
113
|
+
* at zero no matter how stale the witness is, so a non-zero residual is the
|
|
114
|
+
* window difference itself rather than the sample's age.
|
|
115
|
+
*/
|
|
116
|
+
readonly residual: number | null;
|
|
117
|
+
/** The budget `residual` was tested against, as a fraction of the window. */
|
|
118
|
+
readonly tolerance: number | null;
|
|
119
|
+
readonly witnessedAt: string | null;
|
|
120
|
+
readonly witnessTokens: number | null;
|
|
121
|
+
readonly peaksTokens: number | null;
|
|
122
|
+
/** The harness's raw `used_percentage`, verbatim — see `usedPercentageRaw`. */
|
|
123
|
+
readonly witnessRawPercentage: number | null;
|
|
124
|
+
/** Which reading produced `harnessPct`, so a unit misread is visible. */
|
|
125
|
+
readonly witnessPercentageUnit: PercentageUnit | null;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Read the harness's context block out of a parsed statusline stdin payload.
|
|
129
|
+
* Returns `null` only when there was no payload at all (a render on a TTY, or
|
|
130
|
+
* a manual `peaks statusline`) — in which case there is nothing to record and
|
|
131
|
+
* nothing to say. A payload that arrived but carried no usable percentage is
|
|
132
|
+
* recorded, not dropped.
|
|
133
|
+
*/
|
|
134
|
+
export declare function parseHarnessWitness(input: {
|
|
135
|
+
readonly stdin: StatusLineStdin | null;
|
|
136
|
+
readonly nowMs: number;
|
|
137
|
+
}): HarnessContextWitness | null;
|
|
138
|
+
export declare function harnessWitnessPath(projectRoot: string, sessionId: string): string;
|
|
139
|
+
/**
|
|
140
|
+
* Write this render's record. Returns whether a file was written.
|
|
141
|
+
*
|
|
142
|
+
* This is the ONLY side effect on the statusline render path. It is bounded
|
|
143
|
+
* (one small file, inside peaks-loop's own session directory), it cannot
|
|
144
|
+
* influence any decision peaks-loop makes (nothing except the diagnostic in
|
|
145
|
+
* `peaks code context-now` reads it), and it never throws — a statusline that
|
|
146
|
+
* fails to render because an observability file could not be written would be
|
|
147
|
+
* a worse failure than a missing observation. A failed write is not swallowed:
|
|
148
|
+
* the next probe reports `absent`, which is the visible symptom.
|
|
149
|
+
*
|
|
150
|
+
* No `mkdir`: the session directory is created by the session layer, and a
|
|
151
|
+
* statusline render is the wrong place to be creating directories. Its absence
|
|
152
|
+
* is one of the honest reasons the witness can be missing.
|
|
153
|
+
*
|
|
154
|
+
* Written via temp + rename, not in place. A reader runs in ANOTHER process
|
|
155
|
+
* (the probe) and treats unreadable JSON as "no witness" — so a torn read
|
|
156
|
+
* would not be an error, it would be a SILENT loss of the comparison, which is
|
|
157
|
+
* the failure mode this whole slice exists to remove. The rename makes the
|
|
158
|
+
* partial state unobservable. Same shape as `atomicWriteJson` in
|
|
159
|
+
* statusline-settings-service.ts.
|
|
160
|
+
*
|
|
161
|
+
* The rename is not always available: on Windows it throws EPERM while another
|
|
162
|
+
* process holds the target open, which is exactly the reader this guard is
|
|
163
|
+
* written for. Dropping the sample then would be the silent loss the temp
|
|
164
|
+
* rename was introduced to remove, so the write falls back to in-place. The
|
|
165
|
+
* fallback gives up atomicity for that one write — the reader may see a
|
|
166
|
+
* half-written file and call it "no witness" — which is a narrower failure
|
|
167
|
+
* than never recording the sample at all, and the temp path stays the normal
|
|
168
|
+
* one.
|
|
169
|
+
*
|
|
170
|
+
* A LOWER-INFORMATION RECORD DOES NOT REPLACE A HIGHER-INFORMATION ONE (repair
|
|
171
|
+
* cycle 3). The render that carries no readable percentage is still recorded
|
|
172
|
+
* when there is nothing better on disk — that is what tells "rendered, nothing
|
|
173
|
+
* usable" apart from "never rendered" (§17-B) — but it does NOT overwrite a
|
|
174
|
+
* record whose percentage IS readable. Overwriting silences a real comparison:
|
|
175
|
+
* measured 2026-09-14, a witness giving a real 3.3% window gap reported
|
|
176
|
+
* `disagree` with its sentence, and one render whose payload lacked
|
|
177
|
+
* `context_window` turned the same comparison into `unverifiable` with the
|
|
178
|
+
* sentence suppressed. A render that says less must not delete a sample that
|
|
179
|
+
* says more.
|
|
180
|
+
*
|
|
181
|
+
* AND THE PARSE IS INSIDE A TRY (repair cycle 3). The module's contract at the
|
|
182
|
+
* top of this comment — "it never throws" — was true only by inspection: the
|
|
183
|
+
* `parseHarnessWitness` call sat above the only `try`, and a payload shape the
|
|
184
|
+
* guards did not anticipate escaped the function and killed the render (see
|
|
185
|
+
* `parseHarnessWitness` for the measured case). A nested `try` rather than a
|
|
186
|
+
* wider one, because the outer catch says something different: it is the
|
|
187
|
+
* temp+rename FALLBACK, and a parse failure has no JSON to fall back TO.
|
|
188
|
+
* A payload this module cannot read is a missing observation, which is the
|
|
189
|
+
* outcome the contract already prefers.
|
|
190
|
+
*/
|
|
191
|
+
export declare function writeHarnessWitness(input: {
|
|
192
|
+
readonly projectRoot: string | null;
|
|
193
|
+
readonly sessionId: string | null;
|
|
194
|
+
readonly stdin: StatusLineStdin | null;
|
|
195
|
+
readonly nowMs: number;
|
|
196
|
+
}): boolean;
|
|
197
|
+
/**
|
|
198
|
+
* What a read found.
|
|
199
|
+
*
|
|
200
|
+
* This used to be `HarnessContextWitness | null`, which collapsed FOUR file
|
|
201
|
+
* states — absent, unreadable, not JSON, not the shape this module writes —
|
|
202
|
+
* into one `null`. The caller then had to guess the cause from the directory
|
|
203
|
+
* alone and picked `not-rendered` whenever the directory existed, so a render
|
|
204
|
+
* whose record was merely unreadable was reported as "the statusline has not
|
|
205
|
+
* rendered here": the wrong-cause sentence §17-B was written to remove. The
|
|
206
|
+
* three states are named here instead of re-derived by a second look at the
|
|
207
|
+
* disk. Same shape, and deliberately the same words (`missing` / `valid` /
|
|
208
|
+
* `invalid`), as `CompactLifecycleRead` in
|
|
209
|
+
* `src/services/compact-statusline/compact-lifecycle-store.ts`, which reads a
|
|
210
|
+
* sibling file in the same directory for the same purpose.
|
|
211
|
+
*/
|
|
212
|
+
export type HarnessWitnessRead = {
|
|
213
|
+
readonly kind: 'missing';
|
|
214
|
+
} | {
|
|
215
|
+
readonly kind: 'valid';
|
|
216
|
+
readonly witness: HarnessContextWitness;
|
|
217
|
+
} | {
|
|
218
|
+
readonly kind: 'invalid';
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* Read the record for a session. Anything that exists but cannot be used as a
|
|
222
|
+
* record reads as `invalid` — never as `missing`, which would erase the one
|
|
223
|
+
* fact the file's existence carries: a render happened here.
|
|
224
|
+
*/
|
|
225
|
+
export declare function readHarnessWitness(input: {
|
|
226
|
+
readonly projectRoot: string;
|
|
227
|
+
readonly sessionId: string;
|
|
228
|
+
}): HarnessWitnessRead;
|
|
229
|
+
/**
|
|
230
|
+
* The budget, in tokens: what a difference between the two ratios can be
|
|
231
|
+
* explained by WITHOUT the two denominators being different, once the
|
|
232
|
+
* sampling skew has been taken out (see `compareHarnessWitness`).
|
|
233
|
+
*
|
|
234
|
+
* rounding 0.005 x window — the harness's percentage is rounded
|
|
235
|
+
* numerator 0.0017 x usedTokens — measured disagreement of the two sums
|
|
236
|
+
*
|
|
237
|
+
* Sample skew is deliberately NOT a term here. It is not a budget at all: it
|
|
238
|
+
* is MEASURED per sample from the two token counts and SUBTRACTED from the
|
|
239
|
+
* deviation, because under equal denominators the token difference and the
|
|
240
|
+
* ratio difference are the same number — see `compareHarnessWitness`.
|
|
241
|
+
*/
|
|
242
|
+
export declare function witnessToleranceTokens(input: {
|
|
243
|
+
readonly windowTokens: number;
|
|
244
|
+
readonly usedTokens: number;
|
|
245
|
+
}): number;
|
|
246
|
+
/**
|
|
247
|
+
* Compare peaks-loop's ratio with the harness's own percentage.
|
|
248
|
+
*
|
|
249
|
+
* Two identifiers must match before any comparison is allowed:
|
|
250
|
+
* 1. the SESSION — a witness written by another harness session on the same
|
|
251
|
+
* project is not evidence about this one. Lenient in the same direction as
|
|
252
|
+
* the compact-event attribution: refused only when BOTH ids resolve and
|
|
253
|
+
* differ, because a guard that turns a missing field into a permanent
|
|
254
|
+
* "cannot tell" is the failure this whole slice exists to remove.
|
|
255
|
+
* 2. the MOMENT — a witness is a snapshot, and the harness documents that its
|
|
256
|
+
* percentage depends on when it was calculated. The token counts are what
|
|
257
|
+
* says whether the two numbers came from the same API response.
|
|
258
|
+
*
|
|
259
|
+
* THE MOMENT IS REMOVED, NOT BUDGETED (repair cycle 1). If the two ratios
|
|
260
|
+
* share a denominator W, the harness's count and peaks-loop's count differ by
|
|
261
|
+
* exactly the sampling skew, so
|
|
262
|
+
*
|
|
263
|
+
* peaksRatio - harnessPct == (peaksTokens - witnessTokens) / W
|
|
264
|
+
*
|
|
265
|
+
* holds identically — it is not a tolerance to be granted, it is an equality
|
|
266
|
+
* to be tested. Budgeting the skew instead (`tolerance += |skew|`) made the
|
|
267
|
+
* budget grow by `x` while the deviation grew by `x(1+g)`, so a real window
|
|
268
|
+
* difference `g` was cancelled for every skew large enough to absorb it: a
|
|
269
|
+
* measured 3.3% denominator gap read `agree` for skew in [12,400, 22,100]
|
|
270
|
+
* tokens, and gaps up to 5.3% never surfaced at all. Subtracting the skew from
|
|
271
|
+
* the deviation and testing the remainder against a budget that contains only
|
|
272
|
+
* rounding and numerator disagreement makes the test invariant to skew by
|
|
273
|
+
* construction, and leaves `-g x harnessPct/(1+g)` — the window difference
|
|
274
|
+
* itself — as the only thing the residual can be.
|
|
275
|
+
*
|
|
276
|
+
* AND THE RESIDUAL'S SIZE IS THE WITNESS'S, NOT PEAKS-LOOP'S (repair cycle 2).
|
|
277
|
+
* `-g x harnessPct/(1+g)` carries the witness's ratio, so a witness captured
|
|
278
|
+
* while the session was small cannot show a window difference that a bigger
|
|
279
|
+
* witness would. The three answers therefore do not share one gate: a residual
|
|
280
|
+
* past the budget is a `disagree` at any witness size, while `agree` needs the
|
|
281
|
+
* sample to be sharp enough to support it (`sampleSupportsAgreement`).
|
|
282
|
+
*/
|
|
283
|
+
export declare function compareHarnessWitness(input: {
|
|
284
|
+
readonly witness: HarnessContextWitness | null;
|
|
285
|
+
readonly peaksRatio: number;
|
|
286
|
+
readonly peaksTokens: number | null;
|
|
287
|
+
readonly peaksWindowTokens: number | null;
|
|
288
|
+
readonly outerSessionId: string | null;
|
|
289
|
+
readonly absentCause?: WitnessAbsentCause;
|
|
290
|
+
}): HarnessWitnessComparison;
|
|
291
|
+
/**
|
|
292
|
+
* One-way sentence for a disagreeing witness. Advising, never asking: an
|
|
293
|
+
* auto-compact observation must never become an `AskUserQuestion` (see
|
|
294
|
+
* `.peaks/memory/auto-compact-threshold-policy.md`).
|
|
295
|
+
*
|
|
296
|
+
* The sentence states the quantity that decided it (the residual, after the
|
|
297
|
+
* measured skew was removed) and the raw harness value with the reading that
|
|
298
|
+
* was taken from it. Both are there so a reader can tell a real denominator
|
|
299
|
+
* difference from a unit misread without going back to the file.
|
|
300
|
+
*/
|
|
301
|
+
export declare function describeHarnessWitness(comparison: HarnessWitnessComparison): string | null;
|
|
302
|
+
/** Convenience for the CLI: read + compare in one call. */
|
|
303
|
+
export declare function readAndCompareHarnessWitness(input: {
|
|
304
|
+
readonly projectRoot: string;
|
|
305
|
+
readonly sessionId: string;
|
|
306
|
+
readonly peaksRatio: number;
|
|
307
|
+
readonly peaksTokens: number | null;
|
|
308
|
+
readonly peaksWindowTokens: number | null;
|
|
309
|
+
readonly outerSessionId: string | null;
|
|
310
|
+
}): HarnessWitnessComparison;
|