@pellux/goodvibes-daemon 1.28.18 → 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 +100 -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 +7 -7
  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 +31 -29
  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,14 +4,50 @@ All notable changes to the GoodVibes daemon.
4
4
 
5
5
  ---
6
6
 
7
- ## [1.28.18] - 2026-08-08
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
+
28
+ ## [1.28.19] - 2026-08-15
29
+
30
+ ### Changes
31
+
32
+ - **Signing in to a provider subscription anywhere on the platform now reaches
33
+ this daemon** (platform runtime 2.0.17): subscription sessions move to the
34
+ shared tier, so a ChatGPT/Codex login completed in a terminal surface is
35
+ immediately usable by the daemon that runs the turns; existing per-surface
36
+ records fold in automatically, newest first, legacy files untouched.
37
+ - **A turn through a strict OpenAI-compatible gateway works again** (platform
38
+ runtime 2.0.17): the edit tool's schema declared a union with `oneOf`,
39
+ which validators like abacus RouteLLM now reject wholesale, and every hosted
40
+ turn through such a provider failed with "Extra inputs are not permitted".
41
+ The union is `anyOf` now, pinned by a wire-compatibility test.
42
+
43
+ ## [1.28.18] - 2026-08-15
8
44
 
9
45
  ### Changes
10
46
 
11
47
  - **The keep-awake inhibitor can no longer paint an authentication prompt on
12
48
  a terminal** (platform runtime 2.0.15): a polkit refusal of the sleep
13
- inhibitor typical for tmux/SSH sessions logind does not count as an
14
- 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
15
51
  controlling terminal. The inhibitor is now requested with
16
52
  `--no-ask-password`; refusal is silent and the platform runs without it.
17
53
 
@@ -21,8 +57,8 @@ All notable changes to the GoodVibes daemon.
21
57
 
22
58
  - **Post-wake capture ends when the speaker stops, on real microphones**
23
59
  (platform runtime 2.0.14): the silence floor now follows the room during
24
- capture a headset's automatic gain control ramping up after speech no
25
- 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
26
62
  `voice.wake.speechRetriggerMs` (new setting, default 150 ms) no longer
27
63
  reset the silence clock. A pinned `voice.wake.silenceFloorRms` still
28
64
  freezes the floor completely.
@@ -39,7 +75,7 @@ All notable changes to the GoodVibes daemon.
39
75
  module scope; under the single-file compiler's nondeterministic module
40
76
  order that call could run before the helper exists, killing the binary at
41
77
  load. The table is now built on first use. Found by auditing for the same
42
- 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
43
79
  module-scope call into the platform runtime in this product.
44
80
 
45
81
  ## [1.28.15] - 2026-08-07
@@ -51,7 +87,7 @@ All notable changes to the GoodVibes daemon.
51
87
  off the SDK's runtime namespace objects at module scope (`export const X =
