peaks-loop 4.0.45 → 4.0.47

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 (137) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README-en.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/cli/commands/code-runtime-commands.d.ts +22 -0
  5. package/dist/cli/commands/code-runtime-commands.js +128 -34
  6. package/dist/cli/commands/compact-command.js +129 -1
  7. package/dist/cli/commands/container-commands.js +3 -3
  8. package/dist/cli/commands/core/skill-command.js +45 -10
  9. package/dist/cli/commands/cron-commands.js +2 -1
  10. package/dist/cli/commands/e2e-verify.js +3 -3
  11. package/dist/cli/commands/governance-classify-contract-commands.js +1 -0
  12. package/dist/cli/commands/hooks-commands.js +10 -1
  13. package/dist/cli/commands/loop-commands.js +1 -0
  14. package/dist/cli/commands/playwright-commands.js +2 -1
  15. package/dist/cli/commands/reinject-command.d.ts +72 -0
  16. package/dist/cli/commands/reinject-command.js +174 -0
  17. package/dist/cli/commands/request-commands.js +6 -3
  18. package/dist/cli/commands/shadcn-commands.js +1 -0
  19. package/dist/cli/commands/test-commands.js +2 -1
  20. package/dist/cli/commands/vm-commands.js +7 -7
  21. package/dist/cli/commands/workspace/init-command.js +24 -2
  22. package/dist/cli/commands/worktree-lease-commands.js +4 -4
  23. package/dist/cli/index.js +49 -2
  24. package/dist/cli/program.js +5 -0
  25. package/dist/hooks/pre-tool-use-sub-agent.js +1 -1
  26. package/dist/services/adapter/adapter-registry.js +1 -1
  27. package/dist/services/artifacts/artifact-service.js +1 -1
  28. package/dist/services/capability-guard-runner/contracts/J01.js +2 -1
  29. package/dist/services/capability-guard-runner/contracts/J02.js +3 -3
  30. package/dist/services/capability-guard-runner/contracts/J04.js +4 -2
  31. package/dist/services/capability-guard-runner/contracts/J07.js +2 -1
  32. package/dist/services/code/auto-compact-lifecycle.d.ts +50 -4
  33. package/dist/services/code/auto-compact-lifecycle.js +52 -10
  34. package/dist/services/code/auto-compact-orchestrator.d.ts +53 -9
  35. package/dist/services/code/auto-compact-orchestrator.js +173 -43
  36. package/dist/services/code/orchestrator-can-do.d.ts +4 -2
  37. package/dist/services/code/orchestrator-can-do.js +37 -5
  38. package/dist/services/codegraph/codegraph-exclude-reconciler.js +2 -1
  39. package/dist/services/codegraph/codegraph-process-runner.js +3 -2
  40. package/dist/services/compact/request-transition-hook.js +5 -2
  41. package/dist/services/compact-history/compact-history-service.d.ts +61 -0
  42. package/dist/services/compact-history/compact-history-service.js +49 -0
  43. package/dist/services/compact-statusline/compact-lifecycle-store.d.ts +11 -2
  44. package/dist/services/compact-statusline/compact-lifecycle-store.js +19 -1
  45. package/dist/services/compact-statusline/compact-statusline-service.d.ts +1 -1
  46. package/dist/services/compact-statusline/compact-statusline-service.js +22 -0
  47. package/dist/services/config/config-safety.d.ts +52 -0
  48. package/dist/services/config/config-safety.js +75 -1
  49. package/dist/services/context/auto-compact-dispatcher.d.ts +7 -37
  50. package/dist/services/context/auto-compact-dispatcher.js +113 -40
  51. package/dist/services/context/auto-compact-reader.d.ts +68 -28
  52. package/dist/services/context/auto-compact-reader.js +155 -1
  53. package/dist/services/context/auto-compact-types.d.ts +89 -12
  54. package/dist/services/context/auto-compact-types.js +16 -32
  55. package/dist/services/context/harness-window-config.d.ts +412 -0
  56. package/dist/services/context/harness-window-config.js +607 -0
  57. package/dist/services/context/main-session-monitor.d.ts +27 -0
  58. package/dist/services/context/main-session-monitor.js +32 -1
  59. package/dist/services/context/post-compact-reinjection.d.ts +221 -0
  60. package/dist/services/context/post-compact-reinjection.js +491 -0
  61. package/dist/services/dispatch/merge-back-runner.js +5 -5
  62. package/dist/services/dispatch/service-shutdown.js +3 -3
  63. package/dist/services/doc/doc-generator.js +2 -1
  64. package/dist/services/env/shell-probe.js +1 -1
  65. package/dist/services/fuzzy-matching/fzf-pick-service.js +2 -0
  66. package/dist/services/hooks/auto-compact-hook-install.d.ts +10 -2
  67. package/dist/services/hooks/auto-compact-hook-install.js +8 -0
  68. package/dist/services/ide/adapters/claude-code-adapter.d.ts +107 -3
  69. package/dist/services/ide/adapters/claude-code-adapter.js +154 -7
  70. package/dist/services/ide/ide-registry.d.ts +12 -0
  71. package/dist/services/ide/ide-registry.js +14 -0
  72. package/dist/services/ide/ide-types.d.ts +59 -0
  73. package/dist/services/lint/detect-eslint.js +2 -2
  74. package/dist/services/lint/eslint-runner.js +3 -1
  75. package/dist/services/loop/evaluator-dispatcher.js +2 -1
  76. package/dist/services/memory/project-memory-service/index/kind-dispatch.js +1 -1
  77. package/dist/services/memory/project-memory-service/store/paths.d.ts +9 -1
  78. package/dist/services/memory/project-memory-service/store/paths.js +15 -6
  79. package/dist/services/prd/best-practice-auto-trigger.js +1 -0
  80. package/dist/services/release/version-precheck-service.d.ts +2 -1
  81. package/dist/services/release/version-precheck-service.js +82 -12
  82. package/dist/services/runtime/vendor-adapter.d.ts +29 -4
  83. package/dist/services/runtime/vendors/claude-code.js +1 -1
  84. package/dist/services/runtime/vendors/codex.js +1 -1
  85. package/dist/services/runtime/vendors/copilot.js +1 -1
  86. package/dist/services/sc/sc-service.js +1 -1
  87. package/dist/services/scan/diff-scope-service.js +2 -2
  88. package/dist/services/scan/file-size-scan.js +2 -2
  89. package/dist/services/scan/orphan-service.js +2 -1
  90. package/dist/services/scan/type-sanity-service.js +2 -2
  91. package/dist/services/skillhub/tar-runtime.js +1 -0
  92. package/dist/services/skills/hooks-codegate-superpowers.d.ts +8 -0
  93. package/dist/services/skills/hooks-codegate-superpowers.js +40 -3
  94. package/dist/services/skills/hooks-settings-service.d.ts +12 -0
  95. package/dist/services/skills/hooks-settings-service.js +77 -10
  96. package/dist/services/skills/session-start-hook-constants.d.ts +41 -0
  97. package/dist/services/skills/session-start-hook-constants.js +41 -0
  98. package/dist/services/skills/skill-presence-service.js +9 -0
  99. package/dist/services/skills/skill-statusline-renderer.js +30 -5
  100. package/dist/services/skills/statusline-palette.d.ts +6 -0
  101. package/dist/services/skills/statusline-palette.js +4 -1
  102. package/dist/services/slice/slice-check-service.js +2 -1
  103. package/dist/services/slice/slice-decompose-runners.js +2 -1
  104. package/dist/services/upgrade/upgrade-service.js +1 -0
  105. package/dist/services/workflow/workflow-skip-service.js +2 -1
  106. package/dist/services/workspace/migrate-service.js +1 -1
  107. package/dist/services/workspace/workspace-claude-settings-materializer.js +51 -7
  108. package/dist/services/workspace/workspace-service.js +8 -0
  109. package/dist/services/worktree/host-worktree-reconciler.js +1 -0
  110. package/dist/services/worktree/long-path-cleanup.js +3 -2
  111. package/dist/shared/process.js +1 -1
  112. package/package.json +5 -5
  113. package/scripts/install-skills.mjs +1 -0
  114. package/scripts/watch.mjs +3 -1
  115. package/skills/bee/peaks-perf-audit/SKILL.md +1 -1
  116. package/skills/bee/peaks-prd/SKILL.md +1 -1
  117. package/skills/bee/peaks-qa/SKILL.md +2 -2
  118. package/skills/bee/peaks-rd/SKILL.md +2 -2
  119. package/skills/bee/peaks-reviewer/SKILL.md +1 -1
  120. package/skills/bee/peaks-sc/SKILL.md +1 -1
  121. package/skills/bee/peaks-security-audit/SKILL.md +1 -1
  122. package/skills/bee/peaks-txt/SKILL.md +1 -1
  123. package/skills/bee/peaks-ui/SKILL.md +1 -1
  124. package/skills/peaks-audit/SKILL.md +1 -1
  125. package/skills/peaks-code/SKILL.md +4 -4
  126. package/skills/peaks-code/references/sub-agent-dispatch.md +1 -1
  127. package/skills/peaks-content/SKILL.md +1 -1
  128. package/skills/peaks-doctor/SKILL.md +1 -1
  129. package/skills/peaks-final-review/SKILL.md +1 -1
  130. package/skills/peaks-ide/SKILL.md +1 -1
  131. package/skills/peaks-issue-fix-orchestrator/SKILL.md +1 -1
  132. package/skills/peaks-resume/SKILL.md +1 -1
  133. package/skills/peaks-slice-decompose/SKILL.md +1 -1
  134. package/skills/peaks-solo/SKILL.md +1 -1
  135. package/skills/peaks-sop/SKILL.md +1 -1
  136. package/skills/peaks-status/SKILL.md +1 -1
  137. package/skills/peaks-test/SKILL.md +1 -1
