instar 1.3.231 → 1.3.233

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 (53) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +100 -5
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  5. package/dist/config/ConfigDefaults.js +24 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  8. package/dist/core/PostUpdateMigrator.js +15 -0
  9. package/dist/core/PostUpdateMigrator.js.map +1 -1
  10. package/dist/core/types.d.ts +34 -0
  11. package/dist/core/types.d.ts.map +1 -1
  12. package/dist/core/types.js.map +1 -1
  13. package/dist/monitoring/CorrectionCaptureBacklog.d.ts +85 -0
  14. package/dist/monitoring/CorrectionCaptureBacklog.d.ts.map +1 -0
  15. package/dist/monitoring/CorrectionCaptureBacklog.js +290 -0
  16. package/dist/monitoring/CorrectionCaptureBacklog.js.map +1 -0
  17. package/dist/monitoring/CorrectionCaptureLoop.d.ts +73 -3
  18. package/dist/monitoring/CorrectionCaptureLoop.d.ts.map +1 -1
  19. package/dist/monitoring/CorrectionCaptureLoop.js +172 -2
  20. package/dist/monitoring/CorrectionCaptureLoop.js.map +1 -1
  21. package/dist/monitoring/McpProcessReaper.d.ts +135 -0
  22. package/dist/monitoring/McpProcessReaper.d.ts.map +1 -0
  23. package/dist/monitoring/McpProcessReaper.js +229 -0
  24. package/dist/monitoring/McpProcessReaper.js.map +1 -0
  25. package/dist/monitoring/mcpProcessReaperDeps.d.ts +22 -0
  26. package/dist/monitoring/mcpProcessReaperDeps.d.ts.map +1 -0
  27. package/dist/monitoring/mcpProcessReaperDeps.js +125 -0
  28. package/dist/monitoring/mcpProcessReaperDeps.js.map +1 -0
  29. package/dist/monitoring/mcpProcessSignatures.d.ts +38 -0
  30. package/dist/monitoring/mcpProcessSignatures.d.ts.map +1 -0
  31. package/dist/monitoring/mcpProcessSignatures.js +55 -0
  32. package/dist/monitoring/mcpProcessSignatures.js.map +1 -0
  33. package/dist/scaffold/templates.d.ts.map +1 -1
  34. package/dist/scaffold/templates.js +1 -0
  35. package/dist/scaffold/templates.js.map +1 -1
  36. package/dist/server/AgentServer.d.ts +3 -0
  37. package/dist/server/AgentServer.d.ts.map +1 -1
  38. package/dist/server/AgentServer.js +1 -0
  39. package/dist/server/AgentServer.js.map +1 -1
  40. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  41. package/dist/server/CapabilityIndex.js +1 -0
  42. package/dist/server/CapabilityIndex.js.map +1 -1
  43. package/dist/server/routes.d.ts +3 -0
  44. package/dist/server/routes.d.ts.map +1 -1
  45. package/dist/server/routes.js +13 -0
  46. package/dist/server/routes.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/data/builtin-manifest.json +64 -64
  49. package/src/scaffold/templates.ts +1 -0
  50. package/upgrades/1.3.232.md +47 -0
  51. package/upgrades/1.3.233.md +36 -0
  52. package/upgrades/side-effects/correction-capture-backlog.md +137 -0
  53. package/upgrades/side-effects/mcp-process-reaper.md +58 -0
