instar 1.3.211 → 1.3.212
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +11 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/core/SessionRecoveryChannel.d.ts +103 -0
- package/dist/core/SessionRecoveryChannel.d.ts.map +1 -0
- package/dist/core/SessionRecoveryChannel.js +158 -0
- package/dist/core/SessionRecoveryChannel.js.map +1 -0
- package/dist/core/SessionRecoveryConsumer.d.ts +60 -0
- package/dist/core/SessionRecoveryConsumer.d.ts.map +1 -0
- package/dist/core/SessionRecoveryConsumer.js +110 -0
- package/dist/core/SessionRecoveryConsumer.js.map +1 -0
- package/dist/core/StuckInputSentinel.d.ts +59 -2
- package/dist/core/StuckInputSentinel.d.ts.map +1 -1
- package/dist/core/StuckInputSentinel.js +127 -1
- package/dist/core/StuckInputSentinel.js.map +1 -1
- package/dist/core/types.d.ts +23 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/lifeline/TelegramLifeline.d.ts +1 -0
- package/dist/lifeline/TelegramLifeline.d.ts.map +1 -1
- package/dist/lifeline/TelegramLifeline.js +28 -0
- package/dist/lifeline/TelegramLifeline.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +3 -3
- package/upgrades/1.3.212.md +65 -0
- package/upgrades/side-effects/codex-wedge-self-recovery.md +162 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Side-Effects Review — Codex session-wedge self-recovery
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `codex-wedge-self-recovery`
|
|
4
|
+
**Date:** `2026-06-03`
|
|
5
|
+
**Author:** `Echo`
|
|
6
|
+
**Second-pass reviewer:** `Echo (self) — Tier-2, single-pass under standing preapproval; ships dark`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Adds an escalating self-recovery for wedged codex sessions (input delivered but
|
|
11
|
+
not draining). The detector (`StuckInputSentinel`, server process) escalates past
|
|
12
|
+
the keypress ladder by requesting a tier-C recovery — a server restart + queue
|
|
13
|
+
replay — which is executed by a new lifeline-process consumer
|
|
14
|
+
(`SessionRecoveryConsumer`), because `ServerSupervisor` lives in the lifeline. The
|
|
15
|
+
two processes communicate through a new on-disk `SessionRecoveryChannel`
|
|
16
|
+
(single-writer-per-file request + ack + cooldown). Files: `SessionRecoveryChannel.ts`
|
|
17
|
+
(new), `SessionRecoveryConsumer.ts` (new), `StuckInputSentinel.ts` (escalation state
|
|
18
|
+
machine + recovered-request cleanup), `types.ts` (config shape), `server.ts` +
|
|
19
|
+
`TelegramLifeline.ts` (wiring). Highest-blast-radius decision point: **whether to
|
|
20
|
+
restart the agent server.** Ships dark (`monitoring.codexWedgeRecovery`, default
|
|
21
|
+
off + dryRun).
|
|
22
|
+
|
|
23
|
+
## Decision-point inventory
|
|
24
|
+
|
|
25
|
+
- `StuckInputSentinel exhausted-record path` — modify — after the keypress ladder,
|
|
26
|
+
escalate (request tier C) instead of stopping, when enabled.
|
|
27
|
+
- `SessionRecoveryConsumer.handle` — add — decides whether to restart the server
|
|
28
|
+
for a given request (cooldown + dedup + dry-run gates).
|
|
29
|
+
- `monitoring.codexWedgeRecovery` config gate — add — master enable + dryRun +
|
|
30
|
+
cooldown + timeout. Default off.
|
|
31
|
+
- No message block/allow decision point is touched.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1. Over-block
|
|
36
|
+
|
|
37
|
+
No message block/allow surface — over-block not applicable. The closest analogue
|
|
38
|
+
is "would it restart a server it shouldn't?" Guards against that: it only fires
|
|
39
|
+
when (a) the config is enabled, (b) a codex marker is stuck past the keypress
|
|
40
|
+
ladder (≥ minTicks + maxAttempts keypresses all failed), and (c) the durable
|
|
41
|
+
cooldown is clear. In dry-run (the shipping default) it never restarts at all.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 2. Under-block
|
|
46
|
+
|
|
47
|
+
No message block/allow surface. Failure-to-recover modes it still misses: a wedge
|
|
48
|
+
whose pane signature the marker-based detector doesn't recognize won't escalate
|
|
49
|
+
(out of scope — detection is unchanged); a wedge that a server restart genuinely
|
|
50
|
+
can't fix will restart once, hit the cooldown, ack failed, and stop (by design —
|
|
51
|
+
bounded, not infinite). Tier B (a gentler server-side message re-inject before the
|
|
52
|
+
restart) is a documented planned refinement, not in this cut.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 3. Level-of-abstraction fit
|
|
57
|
+
|
|
58
|
+
Correct layering, and it was the central design question. Detection is a low-level
|
|
59
|
+
signal in the server; the restart is a high-level authority in the lifeline. They
|
|
60
|
+
are split across the real process boundary via the channel rather than forcing the
|
|
61
|
+
sentinel to reach across it. Reuses existing primitives (`fireStuckInputRecovery`,
|
|
62
|
+
`ServerSupervisor.performGracefulRestart`, `replayQueue`) instead of reimplementing
|
|
63
|
+
them.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 4. Signal vs authority compliance
|
|
68
|
+
|
|
69
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
70
|
+
|
|
71
|
+
- [x] No — this change has no message block/allow surface.
|
|
72
|
+
|
|
73
|
+
This is the textbook Signal-vs-Authority split: the sentinel emits a SIGNAL (a
|
|
74
|
+
recovery request); the lifeline holds the AUTHORITY to act on it (or decline, via
|
|
75
|
+
cooldown/dry-run). The sentinel never restarts anything; the consumer is the only
|
|
76
|
+
actor, and it is gated.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 5. Interactions
|
|
81
|
+
|
|
82
|
+
- **Shadowing:** The escalation hangs off the END of the existing keypress ladder —
|
|
83
|
+
it only runs after `attempts >= maxAttempts`, so it never shadows the keypress
|
|
84
|
+
recovery (that still runs first, unchanged). The lifeline consumer runs on its own
|
|
85
|
+
15s interval alongside the existing replay + restart-signal intervals; it acts
|
|
86
|
+
only on tier-C requests, so it can't interfere with version-skew restart signals
|
|
87
|
+
(different file, different tier).
|
|
88
|
+
- **Double-fire:** The consumer dedups on `(sessionId, attemptId)` (terminal ack →
|
|
89
|
+
skip) and records the restart durably BEFORE acting, so a crash mid-restart still
|
|
90
|
+
counts against the cooldown. The sentinel won't emit a second request while one is
|
|
91
|
+
in flight (idempotent by attemptId).
|
|
92
|
+
- **Races:** Single-writer-per-file (server owns request + cooldown-trigger via the
|
|
93
|
+
sentinel's clears; lifeline owns ack + cooldown record) — no cross-process write
|
|
94
|
+
contention. Atomic writes via SafeFsExecutor.
|
|
95
|
+
- **Feedback loops:** THE risk for this feature — a tier-C restart wipes the
|
|
96
|
+
sentinel's in-memory escalation bound, so a still-stuck session could re-detect →
|
|
97
|
+
re-restart forever. Closed by the DURABLE cooldown the lifeline checks before every
|
|
98
|
+
restart (the sentinel's in-memory bound is no longer load-bearing across restarts).
|
|
99
|
+
Unit-tested explicitly (cooldown blocks the second restart; allows it after the
|
|
100
|
+
window).
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 6. External surfaces
|
|
105
|
+
|
|
106
|
+
- **Other agents / users:** none — per-agent state files + per-agent processes.
|
|
107
|
+
- **External systems:** none (Telegram/Slack/GitHub untouched). The server restart
|
|
108
|
+
is the agent restarting itself — the same action the auto-updater + `/lifeline
|
|
109
|
+
restart` already perform; it briefly drops the local API/dashboard while the
|
|
110
|
+
server bounces (seconds), then queue replay re-delivers. This only happens when
|
|
111
|
+
the feature is enabled AND not in dry-run.
|
|
112
|
+
- **Persistent state:** three new small JSON files under `state/`
|
|
113
|
+
(`session-recovery-requested/acked/cooldown.json`). Inert when the feature is off.
|
|
114
|
+
- **Timing:** the consumer ticks every 15s only when enabled.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 7. Rollback cost
|
|
119
|
+
|
|
120
|
+
Pure code + new files; the feature is dark by default. Back-out is `git revert` +
|
|
121
|
+
ship the next patch — nothing to undo on existing agents because nothing runs
|
|
122
|
+
until `monitoring.codexWedgeRecovery.enabled` is set (which no agent has). The new
|
|
123
|
+
state files are only written when enabled and are inert otherwise. No data
|
|
124
|
+
migration, no user-visible regression. Disabling live is a config flip (set
|
|
125
|
+
`enabled:false` or `dryRun:true`).
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Conclusion
|
|
130
|
+
|
|
131
|
+
The review's central concern — the restart-loop feedback path — was found DURING
|
|
132
|
+
the build (a tier-C restart wipes the in-memory bound) and closed with a durable
|
|
133
|
+
cooldown, which is the load-bearing safety mechanism and is unit-tested. The
|
|
134
|
+
feature ships dark + dry-run on the Graduated-Feature-Rollout track, with the only
|
|
135
|
+
high-blast-radius action (server restart) gated four ways (config enabled, not
|
|
136
|
+
dry-run, past the keypress ladder, cooldown clear) and bounded. Clear to ship as a
|
|
137
|
+
dark feature for review; enabling (dry-run → live) is a deliberate, reversible
|
|
138
|
+
config decision for the operator.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Second-pass review (if required)
|
|
143
|
+
|
|
144
|
+
**Reviewer:** Echo (self) — Tier-2, ships dark; recommend a dry-run soak before live.
|
|
145
|
+
**Independent read of the artifact: concur**
|
|
146
|
+
|
|
147
|
+
The Signal-vs-Authority split is clean, the restart-loop guard is durable and
|
|
148
|
+
tested, dark-by-default is verified in both unit and integration tiers, and the
|
|
149
|
+
rollback cost is a revert. One flagged follow-up (not blocking): add tier B (a
|
|
150
|
+
server-side message re-inject) as a gentler step before the restart, to reduce how
|
|
151
|
+
often tier C fires once this soaks.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Evidence pointers
|
|
156
|
+
|
|
157
|
+
- `tests/unit/SessionRecoveryChannel.test.ts` (15), `tests/unit/StuckInputSentinel-escalation.test.ts` (7),
|
|
158
|
+
`tests/unit/SessionRecoveryConsumer.test.ts` (8), `tests/integration/codex-wedge-recovery.test.ts` (3),
|
|
159
|
+
plus existing stuck-input suites — 78 total green.
|
|
160
|
+
- `docs/specs/CODEX-SESSION-WEDGE-SELF-RECOVERY.md` — design, cross-process
|
|
161
|
+
constraint, durable-cooldown finding, increment plan.
|
|
162
|
+
- `tsc --noEmit` + `pnpm build` clean.
|