@@ -1,32 +1,5 @@
1
- /**
2
- * AC-1 — auto context-percent probe.
3
- *
4
- * Reads the current AI CLI context-fill ratio without requiring the
5
- * LLM to pass `--prompt-size <bytes>` manually. Strategy: ask the
6
- * registered `IdeAdapter.compact` profile which env-var to read and,
7
- * when that misses, ask the adapter for a vendor-specific fallback —
8
- * no hard-coded IDE names. Per-adapter:
9
- *
10
- * - claude-code: its adapter-declared env-var (MVP) + a
11
- * `readContextPercentFallback` that polls the statusline /
12
- * transcript (see claude-code-adapter.ts).
13
- * - trae / codex / cursor / qoder / tongyi-lingma / hermes /
14
- * openclaw / zcode: each adapter fills its own env-var; until
15
- * L2-dogfood verifies each surface, adapters may omit `compact`
16
- * and the probe returns `source: 'conservative-fallback'`.
17
- *
18
- * Resolution order (user-overridden → env-var → adapter fallback →
19
- * conservative-fallback):
20
- * 1. `promptSizeBytes` (P0 `--prompt-size <bytes>` escape hatch) →
21
- * `source: 'user-overridden'`.
22
- * 2. `adapter.compact.envVarForContextPercent` env-var →
23
- * `source: '<ideId>-env'`.
24
- * 3. `adapter.compact.readContextPercentFallback?.(input)` — the
25
- * adapter owns any vendor-specific statusline / transcript probe.
26
- * 4. `ratio: 0` with `source: 'conservative-fallback'` — the
27
- * orchestrator MUST NOT auto-fire compact on this signal.
28
- */
29
1
  import type { ContextPercentProbe } from './auto-compact-types.js';
