@pellux/goodvibes-daemon 1.28.19 → 1.28.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +85 -63
  2. package/README.md +13 -13
  3. package/bin/launcher-support.js +5 -5
  4. package/package.json +6 -4
  5. package/scripts/postinstall.js +8 -8
  6. package/src/cli/command-catalog.ts +22 -22
  7. package/src/cli/completion.ts +4 -4
  8. package/src/cli/help.ts +5 -5
  9. package/src/cli/index.ts +3 -3
  10. package/src/cli/parser.ts +2 -2
  11. package/src/cli/surface-catalog.ts +1 -1
  12. package/src/cli/types.ts +2 -2
  13. package/src/cluster/daemon-ws-call.ts +5 -5
  14. package/src/cluster/raw-reply-route.ts +5 -5
  15. package/src/config/checkpoint-settings.ts +7 -7
  16. package/src/config/config-key-guard.ts +22 -0
  17. package/src/config/run-daemon-config-migration.ts +3 -3
  18. package/src/config/secret-config.ts +7 -7
  19. package/src/config/surface.ts +3 -3
  20. package/src/core/pairing-banner.ts +5 -5
  21. package/src/daemon/cli.ts +45 -43
  22. package/src/daemon/config-command.ts +15 -15
  23. package/src/daemon/handlers/context.ts +1 -1
  24. package/src/daemon/handlers/contracts.ts +4 -4
  25. package/src/daemon/handlers/credentials.ts +1 -1
  26. package/src/daemon/handlers/drafts/draft-store.ts +3 -3
  27. package/src/daemon/handlers/drafts/register.ts +4 -4
  28. package/src/daemon/handlers/inbox/aggregator.ts +8 -8
  29. package/src/daemon/handlers/inbox/cursor-store.ts +10 -10
  30. package/src/daemon/handlers/inbox/index.ts +7 -7
  31. package/src/daemon/handlers/inbox/mapping.ts +2 -2
  32. package/src/daemon/handlers/inbox/poller.ts +5 -5
  33. package/src/daemon/handlers/inbox/provider-adapter.ts +8 -8
  34. package/src/daemon/handlers/inbox/providers/discord.ts +6 -6
  35. package/src/daemon/handlers/inbox/providers/email.ts +3 -3
  36. package/src/daemon/handlers/inbox/providers/imap-client.ts +1 -1
  37. package/src/daemon/handlers/inbox/providers/slack.ts +4 -4
  38. package/src/daemon/handlers/index.ts +18 -8
  39. package/src/daemon/handlers/payments/card-store.ts +486 -0
  40. package/src/daemon/handlers/payments/index.ts +32 -0
  41. package/src/daemon/handlers/payments/purchase-ledger.ts +108 -0
  42. package/src/daemon/handlers/payments/register.ts +391 -0
  43. package/src/daemon/handlers/register.ts +3 -3
  44. package/src/daemon/handlers/remote/backends/cloud-terminal.ts +9 -1
  45. package/src/daemon/handlers/remote/backends/process-runner.ts +1 -1
  46. package/src/daemon/handlers/remote/backends/ssh.ts +9 -1
  47. package/src/daemon/handlers/remote/backends/types.ts +2 -2
  48. package/src/daemon/handlers/remote/dispatcher.ts +3 -3
  49. package/src/daemon/handlers/remote/index.ts +1 -1
  50. package/src/daemon/handlers/remote/peer-registry.ts +62 -13
  51. package/src/daemon/handlers/routing/inbox-bridge.ts +5 -5
  52. package/src/daemon/handlers/routing/index.ts +1 -1
  53. package/src/daemon/handlers/routing/route-store.ts +1 -1
  54. package/src/daemon/handlers/routing/routing-resolver.ts +3 -3
  55. package/src/daemon/handlers/sqlite-store.ts +9 -9
  56. package/src/daemon/handlers/triage/index.ts +1 -1
  57. package/src/daemon/handlers/triage/integration.ts +3 -3
  58. package/src/daemon/handlers/triage/pipeline.ts +2 -2
  59. package/src/daemon/handlers/triage/scorer.ts +2 -2
  60. package/src/daemon/handlers/triage/tagger/discord.ts +3 -3
  61. package/src/daemon/handlers/triage/tagger/imap.ts +7 -7
  62. package/src/daemon/handlers/triage/tagger/index.ts +1 -1
  63. package/src/daemon/handlers/triage/tagger/shared.ts +3 -3
  64. package/src/daemon/handlers/triage/tagger/slack.ts +1 -1
  65. package/src/daemon/handlers/triage/types.ts +2 -2
  66. package/src/daemon/lifecycle.ts +5 -5
  67. package/src/daemon/local-daemon-state.ts +7 -7
  68. package/src/daemon/pair-command.ts +14 -14
  69. package/src/daemon/provision-wake-model.ts +5 -5
  70. package/src/daemon/send/channels.ts +7 -7
  71. package/src/daemon/send/command.ts +11 -11
  72. package/src/daemon/send/composition.ts +5 -5
  73. package/src/daemon/send/failure-text.ts +6 -6
  74. package/src/daemon/send/inert-text.ts +18 -18
  75. package/src/daemon/send/stdin.ts +3 -3
  76. package/src/daemon/service-commands.ts +32 -32
  77. package/src/daemon/sessions-command.ts +7 -7
  78. package/src/daemon/status-command.ts +22 -22
  79. package/src/daemon/webui-command.ts +14 -14
  80. package/src/runtime/boot-tasks.ts +1 -1
  81. package/src/runtime/cluster-composition.ts +9 -9
  82. package/src/runtime/cluster-group-composition.ts +7 -7
  83. package/src/runtime/conversation-rewind-port.ts +8 -8
  84. package/src/runtime/credential-composition.ts +2 -2
  85. package/src/runtime/daemon-handler-composition.ts +44 -4
  86. package/src/runtime/device-posture-composition.ts +10 -10
  87. package/src/runtime/disposal-wiring.ts +8 -8
  88. package/src/runtime/fleet-needs-input-push.ts +4 -4
  89. package/src/runtime/fleet-services.ts +1 -1
  90. package/src/runtime/hosted-session-composition.ts +13 -13
  91. package/src/runtime/index.ts +1 -1
  92. package/src/runtime/knowledge-services.ts +2 -2
  93. package/src/runtime/legacy-daemon-migration.ts +43 -43
  94. package/src/runtime/legacy-daemon-reconcile.ts +30 -30
  95. package/src/runtime/mail-composition.ts +6 -6
  96. package/src/runtime/notification-dispatch.ts +7 -7
  97. package/src/runtime/payments-composition.ts +143 -0
  98. package/src/runtime/plugin-composition.ts +7 -7
  99. package/src/runtime/runtime-services-types.ts +9 -9
  100. package/src/runtime/services.ts +29 -27
  101. package/src/runtime/trigger-services.ts +1 -1
  102. package/src/runtime/trust/checkpoint-eligibility.ts +5 -5
  103. package/src/runtime/trust/trust-gated-approvals.ts +9 -9
  104. package/src/runtime/update-check.ts +4 -4
  105. package/src/runtime/workspace-checkpointing.ts +6 -6
  106. package/src/testing/daemon-fixture.ts +11 -11
  107. package/src/testing/hosted-session-failures.ts +4 -4
  108. package/src/version.ts +2 -2
