@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 +44 -0
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +31 -18
- package/package.json +3 -3
- package/src/input/commands/profile-types.ts +3 -1
- package/src/input/settings-modal-types.ts +14 -1
- package/src/renderer/settings-modal-helpers.ts +6 -0
- package/src/runtime/runtime-services-types.ts +2 -0
- package/src/runtime/services.ts +1 -0
- package/src/verification/verification-ledger.ts +44 -0
- package/src/version.ts +1 -1
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
|
[](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](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.
|
|
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
|
-
"
|
|
79409
|
-
|
|
79410
|
-
|
|
79411
|
-
"
|
|
79412
|
-
|
|
79413
|
-
|
|
79414
|
-
|
|
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
|
-
|
|
79417
|
-
"type": "
|
|
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.
|
|
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.
|
|
99
|
-
"@pellux/goodvibes-terminal-shell": "2.0.
|
|
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
|
-
|
|
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
|
-
|
|
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;
|
package/src/runtime/services.ts
CHANGED
|
@@ -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.
|
|
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
|