@pellux/goodvibes-daemon 1.28.19 → 1.28.20

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 (108) hide show
  1. package/CHANGELOG.md +85 -63
  2. package/README.md +13 -13
  3. package/bin/launcher-support.js +5 -5
  4. package/package.json +6 -4
  5. package/scripts/postinstall.js +8 -8
  6. package/src/cli/command-catalog.ts +22 -22
  7. package/src/cli/completion.ts +4 -4
  8. package/src/cli/help.ts +5 -5
  9. package/src/cli/index.ts +3 -3
  10. package/src/cli/parser.ts +2 -2
  11. package/src/cli/surface-catalog.ts +1 -1
  12. package/src/cli/types.ts +2 -2
  13. package/src/cluster/daemon-ws-call.ts +5 -5
  14. package/src/cluster/raw-reply-route.ts +5 -5
  15. package/src/config/checkpoint-settings.ts +7 -7
  16. package/src/config/config-key-guard.ts +22 -0
  17. package/src/config/run-daemon-config-migration.ts +3 -3
  18. package/src/config/secret-config.ts +7 -7
  19. package/src/config/surface.ts +3 -3
  20. package/src/core/pairing-banner.ts +5 -5
  21. package/src/daemon/cli.ts +45 -43
  22. package/src/daemon/config-command.ts +15 -15
  23. package/src/daemon/handlers/context.ts +1 -1
  24. package/src/daemon/handlers/contracts.ts +4 -4
  25. package/src/daemon/handlers/credentials.ts +1 -1
  26. package/src/daemon/handlers/drafts/draft-store.ts +3 -3
  27. package/src/daemon/handlers/drafts/register.ts +4 -4
  28. package/src/daemon/handlers/inbox/aggregator.ts +8 -8
  29. package/src/daemon/handlers/inbox/cursor-store.ts +10 -10
  30. package/src/daemon/handlers/inbox/index.ts +7 -7
  31. package/src/daemon/handlers/inbox/mapping.ts +2 -2
  32. package/src/daemon/handlers/inbox/poller.ts +5 -5
  33. package/src/daemon/handlers/inbox/provider-adapter.ts +8 -8
  34. package/src/daemon/handlers/inbox/providers/discord.ts +6 -6
  35. package/src/daemon/handlers/inbox/providers/email.ts +3 -3
  36. package/src/daemon/handlers/inbox/providers/imap-client.ts +1 -1
  37. package/src/daemon/handlers/inbox/providers/slack.ts +4 -4
  38. package/src/daemon/handlers/index.ts +18 -8
  39. package/src/daemon/handlers/payments/card-store.ts +486 -0
  40. package/src/daemon/handlers/payments/index.ts +32 -0
  41. package/src/daemon/handlers/payments/purchase-ledger.ts +108 -0
  42. package/src/daemon/handlers/payments/register.ts +391 -0
  43. package/src/daemon/handlers/register.ts +3 -3
  44. package/src/daemon/handlers/remote/backends/cloud-terminal.ts +9 -1
  45. package/src/daemon/handlers/remote/backends/process-runner.ts +1 -1
  46. package/src/daemon/handlers/remote/backends/ssh.ts +9 -1
  47. package/src/daemon/handlers/remote/backends/types.ts +2 -2
  48. package/src/daemon/handlers/remote/dispatcher.ts +3 -3
  49. package/src/daemon/handlers/remote/index.ts +1 -1
  50. package/src/daemon/handlers/remote/peer-registry.ts +62 -13
  51. package/src/daemon/handlers/routing/inbox-bridge.ts +5 -5
  52. package/src/daemon/handlers/routing/index.ts +1 -1
  53. package/src/daemon/handlers/routing/route-store.ts +1 -1
  54. package/src/daemon/handlers/routing/routing-resolver.ts +3 -3
  55. package/src/daemon/handlers/sqlite-store.ts +9 -9
  56. package/src/daemon/handlers/triage/index.ts +1 -1
  57. package/src/daemon/handlers/triage/integration.ts +3 -3
  58. package/src/daemon/handlers/triage/pipeline.ts +2 -2
  59. package/src/daemon/handlers/triage/scorer.ts +2 -2
  60. package/src/daemon/handlers/triage/tagger/discord.ts +3 -3
  61. package/src/daemon/handlers/triage/tagger/imap.ts +7 -7
  62. package/src/daemon/handlers/triage/tagger/index.ts +1 -1
  63. package/src/daemon/handlers/triage/tagger/shared.ts +3 -3
  64. package/src/daemon/handlers/triage/tagger/slack.ts +1 -1
  65. package/src/daemon/handlers/triage/types.ts +2 -2
  66. package/src/daemon/lifecycle.ts +5 -5
  67. package/src/daemon/local-daemon-state.ts +7 -7
  68. package/src/daemon/pair-command.ts +14 -14
  69. package/src/daemon/provision-wake-model.ts +5 -5
  70. package/src/daemon/send/channels.ts +7 -7
  71. package/src/daemon/send/command.ts +11 -11
  72. package/src/daemon/send/composition.ts +5 -5
  73. package/src/daemon/send/failure-text.ts +6 -6
  74. package/src/daemon/send/inert-text.ts +18 -18
  75. package/src/daemon/send/stdin.ts +3 -3
  76. package/src/daemon/service-commands.ts +32 -32
  77. package/src/daemon/sessions-command.ts +7 -7
  78. package/src/daemon/status-command.ts +22 -22
  79. package/src/daemon/webui-command.ts +14 -14
  80. package/src/runtime/boot-tasks.ts +1 -1
  81. package/src/runtime/cluster-composition.ts +9 -9
  82. package/src/runtime/cluster-group-composition.ts +7 -7
  83. package/src/runtime/conversation-rewind-port.ts +8 -8
  84. package/src/runtime/credential-composition.ts +2 -2
  85. package/src/runtime/daemon-handler-composition.ts +44 -4
  86. package/src/runtime/device-posture-composition.ts +10 -10
  87. package/src/runtime/disposal-wiring.ts +8 -8
  88. package/src/runtime/fleet-needs-input-push.ts +4 -4
  89. package/src/runtime/fleet-services.ts +1 -1
  90. package/src/runtime/hosted-session-composition.ts +13 -13
  91. package/src/runtime/index.ts +1 -1
  92. package/src/runtime/knowledge-services.ts +2 -2
  93. package/src/runtime/legacy-daemon-migration.ts +43 -43
  94. package/src/runtime/legacy-daemon-reconcile.ts +30 -30
  95. package/src/runtime/mail-composition.ts +6 -6
  96. package/src/runtime/notification-dispatch.ts +7 -7
  97. package/src/runtime/payments-composition.ts +143 -0
  98. package/src/runtime/plugin-composition.ts +7 -7
  99. package/src/runtime/runtime-services-types.ts +9 -9
  100. package/src/runtime/services.ts +29 -27
  101. package/src/runtime/trigger-services.ts +1 -1
  102. package/src/runtime/trust/checkpoint-eligibility.ts +5 -5
  103. package/src/runtime/trust/trust-gated-approvals.ts +9 -9
  104. package/src/runtime/update-check.ts +4 -4
  105. package/src/runtime/workspace-checkpointing.ts +6 -6
  106. package/src/testing/daemon-fixture.ts +11 -11
  107. package/src/testing/hosted-session-failures.ts +4 -4
  108. package/src/version.ts +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,27 @@ All notable changes to the GoodVibes daemon.
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.28.20] - 2026-08-21
8
+
9
+ ### Changes
10
+
11
+ - **The payments surface answers on the standalone daemon** (platform runtime
12
+ 2.0.18): cards list/create/delete, budget status, and the purchase ledger
13
+ serve external clients (webui, the desktop app) instead of answering 501.
14
+ Card metadata lives beside the daemon's other control-plane stores and card
15
+ material sits per field in the daemon secure secret tier; a corrupt card
16
+ file refuses writes rather than orphaning stored material, a CVV is never
17
+ supplied while cvvHandling is prompt, and the two checkout verbs keep an
18
+ honest 501 until the browser-driver seam exists.
19
+ - **Stored remote-peer rows are validated when read**: a corrupt or
20
+ hand-edited row raises a typed error instead of flowing downstream
21
+ malformed, and removing such a row still works.
22
+ - **Remote backends sweep their credential directories at construction**, so
23
+ a crashed daemon no longer leaves plaintext keys on disk across restarts.
24
+ - Config keys are narrowed by a shared type guard instead of casts, and the
25
+ daemon's CI now gates coverage, architecture, and the sdk pin before any
26
+ tag exists.
27
+
7
28
  ## [1.28.19] - 2026-08-15
