@pellux/goodvibes-daemon 1.28.7 → 1.28.9

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
@@ -6,6 +6,52 @@ All notable changes to the GoodVibes daemon.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.28.9] - 2026-08-02
10
+
11
+ ### Changes
12
+
13
+ - Changed: a conversational turn is told to understand what it captures, not
14
+ just file it — an itinerary also means an away-span said back in plain
15
+ words, travelers who are people in the owner's life, and durable facts
16
+ about the destination, and the turn uses what it stored: it names
17
+ collisions with existing plans and offers the obviously useful next steps
18
+ once (a reminder before departure, weather at the destination). Anything
19
+ beyond the conversation is still proposed and waits for a yes (platform
20
+ runtime 2.0.7).
21
+
22
+ ## [1.28.8] - 2026-08-02
23
+
24
+ ### Changes
25
+
26
+ - **Fixed: a chat message gets an answer, never workflow paperwork.** The
27
+ conversation gate's decision that a channel message is conversation is now
28
+ authoritative — the review-wording heuristic that turned "I'll review the
29
+ route" in a transcript into a full write-review-fix-confirm chain can no
30
+ longer override it. When a chain does legitimately run, the reply sent back
31
+ over the channel carries what the agent actually found or did; chain status
32
+ lines stay in the operator progress feed. Duplicate replies from the two
33
+ completion reporters are gone (platform runtime 2.0.6).
34
+ - **Added: personal information shared in conversation gets captured.** A
35
+ conversational channel turn — which previously ran with no tools at all —
36
+ now carries the profile capture tool with per-run owner authority: a trip
37
+ itinerary pasted into Telegram lands in the owner profile's Plans section
38
+ with its dates, flights, travelers and confirmation number, and the reply
39
+ states concretely what was stored. Configurable via
40
+ `profile.conversationalCapture` (default on) and `profile.ownerChannels`
41
+ (defaults to the occasions nudge channel).
42
+ - **Fixed: a chat session is filed as a chat.** Channel-originated sessions
43
+ are classified by their channel instead of as TUI project sessions rooted
44
+ in a filesystem path, rollover re-derives the classification, and a sender
45
+ on the channel's owner allowlist is attributed as the owner principal
46
+ instead of unknown. The daemon now hands its channel ingress policy to the
47
+ session intake so that attribution works end to end.
48
+ - Fixed: this daemon's settings file is rewritten only by this daemon.
49
+ Clients reading the daemon settings tier migrate their in-memory view and
50
+ leave the file bytes untouched, so a newer client can no longer rewrite the
51
+ file out from under an older running daemon; the daemon's own migration
52
+ folds the rename, the receipt, and a reader floor into one atomic write
53
+ (platform runtime 2.0.6).
54
+
9
55
  ## [1.28.7] - 2026-08-02
10
56
 
