@pellux/goodvibes-daemon 1.28.19 → 1.28.21

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 (114) hide show
  1. package/CHANGELOG.md +119 -63
  2. package/README.md +24 -19
  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 +19 -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/address-store.ts +54 -0
  40. package/src/daemon/handlers/payments/budget-store.ts +356 -0
  41. package/src/daemon/handlers/payments/card-store.ts +486 -0
  42. package/src/daemon/handlers/payments/checkout-handlers.ts +526 -0
  43. package/src/daemon/handlers/payments/index.ts +38 -0
  44. package/src/daemon/handlers/payments/merchant-judge.ts +57 -0
  45. package/src/daemon/handlers/payments/notifier.ts +112 -0
  46. package/src/daemon/handlers/payments/purchase-ledger.ts +108 -0
  47. package/src/daemon/handlers/payments/register.ts +518 -0
  48. package/src/daemon/handlers/register.ts +3 -3
  49. package/src/daemon/handlers/remote/backends/cloud-terminal.ts +9 -1
  50. package/src/daemon/handlers/remote/backends/process-runner.ts +1 -1
  51. package/src/daemon/handlers/remote/backends/ssh.ts +9 -1
  52. package/src/daemon/handlers/remote/backends/types.ts +2 -2
  53. package/src/daemon/handlers/remote/dispatcher.ts +3 -3
  54. package/src/daemon/handlers/remote/index.ts +1 -1
  55. package/src/daemon/handlers/remote/peer-registry.ts +62 -13
  56. package/src/daemon/handlers/routing/inbox-bridge.ts +5 -5
  57. package/src/daemon/handlers/routing/index.ts +1 -1
  58. package/src/daemon/handlers/routing/route-store.ts +1 -1
  59. package/src/daemon/handlers/routing/routing-resolver.ts +3 -3
  60. package/src/daemon/handlers/sqlite-store.ts +9 -9
  61. package/src/daemon/handlers/triage/index.ts +1 -1
  62. package/src/daemon/handlers/triage/integration.ts +3 -3
  63. package/src/daemon/handlers/triage/pipeline.ts +2 -2
  64. package/src/daemon/handlers/triage/scorer.ts +2 -2
  65. package/src/daemon/handlers/triage/tagger/discord.ts +3 -3
  66. package/src/daemon/handlers/triage/tagger/imap.ts +7 -7
  67. package/src/daemon/handlers/triage/tagger/index.ts +1 -1
  68. package/src/daemon/handlers/triage/tagger/shared.ts +3 -3
  69. package/src/daemon/handlers/triage/tagger/slack.ts +1 -1
  70. package/src/daemon/handlers/triage/types.ts +2 -2
  71. package/src/daemon/lifecycle.ts +5 -5
  72. package/src/daemon/local-daemon-state.ts +7 -7
  73. package/src/daemon/pair-command.ts +14 -14
  74. package/src/daemon/provision-wake-model.ts +5 -5
  75. package/src/daemon/send/channels.ts +7 -7
  76. package/src/daemon/send/command.ts +11 -11
  77. package/src/daemon/send/composition.ts +5 -5
  78. package/src/daemon/send/failure-text.ts +6 -6
  79. package/src/daemon/send/inert-text.ts +18 -18
  80. package/src/daemon/send/stdin.ts +3 -3
  81. package/src/daemon/service-commands.ts +32 -32
  82. package/src/daemon/sessions-command.ts +7 -7
  83. package/src/daemon/status-command.ts +22 -22
  84. package/src/daemon/webui-command.ts +14 -14
  85. package/src/runtime/boot-tasks.ts +1 -1
  86. package/src/runtime/browser-checkout-seam-holder.ts +55 -0
  87. package/src/runtime/cluster-composition.ts +9 -9
  88. package/src/runtime/cluster-group-composition.ts +7 -7
  89. package/src/runtime/conversation-rewind-port.ts +8 -8
  90. package/src/runtime/credential-composition.ts +2 -2
  91. package/src/runtime/daemon-handler-composition.ts +61 -4
  92. package/src/runtime/device-posture-composition.ts +10 -10
  93. package/src/runtime/disposal-wiring.ts +8 -8
  94. package/src/runtime/fleet-needs-input-push.ts +4 -4
  95. package/src/runtime/fleet-services.ts +1 -1
  96. package/src/runtime/hosted-session-composition.ts +13 -13
  97. package/src/runtime/index.ts +1 -1
  98. package/src/runtime/knowledge-services.ts +2 -2
  99. package/src/runtime/legacy-daemon-migration.ts +43 -43
  100. package/src/runtime/legacy-daemon-reconcile.ts +30 -30
  101. package/src/runtime/mail-composition.ts +6 -6
  102. package/src/runtime/notification-dispatch.ts +7 -7
  103. package/src/runtime/payments-composition.ts +187 -0
  104. package/src/runtime/plugin-composition.ts +7 -7
  105. package/src/runtime/runtime-services-types.ts +9 -9
  106. package/src/runtime/services.ts +41 -32
  107. package/src/runtime/trigger-services.ts +1 -1
  108. package/src/runtime/trust/checkpoint-eligibility.ts +5 -5
  109. package/src/runtime/trust/trust-gated-approvals.ts +9 -9
  110. package/src/runtime/update-check.ts +4 -4
  111. package/src/runtime/workspace-checkpointing.ts +6 -6
  112. package/src/testing/daemon-fixture.ts +11 -11
  113. package/src/testing/hosted-session-failures.ts +4 -4
  114. package/src/version.ts +2 -2