@@ -60,7 +60,7 @@ import { createDevicePostureServices } from './device-posture-composition.ts';
60
60
  // Re-exported so the daemon entrypoint reaches the housekeeping sweep through
61
61
  // the same module it already imports the runtime graph from. `installDevicePosture`
62
62
  // is deliberately NOT re-exported: it registers the phone TOOL into a tool
63
- // registry, and the daemon registers no tools the sweep is the half it needs.
63
+ // registry, and the daemon registers no tools, the sweep is the half it needs.
64
64
  export { startDeviceHousekeeping } from './device-posture-composition.ts';
65
65
  import { createClusterServices, startClusterServices } from './cluster-group-composition.ts';
66
66
  import { createWorkspaceTrustDecisionAsk, trustGatedApprovalRaiser } from './trust/trust-gated-approvals.ts';
@@ -69,7 +69,7 @@ import type { RuntimeServicesOptions, RuntimeServices } from './runtime-services
69
69
  export type { RuntimeServicesOptions, RuntimeServices } from './runtime-services-types.ts';
70
70
 
71
71
  /**
72
- * createRuntimeServices the daemon's service graph.
72
+ * createRuntimeServices, the daemon's service graph.
73
73
  *
74
74
  * This is the one composition root the daemon has. Capabilities a client and
75
75
  * the daemon both need live in the SDK and are composed from there (memory
@@ -81,7 +81,7 @@ export type { RuntimeServicesOptions, RuntimeServices } from './runtime-services
81
81
  */