11
57
  ### Changes
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.7-blue.svg)](https://github.com/mgd34msu/goodvibes-daemon)
5
+ [![Version](https://img.shields.io/badge/version-1.28.9-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.7",
3
+ "version": "1.28.9",
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.5",
69
- "@pellux/goodvibes-terminal-shell": "2.0.5",
68
+ "@pellux/goodvibes-sdk": "2.0.7",
69
+ "@pellux/goodvibes-terminal-shell": "2.0.7",
70
70
  "bash-language-server": "^5.6.0",
71
71
  "fuse.js": "^7.1.0",
72
72
  "graphql": "^16.13.2",
@@ -5,6 +5,7 @@ import { ChannelPolicyManager } from '@pellux/goodvibes-sdk/platform/channels';
5
5
  import { ApprovalBroker, GatewayMethodCatalog, SharedSessionBroker, buildSharedSessionAgentSpawnRoutingInput } from '@pellux/goodvibes-sdk/platform/control-plane';
6
6
  import { AcpHostService } from '@pellux/goodvibes-sdk/platform/acp';
7
7
  import { continuationChainOptions } from '@pellux/goodvibes-sdk/platform/agents';
8
+ import { PersonalCaptureHolder, conversationalTurnSpawnOptions } from '@pellux/goodvibes-sdk/platform/personal-capture';
8
9
  import { resolvePairingWebOrigin } from '@pellux/goodvibes-sdk/platform/pairing';
9
10
  import { attachWsOnlyGatewayVerbHandlers } from '@pellux/goodvibes-terminal-shell';
10
11
  import { composeMailDeps } from './mail-composition.ts';
@@ -205,6 +206,13 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
205
206
  } = createAgentGraph({
206
207
  runtimeBus: options.runtimeBus, workingDirectory, configManager, providerRegistry,
207
208
  });
209
+ // The one late-binding holder for the personal-capture port. The gateway verb
210
+ // groups fill it (they own the owner-profile store and the occasions service),
211
+ // and the agent orchestrator reads it when it builds a run's tool registry.
212
+ // Registration happens before setDependencies in this file, but the registry
213
+ // is built per run, so by the time a conversational turn asks for `profile`
214
+ // the port is already in place.
215
+ const personalCapture = new PersonalCaptureHolder();
208
216
  const hookDispatcher = new HookDispatcher({ agentManager, toolLLM, projectRoot: workingDirectory }, hookActivityTracker);
209
217
  configManager.attachHookDispatcher(hookDispatcher);
210
218
  const hookWorkbench = createHookWorkbench({
@@ -238,11 +246,23 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
238
246
  getCategory: (name: string) => configManager.getCategory(name as never),
239
247
  },
240
248
  }),
249
+ // What the answering agent is given and what it is told. `restrictTools:
250
+ // true` with no tool list used to mean an EMPTY registry: the agent could
251
+ // emit text and nothing else, which is why a trip pasted into a channel
252
+ // was answered and stored nowhere. This names the tools a conversational
253
+ // turn actually needs (read, find, fetch, and the `profile` capture tool),
254
+ // supplies the instruction that makes recording part of answering, and
255
+ // carries the write authority for the surface the message arrived on.
256
+ // It also supplies the run's context, which is why the bare
257
+ // `context: shared-session:<id>` line that used to sit below is gone.
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
260
+ // actually has one.
261
+ ...conversationalTurnSpawnOptions(input, { configReader: configManager }),
241
262
  // Spawn routing through the SDK's shared model-reference resolver
242
263
  // (unique-across-registry auto-qualifies; ambiguous and unknown ids throw
243
264
  // errors naming real candidates), against the live registry's models.
244
265
  ...buildSharedSessionAgentSpawnRoutingInput(input.routing, { restrictTools: true, modelCandidates: providerRegistry.listModels() }),
245
- context: `shared-session:${input.sessionId}`,
246
266
  });
247
267
  return { agentId: record.id };
248
268
  });
@@ -511,6 +531,13 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
511
531
  // an unregistered workspace refuses with something actionable.
512
532
  workspaceCheckpointManager: checkpointing.gatewayManager,
513
533
  conversationRewindPort: createSessionConversationRewindPort(), sessionBroker, secretsManager, stepUpService,
534
+ // Fills the capture port with the owner-profile store and the occasions
535
+ // service, so the `profile` tool a conversational turn is given has
536
+ // somewhere real to write.
537
+ personalCapture,
538
+ // The session intake attributes a sender on this policy's owner allowlist
539
+ // to the owner principal instead of unknown.
540
+ channelPolicy,
514
541
  approvalBroker, requestApproval: (input) => approvalBroker.requestApproval(input),
515
542
  // approvals.raise — a surface CREATING an ask in this broker. Without it the
516
543
  // verb is cataloged and unhandled, and a client whose prompt runs outside
@@ -596,6 +623,9 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
596
623
  sandboxSessionRegistry,
597
624
  workflowServices: workflow,
598
625
  contextAccountingHolder,
626
+ // Without this the `profile` tool is never registered, and a conversational
627
+ // turn that was told to record what the owner said has nothing to call.
628
+ personalCapture,
599
629
  });
600
630
 
601
631
  // Continuity reads (recovery-file presence, last-session pointer) scoped to
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.7';
9
+ let _version = '1.28.9';
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