8
29
 
9
30
  ### Changes
@@ -15,7 +36,7 @@ All notable changes to the GoodVibes daemon.
15
36
  records fold in automatically, newest first, legacy files untouched.
16
37
  - **A turn through a strict OpenAI-compatible gateway works again** (platform
17
38
  runtime 2.0.17): the edit tool's schema declared a union with `oneOf`,
18
- which validators like abacus RouteLLM now reject wholesale every hosted
39
+ which validators like abacus RouteLLM now reject wholesale, and every hosted
19
40
  turn through such a provider failed with "Extra inputs are not permitted".
20
41
  The union is `anyOf` now, pinned by a wire-compatibility test.
21
42
 
@@ -25,8 +46,8 @@ All notable changes to the GoodVibes daemon.
25
46
 
26
47
  - **The keep-awake inhibitor can no longer paint an authentication prompt on
27
48
  a terminal** (platform runtime 2.0.15): a polkit refusal of the sleep
28
- inhibitor typical for tmux/SSH sessions logind does not count as an
29
- active seat used to register systemd's interactive auth agent on the
49
+ inhibitor, typical for tmux/SSH sessions logind does not count as an
50
+ active seat, used to register systemd's interactive auth agent on the
30
51
  controlling terminal. The inhibitor is now requested with
31
52
  `--no-ask-password`; refusal is silent and the platform runs without it.
32
53
 
@@ -36,8 +57,8 @@ All notable changes to the GoodVibes daemon.
36
57
 
37
58
  - **Post-wake capture ends when the speaker stops, on real microphones**
38
59
  (platform runtime 2.0.14): the silence floor now follows the room during
39
- capture a headset's automatic gain control ramping up after speech no
40
- longer holds the microphone open and breath ticks shorter than
60
+ capture, so a headset's automatic gain control ramping up after speech no
61
+ longer holds the microphone open, and breath ticks shorter than
41
62
  `voice.wake.speechRetriggerMs` (new setting, default 150 ms) no longer
42
63
  reset the silence clock. A pinned `voice.wake.silenceFloorRms` still
43
64
  freezes the floor completely.
@@ -54,7 +75,7 @@ All notable changes to the GoodVibes daemon.
54
75
  module scope; under the single-file compiler's nondeterministic module
55
76
  order that call could run before the helper exists, killing the binary at
56
77
  load. The table is now built on first use. Found by auditing for the same
57
- build-order lottery class 1.28.15 fixed this was the last direct
78
+ build-order lottery class 1.28.15 fixed. This was the last direct
58
79
  module-scope call into the platform runtime in this product.
59
80
 
60
81
  ## [1.28.15] - 2026-08-07
@@ -66,7 +87,7 @@ All notable changes to the GoodVibes daemon.
66
87
  off the SDK's runtime namespace objects at module scope (`export const X =