82
82
  export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeServices {
83
83
  // The SDK's disposal scope and its all-required poller list, plus the four
84
- // pollers only the daemon has see disposal-wiring.ts.
84
+ // pollers only the daemon has, see disposal-wiring.ts.
85
85
  const disposalScope = createDisposalScope('RuntimeServices');
86
86
  const workingDirectory = options.workingDir;
87
87
  const homeDirectory = options.homeDirectory;
@@ -105,7 +105,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
105
105
  // so the scheduler gates and the knowledge background jobs can consult the pause
106
106
  // controller before the MemoryGovernor (constructed at the composition tail)
107
107
  // drives it. The admission gate is late-bound: expensive entry points capture
108
- // this closure now and the governor binds into it at the tail until then
108
+ // this closure now and the governor binds into it at the tail, until then
109
109
  // everything is admitted (the daemon is still booting).
110
110
  const cacheRegistry = new CacheRegistry();
111
111
  const pauseController = new PauseController();
@@ -149,7 +149,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
149
149
  // Launch-tolerant: a provider whose API key is absent from the environment is
150
150
  // constructed with a placeholder that is stripped immediately afterwards, so
151
151
  // it lands unconfigured instead of throwing during construction. The daemon
152
- // has the same must-boot property the agent has it is a supervised service,
152
+ // has the same must-boot property the agent has, it is a supervised service,
153
153
  // and a constructor that throws on a missing key turns one unset variable into
154
154
  // a crash loop with no screen to explain it.
155
155
  const providerRegistry = createLaunchTolerantProviderRegistry({
@@ -186,8 +186,8 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
186
186
  );
187
187
  const hookActivityTracker = new HookActivityTracker();
188
188
  // featureFlags is REQUIRED here in practice, even though the SDK types it
189
- // optional. isFeatureGateEnabled(null, ...) is permissive by design a narrow
190
- // embed with no manager wired gets the capability rather than a silent off
189
+ // optional. isFeatureGateEnabled(null, ...) is permissive by design, a narrow
190
+ // embed with no manager wired gets the capability rather than a silent off,
191
191
  // so omitting it did not disable the watcher framework when watchers.enabled
192
192
  // is turned off; it made the setting configure nothing.
193
193
  const watcherRegistry = new WatcherRegistry({
@@ -235,9 +235,9 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
235
235
  task,
236
236
  // Conversation first: a follow-up message in a session gets an answer, not
237
237
  // a write-review-fix-confirm chain with a reviewer, quality gates and a
238
- // second agent. A chain opens only for an explicit authorization marker
238
+ // second agent. A chain opens only for an explicit authorization marker,
239
239
  // the channel confirmation the owner gave, or the schedule/trigger that
240
- // was confirmed when it was created or for a follow-up typed on a local
240
+ // was confirmed when it was created, or for a follow-up typed on a local
241
241
  // surface. Both `conversationGate.mode` and the gated-surfaces list are
242
242
  // read live.
243
243
  ...continuationChainOptions(input, {
@@ -256,7 +256,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
256
256
  // It also supplies the run's context, which is why the bare
257
257
  // `context: shared-session:<id>` line that used to sit below is gone.
258
258
  // Spread BEFORE the routing builder so an explicit tool list coming from
259
- // a routing intent still wins that builder emits `tools` only when it
259
+ // a routing intent still wins, that builder emits `tools` only when it
260
260
  // actually has one.
261
261
  ...conversationalTurnSpawnOptions(input, { configReader: configManager }),
262
262
  // Spawn routing through the SDK's shared model-reference resolver
@@ -286,7 +286,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
286
286
  // through. Without the secrets manager it cannot resolve a
287
287
  // goodvibes://secrets/... credential, so Telegram accepted every inbound
288
288
  // message and dropped every reply with "Missing Telegram bot token" while
289
- // ntfy which needs no secret worked.
289
+ // ntfy, which needs no secret, worked.
290
290
  secretsManager,
291
291
  serviceRegistry,
292
292
  runtimeBus: options.runtimeBus,
@@ -326,7 +326,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
326
326
  return record.id;
327
327
  },
328
328
  });
329
- // Knowledge/wiki + home-graph stack (governor backpressure wired in) see knowledge-services.ts.
329
+ // Knowledge/wiki + home-graph stack (governor backpressure wired in), see knowledge-services.ts.
330
330
  const {
331
331
  knowledgeStore, agentKnowledgeStore, homeGraphKnowledgeStore,
332
332
  knowledgeSemanticService, homeGraphSemanticService, agentKnowledgeSemanticService,
@@ -382,19 +382,21 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
382
382
  });
383
383
 
384
384
  // Which machines on this network are "us", and which of them reads the shared
385
- // inbox. Both inert until startCluster() no socket, no key material read;
385
+ // inbox. Both inert until startCluster(), no socket, no key material read;
386
386
  // see cluster-group-composition.ts for why they are built together.
387
387
  const { clusterGroup, clusterCoordinator } = createClusterServices({
388
388
  configManager, shellPaths, secretsManager,
389
389
  });
390
390
  // Daemon handler surfaces (see daemon-handler-composition.ts); the inbox
391
- // poller registers itself with the coordinator rather than starting eagerly.
391
+ // poller registers itself with the coordinator rather than starting eagerly,
392
+ // and the payments family stops being a cataloged 501 facade there.
392
393
  const daemonHandlers = createDaemonHandlerComposition({
393
394
  gatewayMethods,
394
395
  secretsManager,
395
396
  configManager,
396
397
  workingDirectory,
397
398
  homeDirectory,
399
+ shellPaths,
398
400
  distributedRuntime,
399
401
  clusterCoordinator,
400
402
  });
@@ -427,7 +429,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
427
429
  // ONE router, not two. This was a second ChannelDeliveryRouter built from the
428
430
  // same four arguments AutomationDeliveryManager builds its own from, so the
429
431
  // router the gateway verbs held and the router replies actually leave through
430
- // were different objects and a delivery strategy registered on one was
432
+ // were different objects, and a delivery strategy registered on one was
431
433
  // invisible to the other. The manager's is the one that replies; it is the one.
432
434
  const channelDeliveryRouter = deliveryManager.getDeliveryRouter();
433
435
  const processManager = new ProcessManager();
@@ -447,7 +449,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
447
449
  });
448
450
  const codeInjectionOrchestratorDeps = { codeIndex: codeIndexStore, isCodeInjectionSettingEnabled: () => isCodeInjectionSettingEnabled(configManager), codeIndexReindexScheduler };
449
451
  // The trigger family: stream watchers, on-exit process triggers, condition
450
- // checks fed to the fleet below as its trigger supervisor, so a trigger
452
+ // checks, fed to the fleet below as its trigger supervisor, so a trigger
451
453
  // is visible and steerable like every other running thing.
