@pellux/goodvibes-daemon 1.28.11 → 1.28.13

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.
package/CHANGELOG.md CHANGED
@@ -4,7 +4,51 @@ All notable changes to the GoodVibes daemon.
4
4
 
5
5
  ---
6
6
 
7
- ## [Unreleased]
7
+ ## [1.28.13] - 2026-08-06
8
+
9
+ ### Changes
10
+
11
+ - **Fixed: an unnamed transcription or synthesis request goes to the voice
12
+ provider the user actually configured** (platform runtime 2.0.11). The
13
+ daemon's provider picker answered "use whatever this host has configured"
14
+ with the first name on its internal registration list — a cloud provider —
15
+ even when that provider had no key and the host carried fully provisioned
16
+ local engines. On a machine set up for local voice, every wake-word
17
+ transcription failed "OpenAI API key missing" while the user's working
18
+ whisper was never asked, and no settings key could override the pick.
19
+ Unnamed requests now prefer providers that report themselves configured,
20
+ with configured local engines first — free, offline, no key. A named
21
+ provider keeps exactly its previous behavior.
22
+
23
+ ## [1.28.12] - 2026-08-05
24
+
25
+ ### Changes
26
+
27
+ - **Fixed: a hosted conversational turn no longer gets the host.** The daemon
28
+ composes hosted sessions through the same `createClientRuntimeServices` a
29
+ terminal runs, so a hosted command already ran inside the same bubblewrap
30
+ boundary — network, PID, UTS and IPC namespaced, system read-only, /tmp and
31
+ $HOME masked, `sandbox.egressAllowlist` the one way network comes back. What
32
+ differed was the fallback: with no boundary available the command ran
33
+ directly on the host and only said so afterwards, and a conversational turn
34
+ used that to read the whole process table and drive the owner's terminal.
35
+ The daemon now STATES the posture. Every session it hosts is
36
+ `conversational`, which makes the boundary REQUIRED: a command that cannot be
37
+ contained is refused, naming why, and `background: true` is not a spelling
38
+ that gets around it. A hosted workstream that genuinely needs the machine is
39
+ a per-spawn grant written into this daemon's own composition — nothing on the
40
+ wire and nothing in a tool argument can reach it.
41
+ - **Fixed: the owner's terminal is untouchable.** A command that drives an
42
+ existing tmux session, window or pane this platform did not create —
43
+ send-keys, kill, resize, attach, respawn, rename — is refused by the exec
44
+ guard for a hosted turn, with a refusal that names the rule. Creating and
45
+ driving the platform's own sessions is unchanged, and so is reading tmux
46
+ state (`list-sessions`, `list-panes`, `capture-pane`), which the fleet view
47
+ already does. The frozen catastrophic block is untouched.
48
+ - Changed: this daemon's hosted operator prompt carries the platform's
49
+ conversational diagnosis contract — report the state and propose, never
50
+ restart the owner's applications or type into his terminal to "fix" things,
51
+ and a "fixed" claim needs the live evidence it rests on.
8
52
 
9
53
  ## [1.28.11] - 2026-08-05