67
88
  ns.X`). Bun's single-file compiler emits module bodies in an order that
68
89
  varies build-to-build, and such a read can land before the module that
69
- defines the binding the binary then dies at load with a ReferenceError on
90
+ defines the binding, and the binary then dies at load with a ReferenceError on
70
91
  some builds of identical source. Every one of those reads is now a grouped
71
92
  live re-export from the SDK's registered runtime subpaths, resolved by the
72
93
  module system instead of read at module scope. The shared post-build smoke
@@ -83,7 +104,7 @@ All notable changes to the GoodVibes daemon.
83
104
  fan or steady background noise no longer holds the microphone open to the
84
105
  ceiling on every capture. The floor is a real setting
85
106
  (`voice.wake.silenceFloorRms`, 0 = adaptive), and
86
- `voice.wake.captureMaxSeconds: 0` now genuinely means no hard maximum
107
+ `voice.wake.captureMaxSeconds: 0` now genuinely means no hard maximum:
87
108
  whisper has no input limit, and capture closes on silence.
88
109
  - The exec sandbox's self-description now names the built-in tools a turn
89
110
  should use for daemon status and settings, so an assistant inside the
@@ -97,13 +118,13 @@ All notable changes to the GoodVibes daemon.
97
118
  - **Fixed: an unnamed transcription or synthesis request goes to the voice
98
119
  provider the user actually configured** (platform runtime 2.0.11). The
99
120
  daemon's provider picker answered "use whatever this host has configured"
100
- with the first name on its internal registration list a cloud provider
121
+ with the first name on its internal registration list, a cloud provider,
101
122
  even when that provider had no key and the host carried fully provisioned
102
123
  local engines. On a machine set up for local voice, every wake-word
103
124
  transcription failed "OpenAI API key missing" while the user's working
104
125
  whisper was never asked, and no settings key could override the pick.
105
126
  Unnamed requests now prefer providers that report themselves configured,
106
- with configured local engines first free, offline, no key. A named
127
+ with configured local engines first, free, offline, no key. A named
107
128
  provider keeps exactly its previous behavior.
108
129
 
109
130
  ## [1.28.12] - 2026-08-05
@@ -113,7 +134,7 @@ All notable changes to the GoodVibes daemon.
113
134
  - **Fixed: a hosted conversational turn no longer gets the host.** The daemon
114
135
  composes hosted sessions through the same `createClientRuntimeServices` a
115
136
  terminal runs, so a hosted command already ran inside the same bubblewrap
116
- boundary network, PID, UTS and IPC namespaced, system read-only, /tmp and
137
+ boundary: network, PID, UTS and IPC namespaced, system read-only, /tmp and
117
138
  $HOME masked, `sandbox.egressAllowlist` the one way network comes back. What
118
139
  differed was the fallback: with no boundary available the command ran
119
140
  directly on the host and only said so afterwards, and a conversational turn
@@ -122,17 +143,18 @@ All notable changes to the GoodVibes daemon.
122
143
  `conversational`, which makes the boundary REQUIRED: a command that cannot be
123
144
  contained is refused, naming why, and `background: true` is not a spelling
124
145
  that gets around it. A hosted workstream that genuinely needs the machine is
125
- a per-spawn grant written into this daemon's own composition nothing on the
146
+ a per-spawn grant written into this daemon's own composition; nothing on the
126
147
  wire and nothing in a tool argument can reach it.
127
148
  - **Fixed: the owner's terminal is untouchable.** A command that drives an
128
- existing tmux session, window or pane this platform did not create
129
- send-keys, kill, resize, attach, respawn, rename is refused by the exec
130
- guard for a hosted turn, with a refusal that names the rule. Creating and
149
+ existing tmux session, window or pane this platform did not create is
150
+ refused by the exec guard for a hosted turn, with a refusal that names the
151
+ rule. The blocked verbs are send-keys, kill, resize, attach, respawn,
152
+ rename. Creating and
131
153
  driving the platform's own sessions is unchanged, and so is reading tmux
132
154
  state (`list-sessions`, `list-panes`, `capture-pane`), which the fleet view
133
155
  already does. The frozen catastrophic block is untouched.
134
156
  - Changed: this daemon's hosted operator prompt carries the platform's
135
- conversational diagnosis contract report the state and propose, never
157
+ conversational diagnosis contract: report the state and propose, never
136
158
  restart the owner's applications or type into his terminal to "fix" things,
137
159
  and a "fixed" claim needs the live evidence it rests on.
138
160
 
@@ -151,7 +173,7 @@ All notable changes to the GoodVibes daemon.
151
173
  settings unreadable; wake transcription is daemon-first with failure
152
174
  evidence in diagnostics; the managed voice installer supersedes stale
153
175
  manual paths by name and proves itself with a spoken round trip.
154
- - Changed: setup flows complete the inferred intent propose extensions,
176
+ - Changed: setup flows complete the inferred intent: propose extensions,
155
177
  ask at genuine forks, never hand the user a command; the Google
156
178
  walkthrough accepts pasted values in-conversation and answers with the
157
179
  consent link.
@@ -173,7 +195,7 @@ All notable changes to the GoodVibes daemon.
173
195
  with receipts, and the legacy directory empties and is removed.
174
196
  - **Changed: the daemon hosts conversation turns for the agent.** Hosted
175
197
  session event streams are render-grade and session-scoped, so a client
176
- rendering from the stream sees what the model said and what it did and
198
+ rendering from the stream sees what the model said and what it did, and
177
199
  agent conversations become genuinely cross-visible with their messages.
178
200
  - Changed: connecting Google is one action, with every needed scope in a
179
201
  single consent and a live mail-and-calendar proof at the end; the settings
@@ -185,7 +207,7 @@ All notable changes to the GoodVibes daemon.
185
207
  ### Changes
186
208
 
187
209
  - Changed: a conversational turn is told to understand what it captures, not
188
- just file it an itinerary also means an away-span said back in plain
210
+ just file it. An itinerary also means an away-span said back in plain
189
211
  words, travelers who are people in the owner's life, and durable facts
190
212
  about the destination, and the turn uses what it stored: it names
191
213
  collisions with existing plans and offers the obviously useful next steps
@@ -199,14 +221,14 @@ All notable changes to the GoodVibes daemon.
199
221
 
200
222
  - **Fixed: a chat message gets an answer, never workflow paperwork.** The
201
223
  conversation gate's decision that a channel message is conversation is now
202
- authoritative the review-wording heuristic that turned "I'll review the
224
+ authoritative. The review-wording heuristic that turned "I'll review the
203
225
  route" in a transcript into a full write-review-fix-confirm chain can no
204
226
  longer override it. When a chain does legitimately run, the reply sent back
205
227
  over the channel carries what the agent actually found or did; chain status
206
228
  lines stay in the operator progress feed. Duplicate replies from the two
207
229
  completion reporters are gone (platform runtime 2.0.6).
208
230
  - **Added: personal information shared in conversation gets captured.** A
209
- conversational channel turn which previously ran with no tools at all
231
+ conversational channel turn, which previously ran with no tools at all,
210
232
  now carries the profile capture tool with per-run owner authority: a trip
211
233
  itinerary pasted into Telegram lands in the owner profile's Plans section
212
234
  with its dates, flights, travelers and confirmation number, and the reply
@@ -231,15 +253,15 @@ All notable changes to the GoodVibes daemon.
231
253
  ### Changes
232
254
 
233
255
  - **Changed: payment limits hold the amount you would say out loud.** The
234
- budget settings drop their unit suffix — `payments.budget.perPurchaseCeiling`,
235
- `dailyItem`, `dailyOverage`, `overageToleranceDailyAllowance` and hold
256
+ budget settings, `payments.budget.perPurchaseCeiling`,
257
+ `dailyItem`, `dailyOverage`, `overageToleranceDailyAllowance`, drop their unit suffix and hold
236
258
  plain amounts in the configured currency, written exactly as you give them:
237
259
  `100` is a hundred dollars, `19.99` is nineteen ninety-nine, and `$100`,
238
260
  `100.00` and `100` all mean the same hundred. This daemon migrates its
239
261
  settings file on load with a receipt; your limits are unchanged, only how
240
262
  they are written (platform runtime 2.0.5).
241
263
  - Fixed: every platform state store this daemon keeps writes atomically and
242
- quarantines a corrupt file with a receipt instead of failing on it — the
264
+ quarantines a corrupt file with a receipt instead of failing on it. The
243
265
  watcher-snapshot fix from 1.28.6 is now the platform-wide rule. The daemon
244
266
  settings file itself deliberately keeps its stricter contract: an
245
267
  unparseable settings file still refuses the boot loudly, because defaults
@@ -251,7 +273,7 @@ All notable changes to the GoodVibes daemon.
251
273
 
252
274
  - **Fixed: a corrupt watcher snapshot no longer crash-loops this daemon.**
253
275
  A host freeze left the snapshot file as valid JSON followed by NUL bytes,
254
- and the daemon died parsing it once at boot, and once on a periodic tick
276
+ and the daemon died parsing it, once at boot, and once on a periodic tick
255
277
  47 seconds after every restart, so the service never stayed up. The
256
278
  platform runtime (2.0.4) now writes that file atomically and quarantines a
257
279
  corrupt one with a receipt beside it, rebuilding watcher state from live
@@ -266,7 +288,7 @@ All notable changes to the GoodVibes daemon.
266
288
  - Fixed: a client that inherited this daemon's bind host is no longer refused
267
289
  as "insecure PUBLIC transport" when that host is a wildcard. The platform
268
290
  runtime (2.0.3) classifies `0.0.0.0` and `::` with loopback and the other
269
- private-network origins a wildcard is a listen address, and dialing it
291
+ private-network origins: a wildcard is a listen address, and dialing it
270
292
  reaches the local machine. Until this fix, a daemon deliberately bound to
271
293
  `0.0.0.0` for LAN access left local clients unable to call it over plain
272
294
  http, profile reads included.
@@ -288,8 +310,8 @@ All notable changes to the GoodVibes daemon.
288
310
 
289
311
  - Fixed: importing settings that include `display.themeMode` no longer prints
290
312
  an "unknown key" warning. The SDK's configuration schema (2.0.1) now declares
291
- the key `auto` probes the terminal background once at startup, `dark` and
292
- `light` force a fixed appearance so every component ingests it as a real,
313
+ the key: `auto` probes the terminal background once at startup, `dark` and
314
+ `light` force a fixed appearance, so every component ingests it as a real,
293
315
  documented setting.
294
316
 
295
317
  ## [1.28.2] - 2026-08-01
@@ -312,7 +334,7 @@ All notable changes to the GoodVibes daemon.
312
334
 
313
335
  - `pair --host <name>` now reaches a DIFFERENT daemon instead of being refused.
314
336
  It asks that daemon to mint a brand-new per-device pairing token over
315
- `pairing.handoff.create` and prints the pairing block for it a different
337
+ `pairing.handoff.create` and prints the pairing block for it, a different
316
338
  act than the plain `pair` reprint, which still just reprints this machine's
317
339
  existing shared token and never mints. Because it changes state on a daemon
318
340
  that may not be this process's own, it states the plan and asks for
@@ -330,7 +352,7 @@ All notable changes to the GoodVibes daemon.
330
352
  and the advertised REST path.
331
353
 
332
354
  What a client gets is one merged timeline, newest first, across every
333
- provider items interleave by arrival rather than being grouped, and each
355
+ provider, items interleave by arrival rather than being grouped, and each
334
356
  carries its own `provider`, so an inbox reads like an inbox. Pages are bounded
335
357
  and walked with an opaque `nextCursor`; `cursor` stays what it was, the
336
358
  freshness watermark you hand back as `since`. That is a keyset, not an offset:
@@ -338,8 +360,8 @@ All notable changes to the GoodVibes daemon.
338
360
  every insert, so a caller walking pages during a poll would see items twice
339
361
  and miss others.
340
362
 
341
- The answer is served from this daemon's SYNCED MIRROR the sqlite store the
342
- Slack, Discord and IMAP adapters already write into on their own cadences
363
+ The answer is served from this daemon's SYNCED MIRROR, the sqlite store the
364
+ Slack, Discord and IMAP adapters already write into on their own cadences,
343
365
  and not from a fresh remote fetch per call. Four reasons, all of them about
344
366
  what a fetch-per-call would cost: a third-party rate limit would sit behind a
345
367
  read verb any client may call at any rate; the cluster hands FETCHING for each
@@ -356,7 +378,7 @@ All notable changes to the GoodVibes daemon.
356
378
  state, when it last synced, how much of the mirror is its, and whether this
357
379
  node is the one fetching it. `ready`, `empty`, `unconfigured`, `error` and
358
380
  `pending` are five different things, and a caller does something different
359
- about each a fresh install with no tokens is not an outage, and a node that
381
+ about each: a fresh install with no tokens is not an outage, and a node that
360
382
  has not looked yet is not a node reporting an empty inbox. A provider whose
361
383
  sync failed contributes no items, says why, and sets `partial`, so a short
362
384
  list is never mistaken for a quiet week. Nothing configured is an empty list
@@ -368,7 +390,7 @@ All notable changes to the GoodVibes daemon.
368
390
  path, the first rename moved it away, and the second failed with ENOENT on a
369
391
  file it had just written. Not hypothetical: the inbox poller flushes once per
370
392
  provider and polls every provider concurrently, so an ordinary two-provider
371
- startup hit it and once `channels.inbox.list` began reporting per-provider
393
+ startup hit it, and once `channels.inbox.list` began reporting per-provider
372
394
  health, the failure showed up as a provider reporting a filesystem error for
373
395
  its feed. The temp name now carries a per-process counter. Every store on
374
396
  `HandlerSqliteStore` shared the hazard, so the fix is there.
@@ -376,8 +398,8 @@ All notable changes to the GoodVibes daemon.
376
398
  - A gateway invocation that carries no context no longer throws a TypeError out
377
399
  of the handler wrapper. `normalizeContext` read `.metadata` off the context
378
400
  unconditionally, and an in-process invoke that builds the invocation by hand
379
- can omit it; an absent context now reads as the empty one no principal, no
380
- scopes, not admin, nobody claiming a person asked which can only cost a
401
+ can omit it; an absent context now reads as the empty one: no principal, no
402
+ scopes, not admin, nobody claiming a person asked, which can only cost a
381
403
  caller an authorization it never proved, never grant one.
382
404
 
383
405
  - Three config modules the terminal app carried a byte-identical copy of are the
@@ -391,7 +413,7 @@ All notable changes to the GoodVibes daemon.
391
413
  `parseConfigValueText`. `src/cli/config-value.ts` held a byte-identical copy
392
414
  of that function and its `cli/index.ts` re-export is gone with it. The copy
393
415
  existed because the shared one was private; it is exported now, and one
394
- implementation is the whole point `--config x=false` and `config set x
416
+ implementation is the whole point: `--config x=false` and `config set x
395
417
  false` must write the same thing.