452
454
  const triggerManager = createTriggerServices({
453
455
  configManager, shellPaths, surfaceRoot: GOODVIBES_DAEMON_SURFACE_ROOT,
@@ -477,7 +479,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
477
479
  acpHost, // Folds live hosted-agent sessions into the fleet as 'acp' rows
478
480
  });
479
481
  const modeManager = new ModeManager({ featureFlags }); const fileUndoManager = new FileUndoManager();
480
- // Checkpoints, gated on live workspace registration see workspace-checkpointing.ts.
482
+ // Checkpoints, gated on live workspace registration, see workspace-checkpointing.ts.
481
483
  const checkpointing = createWorkspaceCheckpointing({
482
484
  workspaceRoot: workingDirectory, surface, runtimeBus: options.runtimeBus, configManager, shellPaths,
483
485
  });
@@ -487,7 +489,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
487
489
  // admitted (mirrors the SDK's own createRuntimeServices idle gate).
488
490
  const { memoryConsolidationScheduler, powerManager, sessionLiveTurnControls } = wireIdlePowerAndLiveTurn({ configManager, memoryRegistry, runtimeBus: options.runtimeBus, isIdle: () => sessionBroker.countBusySessions() === 0 && !pauseController.isPaused('memory-consolidation') && admitExpensiveWork('memory consolidation').allowed, snapshotTick: () => storeSnapshotScheduler.tick(), heartbeat: async () => { await automationManager.triggerHeartbeat({ source: 'wake-catchup' }); }, powerSeam: options.powerSeam });
489
491
 
490
- // Construct + start the MemoryGovernor (default ON a safety feature) with the
492
+ // Construct + start the MemoryGovernor (default ON, a safety feature) with the
491
493
  // standard KNOWN cache adapters (knowledge stores + shared session broker),
492
494
  // then late-bind the admission gate the expensive entry points captured
493
495
  // earlier. The SDK owns this wiring.
@@ -514,7 +516,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
514
516
  });
515
517
  admitExpensiveWorkRef.current = (label) => memoryGovernor.admitExpensiveWork(label);
516
518
 
517
- // Managed local-voice provisioning (voice.local.status/install) single-flight
519
+ // Managed local-voice provisioning (voice.local.status/install), single-flight
518
520
  // one-act install + no-network status.
