@pellux/goodvibes-tui 1.13.0 → 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 +101 -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 +38 -37
- 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/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,6 +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 { StepUpService } from '@pellux/goodvibes-sdk/daemon';
|
|
9
|
+
import { attachWsOnlyGatewayVerbHandlers, createArchivableFleetRegistry } from '@pellux/goodvibes-terminal-shell';
|
|
8
10
|
import { WatcherRegistry } from '@pellux/goodvibes-sdk/platform/watchers';
|
|
9
11
|
import { ArtifactStore } from '@pellux/goodvibes-sdk/platform/artifacts';
|
|
10
12
|
import {
|
|
@@ -22,16 +24,19 @@ import {
|
|
|
22
24
|
import { MediaProviderRegistry, ensureBuiltinMediaProviders } from '@pellux/goodvibes-sdk/platform/media';
|
|
23
25
|
import { MultimodalService } from '@pellux/goodvibes-sdk/platform/multimodal';
|
|
24
26
|
import { AgentMessageBus, AgentOrchestrator, ArchetypeLoader, WrfcController } from '@pellux/goodvibes-sdk/platform/agents';
|
|
25
|
-
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';
|
|
26
28
|
import { FileStateCache, FileUndoManager, MemoryEmbeddingProviderRegistry, MemoryRegistry, MemoryStore, ModeManager, ProjectIndex, resolveCanonicalMemoryDbPath, type CodeIndexStore, type CodeIndexReindexScheduler } from '@pellux/goodvibes-sdk/platform/state';
|
|
27
29
|
import { MemorySpineClient, createLocalMemoryAccess } from '@pellux/goodvibes-sdk/platform/runtime/memory-spine';
|
|
28
30
|
import { WorkspaceCheckpointManager } from '@pellux/goodvibes-sdk/platform/workspace';
|
|
29
|
-
import
|
|
31
|
+
import { createWorkspaceCheckpointing } from './workspace-checkpointing.ts';
|
|
32
|
+
import { createSessionConversationRewindPort } from './conversation-rewind-port.ts';
|
|
30
33
|
import { createDomainDispatch } from './store/index.ts';
|
|
31
34
|
import type { DomainDispatch, RuntimeStore } from './store/index.ts';
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
|
|
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';
|
|
35
40
|
import { VoiceProviderRegistry, VoiceService, ensureBuiltinVoiceProviders } from '@pellux/goodvibes-sdk/platform/voice';
|
|
36
41
|
import { WebSearchProviderRegistry, WebSearchService } from '@pellux/goodvibes-sdk/platform/web-search';
|
|
37
42
|
import { PanelManager } from '../panels/panel-manager.ts';
|
|
@@ -47,18 +52,11 @@ import { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
|
|
|
47
52
|
import { BenchmarkStore, CacheHitTracker, FavoritesStore, inferFallbackContextWindow, type ModelDefinition, ModelLimitsService, ProviderCapabilityRegistry, ProviderOptimizer, ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
48
53
|
import { KeybindingsManager } from '../input/keybindings.ts';
|
|
49
54
|
import { AdaptivePlanner, DeterministicReplayEngine, ExecutionPlanManager, SessionLineageTracker, SessionMemoryStore } from '@pellux/goodvibes-sdk/platform/core';
|
|
50
|
-
import { IdempotencyStore } from '@/runtime/index.ts';
|
|
51
|
-
import { ComponentHealthMonitor } from '@/runtime/index.ts';
|
|
52
|
-
import { WorktreeRegistry } from '@/runtime/index.ts';
|
|
53
|
-
import { SandboxSessionRegistry } from '@/runtime/index.ts';
|
|
54
|
-
import { createShellPathService, type ShellPathService } from '@/runtime/index.ts';
|
|
55
55
|
import { isFeatureFlagEnabled } from './surface-feature-flags.ts';
|
|
56
|
-
import type
|
|
57
|
-
import { createFeatureFlagManager } from '@/runtime/index.ts';
|
|
58
|
-
import { PolicyRuntimeState } from '@/runtime/index.ts';
|
|
59
|
-
import { createProcessRegistry, withFleetArchive, type ArchivableProcessRegistry } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
56
|
+
import { type ArchivableProcessRegistry } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
60
57
|
import { calcSessionCost, isModelPriced } from '../export/cost-utils.ts';
|
|
61
58
|
import { createWorkstreamServices, type OrchestrationEngine, type WorkstreamCommandService } from './workstream-services.ts';
|
|
59
|
+
import { wireFleetNeedsInputPush } from './fleet-needs-input-push.ts';
|
|
62
60
|
import { codeIndexDbPath, createCodeIndexServices, isCodeInjectionSettingEnabled } from './code-index-services.ts';
|
|
63
61
|
import { WorkPlanStore } from '../work-plans/work-plan-store.ts';
|
|
64
62
|
import {
|
|
@@ -74,6 +72,7 @@ import { registerDraftMethods } from '../daemon/handlers/drafts/index.ts';
|
|
|
74
72
|
import { registerCalendar } from '../daemon/handlers/calendar/index.ts';
|
|
75
73
|
import { registerEmailMethods } from '../daemon/handlers/email/index.ts';
|
|
76
74
|
import { registerRemoteSurface } from '../daemon/handlers/remote/index.ts';
|
|
75
|
+
import { WorkspaceTrustManager } from './trust/workspace-trust.ts';
|
|
77
76
|
|
|
78
77
|
const REGULAR_KNOWLEDGE_DB_FILE = 'knowledge-wiki.sqlite';
|
|
79
78
|
const HOME_GRAPH_KNOWLEDGE_DB_FILE = 'knowledge-home-graph.sqlite';
|
|
@@ -135,6 +134,8 @@ export interface RuntimeServicesOptions {
|
|
|
135
134
|
readonly getConversationTitle?: () => string | undefined;
|
|
136
135
|
readonly workingDir: string;
|
|
137
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;
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
export interface RuntimeServices {
|
|
@@ -171,6 +172,7 @@ export interface RuntimeServices {
|
|
|
171
172
|
readonly memorySpine: MemorySpineClient;
|
|
172
173
|
readonly serviceRegistry: ServiceRegistry;
|
|
173
174
|
readonly secretsManager: SecretsManager;
|
|
175
|
+
readonly stepUpService: StepUpService;
|
|
174
176
|
readonly subscriptionManager: SubscriptionManager;
|
|
175
177
|
readonly localUserAuthManager: UserAuthManager;
|
|
176
178
|
readonly profileManager: ProfileManager;
|
|
@@ -223,6 +225,7 @@ export interface RuntimeServices {
|
|
|
223
225
|
readonly agentManager: AgentManager;
|
|
224
226
|
readonly agentMessageBus: AgentMessageBus;
|
|
225
227
|
readonly agentOrchestrator: AgentOrchestrator;
|
|
228
|
+
readonly contextAccountingHolder: ContextAccountingHolder; // bound at bootstrap.ts; see context-accounting-source.ts
|
|
226
229
|
readonly wrfcController: WrfcController;
|
|
227
230
|
readonly processManager: ProcessManager;
|
|
228
231
|
/** The phase/work-item orchestration engine — see runtime/workstream-services.ts. */
|
|
@@ -236,6 +239,8 @@ export interface RuntimeServices {
|
|
|
236
239
|
readonly modeManager: ModeManager;
|
|
237
240
|
readonly fileUndoManager: FileUndoManager;
|
|
238
241
|
readonly workspaceCheckpointManager: WorkspaceCheckpointManager;
|
|
242
|
+
/** Per-workspace trust gate — restricts write/execute/delegate tools until the workspace is trusted. */
|
|
243
|
+
readonly workspaceTrustManager: WorkspaceTrustManager;
|
|
239
244
|
readonly integrationHelpers: IntegrationHelperService;
|
|
240
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. */
|
|
241
246
|
rerootStores(newWorkingDir: string): Promise<void>;
|
|
@@ -248,6 +253,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
248
253
|
workingDirectory,
|
|
249
254
|
homeDirectory,
|
|
250
255
|
});
|
|
256
|
+
const workspaceTrustManager = new WorkspaceTrustManager({ shellPaths, hadPriorState: options.workspaceHadPriorState ?? false });
|
|
251
257
|
const configManager = options.configManager;
|
|
252
258
|
const featureFlags = options.featureFlags ?? createFeatureFlagManager();
|
|
253
259
|
const runtimeDispatch = createDomainDispatch(options.runtimeStore);
|
|
@@ -272,9 +278,9 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
272
278
|
globalHome: homeDirectory,
|
|
273
279
|
configManager,
|
|
274
280
|
});
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
);
|
|
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'));
|
|
278
284
|
const serviceRegistry = new ServiceRegistry(shellPaths.resolveProjectPath('tui', 'services.json'), {
|
|
279
285
|
secretsManager,
|
|
280
286
|
subscriptionManager,
|
|
@@ -336,6 +342,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
336
342
|
executor: agentOrchestrator,
|
|
337
343
|
configManager,
|
|
338
344
|
});
|
|
345
|
+
const contextAccountingHolder = new ContextAccountingHolder();
|
|
339
346
|
agentOrchestrator.setConversationSink({ // Conversation-snapshot bridge (mirrors the SDK's own createRuntimeServices)
|
|
340
347
|
register: (agentId, source) => agentManager.registerConversationSource(agentId, source),
|
|
341
348
|
release: (agentId) => agentManager.releaseConversationSource(agentId),
|
|
@@ -605,11 +612,8 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
605
612
|
// code-index-services.ts's header doc.
|
|
606
613
|
const { codeIndexStore, codeIndexReindexScheduler } = createCodeIndexServices({ workingDirectory, configManager, memoryEmbeddingRegistry });
|
|
607
614
|
const codeInjectionOrchestratorDeps = { codeIndex: codeIndexStore, isCodeInjectionSettingEnabled: () => isCodeInjectionSettingEnabled(configManager), codeIndexReindexScheduler }; // Code-injection seam (agent here; main via orchestrator-core-services.ts)
|
|
608
|
-
//
|
|
609
|
-
|
|
610
|
-
// Constructed once here (not per-consumer) so the coalesced tick and the
|
|
611
|
-
// agent-activity side-table are shared, not duplicated.
|
|
612
|
-
const processRegistry = withFleetArchive(createProcessRegistry({
|
|
615
|
+
// Shared, archive-aware fleet registry — see gateway-verbs.ts's factory doc.
|
|
616
|
+
const processRegistry = createArchivableFleetRegistry({
|
|
613
617
|
agentManager,
|
|
614
618
|
wrfcController,
|
|
615
619
|
orchestrationEngine, // Folds workstream/phase/work-item nodes into the fleet
|
|
@@ -628,23 +632,16 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
628
632
|
if (!isModelPriced(modelId)) return null;
|
|
629
633
|
return calcSessionCost(usage.inputTokens, usage.outputTokens, usage.cacheReadTokens, usage.cacheWriteTokens, modelId);
|
|
630
634
|
},
|
|
631
|
-
})
|
|
635
|
+
});
|
|
632
636
|
const modeManager = new ModeManager();
|
|
633
637
|
const fileUndoManager = new FileUndoManager();
|
|
634
|
-
const workspaceCheckpointManager =
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
//
|
|
639
|
-
//
|
|
640
|
-
|
|
641
|
-
// diff/restore — each awaits init() first) re-throws the same error
|
|
642
|
-
// forever: checkpointing becomes entirely unavailable for the rest of the
|
|
643
|
-
// session, not "degraded to manual-only". The `.catch(() => {})` here only
|
|
644
|
-
// exists to prevent an unhandled rejection at startup; the checkpoint
|
|
645
|
-
// commands (checkpoint-runtime.ts) are what actually catch and report the
|
|
646
|
-
// failure to the user, on first use.
|
|
647
|
-
void workspaceCheckpointManager.init().catch(() => {});
|
|
638
|
+
const workspaceCheckpointManager = createWorkspaceCheckpointing({ workspaceRoot: workingDirectory, runtimeBus: options.runtimeBus, configManager });
|
|
639
|
+
|
|
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 }) });
|
|
648
645
|
const integrationHelpers = new IntegrationHelperService({
|
|
649
646
|
workingDirectory,
|
|
650
647
|
homeDirectory,
|
|
@@ -691,12 +688,14 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
691
688
|
overflowHandler,
|
|
692
689
|
sandboxSessionRegistry,
|
|
693
690
|
workflowServices: workflow,
|
|
691
|
+
contextAccountingHolder,
|
|
694
692
|
});
|
|
695
693
|
|
|
696
694
|
return {
|
|
697
695
|
workingDirectory,
|
|
698
696
|
homeDirectory,
|
|
699
697
|
shellPaths,
|
|
698
|
+
workspaceTrustManager,
|
|
700
699
|
configManager,
|
|
701
700
|
featureFlags,
|
|
702
701
|
runtimeBus: options.runtimeBus,
|
|
@@ -726,6 +725,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
726
725
|
memorySpine,
|
|
727
726
|
serviceRegistry,
|
|
728
727
|
secretsManager,
|
|
728
|
+
stepUpService,
|
|
729
729
|
subscriptionManager,
|
|
730
730
|
localUserAuthManager,
|
|
731
731
|
profileManager,
|
|
@@ -777,6 +777,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
777
777
|
agentManager,
|
|
778
778
|
agentMessageBus,
|
|
779
779
|
agentOrchestrator,
|
|
780
|
+
contextAccountingHolder,
|
|
780
781
|
wrfcController,
|
|
781
782
|
processManager,
|
|
782
783
|
orchestrationEngine,
|