396
418
 
397
419
  - The local `sql.js` ambient declaration is gone. The SDK ships the declaration
@@ -414,17 +436,17 @@ All notable changes to the GoodVibes daemon.
414
436
  installed agents) and `acp.sessions.create` (spawn one as a long-lived
415
437
  session) were cataloged and advertised as callable on every build, and
416
438
  answered nothing: the composition never constructed the ACP host they are
417
- handlers for. `runtime/services.ts` now builds that host permission asks
439
+ handlers for. `runtime/services.ts` now builds that host: permission asks
418
440
  from a hosted agent route through the same shared approval broker every
419
441
  other confirmation rides, and each hosted agent registers onto a shared
420
- session so it is attachable and steerable like any native one and threads
442
+ session so it is attachable and steerable like any native one, and threads
421
443
  it into the gateway registration and the fleet registry, so a hosted agent
422
444
  also shows up as a fleet row.
423
445
 
424
446
  - The command line is the daemon's, and it is an operator surface rather than a
425
447
  way to start a process. It shipped carrying the terminal app's parser: a table
426
- of two dozen command words `tui`, `run`, `doctor`, `models`, `providers`,
427
- `auth`, `secrets`, `plugin` against an entry point that dispatched on help,
448
+ of two dozen command words (`tui`, `run`, `doctor`, `models`, `providers`,
449
+ `auth`, `secrets`, `plugin`) against an entry point that dispatched on help,
428
450
  version and four service verbs. Everything else fell through to "start a