52
88
  ns.X`). Bun's single-file compiler emits module bodies in an order that
53
89
  varies build-to-build, and such a read can land before the module that
54
- 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
55
91
  some builds of identical source. Every one of those reads is now a grouped
56
92
  live re-export from the SDK's registered runtime subpaths, resolved by the
57
93
  module system instead of read at module scope. The shared post-build smoke
@@ -68,7 +104,7 @@ All notable changes to the GoodVibes daemon.
68
104
  fan or steady background noise no longer holds the microphone open to the
69
105
  ceiling on every capture. The floor is a real setting
70
106
  (`voice.wake.silenceFloorRms`, 0 = adaptive), and
71
- `voice.wake.captureMaxSeconds: 0` now genuinely means no hard maximum
107
+ `voice.wake.captureMaxSeconds: 0` now genuinely means no hard maximum:
72
108
  whisper has no input limit, and capture closes on silence.
73
109
  - The exec sandbox's self-description now names the built-in tools a turn
74
110
  should use for daemon status and settings, so an assistant inside the
@@ -82,13 +118,13 @@ All notable changes to the GoodVibes daemon.
82
118
  - **Fixed: an unnamed transcription or synthesis request goes to the voice
83
119
  provider the user actually configured** (platform runtime 2.0.11). The
84
120
  daemon's provider picker answered "use whatever this host has configured"
85
- 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,
86
122
  even when that provider had no key and the host carried fully provisioned
87
123
  local engines. On a machine set up for local voice, every wake-word
88
124
  transcription failed "OpenAI API key missing" while the user's working
89
125
  whisper was never asked, and no settings key could override the pick.
90
126
  Unnamed requests now prefer providers that report themselves configured,
91
- with configured local engines first free, offline, no key. A named
127
+ with configured local engines first, free, offline, no key. A named
92
128
  provider keeps exactly its previous behavior.
93
129
 
94
130
  ## [1.28.12] - 2026-08-05
@@ -98,7 +134,7 @@ All notable changes to the GoodVibes daemon.
98
134
  - **Fixed: a hosted conversational turn no longer gets the host.** The daemon
99
135
  composes hosted sessions through the same `createClientRuntimeServices` a
100
136
  terminal runs, so a hosted command already ran inside the same bubblewrap
101
- 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
102
138
  $HOME masked, `sandbox.egressAllowlist` the one way network comes back. What
103
139
  differed was the fallback: with no boundary available the command ran
104
140
  directly on the host and only said so afterwards, and a conversational turn
@@ -107,17 +143,18 @@ All notable changes to the GoodVibes daemon.
107
143
  `conversational`, which makes the boundary REQUIRED: a command that cannot be
108
144
  contained is refused, naming why, and `background: true` is not a spelling
109
145
  that gets around it. A hosted workstream that genuinely needs the machine is
110
- 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
111
147
  wire and nothing in a tool argument can reach it.
112
148
  - **Fixed: the owner's terminal is untouchable.** A command that drives an
113
- existing tmux session, window or pane this platform did not create
114
- send-keys, kill, resize, attach, respawn, rename is refused by the exec
115
- 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
116
153
  driving the platform's own sessions is unchanged, and so is reading tmux
117
154
  state (`list-sessions`, `list-panes`, `capture-pane`), which the fleet view
118
155
  already does. The frozen catastrophic block is untouched.
119
156
  - Changed: this daemon's hosted operator prompt carries the platform's
120
- conversational diagnosis contract report the state and propose, never
157
+ conversational diagnosis contract: report the state and propose, never
121
158
  restart the owner's applications or type into his terminal to "fix" things,
122
159
  and a "fixed" claim needs the live evidence it rests on.
123
160
 
@@ -136,7 +173,7 @@ All notable changes to the GoodVibes daemon.
136
173
  settings unreadable; wake transcription is daemon-first with failure
137
174
  evidence in diagnostics; the managed voice installer supersedes stale
138
175
  manual paths by name and proves itself with a spoken round trip.
139
- - Changed: setup flows complete the inferred intent propose extensions,
176
+ - Changed: setup flows complete the inferred intent: propose extensions,
140
177
  ask at genuine forks, never hand the user a command; the Google
141
178
  walkthrough accepts pasted values in-conversation and answers with the
142
179
  consent link.
@@ -158,7 +195,7 @@ All notable changes to the GoodVibes daemon.
158
195
  with receipts, and the legacy directory empties and is removed.
159
196
  - **Changed: the daemon hosts conversation turns for the agent.** Hosted
160
197
  session event streams are render-grade and session-scoped, so a client
161
- 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
162
199
  agent conversations become genuinely cross-visible with their messages.
163
200
  - Changed: connecting Google is one action, with every needed scope in a
164
201
  single consent and a live mail-and-calendar proof at the end; the settings
@@ -170,7 +207,7 @@ All notable changes to the GoodVibes daemon.
170
207
  ### Changes
171
208
 
172
209
  - Changed: a conversational turn is told to understand what it captures, not
173
- 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
174
211
  words, travelers who are people in the owner's life, and durable facts
175
212
  about the destination, and the turn uses what it stored: it names
176
213
  collisions with existing plans and offers the obviously useful next steps
@@ -184,14 +221,14 @@ All notable changes to the GoodVibes daemon.
184
221
 
185
222
  - **Fixed: a chat message gets an answer, never workflow paperwork.** The
186
223
  conversation gate's decision that a channel message is conversation is now
187
- authoritative the review-wording heuristic that turned "I'll review the
224
+ authoritative. The review-wording heuristic that turned "I'll review the
188
225
  route" in a transcript into a full write-review-fix-confirm chain can no
189
226
  longer override it. When a chain does legitimately run, the reply sent back
190
227
  over the channel carries what the agent actually found or did; chain status
191
228
  lines stay in the operator progress feed. Duplicate replies from the two
192
229
  completion reporters are gone (platform runtime 2.0.6).
193
230
  - **Added: personal information shared in conversation gets captured.** A
194
- conversational channel turn which previously ran with no tools at all
231
+ conversational channel turn, which previously ran with no tools at all,
195
232
  now carries the profile capture tool with per-run owner authority: a trip
196
233
  itinerary pasted into Telegram lands in the owner profile's Plans section
197
234
  with its dates, flights, travelers and confirmation number, and the reply
@@ -216,15 +253,15 @@ All notable changes to the GoodVibes daemon.
216
253
  ### Changes
217
254
 
218
255
  - **Changed: payment limits hold the amount you would say out loud.** The
219
- budget settings drop their unit suffix — `payments.budget.perPurchaseCeiling`,
220
- `dailyItem`, `dailyOverage`, `overageToleranceDailyAllowance` and hold
256
+ budget settings, `payments.budget.perPurchaseCeiling`,
257
+ `dailyItem`, `dailyOverage`, `overageToleranceDailyAllowance`, drop their unit suffix and hold
221
258
  plain amounts in the configured currency, written exactly as you give them:
222
259
  `100` is a hundred dollars, `19.99` is nineteen ninety-nine, and `$100`,
223
260
  `100.00` and `100` all mean the same hundred. This daemon migrates its
224
261
  settings file on load with a receipt; your limits are unchanged, only how
225
262
  they are written (platform runtime 2.0.5).
226
263
  - Fixed: every platform state store this daemon keeps writes atomically and
227
- 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
228
265
  watcher-snapshot fix from 1.28.6 is now the platform-wide rule. The daemon
229
266
  settings file itself deliberately keeps its stricter contract: an
230
267
  unparseable settings file still refuses the boot loudly, because defaults
@@ -236,7 +273,7 @@ All notable changes to the GoodVibes daemon.
236
273
 
237
274
  - **Fixed: a corrupt watcher snapshot no longer crash-loops this daemon.**
238
275
  A host freeze left the snapshot file as valid JSON followed by NUL bytes,
239
- 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
240
277
  47 seconds after every restart, so the service never stayed up. The
241
278
  platform runtime (2.0.4) now writes that file atomically and quarantines a
242
279
  corrupt one with a receipt beside it, rebuilding watcher state from live
@@ -251,7 +288,7 @@ All notable changes to the GoodVibes daemon.
251
288
  - Fixed: a client that inherited this daemon's bind host is no longer refused
252
289
  as "insecure PUBLIC transport" when that host is a wildcard. The platform
253
290
  runtime (2.0.3) classifies `0.0.0.0` and `::` with loopback and the other
254
- 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
255
292
  reaches the local machine. Until this fix, a daemon deliberately bound to
256
293
  `0.0.0.0` for LAN access left local clients unable to call it over plain
257
294
  http, profile reads included.
@@ -273,8 +310,8 @@ All notable changes to the GoodVibes daemon.
273
310
 
274
311
  - Fixed: importing settings that include `display.themeMode` no longer prints
275
312
  an "unknown key" warning. The SDK's configuration schema (2.0.1) now declares
276
- the key `auto` probes the terminal background once at startup, `dark` and
277
- `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,
278
315
  documented setting.