package/CHANGELOG.md CHANGED
@@ -2,8 +2,63 @@
2
2
 
3
3
  All notable changes to the GoodVibes daemon.
4
4
 
5
+ ## [1.28.21] - 2026-08-21
6
+
7
+ ### Changes
8
+
9
+ - **The checkout verbs answer.** `payments.checkout.begin` and
10
+ `payments.checkout.fillCard` attach through sdk 2.0.19's browser-checkout
11
+ seam: one payments service per registration holds the in-flight checkout
12
+ registry across both verbs, a second begin on a busy page refuses, and the
13
+ card-material guard is the engine's own instance end to end. Without a
14
+ merchant-response reader wired, purchase outcomes record honestly as
15
+ `submitted-unverified` and the owner report says so. `explicitUserRequest`
16
+ gates entry to `begin` and is documented as what it is: a caller assertion,
17
+ no stronger than a confirm flag; the money controls are the budget ledger,
18
+ the purchase notices and decision windows, and the card-material guard.
19
+ - **The daily budget survives restarts.** Reservations and commits persist
20
+ atomically to `payments-budget.json` and reload at boot; corrupt or
21
+ malformed entries (including non-finite or out-of-range amounts and
22
+ timestamps) are dropped with a warning instead of poisoning the pools; a
23
+ commit whose disk write fails keeps the in-memory spend, logs the exposure
24
+ plainly, and retries on the next mutation, so a full disk cannot erase a
25
+ spend that already happened at the merchant; a failed reserve rolls back
26
+ rather than holding budget; old spend records prune at boot; loaded
27
+ reservations are disclosed at startup for reconciliation.
28
+ - **Five payments verbs ride the sdk registrar.** `budget.status`,
29
+ `cards.list` and `cards.delete` attach through sdk 2.0.19's
30
+ `registerPaymentsGatewayMethods`; `cards.create` and `purchases.list` keep
31
+ thin local wrappers over the same service for stricter field-named
32
+ validation and query-string limits. Wire behavior for all five is pinned
33
+ byte-identical by the contract suites. Teardown restores all seven
34
+ descriptors to their pristine builtin state.
35
+ - Platform runtime 2.0.19: the pin rides the checkout-seam and wake-fix
36
+ cycle; this daemon serves the repaired `voice.wake.model.get`, which fixes
37
+ wake-word model delivery for every surface that fetches models from it.
38
+
5
39
  ---
6
40
 
41
+ ## [1.28.20] - 2026-08-21
42
+
43
+ ### Changes
44
+
45
+ - **The payments surface answers on the standalone daemon** (platform runtime
46
+ 2.0.18): cards list/create/delete, budget status, and the purchase ledger
47
+ serve external clients (webui, the desktop app) instead of answering 501.
48
+ Card metadata lives beside the daemon's other control-plane stores and card
49
+ material sits per field in the daemon secure secret tier; a corrupt card
50
+ file refuses writes rather than orphaning stored material, a CVV is never
51
+ supplied while cvvHandling is prompt, and the two checkout verbs keep an
52
+ honest 501 until the browser-driver seam exists.
53
+ - **Stored remote-peer rows are validated when read**: a corrupt or
54
+ hand-edited row raises a typed error instead of flowing downstream
55
+ malformed, and removing such a row still works.
56
+ - **Remote backends sweep their credential directories at construction**, so
57
+ a crashed daemon no longer leaves plaintext keys on disk across restarts.
58
+ - Config keys are narrowed by a shared type guard instead of casts, and the
59
+ daemon's CI now gates coverage, architecture, and the sdk pin before any
60
+ tag exists.
61
+
7
62
  ## [1.28.19] - 2026-08-15
8
63
 
9
64
  ### Changes
@@ -15,7 +70,7 @@ All notable changes to the GoodVibes daemon.
15
70
  records fold in automatically, newest first, legacy files untouched.
