@pellux/goodvibes-tui 2.0.6 → 2.0.8

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,6 +4,50 @@ All notable changes to GoodVibes TUI.
4
4
 
5
5
  ---
6
6
 
7
+ ## [2.0.8] - 2026-08-05
8
+
9
+ ### Changes
10
+
11
+ - **Fixed: profile reads work against an up-to-date daemon.** The daemon's
12
+ profile answers carried more than the published contract allowed, so strict
13
+ clients refused them; the contract and the answers agree now, held together
14
+ by a conformance test (platform runtime 2.0.8).
15
+ - Changed: session event streams are render-grade and session-scoped, so a
16
+ session viewed from this client shows what the model said and what it did —
17
+ including the agent's daemon-hosted conversations, now cross-visible with
18
+ their messages.
19
+ - Changed: browser automation uses the platform's own managed profile, one
20
+ live session, capped retries, and hands sign-in pages back as URLs.
21
+ - Changed: the settings catalog carries the connector keys the daemon really
22
+ reads (calendar, google, email), and catalog search understands plain words
23
+ (platform runtime 2.0.8).
24
+
25
+ ## [2.0.7] - 2026-08-02
26
+
27
+ ### Changes
28
+
29
+ - **Fixed: a chat message gets an answer, never workflow paperwork.** The
30
+ conversation gate's decision that a channel message is conversation is now
31
+ authoritative over the review-wording heuristic, replies carry the agent's
32
+ answer instead of chain status lines, and duplicate completion replies are
33
+ gone (platform runtime 2.0.7).
34
+ - **Added: personal information shared in conversation gets captured — and
35
+ understood.** A conversational channel turn carries the profile capture
36
+ tool: a pasted itinerary lands in the owner profile's Plans section, the
37
+ turn infers what it means (the away-span, the travelers, durable facts
38
+ about the destination), uses it in the answer, and offers the obviously
39
+ useful next steps once. Anything beyond the conversation is proposed and
40
+ waits for a yes.
41
+ - **Fixed: a chat session is filed as a chat.** Channel-originated sessions
42
+ in the cross-visible session list carry the new `channel` kind instead of
43
+ reading as TUI project sessions rooted in a filesystem path; this client
44
+ understands the new kind, and a sender on a channel's owner allowlist is
45
+ attributed as the owner principal.
46
+ - Fixed: the daemon settings file is rewritten only by the daemon — this
47
+ client migrates its in-memory view of renamed settings and leaves the file
48
+ bytes untouched, so updating the TUI can no longer rewrite settings out
49
+ from under an older running daemon (platform runtime 2.0.7).
50
+
7
51
  ## [2.0.6] - 2026-08-02
8
52
 
9
53
  ### Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/mgd34msu/goodvibes-tui/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-2.0.6-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
5
+ [![Version](https://img.shields.io/badge/version-2.0.8-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
6
6
 
7
7
  GoodVibes is a terminal console for coding and operations work with an AI model. You run `goodvibes` in a project directory and get a full-screen terminal app where you talk to a model that can read and edit your files, run shell commands, search the web, and hand work off to background agents — asking your permission before anything that writes or executes. It talks to many model providers (OpenAI, Anthropic, Gemini, Bedrock, Copilot, OpenRouter and other OpenAI-compatible gateways, plus local servers like Ollama and LM Studio that it finds on startup), keeps its settings, sessions, and secrets on your own machine, and shows you the token count and running cost of every turn. Alongside the conversation, panels turn background work — running agents, git state, diffs, tokens, cost — into live control rooms.
8
8
 
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "2.0.5"
6
+ "version": "2.0.8"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
@@ -79225,6 +79225,9 @@
79225
79225
  "valid": {
79226
79226
  "type": "boolean"
79227
79227
  },
79228
+ "section": {
79229
+ "type": "string"
79230
+ },
79228
79231
  "invalidReason": {
79229
79232
  "type": "string"
79230
79233
  },
@@ -79253,7 +79256,8 @@
79253
79256
  "fieldId",
79254
79257
  "label",
79255
79258
  "value",
79256
- "valid"
79259
+ "valid",
79260
+ "section"
79257
79261
  ],
79258
79262
  "additionalProperties": false
79259
79263
  },