279
316
 
280
317
  ## [1.28.2] - 2026-08-01
@@ -297,7 +334,7 @@ All notable changes to the GoodVibes daemon.
297
334
 
298
335
  - `pair --host <name>` now reaches a DIFFERENT daemon instead of being refused.
299
336
  It asks that daemon to mint a brand-new per-device pairing token over
300
- `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
301
338
  act than the plain `pair` reprint, which still just reprints this machine's
302
339
  existing shared token and never mints. Because it changes state on a daemon
303
340
  that may not be this process's own, it states the plan and asks for
@@ -315,7 +352,7 @@ All notable changes to the GoodVibes daemon.
315
352
  and the advertised REST path.
316
353
 
317
354
  What a client gets is one merged timeline, newest first, across every
318
- provider items interleave by arrival rather than being grouped, and each
355
+ provider, items interleave by arrival rather than being grouped, and each
319
356
  carries its own `provider`, so an inbox reads like an inbox. Pages are bounded
320
357
  and walked with an opaque `nextCursor`; `cursor` stays what it was, the
321
358
  freshness watermark you hand back as `since`. That is a keyset, not an offset:
@@ -323,8 +360,8 @@ All notable changes to the GoodVibes daemon.
323
360
  every insert, so a caller walking pages during a poll would see items twice
324
361
  and miss others.
325
362
 
326
- The answer is served from this daemon's SYNCED MIRROR the sqlite store the
327
- 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,
328
365
  and not from a fresh remote fetch per call. Four reasons, all of them about
329
366
  what a fetch-per-call would cost: a third-party rate limit would sit behind a
330
367
  read verb any client may call at any rate; the cluster hands FETCHING for each
@@ -341,7 +378,7 @@ All notable changes to the GoodVibes daemon.
341
378
  state, when it last synced, how much of the mirror is its, and whether this
342
379
  node is the one fetching it. `ready`, `empty`, `unconfigured`, `error` and
343
380
  `pending` are five different things, and a caller does something different
344
- 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
345
382
  has not looked yet is not a node reporting an empty inbox. A provider whose
346
383
  sync failed contributes no items, says why, and sets `partial`, so a short
347
384
  list is never mistaken for a quiet week. Nothing configured is an empty list
@@ -353,7 +390,7 @@ All notable changes to the GoodVibes daemon.
353
390
  path, the first rename moved it away, and the second failed with ENOENT on a
354
391
  file it had just written. Not hypothetical: the inbox poller flushes once per
355
392
  provider and polls every provider concurrently, so an ordinary two-provider
356
- 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
357
394
  health, the failure showed up as a provider reporting a filesystem error for
358
395
  its feed. The temp name now carries a per-process counter. Every store on
359
396
  `HandlerSqliteStore` shared the hazard, so the fix is there.
@@ -361,8 +398,8 @@ All notable changes to the GoodVibes daemon.
361
398
  - A gateway invocation that carries no context no longer throws a TypeError out
362
399
  of the handler wrapper. `normalizeContext` read `.metadata` off the context
363
400
  unconditionally, and an in-process invoke that builds the invocation by hand
364
- can omit it; an absent context now reads as the empty one no principal, no
365
- 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
366
403
  caller an authorization it never proved, never grant one.
367
404
 
368
405
  - Three config modules the terminal app carried a byte-identical copy of are the
@@ -376,7 +413,7 @@ All notable changes to the GoodVibes daemon.
376
413
  `parseConfigValueText`. `src/cli/config-value.ts` held a byte-identical copy
377
414
  of that function and its `cli/index.ts` re-export is gone with it. The copy
378
415
  existed because the shared one was private; it is exported now, and one
379
- 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
380
417
  false` must write the same thing.
