@pellux/goodvibes-tui 1.13.1 → 1.14.0
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 +96 -0
- package/README.md +6 -6
- package/bin/goodvibes +6 -0
- package/bin/goodvibes-daemon +6 -0
- package/bin/launcher-support.js +34 -1
- package/docs/foundation-artifacts/operator-contract.json +8076 -2047
- package/package.json +15 -11
- package/scripts/postinstall.js +73 -48
- package/src/cli/bundle-command.ts +5 -5
- package/src/cli/completion.ts +1 -1
- package/src/cli/completions/generate.ts +15 -3
- package/src/cli/doctor.ts +385 -0
- package/src/cli/entrypoint.ts +72 -1
- package/src/cli/help.ts +29 -9
- package/src/cli/hooks-command.ts +74 -0
- package/src/cli/hooks-report.ts +167 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/management.ts +13 -1
- package/src/cli/parser.ts +10 -2
- package/src/cli/plugin-bundles-command.ts +228 -0
- package/src/cli/plugin-command.ts +179 -0
- package/src/cli/service-posture.ts +10 -0
- package/src/cli/status.ts +275 -2
- package/src/cli/tui-startup.ts +119 -1
- package/src/cli/types.ts +3 -1
- package/src/config/index.ts +16 -0
- package/src/config/tui-extension-settings.ts +169 -0
- package/src/core/approval-alert.ts +15 -2
- package/src/core/compaction-receipt.ts +127 -0
- package/src/core/model-routing-chip.ts +82 -0
- package/src/core/permission-mode.ts +89 -0
- package/src/core/rewind-receipt.ts +83 -0
- package/src/core/rewind-turn-anchors.ts +160 -0
- package/src/core/scriptable-statusline.ts +141 -0
- package/src/core/session-auto-titler.ts +110 -0
- package/src/core/stream-event-wiring.ts +20 -0
- package/src/core/system-message-router.ts +1 -1
- package/src/core/terminal-notifier.ts +102 -0
- package/src/core/turn-event-wiring.ts +80 -1
- package/src/input/command-reference.ts +96 -0
- package/src/input/command-registry.ts +25 -0
- package/src/input/commands/channel-pairing.ts +245 -0
- package/src/input/commands/channel-runtime.ts +152 -6
- package/src/input/commands/checkin-runtime.ts +89 -0
- package/src/input/commands/checkpoint-runtime.ts +7 -179
- package/src/input/commands/ci-runtime.ts +207 -0
- package/src/input/commands/cost-attribution-format.ts +52 -0
- package/src/input/commands/cost-runtime.ts +44 -4
- package/src/input/commands/editor-runtime.ts +22 -0
- package/src/input/commands/flags-runtime.ts +328 -0
- package/src/input/commands/health-metrics-format.ts +67 -0
- package/src/input/commands/health-runtime.ts +55 -1
- package/src/input/commands/memory.ts +23 -0
- package/src/input/commands/operator-rpc.ts +95 -0
- package/src/input/commands/palette-runtime.ts +22 -0
- package/src/input/commands/permissions-provenance.ts +264 -0
- package/src/input/commands/permissions-runtime.ts +21 -0
- package/src/input/commands/planning-runtime.ts +28 -1
- package/src/input/commands/platform-access-runtime.ts +2 -97
- package/src/input/commands/principals-runtime.ts +212 -0
- package/src/input/commands/product-runtime.ts +43 -3
- package/src/input/commands/recall-files-config.ts +54 -0
- package/src/input/commands/recall-files-git.ts +57 -0
- package/src/input/commands/recall-files.ts +185 -0
- package/src/input/commands/recall-query.ts +8 -3
- package/src/input/commands/recall-review.ts +2 -2
- package/src/input/commands/recall-shared.ts +13 -0
- package/src/input/commands/relay-runtime.ts +86 -0
- package/src/input/commands/review-runtime.ts +160 -0
- package/src/input/commands/rewind-runtime.ts +362 -0
- package/src/input/commands/schedule-nl.ts +147 -0
- package/src/input/commands/schedule-runtime.ts +29 -10
- package/src/input/commands/secret-runtime.ts +55 -0
- package/src/input/commands/session-content.ts +19 -8
- package/src/input/commands/session-workflow.ts +10 -0
- package/src/input/commands/update-runtime.ts +363 -0
- package/src/input/commands/workstream-attempt-dependents.ts +103 -0
- package/src/input/commands/workstream-attempts.ts +244 -0
- package/src/input/commands/workstream-runtime.ts +164 -26
- package/src/input/commands/worktree-runtime.ts +56 -2
- package/src/input/commands.ts +135 -80
- package/src/input/composer-capture.ts +67 -0
- package/src/input/composer-editor.ts +134 -0
- package/src/input/concealed-input.ts +94 -0
- package/src/input/exec-env-scrub-config.ts +84 -0
- package/src/input/feature-unit-layout.ts +142 -0
- package/src/input/feed-context-factory.ts +2 -0
- package/src/input/handler-feed-routes.ts +17 -0
- package/src/input/handler-feed.ts +3 -0
- package/src/input/handler-modal-routes.ts +5 -2
- package/src/input/handler-shortcuts.ts +37 -0
- package/src/input/handler.ts +24 -2
- package/src/input/keybindings.ts +11 -2
- package/src/input/onboarding/onboarding-feature-units.ts +536 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +40 -6
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +324 -629
- package/src/input/onboarding/onboarding-wizard-steps.ts +5 -5
- package/src/input/onboarding/onboarding-wizard-types.ts +6 -0
- package/src/input/relay-settings-descriptions.ts +35 -0
- package/src/input/sandbox-exec-config.ts +107 -0
- package/src/input/settings-modal-adjustment.ts +4 -7
- package/src/input/settings-modal-data.ts +178 -8
- package/src/input/settings-modal-mutations.ts +41 -13
- package/src/input/settings-modal-types.ts +42 -5
- package/src/input/settings-modal.ts +67 -15
- package/src/input/submission-router.ts +4 -1
- package/src/input/worktree-setup-config.ts +93 -0
- package/src/main.ts +43 -43
- package/src/panels/builtin/development.ts +14 -0
- package/src/panels/diff-review-model.ts +189 -0
- package/src/panels/diff-review-panel.ts +395 -0
- package/src/panels/fleet-panel-format.ts +114 -2
- package/src/panels/fleet-panel.ts +65 -78
- package/src/panels/fleet-read-model.ts +91 -11
- package/src/panels/fleet-stop.ts +19 -4
- package/src/panels/modals/memory-modal.ts +11 -2
- package/src/permissions/hunk-selection.ts +86 -5
- package/src/permissions/prompt.ts +108 -7
- package/src/permissions/sandbox-exec-gate.ts +162 -0
- package/src/renderer/settings-modal-helpers.ts +21 -1
- package/src/renderer/settings-modal.ts +70 -44
- package/src/renderer/shell-surface.ts +23 -1
- package/src/renderer/system-message.ts +4 -0
- package/src/renderer/tool-result-summary.ts +95 -5
- package/src/renderer/ui-factory.ts +22 -0
- package/src/runtime/bootstrap-command-context.ts +15 -1
- package/src/runtime/bootstrap-command-parts.ts +14 -2
- package/src/runtime/bootstrap-core.ts +38 -38
- package/src/runtime/bootstrap-notifier-sync.ts +41 -0
- package/src/runtime/bootstrap-shell.ts +8 -0
- package/src/runtime/bootstrap.ts +21 -23
- package/src/runtime/context-accounting-source.ts +103 -0
- package/src/runtime/conversation-rewind-port.ts +130 -0
- package/src/runtime/fleet-needs-input-push.ts +70 -0
- package/src/runtime/install-self-check-startup.ts +36 -0
- package/src/runtime/install-self-check.ts +149 -0
- package/src/runtime/onboarding/fast-path.ts +93 -0
- package/src/runtime/process-lifecycle.ts +32 -24
- package/src/runtime/relay-reachability-bridge.ts +53 -0
- package/src/runtime/release-artifacts.ts +79 -0
- package/src/runtime/services.ts +33 -33
- package/src/runtime/trust/workspace-registration.ts +184 -0
- package/src/runtime/trust/workspace-trust.ts +202 -0
- package/src/runtime/ui-services.ts +2 -1
- package/src/runtime/update-check.ts +113 -0
- package/src/runtime/workspace-checkpointing.ts +32 -0
- package/src/runtime/workstream-attempts-validation.ts +77 -0
- package/src/runtime/workstream-draft-edits.ts +95 -0
- package/src/runtime/workstream-draft-store.ts +101 -0
- package/src/runtime/workstream-draft-types.ts +42 -0
- package/src/runtime/workstream-services.ts +70 -39
- package/src/shell/blocking-input.ts +29 -0
- package/src/shell/ui-openers.ts +65 -0
- package/src/verification/verification-ledger.ts +56 -2
- package/src/version.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +0 -130
- package/src/runtime/gateway-verbs.ts +0 -47
- package/src/runtime/render-scheduler.ts +0 -90
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fast-path.ts — minimum-steps first run.
|
|
3
|
+
*
|
|
4
|
+
* The full onboarding wizard is comprehensive but long (5+ steps for a brand
|
|
5
|
+
* new user). The fast path gets a new user to a working session in the fewest
|
|
6
|
+
* possible steps:
|
|
7
|
+
* - If a provider API key is already configured (env / secrets), skip
|
|
8
|
+
* onboarding entirely — write the check marker and drop straight into a
|
|
9
|
+
* working session. ZERO steps.
|
|
10
|
+
* - Otherwise ask exactly one question: start now on the free default model,
|
|
11
|
+
* or run the full guided setup. Dismissing (Esc) starts now — so it is
|
|
12
|
+
* always skippable.
|
|
13
|
+
*
|
|
14
|
+
* The full wizard remains one keystroke away (the "Full guided setup" choice,
|
|
15
|
+
* or `/onboarding` at any time). If the command context can't answer whether a
|
|
16
|
+
* provider is configured (e.g. a headless/test surface), we fall back to the
|
|
17
|
+
* classic behavior of opening the wizard, so nothing regresses.
|
|
18
|
+
*/
|
|
19
|
+
import type { CommandContext } from '../../input/command-registry.ts';
|
|
20
|
+
import type { SelectionItem } from '../../input/selection-modal.ts';
|
|
21
|
+
import { writeOnboardingCheckMarker } from './markers.ts';
|
|
22
|
+
import type { WriteOnboardingCheckMarkerOptions } from './types.ts';
|
|
23
|
+
|
|
24
|
+
type OnboardingMarkerShellPaths = Parameters<typeof writeOnboardingCheckMarker>[0];
|
|
25
|
+
|
|
26
|
+
export interface OnboardingFastPathDeps {
|
|
27
|
+
readonly input: { openOnboardingWizard: (options: { mode: 'new'; reset: boolean }) => void };
|
|
28
|
+
readonly commandContext: CommandContext;
|
|
29
|
+
readonly shellPaths: OnboardingMarkerShellPaths;
|
|
30
|
+
readonly render: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Read configured provider ids defensively — returns [] if the surface can't answer. */
|
|
34
|
+
function readConfiguredProviderIds(commandContext: CommandContext): string[] | null {
|
|
35
|
+
const registry = commandContext.provider?.providerRegistry;
|
|
36
|
+
if (!registry || typeof registry.getConfiguredProviderIds !== 'function') return null;
|
|
37
|
+
try {
|
|
38
|
+
return registry.getConfiguredProviderIds();
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const MARKER_OPTIONS: WriteOnboardingCheckMarkerOptions = { source: 'command' };
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Run the fast-path first-run flow. Returns nothing; side effects (wizard open,
|
|
48
|
+
* marker write, modal) happen through the injected deps.
|
|
49
|
+
*/
|
|
50
|
+
export function startOnboardingFastPath(deps: OnboardingFastPathDeps): void {
|
|
51
|
+
const { input, commandContext, shellPaths, render } = deps;
|
|
52
|
+
const openWizard = (): void => input.openOnboardingWizard({ mode: 'new', reset: true });
|
|
53
|
+
|
|
54
|
+
const configured = readConfiguredProviderIds(commandContext);
|
|
55
|
+
// Fall back to the classic wizard if we can't detect providers or can't prompt.
|
|
56
|
+
if (configured === null || !commandContext.openSelection) {
|
|
57
|
+
openWizard();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (configured.length > 0) {
|
|
62
|
+
// A provider key already exists — nothing to ask. Straight to a working session.
|
|
63
|
+
writeOnboardingCheckMarker(shellPaths, MARKER_OPTIONS);
|
|
64
|
+
commandContext.print?.(
|
|
65
|
+
`Ready — provider configured (${configured.join(', ')}). Run /onboarding anytime for full setup.`,
|
|
66
|
+
);
|
|
67
|
+
render();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const items: SelectionItem[] = [
|
|
72
|
+
{ id: 'start-now', label: 'Start now', detail: 'Use the free default model — no API key needed', primaryAction: 'select' },
|
|
73
|
+
{ id: 'full-setup', label: 'Full guided setup', detail: 'Choose provider, model, channels, and more', primaryAction: 'select' },
|
|
74
|
+
];
|
|
75
|
+
commandContext.openSelection(
|
|
76
|
+
'Welcome to GoodVibes — get started',
|
|
77
|
+
items,
|
|
78
|
+
{ allowSearch: false, primaryVerbLabel: 'Choose' },
|
|
79
|
+
(result) => {
|
|
80
|
+
if (result?.item.id === 'full-setup') {
|
|
81
|
+
openWizard();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Start now (or dismissed): mark first-run done and drop into a working
|
|
85
|
+
// session on the free default model.
|
|
86
|
+
writeOnboardingCheckMarker(shellPaths, MARKER_OPTIONS);
|
|
87
|
+
commandContext.print?.(
|
|
88
|
+
'You\'re on the free default model. Add a provider key anytime with /provider, or run /onboarding for full setup.',
|
|
89
|
+
);
|
|
90
|
+
render();
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* `unsubs` registry is shared by reference and drained on exit.
|
|
20
20
|
*/
|
|
21
21
|
import { logger, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
22
|
+
import { createTerminalLifecycle, TERMINAL_ESCAPES } from '@pellux/goodvibes-terminal-shell';
|
|
22
23
|
import { formatUserFacingErrorLine } from '../core/format-user-error.ts';
|
|
23
24
|
import { allowTerminalWrite } from './terminal-output-guard.ts';
|
|
24
25
|
import { buildPersistedSessionContext, deleteRecoveryFile } from '@/runtime/index.ts';
|
|
@@ -129,30 +130,37 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
|
|
|
129
130
|
render();
|
|
130
131
|
};
|
|
131
132
|
|
|
132
|
-
|
|
133
|
+
// Terminal enter/restore sequencing is the byte-identical seam both daemon
|
|
134
|
+
// front-ends share, so it lives in @pellux/goodvibes-terminal-shell and this
|
|
135
|
+
// wiring delegates to it. Only the restore path is used here (the TUI's
|
|
136
|
+
// startup enter sequence stays in main.ts); the graceful shutdown below
|
|
137
|
+
// (draining services, persisting sessions, exit codes) is TUI-specific and
|
|
138
|
+
// stays local, calling restoreTerminal() for the synchronous hand-back.
|
|
139
|
+
//
|
|
140
|
+
// The injected `ansi` restore sequences are mapped onto the package's escape
|
|
141
|
+
// set so the exact bytes the TUI has always emitted are preserved. The no-alt
|
|
142
|
+
// exit deliberately uses the package's CLEAR_VIEWPORT_HOME ('\x1b[2J\x1b[H',
|
|
143
|
+
// no ESC[3J) — never ansi.CLEAR_SCREEN, which carries the scrollback-wiping 3J.
|
|
144
|
+
const terminalLifecycle = createTerminalLifecycle({
|
|
145
|
+
write: (data) => { stdout.write(data); },
|
|
146
|
+
noAltScreen,
|
|
147
|
+
guardedWrite: allowTerminalWrite,
|
|
148
|
+
disposeOutputGuard: () => getTerminalOutputGuard().dispose(),
|
|
149
|
+
setRawMode: (enabled) => stdin.setRawMode(enabled),
|
|
150
|
+
escapes: {
|
|
151
|
+
...TERMINAL_ESCAPES,
|
|
152
|
+
ALT_SCREEN_EXIT: ansi.ALT_SCREEN_EXIT,
|
|
153
|
+
PASTE_DISABLE: ansi.PASTE_DISABLE,
|
|
154
|
+
KEYBOARD_EXT_DISABLE: ansi.KEYBOARD_EXT_DISABLE,
|
|
155
|
+
MOUSE_DISABLE: ansi.MOUSE_DISABLE,
|
|
156
|
+
CURSOR_SHOW: ansi.CURSOR_SHOW,
|
|
157
|
+
FOCUS_DISABLE: ansi.FOCUS_DISABLE,
|
|
158
|
+
},
|
|
159
|
+
});
|
|
133
160
|
// Idempotent, synchronous-only terminal restore. Safe to call from process.on('exit'),
|
|
134
|
-
// signal handlers, uncaughtException, and exitApp. Disposes the output guard
|
|
135
|
-
// crash stack reaches the real stderr instead of being suppressed
|
|
136
|
-
const restoreTerminal =
|
|
137
|
-
if (terminalRestored) return;
|
|
138
|
-
terminalRestored = true;
|
|
139
|
-
// Alt-screen path: just leave the alt screen — 1049l restores the primary
|
|
140
|
-
// screen and cursor exactly as they were at launch. Clearing first is
|
|
141
|
-
// pointless (the alt screen is discarded) and actively harmful: the old
|
|
142
|
-
// CLEAR_SCREEN included ESC[3J, which wipes the PRIMARY scrollback on
|
|
143
|
-
// several emulators even when issued from the alt screen.
|
|
144
|
-
// No-alt path: the compositor painted over the primary screen, so clear
|
|
145
|
-
// the viewport and home the cursor — but WITHOUT 3J, the user's scrollback
|
|
146
|
-
// is theirs. CURSOR_SHOW goes AFTER the screen switch so visibility
|
|
147
|
-
// applies to the screen the shell prompt lands on.
|
|
148
|
-
const exitScreen = noAltScreen ? '\x1b[2J\x1b[H' : ansi.ALT_SCREEN_EXIT;
|
|
149
|
-
allowTerminalWrite(() => stdout.write(
|
|
150
|
-
ansi.PASTE_DISABLE + ansi.KEYBOARD_EXT_DISABLE + ansi.MOUSE_DISABLE + ansi.FOCUS_DISABLE
|
|
151
|
-
+ exitScreen + ansi.CURSOR_SHOW,
|
|
152
|
-
));
|
|
153
|
-
getTerminalOutputGuard().dispose();
|
|
154
|
-
try { stdin.setRawMode(false); } catch { /* stdin may not be a TTY */ }
|
|
155
|
-
};
|
|
161
|
+
// signal handlers, uncaughtException, and exitApp. Disposes the output guard AFTER the
|
|
162
|
+
// restore write so a crash stack reaches the real stderr instead of being suppressed.
|
|
163
|
+
const restoreTerminal = terminalLifecycle.restoreTerminal;
|
|
156
164
|
|
|
157
165
|
const uncaughtExceptionHandler = (err: Error): void => {
|
|
158
166
|
restoreTerminal();
|
|
@@ -220,7 +228,7 @@ export function installProcessLifecycle(deps: ProcessLifecycleDeps): ProcessLife
|
|
|
220
228
|
return {
|
|
221
229
|
exitApp,
|
|
222
230
|
restoreTerminal,
|
|
223
|
-
isTerminalRestored:
|
|
231
|
+
isTerminalRestored: terminalLifecycle.isTerminalRestored,
|
|
224
232
|
resizeHandler,
|
|
225
233
|
sigintHandler,
|
|
226
234
|
unhandledRejectionHandler,
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* relay-reachability-bridge.ts — reads the LIVE outbound-relay reachability
|
|
3
|
+
* state off the running embedded/standalone DaemonServer.
|
|
4
|
+
*
|
|
5
|
+
* The SDK's `DaemonServer` composes and owns a `RelayReachability` controller
|
|
6
|
+
* internally (gated by `relay.enabled` + the `relay-connect` feature flag) and
|
|
7
|
+
* exposes it via `getRelayReachability(): RelayReachability | null` — but that
|
|
8
|
+
* accessor is not on the narrow `DaemonService` interface `HostServicesHandle`
|
|
9
|
+
* types its `daemonServer` field with (see
|
|
10
|
+
* `@pellux/goodvibes-sdk/platform/runtime/bootstrap-services`), so the field
|
|
11
|
+
* needs a defensive structural check before use — same pattern
|
|
12
|
+
* `waitForConfigDrivenRestarts` in bootstrap.ts already uses for
|
|
13
|
+
* `waitForRestart`. `platform/relay` itself has no public export subpath
|
|
14
|
+
* (daemon-internal), so the RelayReachability shape is described locally as a
|
|
15
|
+
* minimal structural type rather than imported by name.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { RelayRegistrationStatus } from '@pellux/goodvibes-daemon-sdk';
|
|
19
|
+
import { encodeRelayPairingString, type RelayPairingPayload } from '@pellux/goodvibes-transport-core/relay';
|
|
20
|
+
|
|
21
|
+
/** Structural subset of the SDK's `RelayReachability` this bridge needs. */
|
|
22
|
+
interface RelayReachabilityLike {
|
|
23
|
+
readonly status: RelayRegistrationStatus | 'disabled';
|
|
24
|
+
mintPairing(): Promise<RelayPairingPayload | null>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Structural subset of a live daemon that may expose relay reachability. */
|
|
28
|
+
interface RelayCapableDaemonService {
|
|
29
|
+
getRelayReachability?(): RelayReachabilityLike | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getRelay(daemonServer: unknown): RelayReachabilityLike | null {
|
|
33
|
+
const candidate = daemonServer as RelayCapableDaemonService | null;
|
|
34
|
+
if (!candidate || typeof candidate.getRelayReachability !== 'function') return null;
|
|
35
|
+
return candidate.getRelayReachability() ?? null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Two live-relay accessors, spread into `externalServices` from bootstrap.ts. */
|
|
39
|
+
export function buildRelayExternalServiceMethods(getDaemonServer: () => unknown): {
|
|
40
|
+
relayStatus(): RelayRegistrationStatus | 'disabled';
|
|
41
|
+
mintRelayPairing(): Promise<{ readonly payload: RelayPairingPayload; readonly encoded: string } | null>;
|
|
42
|
+
} {
|
|
43
|
+
return {
|
|
44
|
+
relayStatus: () => getRelay(getDaemonServer())?.status ?? 'disabled',
|
|
45
|
+
mintRelayPairing: async () => {
|
|
46
|
+
const relay = getRelay(getDaemonServer());
|
|
47
|
+
if (!relay) return null;
|
|
48
|
+
const payload = await relay.mintPairing();
|
|
49
|
+
if (!payload) return null;
|
|
50
|
+
return { payload, encoded: encodeRelayPairingString(payload) };
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared release-artifact naming and checksum-verification logic.
|
|
3
|
+
*
|
|
4
|
+
* This is the one place that knows the release asset naming convention
|
|
5
|
+
* (`goodvibes[-daemon]-{platform}-{arch}`) and how to verify a downloaded
|
|
6
|
+
* artifact against SHA256SUMS.txt. Two callers share it and must stay in
|
|
7
|
+
* lockstep:
|
|
8
|
+
* - scripts/postinstall.js (npm install-time binary download)
|
|
9
|
+
* - src/input/commands/update-runtime.ts (the /update command's self-update)
|
|
10
|
+
*
|
|
11
|
+
* A downloaded artifact with NO entry in the checksum manifest is just as
|
|
12
|
+
* unverified as one with a mismatching entry — verifyChecksum() throws in
|
|
13
|
+
* both cases, never silently skips.
|
|
14
|
+
*/
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
16
|
+
|
|
17
|
+
export const CHECKSUM_MANIFEST_NAME = 'SHA256SUMS.txt';
|
|
18
|
+
|
|
19
|
+
export type SupportedPlatform = 'linux' | 'darwin';
|
|
20
|
+
export type SupportedArch = 'x64' | 'arm64';
|
|
21
|
+
|
|
22
|
+
export interface ReleaseArtifactNames {
|
|
23
|
+
readonly app: string;
|
|
24
|
+
readonly daemon: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Release-asset platform tag as used in artifact filenames ("linux" | "macos"). */
|
|
28
|
+
const PLATFORM_TAGS: Record<SupportedPlatform, string> = {
|
|
29
|
+
linux: 'linux',
|
|
30
|
+
darwin: 'macos',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function resolveArtifactNames(platform: string, arch: string): ReleaseArtifactNames | null {
|
|
34
|
+
const platformTag = PLATFORM_TAGS[platform as SupportedPlatform];
|
|
35
|
+
if (!platformTag || (arch !== 'x64' && arch !== 'arm64')) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const suffix = `${platformTag}-${arch}`;
|
|
39
|
+
return {
|
|
40
|
+
app: `goodvibes-${suffix}`,
|
|
41
|
+
daemon: `goodvibes-daemon-${suffix}`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function sha256(buffer: Buffer | Uint8Array): string {
|
|
46
|
+
return createHash('sha256').update(buffer).digest('hex');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function parseChecksumFile(contents: string): Map<string, string> {
|
|
50
|
+
const checksums = new Map<string, string>();
|
|
51
|
+
for (const rawLine of contents.split(/\r?\n/)) {
|
|
52
|
+
const line = rawLine.trim();
|
|
53
|
+
if (!line) continue;
|
|
54
|
+
const match = line.match(/^([a-f0-9]{64})\s+\*?(.+)$/i);
|
|
55
|
+
if (!match) continue;
|
|
56
|
+
checksums.set(match[2], match[1].toLowerCase());
|
|
57
|
+
}
|
|
58
|
+
return checksums;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Verify a downloaded artifact's checksum against the parsed manifest.
|
|
63
|
+
* An artifact with no entry in the manifest is a hard failure, identical
|
|
64
|
+
* in severity to a mismatching entry — never treated as "unverifiable, so
|
|
65
|
+
* skip the check". Throws naming the artifact and the manifest.
|
|
66
|
+
*/
|
|
67
|
+
export function verifyChecksum(
|
|
68
|
+
artifactName: string,
|
|
69
|
+
actual: string,
|
|
70
|
+
expected: string | undefined,
|
|
71
|
+
manifestName: string = CHECKSUM_MANIFEST_NAME,
|
|
72
|
+
): void {
|
|
73
|
+
if (expected === undefined) {
|
|
74
|
+
throw new Error(`no checksum entry for ${artifactName} in ${manifestName} — refusing to install an unverified binary`);
|
|
75
|
+
}
|
|
76
|
+
if (expected !== actual) {
|
|
77
|
+
throw new Error(`checksum mismatch for ${artifactName}: expected ${expected}, got ${actual}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/runtime/services.ts
CHANGED
|
@@ -5,7 +5,8 @@ import { SecretsManager } from '../config/secrets.ts';
|
|
|
5
5
|
import { AutomationDeliveryManager, AutomationManager, AutomationRouteStore } from '@pellux/goodvibes-sdk/platform/automation';
|
|
6
6
|
import { ChannelDeliveryRouter, ChannelPluginRegistry, ChannelPolicyManager, RouteBindingManager, SurfaceRegistry } from '@pellux/goodvibes-sdk/platform/channels';
|
|
7
7
|
import { ApprovalBroker, GatewayMethodCatalog, SharedSessionBroker } from '@pellux/goodvibes-sdk/platform/control-plane';
|
|
8
|
-
import {
|
|
8
|
+
import { StepUpService } from '@pellux/goodvibes-sdk/daemon';
|
|
9
|
+
import { attachWsOnlyGatewayVerbHandlers, createArchivableFleetRegistry } from '@pellux/goodvibes-terminal-shell';
|
|
9
10
|
import { WatcherRegistry } from '@pellux/goodvibes-sdk/platform/watchers';
|
|
10
11
|
import { ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
|
|
11
12
|
import {
|
|
@@ -23,16 +24,19 @@ import {
|
|
|
23
24
|
import { MediaProviderRegistry, ensureBuiltinMediaProviders } from '@pellux/goodvibes-sdk/platform/media';
|
|
24
25
|
import { MultimodalService } from '@pellux/goodvibes-sdk/platform/multimodal';
|
|
25
26
|
import { AgentMessageBus, AgentOrchestrator, ArchetypeLoader, WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
|
|
26
|
-
import { AgentManager, OverflowHandler, ProcessManager, createWorkflowServices, type WorkflowServices } from '@pellux/goodvibes-sdk/platform/tools';
|
|
27
|
+
import { AgentManager, ContextAccountingHolder, OverflowHandler, ProcessManager, createWorkflowServices, type WorkflowServices } from '@pellux/goodvibes-sdk/platform/tools';
|
|
27
28
|
import { FileStateCache, FileUndoManager, MemoryEmbeddingProviderRegistry, MemoryRegistry, MemoryStore, ModeManager, ProjectIndex, resolveCanonicalMemoryDbPath, type CodeIndexStore, type CodeIndexReindexScheduler } from '@pellux/goodvibes-sdk/platform/state';
|
|
28
29
|
import { MemorySpineClient, createLocalMemoryAccess } from '@pellux/goodvibes-sdk/platform/runtime/memory-spine';
|
|
29
30
|
import { WorkspaceCheckpointManager } from '@pellux/goodvibes-sdk/platform/workspace';
|
|
30
|
-
import
|
|
31
|
+
import { createWorkspaceCheckpointing } from './workspace-checkpointing.ts';
|
|
32
|
+
import { createSessionConversationRewindPort } from './conversation-rewind-port.ts';
|
|
31
33
|
import { createDomainDispatch } from './store/index.ts';
|
|
32
34
|
import type { DomainDispatch, RuntimeStore } from './store/index.ts';
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
import {
|
|
36
|
+
type RuntimeEventBus, DistributedRuntimeManager, RemoteRunnerRegistry, RemoteSupervisor, IntegrationHelperService,
|
|
37
|
+
IdempotencyStore, ComponentHealthMonitor, WorktreeRegistry, SandboxSessionRegistry, createShellPathService,
|
|
38
|
+
type ShellPathService, type FeatureFlagManager, createFeatureFlagManager, PolicyRuntimeState,
|
|
39
|
+
} from '@/runtime/index.ts';
|
|
36
40
|
import { VoiceProviderRegistry, VoiceService, ensureBuiltinVoiceProviders } from '@pellux/goodvibes-sdk/platform/voice';
|
|
37
41
|
import { WebSearchProviderRegistry, WebSearchService } from '@pellux/goodvibes-sdk/platform/web-search';
|
|
38
42
|
import { PanelManager } from '../panels/panel-manager.ts';
|
|
@@ -48,18 +52,11 @@ import { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
|
|
|
48
52
|
import { BenchmarkStore, CacheHitTracker, FavoritesStore, inferFallbackContextWindow, type ModelDefinition, ModelLimitsService, ProviderCapabilityRegistry, ProviderOptimizer, ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
49
53
|
import { KeybindingsManager } from '../input/keybindings.ts';
|
|
50
54
|
import { AdaptivePlanner, DeterministicReplayEngine, ExecutionPlanManager, SessionLineageTracker, SessionMemoryStore } from '@pellux/goodvibes-sdk/platform/core';
|
|
51
|
-
import { IdempotencyStore } from '@/runtime/index.ts';
|
|
52
|
-
import { ComponentHealthMonitor } from '@/runtime/index.ts';
|
|
53
|
-
import { WorktreeRegistry } from '@/runtime/index.ts';
|
|
54
|
-
import { SandboxSessionRegistry } from '@/runtime/index.ts';
|
|
55
|
-
import { createShellPathService, type ShellPathService } from '@/runtime/index.ts';
|
|
56
55
|
import { isFeatureFlagEnabled } from './surface-feature-flags.ts';
|
|
57
|
-
import type { FeatureFlagManager } from '@/runtime/index.ts';
|
|
58
|
-
import { createFeatureFlagManager } from '@/runtime/index.ts';
|
|
59
|
-
import { PolicyRuntimeState } from '@/runtime/index.ts';
|
|
60
56
|
import { type ArchivableProcessRegistry } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
61
57
|
import { calcSessionCost, isModelPriced } from '../export/cost-utils.ts';
|
|
62
58
|
import { createWorkstreamServices, type OrchestrationEngine, type WorkstreamCommandService } from './workstream-services.ts';
|
|
59
|
+
import { wireFleetNeedsInputPush } from './fleet-needs-input-push.ts';
|
|
63
60
|
import { codeIndexDbPath, createCodeIndexServices, isCodeInjectionSettingEnabled } from './code-index-services.ts';
|
|
64
61
|
import { WorkPlanStore } from '../work-plans/work-plan-store.ts';
|
|
65
62
|
import {
|
|
@@ -75,6 +72,7 @@ import { registerDraftMethods } from '../daemon/handlers/drafts/index.ts';
|
|
|
75
72
|
import { registerCalendar } from '../daemon/handlers/calendar/index.ts';
|
|
76
73
|
import { registerEmailMethods } from '../daemon/handlers/email/index.ts';
|
|
77
74
|
import { registerRemoteSurface } from '../daemon/handlers/remote/index.ts';
|
|
75
|
+
import { WorkspaceTrustManager } from './trust/workspace-trust.ts';
|
|
78
76
|
|
|
79
77
|
const REGULAR_KNOWLEDGE_DB_FILE = 'knowledge-wiki.sqlite';
|
|
80
78
|
const HOME_GRAPH_KNOWLEDGE_DB_FILE = 'knowledge-home-graph.sqlite';
|
|
@@ -136,6 +134,8 @@ export interface RuntimeServicesOptions {
|
|
|
136
134
|
readonly getConversationTitle?: () => string | undefined;
|
|
137
135
|
readonly workingDir: string;
|
|
138
136
|
readonly homeDirectory: string;
|
|
137
|
+
/** Prior GoodVibes runtime state existed at startup — sampled before any write; seeds the trust gate's grandfathering. */
|
|
138
|
+
readonly workspaceHadPriorState?: boolean;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
export interface RuntimeServices {
|
|
@@ -172,6 +172,7 @@ export interface RuntimeServices {
|
|
|
172
172
|
readonly memorySpine: MemorySpineClient;
|
|
173
173
|
readonly serviceRegistry: ServiceRegistry;
|
|
174
174
|
readonly secretsManager: SecretsManager;
|
|
175
|
+
readonly stepUpService: StepUpService;
|
|
175
176
|
readonly subscriptionManager: SubscriptionManager;
|
|
176
177
|
readonly localUserAuthManager: UserAuthManager;
|
|
177
178
|
readonly profileManager: ProfileManager;
|
|
@@ -224,6 +225,7 @@ export interface RuntimeServices {
|
|
|
224
225
|
readonly agentManager: AgentManager;
|
|
225
226
|
readonly agentMessageBus: AgentMessageBus;
|
|
226
227
|
readonly agentOrchestrator: AgentOrchestrator;
|
|
228
|
+
readonly contextAccountingHolder: ContextAccountingHolder; // bound at bootstrap.ts; see context-accounting-source.ts
|
|
227
229
|
readonly wrfcController: WrfcController;
|
|
228
230
|
readonly processManager: ProcessManager;
|
|
229
231
|
/** The phase/work-item orchestration engine — see runtime/workstream-services.ts. */
|
|
@@ -237,6 +239,8 @@ export interface RuntimeServices {
|
|
|
237
239
|
readonly modeManager: ModeManager;
|
|
238
240
|
readonly fileUndoManager: FileUndoManager;
|
|
239
241
|
readonly workspaceCheckpointManager: WorkspaceCheckpointManager;
|
|
242
|
+
/** Per-workspace trust gate — restricts write/execute/delegate tools until the workspace is trusted. */
|
|
243
|
+
readonly workspaceTrustManager: WorkspaceTrustManager;
|
|
240
244
|
readonly integrationHelpers: IntegrationHelperService;
|
|
241
245
|
/** Re-root path-bound stores (MemoryStore, ProjectIndex) to a new working directory, called by WorkspaceSwapManager after verification; stores needing a process restart just warn-log and keep serving the old path until the daemon restarts with the new --working-dir. */
|
|
242
246
|
rerootStores(newWorkingDir: string): Promise<void>;
|
|
@@ -249,6 +253,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
249
253
|
workingDirectory,
|
|
250
254
|
homeDirectory,
|
|
251
255
|
});
|
|
256
|
+
const workspaceTrustManager = new WorkspaceTrustManager({ shellPaths, hadPriorState: options.workspaceHadPriorState ?? false });
|
|
252
257
|
const configManager = options.configManager;
|
|
253
258
|
const featureFlags = options.featureFlags ?? createFeatureFlagManager();
|
|
254
259
|
const runtimeDispatch = createDomainDispatch(options.runtimeStore);
|
|
@@ -273,9 +278,9 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
273
278
|
globalHome: homeDirectory,
|
|
274
279
|
configManager,
|
|
275
280
|
});
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
);
|
|
281
|
+
// Step-up (WebAuthn) ceremony service, shared between the ceremony gateway verbs and the relay gate's verifier.
|
|
282
|
+
const stepUpService = new StepUpService({ secrets: secretsManager });
|
|
283
|
+
const subscriptionManager = new SubscriptionManager(shellPaths.resolveUserPath('tui', 'subscriptions.json'));
|
|
279
284
|
const serviceRegistry = new ServiceRegistry(shellPaths.resolveProjectPath('tui', 'services.json'), {
|
|
280
285
|
secretsManager,
|
|
281
286
|
subscriptionManager,
|
|
@@ -337,6 +342,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
337
342
|
executor: agentOrchestrator,
|
|
338
343
|
configManager,
|
|
339
344
|
});
|
|
345
|
+
const contextAccountingHolder = new ContextAccountingHolder();
|
|
340
346
|
agentOrchestrator.setConversationSink({ // Conversation-snapshot bridge (mirrors the SDK's own createRuntimeServices)
|
|
341
347
|
register: (agentId, source) => agentManager.registerConversationSource(agentId, source),
|
|
342
348
|
release: (agentId) => agentManager.releaseConversationSource(agentId),
|
|
@@ -629,23 +635,13 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
629
635
|
});
|
|
630
636
|
const modeManager = new ModeManager();
|
|
631
637
|
const fileUndoManager = new FileUndoManager();
|
|
632
|
-
const workspaceCheckpointManager =
|
|
633
|
-
workspaceRoot: workingDirectory,
|
|
634
|
-
runtimeBus: options.runtimeBus,
|
|
635
|
-
});
|
|
636
|
-
// Fire-and-forget: subscriptions go live immediately if init() succeeds.
|
|
637
|
-
// If it rejects, WorkspaceCheckpointManager caches that rejection on
|
|
638
|
-
// `initPromise` and never clears it, so every later call (create/list/
|
|
639
|
-
// diff/restore — each awaits init() first) re-throws the same error
|
|
640
|
-
// forever: checkpointing becomes entirely unavailable for the rest of the
|
|
641
|
-
// session, not "degraded to manual-only". The `.catch(() => {})` here only
|
|
642
|
-
// exists to prevent an unhandled rejection at startup; the checkpoint
|
|
643
|
-
// commands (checkpoint-runtime.ts) are what actually catch and report the
|
|
644
|
-
// failure to the user, on first use.
|
|
645
|
-
void workspaceCheckpointManager.init().catch(() => {});
|
|
638
|
+
const workspaceCheckpointManager = createWorkspaceCheckpointing({ workspaceRoot: workingDirectory, runtimeBus: options.runtimeBus, configManager });
|
|
646
639
|
|
|
647
|
-
//
|
|
648
|
-
|
|
640
|
+
// Shared terminal-shell wrapper over the SDK's registerGatewayVerbGroups (see gateway-verbs.ts): 501s the ws-only
|
|
641
|
+
// family without it. principals.*/channels.profiles.*/ci.* register unconditionally; checkin.* only with
|
|
642
|
+
// channelDeliveryRouter/providerRegistry/automationManager/sessionLister ALL present; fleet needs-input push gated by
|
|
643
|
+
// runtimeBus/sessionPresence (fleet-needs-input-push.ts). conversationRewindPort serves conversation-scope rewind live.
|
|
644
|
+
attachWsOnlyGatewayVerbHandlers(gatewayMethods, { processRegistry, workspaceCheckpointManager, conversationRewindPort: createSessionConversationRewindPort(), sessionBroker, secretsManager, stepUpService, approvalBroker, shellPaths, configManager, runtimeStore: options.runtimeStore, channelDeliveryRouter, providerRegistry, automationManager, sessionLister: sessionBroker, ...wireFleetNeedsInputPush({ registry: processRegistry, runtimeBus: options.runtimeBus, sessionBroker }) });
|
|
649
645
|
const integrationHelpers = new IntegrationHelperService({
|
|
650
646
|
workingDirectory,
|
|
651
647
|
homeDirectory,
|
|
@@ -692,12 +688,14 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
692
688
|
overflowHandler,
|
|
693
689
|
sandboxSessionRegistry,
|
|
694
690
|
workflowServices: workflow,
|
|
691
|
+
contextAccountingHolder,
|
|
695
692
|
});
|
|
696
693
|
|
|
697
694
|
return {
|
|
698
695
|
workingDirectory,
|
|
699
696
|
homeDirectory,
|
|
700
697
|
shellPaths,
|
|
698
|
+
workspaceTrustManager,
|
|
701
699
|
configManager,
|
|
702
700
|
featureFlags,
|
|
703
701
|
runtimeBus: options.runtimeBus,
|
|
@@ -727,6 +725,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
727
725
|
memorySpine,
|
|
728
726
|
serviceRegistry,
|
|
729
727
|
secretsManager,
|
|
728
|
+
stepUpService,
|
|
730
729
|
subscriptionManager,
|
|
731
730
|
localUserAuthManager,
|
|
732
731
|
profileManager,
|
|
@@ -778,6 +777,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
778
777
|
agentManager,
|
|
779
778
|
agentMessageBus,
|
|
780
779
|
agentOrchestrator,
|
|
780
|
+
contextAccountingHolder,
|
|
781
781
|
wrfcController,
|
|
782
782
|
processManager,
|
|
783
783
|
orchestrationEngine,
|