10
54
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
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.11-blue.svg)](https://github.com/mgd34msu/goodvibes-daemon)
5
+ [![Version](https://img.shields.io/badge/version-1.28.13-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
8
  GoodVibes client talks to. It answers the operator verb families over HTTP, reads and replies on
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-daemon",
3
- "version": "1.28.11",
3
+ "version": "1.28.13",
4
4
  "description": "The GoodVibes daemon \u2014 the one long-running host for the control plane, channels, cluster membership, scheduled work, knowledge and memory stores, and the verb families every GoodVibes client calls.",
5
5
  "type": "module",
6
6
  "main": "src/daemon/cli.ts",
@@ -65,8 +65,8 @@
65
65
  "@anthropic-ai/bedrock-sdk": "^0.28.1",
66
66
  "@anthropic-ai/sdk": "^0.82.0",
67
67
  "@ast-grep/napi": "^0.42.0",
68
- "@pellux/goodvibes-sdk": "2.0.9",
69
- "@pellux/goodvibes-terminal-shell": "2.0.9",
68
+ "@pellux/goodvibes-sdk": "2.0.11",
69
+ "@pellux/goodvibes-terminal-shell": "2.0.11",
70
70
  "bash-language-server": "^5.6.0",
71
71
  "fuse.js": "^7.1.0",
72
72
  "graphql": "^16.13.2",
@@ -23,13 +23,46 @@
23
23
  * Everything else in a floor comes from `createClientRuntimeServices` — the
24
24
  * same composition a terminal runs — so a hosted turn's tools, hooks, plugins
25
25
  * and model stack are the ones the platform already has, not a second set.
26
+ *
27
+ * ── The exec posture a hosted turn runs under ──────────────────────────────
28
+ *
29
+ * "The same composition a terminal runs" is what BUILDS the exec tool, and it
30
+ * builds it the same way here: the `sandbox.*` config and the `exec-sandbox`
31
+ * gate this daemon already reads produce the same bubblewrap boundary a local
32
+ * exec gets — network, PID, UTS and IPC namespaced, the system read-only, /tmp
33
+ * and $HOME masked, the workspace writable, and `sandbox.egressAllowlist` the
34
+ * one way network comes back — with the self-labelling note on every result.
35
+ *
36
+ * What was NOT the same was the fallback. When no boundary could be applied,
37
+ * the exec tool ran the command on the host and said so, which is right for a
38
+ * terminal (a person asked, a person is reading) and wrong for a turn nobody is
39
+ * watching. A hosted conversational turn reached the whole host that way: the
40
+ * full process table, the owner's /proc, and his tmux session — where it typed.
41
+ *
42
+ * So this daemon states the posture rather than inheriting a default:
43
+ *
44
+ * - CONVERSATIONAL is what every session created over `sessions.hosted.*`
45
+ * gets, and it is the posture the engine defaults to. The boundary is
46
+ * REQUIRED: a command that cannot be contained is refused, naming why, and
47
+ * running on the host is not a fallback available to it. The owner's
48
+ * terminal is denied outright, at the exec guard, regardless of boundary.
49
+ * - WORKSTREAM — a real work chain the owner authorized, which may genuinely
50
+ * need the machine itself — is a per-spawn grant this composition makes
51
+ * explicitly. There is no such spawn today, so the function below never
52
+ * returns it; when there is one, the grant is written HERE, in the daemon's
53
+ * own composition, where a reader can see which spawns hold it. Nothing on
54
+ * the wire and nothing in a tool argument can reach it.
26
55
  */
27
56
  import { createShellPathService } from '@/runtime/index.ts';
28
57
  import { createClientRuntimeServices } from '@pellux/goodvibes-sdk/platform/runtime/client-services';
29
58
  import { createRuntimeStore } from '@pellux/goodvibes-sdk/platform/runtime/store';
30
59
  import { createLaunchTolerantProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
31
60
  import type { DaemonHostedSessionsOptions } from '@pellux/goodvibes-sdk/platform/daemon';
32
- import type { HostedWorkspaceFloor } from '@pellux/goodvibes-sdk/platform/hosted-sessions';
61
+ import type {
62
+ HostedSessionExecPosture,
63
+ HostedWorkspaceFloor,
64
+ } from '@pellux/goodvibes-sdk/platform/hosted-sessions';
65
+ import { CONVERSATIONAL_DIAGNOSIS_SECTION } from '@pellux/goodvibes-sdk/platform/agents';
33
66
  import { operations } from '@pellux/goodvibes-sdk/platform/runtime';
34
67
  const { WorkspaceTrustManager } = operations;
35
68
  import { createWorkspaceTrustDecisionAsk, trustGatedApprovalRaiser, type ApprovalRaise } from './trust/trust-gated-approvals.ts';
@@ -39,13 +72,32 @@ import type { RuntimeServices } from './runtime-services-types.ts';
39
72
  /** The operator policy a session hosted by THIS daemon runs under. */
40
73
  function hostedSystemPrompt(input: { readonly workspaceRoot: string }): string {
41
74
  return [
42
- 'You are a GoodVibes session hosted by the daemon rather than by a terminal.',
43
- `Your working directory is ${input.workspaceRoot}.`,
44
- 'Someone may be attached and watching this turn, or may have detached and read it later —',
45
- 'write for both. Say what you did and why; never report work you did not do.',
46
- 'Tool permissions are decided by whoever is attached: an ask you raise may take a while to be',
47
- 'answered, and an unanswered one is a refusal, not a reason to find another way round.',
48
- ].join(' ');
75
+ [
76
+ 'You are a GoodVibes session hosted by the daemon rather than by a terminal.',
77
+ `Your working directory is ${input.workspaceRoot}.`,
78
+ 'Someone may be attached and watching this turn, or may have detached and read it later —',
79
+ 'write for both. Say what you did and why; never report work you did not do.',
80
+ 'Tool permissions are decided by whoever is attached: an ask you raise may take a while to be',
81
+ 'answered, and an unanswered one is a refusal, not a reason to find another way round.',
82
+ ].join(' '),
83
+ // The same contract every other conversational turn is held to, from the
84
+ // SDK rather than restated here — a second copy is a copy that drifts.
85
+ CONVERSATIONAL_DIAGNOSIS_SECTION,
86
+ ].join('\n\n');
87
+ }
88
+
89
+ /**
90
+ * What a hosted session's exec tool may do.
91
+ *
92
+ * Every session this daemon hosts is a conversation, so every one of them is
93
+ * contained (see the module header). Stated on the floor rather than left to
94
+ * the engine's default so that the day a workstream spawn exists, the grant is
95
+ * a visible change to THIS function and not an option someone set elsewhere —
96
+ * and it sits beside the trust gate, which is the other statement this daemon
97
+ * makes about how much authority a hosted run carries.
98
+ */
99
+ function hostedExecPosture(): HostedSessionExecPosture {
100
+ return 'conversational';
49
101
  }
50
102
 
51
103
  /**
@@ -120,6 +172,7 @@ export function createHostedSessionOptions(services: RuntimeServices): DaemonHos
120
172
  // The daemon has a real review-chain controller; a hosted session's
121
173
  // orchestrator lists through it rather than reporting none.
122
174
  wrfcController: services.wrfcController,
175
+ execPosture: hostedExecPosture,
123
176
  dispose: (): void => floor.dispose(),
124
177
  };
125
178
  },
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '1.28.11';
9
+ let _version = '1.28.13';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  // Only trust a version read from OUR OWN package.json. A Bun single-file