@pellux/goodvibes-agent 1.4.4 → 1.5.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 +14 -0
- package/README.md +7 -7
- package/dist/package/main.js +7424 -12241
- package/docs/README.md +2 -2
- package/docs/channels-remote-and-api.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/release-and-publishing.md +1 -1
- package/docs/tools-and-commands.md +5 -5
- package/package.json +4 -2
- package/release/performance-snapshot.json +2 -2
- package/release/release-notes.md +11 -7
- package/release/release-readiness.json +6 -6
- package/src/agent/behavior-discovery-summary.ts +4 -18
- package/src/agent/calendar-registry.ts +322 -0
- package/src/agent/competitive-feature-inventory.ts +268 -130
- package/src/agent/document-registry.ts +5 -1
- package/src/agent/email/email-service.ts +350 -0
- package/src/agent/email/imap-client.ts +596 -0
- package/src/agent/email/smtp-client.ts +453 -0
- package/src/agent/ics-calendar.ts +662 -0
- package/src/agent/ics-timezone.ts +172 -0
- package/src/agent/markdown-frontmatter.ts +31 -0
- package/src/agent/memory-safety.ts +1 -1
- package/src/agent/note-registry.ts +3 -9
- package/src/agent/persona-discovery.ts +3 -15
- package/src/agent/persona-registry.ts +1 -10
- package/src/agent/research-source-registry.ts +5 -1
- package/src/agent/routine-discovery.ts +3 -15
- package/src/agent/runtime-profile.ts +3 -0
- package/src/agent/skill-discovery.ts +3 -15
- package/src/agent/skill-draft-proposer.ts +203 -0
- package/src/agent/skill-draft-runner.ts +201 -0
- package/src/agent/skill-registry.ts +36 -1
- package/src/agent/skill-standard.ts +99 -0
- package/src/agent/vibe-file.ts +7 -22
- package/src/cli/completion.ts +1 -1
- package/src/cli/config-overrides.ts +10 -1
- package/src/cli/redaction.ts +17 -5
- package/src/config/provider-model.ts +2 -1
- package/src/config/secret-config.ts +13 -6
- package/src/core/activity-feed.ts +97 -0
- package/src/core/away-digest.ts +161 -0
- package/src/core/conversation-rendering.ts +7 -3
- package/src/core/conversation.ts +22 -15
- package/src/core/hardware-profile.ts +362 -0
- package/src/core/last-seen-store.ts +78 -0
- package/src/core/plain-language.ts +52 -0
- package/src/core/setup-incomplete-hint.ts +90 -0
- package/src/core/system-message-router.ts +38 -87
- package/src/input/agent-workspace-basic-command-editor-submission.ts +60 -220
- package/src/input/agent-workspace-basic-command-editors.ts +39 -141
- package/src/input/agent-workspace-categories.ts +40 -125
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-host-category.ts +0 -5
- package/src/input/agent-workspace-local-editor-submission.ts +3 -1
- package/src/input/agent-workspace-navigation.ts +10 -3
- package/src/input/agent-workspace-onboarding-actions.ts +132 -0
- package/src/input/agent-workspace-onboarding-categories.ts +29 -26
- package/src/input/agent-workspace-onboarding-finish.ts +11 -4
- package/src/input/agent-workspace-onboarding-state.ts +111 -0
- package/src/input/agent-workspace-profile-editor-submission.ts +260 -0
- package/src/input/agent-workspace-profile-editors.ts +155 -0
- package/src/input/agent-workspace-subscription-editor.ts +7 -0
- package/src/input/agent-workspace-types.ts +5 -1
- package/src/input/agent-workspace.ts +65 -39
- package/src/input/command-registry.ts +18 -5
- package/src/input/commands/agent-runtime-profile-runtime.ts +2 -1
- package/src/input/commands/agent-skills-runtime.ts +60 -3
- package/src/input/commands/calendar-runtime.ts +209 -0
- package/src/input/commands/channels-runtime.ts +1 -0
- package/src/input/commands/command-error.ts +9 -0
- package/src/input/commands/compat-runtime.ts +1 -0
- package/src/input/commands/config.ts +1 -0
- package/src/input/commands/conversation-runtime.ts +1 -1
- package/src/input/commands/delegation-runtime.ts +5 -6
- package/src/input/commands/email-runtime.ts +387 -0
- package/src/input/commands/experience-runtime.ts +17 -4
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +6 -1
- package/src/input/commands/knowledge-format.ts +73 -0
- package/src/input/commands/knowledge.ts +9 -74
- package/src/input/commands/local-provider-runtime.ts +2 -1
- package/src/input/commands/local-runtime.ts +10 -4
- package/src/input/commands/mcp-runtime.ts +7 -0
- package/src/input/commands/notify-runtime.ts +17 -1
- package/src/input/commands/onboarding-runtime.ts +1 -0
- package/src/input/commands/operator-actions-runtime.ts +1 -0
- package/src/input/commands/operator-runtime.ts +3 -0
- package/src/input/commands/personas-runtime.ts +1 -0
- package/src/input/commands/platform-access-runtime.ts +40 -17
- package/src/input/commands/product-runtime.ts +6 -1
- package/src/input/commands/provider-accounts-runtime.ts +3 -2
- package/src/input/commands/qrcode-runtime.ts +1 -0
- package/src/input/commands/routines-runtime.ts +1 -0
- package/src/input/commands/runtime-services.ts +0 -13
- package/src/input/commands/security-runtime.ts +1 -0
- package/src/input/commands/session-content.ts +1 -0
- package/src/input/commands/shell-core.ts +5 -2
- package/src/input/commands/subscription-runtime.ts +33 -9
- package/src/input/commands/support-bundle-runtime.ts +8 -1
- package/src/input/commands/tasks-runtime.ts +1 -0
- package/src/input/commands/tts-runtime.ts +1 -0
- package/src/input/commands/vibe-runtime.ts +1 -0
- package/src/input/commands.ts +4 -0
- package/src/input/feed-context-factory.ts +3 -12
- package/src/input/handler-command-route.ts +7 -60
- package/src/input/handler-feed-routes.ts +0 -194
- package/src/input/handler-feed.ts +2 -54
- package/src/input/handler-interactions.ts +0 -2
- package/src/input/handler-modal-stack.ts +0 -9
- package/src/input/handler-picker-routes.ts +24 -1
- package/src/input/handler-shortcuts.ts +11 -40
- package/src/input/handler-ui-state.ts +13 -0
- package/src/input/handler.ts +8 -35
- package/src/input/keybindings.ts +40 -17
- package/src/input/model-picker-local-fit.ts +130 -0
- package/src/input/submission-router.ts +0 -5
- package/src/main.ts +111 -89
- package/src/renderer/activity-sidebar.ts +186 -0
- package/src/renderer/agent-workspace-context-lines.ts +5 -48
- package/src/renderer/agent-workspace-style.ts +1 -1
- package/src/renderer/agent-workspace.ts +14 -2
- package/src/renderer/compositor.ts +37 -125
- package/src/renderer/conversation-overlays.ts +3 -3
- package/src/renderer/help-overlay.ts +7 -5
- package/src/renderer/model-workspace.ts +101 -86
- package/src/{panels → renderer}/polish.ts +3 -3
- package/src/renderer/shell-surface.ts +4 -5
- package/src/renderer/status-token.ts +31 -11
- package/src/renderer/tab-strip.ts +1 -1
- package/src/renderer/tool-call.ts +7 -8
- package/src/renderer/tool-labels.ts +92 -0
- package/src/renderer/ui-factory.ts +48 -96
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +6 -15
- package/src/runtime/bootstrap-core.ts +34 -13
- package/src/runtime/bootstrap-hook-bridge.ts +3 -1
- package/src/runtime/bootstrap-shell.ts +3 -50
- package/src/runtime/bootstrap.ts +3 -4
- package/src/runtime/context.ts +1 -1
- package/src/runtime/diagnostics/panels/index.ts +0 -1
- package/src/runtime/diagnostics/panels/policy.ts +1 -1
- package/src/runtime/execution-ledger.ts +16 -1
- package/src/runtime/index.ts +6 -1
- package/src/runtime/onboarding/index.ts +1 -0
- package/src/runtime/onboarding/onboarding-state.ts +200 -0
- package/src/{panels → runtime}/provider-account-snapshot.ts +5 -2
- package/src/runtime/services.ts +16 -4
- package/src/runtime/terminal-output-guard.ts +7 -0
- package/src/runtime/tool-permission-safety.ts +1 -1
- package/src/runtime/ui/model-picker/data-provider.ts +1 -1
- package/src/runtime/ui/model-picker/health-enrichment.ts +2 -2
- package/src/runtime/ui/model-picker/types.ts +2 -2
- package/src/runtime/ui/provider-health/data-provider.ts +3 -3
- package/src/runtime/ui/provider-health/types.ts +2 -2
- package/src/runtime/ui-services.ts +0 -2
- package/src/shell/agent-workspace-fullscreen.ts +0 -1
- package/src/shell/autonomy-surfacing.ts +272 -0
- package/src/shell/session-continuity-hints.ts +0 -6
- package/src/shell/startup-wiring.ts +221 -0
- package/src/shell/ui-openers.ts +18 -29
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-harness-background-processes.ts +4 -4
- package/src/tools/agent-harness-browser-cockpit-route.ts +3 -3
- package/src/tools/agent-harness-command-runner.ts +6 -1
- package/src/tools/agent-harness-document-ops.ts +26 -53
- package/src/tools/agent-harness-execution-history.ts +1 -13
- package/src/tools/agent-harness-execution-posture.ts +0 -15
- package/src/tools/agent-harness-keybinding-metadata.ts +28 -29
- package/src/tools/agent-harness-local-model-cookbook.ts +24 -1
- package/src/tools/agent-harness-metadata.ts +16 -0
- package/src/tools/agent-harness-mode-catalog.ts +2 -9
- package/src/tools/agent-harness-provider-account-metadata.ts +2 -2
- package/src/tools/agent-harness-setup-posture-utils.ts +1 -1
- package/src/tools/agent-harness-tool-schema.ts +13 -14
- package/src/tools/agent-harness-tool.ts +27 -23
- package/src/tools/agent-harness-ui-surface-metadata.ts +10 -20
- package/src/tools/agent-harness-workspace-action-runner.ts +3 -4
- package/src/tools/agent-workspace-tool.ts +2 -33
- package/src/utils/terminal-width.ts +9 -3
- package/src/version.ts +1 -1
- package/src/input/agent-workspace-panel-route.ts +0 -44
- package/src/input/panel-integration-actions.ts +0 -26
- package/src/panels/approval-panel.ts +0 -149
- package/src/panels/automation-control-panel.ts +0 -212
- package/src/panels/base-panel.ts +0 -254
- package/src/panels/builtin/agent.ts +0 -58
- package/src/panels/builtin/knowledge.ts +0 -26
- package/src/panels/builtin/operations.ts +0 -121
- package/src/panels/builtin/session.ts +0 -138
- package/src/panels/builtin/shared.ts +0 -275
- package/src/panels/builtin/usage.ts +0 -21
- package/src/panels/builtin-panels.ts +0 -23
- package/src/panels/confirm-state.ts +0 -61
- package/src/panels/context-visualizer-panel.ts +0 -204
- package/src/panels/cost-tracker-panel.ts +0 -444
- package/src/panels/docs-panel.ts +0 -285
- package/src/panels/index.ts +0 -25
- package/src/panels/knowledge-panel.ts +0 -417
- package/src/panels/memory-panel.ts +0 -226
- package/src/panels/panel-list-panel.ts +0 -464
- package/src/panels/panel-manager.ts +0 -570
- package/src/panels/provider-accounts-panel.ts +0 -233
- package/src/panels/provider-health-domains.ts +0 -208
- package/src/panels/provider-health-panel.ts +0 -720
- package/src/panels/provider-health-tracker.ts +0 -115
- package/src/panels/provider-stats-panel.ts +0 -366
- package/src/panels/qr-panel.ts +0 -207
- package/src/panels/schedule-panel.ts +0 -321
- package/src/panels/scrollable-list-panel.ts +0 -491
- package/src/panels/search-focus.ts +0 -32
- package/src/panels/security-panel.ts +0 -295
- package/src/panels/session-browser-panel.ts +0 -395
- package/src/panels/session-maintenance.ts +0 -125
- package/src/panels/subscription-panel.ts +0 -263
- package/src/panels/system-messages-panel.ts +0 -230
- package/src/panels/tasks-panel.ts +0 -344
- package/src/panels/thinking-panel.ts +0 -304
- package/src/panels/token-budget-panel.ts +0 -475
- package/src/panels/tool-inspector-panel.ts +0 -436
- package/src/panels/types.ts +0 -54
- package/src/renderer/panel-composite.ts +0 -158
- package/src/renderer/panel-tab-bar.ts +0 -69
- package/src/renderer/panel-workspace-bar.ts +0 -42
- package/src/runtime/diagnostics/panels/panel-resources.ts +0 -118
- package/src/tools/agent-harness-panel-metadata.ts +0 -211
- /package/src/runtime/perf/{panel-health-monitor.ts → component-health.ts} +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { logger, summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
2
|
+
import type { HookDispatcher, HookEventPath, HookPhase, HookCategory } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
3
|
+
import type { SessionManager } from '@pellux/goodvibes-sdk/platform/sessions';
|
|
4
|
+
import {
|
|
5
|
+
checkRecoveryFile,
|
|
6
|
+
formatReturnContextForDisplay,
|
|
7
|
+
persistConversation,
|
|
8
|
+
writeRecoveryFile,
|
|
9
|
+
} from '@/runtime/index.ts';
|
|
10
|
+
import type { SessionSnapshot } from '@/runtime/index.ts';
|
|
11
|
+
import {
|
|
12
|
+
readOnboardingCheckMarker,
|
|
13
|
+
readOnboardingCompletionMarker,
|
|
14
|
+
} from '../runtime/onboarding/index.ts';
|
|
15
|
+
import { deriveOnboardingState } from '../runtime/onboarding/onboarding-state.ts';
|
|
16
|
+
import { buildSetupIncompleteHint } from '../core/setup-incomplete-hint.ts';
|
|
17
|
+
|
|
18
|
+
export interface SessionPersistenceAndRecoveryDeps {
|
|
19
|
+
readonly buildCurrentSessionSnapshot: () => SessionSnapshot;
|
|
20
|
+
readonly runtime: { readonly sessionId: string; readonly model: string; readonly provider: string };
|
|
21
|
+
readonly conversation: { readonly title?: string | null };
|
|
22
|
+
readonly workingDir: string;
|
|
23
|
+
readonly homeDirectory: string;
|
|
24
|
+
readonly systemMessageRouter: { high(message: string): void; low(message: string): void };
|
|
25
|
+
readonly render: () => void;
|
|
26
|
+
readonly unsubs: Array<() => void>;
|
|
27
|
+
readonly uiServicesTurns: {
|
|
28
|
+
on(event: 'TURN_COMPLETED' | 'STREAM_START' | 'STREAM_DELTA', handler: () => void): () => void;
|
|
29
|
+
};
|
|
30
|
+
readonly hookDispatcher: HookDispatcher;
|
|
31
|
+
readonly sessionManager: SessionManager;
|
|
32
|
+
/**
|
|
33
|
+
* Called whenever the computed stream-token speed changes so that main.ts
|
|
34
|
+
* (which owns the render closure) can keep the value up to date.
|
|
35
|
+
*/
|
|
36
|
+
readonly onStreamSpeedUpdate: (tokensPerSecond: number) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SessionPersistenceAndRecoveryResult {
|
|
40
|
+
/** Interval handle for the periodic recovery-file writer; clear it on exit. */
|
|
41
|
+
recoveryInterval: ReturnType<typeof setInterval>;
|
|
42
|
+
/** True if an unsaved recovery session was found and the user prompt was shown. */
|
|
43
|
+
recoveryPending: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Wires streaming-speed event subscriptions, auto-save on turn completion,
|
|
48
|
+
* and the post-first-render recovery-file check + periodic writer.
|
|
49
|
+
*
|
|
50
|
+
* Call AFTER the first render() so that:
|
|
51
|
+
* - announceAwayDigest has already run
|
|
52
|
+
* - startHardwareProbe has already fired
|
|
53
|
+
* - The recovery prompt lands as ambient context, not a startup blocker
|
|
54
|
+
*/
|
|
55
|
+
export function wireSessionPersistenceAndRecovery(
|
|
56
|
+
deps: SessionPersistenceAndRecoveryDeps,
|
|
57
|
+
): SessionPersistenceAndRecoveryResult {
|
|
58
|
+
const {
|
|
59
|
+
buildCurrentSessionSnapshot,
|
|
60
|
+
runtime,
|
|
61
|
+
conversation,
|
|
62
|
+
workingDir,
|
|
63
|
+
homeDirectory,
|
|
64
|
+
systemMessageRouter,
|
|
65
|
+
render,
|
|
66
|
+
unsubs,
|
|
67
|
+
uiServicesTurns,
|
|
68
|
+
hookDispatcher,
|
|
69
|
+
sessionManager,
|
|
70
|
+
onStreamSpeedUpdate,
|
|
71
|
+
} = deps;
|
|
72
|
+
|
|
73
|
+
// --- Streaming speed + tool preview wiring ---
|
|
74
|
+
let streamStartTime = 0;
|
|
75
|
+
let streamDeltaCount = 0;
|
|
76
|
+
|
|
77
|
+
unsubs.push(uiServicesTurns.on('TURN_COMPLETED', () => {
|
|
78
|
+
// Auto-save after every LLM turn so kills don't lose the session
|
|
79
|
+
try {
|
|
80
|
+
const snapshot = buildCurrentSessionSnapshot();
|
|
81
|
+
persistConversation(
|
|
82
|
+
runtime.sessionId,
|
|
83
|
+
snapshot,
|
|
84
|
+
runtime.model,
|
|
85
|
+
runtime.provider,
|
|
86
|
+
conversation.title || '',
|
|
87
|
+
{ workingDirectory: workingDir, homeDirectory, sessionManager },
|
|
88
|
+
);
|
|
89
|
+
hookDispatcher.fire({ path: 'Lifecycle:session:save' as HookEventPath, phase: 'Lifecycle' as HookPhase, category: 'session' as HookCategory, specific: 'save', sessionId: runtime.sessionId, timestamp: Date.now(), payload: { sessionId: runtime.sessionId } }).catch((err: unknown) => logger.debug('hook fire error', { error: summarizeError(err) }));
|
|
90
|
+
} catch (e) { logger.debug('auto-save on turn:complete failed', { error: summarizeError(e) }); }
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
unsubs.push(uiServicesTurns.on('STREAM_START', () => {
|
|
94
|
+
streamStartTime = Date.now();
|
|
95
|
+
streamDeltaCount = 0;
|
|
96
|
+
onStreamSpeedUpdate(0);
|
|
97
|
+
}));
|
|
98
|
+
unsubs.push(uiServicesTurns.on('STREAM_DELTA', () => {
|
|
99
|
+
streamDeltaCount++;
|
|
100
|
+
const elapsed = (Date.now() - streamStartTime) / 1000;
|
|
101
|
+
// Note: counts stream deltas, not actual tokens. ~1 delta per token for most providers.
|
|
102
|
+
onStreamSpeedUpdate(elapsed > 0 ? streamDeltaCount / elapsed : 0);
|
|
103
|
+
}));
|
|
104
|
+
|
|
105
|
+
// Recovery file check: display prompt if an unsaved session exists.
|
|
106
|
+
// Runs after the first render so the message lands as ambient context.
|
|
107
|
+
let recoveryPending = false;
|
|
108
|
+
const recoveryInfo = checkRecoveryFile({ workingDirectory: workingDir, homeDirectory });
|
|
109
|
+
if (recoveryInfo) {
|
|
110
|
+
systemMessageRouter.high(`[Recovery] Found unsaved session from ${new Date(recoveryInfo.timestamp).toLocaleString()}. Title: "${recoveryInfo.title}". Press Ctrl+R to restore, Esc to discard, or start typing to ignore it.`);
|
|
111
|
+
for (const line of formatReturnContextForDisplay(recoveryInfo.returnContext)) {
|
|
112
|
+
systemMessageRouter.low(`[Recovery] ${line}`);
|
|
113
|
+
}
|
|
114
|
+
render();
|
|
115
|
+
recoveryPending = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const recoveryInterval = setInterval(() => {
|
|
119
|
+
const snapshot = buildCurrentSessionSnapshot();
|
|
120
|
+
writeRecoveryFile(
|
|
121
|
+
snapshot,
|
|
122
|
+
runtime.sessionId,
|
|
123
|
+
conversation.title ?? '',
|
|
124
|
+
{ workingDirectory: workingDir, homeDirectory },
|
|
125
|
+
);
|
|
126
|
+
}, 60_000);
|
|
127
|
+
|
|
128
|
+
return { recoveryInterval, recoveryPending };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Deps for the setup-incomplete hint, kept narrow so the function is easily
|
|
133
|
+
* testable and doesn't pull in the full bootstrap context.
|
|
134
|
+
*/
|
|
135
|
+
export interface SetupIncompleteHintDeps {
|
|
136
|
+
/**
|
|
137
|
+
* Shell path service — used to locate onboarding marker files.
|
|
138
|
+
* Must satisfy the minimal interface required by readOnboardingCheckMarker.
|
|
139
|
+
*/
|
|
140
|
+
readonly shellPaths: Parameters<typeof readOnboardingCheckMarker>[0];
|
|
141
|
+
/**
|
|
142
|
+
* Whether a model route is currently configured and usable for chat.
|
|
143
|
+
* Pass true when the provider registry has a ready model, false otherwise.
|
|
144
|
+
*/
|
|
145
|
+
readonly providerReady: boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Whether a local model route has been detected and is ready for chat.
|
|
148
|
+
* Pass true when the local-model-cookbook status === 'detected-local-route'.
|
|
149
|
+
* Omit or pass false when unavailable. Best-effort: errors should produce false.
|
|
150
|
+
* This mirrors the 'local-model-readiness' plan item in the real buildSetupPlan.
|
|
151
|
+
*/
|
|
152
|
+
readonly localReady?: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Whether the assistant service (connected host) is known to be running.
|
|
155
|
+
* Pass true/false for a confirmed live signal; omit or pass null/undefined
|
|
156
|
+
* when the signal is unreliable or unavailable at startup time.
|
|
157
|
+
*/
|
|
158
|
+
readonly hostReady?: boolean | null;
|
|
159
|
+
/** Low-priority message channel — same interface as SystemMessageRouter.low(). */
|
|
160
|
+
readonly systemMessageRouter: { low(message: string): void };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Derives the current onboarding state from disk markers and, if setup is
|
|
165
|
+
* in-progress, pushes a plain-language hint to the conversation feed.
|
|
166
|
+
*
|
|
167
|
+
* Best-effort: any error is caught silently so startup is never blocked.
|
|
168
|
+
* Call after the first render, alongside announceAwayDigest.
|
|
169
|
+
*/
|
|
170
|
+
export function wireSetupIncompleteHint(deps: SetupIncompleteHintDeps): void {
|
|
171
|
+
try {
|
|
172
|
+
const { shellPaths, providerReady, localReady, hostReady, systemMessageRouter } = deps;
|
|
173
|
+
|
|
174
|
+
const checkMarker = readOnboardingCheckMarker(shellPaths, 'user');
|
|
175
|
+
const completionMarker = readOnboardingCompletionMarker(shellPaths, 'user');
|
|
176
|
+
|
|
177
|
+
// Build a minimal plan with BOTH provider-access AND local-model-readiness so
|
|
178
|
+
// that deriveReadyToChat mirrors the workspace's canonical definition:
|
|
179
|
+
// readyToChat = providerItem.ready OR localModelItem.ready
|
|
180
|
+
//
|
|
181
|
+
// local-model-readiness uses blocksAutonomy:false so it never becomes a false
|
|
182
|
+
// blocker when only the local route is available (matching real plan behaviour).
|
|
183
|
+
// We intentionally skip the full buildSetupPlan (requires CommandContext +
|
|
184
|
+
// async collectSnapshot) because this is a best-effort ambient hint.
|
|
185
|
+
const minimalPlan = [
|
|
186
|
+
{
|
|
187
|
+
id: 'provider-access',
|
|
188
|
+
label: 'Model access',
|
|
189
|
+
status: providerReady ? ('ready' as const) : ('blocked' as const),
|
|
190
|
+
priority: 20,
|
|
191
|
+
blocksAutonomy: true,
|
|
192
|
+
reason: '',
|
|
193
|
+
nextAction: 'Choose a model to start chatting.',
|
|
194
|
+
userRoute: 'Agent Workspace -> Start -> Choose main model',
|
|
195
|
+
modelRoute: '',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'local-model-readiness',
|
|
199
|
+
label: 'Local model',
|
|
200
|
+
status: localReady ? ('ready' as const) : ('recommended' as const),
|
|
201
|
+
priority: 25,
|
|
202
|
+
blocksAutonomy: false,
|
|
203
|
+
reason: '',
|
|
204
|
+
nextAction: 'Run /agent to set up a local model route.',
|
|
205
|
+
userRoute: 'Agent Workspace -> Models -> Local models',
|
|
206
|
+
modelRoute: '',
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const state = deriveOnboardingState({ plan: minimalPlan, checkMarker, completionMarker });
|
|
211
|
+
|
|
212
|
+
const hint = buildSetupIncompleteHint(state, hostReady);
|
|
213
|
+
if (hint === null) return;
|
|
214
|
+
|
|
215
|
+
for (const line of hint.lines) {
|
|
216
|
+
systemMessageRouter.low(`[Setup] ${line}`);
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
// Never block startup on hint errors.
|
|
220
|
+
}
|
|
221
|
+
}
|
package/src/shell/ui-openers.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { getProviderIdFromModel } from '../config/provider-model.ts';
|
|
|
3
3
|
import type { ConversationManager } from '../core/conversation';
|
|
4
4
|
import type { CommandContext } from '../input/command-registry.ts';
|
|
5
5
|
import type { InputHandler } from '../input/handler.ts';
|
|
6
|
-
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
7
6
|
import { EFFORT_DESCRIPTIONS } from '@pellux/goodvibes-sdk/platform/providers';
|
|
8
7
|
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
9
8
|
import type { MutableRuntimeState } from '@/runtime/index.ts';
|
|
@@ -13,13 +12,12 @@ import type { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config'
|
|
|
13
12
|
import type { SecretsManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
14
13
|
import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
|
|
15
14
|
import type { ModelPickerTargetInfo } from '../input/model-picker.ts';
|
|
15
|
+
import { buildLocalFitRecommendations, buildSignInRow, LOCAL_REC_PROVIDER } from '../input/model-picker-local-fit.ts';
|
|
16
16
|
import { syncServiceSettingToPlatform } from './service-settings-sync.ts';
|
|
17
|
-
import { agentWorkspaceCategoryForPanel } from '../input/agent-workspace-panel-route.ts';
|
|
18
17
|
|
|
19
18
|
type WireShellUiOpenersOptions = {
|
|
20
19
|
commandContext: CommandContext;
|
|
21
20
|
input: InputHandler;
|
|
22
|
-
panelManager: PanelManager;
|
|
23
21
|
conversation: ConversationManager;
|
|
24
22
|
configManager: ConfigManager;
|
|
25
23
|
providerRegistry: ProviderRegistry;
|
|
@@ -84,7 +82,6 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
84
82
|
const {
|
|
85
83
|
commandContext,
|
|
86
84
|
input,
|
|
87
|
-
panelManager,
|
|
88
85
|
conversation,
|
|
89
86
|
configManager,
|
|
90
87
|
providerRegistry,
|
|
@@ -190,10 +187,19 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
190
187
|
|
|
191
188
|
commandContext.openModelPicker = () => {
|
|
192
189
|
void (async () => {
|
|
193
|
-
const
|
|
190
|
+
const catalogModels = providerRegistry.getSelectableModels();
|
|
194
191
|
const configuredIds = new Set(getConfiguredProviderIds());
|
|
195
192
|
input.modelPicker.configuredProviders = configuredIds;
|
|
196
|
-
|
|
193
|
+
|
|
194
|
+
// CRITICAL GATE: inject synthetic local recommendations ONLY when zero
|
|
195
|
+
// providers are configured. When any real credential exists these must
|
|
196
|
+
// not be present in the list. The sign-in row is appended last so it
|
|
197
|
+
// is always reachable but does not displace hardware-fit entries.
|
|
198
|
+
const models = configuredIds.size === 0
|
|
199
|
+
? [...buildLocalFitRecommendations(), buildSignInRow(), ...catalogModels]
|
|
200
|
+
: catalogModels;
|
|
201
|
+
|
|
202
|
+
const providerIds = [...new Set(models.map((m) => m.provider).filter((p) => p !== LOCAL_REC_PROVIDER))];
|
|
197
203
|
const secretProviderIds = await resolveSecretProviderIds();
|
|
198
204
|
input.modelPicker.configuredViaMap = buildConfiguredViaMap(providerIds, configuredIds, subscriptionManager, secretProviderIds);
|
|
199
205
|
void getPinned().then((pinned) => {
|
|
@@ -202,7 +208,11 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
202
208
|
void input.modelPicker.loadRecentModels().catch(() => {}); // best-effort: prefetch for UI, failure is non-visible
|
|
203
209
|
input.modalOpened('modelPicker');
|
|
204
210
|
input.modelPicker.setTargetInfos(buildModelPickerTargets());
|
|
205
|
-
|
|
211
|
+
// Pre-select the best-fit local recommendation when no providers are configured.
|
|
212
|
+
const preSelectId = configuredIds.size === 0 && models.length > 0
|
|
213
|
+
? (models[0]?.registryKey ?? models[0]?.id ?? getCurrentModelForPickerTarget())
|
|
214
|
+
: getCurrentModelForPickerTarget();
|
|
215
|
+
input.modelPicker.openAllModels(models, preSelectId);
|
|
206
216
|
render();
|
|
207
217
|
})().catch((error: unknown) => {
|
|
208
218
|
commandContext.print?.(`Model picker failed to open: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -439,36 +449,15 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
439
449
|
render();
|
|
440
450
|
};
|
|
441
451
|
|
|
442
|
-
commandContext.
|
|
443
|
-
panelManager.hide();
|
|
444
|
-
input.panelFocused = false;
|
|
452
|
+
commandContext.openWorkspacePicker = () => {
|
|
445
453
|
conversation.setSplashSuppressed(false);
|
|
446
|
-
conversation.log('Panel picker is handled through Agent Workspace. Use /agent for current operator controls.', { fg: '214' });
|
|
447
454
|
input.openAgentWorkspace(commandContext, 'home');
|
|
448
455
|
conversation.rebuildHistory();
|
|
449
456
|
render();
|
|
450
457
|
};
|
|
451
458
|
|
|
452
|
-
commandContext.focusPanels = () => {
|
|
453
|
-
input.panelFocused = false;
|
|
454
|
-
input.openAgentWorkspace(commandContext, 'home');
|
|
455
|
-
render();
|
|
456
|
-
};
|
|
457
|
-
|
|
458
459
|
commandContext.focusPrompt = () => {
|
|
459
|
-
input.panelFocused = false;
|
|
460
460
|
input.indicatorFocused = false;
|
|
461
461
|
render();
|
|
462
462
|
};
|
|
463
|
-
|
|
464
|
-
commandContext.showPanel = (panelId, pane) => {
|
|
465
|
-
void pane;
|
|
466
|
-
panelManager.hide();
|
|
467
|
-
input.panelFocused = false;
|
|
468
|
-
conversation.setSplashSuppressed(false);
|
|
469
|
-
conversation.log(`Panel route "${panelId}" is handled through Agent Workspace. Opening the matching operator area.`, { fg: '214' });
|
|
470
|
-
input.openAgentWorkspace(commandContext, agentWorkspaceCategoryForPanel(panelId));
|
|
471
|
-
conversation.rebuildHistory();
|
|
472
|
-
render();
|
|
473
|
-
};
|
|
474
463
|
}
|
|
@@ -160,7 +160,7 @@ function buildAgentCapabilitiesContract(registry?: ToolRegistry): Record<string,
|
|
|
160
160
|
},
|
|
161
161
|
{
|
|
162
162
|
area: 'Harness operation',
|
|
163
|
-
can: 'Discover and use harness modes, slash commands, workspace actions, settings,
|
|
163
|
+
can: 'Discover and use harness modes, slash commands, workspace actions, settings, UI surfaces, keybindings, and model tools.',
|
|
164
164
|
tools: [optionalTool('agent_harness'), optionalTool('host'), optionalTool('settings'), 'goodvibes_context'],
|
|
165
165
|
inspect: 'agent_harness mode:"modes" query:"capability"',
|
|
166
166
|
},
|
|
@@ -658,7 +658,7 @@ export async function runBackgroundProcessAction(context: CommandContext, args:
|
|
|
658
658
|
const action = readProcessAction(args);
|
|
659
659
|
if (action === 'write') {
|
|
660
660
|
const confirmationError = requireConfirmed(args, 'Background process stdin write');
|
|
661
|
-
if (confirmationError)
|
|
661
|
+
if (confirmationError) return { status: 'needs_confirmation', reason: confirmationError, policy: 'Writing stdin to a background process requires confirm:true and explicitUserRequest.' };
|
|
662
662
|
const writeInput = firstManagerFunction(manager, STDIN_WRITE_METHOD_NAMES);
|
|
663
663
|
if (!writeInput) {
|
|
664
664
|
return {
|
|
@@ -710,7 +710,7 @@ export async function runBackgroundProcessAction(context: CommandContext, args:
|
|
|
710
710
|
}
|
|
711
711
|
if (action === 'start' || action === 'spawn' || action === 'run') {
|
|
712
712
|
const confirmationError = requireConfirmed(args, 'Background process start');
|
|
713
|
-
if (confirmationError)
|
|
713
|
+
if (confirmationError) return { status: 'needs_confirmation', reason: confirmationError, policy: 'Starting a background process requires confirm:true and explicitUserRequest.' };
|
|
714
714
|
const command = readCommand(args);
|
|
715
715
|
if (!command) throw new Error('Background process start requires command.');
|
|
716
716
|
if (looksLikeSudo(command)) {
|
|
@@ -749,7 +749,7 @@ export async function runBackgroundProcessAction(context: CommandContext, args:
|
|
|
749
749
|
|
|
750
750
|
if (action === 'stop' || action === 'kill' || action === 'cancel') {
|
|
751
751
|
const confirmationError = requireConfirmed(args, 'Background process stop');
|
|
752
|
-
if (confirmationError)
|
|
752
|
+
if (confirmationError) return { status: 'needs_confirmation', reason: confirmationError, policy: 'Stopping a background process requires confirm:true and explicitUserRequest.' };
|
|
753
753
|
const processId = readProcessId(args);
|
|
754
754
|
if (!processId) throw new Error('Background process stop requires processId.');
|
|
755
755
|
const stopped = manager.stop(processId);
|
|
@@ -767,7 +767,7 @@ export async function runBackgroundProcessAction(context: CommandContext, args:
|
|
|
767
767
|
|
|
768
768
|
if (action === 'wait') {
|
|
769
769
|
const confirmationError = requireConfirmed(args, 'Background process wait');
|
|
770
|
-
if (confirmationError)
|
|
770
|
+
if (confirmationError) return { status: 'needs_confirmation', reason: confirmationError, policy: 'Waiting on a background process requires confirm:true and explicitUserRequest.' };
|
|
771
771
|
const processId = readProcessId(args);
|
|
772
772
|
if (!processId) throw new Error('Background process wait requires processId.');
|
|
773
773
|
const timeoutMs = clampTimeout(args.timeoutMs ?? readField(args, 'timeoutMs'), DEFAULT_WAIT_TIMEOUT_MS);
|
|
@@ -36,7 +36,7 @@ const BROWSER_COCKPIT_EXPECTED_LANES = [
|
|
|
36
36
|
{
|
|
37
37
|
id: 'mobile-and-channels',
|
|
38
38
|
label: 'Mobile and channels',
|
|
39
|
-
categoryIds: ['onboarding-channels', 'onboarding-voice-media', '
|
|
39
|
+
categoryIds: ['onboarding-channels', 'onboarding-voice-media', 'personal-ops'],
|
|
40
40
|
userOutcome: 'Configure mobile-friendly messaging, notification, voice, phone, and daily personal-operation routes from one cockpit.',
|
|
41
41
|
},
|
|
42
42
|
{
|
|
@@ -48,13 +48,13 @@ const BROWSER_COCKPIT_EXPECTED_LANES = [
|
|
|
48
48
|
{
|
|
49
49
|
id: 'work-and-automation',
|
|
50
50
|
label: 'Work and automation',
|
|
51
|
-
categoryIds: ['work', 'automation'
|
|
51
|
+
categoryIds: ['work', 'automation'],
|
|
52
52
|
userOutcome: 'Approve, supervise, recover, schedule, and delegate autonomous work with touch-friendly controls and receipts.',
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
id: 'research-and-artifacts',
|
|
56
56
|
label: 'Research and artifacts',
|
|
57
|
-
categoryIds: ['research', 'documents'
|
|
57
|
+
categoryIds: ['research', 'documents'],
|
|
58
58
|
userOutcome: 'Run research, draft documents, review packets, browse artifacts, and export/share deliverables from the browser.',
|
|
59
59
|
},
|
|
60
60
|
{
|
|
@@ -34,9 +34,14 @@ export async function runCommand(deps: AgentHarnessToolDeps, args: AgentHarnessT
|
|
|
34
34
|
const commandArgs = invocationArgsFromLookup(resolved.lookup);
|
|
35
35
|
const handled = await deps.commandRegistry.execute(resolved.command.name, [...commandArgs], toolContext);
|
|
36
36
|
if (!handled) return error(`Unknown slash command /${resolved.command.name}.`);
|
|
37
|
+
const MAX_PRINTED_CHARS = 6000;
|
|
38
|
+
const raw = printed.length > 0 ? printed.join('\n') : '(no text output)';
|
|
39
|
+
const printedText = raw.length > MAX_PRINTED_CHARS
|
|
40
|
+
? `${raw.slice(0, MAX_PRINTED_CHARS)}\n... output truncated`
|
|
41
|
+
: raw;
|
|
37
42
|
return output([
|
|
38
43
|
`Command ${safeCommandDisplay(resolved.command.name)} completed.`,
|
|
39
44
|
`Resolved by ${resolved.lookup.source} ${resolved.lookup.resolvedBy}.`,
|
|
40
|
-
|
|
45
|
+
printedText,
|
|
41
46
|
].join('\n'));
|
|
42
47
|
}
|
|
@@ -222,19 +222,14 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
222
222
|
'document-draft-chat',
|
|
223
223
|
], available);
|
|
224
224
|
const uploadActions = existingActions([
|
|
225
|
-
'artifact-attach-image',
|
|
226
|
-
'artifact-paste',
|
|
227
|
-
'artifact-ingest-file',
|
|
228
|
-
'artifact-ingest-url-list',
|
|
229
|
-
'artifact-import-bookmarks',
|
|
230
|
-
'artifact-browser-history',
|
|
231
225
|
'document-attach-image',
|
|
232
|
-
'document-paste',
|
|
233
226
|
'document-ingest-file',
|
|
227
|
+
'document-ingest-url-list',
|
|
228
|
+
'knowledge-import-bookmarks',
|
|
229
|
+
'knowledge-import-browser-history',
|
|
230
|
+
'conversation-image',
|
|
234
231
|
], available);
|
|
235
232
|
const exportActions = existingActions([
|
|
236
|
-
'artifact-export-conversation',
|
|
237
|
-
'artifact-session-export',
|
|
238
233
|
'conversation-export-current',
|
|
239
234
|
'conversation-session-export',
|
|
240
235
|
'document-export-draft',
|
|
@@ -242,20 +237,14 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
242
237
|
'document-export-artifact-package',
|
|
243
238
|
'document-export-conversation',
|
|
244
239
|
'document-export-session',
|
|
245
|
-
'artifact-export-file',
|
|
246
|
-
'artifact-export-package',
|
|
247
240
|
], available);
|
|
248
241
|
const sourceActions = existingActions([
|
|
249
|
-
'artifact-source-library',
|
|
250
|
-
'artifact-show-source',
|
|
251
242
|
'research-knowledge-search',
|
|
252
243
|
'research-knowledge-ask',
|
|
253
244
|
'document-sources',
|
|
254
245
|
'document-show-source',
|
|
255
246
|
], available);
|
|
256
247
|
const mediaActions = existingActions([
|
|
257
|
-
'artifact-media-providers',
|
|
258
|
-
'artifact-generate-media',
|
|
259
248
|
'media-providers',
|
|
260
249
|
'media-generate',
|
|
261
250
|
'document-media-providers',
|
|
@@ -267,16 +256,8 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
267
256
|
'document-export-artifact-file',
|
|
268
257
|
'document-export-artifact-package',
|
|
269
258
|
'document-promote-artifact',
|
|
270
|
-
'artifact
|
|
271
|
-
'artifact
|
|
272
|
-
'artifact-export-file',
|
|
273
|
-
'artifact-export-package',
|
|
274
|
-
'artifact-promote-knowledge',
|
|
275
|
-
'artifact-insert-document',
|
|
276
|
-
'artifact-attach-document',
|
|
277
|
-
'document-artifacts',
|
|
278
|
-
'artifact-flow',
|
|
279
|
-
'artifact-show-source',
|
|
259
|
+
'document-insert-artifact',
|
|
260
|
+
'document-attach-artifact',
|
|
280
261
|
], available);
|
|
281
262
|
const modelCompareActions = existingActions([
|
|
282
263
|
'document-run-compare',
|
|
@@ -287,12 +268,6 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
287
268
|
'document-apply-compare',
|
|
288
269
|
'document-record-route-decision',
|
|
289
270
|
'document-export-compare',
|
|
290
|
-
'artifact-review-compare',
|
|
291
|
-
'artifact-diff-handoffs',
|
|
292
|
-
'artifact-judge-compare',
|
|
293
|
-
'artifact-compare-analytics',
|
|
294
|
-
'artifact-apply-compare',
|
|
295
|
-
'artifact-export-compare',
|
|
296
271
|
'document-model-routing',
|
|
297
272
|
'account-main-model',
|
|
298
273
|
], available);
|
|
@@ -314,11 +289,11 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
314
289
|
&& hasTool(context, 'agent_knowledge_ingest')
|
|
315
290
|
&& Boolean(context.platform.artifactStore?.list)
|
|
316
291
|
&& Boolean(context.platform.artifactStore?.readContent)
|
|
317
|
-
&& artifactBrowserActions.includes('
|
|
318
|
-
&& artifactBrowserActions.includes('
|
|
319
|
-
&& artifactBrowserActions.includes('
|
|
320
|
-
&& artifactBrowserActions.includes('
|
|
321
|
-
&& artifactBrowserActions.includes('
|
|
292
|
+
&& artifactBrowserActions.includes('document-browse-artifacts')
|
|
293
|
+
&& artifactBrowserActions.includes('document-show-artifact')
|
|
294
|
+
&& artifactBrowserActions.includes('document-export-artifact-file')
|
|
295
|
+
&& artifactBrowserActions.includes('document-export-artifact-package')
|
|
296
|
+
&& artifactBrowserActions.includes('document-promote-artifact');
|
|
322
297
|
const reviewerReadiness = buildReviewerReadinessChecklist(context, {
|
|
323
298
|
documentsReady,
|
|
324
299
|
modelCompareReady,
|
|
@@ -337,7 +312,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
337
312
|
next: documentsReady
|
|
338
313
|
? 'Use versioned drafts, comments, suggestions, artifact reuse, artifact packages or ZIP archives, and artifact-backed comparison as one document workflow.'
|
|
339
314
|
: 'Wire agent_documents plus browse/show/create/revise/review/export workspace actions.',
|
|
340
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
315
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Create document draft',
|
|
341
316
|
modelRoute: documentsReady ? 'agent_documents' : 'agent_harness mode:"workspace_actions" categoryId:"documents"',
|
|
342
317
|
signals: [
|
|
343
318
|
`Chat route ${snapshot.provider} / ${snapshot.modelDisplayName}`,
|
|
@@ -363,7 +338,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
363
338
|
next: uploadActions.length > 0
|
|
364
339
|
? 'Use attach, paste, or reviewed source ingest depending on whether the file is prompt context or durable Agent Knowledge.'
|
|
365
340
|
: 'Wire upload, paste, and file ingest actions before exposing this as ready.',
|
|
366
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
341
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Attach or ingest',
|
|
367
342
|
modelRoute: 'agent_harness mode:"workspace_actions" query:"upload file ingest"',
|
|
368
343
|
signals: [
|
|
369
344
|
`${uploadActions.length} upload/ingest action(s)`,
|
|
@@ -378,7 +353,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
378
353
|
outcome: 'Turn conversations, sessions, documents, and saved artifacts into local files the user can keep or reuse.',
|
|
379
354
|
current: `${exportActions.length} export action(s) are reachable for conversation, session, document, comparison, and saved artifact output.`,
|
|
380
355
|
next: 'Keep transcript, session, document, comparison, and artifact exports visible, explicit, and reusable from one place.',
|
|
381
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
356
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Export',
|
|
382
357
|
modelRoute: 'agent_harness mode:"workspace_actions" query:"export artifact"',
|
|
383
358
|
signals: [
|
|
384
359
|
`${exportActions.length} export action(s)`,
|
|
@@ -393,7 +368,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
393
368
|
outcome: 'Know what must be resolved before exporting, handing off, archiving, or applying comparison-backed route changes.',
|
|
394
369
|
current: `${reviewerReadiness.summary.documents} document draft(s), ${reviewerReadiness.summary.openComments} open comment(s), ${reviewerReadiness.summary.proposedSuggestions} proposed suggestion(s), ${reviewerReadiness.summary.savedComparisons} saved comparison(s), ${reviewerReadiness.summary.revealedJudgments} revealed judgment(s).`,
|
|
395
370
|
next: reviewerReadiness.next,
|
|
396
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
371
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Review readiness preflight',
|
|
397
372
|
modelRoute: 'agent_harness mode:"document_ops_lane" laneId:"reviewer_readiness"',
|
|
398
373
|
signals: [
|
|
399
374
|
`Reviewer readiness ${reviewerReadiness.status}`,
|
|
@@ -423,9 +398,8 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
423
398
|
'document-export-compare',
|
|
424
399
|
].includes(id)),
|
|
425
400
|
...artifactBrowserActions.filter((id) => [
|
|
426
|
-
'
|
|
427
|
-
'
|
|
428
|
-
'artifact-export-package',
|
|
401
|
+
'document-browse-artifacts',
|
|
402
|
+
'document-show-artifact',
|
|
429
403
|
].includes(id)),
|
|
430
404
|
],
|
|
431
405
|
reviewerReadiness,
|
|
@@ -445,7 +419,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
445
419
|
? `${snapshot.reviewPacketTimeline.count} packet event(s); latest ${snapshot.reviewPacketTimeline.items[0].label}.`
|
|
446
420
|
: 'No document, artifact, comparison, judgment, handoff, archive, or route-decision packet events are available yet.',
|
|
447
421
|
next: snapshot.reviewPacketTimeline.next,
|
|
448
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
422
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Review packet timeline',
|
|
449
423
|
modelRoute: 'agent_harness mode:"document_ops_lane" laneId:"review_packet_timeline"',
|
|
450
424
|
signals: [
|
|
451
425
|
`Timeline available ${snapshot.reviewPacketTimeline.available ? 'yes' : 'no'}`,
|
|
@@ -471,9 +445,8 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
471
445
|
'document-export-compare',
|
|
472
446
|
].includes(id)),
|
|
473
447
|
...artifactBrowserActions.filter((id) => [
|
|
474
|
-
'
|
|
475
|
-
'
|
|
476
|
-
'artifact-export-package',
|
|
448
|
+
'document-browse-artifacts',
|
|
449
|
+
'document-show-artifact',
|
|
477
450
|
].includes(id)),
|
|
478
451
|
],
|
|
479
452
|
},
|
|
@@ -492,7 +465,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
492
465
|
? `${snapshot.reviewPacketWizard.completedSteps}/${snapshot.reviewPacketWizard.totalSteps} step(s) complete; current ${snapshot.reviewPacketWizard.currentStepLabel}.`
|
|
493
466
|
: `${snapshot.reviewPacketWizard.completedSteps}/${snapshot.reviewPacketWizard.totalSteps} step(s) complete.`,
|
|
494
467
|
next: snapshot.reviewPacketWizard.next,
|
|
495
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
468
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Review packet wizard',
|
|
496
469
|
modelRoute: 'agent_harness mode:"document_ops_lane" laneId:"review_packet_wizard"',
|
|
497
470
|
signals: [
|
|
498
471
|
`Wizard status ${snapshot.reviewPacketWizard.status}`,
|
|
@@ -516,7 +489,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
516
489
|
outcome: 'Inspect source-backed records before citing, summarizing, or promoting knowledge.',
|
|
517
490
|
current: `${sourceActions.length} source lookup/search action(s) are reachable through isolated Agent Knowledge.`,
|
|
518
491
|
next: 'Use source search/show for citation checks; ingest reviewed files or URLs only through explicit source actions.',
|
|
519
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
492
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Sources',
|
|
520
493
|
modelRoute: 'agent_harness mode:"workspace_actions" query:"Agent Knowledge sources"',
|
|
521
494
|
signals: [
|
|
522
495
|
`Knowledge route ${snapshot.knowledgeRoute}`,
|
|
@@ -533,7 +506,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
533
506
|
next: snapshot.mediaGenerationProviderCount > 0
|
|
534
507
|
? 'Use confirmed media generation when the user asks for a concrete artifact.'
|
|
535
508
|
: 'Configure a media generation provider before claiming generated media is ready.',
|
|
536
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
509
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Generate media',
|
|
537
510
|
modelRoute: 'agent_media_generate',
|
|
538
511
|
signals: [
|
|
539
512
|
`${mediaActions.length} media action(s)`,
|
|
@@ -559,8 +532,8 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
559
532
|
`Artifact browser tool: ${hasTool(context, 'agent_artifacts') ? 'available' : 'gap'}`,
|
|
560
533
|
`Knowledge ingest tool: ${hasTool(context, 'agent_knowledge_ingest') ? 'available' : 'gap'}`,
|
|
561
534
|
`Artifact list/read store: ${context.platform.artifactStore?.list && context.platform.artifactStore?.readContent ? 'available' : 'gap'}`,
|
|
562
|
-
`Artifact export action: ${artifactBrowserActions.includes('
|
|
563
|
-
`Artifact package export action: ${artifactBrowserActions.includes('
|
|
535
|
+
`Artifact export action: ${artifactBrowserActions.includes('document-export-artifact-file') ? 'available' : 'gap'}`,
|
|
536
|
+
`Artifact package export action: ${artifactBrowserActions.includes('document-export-artifact-package') ? 'available' : 'gap'}`,
|
|
564
537
|
`Knowledge promotion action: ${artifactBrowserActions.includes('artifact-promote-knowledge') ? 'available' : 'gap'}`,
|
|
565
538
|
`Document attachment action: ${artifactBrowserActions.includes('artifact-attach-document') ? 'available' : 'gap'}`,
|
|
566
539
|
`Document insertion action: ${artifactBrowserActions.includes('artifact-insert-document') ? 'available' : 'gap'}`,
|
|
@@ -578,7 +551,7 @@ function buildLanes(context: CommandContext): readonly DocumentOpsLane[] {
|
|
|
578
551
|
next: modelCompareReady
|
|
579
552
|
? 'Use cross-session synthesis and reviewer handoff ZIP archives around saved comparison, judgment, export, route-update, route-decision receipt, and source-artifact reuse artifacts.'
|
|
580
553
|
: 'Implement a blind compare runner with selectable candidate models, identical prompt/context, rubric capture, delayed reveal, export, and route update handoff.',
|
|
581
|
-
userRoute: 'Agent Workspace -> Documents &
|
|
554
|
+
userRoute: 'Agent Workspace -> Documents & Files -> Run blind compare',
|
|
582
555
|
modelRoute: modelCompareReady ? 'agent_model_compare' : 'models action:"status"',
|
|
583
556
|
signals: [
|
|
584
557
|
`Current model ${snapshot.provider} / ${snapshot.modelDisplayName}`,
|
|
@@ -78,20 +78,8 @@ function isRecoverableRoute(record: AgentExecutionRecord): boolean {
|
|
|
78
78
|
return tool.includes('write') || tool.includes('edit') || tool.includes('patch') || tool.includes('delete');
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
function toolInspectorAvailable(context: CommandContext): boolean {
|
|
82
|
-
return Boolean(context.workspace.panelManager?.getRegisteredTypes().some((panel) => panel.id === 'tools'));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
81
|
function supervisionRoutes(context: CommandContext, record: AgentExecutionRecord): readonly Record<string, unknown>[] {
|
|
86
82
|
const routes: Record<string, unknown>[] = [];
|
|
87
|
-
if (toolInspectorAvailable(context)) {
|
|
88
|
-
routes.push({
|
|
89
|
-
id: 'tool-inspector',
|
|
90
|
-
label: 'Tool Call Inspector',
|
|
91
|
-
modelRoute: 'workspace action:"open_panel" panelId:"tools"',
|
|
92
|
-
requiresConfirmation: true,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
83
|
if (record.routeKind === 'shell') {
|
|
96
84
|
if (typeof context.openProcessModal === 'function') {
|
|
97
85
|
routes.push({
|
|
@@ -397,7 +385,7 @@ function describeRecord(
|
|
|
397
385
|
modelAccess: {
|
|
398
386
|
inspectHistory: 'execution action:"history"',
|
|
399
387
|
inspectRecord: routeForRecord(record),
|
|
400
|
-
toolInspector: '
|
|
388
|
+
toolInspector: 'execution action:"status"',
|
|
401
389
|
fileRecovery: 'execution action:"recovery"',
|
|
402
390
|
},
|
|
403
391
|
} : {}),
|