@@ -79405,24 +79409,31 @@
79405
79409
  "type": "boolean"
79406
79410
  },
79407
79411
  "provenance": {
79408
- "type": "object",
79409
- "properties": {
79410
- "surface": {
79411
- "type": "string"
79412
- },
79413
- "date": {
79414
- "type": "string"
79412
+ "anyOf": [
79413
+ {
79414
+ "type": "object",
79415
+ "properties": {
79416
+ "surface": {
79417
+ "type": "string"
79418
+ },
79419
+ "date": {
79420
+ "type": "string"
79421
+ },
79422
+ "said": {
79423
+ "type": "string"
79424
+ }
79425
+ },
79426
+ "required": [
79427
+ "surface",
79428
+ "date",
79429
+ "said"
79430
+ ],
79431
+ "additionalProperties": false
79415
79432
  },
79416
- "said": {
79417
- "type": "string"
79433
+ {
79434
+ "type": "null"
79418
79435
  }
79419
- },
79420
- "required": [
79421
- "surface",
79422
- "date",
79423
- "said"
79424
- ],
79425
- "additionalProperties": false
79436
+ ]
79426
79437
  },
79427
79438
  "superseded": {
79428
79439
  "type": "array",
@@ -79488,6 +79499,7 @@
79488
79499
  "fieldId",
79489
79500
  "present",
79490
79501
  "handEdited",
79502
+ "provenance",
79491
79503
  "superseded"
79492
79504
  ],
79493
79505
  "additionalProperties": false
@@ -95946,6 +95958,7 @@
95946
95958
  "companion-task",
95947
95959
  "companion-chat",
95948
95960
  "automation",
95961
+ "channel",
95949
95962
  "acp",
95950
95963
  "hosted"
95951
95964
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "description": "Terminal-native GoodVibes client for coding, operations, knowledge and channel workflows — a pure client of the GoodVibes daemon.",
5
5
  "type": "module",
6
6
  "main": "src/main.ts",
@@ -95,8 +95,8 @@
95
95
  "@anthropic-ai/bedrock-sdk": "^0.29.1",
96
96
  "@anthropic-ai/sdk": "^0.92.0",
97
97
  "@ast-grep/napi": "^0.42.0",
98
- "@pellux/goodvibes-sdk": "2.0.5",
99
- "@pellux/goodvibes-terminal-shell": "2.0.5",
98
+ "@pellux/goodvibes-sdk": "2.0.8",
99
+ "@pellux/goodvibes-terminal-shell": "2.0.8",
100
100
  "bash-language-server": "^5.6.0",
101
101
  "fuse.js": "^7.1.0",
102
102
  "@pellux/goodvibes-daemon": "1.28.7",
@@ -354,7 +354,9 @@ export function toProfileProvenanceReport(value: unknown): Checked<ProfileProven
354
354
  if (provenance === MALFORMED) return MALFORMED;
355
355
  const superseded = checkedArray(record.superseded, toSuperseded);
356
356
  if (superseded === MALFORMED) return MALFORMED;
357
- return { fieldId, present, handEdited, ...(provenance ? { provenance } : {}), superseded };
357
+ // Platform runtime 2.0.8: the contract declares provenance nullable and
358
+ // always present — null IS the answer for a hand-edited field.
359
+ return { fieldId, present, handEdited, provenance: provenance ?? null, superseded };
358
360
  }
359
361
 
360
362
  function toChange(value: unknown): Checked<ProfileChangeView> {
@@ -81,6 +81,16 @@ export type SettingsCategory =
81
81
  // hostedSessions.* key from the workspace, which is exactly what happened to
82
82
  // push.* and cluster.* before them.
83
83
  | 'hostedSessions'
84
+ // email.* / calendar.* / google.* — the connector keys the daemon really
85
+ // reads for its Google-backed mail and calendar services (platform runtime
86
+ // 2.0.8 registered them as described schema rows; before that they were
87
+ // cast onto the live config invisibly and answered "Unknown setting").
88
+ // Same mandatory-dual-membership rule as profile and hostedSessions above —
89
+ // without BOTH these entries and the SETTINGS_CATEGORY_GROUPS membership
90
+ // below, every one of the 22 connector keys is dropped from the workspace.
91
+ | 'email'
92
+ | 'calendar'
93
+ | 'google'
84
94
  | 'danger';
85
95
 
86
96
  export type SettingsFocusPane = 'categories' | 'settings';
@@ -96,7 +106,10 @@ export const SETTINGS_CATEGORY_GROUPS: ReadonlyArray<{
96
106
  // answer "where does this conversation actually run, and who can reach it" —
97
107
  // the detach policy is a fact about the daemon holding a session open, not
98
108
  // about this terminal's presentation of it.
99
- { label: 'Surfaces & Cloud', categories: ['surfaces', 'hostedSessions', 'device', 'conversationGate', 'integrations', 'connections', 'mcp', 'cloudflare'] },
109
+ // 'email', 'calendar' and 'google' sit beside 'surfaces': they configure
110
+ // the daemon's connections to the owner's mail and calendar — where those
111
+ // services reach the world, not how this terminal presents anything.
112
+ { label: 'Surfaces & Cloud', categories: ['surfaces', 'hostedSessions', 'device', 'conversationGate', 'integrations', 'connections', 'email', 'calendar', 'google', 'mcp', 'cloudflare'] },
100
113
  // 'occasions' sits beside 'checkin': both are proactive background loops
101
114
  // (a cadence, a judgment pass, a conditional channel delivery) rather than
102
115
  // facts held about the owner — the data occasions/plans declare lives in
@@ -115,6 +115,9 @@ export const CATEGORY_INFO: Record<SettingsCategory, string> = {
115
115
  relay: 'Outbound zero-knowledge relay reachability, for reaching this daemon from outside the LAN without opening an inbound port. Off by default — relay.enabled is the switch. The relay operator sees only ciphertext and connection metadata — self-host your own relay for full control.',
116
116
  learning: 'Idle-time memory consolidation: merging duplicate standing memory records and decaying/archiving stale ones. Off by default — nothing runs until enabled.',
117
117
  payments: 'Payment card on file for daemon-initiated purchases: card number, expiry, CVV, cardholder name, billing and shipping address, and whether the CVV is stored or requested at purchase time. Card fields are entered through /payments card (concealed input) and are secret-tier — never rendered in plaintext.',
118
+ email: 'The daemon\'s own mailbox connection — IMAP and SMTP hosts, ports and security, the mailbox and drafts folders, and the username. Password fields hold secret-store references, never values; /google connects a Gmail-backed setup in one action.',
119
+ calendar: 'Calendar connections the daemon reads and writes: the Google or Microsoft OAuth client for CalDAV-free access, or a read-only ICS URL. Secret fields hold secret-store references, never values.',
120
+ google: 'The Google OAuth connection record: project id, publishing status, and the refresh-token reference the mail and calendar services share. Established by /google in one action; a dead grant is diagnosed in plain words there.',
118
121
  profile: 'What the platform knows about you — your name, how to reach you, where you are, how you like answers written, the people and places you mention — kept as one Markdown file at daemon scope that you can open and edit by hand at any time; your edits win and are never rewritten. These rows decide whether it is loaded at all, whether the assistant records facts it learns from things you say directly to it, whether it tells you in one line each time it does, whether the harmless part (how you are addressed, your city, your timezone, your formatting preferences, your style) is put in the model\'s context so it stops guessing, whether using a private detail such as your address is announced in the reply, whether settings left unset read their value from the profile, and where the file lives. Everything private — your name, contact details, home address, billing and shipping details, quiet hours, and the People, Places, Work and Notes sections — is never put in context in bulk and is reachable only by a named lookup that is disclosed when it happens. Run /profile to read the whole thing, /profile where <field> to see the surface, date and your exact words behind any recorded line, and /profile forget <field> to delete one.',
119
122
  };
120
123
 
@@ -159,6 +162,9 @@ export const CATEGORY_LABELS: Record<(typeof SETTINGS_CATEGORIES)[number], strin
159
162
  release: 'Release',
160
163
  power: 'Power',
161
164
  memory: 'Memory',
165
+ email: 'Email Connection',
166
+ calendar: 'Calendar Connections',
167
+ google: 'Google Connection',
162
168
  danger: 'Danger',
163
169
  tools: 'Tools',
164
170
  network: 'Network',
@@ -108,6 +108,8 @@ export interface RuntimeServicesOptions {
108
108
  export interface RuntimeServices {
109
109
  readonly workingDirectory: string;
110
110
  readonly homeDirectory: string;
111
+ /** The surface segment this runtime's stores live under (platform runtime 2.0.8 requires it — a blank segment resolved to the unscoped control-plane orphan). */
112
+ readonly surfaceRoot: string;
111
113
  /** The declare-once session-storage handle every session reader and writer threads through — see session-storage-services.ts. */
112
114
  readonly surface: SessionSurface;
113
115
  readonly shellPaths: ShellPathService;
@@ -559,6 +559,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
559
559
  const services: RuntimeServices = {
560
560
  workingDirectory,
561
561
  homeDirectory,
562
+ surfaceRoot: GOODVIBES_TUI_SURFACE_ROOT,
562
563
  surface,
563
564
  shellPaths,
564
565
  workspaceTrustManager,
@@ -438,12 +438,56 @@ export const PAYMENTS_LOCAL_SETTINGS = [
438
438
  'payments.notifyChannels',
439
439
  ] as const;
440
440
 
441
+ /**
442
+ * Connector settings — the daemon's Google-backed mail/calendar keys plus the
443
+ * connected-host dial switch and the hosted-turn routing switch, declared as
444
+ * described schema rows by platform runtime 2.0.8 (before that the connector
445
+ * keys were cast onto the live config invisibly and the settings surface
446
+ * answered "Unknown setting" for keys the daemon really reads). Counted for
447
+ * exactly the reason DAEMON_MAILBOX_LOCAL_SETTINGS is: the same four-part
448
+ * persistence contract runs over every one of them in
449
+ * src/test/verification/connector-settings-persistence.test.ts, and that test
450
+ * asserts this list is the complete schema set for the email, calendar and
451
+ * google prefixes
452
+ * plus the two switches. Arriving uncounted is what dropped
453
+ * localBehaviorPercent through its floor when the schema grew. The three
454
+ * secret-valued keys hold goodvibes://secrets/ references, never values —
455
+ * same note as the mailbox passwords above.
456
+ */
457
+ export const CONNECTOR_LOCAL_SETTINGS = [
458
+ 'email.enabled',
459
+ 'email.imapHost',
460
+ 'email.imapPort',
461
+ 'email.imapSecurity',
462
+ 'email.smtpHost',
463
+ 'email.smtpPort',
464
+ 'email.smtpSecurity',
465
+ 'email.username',
466
+ 'email.passwordRef',
467
+ 'email.smtpPasswordRef',
468
+ 'email.fromAddress',
469
+ 'email.mailbox',
470
+ 'email.draftsMailbox',
471
+ 'calendar.google.clientId',
472
+ 'calendar.google.clientSecretRef',
473
+ 'calendar.google.icsUrl',
474
+ 'calendar.microsoft.clientId',
475
+ 'calendar.microsoft.clientSecretRef',
476
+ 'google.oauth.projectId',
477
+ 'google.oauth.publishingStatus',
478
+ 'google.oauth.refreshToken',
479
+ 'google.credentials.migratedFrom',
480
+ 'daemon.connectedHost.enabled',
481
+ 'hostedSessions.routeConversationTurns',
482
+ ] as const;
483
+
441
484
  const SETTINGS_BEHAVIOR_VERIFIED = SETTINGS_BEHAVIOR_BASELINE
442
485
  + FEATURE_KNOB_LOCAL_SETTINGS.length
443
486
  + DEVICE_AND_TRIGGER_LOCAL_SETTINGS.length
444
487
  + CLUSTER_GROUP_LOCAL_SETTINGS.length
445
488
  + DAEMON_MAILBOX_LOCAL_SETTINGS.length
446
489
  + PAYMENTS_LOCAL_SETTINGS.length
490
+ + CONNECTOR_LOCAL_SETTINGS.length
447
491
  + ENABLEMENT_KEYS_BEHAVIOR_VERIFIED;
448
492
 
449
493
  const EXTERNAL_SLASH_COMMANDS = new Set([
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 = '2.0.6';
9
+ let _version = '2.0.8';
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