429
451
  daemon in the foreground", so `goodvibes-daemon status` served, and so did
430
452
  `goodvibes-daemon install-servce`. The parser's own unknown-command error was
@@ -434,9 +456,9 @@ All notable changes to the GoodVibes daemon.
434
456
  (`src/cli/command-catalog.ts`) that the parser, the help text and the shell
435
457
  completions all read. Serving happens on a bare invocation or on `serve`, and
436
458
  on nothing else; any other unrecognized word exits 2 with `Unknown command: X`
437
- and the help. The terminal app's conversation flags `--resume`, `--continue`,
459
+ and the help. The terminal app's conversation flags (`--resume`, `--continue`,
438
460
  `--fork`, `--print`, `--prompt`, `-o/--output`, `--open`, `--no-alt-screen`,
439
- `--session`, `--strict` were accepted in silence and read by nothing; each is
461
+ `--session`, `--strict`) were accepted in silence and read by nothing; each is
440
462
  now refused by name and says which surface owns it.
441
463
 
442
464
  - New commands, all of them things a headless box's operator previously had no
@@ -452,8 +474,8 @@ All notable changes to the GoodVibes daemon.
452
474
  with the same operator token.
453
475
  - `config list|get|set|unset` reads and writes this machine's settings
454
476
  directly, so it works whether or not a daemon is running. Every value it
455
- PRINTS goes through the redaction rules first a token, a password or an API
456
- key reads as `<redacted>` while `config set` still writes the real value.
477
+ PRINTS goes through the redaction rules first, so a token, a password or an API
478
+ key reads as `<redacted>`, while `config set` still writes the real value.
457
479
  - `pair` prints the pairing link and QR again, from the same renderer the
458
480
  daemon uses at startup and carrying the same existing token, so the block is
459
481
  no longer lost when the boot banner scrolls away.
@@ -468,14 +490,14 @@ All notable changes to the GoodVibes daemon.
468
490
  - `completion bash|zsh|fish`, generated from the catalog, and `help <command>`
469
491
  for any command's own arguments and flags.
470
492
 
