@pellux/goodvibes-agent 0.1.116 → 1.0.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 +48 -661
- package/README.md +64 -57
- package/bin/goodvibes-agent.ts +16 -2
- package/dist/package/main.js +199180 -192185
- package/docs/README.md +15 -8
- package/docs/channels-remote-and-api.md +50 -0
- package/docs/connected-host.md +59 -0
- package/docs/getting-started.md +61 -50
- package/docs/knowledge-artifacts-and-multimodal.md +91 -0
- package/docs/project-planning.md +79 -0
- package/docs/providers-and-routing.md +46 -0
- package/docs/release-and-publishing.md +48 -11
- package/docs/tools-and-commands.md +123 -0
- package/docs/voice-and-live-tts.md +51 -0
- package/package.json +3 -6
- package/src/agent/behavior-discovery-summary.ts +1 -3
- package/src/agent/channel-delivery.ts +201 -0
- package/src/agent/media-generation.ts +159 -0
- package/src/agent/memory-prompt.ts +0 -1
- package/src/agent/note-registry.ts +329 -0
- package/src/agent/operator-actions.ts +343 -0
- package/src/agent/persona-discovery.ts +1 -3
- package/src/agent/persona-registry.ts +19 -16
- package/src/agent/record-labels.ts +107 -0
- package/src/agent/reminder-schedule-format.ts +33 -24
- package/src/agent/reminder-schedule.ts +27 -26
- package/src/agent/routine-registry.ts +54 -14
- package/src/agent/routine-schedule-args.ts +2 -1
- package/src/agent/routine-schedule-format.ts +77 -53
- package/src/agent/routine-schedule-promotion.ts +35 -33
- package/src/agent/routine-schedule-receipts.ts +29 -27
- package/src/agent/runtime-profile-starters.ts +5 -5
- package/src/agent/runtime-profile.ts +91 -12
- package/src/agent/skill-registry.ts +210 -32
- package/src/cli/agent-knowledge-args.ts +5 -1
- package/src/cli/agent-knowledge-command.ts +40 -34
- package/src/cli/agent-knowledge-format.ts +80 -67
- package/src/cli/agent-knowledge-methods.ts +1 -1
- package/src/cli/agent-knowledge-runtime.ts +32 -26
- package/src/cli/bundle-command.ts +14 -9
- package/src/cli/config-overrides.ts +37 -36
- package/src/cli/entrypoint.ts +16 -7
- package/src/cli/external-runtime.ts +10 -4
- package/src/cli/help.ts +54 -29
- package/src/cli/local-library-command.ts +203 -83
- package/src/cli/management-commands.ts +105 -68
- package/src/cli/management.ts +55 -74
- package/src/cli/memory-command.ts +66 -32
- package/src/cli/parser.ts +37 -3
- package/src/cli/profiles-command.ts +166 -31
- package/src/cli/provider-auth-routes.ts +2 -1
- package/src/cli/routines-command.ts +176 -40
- package/src/cli/service-posture.ts +16 -16
- package/src/cli/status.ts +56 -135
- package/src/core/conversation-message-snapshot.ts +131 -0
- package/src/input/agent-workspace-access-command-editor-submission.ts +149 -0
- package/src/input/agent-workspace-access-command-editors.ts +170 -0
- package/src/input/agent-workspace-activation.ts +35 -8
- package/src/input/agent-workspace-basic-command-editor-submission.ts +351 -94
- package/src/input/agent-workspace-basic-command-editors.ts +502 -9
- package/src/input/agent-workspace-categories.ts +423 -51
- package/src/input/agent-workspace-channel-command-editor-submission.ts +83 -0
- package/src/input/agent-workspace-channel-command-editors.ts +65 -0
- package/src/input/agent-workspace-channels.ts +35 -2
- package/src/input/agent-workspace-command-editor.ts +41 -3
- package/src/input/agent-workspace-config-reader.ts +16 -0
- package/src/input/agent-workspace-delegation-editor-submission.ts +55 -0
- package/src/input/agent-workspace-editors.ts +166 -1
- package/src/input/agent-workspace-knowledge-command-editor-submission.ts +132 -0
- package/src/input/agent-workspace-knowledge-command-editors.ts +110 -0
- package/src/input/agent-workspace-knowledge-query-editor.ts +1 -1
- package/src/input/agent-workspace-learned-behavior.ts +67 -0
- package/src/input/agent-workspace-library-command-editor-submission.ts +50 -0
- package/src/input/agent-workspace-library-command-editors.ts +35 -0
- package/src/input/agent-workspace-local-operations.ts +218 -0
- package/src/input/agent-workspace-local-selection.ts +75 -0
- package/src/input/agent-workspace-mcp-command-editor-submission.ts +95 -0
- package/src/input/agent-workspace-media-command-editor-submission.ts +62 -0
- package/src/input/agent-workspace-media-command-editors.ts +27 -0
- package/src/input/agent-workspace-memory-command-editor-submission.ts +232 -0
- package/src/input/agent-workspace-memory-command-editors.ts +180 -0
- package/src/input/agent-workspace-memory-editor.ts +2 -2
- package/src/input/agent-workspace-navigation.ts +81 -0
- package/src/input/agent-workspace-notify-editor-submission.ts +121 -0
- package/src/input/agent-workspace-operations-command-editor-submission.ts +208 -0
- package/src/input/agent-workspace-operations-command-editors.ts +196 -0
- package/src/input/agent-workspace-panel-route.ts +43 -0
- package/src/input/agent-workspace-provider-command-editor-submission.ts +155 -0
- package/src/input/agent-workspace-provider-command-editors.ts +93 -0
- package/src/input/agent-workspace-reminder-schedule-editor.ts +1 -1
- package/src/input/agent-workspace-requirements.ts +11 -0
- package/src/input/agent-workspace-routine-schedule-editor.ts +2 -2
- package/src/input/agent-workspace-search.ts +169 -0
- package/src/input/agent-workspace-secret-editor-submission.ts +153 -0
- package/src/input/agent-workspace-session-command-editor-submission.ts +199 -0
- package/src/input/agent-workspace-session-command-editors.ts +249 -0
- package/src/input/agent-workspace-setup.ts +37 -16
- package/src/input/agent-workspace-skill-bundle-command-editor-submission.ts +101 -0
- package/src/input/agent-workspace-skill-bundle-command-editors.ts +110 -0
- package/src/input/agent-workspace-snapshot.ts +162 -21
- package/src/input/agent-workspace-task-command-editor-submission.ts +61 -0
- package/src/input/agent-workspace-task-command-editors.ts +47 -0
- package/src/input/agent-workspace-token.ts +18 -2
- package/src/input/agent-workspace-types.ts +212 -4
- package/src/input/agent-workspace-voice-media.ts +4 -7
- package/src/input/agent-workspace-web-research-editor.ts +104 -0
- package/src/input/agent-workspace-workplan-editor-submission.ts +153 -0
- package/src/input/agent-workspace.ts +178 -240
- package/src/input/command-registry.ts +5 -2
- package/src/input/commands/agent-runtime-profile-runtime.ts +126 -37
- package/src/input/commands/agent-skills-runtime.ts +149 -72
- package/src/input/commands/agent-workspace-runtime.ts +23 -6
- package/src/input/commands/brief-runtime.ts +55 -25
- package/src/input/commands/channels-runtime.ts +147 -33
- package/src/input/commands/compat-runtime.ts +32 -0
- package/src/input/commands/delegation-runtime.ts +29 -23
- package/src/input/commands/experience-runtime.ts +6 -4
- package/src/input/commands/guidance-runtime.ts +4 -4
- package/src/input/commands/health-runtime.ts +149 -126
- package/src/input/commands/knowledge.ts +82 -60
- package/src/input/commands/local-provider-runtime.ts +43 -21
- package/src/input/commands/local-runtime.ts +138 -16
- package/src/input/commands/local-setup-review.ts +10 -20
- package/src/input/commands/mcp-runtime.ts +63 -46
- package/src/input/commands/notify-runtime.ts +38 -9
- package/src/input/commands/operator-actions-runtime.ts +138 -0
- package/src/input/commands/operator-runtime.ts +6 -76
- package/src/input/commands/personas-runtime.ts +46 -31
- package/src/input/commands/planning-runtime.ts +1 -1
- package/src/input/commands/platform-access-runtime.ts +35 -98
- package/src/input/commands/product-runtime.ts +2 -2
- package/src/input/commands/provider-accounts-runtime.ts +60 -39
- package/src/input/commands/qrcode-runtime.ts +50 -9
- package/src/input/commands/recall-bundle.ts +11 -11
- package/src/input/commands/recall-capture.ts +13 -11
- package/src/input/commands/recall-query.ts +29 -21
- package/src/input/commands/recall-review.ts +2 -1
- package/src/input/commands/routines-runtime.ts +99 -45
- package/src/input/commands/runtime-services.ts +1 -14
- package/src/input/commands/schedule-runtime.ts +33 -20
- package/src/input/commands/security-runtime.ts +6 -11
- package/src/input/commands/session-content.ts +80 -78
- package/src/input/commands/session-workflow.ts +134 -104
- package/src/input/commands/session.ts +3 -174
- package/src/input/commands/shell-core.ts +38 -17
- package/src/input/commands/subscription-runtime.ts +54 -185
- package/src/input/commands/support-bundle-runtime.ts +53 -0
- package/src/input/commands/tasks-runtime.ts +21 -21
- package/src/input/commands/work-plan-runtime.ts +33 -8
- package/src/input/commands.ts +6 -2
- package/src/input/feed-context-factory.ts +2 -2
- package/src/input/file-picker.ts +3 -2
- package/src/input/handler-command-route.ts +4 -7
- package/src/input/handler-content-actions.ts +89 -26
- package/src/input/handler-feed-routes.ts +19 -12
- package/src/input/handler-feed.ts +6 -5
- package/src/input/handler-interactions.ts +9 -36
- package/src/input/handler-modal-stack.ts +3 -3
- package/src/input/handler-onboarding.ts +52 -91
- package/src/input/handler-shortcuts.ts +16 -7
- package/src/input/handler.ts +50 -23
- package/src/input/input-history.ts +5 -6
- package/src/input/keybindings.ts +27 -16
- package/src/input/mcp-workspace.ts +90 -26
- package/src/input/onboarding/handler-onboarding-routes.ts +3 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +62 -0
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -0
- package/src/input/onboarding/onboarding-wizard-helpers.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +223 -55
- package/src/input/onboarding/onboarding-wizard-steps.ts +59 -35
- package/src/input/onboarding/onboarding-wizard-types.ts +4 -1
- package/src/input/onboarding/onboarding-wizard.ts +87 -2
- package/src/input/profile-picker-modal.ts +31 -12
- package/src/input/session-picker-modal.ts +21 -4
- package/src/input/settings-modal-agent-policy.ts +6 -6
- package/src/input/settings-modal-behavior.ts +0 -3
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/input/settings-modal-types.ts +2 -13
- package/src/input/settings-modal.ts +20 -66
- package/src/input/submission-intent.ts +0 -1
- package/src/input/submission-router.ts +3 -3
- package/src/main.ts +18 -10
- package/src/panels/approval-panel.ts +8 -8
- package/src/panels/automation-control-panel.ts +4 -4
- package/src/panels/base-panel.ts +1 -1
- package/src/panels/builtin/agent.ts +1 -1
- package/src/panels/builtin/operations.ts +3 -12
- package/src/panels/builtin/session.ts +32 -24
- package/src/panels/builtin/shared.ts +7 -7
- package/src/panels/cost-tracker-panel.ts +1 -1
- package/src/panels/docs-panel.ts +5 -3
- package/src/panels/index.ts +0 -1
- package/src/panels/knowledge-panel.ts +6 -5
- package/src/panels/memory-panel.ts +7 -6
- package/src/panels/panel-list-panel.ts +36 -80
- package/src/panels/project-planning-panel.ts +19 -12
- package/src/panels/provider-account-snapshot.ts +51 -25
- package/src/panels/provider-accounts-panel.ts +33 -18
- package/src/panels/provider-health-domains.ts +48 -7
- package/src/panels/provider-health-panel.ts +5 -4
- package/src/panels/qr-panel.ts +44 -20
- package/src/panels/schedule-panel.ts +9 -17
- package/src/panels/security-panel.ts +8 -8
- package/src/panels/session-browser-panel.ts +10 -10
- package/src/panels/subscription-panel.ts +6 -6
- package/src/panels/system-messages-panel.ts +3 -3
- package/src/panels/tasks-panel.ts +21 -14
- package/src/panels/tool-inspector-panel.ts +19 -12
- package/src/panels/work-plan-panel.ts +5 -5
- package/src/planning/project-planning-coordinator.ts +3 -3
- package/src/provider-auth-route-display.ts +9 -0
- package/src/renderer/agent-workspace-style.ts +34 -0
- package/src/renderer/agent-workspace.ts +254 -57
- package/src/renderer/autocomplete-overlay.ts +25 -6
- package/src/renderer/block-actions.ts +1 -3
- package/src/renderer/bookmark-modal.ts +19 -4
- package/src/renderer/buffer.ts +4 -2
- package/src/renderer/context-inspector.ts +50 -13
- package/src/renderer/diff.ts +1 -1
- package/src/renderer/file-picker-overlay.ts +19 -6
- package/src/renderer/help-overlay.ts +112 -34
- package/src/renderer/history-search-overlay.ts +19 -4
- package/src/renderer/live-tail-modal.ts +27 -5
- package/src/renderer/mcp-workspace.ts +176 -43
- package/src/renderer/model-picker-overlay.ts +58 -3
- package/src/renderer/model-workspace.ts +104 -22
- package/src/renderer/onboarding/onboarding-wizard.ts +20 -4
- package/src/renderer/process-modal.ts +27 -6
- package/src/renderer/profile-picker-modal.ts +21 -5
- package/src/renderer/search-overlay.ts +25 -5
- package/src/renderer/selection-modal-overlay.ts +46 -14
- package/src/renderer/session-picker-modal.ts +18 -1
- package/src/renderer/settings-modal-helpers.ts +2 -40
- package/src/renderer/settings-modal.ts +88 -55
- package/src/renderer/system-message.ts +1 -1
- package/src/renderer/tool-call.ts +20 -11
- package/src/runtime/agent-runtime-events.ts +129 -0
- package/src/runtime/bootstrap-command-context.ts +7 -1
- package/src/runtime/bootstrap-command-parts.ts +11 -8
- package/src/runtime/bootstrap-core.ts +23 -65
- package/src/runtime/bootstrap-hook-bridge.ts +7 -18
- package/src/runtime/bootstrap-shell.ts +46 -8
- package/src/runtime/bootstrap.ts +36 -46
- package/src/runtime/connected-host-auth.ts +47 -0
- package/src/runtime/diagnostics/panels/index.ts +2 -2
- package/src/runtime/diagnostics/panels/panel-resources.ts +1 -1
- package/src/runtime/diagnostics/panels/policy.ts +7 -7
- package/src/runtime/index.ts +2 -5
- package/src/runtime/onboarding/apply-file-helpers.ts +66 -0
- package/src/runtime/onboarding/apply.ts +288 -74
- package/src/runtime/onboarding/derivation.ts +10 -13
- package/src/runtime/onboarding/snapshot.ts +2 -17
- package/src/runtime/onboarding/types.ts +34 -20
- package/src/runtime/onboarding/verify.ts +105 -4
- package/src/runtime/operator-token-cleanup.ts +3 -3
- package/src/runtime/services.ts +95 -11
- package/src/runtime/store/selectors/index.ts +3 -3
- package/src/runtime/store/state.ts +1 -4
- package/src/runtime/surface-feature-flags.ts +41 -6
- package/src/runtime/ui-read-models.ts +3 -4
- package/src/runtime/ui-services.ts +6 -6
- package/src/shell/blocking-input.ts +2 -1
- package/src/shell/service-settings-sync.ts +7 -7
- package/src/shell/ui-openers.ts +17 -38
- package/src/tools/agent-analysis-registry-policy.ts +0 -1
- package/src/tools/agent-channel-send-tool.ts +133 -0
- package/src/tools/agent-context-policy.ts +1 -1
- package/src/tools/agent-knowledge-ingest-tool.ts +405 -0
- package/src/tools/agent-knowledge-tool.ts +170 -0
- package/src/tools/agent-local-registry-tool.ts +279 -73
- package/src/tools/agent-media-generate-tool.ts +133 -0
- package/src/tools/agent-notify-tool.ts +143 -0
- package/src/tools/agent-operator-action-tool.ts +137 -0
- package/src/tools/agent-operator-briefing-tool.ts +217 -0
- package/src/tools/agent-reminder-schedule-tool.ts +237 -0
- package/src/tools/agent-tool-policy-guard.ts +13 -15
- package/src/tools/agent-work-plan-tool.ts +256 -0
- package/src/version.ts +1 -1
- package/docs/connected-services.md +0 -51
- package/src/cli/package-verification.ts +0 -274
- package/src/input/commands/policy-dispatch.ts +0 -339
- package/src/input/commands/policy.ts +0 -17
- package/src/panels/panel-picker.ts +0 -105
- package/src/panels/policy-panel.ts +0 -308
- package/src/renderer/panel-picker-overlay.ts +0 -202
|
@@ -3,12 +3,13 @@ import { randomBytes } from 'node:crypto';
|
|
|
3
3
|
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
4
4
|
import { type SessionMeta } from '@pellux/goodvibes-sdk/platform/sessions';
|
|
5
5
|
import type { TranscriptEventKind } from '@pellux/goodvibes-sdk/platform/core';
|
|
6
|
-
import type { ConversationTitleSource } from '../../core/conversation';
|
|
7
6
|
import type { SessionReturnContextSummary } from '@/runtime/index.ts';
|
|
7
|
+
import type { ConversationMessageSnapshot } from '../../core/conversation.ts';
|
|
8
8
|
import { formatReturnContextForDisplay, getReturnContextMode, maybeAssistReturnContextSummary } from '@/runtime/index.ts';
|
|
9
|
-
import {
|
|
9
|
+
import { requireProviderApi, requireSessionManager } from './runtime-services.ts';
|
|
10
10
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
11
11
|
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
12
|
+
import { readConversationMessageSnapshots } from '../../core/conversation-message-snapshot.ts';
|
|
12
13
|
|
|
13
14
|
function parseTranscriptKind(raw: string | undefined): TranscriptEventKind | 'all' {
|
|
14
15
|
const normalized = (raw ?? 'all').toLowerCase().replace(/-/g, '_');
|
|
@@ -32,6 +33,13 @@ function parseTranscriptKind(raw: string | undefined): TranscriptEventKind | 'al
|
|
|
32
33
|
return allowed.has(normalized as TranscriptEventKind | 'all') ? (normalized as TranscriptEventKind | 'all') : 'all';
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
function formatSessionFailure(action: string, error: unknown): string {
|
|
37
|
+
return [
|
|
38
|
+
`Failed to ${action}`,
|
|
39
|
+
` error ${summarizeError(error)}`,
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
function buildTranscriptReviewLines(
|
|
36
44
|
ctx: CommandContext,
|
|
37
45
|
kind: TranscriptEventKind | 'all',
|
|
@@ -43,8 +51,8 @@ function buildTranscriptReviewLines(
|
|
|
43
51
|
|
|
44
52
|
if (mode === 'groups') {
|
|
45
53
|
return [
|
|
46
|
-
`Transcript Groups${kind === 'all' ? '' :
|
|
47
|
-
` groups
|
|
54
|
+
`Transcript Groups${kind === 'all' ? '' : ` ${kind}`}`,
|
|
55
|
+
` groups ${groups.length}`,
|
|
48
56
|
...groups.slice(0, 12).map((group) => (
|
|
49
57
|
` ${group.kind.padEnd(20)} ${String(group.events.length).padStart(2)} event(s) msgs=${group.messageIndexes[0]}-${group.messageIndexes[group.messageIndexes.length - 1]} ${group.title}`
|
|
50
58
|
)),
|
|
@@ -72,34 +80,33 @@ function buildTranscriptReviewLines(
|
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
return [
|
|
75
|
-
`Transcript Events${kind === 'all' ? '' :
|
|
76
|
-
` events
|
|
83
|
+
`Transcript Events${kind === 'all' ? '' : ` ${kind}`}`,
|
|
84
|
+
` events ${events.length}`,
|
|
77
85
|
...events.slice(0, 16).map((event) => ` #${String(event.messageIndex).padStart(3)} ${event.kind.padEnd(20)} ${event.title} — ${event.detail}`),
|
|
78
86
|
...(events.length > 16 ? [` … ${events.length - 16} more event(s)`] : []),
|
|
79
87
|
];
|
|
80
88
|
}
|
|
81
89
|
|
|
82
|
-
function
|
|
83
|
-
if (!summary?.openPanels || summary.openPanels.length === 0) return
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return reopened;
|
|
90
|
+
function printIgnoredPanelsFromReturnContext(ctx: CommandContext, summary: SessionReturnContextSummary | undefined): void {
|
|
91
|
+
if (!summary?.openPanels || summary.openPanels.length === 0) return;
|
|
92
|
+
ctx.print(` Saved panel state ignored: ${summary.openPanels.slice(0, 4).join(', ')}. Open the Agent workspace for current operator controls.`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function formatAgentReturnContextForDisplay(summary: SessionReturnContextSummary): string[] {
|
|
96
|
+
const ignoredPanels = summary.openPanels?.slice(0, 4) ?? [];
|
|
97
|
+
return [
|
|
98
|
+
...formatReturnContextForDisplay(summary).filter((line) => !line.startsWith('Open panels:')),
|
|
99
|
+
...(ignoredPanels.length > 0
|
|
100
|
+
? [`Saved panel state ignored: ${ignoredPanels.join(', ')}. Open the Agent workspace for current operator controls.`]
|
|
101
|
+
: []),
|
|
102
|
+
];
|
|
96
103
|
}
|
|
97
104
|
|
|
98
105
|
function printSessionExport(
|
|
99
106
|
ctx: { print: (text: string) => void },
|
|
100
107
|
sessionId: string,
|
|
101
108
|
title: string,
|
|
102
|
-
messages:
|
|
109
|
+
messages: readonly ConversationMessageSnapshot[],
|
|
103
110
|
format: string,
|
|
104
111
|
): void {
|
|
105
112
|
const lines: string[] = [];
|
|
@@ -107,21 +114,20 @@ function printSessionExport(
|
|
|
107
114
|
lines.push(`# Session: ${title || sessionId}`);
|
|
108
115
|
lines.push('');
|
|
109
116
|
for (const msg of messages) {
|
|
110
|
-
const
|
|
111
|
-
const content = String(msg.content ?? '');
|
|
117
|
+
const content = conversationMessageContentText(msg);
|
|
112
118
|
if (!content.trim()) continue;
|
|
113
|
-
if (role === 'user') {
|
|
119
|
+
if (msg.role === 'user') {
|
|
114
120
|
lines.push('## User');
|
|
115
121
|
lines.push('');
|
|
116
122
|
lines.push(content);
|
|
117
123
|
lines.push('');
|
|
118
|
-
} else if (role === 'assistant') {
|
|
124
|
+
} else if (msg.role === 'assistant') {
|
|
119
125
|
lines.push('## Assistant');
|
|
120
126
|
lines.push('');
|
|
121
127
|
lines.push(content);
|
|
122
128
|
lines.push('');
|
|
123
|
-
} else if (role === 'tool') {
|
|
124
|
-
const toolName =
|
|
129
|
+
} else if (msg.role === 'tool') {
|
|
130
|
+
const toolName = msg.toolName ?? 'tool';
|
|
125
131
|
lines.push(`## Tool Result: ${toolName}`);
|
|
126
132
|
lines.push('');
|
|
127
133
|
lines.push('```');
|
|
@@ -132,8 +138,8 @@ function printSessionExport(
|
|
|
132
138
|
}
|
|
133
139
|
} else {
|
|
134
140
|
for (const msg of messages) {
|
|
135
|
-
const role =
|
|
136
|
-
const content =
|
|
141
|
+
const role = msg.role.toUpperCase();
|
|
142
|
+
const content = conversationMessageContentText(msg);
|
|
137
143
|
if (!content.trim()) continue;
|
|
138
144
|
lines.push(`[${role}]`);
|
|
139
145
|
lines.push(content);
|
|
@@ -143,6 +149,14 @@ function printSessionExport(
|
|
|
143
149
|
ctx.print(lines.join('\n'));
|
|
144
150
|
}
|
|
145
151
|
|
|
152
|
+
function conversationMessageContentText(message: ConversationMessageSnapshot): string {
|
|
153
|
+
if (typeof message.content === 'string') return message.content;
|
|
154
|
+
return message.content.map((part) => {
|
|
155
|
+
if (part.type === 'text') return part.text;
|
|
156
|
+
return `[image: ${part.mediaType}]`;
|
|
157
|
+
}).join('\n');
|
|
158
|
+
}
|
|
159
|
+
|
|
146
160
|
export async function handleSessionWorkflowCommand(args: string[], ctx: CommandContext): Promise<boolean> {
|
|
147
161
|
const sm = requireSessionManager(ctx);
|
|
148
162
|
const sub = args[0];
|
|
@@ -155,11 +169,11 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
155
169
|
const started = meta ? new Date(meta.timestamp).toLocaleString() : 'this session';
|
|
156
170
|
ctx.print([
|
|
157
171
|
'Current session',
|
|
158
|
-
`
|
|
159
|
-
`
|
|
160
|
-
`
|
|
161
|
-
`
|
|
162
|
-
`
|
|
172
|
+
` id ${id}`,
|
|
173
|
+
` name ${title}`,
|
|
174
|
+
` started ${started}`,
|
|
175
|
+
` messages ${msgCount}`,
|
|
176
|
+
` model ${ctx.session.runtime.model} (${ctx.session.runtime.provider})`,
|
|
163
177
|
].join('\n'));
|
|
164
178
|
return true;
|
|
165
179
|
}
|
|
@@ -170,7 +184,7 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
170
184
|
ctx.print('No saved sessions. Use /session save [name] to save the current session.');
|
|
171
185
|
return true;
|
|
172
186
|
}
|
|
173
|
-
const lines = ['Sessions (most recent first)
|
|
187
|
+
const lines = ['Sessions (most recent first)', ''];
|
|
174
188
|
for (const session of sessions) {
|
|
175
189
|
const date = new Date(session.timestamp).toLocaleString();
|
|
176
190
|
const name = session.title || session.name;
|
|
@@ -179,12 +193,12 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
179
193
|
lines.push(`${active} ${session.name.padEnd(28)} ${name.slice(0, 22).padEnd(22)} ${date} ${session.messageCount} msgs${model}`);
|
|
180
194
|
if (session.returnContext?.activeTasks || session.returnContext?.blockedTasks || session.returnContext?.pendingApprovals || session.returnContext?.openPanels?.length) {
|
|
181
195
|
const posture = [
|
|
182
|
-
session.returnContext.activeTasks ? `active
|
|
183
|
-
session.returnContext.blockedTasks ? `blocked
|
|
184
|
-
session.returnContext.pendingApprovals ? `approvals
|
|
185
|
-
session.returnContext.openPanels?.length ? `panels
|
|
196
|
+
session.returnContext.activeTasks ? `active ${session.returnContext.activeTasks}` : null,
|
|
197
|
+
session.returnContext.blockedTasks ? `blocked ${session.returnContext.blockedTasks}` : null,
|
|
198
|
+
session.returnContext.pendingApprovals ? `approvals ${session.returnContext.pendingApprovals}` : null,
|
|
199
|
+
session.returnContext.openPanels?.length ? `saved panels ignored ${session.returnContext.openPanels.slice(0, 3).join(',')}` : null,
|
|
186
200
|
].filter(Boolean).join(' ');
|
|
187
|
-
if (posture) lines.push(` posture
|
|
201
|
+
if (posture) lines.push(` posture ${posture}`);
|
|
188
202
|
}
|
|
189
203
|
}
|
|
190
204
|
ctx.print(lines.join('\n'));
|
|
@@ -200,20 +214,23 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
200
214
|
try {
|
|
201
215
|
const existingMeta = sm.getMeta(ctx.session.runtime.sessionId);
|
|
202
216
|
if (!existingMeta) {
|
|
203
|
-
|
|
204
|
-
sm.save(ctx.session.runtime.sessionId, exportData.messages ?? [], {
|
|
217
|
+
sm.save(ctx.session.runtime.sessionId, ctx.session.conversationManager.getMessageSnapshot(), {
|
|
205
218
|
title: ctx.session.conversationManager.title || '',
|
|
206
219
|
model: ctx.session.runtime.model,
|
|
207
220
|
provider: ctx.session.runtime.provider,
|
|
208
221
|
timestamp: Date.now(),
|
|
222
|
+
titleSource: ctx.session.conversationManager.getTitleSource(),
|
|
209
223
|
});
|
|
210
224
|
}
|
|
211
225
|
sm.rename(ctx.session.runtime.sessionId, newName);
|
|
212
226
|
ctx.session.conversationManager.title = newName;
|
|
213
|
-
ctx.print(
|
|
227
|
+
ctx.print([
|
|
228
|
+
'Session renamed',
|
|
229
|
+
` name ${newName}`,
|
|
230
|
+
].join('\n'));
|
|
214
231
|
ctx.renderRequest();
|
|
215
232
|
} catch (e) {
|
|
216
|
-
ctx.print(
|
|
233
|
+
ctx.print(formatSessionFailure('rename session', e));
|
|
217
234
|
}
|
|
218
235
|
return true;
|
|
219
236
|
}
|
|
@@ -231,14 +248,14 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
231
248
|
session.title.toLowerCase() === target.toLowerCase(),
|
|
232
249
|
);
|
|
233
250
|
if (!found) {
|
|
234
|
-
ctx.print(`Session not found
|
|
251
|
+
ctx.print(`Session not found ${target}\nUse /session list to see available sessions.`);
|
|
235
252
|
return true;
|
|
236
253
|
}
|
|
237
254
|
try {
|
|
238
255
|
const { meta, messages } = sm.load(found.name);
|
|
239
256
|
const providerApi = requireProviderApi(ctx);
|
|
240
257
|
ctx.session.conversationManager.resetAll();
|
|
241
|
-
ctx.session.conversationManager.fromJSON({ messages: messages
|
|
258
|
+
ctx.session.conversationManager.fromJSON({ messages: readConversationMessageSnapshots(messages), title: meta.title, titleSource: meta.titleSource });
|
|
242
259
|
ctx.session.conversationManager.rebuildHistory();
|
|
243
260
|
ctx.session.runtime.sessionId = found.name;
|
|
244
261
|
if (meta.model) {
|
|
@@ -253,82 +270,93 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
253
270
|
}
|
|
254
271
|
if (meta.provider) ctx.session.runtime.provider = meta.provider;
|
|
255
272
|
ctx.renderRequest();
|
|
256
|
-
ctx.print(
|
|
257
|
-
|
|
273
|
+
ctx.print([
|
|
274
|
+
'Resumed session',
|
|
275
|
+
` id ${found.name}`,
|
|
276
|
+
` name ${meta.title || '(untitled)'}`,
|
|
277
|
+
` messages ${messages.length}`,
|
|
278
|
+
` model ${meta.model || ctx.session.runtime.model}`,
|
|
279
|
+
].join('\n'));
|
|
280
|
+
printIgnoredPanelsFromReturnContext(ctx, meta.returnContext);
|
|
258
281
|
const returnContextMode = getReturnContextMode(ctx.platform.configManager);
|
|
259
282
|
if (returnContextMode !== 'off' && meta.returnContext) {
|
|
260
283
|
for (const line of formatReturnContextForDisplay(meta.returnContext)) {
|
|
284
|
+
if (line.startsWith('Open panels:')) continue;
|
|
261
285
|
ctx.print(` ${line}`);
|
|
262
286
|
}
|
|
263
|
-
if (reopenedPanels.length > 0) {
|
|
264
|
-
ctx.print(` Reopened panels: ${reopenedPanels.join(', ')}`);
|
|
265
|
-
}
|
|
266
287
|
if ((meta.returnContext.remoteRunners?.length ?? 0) > 0) {
|
|
267
|
-
ctx.print(' Remote re-entry
|
|
288
|
+
ctx.print(' Remote re-entry. Handle remote build-host recovery outside Agent; delegate explicit build/fix/review recovery from Agent.');
|
|
268
289
|
}
|
|
269
290
|
if ((meta.returnContext.worktreePaths?.length ?? 0) > 0) {
|
|
270
|
-
ctx.print(' Worktree re-entry
|
|
291
|
+
ctx.print(' Worktree re-entry. Open GoodVibes TUI in the target workspace; delegate explicit build/fix/review recovery from Agent.');
|
|
271
292
|
}
|
|
272
293
|
if (returnContextMode === 'assisted') {
|
|
273
294
|
const helperModel = providerApi.createHelperModel(ctx.platform.configManager);
|
|
274
295
|
void maybeAssistReturnContextSummary(ctx.platform.configManager, helperModel, meta.returnContext).then((assisted) => {
|
|
275
296
|
if (!assisted.assistedNarrative) return;
|
|
276
|
-
ctx.print(` Assist
|
|
297
|
+
ctx.print(` Assist ${assisted.assistedNarrative}`);
|
|
277
298
|
ctx.renderRequest();
|
|
278
299
|
});
|
|
279
300
|
}
|
|
280
301
|
}
|
|
281
302
|
} catch (e) {
|
|
282
|
-
ctx.print(
|
|
303
|
+
ctx.print(formatSessionFailure('resume session', e));
|
|
283
304
|
}
|
|
284
305
|
return true;
|
|
285
306
|
}
|
|
286
307
|
|
|
287
308
|
if (sub === 'fork') {
|
|
309
|
+
const sourceSessionId = ctx.session.runtime.sessionId;
|
|
288
310
|
const newId = `user-${randomBytes(4).toString('hex')}`;
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
provider: ctx.session.runtime.provider,
|
|
311
|
+
const messages = ctx.session.conversationManager.getMessageSnapshot();
|
|
312
|
+
const currentTitle = ctx.session.conversationManager.title;
|
|
313
|
+
const forkName = args[1] ? args.slice(1).join(' ').trim() : `fork-of-${sourceSessionId.slice(0, 8)}`;
|
|
314
|
+
const meta: SessionMeta = {
|
|
315
|
+
title: forkName,
|
|
316
|
+
model: ctx.session.runtime.model,
|
|
317
|
+
provider: ctx.session.runtime.provider,
|
|
297
318
|
timestamp: Date.now(),
|
|
298
|
-
titleSource:
|
|
299
|
-
returnContext: exportData.returnContext,
|
|
319
|
+
titleSource: ctx.session.conversationManager.getTitleSource(),
|
|
300
320
|
};
|
|
301
321
|
try {
|
|
302
322
|
sm.save(newId, messages, meta);
|
|
303
323
|
ctx.session.runtime.sessionId = newId;
|
|
304
324
|
ctx.session.conversationManager.title = forkName;
|
|
305
325
|
ctx.renderRequest();
|
|
306
|
-
ctx.print(
|
|
326
|
+
ctx.print([
|
|
327
|
+
'Session forked',
|
|
328
|
+
` id ${newId}`,
|
|
329
|
+
` name ${forkName}`,
|
|
330
|
+
` from ${currentTitle || sourceSessionId}`,
|
|
331
|
+
` messages ${messages.length}`,
|
|
332
|
+
].join('\n'));
|
|
307
333
|
} catch (e) {
|
|
308
|
-
ctx.print(
|
|
334
|
+
ctx.print(formatSessionFailure('fork session', e));
|
|
309
335
|
}
|
|
310
336
|
return true;
|
|
311
337
|
}
|
|
312
338
|
|
|
313
339
|
if (sub === 'save') {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
provider: ctx.session.runtime.provider,
|
|
340
|
+
const messages = ctx.session.conversationManager.getMessageSnapshot();
|
|
341
|
+
const rawName = args[1] ? args.slice(1).join(' ').trim() : (ctx.session.conversationManager.title || ctx.session.runtime.sessionId);
|
|
342
|
+
const meta: SessionMeta = {
|
|
343
|
+
title: ctx.session.conversationManager.title,
|
|
344
|
+
model: ctx.session.runtime.model,
|
|
345
|
+
provider: ctx.session.runtime.provider,
|
|
321
346
|
timestamp: Date.now(),
|
|
322
|
-
titleSource:
|
|
323
|
-
returnContext: exportData.returnContext,
|
|
347
|
+
titleSource: ctx.session.conversationManager.getTitleSource(),
|
|
324
348
|
};
|
|
325
349
|
try {
|
|
326
350
|
const { filePath, sanitizedName } = sm.save(rawName, messages, meta);
|
|
327
351
|
ctx.session.runtime.sessionId = sanitizedName;
|
|
328
|
-
|
|
329
|
-
|
|
352
|
+
ctx.print([
|
|
353
|
+
'Session saved',
|
|
354
|
+
` name ${rawName}`,
|
|
355
|
+
...(sanitizedName !== rawName ? [` saved as ${sanitizedName}`] : []),
|
|
356
|
+
` path ${filePath}`,
|
|
357
|
+
].join('\n'));
|
|
330
358
|
} catch (e) {
|
|
331
|
-
ctx.print(
|
|
359
|
+
ctx.print(formatSessionFailure('save session', e));
|
|
332
360
|
}
|
|
333
361
|
return true;
|
|
334
362
|
}
|
|
@@ -338,19 +366,19 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
338
366
|
const sessions = sm.list();
|
|
339
367
|
const found = sessions.find((session) => session.name === target || session.name.startsWith(target));
|
|
340
368
|
if (!found) {
|
|
341
|
-
ctx.print(`Session not found
|
|
369
|
+
ctx.print(`Session not found ${target}`);
|
|
342
370
|
return true;
|
|
343
371
|
}
|
|
344
372
|
const date = new Date(found.timestamp).toLocaleString();
|
|
345
373
|
ctx.print([
|
|
346
|
-
`Session
|
|
347
|
-
`
|
|
348
|
-
`
|
|
349
|
-
`
|
|
350
|
-
`
|
|
351
|
-
`
|
|
352
|
-
`
|
|
353
|
-
...(found.returnContext ?
|
|
374
|
+
`Session ${found.name}`,
|
|
375
|
+
` title ${found.title || '(untitled)'}`,
|
|
376
|
+
` model ${found.model || '(unknown)'}`,
|
|
377
|
+
` provider ${found.provider || '(unknown)'}`,
|
|
378
|
+
` date ${date}`,
|
|
379
|
+
` messages ${found.messageCount}`,
|
|
380
|
+
` file ${found.filePath}`,
|
|
381
|
+
...(found.returnContext ? formatAgentReturnContextForDisplay(found.returnContext).map((line) => ` ${line}`) : []),
|
|
354
382
|
].join('\n'));
|
|
355
383
|
return true;
|
|
356
384
|
}
|
|
@@ -368,18 +396,18 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
368
396
|
if (!found && target !== '.') {
|
|
369
397
|
try {
|
|
370
398
|
const { meta, messages } = sm.load(sessionId);
|
|
371
|
-
printSessionExport(ctx, sessionId, meta.title, messages
|
|
399
|
+
printSessionExport(ctx, sessionId, meta.title, readConversationMessageSnapshots(messages), format);
|
|
372
400
|
} catch {
|
|
373
|
-
ctx.print(`Session not found
|
|
401
|
+
ctx.print(`Session not found ${sessionId}`);
|
|
374
402
|
}
|
|
375
403
|
return true;
|
|
376
404
|
}
|
|
377
405
|
const loadName = found ? found.name : sessionId;
|
|
378
406
|
try {
|
|
379
407
|
const { meta, messages } = sm.load(loadName);
|
|
380
|
-
printSessionExport(ctx, loadName, meta.title, messages
|
|
408
|
+
printSessionExport(ctx, loadName, meta.title, readConversationMessageSnapshots(messages), format);
|
|
381
409
|
} catch (e) {
|
|
382
|
-
ctx.print(
|
|
410
|
+
ctx.print(formatSessionFailure('export session', e));
|
|
383
411
|
}
|
|
384
412
|
return true;
|
|
385
413
|
}
|
|
@@ -392,10 +420,10 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
392
420
|
}
|
|
393
421
|
const results = sm.search(query);
|
|
394
422
|
if (results.length === 0) {
|
|
395
|
-
ctx.print(`No sessions found matching
|
|
423
|
+
ctx.print(`No sessions found matching "${query}"`);
|
|
396
424
|
return true;
|
|
397
425
|
}
|
|
398
|
-
const lines = [`Search results for "${query}" (${results.length} session${results.length !== 1 ? 's' : ''})
|
|
426
|
+
const lines = [`Search results for "${query}" (${results.length} session${results.length !== 1 ? 's' : ''})\n`];
|
|
399
427
|
for (const result of results) {
|
|
400
428
|
const date = new Date(result.session.timestamp).toLocaleString();
|
|
401
429
|
lines.push(` ${result.session.name} ${result.session.title || '(untitled)'} ${date} (${result.matchCount} match${result.matchCount !== 1 ? 'es' : ''})`);
|
|
@@ -428,7 +456,7 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
428
456
|
const sessions = sm.list();
|
|
429
457
|
const found = sessions.find((session) => session.name === target || session.name.startsWith(target));
|
|
430
458
|
if (!found) {
|
|
431
|
-
ctx.print(`Session not found
|
|
459
|
+
ctx.print(`Session not found ${target}`);
|
|
432
460
|
return true;
|
|
433
461
|
}
|
|
434
462
|
if (found.name === ctx.session.runtime.sessionId) {
|
|
@@ -437,9 +465,13 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
437
465
|
}
|
|
438
466
|
try {
|
|
439
467
|
sm.delete(found.name);
|
|
440
|
-
ctx.print(
|
|
468
|
+
ctx.print([
|
|
469
|
+
'Session deleted',
|
|
470
|
+
` id ${found.name}`,
|
|
471
|
+
...(found.title ? [` title ${found.title}`] : []),
|
|
472
|
+
].join('\n'));
|
|
441
473
|
} catch (e) {
|
|
442
|
-
ctx.print(
|
|
474
|
+
ctx.print(formatSessionFailure('delete session', e));
|
|
443
475
|
}
|
|
444
476
|
return true;
|
|
445
477
|
}
|
|
@@ -457,14 +489,12 @@ export function registerSessionWorkflowCommands(registry: CommandRegistry): void
|
|
|
457
489
|
async handler(args, ctx) {
|
|
458
490
|
const handled = await handleSessionWorkflowCommand(args, ctx);
|
|
459
491
|
if (!handled) {
|
|
460
|
-
ctx.print(
|
|
492
|
+
ctx.print([
|
|
493
|
+
'Unknown session subcommand',
|
|
494
|
+
` subcommand ${args[0] ?? ''}`,
|
|
495
|
+
'Usage: /session [list | rename <name> | resume <id> | fork [name] | save [name] | info [id] | events [kind] | groups [kind] | hotspots | export <id> [format] | search <query> | delete <id> --yes]',
|
|
496
|
+
].join('\n'));
|
|
461
497
|
}
|
|
462
498
|
},
|
|
463
499
|
});
|
|
464
500
|
}
|
|
465
|
-
interface SessionExportData {
|
|
466
|
-
readonly messages: object[];
|
|
467
|
-
readonly timestamp?: number;
|
|
468
|
-
readonly titleSource?: ConversationTitleSource;
|
|
469
|
-
readonly returnContext?: SessionReturnContextSummary;
|
|
470
|
-
}
|