2
+ import { type HarnessWindowLocation, type HarnessWindowReadResult, type HarnessWindowSyncResult } from './harness-window-config.js';
30
3
  export interface ReadContextPercentInput {
31
4
  readonly projectRoot: string;
32
5
  readonly sessionId: string;
@@ -50,6 +23,73 @@ export interface ReadContextPercentInput {
50
23
  */
51
24
  readonly promptSizeBytes?: number | undefined;
52
25
  }
26
+ /**
27
+ * Resolve WHERE the harness auto-compact window lives, for the IDE the
28
+ * current env selects. Returns `null` when either half is undeclared:
29
+ * - the adapter exposes no `compact` profile, or
30
+ * - the adapter's `compact` profile declares no
31
+ * `autoCompactWindowEnvVar` (the IDE has no such knob), or
32
+ * - the adapter declares no machine-local settings layer.
33
+ *
34
+ * Both the path and the key name come from the adapter's OWN declarations,
35
+ * so this module names no IDE and no settings literal — the vendor-neutrality
36
+ * guard's shape-3 rule applies to every registry consumer, and this is one.
37
+ *
38
+ * Slice 2026-09-13-auto-compact-trigger-ownership.
39
+ */
40
+ export declare function resolveHarnessWindowLocation(input: {
41
+ readonly projectRoot: string;
42
+ readonly env?: NodeJS.ProcessEnv | undefined;
43
+ }): HarnessWindowLocation | null;
44
+ /**
45
+ * Read the window peaks-loop last configured for the harness, plus whether
46
+ * the user has opted out of peaks-loop managing it. Returns `null` when the
47
+ * active adapter exposes no window knob at all.
48
+ */
49
+ export declare function readHarnessWindowState(input: {
50
+ readonly projectRoot: string;
51
+ readonly env?: NodeJS.ProcessEnv | undefined;
52
+ }): HarnessWindowReadResult | null;
53
+ /**
54
+ * The RAW window peaks-loop's context ratio must divide by — T1's number.
55
+ *
56
+ * The SETTINGS FILE is the source of truth, so its value wins whenever it has
57
+ * one; the env-first read is consulted only when the file is silent (no key
58
+ * yet, or the IDE injects the value from an outer layer).
59
+ *
60
+ * Why not "whatever the read returned" (the round-2 shape): the process env is
61
+ * a snapshot the harness froze at session start, so it is stale by exactly one
62
+ * write. Trusting it over the file is what let a stale env revert a human's
63
+ * hand-edited window (B1) and downgrade a 1M file to a 200K one (a second
64
+ * session's frozen env), and it is also what made T1's consistency depend on
65
+ * *rewriting the file* — the same unconditional write that destroyed the human's
66
+ * value. Dividing by the file instead makes peaks-loop's ratio and the harness's
67
+ * configured window one number BY CONSTRUCTION, with no write required. The
68
+ * write is then free to be gated on provenance (see `syncHarnessWindow`).
69
+ *
70
+ * Falls back to `undefined` (no harness layer at all) when the adapter
71
+ * declares no knob or neither side carries the key — the adapter then resolves
72
+ * its own window exactly as it did before this slice.
73
+ */
74
+ export declare function resolveHarnessRatioWindow(state: HarnessWindowReadResult | null): unknown;
75
+ /**
76
+ * Materialize a window into the harness's own settings, so the number
77
+ * peaks-loop divides by and the number the harness compacts against are one
78
+ * value rather than two resolutions (slice
79
+ * 2026-09-13-auto-compact-trigger-ownership).
80
+ *
81
+ * The caller passes the denominator it JUST USED (`probe.capacityTokens`), not
82
+ * a re-derived one — that is what makes the two sides structurally identical.
83
+ * `tokens: null` (a percent-only probe carries no token window) is a
84
+ * deliberate no-op: peaks-loop never invents a window it did not measure.
85
+ *
86
+ * Returns `null` when the active adapter declares no window knob at all.
87
+ */
88
+ export declare function syncHarnessWindowForProject(input: {
89
+ readonly projectRoot: string;
90
+ readonly env?: NodeJS.ProcessEnv | undefined;
91
+ readonly tokens: number | null;
92
+ }): HarnessWindowSyncResult | null;
53
93
  /**
54
94
  * Read env-var-based context percentage. Returns `null` when the env
55
95
  * is absent or unparseable. Adapter-driven: the caller passes the
@@ -1,6 +1,141 @@
1
+ /**
2
+ * AC-1 — auto context-percent probe.
3
+ *
4
+ * Reads the current AI CLI context-fill ratio without requiring the
5
+ * LLM to pass `--prompt-size <bytes>` manually. Strategy: ask the
6
+ * registered `IdeAdapter.compact` profile which env-var to read and,
7
+ * when that misses, ask the adapter for a vendor-specific fallback —
8
+ * no hard-coded IDE names. Per-adapter:
9
+ *
10
+ * - claude-code: its adapter-declared env-var (MVP) + a
11
+ * `readContextPercentFallback` that polls the statusline /
12
+ * transcript (see claude-code-adapter.ts).
13
+ * - trae / codex / cursor / qoder / tongyi-lingma / hermes /
14
+ * openclaw / zcode: each adapter fills its own env-var; until
15
+ * L2-dogfood verifies each surface, adapters may omit `compact`
16
+ * and the probe returns `source: 'conservative-fallback'`.
17
+ *
18
+ * Resolution order (user-overridden → env-var → adapter fallback →
19
+ * conservative-fallback):
20
+ * 1. `promptSizeBytes` (P0 `--prompt-size <bytes>` escape hatch) →
21
+ * `source: 'user-overridden'`.
22
+ * 2. `adapter.compact.envVarForContextPercent` env-var →
23
+ * `source: '<ideId>-env'`.
24
+ * 3. `adapter.compact.readContextPercentFallback?.(input)` — the
25
+ * adapter owns any vendor-specific statusline / transcript probe.
26
+ * 4. `ratio: 0` with `source: 'conservative-fallback'` — the
27
+ * orchestrator MUST NOT auto-fire compact on this signal.
28
+ */
29
+ import { join } from 'node:path';
1
30
  import { detectIdeFromEnv } from './ide-detect.js';
2
31
  import { getAdapter } from '../ide/ide-registry.js';
3
32
  import { readContextWindowTokensOverride } from '../config/config-service.js';
33
+ import { isHarnessWindowInRange, parseHarnessWindowTokens, readHarnessWindow, syncHarnessWindow } from './harness-window-config.js';
34
+ /**
35
+ * Resolve WHERE the harness auto-compact window lives, for the IDE the
36
+ * current env selects. Returns `null` when either half is undeclared:
37
+ * - the adapter exposes no `compact` profile, or
38
+ * - the adapter's `compact` profile declares no
39
+ * `autoCompactWindowEnvVar` (the IDE has no such knob), or
40
+ * - the adapter declares no machine-local settings layer.
41
+ *
42
+ * Both the path and the key name come from the adapter's OWN declarations,
43
+ * so this module names no IDE and no settings literal — the vendor-neutrality
44
+ * guard's shape-3 rule applies to every registry consumer, and this is one.
45
+ *
46
+ * Slice 2026-09-13-auto-compact-trigger-ownership.
47
+ */
48
+ export function resolveHarnessWindowLocation(input) {
49
+ const env = input.env ?? process.env;
50
+ const detected = detectIdeFromEnv(env);
51
+ const ideId = (detected === 'unknown' ? 'claude-code' : detected);
52
+ const adapter = getAdapter(ideId);
53
+ const envVar = adapter.compact?.autoCompactWindowEnvVar;
54
+ const fileName = adapter.settings.localSettingsFileName ?? adapter.settings.settingsFileName;
55
+ if (envVar === undefined || fileName === undefined)
56
+ return null;
57
+ return {
58
+ settingsPath: join(input.projectRoot, adapter.settings.dirName, fileName),
59
+ envVar,
60
+ // Carried so the writer can refuse to target the user's own home directory
61
+ // (`--project .` from a fresh terminal): the path alone cannot say which
62
+ // root it came from, and this is the only place a location is built.
63
+ projectRoot: input.projectRoot
64
+ };
65
+ }
66
+ /**
67
+ * Read the window peaks-loop last configured for the harness, plus whether
68
+ * the user has opted out of peaks-loop managing it. Returns `null` when the
69
+ * active adapter exposes no window knob at all.
70
+ */
71
+ export function readHarnessWindowState(input) {
72
+ const location = resolveHarnessWindowLocation(input);
73
+ if (location === null)
74
+ return null;
75
+ return readHarnessWindow({ location, env: input.env ?? process.env });
76
+ }
77
+ /**
78
+ * The RAW window peaks-loop's context ratio must divide by — T1's number.
79
+ *
80
+ * The SETTINGS FILE is the source of truth, so its value wins whenever it has
81
+ * one; the env-first read is consulted only when the file is silent (no key
82
+ * yet, or the IDE injects the value from an outer layer).
83
+ *
84
+ * Why not "whatever the read returned" (the round-2 shape): the process env is
85
+ * a snapshot the harness froze at session start, so it is stale by exactly one
86
+ * write. Trusting it over the file is what let a stale env revert a human's
87
+ * hand-edited window (B1) and downgrade a 1M file to a 200K one (a second
88
+ * session's frozen env), and it is also what made T1's consistency depend on
89
+ * *rewriting the file* — the same unconditional write that destroyed the human's
90
+ * value. Dividing by the file instead makes peaks-loop's ratio and the harness's
91
+ * configured window one number BY CONSTRUCTION, with no write required. The
92
+ * write is then free to be gated on provenance (see `syncHarnessWindow`).
93
+ *
94
+ * Falls back to `undefined` (no harness layer at all) when the adapter
95
+ * declares no knob or neither side carries the key — the adapter then resolves
96
+ * its own window exactly as it did before this slice.
97
+ */
98
+ export function resolveHarnessRatioWindow(state) {
99
+ if (state === null)
100
+ return undefined;
101
+ // E1 (rid 2026-09-13-defects-e): a value outside the band the harness accepts
102
+ // is not a window, however well-formed an integer it is — the harness ignores
103
+ // or caps it, so handing it over here would make the ratio's denominator a
104
+ // number the harness is not compacting on. This is the read half of the same
105
+ // guard the writer applies before planting one; without it, a value already on
106
+ // disk (hand-planted, or written by a release that predates the band) would
107
+ // keep poisoning the ratio on every probe.
108
+ //
109
+ // The two NULLs are deliberate and different: an out-of-band value is refused
110
+ // here (the adapter falls through to its own resolution and the writer's
111
+ // notice names the skipped number), while an UNPARSEABLE one is still handed
112
+ // over — the adapter validates it and warns about a hand-edited `500k`, which
113
+ // is a closer reading of the user's intent than silently ignoring it.
114
+ const raw = state.fileRaw !== undefined ? state.fileRaw : state.raw;
115
+ const tokens = parseHarnessWindowTokens(raw);
116
+ if (tokens !== null && !isHarnessWindowInRange(tokens))
117
+ return undefined;
118
+ return raw;
119
+ }
120
+ /**
121
+ * Materialize a window into the harness's own settings, so the number
122
+ * peaks-loop divides by and the number the harness compacts against are one
123
+ * value rather than two resolutions (slice
124
+ * 2026-09-13-auto-compact-trigger-ownership).
125
+ *
126
+ * The caller passes the denominator it JUST USED (`probe.capacityTokens`), not
127
+ * a re-derived one — that is what makes the two sides structurally identical.
128
+ * `tokens: null` (a percent-only probe carries no token window) is a
129
+ * deliberate no-op: peaks-loop never invents a window it did not measure.
130
+ *
131
+ * Returns `null` when the active adapter declares no window knob at all.
132
+ */
133
+ export function syncHarnessWindowForProject(input) {
134
+ const location = resolveHarnessWindowLocation(input);
135
+ if (location === null)
136
+ return null;
137
+ return syncHarnessWindow({ location, tokens: input.tokens, env: input.env ?? process.env });
138
+ }
4
139
  /**
5
140
  * Read env-var-based context percentage. Returns `null` when the env
6
141
  * is absent or unparseable. Adapter-driven: the caller passes the
@@ -61,6 +196,17 @@ export function readContextPercent(input) {
61
196
  };
62
197
  }
63
198
  if (adapter.compact) {
199
+ // Slice 2026-09-13-auto-compact-trigger-ownership: the window peaks-loop
200
+ // configured for the harness, read from the adapter's declared settings
201
+ // path + key. `null` (no knob declared, or the key absent) leaves the
202
+ // adapter's chain byte-identical to before this slice.
203
+ //
204
+ // The RAW value is handed over, not the parsed one: the adapter validates
205
+ // it (`parseContextWindowOverride`) and warns on a bad hand-edited value,
206
+ // so parsing here too would be a second, silently-disagreeing parser —
207
+ // exactly the duplicated-resolution shape this slice exists to delete.
208
+ const harnessWindowState = readHarnessWindowState({ projectRoot: input.projectRoot, env });
209
+ const harnessWindowTokens = resolveHarnessRatioWindow(harnessWindowState);
64
210
  // Primary: read the adapter-declared env-var (no hard-coded IDE names).
65
211
  const primary = readEnvPercent(env, adapter.compact.envVarForContextPercent);
66
212
  if (primary !== null) {
@@ -85,7 +231,15 @@ export function readContextPercent(input) {
85
231
  sessionId: input.sessionId,
86
232
  outerSessionId: input.outerSessionId,
87
233
  env,
88
- configWindowTokens: readContextWindowTokensOverride(input.projectRoot)
234
+ configWindowTokens: readContextWindowTokensOverride(input.projectRoot),
235
+ // Slice 2026-09-13-auto-compact-trigger-ownership: hand the adapter the
236
+ // window peaks-loop configured for the harness, so the ratio it reports
237
+ // divides by the very number the harness compacts against — plus whether
238
+ // peaks-loop is the one that wrote it, so the adapter may self-correct
239
+ // its own earlier resolution without ever overruling a human's pin.
240
+ ...(harnessWindowTokens !== undefined
241
+ ? { harnessWindowTokens, harnessWindowPeakWritten: harnessWindowState?.peakWritten === true }
242
+ : {})
89
243
  });
90
244
  if (fallback)
91
245
  return fallback;
@@ -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 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%.
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 the window
30
- * is gone and peaks-loop takes over synchronously to keep the runner
31
- * alive.
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
- /** Red line (ratio ≥ 0.95): peaks-loop forces synchronous compact; LLM cannot opt out. */
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
+ };