471
- - `service-status` answers with an exit code 0 installed and running, 3
472
- installed but not running, 4 not installed and takes `--json`. A script no
493
+ - `service-status` answers with an exit code: 0 installed and running, 3
494
+ installed but not running, 4 not installed, and takes `--json`. A script no
473
495
  longer has to read the prose to find out.
474
496
 
475
497
  - The help text describes the binary that exists: every command, the flags that
476
498
  work, `-y/--yes`, `--config`, `--enable`/`--disable`, `--json`, the exit codes,
477
499
  and a systemd user service, a launchd agent or a Scheduled Task depending on
478
- the platform it is printed on it used to say systemd on every platform,
500
+ the platform it is printed on. It used to say systemd on every platform,
479
501
  including macOS, where `install-service` writes a launchd agent.
480
502
 
481
503
  - `status` reads the daemon's identity, health and channel routes with the
@@ -492,7 +514,7 @@ All notable changes to the GoodVibes daemon.
492
514
  floor is built (`DaemonConfig.hostedSessions`, wired in
493
515
  `runtime/hosted-session-composition.ts`) turns on the SDK's hosted-session
494
516
  engine and its `sessions.hosted.create/attach/detach/kill/list` verbs: a full
495
- loop composed inside this process the same orchestrator, the same tool
517
+ loop composed inside this process: the same orchestrator, the same tool
496
518
  registry rooted at the named workspace, the same permission machinery a
497
519
  terminal runs. Driving one uses the verbs that already existed
