Commit 4fa110d
authored
coding-agents: keep the installed runtime current by itself (#3965)
`install` stages this package into ~/.hindsight/coding-agents and points every wired
agent's hooks at that copy. Nothing ever refreshed it: the only update path was the
user remembering to re-run `install`, so a machine could sit several versions behind
indefinitely — a fix only reached people who happened to re-install. Found on a
machine running 0.4.2 while 0.4.3 had been published for days, with no signal
anywhere that an update existed.
Once a day, at session start, ask the registry for the published version and — when
it is newer — spawn a detached updater. The current session keeps running the code
it already loaded; the next one starts on the new build.
`update` is a new installer command: `install`'s staging half and nothing else. It
replaces the staged runtime (a path stable across versions, so every wired agent
picks the new code up on its next spawn) and writes to NO host config. That
separation is what makes it safe to run unattended — an `install` would need a
harness list, and choosing one on the user's behalf would rewire agents they never
asked us to touch. The cost is bounded and documented: a release introducing a NEW
hook entry point is staged but not referenced until a manual `install`.
Only ever replaces a runtime it can prove npx downloaded. `stageRuntime` records the
directory it copied from, and a copy staged from `npm i -g`, from a project
dependency, or from a local checkout is left to whoever manages that source:
re-staging behind npm's back would leave `npm ls -g` naming a version that is no
longer what runs, and re-staging over a checkout would replace a developer's own
build mid-session. A missing marker means no — it is written on every install from
this version on, and a machine has to re-install once to get this code at all, so a
runtime old enough to lack it is too old to be running the check. Failing closed
costs one manual install; failing open costs somebody their working tree.
Concurrency is real here, not hypothetical: this is a plugin for machines that run
five agents at once, and a 24h stamp does not serialise anything — several sessions
starting in the same second all read "due" before any has written it. Two concurrent
stageRuntime runs are `rmSync(dist)` then `cpSync`, where one process deletes the
directory the other is half way through writing, leaving a runtime with missing
entry points and every hook broken. A lock claimed before the registry call makes a
burst produce ONE request and one updater; same shape as deepen.ts's per-bank lock,
with the holder's pid deciding liveness so a crash cannot wedge the window, and the
stored pid is the detached CHILD's since the copy outlives the session.
Other guards, each with a test: `npx` must be on PATH (without it there is nothing
to spawn, so the check is skipped rather than burning a request and failing a spawn
asynchronously); a prerelease never supersedes the release of the same version; an
unreadable staged version never guesses; the survey's own headless session is
excluded; and both ownership refusals stamp the check so each states its reason at
most once a day.
`autoUpdate: false` (or HINDSIGHT_AUTO_UPDATE=false) pins the installed version,
settable globally, per harness or per bank. `disabled` stops it too — an inert
plugin should stay inert, and a network call plus a background npm install is not
inert.
Wired at BOTH session-start paths — `runSessionStartHook` for the hook harnesses and
`RuntimeCore.seedIfCold` for the persistent-plugin hosts — plus a family-wide guard
test that enumerates session starts structurally rather than from a hand-maintained
list, so a third host cannot land without an update check. Session-start
housekeeping has gone missing on the plugin hosts before (#3524), and the harness
that forgets is by definition the one whose test nobody wrote.
Known window, documented in the module doc: staging replaces dist/ wholesale, so a
hook spawning during the copy can fail to load. Running processes are unaffected,
the window is milliseconds once a day, and the cost is one turn without memory.
Serialising against it would need a lock every hook takes on every turn — a worse
trade than the window it closes.
Also hoists survey.ts's `binExists` to util.ts as `binOnPath`.
Claude-Session: https://claude.ai/code/session_011KDT484YujNcBxHzbfzNbk1 parent c959122 commit 4fa110d
15 files changed
Lines changed: 1114 additions & 162 deletions
File tree
- hindsight-docs/docs-integrations
- hindsight-integrations/coding-agents
- skill
- src
- core
- skills/hindsight-docs/references/sdks/integrations
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Lines changed: 421 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 365 additions & 0 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
124 | 124 | | |
125 | 125 | | |
126 | 126 | | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
127 | 134 | | |
128 | 135 | | |
129 | 136 | | |
| |||
201 | 208 | | |
202 | 209 | | |
203 | 210 | | |
| 211 | + | |
204 | 212 | | |
205 | 213 | | |
206 | 214 | | |
| |||
344 | 352 | | |
345 | 353 | | |
346 | 354 | | |
| 355 | + | |
347 | 356 | | |
348 | 357 | | |
349 | 358 | | |
| |||
433 | 442 | | |
434 | 443 | | |
435 | 444 | | |
| 445 | + | |
436 | 446 | | |
437 | 447 | | |
438 | 448 | | |
| |||
452 | 462 | | |
453 | 463 | | |
454 | 464 | | |
| 465 | + | |
455 | 466 | | |
456 | 467 | | |
457 | 468 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| |||
112 | 113 | | |
113 | 114 | | |
114 | 115 | | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
115 | 120 | | |
116 | 121 | | |
117 | 122 | | |
| |||
Lines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
| |||
352 | 353 | | |
353 | 354 | | |
354 | 355 | | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
355 | 362 | | |
356 | 363 | | |
357 | 364 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
| 40 | + | |
41 | 41 | | |
42 | | - | |
| 42 | + | |
43 | 43 | | |
| 44 | + | |
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
| |||
117 | 118 | | |
118 | 119 | | |
119 | 120 | | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | 121 | | |
143 | 122 | | |
144 | 123 | | |
| |||
318 | 297 | | |
319 | 298 | | |
320 | 299 | | |
321 | | - | |
| 300 | + | |
322 | 301 | | |
323 | 302 | | |
324 | 303 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
2 | 31 | | |
3 | 32 | | |
4 | 33 | | |
| |||
0 commit comments