16
71
  - **A turn through a strict OpenAI-compatible gateway works again** (platform
17
72
  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
73
+ which validators like abacus RouteLLM now reject wholesale, and every hosted
19
74
  turn through such a provider failed with "Extra inputs are not permitted".
20
75
  The union is `anyOf` now, pinned by a wire-compatibility test.
21
76
 
@@ -25,8 +80,8 @@ All notable changes to the GoodVibes daemon.
25
80
 
26
81
  - **The keep-awake inhibitor can no longer paint an authentication prompt on
27
82
  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
83
+ inhibitor, typical for tmux/SSH sessions logind does not count as an
84
+ active seat, used to register systemd's interactive auth agent on the
30
85
  controlling terminal. The inhibitor is now requested with
31
86
  `--no-ask-password`; refusal is silent and the platform runs without it.
32
87
 
@@ -36,8 +91,8 @@ All notable changes to the GoodVibes daemon.
36
91
 
37
92
  - **Post-wake capture ends when the speaker stops, on real microphones**
38
93
  (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
94
+ capture, so a headset's automatic gain control ramping up after speech no
95
+ longer holds the microphone open, and breath ticks shorter than
41
96
  `voice.wake.speechRetriggerMs` (new setting, default 150 ms) no longer
42
97
  reset the silence clock. A pinned `voice.wake.silenceFloorRms` still
43
98
  freezes the floor completely.
@@ -54,7 +109,7 @@ All notable changes to the GoodVibes daemon.
54
109
  module scope; under the single-file compiler's nondeterministic module
55
110
  order that call could run before the helper exists, killing the binary at
56
111
  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
112
+ build-order lottery class 1.28.15 fixed. This was the last direct
58
113
  module-scope call into the platform runtime in this product.
59
114
 
60
115
  ## [1.28.15] - 2026-08-07
@@ -66,7 +121,7 @@ All notable changes to the GoodVibes daemon.
66
121
  off the SDK's runtime namespace objects at module scope (`export const X =
67
122
  ns.X`). Bun's single-file compiler emits module bodies in an order that
68
123
  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
124
+ defines the binding, and the binary then dies at load with a ReferenceError on
70
125
  some builds of identical source. Every one of those reads is now a grouped
71
126
  live re-export from the SDK's registered runtime subpaths, resolved by the
72
127
  module system instead of read at module scope. The shared post-build smoke
@@ -83,7 +138,7 @@ All notable changes to the GoodVibes daemon.
83
138
  fan or steady background noise no longer holds the microphone open to the
84
139
  ceiling on every capture. The floor is a real setting
85
140
  (`voice.wake.silenceFloorRms`, 0 = adaptive), and
86
- `voice.wake.captureMaxSeconds: 0` now genuinely means no hard maximum
141
+ `voice.wake.captureMaxSeconds: 0` now genuinely means no hard maximum:
87
142
  whisper has no input limit, and capture closes on silence.
88
143
  - The exec sandbox's self-description now names the built-in tools a turn
89
144
  should use for daemon status and settings, so an assistant inside the
@@ -97,13 +152,13 @@ All notable changes to the GoodVibes daemon.
97
152
  - **Fixed: an unnamed transcription or synthesis request goes to the voice
98
153
  provider the user actually configured** (platform runtime 2.0.11). The
99
154
  daemon's provider picker answered "use whatever this host has configured"
100
- with the first name on its internal registration list a cloud provider
155
+ with the first name on its internal registration list, a cloud provider,
101
156
  even when that provider had no key and the host carried fully provisioned
102
157
  local engines. On a machine set up for local voice, every wake-word
103
158
  transcription failed "OpenAI API key missing" while the user's working
104
159
  whisper was never asked, and no settings key could override the pick.
105
160
  Unnamed requests now prefer providers that report themselves configured,
106
- with configured local engines first free, offline, no key. A named
161
+ with configured local engines first, free, offline, no key. A named
107
162
  provider keeps exactly its previous behavior.
108
163
 
109
164
  ## [1.28.12] - 2026-08-05
@@ -113,7 +168,7 @@ All notable changes to the GoodVibes daemon.
113
168
  - **Fixed: a hosted conversational turn no longer gets the host.** The daemon
114
169
  composes hosted sessions through the same `createClientRuntimeServices` a
115
170
  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
171
+ boundary: network, PID, UTS and IPC namespaced, system read-only, /tmp and
117
172
  $HOME masked, `sandbox.egressAllowlist` the one way network comes back. What
118
173
  differed was the fallback: with no boundary available the command ran
119
174
  directly on the host and only said so afterwards, and a conversational turn
@@ -122,17 +177,18 @@ All notable changes to the GoodVibes daemon.
122
177
  `conversational`, which makes the boundary REQUIRED: a command that cannot be
123
178
  contained is refused, naming why, and `background: true` is not a spelling
124
179
  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
180
+ a per-spawn grant written into this daemon's own composition; nothing on the
126
181
  wire and nothing in a tool argument can reach it.
127
182
  - **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
183
+ existing tmux session, window or pane this platform did not create is
184
+ refused by the exec guard for a hosted turn, with a refusal that names the
185
+ rule. The blocked verbs are send-keys, kill, resize, attach, respawn,
186
+ rename. Creating and
131
187
  driving the platform's own sessions is unchanged, and so is reading tmux
132
188
  state (`list-sessions`, `list-panes`, `capture-pane`), which the fleet view
133
189
  already does. The frozen catastrophic block is untouched.
134
190
  - Changed: this daemon's hosted operator prompt carries the platform's
135
- conversational diagnosis contract report the state and propose, never
191
+ conversational diagnosis contract: report the state and propose, never
136
192
  restart the owner's applications or type into his terminal to "fix" things,
137
193
  and a "fixed" claim needs the live evidence it rests on.
138
194
 
@@ -151,7 +207,7 @@ All notable changes to the GoodVibes daemon.
151
207
  settings unreadable; wake transcription is daemon-first with failure
152
208
  evidence in diagnostics; the managed voice installer supersedes stale
153
209
  manual paths by name and proves itself with a spoken round trip.
154
- - Changed: setup flows complete the inferred intent propose extensions,
210
+ - Changed: setup flows complete the inferred intent: propose extensions,
155
211
  ask at genuine forks, never hand the user a command; the Google
156
212
  walkthrough accepts pasted values in-conversation and answers with the
157
213
  consent link.
@@ -173,7 +229,7 @@ All notable changes to the GoodVibes daemon.
173
229
  with receipts, and the legacy directory empties and is removed.
174
230
  - **Changed: the daemon hosts conversation turns for the agent.** Hosted
175
231
  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
232
+ rendering from the stream sees what the model said and what it did, and
177
233
  agent conversations become genuinely cross-visible with their messages.
178
234
  - Changed: connecting Google is one action, with every needed scope in a
179
235
  single consent and a live mail-and-calendar proof at the end; the settings
@@ -185,7 +241,7 @@ All notable changes to the GoodVibes daemon.
185
241
  ### Changes
186
242
 
187
243
  - 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
244
+ just file it. An itinerary also means an away-span said back in plain
189
245
  words, travelers who are people in the owner's life, and durable facts
190
246
  about the destination, and the turn uses what it stored: it names
191
247
  collisions with existing plans and offers the obviously useful next steps
@@ -199,14 +255,14 @@ All notable changes to the GoodVibes daemon.
199
255
 
200
256
  - **Fixed: a chat message gets an answer, never workflow paperwork.** The
201
257
  conversation gate's decision that a channel message is conversation is now
202
- authoritative the review-wording heuristic that turned "I'll review the
258
+ authoritative. The review-wording heuristic that turned "I'll review the
203
259
  route" in a transcript into a full write-review-fix-confirm chain can no
204
260
  longer override it. When a chain does legitimately run, the reply sent back
205
261
  over the channel carries what the agent actually found or did; chain status
206
262
  lines stay in the operator progress feed. Duplicate replies from the two
207
263
  completion reporters are gone (platform runtime 2.0.6).
208
264
  - **Added: personal information shared in conversation gets captured.** A
209
- conversational channel turn which previously ran with no tools at all
265
+ conversational channel turn, which previously ran with no tools at all,
210
266
  now carries the profile capture tool with per-run owner authority: a trip
211
267
  itinerary pasted into Telegram lands in the owner profile's Plans section
212
268
  with its dates, flights, travelers and confirmation number, and the reply
@@ -231,15 +287,15 @@ All notable changes to the GoodVibes daemon.
231
287
  ### Changes
232
288
 
233
289
  - **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
290
+ budget settings, `payments.budget.perPurchaseCeiling`,
291
+ `dailyItem`, `dailyOverage`, `overageToleranceDailyAllowance`, drop their unit suffix and hold
236
292
  plain amounts in the configured currency, written exactly as you give them:
237
293
  `100` is a hundred dollars, `19.99` is nineteen ninety-nine, and `$100`,
238
294
  `100.00` and `100` all mean the same hundred. This daemon migrates its
239
295
  settings file on load with a receipt; your limits are unchanged, only how
240
296
  they are written (platform runtime 2.0.5).
241
297
  - 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
298
+ quarantines a corrupt file with a receipt instead of failing on it. The
243
299
  watcher-snapshot fix from 1.28.6 is now the platform-wide rule. The daemon
244
300
  settings file itself deliberately keeps its stricter contract: an
245
301
  unparseable settings file still refuses the boot loudly, because defaults
@@ -251,7 +307,7 @@ All notable changes to the GoodVibes daemon.
251
307
 
252
308
  - **Fixed: a corrupt watcher snapshot no longer crash-loops this daemon.**
253
309
  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
310
+ and the daemon died parsing it, once at boot, and once on a periodic tick
255
311
  47 seconds after every restart, so the service never stayed up. The
256
312
  platform runtime (2.0.4) now writes that file atomically and quarantines a
257
313
  corrupt one with a receipt beside it, rebuilding watcher state from live
@@ -266,7 +322,7 @@ All notable changes to the GoodVibes daemon.
266
322
  - Fixed: a client that inherited this daemon's bind host is no longer refused
267
323
  as "insecure PUBLIC transport" when that host is a wildcard. The platform
268
324
  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
325
+ private-network origins: a wildcard is a listen address, and dialing it
270
326
  reaches the local machine. Until this fix, a daemon deliberately bound to
271
327
  `0.0.0.0` for LAN access left local clients unable to call it over plain
272
328
  http, profile reads included.
@@ -288,8 +344,8 @@ All notable changes to the GoodVibes daemon.
288
344
 
289
345
  - Fixed: importing settings that include `display.themeMode` no longer prints
290
346
  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,
347
+ the key: `auto` probes the terminal background once at startup, `dark` and
348
+ `light` force a fixed appearance, so every component ingests it as a real,
293
349
  documented setting.
294
350
 
295
351
  ## [1.28.2] - 2026-08-01
@@ -312,7 +368,7 @@ All notable changes to the GoodVibes daemon.
312
368
 
313
369
  - `pair --host <name>` now reaches a DIFFERENT daemon instead of being refused.
314
370
  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
371
+ `pairing.handoff.create` and prints the pairing block for it, a different
316
372
  act than the plain `pair` reprint, which still just reprints this machine's
317
373
  existing shared token and never mints. Because it changes state on a daemon
318
374
  that may not be this process's own, it states the plan and asks for
@@ -330,7 +386,7 @@ All notable changes to the GoodVibes daemon.
330
386
  and the advertised REST path.
331
387
 
332
388
  What a client gets is one merged timeline, newest first, across every
333
- provider items interleave by arrival rather than being grouped, and each
389
+ provider, items interleave by arrival rather than being grouped, and each
334
390
  carries its own `provider`, so an inbox reads like an inbox. Pages are bounded
335
391
  and walked with an opaque `nextCursor`; `cursor` stays what it was, the
336
392
  freshness watermark you hand back as `since`. That is a keyset, not an offset:
@@ -338,8 +394,8 @@ All notable changes to the GoodVibes daemon.
338
394
  every insert, so a caller walking pages during a poll would see items twice
339
395
  and miss others.
340
396
 
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
397
+ The answer is served from this daemon's SYNCED MIRROR, the sqlite store the
398
+ Slack, Discord and IMAP adapters already write into on their own cadences,
343
399
  and not from a fresh remote fetch per call. Four reasons, all of them about
344
400
  what a fetch-per-call would cost: a third-party rate limit would sit behind a
345
401
  read verb any client may call at any rate; the cluster hands FETCHING for each
@@ -356,7 +412,7 @@ All notable changes to the GoodVibes daemon.
356
412
  state, when it last synced, how much of the mirror is its, and whether this
357
413
  node is the one fetching it. `ready`, `empty`, `unconfigured`, `error` and
358
414
  `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
415
+ about each: a fresh install with no tokens is not an outage, and a node that
360
416
  has not looked yet is not a node reporting an empty inbox. A provider whose
361
417
  sync failed contributes no items, says why, and sets `partial`, so a short
362
418
  list is never mistaken for a quiet week. Nothing configured is an empty list
@@ -368,7 +424,7 @@ All notable changes to the GoodVibes daemon.
368
424
  path, the first rename moved it away, and the second failed with ENOENT on a
369
425
  file it had just written. Not hypothetical: the inbox poller flushes once per
370
426
  provider and polls every provider concurrently, so an ordinary two-provider
371
- startup hit it and once `channels.inbox.list` began reporting per-provider
427
+ startup hit it, and once `channels.inbox.list` began reporting per-provider
372
428
  health, the failure showed up as a provider reporting a filesystem error for
373
429
  its feed. The temp name now carries a per-process counter. Every store on
374
430
  `HandlerSqliteStore` shared the hazard, so the fix is there.
@@ -376,8 +432,8 @@ All notable changes to the GoodVibes daemon.
376
432
  - A gateway invocation that carries no context no longer throws a TypeError out
377
433
  of the handler wrapper. `normalizeContext` read `.metadata` off the context
378
434
  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
435
+ can omit it; an absent context now reads as the empty one: no principal, no
436
+ scopes, not admin, nobody claiming a person asked, which can only cost a
381
437
  caller an authorization it never proved, never grant one.
382
438
 
383
439
  - Three config modules the terminal app carried a byte-identical copy of are the
@@ -391,7 +447,7 @@ All notable changes to the GoodVibes daemon.
391
447
  `parseConfigValueText`. `src/cli/config-value.ts` held a byte-identical copy
392
448
  of that function and its `cli/index.ts` re-export is gone with it. The copy
393
449
  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
450
+ implementation is the whole point: `--config x=false` and `config set x
395
451
  false` must write the same thing.
396
452
 
397
453
  - The local `sql.js` ambient declaration is gone. The SDK ships the declaration
@@ -414,17 +470,17 @@ All notable changes to the GoodVibes daemon.
414
470
  installed agents) and `acp.sessions.create` (spawn one as a long-lived
415
471
  session) were cataloged and advertised as callable on every build, and
416
472
  answered nothing: the composition never constructed the ACP host they are
417
- handlers for. `runtime/services.ts` now builds that host permission asks
473
+ handlers for. `runtime/services.ts` now builds that host: permission asks
418
474
  from a hosted agent route through the same shared approval broker every
419
475
  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
476
+ session so it is attachable and steerable like any native one, and threads
421
477
  it into the gateway registration and the fleet registry, so a hosted agent
422
478
  also shows up as a fleet row.
423
479
 
424
480
  - The command line is the daemon's, and it is an operator surface rather than a
425
481
  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,
482
+ of two dozen command words (`tui`, `run`, `doctor`, `models`, `providers`,
483
+ `auth`, `secrets`, `plugin`) against an entry point that dispatched on help,
428
484
  version and four service verbs. Everything else fell through to "start a
429
485
  daemon in the foreground", so `goodvibes-daemon status` served, and so did
430
486
  `goodvibes-daemon install-servce`. The parser's own unknown-command error was
@@ -434,9 +490,9 @@ All notable changes to the GoodVibes daemon.
434
490
  (`src/cli/command-catalog.ts`) that the parser, the help text and the shell
435
491
  completions all read. Serving happens on a bare invocation or on `serve`, and
436
492
  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`,
493
+ and the help. The terminal app's conversation flags (`--resume`, `--continue`,
438
494
  `--fork`, `--print`, `--prompt`, `-o/--output`, `--open`, `--no-alt-screen`,
439
- `--session`, `--strict` were accepted in silence and read by nothing; each is
495
+ `--session`, `--strict`) were accepted in silence and read by nothing; each is
440
496
  now refused by name and says which surface owns it.
441
497
 
442
498
  - New commands, all of them things a headless box's operator previously had no
@@ -452,8 +508,8 @@ All notable changes to the GoodVibes daemon.
452
508
  with the same operator token.
453
509
  - `config list|get|set|unset` reads and writes this machine's settings
454
510
  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.
511
+ PRINTS goes through the redaction rules first, so a token, a password or an API
512
+ key reads as `<redacted>`, while `config set` still writes the real value.
457
513
  - `pair` prints the pairing link and QR again, from the same renderer the
458
514
  daemon uses at startup and carrying the same existing token, so the block is
459
515
  no longer lost when the boot banner scrolls away.
@@ -468,14 +524,14 @@ All notable changes to the GoodVibes daemon.
468
524
  - `completion bash|zsh|fish`, generated from the catalog, and `help <command>`
469
525
  for any command's own arguments and flags.
470
526
 
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
527
+ - `service-status` answers with an exit code: 0 installed and running, 3
528
+ installed but not running, 4 not installed, and takes `--json`. A script no
473
529
  longer has to read the prose to find out.
474
530
 
475
531
  - The help text describes the binary that exists: every command, the flags that
476
532
  work, `-y/--yes`, `--config`, `--enable`/`--disable`, `--json`, the exit codes,
477
533
  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,
534
+ the platform it is printed on. It used to say systemd on every platform,
479
535
  including macOS, where `install-service` writes a launchd agent.
480
536
 
481
537
  - `status` reads the daemon's identity, health and channel routes with the
@@ -492,7 +548,7 @@ All notable changes to the GoodVibes daemon.
492
548
  floor is built (`DaemonConfig.hostedSessions`, wired in
493
549
  `runtime/hosted-session-composition.ts`) turns on the SDK's hosted-session
494
550
  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
551
+ loop composed inside this process: the same orchestrator, the same tool
496
552
  registry rooted at the named workspace, the same permission machinery a
497
553
  terminal runs. Driving one uses the verbs that already existed
498
554
  (`sessions.steer`, `sessions.followUp`, `sessions.toolCalls.cancel`,
@@ -508,7 +564,7 @@ All notable changes to the GoodVibes daemon.
508
564
  directories asks three separate questions.
509
565
 
510
566
  Detaching is governed by `hostedSessions.detachPolicy`, which defaults to
511
- `kill` closing a client has always ended its work. `survive` opts into
567
+ `kill`. Closing a client has always ended its work. `survive` opts into
512
568
  sessions that outlive both the client and a restart of this daemon; a single
513
569
  session may override the setting when it is created. `hostedSessions.maxSessions`
514
570
  caps how many loops this machine holds at once, and the transcript bound and
@@ -532,13 +588,13 @@ All notable changes to the GoodVibes daemon.
532
588
  by id. The runtime is handed over whole, deliberately: the verbs and the `phone`
533
589
  tool must reach the same service, because a second path to a phone would be a second
534
590
  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
591
+ Nothing about the gates moved. The prompt still rides this daemon's shared approval
536
592
  seam and appears wherever the person is looking.
537
593
 
538
594
  - Conversation-scope rewind stopped answering for sessions it holds nothing for.
539
595
  `conversation-rewind-port.ts` resolves a session's conversation from an in-process
540
596
  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
597
+ empty here. It reported "0 messages to drop", the same answer a conversation
542
598
  already at the anchor gives, so a caller could not tell a rewind that found nothing
543
599
  from one that reached nobody. It now reports the anchor as unavailable with the
544
600
  reason, which `rewind.plan` surfaces as a warning and `rewind.apply` records instead
@@ -555,8 +611,8 @@ All notable changes to the GoodVibes daemon.
555
611
  rode a terminal-app release. It now has its own repository, its own release line and its own
556
612
  binary, and the terminal app and the agent become clients of it.
557
613
 
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
614
+ - The suite installer lives here now. `scripts/install.sh` (the script behind
615
+ `curl -fsSL https://goodvibes.sh/install.sh | sh`) moved out of the terminal app's
560
616
  repository into this one, because the daemon is the product everything else is
561
617
  installed alongside and this repository's release lane is the one that publishes it.
562
618
  There is exactly one copy: two installers in two repositories is how two installers
@@ -585,7 +641,7 @@ All notable changes to the GoodVibes daemon.
585
641
  act, and the install receipt prints both the URL and the one command that does it.
586
642
 
587
643
  - **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
644
+ serving the web UI: which directory, whether it is served at all, and the honest
589
645
  answer to "what URL do I open and who can reach it". `enable --bundle-dir <dir>`
590
646
  refuses a directory with no index.html rather than pointing the daemon at something it
591
647
  cannot serve; `--lan` is the one act that widens exposure and `--loopback` takes it
@@ -594,7 +650,7 @@ All notable changes to the GoodVibes daemon.
594
650
  The URL it reports is the control-plane origin, because that is the listener serving
595
651
  the bundle. `web.port` is the surface's declared endpoint and nothing binds it, so
596
652
  `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
653
+ with the origin that actually answers, leaving any value an operator chose alone, and
598
654
  saying so when the two differ.
599
655
 
600
656
  - The daemon updates itself from this repository. The platform default for
@@ -610,7 +666,7 @@ All notable changes to the GoodVibes daemon.
610
666
  settings file carries it and no migration rewrites it. The terminal repository no
611
667
  longer builds daemon binaries, so those daemons resolve a release with no
612
668
  `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
669
+ instead does not rescue them either. Their shipped updater adds the terminal binary
614
670
  beside them to the same all-or-nothing download whenever `goodvibes` sits in the
615
671
  install directory, which `scripts/install.sh` guarantees, and this repository
616
672
  deliberately publishes no terminal binary. There is also no remote write path to
@@ -629,7 +685,7 @@ All notable changes to the GoodVibes daemon.
629
685
  Named rather than implied away: the SDK's `resolveDaemonInstalledFiles` still adds
630
686
  the terminal binary to the daemon's OWN update target set when one sits beside it, so
631
687
  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
688
+ unattended self-update either. It asks for a `goodvibes-<os>-<arch>` asset this
633
689
  repository does not publish and takes the 404. Making each product update strictly
634
690
  its own files is an SDK change, not one this repository can make.
635
691
 
@@ -650,7 +706,7 @@ All notable changes to the GoodVibes daemon.
650
706
 
651
707
  Nothing about where this daemon keeps its state changed. The hoisted modules
652
708
  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,
709
+ call site here passes the daemon's own: the work plan, the session surface,
654
710
  the workspace trust file, the code-index database and the operator-token
655
711
  pruning candidates all resolve to exactly the paths they resolved to before.
656
712
 
@@ -663,13 +719,13 @@ All notable changes to the GoodVibes daemon.
663
719
  which the shared override path only does for whole `key=value` strings.
664
720
 
665
721
  - 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
722
+ binary's catalog. `src/cli/parser.ts` was a full engine: the command-word
723
+ pre-scan, arity skipping, `--`, inline `=value`, per-kind application, with
668
724
  a switch over one product's flag field names. The engine is now
669
725
  `parseWithCatalog`, and `src/cli/command-catalog.ts` is the vocabulary it
670
726
  reads: the same commands, the same aliases, the same flags per command, the
671
727
  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
728
+ One sentence reads differently. A conversation flag this binary does not
673
729
  have is now refused as "`--resume` is not a goodvibes-daemon flag — resuming
674
730
  a conversation, a terminal app concern that belongs to another surface."
675
731
 
@@ -681,5 +737,5 @@ All notable changes to the GoodVibes daemon.
681
737
  its tools when an optional install quietly fails. They are declared here at
682
738
  the ranges the platform states, and a dependency check makes a missing one
683
739
  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
740
+ and `@aws/bedrock-token-generator` are removed. Nothing in this repository or
685
741
  the platform imports either.
package/README.md CHANGED
@@ -2,14 +2,19 @@
2
2
 
3
3
  [![CI](https://github.com/mgd34msu/goodvibes-daemon/actions/workflows/ci.yml/badge.svg)](https://github.com/mgd34msu/goodvibes-daemon/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Version](https://img.shields.io/badge/version-1.28.14-blue.svg)](https://github.com/mgd34msu/goodvibes-daemon)
5
+ [![Version](https://img.shields.io/badge/version-1.28.20-blue.svg)](https://github.com/mgd34msu/goodvibes-daemon)
6
6
 
7
7
  The GoodVibes daemon: one long-running process per machine that holds the control plane every
8
- GoodVibes client talks to. It answers the operator verb families over HTTP, reads and replies on
9
- your channels, elects a leader among the machines you have grouped together so only one of them
10
- answers a shared inbox, runs scheduled and triggered work, keeps the session, memory, knowledge
11
- and code-index stores, provisions the local voice and wake-word models, and updates itself at an
12
- idle moment with a rollback if the new binary will not start.
8
+ GoodVibes client talks to. It:
9
+
10
+ - answers the operator verb families over HTTP
11
+ - reads and replies on your channels
12
+ - elects a leader among the machines you have grouped together, so only one of them answers a
13
+ shared inbox
14
+ - runs scheduled and triggered work
15
+ - keeps the session, memory, knowledge and code-index stores
16
+ - provisions the local voice and wake-word models
17
+ - updates itself at an idle moment, with a rollback if the new binary will not start
13
18
 
14
19
  The terminal app (`goodvibes`), the conversational agent (`goodvibes-agent`) and the web app are
15
20
  clients of this process. They render, they capture input, and they call verbs; the work happens
@@ -25,8 +30,8 @@ A **product** over `@pellux/goodvibes-sdk`, exactly like the TUI and the agent a
25
30
  - the CLI (`send`, `cluster`, `webui`, `provision-wake-model`, `install-service` and friends),
26
31
  - packaging: the compiled `goodvibes-daemon-<os>-<arch>` binaries.
27
32
 
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
33
+ Every engine (the facade, the routes, the brokers, the updater, the channel adapters, the
34
+ schedulers) lives in the SDK and is consumed from the published package. Nothing was moved out of
30
35
  the SDK to build this repository, and nothing should be: a capability that both a client and the
31
36
  daemon need belongs in the SDK, not here.
32
37
 
@@ -34,7 +39,7 @@ daemon need belongs in the SDK, not here.
34
39
 
35
40
  The daemon's version is **1.28.0**. Live installs already carry a settings reader-floor
36
41
  (`$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
42
+ rejected-version record is keyed by version. Those three mechanics all depend on the version
38
43
  line staying continuous and monotonically increasing.
39
44
 
40
45
  ## Install
@@ -43,14 +48,14 @@ line staying continuous and monotonically increasing.
43
48
  curl -fsSL https://goodvibes.sh/install.sh | sh
44
49
  ```
45
50
 
46
- This installs the whole GoodVibes suite the daemon, the terminal app, the
47
- agent, and the browser operator surface from checksum-verified binaries,
51
+ This installs the whole GoodVibes suite (the daemon, the terminal app, the
52
+ agent, and the browser operator surface) from checksum-verified binaries,
48
53
  with no package manager involved.
49
54
 
50
55
  The browser surface is not a fourth binary and not a fourth service: the bundle
51
56
  unpacks to `<install dir>/webui/<version>` and this daemon serves it on its own
52
57
  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.
58
+ network. The shipped binding is loopback and the installer does not change it.
54
59
  `goodvibes-daemon webui --lan` is the deliberate act that widens it, and
55
60
  `goodvibes-daemon webui status` says which posture is in force.
56
61
 
@@ -101,13 +106,13 @@ goodvibes-daemon provision-wake-model
101
106
 
102
107
  ## Documentation
103
108
 
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
109
+ - [Getting started](docs/getting-started.md): install, first boot, pairing, where state lives, health checks
110
+ - [Command reference](docs/commands-reference.md): every command, its flags, and its exit codes
111
+ - [Configuration](docs/configuration.md): the settings this daemon reads, by key
112
+ - [Service and deployment](docs/service-and-deployment.md): the host service, migration from an older install, `--daemon-home` vs the data home
113
+ - [Updates and rollback](docs/updates-and-rollback.md): the hourly self-update loop, automatic crash-loop rollback, `.previous`
114
+ - [Daemon-hosted sessions](docs/hosted-sessions.md): conversations that run inside the daemon and outlive any one client
115
+ - [Troubleshooting](docs/troubleshooting.md): startup failures, log locations, port conflicts, service-status oddities
111
116
 
112
117
  ## License
113
118
 
@@ -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);