381
418
 
382
419
  - The local `sql.js` ambient declaration is gone. The SDK ships the declaration
@@ -399,17 +436,17 @@ All notable changes to the GoodVibes daemon.
399
436
  installed agents) and `acp.sessions.create` (spawn one as a long-lived
400
437
  session) were cataloged and advertised as callable on every build, and
401
438
  answered nothing: the composition never constructed the ACP host they are
402
- handlers for. `runtime/services.ts` now builds that host permission asks
439
+ handlers for. `runtime/services.ts` now builds that host: permission asks
403
440
  from a hosted agent route through the same shared approval broker every
404
441
  other confirmation rides, and each hosted agent registers onto a shared
405
- 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
406
443
  it into the gateway registration and the fleet registry, so a hosted agent
407
444
  also shows up as a fleet row.
408
445
 
409
446
  - The command line is the daemon's, and it is an operator surface rather than a
410
447
  way to start a process. It shipped carrying the terminal app's parser: a table
411
- of two dozen command words `tui`, `run`, `doctor`, `models`, `providers`,
412
- `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,
413
450
  version and four service verbs. Everything else fell through to "start a
414
451
  daemon in the foreground", so `goodvibes-daemon status` served, and so did
415
452
  `goodvibes-daemon install-servce`. The parser's own unknown-command error was
@@ -419,9 +456,9 @@ All notable changes to the GoodVibes daemon.
419
456
  (`src/cli/command-catalog.ts`) that the parser, the help text and the shell
420
457
  completions all read. Serving happens on a bare invocation or on `serve`, and
421
458
  on nothing else; any other unrecognized word exits 2 with `Unknown command: X`
422
- and the help. The terminal app's conversation flags `--resume`, `--continue`,
459
+ and the help. The terminal app's conversation flags (`--resume`, `--continue`,
423
460
  `--fork`, `--print`, `--prompt`, `-o/--output`, `--open`, `--no-alt-screen`,
424
- `--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
425
462
  now refused by name and says which surface owns it.