498
520
  (`sessions.steer`, `sessions.followUp`, `sessions.toolCalls.cancel`,
@@ -508,7 +530,7 @@ All notable changes to the GoodVibes daemon.
508
530
  directories asks three separate questions.
509
531
 
510
532
  Detaching is governed by `hostedSessions.detachPolicy`, which defaults to
511
- `kill` closing a client has always ended its work. `survive` opts into
533
+ `kill`. Closing a client has always ended its work. `survive` opts into
512
534
  sessions that outlive both the client and a restart of this daemon; a single
513
535
  session may override the setting when it is created. `hostedSessions.maxSessions`
514
536
  caps how many loops this machine holds at once, and the transcript bound and
@@ -532,13 +554,13 @@ All notable changes to the GoodVibes daemon.
532
554
  by id. The runtime is handed over whole, deliberately: the verbs and the `phone`
533
555
  tool must reach the same service, because a second path to a phone would be a second
534
556
  place the confirmation prompt and the durable grants could be decided differently.
535
- Nothing about the gates moved the prompt still rides this daemon's shared approval
557
+ Nothing about the gates moved. The prompt still rides this daemon's shared approval
536
558
  seam and appears wherever the person is looking.
537
559
 
538
560
  - Conversation-scope rewind stopped answering for sessions it holds nothing for.
539
561
  `conversation-rewind-port.ts` resolves a session's conversation from an in-process
540
562
  registry, and while the conversation loops run in the surfaces that registry is
541
- empty here. It reported "0 messages to drop" the same answer a conversation
563
+ empty here. It reported "0 messages to drop", the same answer a conversation
542
564
  already at the anchor gives, so a caller could not tell a rewind that found nothing
543
565
  from one that reached nobody. It now reports the anchor as unavailable with the
544
566
  reason, which `rewind.plan` surfaces as a warning and `rewind.apply` records instead
@@ -555,8 +577,8 @@ All notable changes to the GoodVibes daemon.
555
577
  rode a terminal-app release. It now has its own repository, its own release line and its own
556
578
  binary, and the terminal app and the agent become clients of it.
557
579
 
558
- - The suite installer lives here now. `scripts/install.sh` the script behind
559
- `curl -fsSL https://goodvibes.sh/install.sh | sh` moved out of the terminal app's
580
+ - The suite installer lives here now. `scripts/install.sh` (the script behind
581
+ `curl -fsSL https://goodvibes.sh/install.sh | sh`) moved out of the terminal app's
560
582
  repository into this one, because the daemon is the product everything else is
561
583
  installed alongside and this repository's release lane is the one that publishes it.
562
584
  There is exactly one copy: two installers in two repositories is how two installers
@@ -585,7 +607,7 @@ All notable changes to the GoodVibes daemon.
585
607
  act, and the install receipt prints both the URL and the one command that does it.
586
608
 
587
609
  - **New: `goodvibes-daemon webui enable | disable | status`.** The command that owns
588
- serving the web UI which directory, whether it is served at all, and the honest
610
+ serving the web UI: which directory, whether it is served at all, and the honest
589
611
  answer to "what URL do I open and who can reach it". `enable --bundle-dir <dir>`
590
612
  refuses a directory with no index.html rather than pointing the daemon at something it
591
613
  cannot serve; `--lan` is the one act that widens exposure and `--loopback` takes it
@@ -594,7 +616,7 @@ All notable changes to the GoodVibes daemon.
594
616
  The URL it reports is the control-plane origin, because that is the listener serving
595
617
  the bundle. `web.port` is the surface's declared endpoint and nothing binds it, so
596
618
  `enable` also replaces the shipped `web.publicBaseUrl` placeholder (`http://127.0.0.1:3423`)
597
- with the origin that actually answers leaving any value an operator chose alone, and
619
+ with the origin that actually answers, leaving any value an operator chose alone, and
598
620
  saying so when the two differ.
599
621
 
600
622
  - The daemon updates itself from this repository. The platform default for
@@ -610,7 +632,7 @@ All notable changes to the GoodVibes daemon.
610
632
  settings file carries it and no migration rewrites it. The terminal repository no
611
633
  longer builds daemon binaries, so those daemons resolve a release with no
612
634
  `goodvibes-daemon-<os>-<arch>` asset and fail. Pointing them at this repository
613
- instead does not rescue them either their shipped updater adds the terminal binary
635
+ instead does not rescue them either. Their shipped updater adds the terminal binary
614
636
  beside them to the same all-or-nothing download whenever `goodvibes` sits in the
615
637
  install directory, which `scripts/install.sh` guarantees, and this repository
616
638
  deliberately publishes no terminal binary. There is also no remote write path to
@@ -629,7 +651,7 @@ All notable changes to the GoodVibes daemon.
629
651
  Named rather than implied away: the SDK's `resolveDaemonInstalledFiles` still adds
630
652
  the terminal binary to the daemon's OWN update target set when one sits beside it, so
631
653
  on a three-binary install a daemon from this repository cannot yet complete an
632
- unattended self-update either it asks for a `goodvibes-<os>-<arch>` asset this
654
+ unattended self-update either. It asks for a `goodvibes-<os>-<arch>` asset this
633
655
  repository does not publish and takes the 404. Making each product update strictly
634
656
  its own files is an SDK change, not one this repository can make.
635
657
 
@@ -650,7 +672,7 @@ All notable changes to the GoodVibes daemon.
650
672
 
651
673
  Nothing about where this daemon keeps its state changed. The hoisted modules
652
674
  that used to spell the storage scope now take it as a parameter, and every
653
- call site here passes the daemon's own the work plan, the session surface,
675
+ call site here passes the daemon's own: the work plan, the session surface,
654
676
  the workspace trust file, the code-index database and the operator-token
655
677
  pruning candidates all resolve to exactly the paths they resolved to before.
656
678
 
@@ -663,13 +685,13 @@ All notable changes to the GoodVibes daemon.
663
685
  which the shared override path only does for whole `key=value` strings.
664
686
 
665
687
  - The command line is parsed by the shared argument engine, driven by this
666
- binary's catalog. `src/cli/parser.ts` was a full engine the command-word
667
- pre-scan, arity skipping, `--`, inline `=value`, per-kind application with
688
+ binary's catalog. `src/cli/parser.ts` was a full engine: the command-word
689
+ pre-scan, arity skipping, `--`, inline `=value`, per-kind application, with
668
690
  a switch over one product's flag field names. The engine is now
669
691
  `parseWithCatalog`, and `src/cli/command-catalog.ts` is the vocabulary it
670
692
  reads: the same commands, the same aliases, the same flags per command, the
671
693
  same refusal that an unrecognized word exits 2 rather than starting a daemon.
672
- One sentence reads differently a conversation flag this binary does not
694
+ One sentence reads differently. A conversation flag this binary does not
673
695
  have is now refused as "`--resume` is not a goodvibes-daemon flag — resuming
674
696
  a conversation, a terminal app concern that belongs to another surface."
675
697
 
@@ -681,5 +703,5 @@ All notable changes to the GoodVibes daemon.
681
703
  its tools when an optional install quietly fails. They are declared here at
682
704
  the ranges the platform states, and a dependency check makes a missing one
683
705
  fail at build time instead of at the first hosted turn. `@anthropic-ai/vertex-sdk`
684
- and `@aws/bedrock-token-generator` are removed nothing in this repository or
706
+ and `@aws/bedrock-token-generator` are removed. Nothing in this repository or
685
707
  the platform imports either.
package/README.md CHANGED
@@ -25,8 +25,8 @@ A **product** over `@pellux/goodvibes-sdk`, exactly like the TUI and the agent a
25
25
  - the CLI (`send`, `cluster`, `webui`, `provision-wake-model`, `install-service` and friends),
26
26
  - packaging: the compiled `goodvibes-daemon-<os>-<arch>` binaries.
27
27
 
28
- Every engine the facade, the routes, the brokers, the updater, the channel adapters, the
29
- schedulers lives in the SDK and is consumed from the published package. Nothing was moved out of
28
+ Every engine (the facade, the routes, the brokers, the updater, the channel adapters, the
29
+ schedulers) lives in the SDK and is consumed from the published package. Nothing was moved out of
30
30
  the SDK to build this repository, and nothing should be: a capability that both a client and the
31
31
  daemon need belongs in the SDK, not here.
32
32
 
@@ -34,7 +34,7 @@ daemon need belongs in the SDK, not here.
34
34
 
35
35
  The daemon's version is **1.28.0**. Live installs already carry a settings reader-floor
36
36
  (`$goodvibes.minReaderVersion`), the update handover compares versions monotonically, and the
37
- rejected-version record is keyed by version those three mechanics all depend on the version
37
+ rejected-version record is keyed by version. Those three mechanics all depend on the version
38
38
  line staying continuous and monotonically increasing.
39
39
 
40
40
  ## Install
@@ -43,14 +43,14 @@ line staying continuous and monotonically increasing.
43
43
  curl -fsSL https://goodvibes.sh/install.sh | sh
44
44
  ```
45
45
 
46
- This installs the whole GoodVibes suite the daemon, the terminal app, the
47
- agent, and the browser operator surface from checksum-verified binaries,
46
+ This installs the whole GoodVibes suite (the daemon, the terminal app, the
47
+ agent, and the browser operator surface) from checksum-verified binaries,
48
48
  with no package manager involved.
49
49
 
50
50
  The browser surface is not a fourth binary and not a fourth service: the bundle
51
51
  unpacks to `<install dir>/webui/<version>` and this daemon serves it on its own
52
52
  listener, same origin as the API. Installing it exposes nothing new to your
53
- network the shipped binding is loopback and the installer does not change it.
53
+ network. The shipped binding is loopback and the installer does not change it.
54
54
  `goodvibes-daemon webui --lan` is the deliberate act that widens it, and
55
55
  `goodvibes-daemon webui status` says which posture is in force.
56
56
 
@@ -101,13 +101,13 @@ goodvibes-daemon provision-wake-model
101
101
 
102
102
  ## Documentation
103
103
 
104
- - [Getting Started](docs/getting-started.md) install, first boot, pairing, where state lives, health checks
105
- - [Command Reference](docs/commands-reference.md) every command, its flags, and its exit codes
106
- - [Configuration](docs/configuration.md) the settings this daemon reads, by key
107
- - [Service and Deployment](docs/service-and-deployment.md) the host service, migration from an older install, `--daemon-home` vs the data home
108
- - [Updates and Rollback](docs/updates-and-rollback.md) the hourly self-update loop, automatic crash-loop rollback, `.previous`
109
- - [Daemon-Hosted Sessions](docs/hosted-sessions.md) conversations that run inside the daemon and outlive any one client
110
- - [Troubleshooting](docs/troubleshooting.md) startup failures, log locations, port conflicts, service-status oddities
104
+ - [Getting started](docs/getting-started.md): install, first boot, pairing, where state lives, health checks
105
+ - [Command reference](docs/commands-reference.md): every command, its flags, and its exit codes
106
+ - [Configuration](docs/configuration.md): the settings this daemon reads, by key
107
+ - [Service and deployment](docs/service-and-deployment.md): the host service, migration from an older install, `--daemon-home` vs the data home
108
+ - [Updates and rollback](docs/updates-and-rollback.md): the hourly self-update loop, automatic crash-loop rollback, `.previous`
109
+ - [Daemon-hosted sessions](docs/hosted-sessions.md): conversations that run inside the daemon and outlive any one client
110
+ - [Troubleshooting](docs/troubleshooting.md): startup failures, log locations, port conflicts, service-status oddities
111
111
 
112
112
  ## License
113
113
 
@@ -14,8 +14,8 @@
14
14
  *
15
15
  * The sqlite-vec native addon gets the same self-heal: `resolveSqliteVecPath()`
16
16
  * (platform/state/sqlite-vec-loader.ts, reached from a compiled binary) expects
17
- * it at `<execDir>/lib/sqlite-vec-<platform>-<arch>/vec0.<suffix>` —
18
- * `vendor/lib/...` once the binary above is placed there and a blocked
17
+ * it at `<execDir>/lib/sqlite-vec-<platform>-<arch>/vec0.<suffix>`,
18
+ * `vendor/lib/...` once the binary above is placed there, and a blocked
19
19
  * postinstall never staged it either. Without this, a self-healed install
20
20
  * would get the daemon binary back but stay on lexical-only search forever.
21
21
  */
@@ -26,7 +26,7 @@ import { join } from 'node:path';
26
26
 
27
27
  const SUPPORTED_TARGETS = ['linux-x64', 'linux-arm64', 'darwin-x64', 'darwin-arm64'];
28
28
 
29
- /** Names the sqlite-vec addon for a platform/arch mirrors resolveSqliteVecAsset
29
+ /** Names the sqlite-vec addon for a platform/arch, mirrors resolveSqliteVecAsset
30
30
  * in the SDK's platform/runtime/self-update module. Reimplemented (rather than
31
31
  * imported) so this launcher never depends on that package resolving: it must
32
32
  * keep working from the packaged bin/ directory alone, with no bundler step
@@ -120,11 +120,11 @@ export async function ensureVendoredBinary({ packageRoot, artifactName }) {
120
120
  * Self-heal counterpart to `ensureVendoredBinary` for the sqlite-vec native
121
121
  * addon. Same shape, same verification posture (a missing manifest entry does
122
122
  * not block the install, matching `ensureVendoredBinary` above), same
123
- * vendor/ destination placed at `vendor/lib/sqlite-vec-<platform>-<arch>/
123
+ * vendor/ destination, placed at `vendor/lib/sqlite-vec-<platform>-<arch>/
124
124
  * vec0.<suffix>`, which is exactly where `resolveSqliteVecPath()` looks
125
125
  * relative to the vendored binary this module also places. A platform/arch
126
126
  * with no addon (`resolveSqliteVecAddonName` returns null) is a no-op, not an
127
- * error same as an unsupported binary target.
127
+ * error, same as an unsupported binary target.
128
128
  */
129
129
  export async function ensureVendoredSqliteVecAddon({ packageRoot, platform, arch }) {
130
130
  const asset = resolveSqliteVecAddonName(platform, arch);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-daemon",
3
- "version": "1.28.19",
3
+ "version": "1.28.20",
4
4
  "description": "The GoodVibes daemon \u2014 the one long-running host for the control plane, channels, cluster membership, scheduled work, knowledge and memory stores, and the verb families every GoodVibes client calls.",
5
5
  "type": "module",
6
6
  "main": "src/daemon/cli.ts",
@@ -32,10 +32,12 @@
32
32
  "typecheck": "bunx tsc --noEmit",
33
33
  "typecheck:test": "bunx tsc --noEmit -p tsconfig.test.json",
34
34
  "test": "bun run scripts/run-tests.ts",
35
+ "test:coverage": "bun run scripts/coverage-gate.ts",
35
36
  "preinstall": "sh scripts/check-bun.sh",
36
37
  "postinstall": "bun scripts/postinstall.js",
37
38
  "postbuild": "bun scripts/postinstall.js --no-download",
38
39
  "version": "bun run scripts/prebuild.ts",
40
+ "architecture:check": "bun run scripts/check-architecture.ts",
39
41
  "workflows:check": "bun run scripts/check-workflows.ts",
40
42
  "changelog:check": "bun run scripts/check-changelog.ts",
41
43
  "verify:tag-version": "bun run scripts/verify-release-tag-version.ts",
@@ -65,8 +67,8 @@
65
67
  "@anthropic-ai/bedrock-sdk": "^0.28.1",
66
68
  "@anthropic-ai/sdk": "^0.82.0",
67
69
  "@ast-grep/napi": "^0.42.0",
68
- "@pellux/goodvibes-sdk": "2.0.17",
69
- "@pellux/goodvibes-terminal-shell": "2.0.17",
70
+ "@pellux/goodvibes-sdk": "2.0.18",
71
+ "@pellux/goodvibes-terminal-shell": "2.0.18",
70
72
  "bash-language-server": "^5.6.0",
71
73
  "fuse.js": "^7.1.0",
72
74
  "graphql": "^16.13.2",
@@ -87,7 +89,7 @@
87
89
  "web-tree-sitter": "^0.26.7"
88
90
  },
89
91
  "devDependencies": {
90
- "@pellux/goodvibes-toolchain": "2.0.17",
92
+ "@pellux/goodvibes-toolchain": "2.0.18",
91
93
  "@types/bun": "^1.3.10",
92
94
  "typescript": "^5.9.3"
93
95
  },