519
521
  const { voiceSetup, stopWakeHousekeeping } = wireVoiceSetup({ configManager, shellPaths, voiceProviders, admitExpensiveWork,
520
522
  // Boot provisioning of the wake-word model + its recovery sweep, opted into
@@ -543,11 +545,11 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
543
545
  // to the owner principal instead of unknown.
544
546
  channelPolicy,
545
547
  approvalBroker, requestApproval: (input) => approvalBroker.requestApproval(input),
546
- // approvals.raise a surface CREATING an ask in this broker. Without it the
548
+ // approvals.raise, a surface CREATING an ask in this broker. Without it the
547
549
  // verb is cataloged and unhandled, and a client whose prompt runs outside
548
550
  // this process has no way to raise one.
549
551
  approvalRaise: approvalBroker,
550
- // credentials.set / credentials.delete a credential written THROUGH the
552
+ // credentials.set / credentials.delete, a credential written THROUGH the
551
553
  // control plane, so a client with no access to the daemon's settings file can
552
554
  // configure one. The value lands in the daemon's secret tier and the verb
553
555
  // never echoes it back.
@@ -573,15 +575,15 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
573
575
  const execPromptAnswerHandler = buildExecPromptAnswerHandler({ requestApproval: (input) => approvalBroker.requestApproval(input) });
574
576
  // Tool asks from the runs this daemon HOSTS. Without a manager here, the
575
577
  // background permission gate short-circuits to approved and every hosted
576
- // write, command and delegation ran ungated the workspace trust decision
578
+ // write, command and delegation ran ungated, the workspace trust decision
577
579
  // was read by nobody in this process.
578
580
  //
579
581
  // The ask seam is the trust gate wrapping the approval broker: a workspace
580
582
  // with no decision yet has the question raised as an approval record and
581
- // answered by whichever surface is attached (trust-gated-approvals.ts)
583
+ // answered by whichever surface is attached (trust-gated-approvals.ts),
582
584
  // there is no screen here to show a modal on, so the raise replaces it. The
583
- // manager's own layers permission mode, policy, session cache, durable
584
- // user rules still run first and are unchanged.
585
+ // manager's own layers, permission mode, policy, session cache, durable
586
+ // user rules, still run first and are unchanged.
585
587
  const permissionManager = createBrokeredPermissionManager({
586
588
  requestApproval: trustGatedApprovalRaiser(
587
589
  workspaceTrustManager,
@@ -648,7 +650,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
648
650
  // whole high-churn 'ops' domain, and sent over the SAME WebhookNotifier the
649
651
  // notification verbs keep live and boot-tasks attaches to the bus. There is
650
652
  // no panel notification router here: its targets are all screen targets and
651
- // this product has no screen see notification-dispatch.ts.
653
+ // this product has no screen, see notification-dispatch.ts.
652
654
  wireMemoryPressureChannelNotice(options.runtimeBus, webhookNotifier);
653
655
 
654
656
  // In-process config changes become key-level events on the `config` domain, so
@@ -780,7 +782,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
780
782
  // Cancels the agent runs this graph was hosting. By dispose() time the fleet
781
783
  // registry, orchestration engine, process registry and bus these runs report
782
784
  // through are already down, so a run still described as "running" is orphaned
783
- // rather than preserved and this is the only shutdown-reachable way to
785
+ // rather than preserved, and this is the only shutdown-reachable way to
784
786
  // abort its in-flight provider call instead of letting it sleep out a retry
785
787
  // backoff nobody is waiting on.
786
788
  cancelHostedAgentRuns: () => cancelAllAgentRuns(agentManager),
@@ -11,7 +11,7 @@ import type { ShellPathService } from '@/runtime/index.ts';
11
11
  *
12
12
  * This daemon composes the full family and feeds the manager to the fleet as
13
13
  * its trigger supervisor, so a trigger defined against the daemon fires
14
- * reliably. The daemon is the right process to own it it is the one that
14
+ * reliably. The daemon is the right process to own it, it is the one that
15
15
  * stays running.
16
16
  *
17
17
  * Two things about the shape are load-bearing:
@@ -23,7 +23,7 @@ import {
23
23
  *
24
24
  * This read is synchronous by design. The decision is made per lifecycle event,
25
25
  * inside a subscription callback that cannot await, and it has to reflect the
26
- * store as it is on disk RIGHT NOW registering a workspace while the daemon is
26
+ * store as it is on disk RIGHT NOW, registering a workspace while the daemon is
27
27
  * running has to take effect on the next eligible event, not on the next
28
28
  * restart. The SDK's resolver is pure, so the only I/O is one small JSON read
29
29
  * plus a single git worktree probe amortized at construction.
@@ -32,13 +32,13 @@ import {
32
32
  export type StoreShellPaths = Pick<ShellPathService, 'resolveUserPath' | 'homeDirectory'>;
33
33
 
34
34
  /**
35
- * Path of the shared store's JSON document the same path the SDK's gateway
35
+ * Path of the shared store's JSON document, the same path the SDK's gateway
36
36
  * verb group constructs its own store over.
37
37
  *
38
38
  * "The same path" is the whole contract, and it is why this goes through the
39
39
  * SDK's resolver rather than spelling any location out. goodvibes-agent reads
40
40
  * and writes this same file directly and the SDK's gateway writes it, so the
41
- * register is cross-product state rather than the daemon's own it lives in
41
+ * register is cross-product state rather than the daemon's own, it lives in
42
42
  * the shared tier (~/.goodvibes/shared/), which takes no surface root, so all
43
43
  * three resolve one identical path. Until the boot fold has moved it, the
44
44
  * resolver falls back to the pre-split location read-only, so this reader never
@@ -92,7 +92,7 @@ interface SharedRegistrationSnapshot {
92
92
  /**
93
93
  * Synchronous read of the shared store's on-disk JSON, mirroring the store's own
94
94
  * validation exactly (version 1, workspaces[], declines[]). A missing or
95
- * unparsable file reads as empty never throws.
95
+ * unparsable file reads as empty, never throws.
96
96
  */
97
97
  export function readSharedWorkspaceRegistrationSnapshotSync(shellPaths: StoreShellPaths): SharedRegistrationSnapshot {
98
98
  const path = sharedWorkspaceRegistrationStorePath(shellPaths);
@@ -116,7 +116,7 @@ export function readSharedWorkspaceRegistrationSnapshotSync(shellPaths: StoreShe
116
116
  }
117
117
 
118
118
  /**
119
- * Resolve `path` against ONLY the checkpoint-eligible registrations the
119
+ * Resolve `path` against ONLY the checkpoint-eligible registrations, the
120
120
  * boundary the automatic and explicit checkpoint gates consume. Worktree-link
121
121
  * inheritance still applies: a linked worktree of a checkpoint-eligible main
122
122
  * repository resolves as covered.
@@ -1,11 +1,11 @@
1
1
  /**
2
- * trust-gated-approvals.ts how a headless daemon asks the workspace trust
2
+ * trust-gated-approvals.ts, how a headless daemon asks the workspace trust
3
3
  * question, and how the answer reaches the runs it hosts.
4
4
  *
5
5
  * The terminal app composes the trust gate at the permission machinery's final
6
6
  * ask layer and raises the question as a modal on its own screen. The daemon
7
- * has the same gate `trustGatedAsk` next door, reading the same
8
- * `<cwd>/.goodvibes/<surface>/trust.json` the terminal app writes and no
7
+ * has the same gate, `trustGatedAsk` next door, reading the same
8
+ * `<cwd>/.goodvibes/<surface>/trust.json` the terminal app writes, and no
9
9
  * screen to raise anything on. Before this module it therefore
10
10
  * did neither: the gate was constructed, never loaded, never consulted, and no
11
11
  * hosted run passed through it.
@@ -16,7 +16,7 @@
16
16
  * ordinary approval record. That is the whole point of the approval-raise
17
17
  * path: a process with no screen states the question, publishes it on
18
18
  * `approval-update`, and whichever surface is attached answers it. Approved
19
- * means "trusted", denied means "restricted" a real decision either way,
19
+ * means "trusted", denied means "restricted", a real decision either way,
20
20
  * persisted by the gate, and never asked again for this workspace.
21
21
  *
22
22
  * 2. `trustGatedApprovalRaiser` puts the gate in front of the raiser the
@@ -30,8 +30,8 @@
30
30
  *
31
31
  * What the daemon does NOT do here is decide by default. An untrusted
32
32
  * workspace's hosted run neither runs as if trusted nor fails as if refused: it
33
- * asks. If the ask cannot be answered nothing attached, or the broker itself
34
- * failed the workspace stays undecided and this run is refused, with the
33
+ * asks. If the ask cannot be answered, nothing attached, or the broker itself
34
+ * failed, the workspace stays undecided and this run is refused, with the
35
35
  * reason in the log rather than in a silence. A refusal that records nothing is
36
36
  * the failure mode this whole seam exists to remove.
37
37
  */
@@ -44,7 +44,7 @@ type WorkspaceTrustLevel = operations.WorkspaceTrustLevel;
44
44
  type WorkspaceTrustManager = operations.WorkspaceTrustManager;
45
45
 
46
46
  /**
47
- * The extra fields a raise carries beside the request itself the attribution
47
+ * The extra fields a raise carries beside the request itself, the attribution
48
48
  * routing/metadata a background-agent ask is stamped with before it reaches the
49
49
  * broker. The gate only reads `request.category`, so these ride around it.
50
50
  */
@@ -73,7 +73,7 @@ export const WORKSPACE_TRUST_ASK_TIMEOUT_MS = 10 * 60 * 1000;
73
73
 
74
74
  export interface WorkspaceTrustDecisionAskDeps {
75
75
  readonly requestApproval: ApprovalRaise;
76
- /** The workspace the question is about it names the directory being trusted. */
76
+ /** The workspace the question is about, it names the directory being trusted. */
77
77
  readonly workingDirectory: string;
78
78
  readonly timeoutMs?: number | undefined;
79
79
  }
@@ -118,7 +118,7 @@ export function createWorkspaceTrustDecisionAsk(
118
118
  return decision.approved ? 'trusted' : 'restricted';
119
119
  } catch (error) {
120
120
  // Nothing attached, or the broker failed. Say so and leave the workspace
121
- // undecided the next run asks again rather than inheriting a decision
121
+ // undecided, the next run asks again rather than inheriting a decision
122
122
  // nobody made.
123
123
  logger.warn('Workspace trust question could not be answered; this run is refused and the workspace stays undecided', {
124
124
  workspace: deps.workingDirectory,
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Version comparison and the release-tag lookup are re-exported from the
6
6
  * SDK's canonical update policy module (platform/runtime/self-update), which
7
- * was hoisted from this file's semantics one mechanism everywhere.
7
+ * was hoisted from this file's semantics, one mechanism everywhere.
8
8
  * Install-kind detection stays local: it encodes how THIS package is
9
9
  * installed (compiled binary vs bun/npm package vs source run) and what
10
10
  * command replaces a swap for each kind.
@@ -27,14 +27,14 @@ export {
27
27
  * How this running process was installed, detected honestly from
28
28
  * process.execPath rather than assumed:
29
29
  * - "binary": a standalone `bun build --compile` executable with no
30
- * package-manager ancestry the scripts/install.sh install path.
30
+ * package-manager ancestry, the scripts/install.sh install path.
31
31
  * Swappable in place.
32
32
  * - "bun-global-package": running the vendored binary shipped inside an
33
33
  * npm/bun-managed package install (execPath contains a "node_modules"
34
- * path segment true for both `bun add -g` and a local project
34
+ * path segment, true for both `bun add -g` and a local project
35
35
  * dependency). Managed by the package manager; swapping the vendored
36
36
  * file in place would fight the next `bun add -g` upgrade, so this is
37
- * never swapped the user re-runs their package manager instead.
37
+ * never swapped, the user re-runs their package manager instead.
38
38
  * - "source": running directly via the `bun` interpreter (`bun run
39
39
  * src/main.ts`), not a compiled binary at all.
40
40
  */
@@ -17,19 +17,19 @@ import { createWorkspaceRegistrationLiveChecker, type StoreShellPaths } from './
17
17
  * runtime bus, so building it without one for an unregistered workspace would
18
18
  * mean registering that workspace mid-run had no effect until a restart. It is
19
19
  * always built WITH the bus and each individual automatic snapshot attempt is
20
- * refused instead by overriding this one instance's own `create`, since the
20
+ * refused instead, by overriding this one instance's own `create`, since the
21
21
  * manager has no predicate hook and `create` is the single seam both the
22
22
  * automatic subscription and every explicit caller pass through.
23
23
  */
24
24
 
25
25
  export interface WorkspaceCheckpointing {
26
- /** The manager itself automatic snapshots gated, explicit creates unrestricted. */
26
+ /** The manager itself, automatic snapshots gated, explicit creates unrestricted. */
27
27
  readonly manager: WorkspaceCheckpointManager;
28
28
  /**
29
29
  * The narrower surface handed to the `checkpoints.*` gateway verbs: identical
30
30
  * except that an explicit create refuses, with an actionable message, when the
31
31
  * workspace is not checkpoint-eligible. Reads (list/diff/sessionChanges) and
32
- * restore stay unrestricted they operate over checkpoints that may already
32
+ * restore stay unrestricted, they operate over checkpoints that may already
33
33
  * exist, including from a since-unregistered workspace.
34
34
  */
35
35
  readonly gatewayManager: Pick<WorkspaceCheckpointManager, 'list' | 'create' | 'diff' | 'restore' | 'sessionChanges' | 'workspaceRoot'>;
@@ -69,7 +69,7 @@ export function createWorkspaceCheckpointing(opts: {
69
69
  });
70
70
 
71
71
  // Automatic snapshots ('turn' | 'agent-run', fired by the manager's own bus
72
- // subscription) resolve to null quietly when the workspace is not eligible
72
+ // subscription) resolve to null quietly when the workspace is not eligible,
73
73
  // there is no caller to throw to, and the manager already documents a null
74
74
  // return as the cheap no-op for an unchanged tree. Explicit ('manual') creates
75
75
  // are NOT re-gated here: they go through the gateway surface below, which
@@ -82,7 +82,7 @@ export function createWorkspaceCheckpointing(opts: {
82
82
 
83
83
  // Eagerly initialize so the automatic-snapshot subscription is live before the
84
84
  // first turn completes. If init() rejects, the manager caches that rejection
85
- // forever and every later call re-throws it the catch here only prevents an
85
+ // forever and every later call re-throws it, the catch here only prevents an
86
86
  // unhandled rejection at startup; the checkpoint verbs report the failure to
87
87
  // whoever calls them.
88
88
  void manager.init().catch((error: unknown) => {
@@ -104,7 +104,7 @@ export function createWorkspaceCheckpointing(opts: {
104
104
  );
105
105
  }
106
106
  // Default the session stamp from the live resolver when the caller omits
107
- // it the resolveSessionId hook only auto-stamps automatic snapshots, so
107
+ // it, the resolveSessionId hook only auto-stamps automatic snapshots, so
108
108
  // without this an explicit checkpoint made this launch would be written
109
109
  // unstamped and excluded by the session-scoped restore lookup.
110
110
  const sessionId = createOpts.sessionId ?? opts.resolveSessionId?.({});
@@ -1,5 +1,5 @@
1
1
  /**
2
- * daemon-fixture.ts a real, running daemon, composed the way this product
2
+ * daemon-fixture.ts, a real, running daemon, composed the way this product
3
3
  * composes one, for a test to drive.
4
4
  *
5
5
  * ── Why this is a shipped module and not a test helper ────────────────────
@@ -8,7 +8,7 @@
8
8
  * and each of them hand-builds a composition to test against: a stub session
9
9
  * broker here, a fake approval broker there, a catalog with the handlers
10
10
  * somebody remembered to attach. They are large (several hundred lines each),
11
- * they duplicate each other, and the part that matters they are each a
11
+ * they duplicate each other, and, the part that matters, they are each a
12
12
  * SECOND idea of what a daemon is. A client contract test passing against a
13
13
  * hand-rolled stand-in tells you the stand-in agrees with the client, which is
14
14
  * the one agreement that was never in doubt.
@@ -43,19 +43,19 @@
43
43
  * can do for itself:
44
44
  *
45
45
  * 1. A NAMED ENTRY POINT. `goodvibes-daemon/testing` instead of the deep
46
- * path. That means adding an `exports` map to package.json and adding
46
+ * path. That means adding an `exports` map to package.json, and adding
47
47
  * one is not additive: an `exports` map REPLACES path-based resolution, so
48
48
  * every existing deep import into this package (its own `bin` shim
49
49
  * included) stops resolving unless the map enumerates them. That is a
50
50
  * packaging change with a blast radius, made once, deliberately, by
51
- * whoever owns distribution not a side effect of adding a test helper.
51
+ * whoever owns distribution, not a side effect of adding a test helper.
52
52
  *
53
53
  * 2. THE CONSUMER DEPENDENCY. A consumer repo has to depend on
54
54
  * `goodvibes-daemon` to import this at all. Today the agent does (it is
55
55
  * what makes its own install fail against an unpublished version); the
56
56
  * terminal app and the webui do not. For the terminal app that is a
57
57
  * devDependency and a version pin. For the webui, whose suites run in a
58
- * browser context, the fixture cannot run in-process at all it needs a
58
+ * browser context, the fixture cannot run in-process at all, it needs a
59
59
  * launcher script that starts the fixture in a node process and hands the
60
60
  * Playwright suite `baseUrl` and `token`. That launcher does not exist and
61
61
  * should be written on the webui side, where its runner lives.
@@ -65,12 +65,12 @@
65
65
  * per test file pays about a second each and must `stop()` every one; the
66
66
  * three hand-built stand-ins it replaces cost nothing and leak nothing.
67
67
  * The honest guidance is one fixture per FILE (`beforeAll`/`afterAll`),
68
- * which is how this repository's own suites use it not one per test.
68
+ * which is how this repository's own suites use it, not one per test.
69
69
  *
70
70
  * 4. WHAT IT DOES NOT REPLACE. A stand-in is still the right tool for
71
71
  * driving a client through a daemon state that is hard to reach for real:
72
72
  * a wedged session, a specific 500, a torn connection. This fixture
73
- * replaces the stand-ins that exist only to answer normally which is
73
+ * replaces the stand-ins that exist only to answer normally, which is
74
74
  * most of the several hundred lines in each consumer, and all of the part
75
75
  * that silently drifts. The refusal shapes it cannot easily produce live
76
76
  * are exported separately and pinned against the real engine; see
@@ -145,7 +145,7 @@ export interface DaemonFixtureOptions {
145
145
  }
146
146
 
147
147
  export interface DaemonFixture {
148
- /** The composed runtime graph the same object `DaemonServer` was handed. */
148
+ /** The composed runtime graph, the same object `DaemonServer` was handed. */
149
149
  readonly services: RuntimeServices;
150
150
  /** The running server. */
151
151
  readonly daemon: DaemonServer;
@@ -159,7 +159,7 @@ export interface DaemonFixture {
159
159
  fetch(path: string, init?: RequestInit): Promise<Response>;
160
160
  /**
161
161
  * Fetch a path with NO credential. A route that exists answers 401; a path
162
- * nothing serves answers 404 which is what makes this the side-effect-free
162
+ * nothing serves answers 404, which is what makes this the side-effect-free
163
163
  * way to ask whether a route exists, even for a write verb.
164
164
  */
165
165
  fetchAnonymous(path: string, init?: RequestInit): Promise<Response>;
@@ -170,7 +170,7 @@ export interface DaemonFixture {
170
170
  }
171
171
 
172
172
  /**
173
- * Compose and start a daemon. Always `await fixture.stop()` the runtime graph
173
+ * Compose and start a daemon. Always `await fixture.stop()`, the runtime graph
174
174
  * starts pollers while it builds, and abandoning it leaves every one of them
175
175
  * firing for the rest of the process.
176
176
  */
@@ -234,7 +234,7 @@ export async function startDaemonFixture(options: DaemonFixtureOptions = {}): Pr
234
234
  // What the real entrypoint states, for the same reason it states it: the
235
235
  // hosted-session verbs are registered by this composition and by nothing
236
236
  // else, so a fixture that leaves it out is a daemon a client cannot start a
237
- // session on and every contract test written against it would agree.
237
+ // session on, and every contract test written against it would agree.
238
238
  ...(options.hostSessions === false ? {} : { hostedSessions: createHostedSessionOptions(services) }),
239
239
  });
240
240
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * hosted-session-failures.ts what `sessions.hosted.*` answers when it refuses.
2
+ * hosted-session-failures.ts, what `sessions.hosted.*` answers when it refuses.
3
3
  *
4
4
  * ── Why this is a shipped module ─────────────────────────────────────────
5
5
  *
@@ -19,7 +19,7 @@
19
19
  * These are the three refusals a client has to handle and could not previously
20
20
  * mock. They are declared here rather than hand-written in each consumer, and
21
21
  * `src/test/daemon/gateway-hosted-session-failures.test.ts` drives the REAL
22
- * engine to produce each one and asserts it still matches so a fixture that
22
+ * engine to produce each one and asserts it still matches, so a fixture that
23
23
  * drifts from the daemon fails in this repository, not in a consumer's CI six
24
24
  * weeks later.
25
25
  */
@@ -28,7 +28,7 @@
28
28
  export interface HostedSessionFailureFixture {
29
29
  /** The verb whose refusal this is. */
30
30
  readonly methodId: string;
31
- /** Machine-readable code the field a client branches on. */
31
+ /** Machine-readable code, the field a client branches on. */
32
32
  readonly code: string;
33
33
  /** HTTP status the control plane maps this refusal to. */
34
34
  readonly status: number;
@@ -65,7 +65,7 @@ export const HOSTED_SESSION_UNAVAILABLE: HostedSessionFailureFixture = {
65
65
 
66
66
  /**
67
67
  * At the configured cap. 429: the request is well-formed and the daemon is not
68
- * broken there is no room. Retrying after a kill succeeds.
68
+ * broken, there is no room. Retrying after a kill succeeds.
69
69
  */
70
70
  export const HOSTED_SESSION_LIMIT_REACHED: HostedSessionFailureFixture = {
71
71
  methodId: 'sessions.hosted.create',
package/src/version.ts CHANGED
@@ -13,14 +13,14 @@ try {
13
13
  // compiled binary resolves import.meta.dir to a virtual root where the
14
14
  // `../package.json` path can land on a DIFFERENT package.json (a bundled
15
15
  // dependency's, or the embedded runtime's) that reports a placeholder like
16
- // "0.0.0" exactly the wrong-version banner a bare daemon launch showed in
16
+ // "0.0.0", exactly the wrong-version banner a bare daemon launch showed in
17
17
  // the field. Guarding on the package name means the prebuild-baked fallback
18
18
  // above wins in that case instead of a stray version.
19
19
  if (pkg?.name === 'goodvibes-daemon' && typeof pkg.version === 'string' && pkg.version.length > 0) {
20
20
  _version = pkg.version;
21
21
  }
22
22
  } catch {
23
- // Compiled binary or missing package.json use fallback
23
+ // Compiled binary or missing package.json, use fallback
24
24
  }
25
25
 
26
26
  export const VERSION = _version;