426
463
 
427
464
  - New commands, all of them things a headless box's operator previously had no
@@ -437,8 +474,8 @@ All notable changes to the GoodVibes daemon.
437
474
  with the same operator token.
438
475
  - `config list|get|set|unset` reads and writes this machine's settings
439
476
  directly, so it works whether or not a daemon is running. Every value it
440
- PRINTS goes through the redaction rules first a token, a password or an API
441
- 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.
442
479
  - `pair` prints the pairing link and QR again, from the same renderer the
443
480
  daemon uses at startup and carrying the same existing token, so the block is
444
481
  no longer lost when the boot banner scrolls away.
@@ -453,14 +490,14 @@ All notable changes to the GoodVibes daemon.
453
490
  - `completion bash|zsh|fish`, generated from the catalog, and `help <command>`
454
491
  for any command's own arguments and flags.
455
492
 
456
- - `service-status` answers with an exit code 0 installed and running, 3
457
- 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
458
495
  longer has to read the prose to find out.
459
496
 
460
497
  - The help text describes the binary that exists: every command, the flags that
461
498
  work, `-y/--yes`, `--config`, `--enable`/`--disable`, `--json`, the exit codes,
462
499
  and a systemd user service, a launchd agent or a Scheduled Task depending on
463
- 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,
464
501
  including macOS, where `install-service` writes a launchd agent.
465
502
 
466
503
  - `status` reads the daemon's identity, health and channel routes with the
@@ -477,7 +514,7 @@ All notable changes to the GoodVibes daemon.
477
514
  floor is built (`DaemonConfig.hostedSessions`, wired in
478
515
  `runtime/hosted-session-composition.ts`) turns on the SDK's hosted-session
479
516
  engine and its `sessions.hosted.create/attach/detach/kill/list` verbs: a full
480
- loop composed inside this process the same orchestrator, the same tool
517
+ loop composed inside this process: the same orchestrator, the same tool
481
518
  registry rooted at the named workspace, the same permission machinery a
482
519
  terminal runs. Driving one uses the verbs that already existed