@@ -0,0 +1,36 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: minor -->
5
+
6
+ ## What Changed
7
+
8
+ The Correction & Preference Learning Sentinel now survives an LLM throttle without losing corrections.
9
+
10
+ Previously, the capture hop distilled each detected correction through a rate-limited LLM queue behind the account-global circuit breaker. When that distill call was rejected for a capacity reason — daily spend cap reached, interactive-reserve breach, an aborted background call, or the breaker being open — `CorrectionCaptureLoop` dropped the capture silently with no retry and no backlog. On a production agent under sustained throttling that meant Layer 0 kept *detecting* corrections while the `CorrectionLedger` stayed permanently EMPTY, because every distill attempt hit the breaker and was discarded.
11
+
12
+ This release adds `CorrectionCaptureBacklog` — a bounded, durable SQLite store at `correction-capture-backlog.db`. When a distill is rejected for a capacity reason, the already-pre-scrubbed capture is PERSISTED to the backlog instead of dropped. A new `isCapacityThrow` classifier distinguishes a capacity rejection (worth retrying later) from a genuine fault (a malformed envelope still drops, preserving the old behavior). A new audit decision `distill-backlogged` records the persist.
13
+
14
+ A new `drainBacklog` function works the backlog down off the message hot path. It is triggered two ways: opportunistically right after a live capture's distill SUCCEEDS (proof the LLM has headroom *now*), and by a slow 5-minute periodic sweep. The drainer is breaker-gated — it consults `llmCircuitAvailable()` (a pure, non-probe-consuming read) and never drains while the breaker is open, since that would just re-fail every entry and re-trip the circuit. For each claimed entry it rebuilds the same distill prompt, records a real learning into the `CorrectionLedger`, then DELETES the backlog row (`markDistilled`); a failed attempt calls `bumpAttempt`, which drops the entry once it exceeds its retry budget.
15
+
16
+ Retention is bounded two ways: a max-entries cap (oldest evicted on overflow) and a TTL (stale entries pruned before each drain). The store persists ONLY the pre-scrubbed turns — the same secret scrub that guards the distill prompt runs before anything is written, plus a defensive re-scrub on enqueue — and no API route ever serves backlog contents. It is on by default whenever the feature is enabled (pure resilience) and is disabled by setting its max-entries to zero, which restores the old drop-on-throttle behavior. All four new dials backfill into existing agents via `applyDefaults` deep-merge.
17
+
18
+ The whole path is fail-open and signal-only: a backlog or drain error can never block, delay, or throw into message delivery.
19
+
20
+ ## What to Tell Your User
21
+
22
+ - **Corrections survive a busy stretch now**: "When you correct me about the same thing and I happen to be rate-limited at that moment, I no longer just lose it. I tuck the correction away safely and learn from it a little later once I have headroom — so a busy period can't make me quietly forget what you taught me. It only ever keeps a private, scrubbed note of the lesson, never your raw words, and it tidies those away as soon as they're learned. This whole thing only runs when correction learning is switched on, and I can turn it off for you if you ever want the old behavior back."
23
+
24
+ ## Summary of New Capabilities
25
+
26
+ | Capability | How to Use |
27
+ |-----------|-----------|
28
+ | Durable retry of throttled corrections | Automatic when correction learning is enabled |
29
+ | Disable the backlog (old drop behavior) | Set the correction-learning capture-backlog max-entries to 0 |
30
+ | See learned corrections (the ledger the backlog feeds) | GET /corrections |
31
+
32
+ ## Evidence
33
+
34
+ - **Reported live**: on a production agent with correction learning enabled, Layer 0 was detecting corrections but GET /corrections returned an empty ledger. Root cause confirmed in `src/monitoring/CorrectionCaptureLoop.ts` (~line 204): the distill catch block dropped every capture on any LlmQueue throw, and the agent's account was sustained-throttled, so every distill hit the open circuit breaker and was discarded.
35
+ - **After (code path), full round-trip proven**: `tests/e2e/correction-capture-backlog-lifecycle.test.ts` drives a learning-signal capture whose distill is rate-limited → asserts decision `distill-backlogged` and a backlog count of 1 (NOT dropped); asserts the drain is SKIPPED while the breaker is open (entry retained); then flips the breaker closed, drains, and asserts the distilled record is recorded into the ledger, the backlog row is deleted, and the record is observable on the real GET /corrections route with only its scrubbed summary served (raw learning never crosses HTTP).
36
+ - **Unit coverage**: `tests/unit/CorrectionCaptureBacklog.test.ts` (11 cases: enqueue, max-entries eviction oldest-first, near-identical dedupe, claimBatch ordering + min-retry-gap, markDistilled, bumpAttempt-drops-at-maxRetries, TTL prune, pre-scrubbed-only persistence, fail-open). `tests/unit/CorrectionCaptureBacklog-drain.test.ts` (12 cases: the four-shape isCapacityThrow classifier, capacity-throw → backlog vs non-capacity → drop, no-backlog-wired old drop, backlog-fault fallback, drain breaker-gated/recorded/bumped/pruned/fail-open). Wiring-integrity in `tests/unit/correction-learning-wiring-integrity.test.ts` (backlog constructed iff enabled+maxEntries>0; a rate-limited capture lands in the backlog without throwing into the hook; drain skipped while breaker open). Integration round-trip in `tests/integration/corrections-routes.test.ts`. All green: 11 + 12 + 9 + 13 + 2 = the new/updated suites pass with zero failures.
@@ -0,0 +1,137 @@
1
+ # Side-Effects Review — correction capture-backlog with retry
2
+
3
+ **Version / slug:** `correction-capture-backlog`
4
+ **Date:** `2026-06-02`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `pending`
7
+
8
+ ## Summary of the change
9
+
10
+ Adds `src/monitoring/CorrectionCaptureBacklog.ts` — a bounded, durable SQLite store
11
+ that persists pre-scrubbed captures the Correction & Preference Learning Sentinel
12
+ could not distill because the LLM was rate-limited (daily-cap / reserve-breach /
13
+ aborted / circuit-breaker-open). `CorrectionCaptureLoop.captureAndDistill` now, in
14
+ its distill catch block, classifies the rejection via the new `isCapacityThrow`: a
15
+ CAPACITY rejection enqueues the pre-scrubbed capture to the backlog (audit decision
16
+ `distill-backlogged`) instead of dropping it; a genuine fault still drops
17
+ (`distill-dropped`). A new `drainBacklog` distills backlogged entries into the
18
+ `CorrectionLedger` off the hot path, breaker-gated on `llmCircuitAvailable()`,
19
+ triggered after a successful live distill and by a 5-minute periodic sweep. Four
20
+ config dials (`captureBacklogMaxEntries` default 200, `captureBacklogTtlHours`
21
+ default 24, `captureBacklogDrainPerTick` default 5, `captureBacklogMaxRetries`
22
+ default 3) backfill via `applyDefaults`; maxEntries=0 disables the backlog.
23
+
24
+ ## Decision-point inventory
25
+
26
+ - `captureAndDistill: distill threw — backlog or drop?` (CorrectionCaptureLoop.ts) —
27
+ **new branch** — a capacity throw now persists (was: always drop). A non-capacity
28
+ throw is unchanged (drops). Wrapped in try/catch → a backlog fault falls back to drop.
29
+ - `drainBacklog: is the LLM available?` (CorrectionCaptureLoop.ts) — **new gate** —
30
+ skips the whole drain while the breaker is open; re-checks between entries.
31
+ - `server.ts: construct the backlog?` — **new gate** — constructed IFF the feature is
32
+ enabled AND `captureBacklogMaxEntries > 0`. Else null (old drop behavior).
33
+ - `CorrectionCaptureBacklog.enqueue: at the cap?` — **new** — evicts oldest on overflow;
34
+ dedupes a near-identical entry instead of inserting a duplicate.
35
+
36
+ ---
37
+
38
+ ## 1. Over-block
39
+
40
+ **No block/allow surface — over-block not applicable.** Nothing in this change can
41
+ reject, block, delay, or gate a message or an operation. The backlog/drain only
42
+ move an internal observe-only record (a distilled correction) from a deferred state
43
+ to the ledger. The worst over-reach is a redundant LLM distill call on an entry that
44
+ would have been noise — bounded by `captureBacklogDrainPerTick` (5) per sweep and by
45
+ the per-sentinel daily LLM cap (the drain shares the same `correctionLlmQueue`
46
+ budget, so it cannot exceed the feature's existing spend ceiling).
47
+
48
+ ---
49
+
50
+ ## 2. Under-block
51
+
52
+ **What does it still miss?** `isCapacityThrow` is conservative — an unrecognized
53
+ error string is treated as a non-capacity fault and DROPS (preserving old behavior),
54
+ so a novel provider rate-limit phrasing not in the matcher would still be dropped
55
+ rather than backlogged. That is the safe direction (no false persistence). A capture
56
+ also still drops if the backlog enqueue itself faults (disk full) — again fail-open,
57
+ no worse than today. Entries that exhaust `captureBacklogMaxRetries` or age past the
58
+ TTL are intentionally discarded (bounded retention), so a persistently-un-distillable
59
+ capture is eventually dropped by design.
60
+
61
+ ---
62
+
63
+ ## 3. Level-of-abstraction fit
64
+
65
+ Correct layer. The backlog mirrors `CorrectionLedger`'s own SQLite discipline (WAL,
66
+ prune-in-transaction, indexes, fail-open, `NativeModuleHealer` open, `SqliteRegistry`
67
+ close-on-exit) and lives beside it in `src/monitoring/`. The drain reuses the existing
68
+ `buildDistillPrompt` / `parseDistillEnvelope` rather than inventing a second distill
69
+ path, and rides the existing account-global `LlmCircuitBreaker` via its pure
70
+ `llmCircuitAvailable()` read rather than adding a parallel availability notion.
71
+
72
+ ---
73
+
74
+ ## 4. Signal vs authority compliance
75
+
76
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
77
+
78
+ - [x] No authority added — this change produces and defers a SIGNAL (a distilled
79
+ correction record) consumed by the existing `CorrectionAnalyzer` / `CorrectionLoopDriver`,
80
+ which own every downstream decision. The backlog cannot mint a proposal, write a
81
+ preference, post feedback, or change policy; it only delays then completes the same
82
+ ledger write the hot path would have done. The `CorrectionLoopDriver`'s
83
+ by-construction authority guard (no proposal-mint, no memory-write) is untouched.
84
+
85
+ ---
86
+
87
+ ## 5. Interactions
88
+
89
+ - **Shadowing:** none — the backlog is a NEW db file (`correction-capture-backlog.db`),
90
+ separate from `correction-ledger.db`; no existing query path changes.
91
+ - **LLM budget:** the drain shares the per-sentinel `correctionLlmQueue` daily cap, so
92
+ it competes with (never exceeds) the existing distill budget — and it only runs when
93
+ `llmCircuitAvailable()` is true, so it cannot pile onto an already-throttled account.
94
+ - **Circuit breaker:** the drain is a pure consumer of the breaker's read state; it does
95
+ not trip, reset, or consume a half-open probe slot.
96
+ - **Multi-machine:** the db is per-stateDir (per machine), like the ledger; no mesh
97
+ coordination needed (the worst case is each machine independently distilling its own
98
+ backlogged captures, which dedupe-collapse in the ledger by `dedupeKey`).
99
+
100
+ ---
101
+
102
+ ## 6. Privacy / data exposure
103
+
104
+ **This is the load-bearing dimension and the central design constraint.**
105
+
106
+ - **Persist-pre-scrubbed-only posture.** The backlog persists ONLY the
107
+ already-pre-scrubbed turns. The §3.3 deterministic `scrubSecrets` runs inside
108
+ `buildDistillPrompt` BEFORE the catch that enqueues, and `enqueue` re-scrubs every
109
+ turn defensively (belt-and-suspenders) so even a mis-wired caller cannot write raw
110
+ text. Unit-proven: a `ghp_…` token in a turn is `gh***_REDACTED` on disk
111
+ (`CorrectionCaptureBacklog.test.ts` + `CorrectionCaptureBacklog-drain.test.ts`).
112
+ - **Bounded retention.** Max-entries cap (oldest evicted) AND a TTL (stale pruned),
113
+ and every entry is DELETED the instant it distills (`markDistilled`) or exhausts its
114
+ retries (`bumpAttempt`). It is a bounded, time-limited extension of the existing
115
+ ephemeral look-back ring — not a new long-lived data sink.
116
+ - **No new exposure surface.** No API route serves backlog row content; the scrubbed
117
+ turn text never crosses HTTP. The only observable is a count. The E2E pins that the
118
+ drained record surfaces on GET /corrections with ONLY its scrubbed summary (raw
119
+ learning absent from the payload), exactly like the existing ledger guarantee.
120
+
121
+ ---
122
+
123
+ ## 7. Failure modes / rollback
124
+
125
+ - **Fail-open everywhere.** Every backlog method swallows storage errors and returns a
126
+ safe default; `captureAndDistill`'s backlog enqueue is wrapped in try/catch and falls
127
+ back to the old drop; `drainBacklog` is fully detached (`void`/async), single-flight
128
+ guarded, and its top-level catch can never throw into a seam. A backlog fault degrades
129
+ to exactly the pre-change behavior (drop-on-throttle).
130
+ - **Rollback.** Per-agent: set `monitoring.correctionLearning.captureBacklogMaxEntries`
131
+ to 0 — the backlog is not constructed and the loop reverts to the old drop. Whole
132
+ feature: the existing `monitoring.correctionLearning.enabled` dark gate. No schema
133
+ migration is destructive (a new db file); deleting `correction-capture-backlog.db`
134
+ is safe and just discards any pending retries.
135
+ - **Boot cost.** The store opens an empty WAL db with two small indexes — no boot-time
136
+ full-scan (the `TokenLedger` backfill-boot-hang class is explicitly avoided; there is
137
+ no startup scan, only on-demand claim/prune).
@@ -0,0 +1,58 @@
1
+ # Side-Effects Review — MCP-Process Reaper (Option B)
2
+
3
+ **Version / slug:** `mcp-process-reaper`
4
+ **Date:** `2026-06-04`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `recommended before dryRun:false is set on any agent (it kills processes)`
7
+
8
+ ## Summary of the change
9
+
10
+ A new `McpProcessReaper` (separate sibling, NOT a change to `OrphanProcessReaper` /
11
+ `ReapGuard` / `ReapAuthority`) reaps leaked MCP-server children (playwright-mcp /
12
+ mcp-remote / instar-mcp-stdio) whose owning tmux session is dead/stale or fully
13
+ orphaned. Ships OFF + dry-run fleet-wide; the `developmentAgent` gate enables it on
14
+ echo but leaves `dryRun:true`, so it only observes + audits would-reap. Read-only
15
+ report at `GET /processes/mcp-reaper`.
16
+
17
+ ## Decision-point inventory
18
+
19
+ One decision point: the per-proc keep/reap classifier (`classifyMcpProcess`). It is
20
+ pure and unit-tested on both sides of every boundary. The kill action is gated
21
+ behind `killsEnabled = enabled && !dryRun`.
22
+
23
+ ## 1. Over-block (what legitimate things could it wrongly KILL?)
24
+
25
+ The risk is killing an MCP server that a live session still needs. Mitigations,
26
+ each unit-tested:
27
+ - A proc under a **live/tracked** session is KEPT regardless of age (`session-live`)
28
+ — a long-running autonomous session keeps its old MCP servers.
29
+ - A proc under an **external (non-instar)** tmux session is KEPT (`external-session`)
30
+ — the reaper never touches the user's own processes.
31
+ - A proc **younger than `minAgeMs` (2h)** is KEPT — a starting session's children are
32
+ never candidates.
33
+ - A **failed evaluation** KEEPS (never reap on error).
34
+ - Only **three exact signatures** match — never a broad node/npm match.
35
+ - Ships **dark + dry-run**; even on the dev agent it only observes. Kills require an
36
+ explicit `dryRun:false`.
37
+ - `maxReapsPerPass` (25) bounds the blast radius per pass.
38
+
39
+ ## 2. Under-block (what leaks does it still MISS?)
40
+
41
+ - It does not prevent NEW leaks — that is Option A (tree-kill on session teardown),
42
+ noted as a follow-up. B is the backstop that cleans existing + future unclean deaths.
43
+ - A leaked MCP proc whose dead session's tmux pane somehow still lingers AND is not
44
+ recognized as an instar session would be treated as external (KEPT). This is the
45
+ safe failure direction (favor false-negatives).
46
+ - Non-MCP leaked processes are out of scope by design (precise allow-list).
47
+
48
+ ## 3. Reversibility
49
+
50
+ SIGTERM on an already-orphaned helper is low-risk (its session is gone). The reaper
51
+ itself is fully reversible via config (`enabled:false`). No persistent state is
52
+ mutated except the append-only audit log.
53
+
54
+ ## 4. Blast radius on the existing reaper
55
+
56
+ **Zero.** This is a separate class; the shared `OrphanProcessReaper` / `ReapGuard` /
57
+ `ReapAuthority` / session-reap authority path is untouched (the same Option-B
58
+ isolation used for #722).