483
520
  (`sessions.steer`, `sessions.followUp`, `sessions.toolCalls.cancel`,
@@ -493,7 +530,7 @@ All notable changes to the GoodVibes daemon.
493
530
  directories asks three separate questions.
494
531
 
495
532
  Detaching is governed by `hostedSessions.detachPolicy`, which defaults to
496
- `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
497
534
  sessions that outlive both the client and a restart of this daemon; a single
498
535
  session may override the setting when it is created. `hostedSessions.maxSessions`
499
536
  caps how many loops this machine holds at once, and the transcript bound and
@@ -517,13 +554,13 @@ All notable changes to the GoodVibes daemon.
517
554
  by id. The runtime is handed over whole, deliberately: the verbs and the `phone`
518
555
  tool must reach the same service, because a second path to a phone would be a second
519
556
  place the confirmation prompt and the durable grants could be decided differently.
520
- 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
521
558
  seam and appears wherever the person is looking.
522
559
 
523
560
  - Conversation-scope rewind stopped answering for sessions it holds nothing for.
524
561
  `conversation-rewind-port.ts` resolves a session's conversation from an in-process
525
562
  registry, and while the conversation loops run in the surfaces that registry is
526
- 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
527
564
  already at the anchor gives, so a caller could not tell a rewind that found nothing
528
565
  from one that reached nobody. It now reports the anchor as unavailable with the
529
566
  reason, which `rewind.plan` surfaces as a warning and `rewind.apply` records instead
@@ -540,8 +577,8 @@ All notable changes to the GoodVibes daemon.
540
577
  rode a terminal-app release. It now has its own repository, its own release line and its own
541
578
  binary, and the terminal app and the agent become clients of it.
542
579
 
543
- - The suite installer lives here now. `scripts/install.sh` the script behind
544
- `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
545
582
  repository into this one, because the daemon is the product everything else is
546
583
  installed alongside and this repository's release lane is the one that publishes it.
547
584
  There is exactly one copy: two installers in two repositories is how two installers
@@ -570,7 +607,7 @@ All notable changes to the GoodVibes daemon.
570
607
  act, and the install receipt prints both the URL and the one command that does it.
571
608
 
572
609
  - **New: `goodvibes-daemon webui enable | disable | status`.** The command that owns
573
- 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
574
611
  answer to "what URL do I open and who can reach it". `enable --bundle-dir <dir>`
575
612
  refuses a directory with no index.html rather than pointing the daemon at something it
576
613
  cannot serve; `--lan` is the one act that widens exposure and `--loopback` takes it
@@ -579,7 +616,7 @@ All notable changes to the GoodVibes daemon.
579
616
  The URL it reports is the control-plane origin, because that is the listener serving
580
617
  the bundle. `web.port` is the surface's declared endpoint and nothing binds it, so
581
618
  `enable` also replaces the shipped `web.publicBaseUrl` placeholder (`http://127.0.0.1:3423`)
582
- 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
583
620
  saying so when the two differ.
584
621
 
585
622
  - The daemon updates itself from this repository. The platform default for
@@ -595,7 +632,7 @@ All notable changes to the GoodVibes daemon.
595
632
  settings file carries it and no migration rewrites it. The terminal repository no
596
633
  longer builds daemon binaries, so those daemons resolve a release with no
597
634
  `goodvibes-daemon-<os>-<arch>` asset and fail. Pointing them at this repository
598
- 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
599
636
  beside them to the same all-or-nothing download whenever `goodvibes` sits in the
600
637
  install directory, which `scripts/install.sh` guarantees, and this repository
601
638
  deliberately publishes no terminal binary. There is also no remote write path to
@@ -614,7 +651,7 @@ All notable changes to the GoodVibes daemon.
614
651
  Named rather than implied away: the SDK's `resolveDaemonInstalledFiles` still adds
615
652
  the terminal binary to the daemon's OWN update target set when one sits beside it, so
616
653
  on a three-binary install a daemon from this repository cannot yet complete an
617
- 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
618
655
  repository does not publish and takes the 404. Making each product update strictly
619
656
  its own files is an SDK change, not one this repository can make.
620
657
 
@@ -635,7 +672,7 @@ All notable changes to the GoodVibes daemon.
635
672
 
636
673
  Nothing about where this daemon keeps its state changed. The hoisted modules
637
674
  that used to spell the storage scope now take it as a parameter, and every
638
- 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,
639
676
  the workspace trust file, the code-index database and the operator-token
640
677
  pruning candidates all resolve to exactly the paths they resolved to before.
641
678
 
@@ -648,13 +685,13 @@ All notable changes to the GoodVibes daemon.
648
685
  which the shared override path only does for whole `key=value` strings.
649
686
 
650
687
  - The command line is parsed by the shared argument engine, driven by this
651
- binary's catalog. `src/cli/parser.ts` was a full engine the command-word
652
- 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
653
690
  a switch over one product's flag field names. The engine is now
654
691
  `parseWithCatalog`, and `src/cli/command-catalog.ts` is the vocabulary it
655
692
  reads: the same commands, the same aliases, the same flags per command, the
656
693
  same refusal that an unrecognized word exits 2 rather than starting a daemon.
657
- One sentence reads differently a conversation flag this binary does not
694
+ One sentence reads differently. A conversation flag this binary does not
658
695
  have is now refused as "`--resume` is not a goodvibes-daemon flag — resuming
659
696
  a conversation, a terminal app concern that belongs to another surface."
660
697
 
@@ -666,5 +703,5 @@ All notable changes to the GoodVibes daemon.
666
703
  its tools when an optional install quietly fails. They are declared here at
667
704
  the ranges the platform states, and a dependency check makes a missing one
668
705
  fail at build time instead of at the first hosted turn. `@anthropic-ai/vertex-sdk`
669
- 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
670
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.18",
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.15",
69
- "@pellux/goodvibes-terminal-shell": "2.0.15",
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.15",
92
+ "@pellux/goodvibes-toolchain": "2.0.18",
91
93
  "@types/bun": "^1.3.10",
92
94
  "typescript": "^5.9.3"
93
95
  },