@pellux/goodvibes-agent 1.4.4 → 1.5.1
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 +22 -0
- package/README.md +52 -7
- package/dist/package/main.js +7491 -12253
- 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/live-verification/live-verification.json +13 -13
- package/release/live-verification/live-verification.md +15 -15
- package/release/performance-snapshot.json +2 -2
- package/release/release-notes.md +5 -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/shell-passthrough.ts +58 -0
- package/src/input/submission-router.ts +0 -5
- package/src/main.ts +129 -90
- 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
|
@@ -1,417 +0,0 @@
|
|
|
1
|
-
import type { Line } from '../types/grid.ts';
|
|
2
|
-
import { ScrollableListPanel } from './scrollable-list-panel.ts';
|
|
3
|
-
import { type ConfirmState, handleConfirmInput, renderConfirmLines } from './confirm-state.ts';
|
|
4
|
-
import type { MemoryClass, MemoryRecord, MemoryRegistry, MemoryReviewState } from '@pellux/goodvibes-sdk/platform/state';
|
|
5
|
-
import type { KnowledgeStatus } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
6
|
-
import { formatAgentRecordReferences } from '../agent/record-labels.ts';
|
|
7
|
-
import {
|
|
8
|
-
buildBodyText,
|
|
9
|
-
buildGuidanceLine,
|
|
10
|
-
buildKeyValueLine,
|
|
11
|
-
buildPanelLine,
|
|
12
|
-
buildPanelWorkspace,
|
|
13
|
-
DEFAULT_PANEL_PALETTE,
|
|
14
|
-
} from './polish.ts';
|
|
15
|
-
|
|
16
|
-
export interface AgentKnowledgePanelService {
|
|
17
|
-
readonly getStatus: () => Promise<KnowledgeStatus & { readonly note?: string }>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function summarize(records: MemoryRecord[], cls: MemoryClass): MemoryRecord[] {
|
|
21
|
-
return records.filter((record) => record.cls === cls).slice(0, 3);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const C = {
|
|
25
|
-
...DEFAULT_PANEL_PALETTE,
|
|
26
|
-
header: '#94a3b8',
|
|
27
|
-
headerBg: '#1e293b',
|
|
28
|
-
} as const;
|
|
29
|
-
|
|
30
|
-
function reviewStateColor(state: MemoryReviewState): string {
|
|
31
|
-
switch (state) {
|
|
32
|
-
case 'reviewed':
|
|
33
|
-
return C.good;
|
|
34
|
-
case 'stale':
|
|
35
|
-
return C.warn;
|
|
36
|
-
case 'contradicted':
|
|
37
|
-
return C.bad;
|
|
38
|
-
case 'fresh':
|
|
39
|
-
default:
|
|
40
|
-
return C.info;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function formatConfidence(confidence: number): string {
|
|
45
|
-
return `${confidence.toString().padStart(3, ' ')}%`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export class KnowledgePanel extends ScrollableListPanel<MemoryRecord> {
|
|
49
|
-
private readonly registry: MemoryRegistry;
|
|
50
|
-
private readonly agentKnowledgeService: AgentKnowledgePanelService | null;
|
|
51
|
-
private unsubscribe?: () => void;
|
|
52
|
-
private records: MemoryRecord[] = [];
|
|
53
|
-
private agentKnowledgeStatus: (KnowledgeStatus & { readonly note?: string }) | null = null;
|
|
54
|
-
private agentKnowledgeError: string | null = null;
|
|
55
|
-
private agentKnowledgeLoading = false;
|
|
56
|
-
// I1: confirm for destructive review-state mutations
|
|
57
|
-
private confirm: ConfirmState<{ id: string; action: 'stale' | 'contradicted' }> | null = null;
|
|
58
|
-
|
|
59
|
-
public constructor(registry: MemoryRegistry, agentKnowledgeService: AgentKnowledgePanelService | null = null) {
|
|
60
|
-
super('knowledge', 'Knowledge', 'K', 'agent');
|
|
61
|
-
this.registry = registry;
|
|
62
|
-
this.agentKnowledgeService = agentKnowledgeService;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public override onActivate(): void {
|
|
66
|
-
super.onActivate();
|
|
67
|
-
this.refresh();
|
|
68
|
-
this.refreshAgentKnowledgeStatus();
|
|
69
|
-
this.unsubscribe = this.registry.subscribe(() => {
|
|
70
|
-
this.refresh();
|
|
71
|
-
this.markDirty();
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
public override onDeactivate(): void {
|
|
76
|
-
super.onDeactivate();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public override onDestroy(): void {
|
|
80
|
-
this.unsubscribe?.();
|
|
81
|
-
this.unsubscribe = undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ---------------------------------------------------------------------------
|
|
85
|
-
// ScrollableListPanel implementation
|
|
86
|
-
// ---------------------------------------------------------------------------
|
|
87
|
-
|
|
88
|
-
protected getItems(): readonly MemoryRecord[] {
|
|
89
|
-
return this.records;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
protected renderItem(record: MemoryRecord, index: number, selected: boolean, width: number): Line {
|
|
93
|
-
const bg = selected ? C.selectBg : undefined;
|
|
94
|
-
return buildPanelLine(width, [
|
|
95
|
-
[' ', C.label, bg],
|
|
96
|
-
[record.reviewState.padEnd(13), reviewStateColor(record.reviewState), bg],
|
|
97
|
-
[` ${formatConfidence(record.confidence)} `, C.value, bg],
|
|
98
|
-
[record.summary.slice(0, Math.max(0, width - 26)), C.value, bg],
|
|
99
|
-
]);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
protected override getPalette() { return C; }
|
|
103
|
-
protected override getEmptyStateMessage() { return 'No Agent Knowledge sources or local memory review records'; }
|
|
104
|
-
protected override getEmptyStateActions() {
|
|
105
|
-
return [
|
|
106
|
-
{ command: '/knowledge status', summary: 'inspect the isolated Agent Knowledge store' },
|
|
107
|
-
{ command: '/knowledge ingest-url <url> --yes', summary: 'ingest source-backed material into Agent Knowledge only' },
|
|
108
|
-
{ command: '/knowledge queue', summary: 'review Agent Knowledge issues' },
|
|
109
|
-
{ command: '/memory add fact <summary>', summary: 'capture a local non-secret memory record when appropriate' },
|
|
110
|
-
];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
// Input
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
|
|
117
|
-
public handleInput(key: string): boolean {
|
|
118
|
-
// I1: y/n confirm for stale/contradict
|
|
119
|
-
if (this.confirm) {
|
|
120
|
-
const result = handleConfirmInput(this.confirm, key);
|
|
121
|
-
if (result === 'confirmed') {
|
|
122
|
-
const { id, action } = this.confirm.subject;
|
|
123
|
-
this.confirm = null;
|
|
124
|
-
const selected = this.records.find((r) => r.id === id);
|
|
125
|
-
if (selected) {
|
|
126
|
-
try {
|
|
127
|
-
if (action === 'stale') {
|
|
128
|
-
this.registry.review(id, {
|
|
129
|
-
state: 'stale',
|
|
130
|
-
confidence: Math.min(selected.confidence, 40),
|
|
131
|
-
reviewedBy: 'operator',
|
|
132
|
-
staleReason: 'marked stale from the knowledge panel',
|
|
133
|
-
});
|
|
134
|
-
} else {
|
|
135
|
-
this.registry.review(id, {
|
|
136
|
-
state: 'contradicted',
|
|
137
|
-
confidence: 0,
|
|
138
|
-
reviewedBy: 'operator',
|
|
139
|
-
staleReason: 'marked contradicted from the knowledge panel',
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
} catch (e) {
|
|
143
|
-
// I2: surface async failure
|
|
144
|
-
this.setError(`Review update failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
this.refresh();
|
|
148
|
-
this.markDirty();
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
151
|
-
if (result === 'cancelled') {
|
|
152
|
-
this.confirm = null;
|
|
153
|
-
this.markDirty();
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
if (result === 'absorbed') return true;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// I2: auto-clear error on next keypress (inherited via super.handleInput)
|
|
160
|
-
if (this.records.length === 0) return super.handleInput(key);
|
|
161
|
-
|
|
162
|
-
const selected = this.records[this.selectedIndex];
|
|
163
|
-
|
|
164
|
-
if (key === 'Enter' || key === 'return' || key === 'r') {
|
|
165
|
-
if (!selected) return false;
|
|
166
|
-
this.registry.review(selected.id, {
|
|
167
|
-
state: 'reviewed',
|
|
168
|
-
confidence: Math.max(selected.confidence, 85),
|
|
169
|
-
reviewedBy: 'operator',
|
|
170
|
-
});
|
|
171
|
-
this.refresh();
|
|
172
|
-
this.markDirty();
|
|
173
|
-
return true;
|
|
174
|
-
}
|
|
175
|
-
if (key === 's') {
|
|
176
|
-
if (!selected) return false;
|
|
177
|
-
// I1: prompt confirm before marking stale
|
|
178
|
-
this.confirm = { subject: { id: selected.id, action: 'stale' }, label: selected.summary.slice(0, 40) };
|
|
179
|
-
this.markDirty();
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
if (key === 'c') {
|
|
183
|
-
if (!selected) return false;
|
|
184
|
-
// I1: prompt confirm before marking contradicted
|
|
185
|
-
this.confirm = { subject: { id: selected.id, action: 'contradicted' }, label: selected.summary.slice(0, 40) };
|
|
186
|
-
this.markDirty();
|
|
187
|
-
return true;
|
|
188
|
-
}
|
|
189
|
-
if (key === 'f') {
|
|
190
|
-
if (!selected) return false;
|
|
191
|
-
this.registry.review(selected.id, {
|
|
192
|
-
state: 'fresh',
|
|
193
|
-
confidence: Math.max(selected.confidence, 60),
|
|
194
|
-
reviewedBy: 'operator',
|
|
195
|
-
});
|
|
196
|
-
this.refresh();
|
|
197
|
-
this.markDirty();
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Normalize arrow keys to base class format
|
|
202
|
-
if (key === 'ArrowUp') return super.handleInput('up');
|
|
203
|
-
if (key === 'ArrowDown') return super.handleInput('down');
|
|
204
|
-
return super.handleInput(key);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
private refresh(): void {
|
|
208
|
-
const queue = this.registry.reviewQueue(24);
|
|
209
|
-
this.records = queue.length > 0 ? queue : this.registry.search({ limit: 24 });
|
|
210
|
-
this.clampSelection();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
private refreshAgentKnowledgeStatus(): void {
|
|
214
|
-
if (!this.agentKnowledgeService || this.agentKnowledgeLoading) return;
|
|
215
|
-
this.agentKnowledgeLoading = true;
|
|
216
|
-
this.agentKnowledgeError = null;
|
|
217
|
-
this.agentKnowledgeService.getStatus()
|
|
218
|
-
.then((status) => {
|
|
219
|
-
this.agentKnowledgeStatus = status;
|
|
220
|
-
this.agentKnowledgeError = null;
|
|
221
|
-
})
|
|
222
|
-
.catch((error: unknown) => {
|
|
223
|
-
this.agentKnowledgeError = error instanceof Error ? error.message : String(error);
|
|
224
|
-
})
|
|
225
|
-
.finally(() => {
|
|
226
|
-
this.agentKnowledgeLoading = false;
|
|
227
|
-
this.markDirty();
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
private buildAgentKnowledgeHeader(width: number): Line[] {
|
|
232
|
-
const lines: Line[] = [
|
|
233
|
-
buildPanelLine(width, [[' Agent Knowledge Segment', C.label]]),
|
|
234
|
-
buildPanelLine(width, [
|
|
235
|
-
[' route ', C.label],
|
|
236
|
-
['/api/goodvibes-agent/knowledge/*', C.info],
|
|
237
|
-
[' isolated: no default knowledge or non-Agent fallback', C.dim],
|
|
238
|
-
]),
|
|
239
|
-
];
|
|
240
|
-
if (this.agentKnowledgeLoading && !this.agentKnowledgeStatus) {
|
|
241
|
-
lines.push(buildPanelLine(width, [[' loading isolated Agent Knowledge status...', C.dim]]));
|
|
242
|
-
} else if (this.agentKnowledgeStatus) {
|
|
243
|
-
const status = this.agentKnowledgeStatus;
|
|
244
|
-
lines.push(buildKeyValueLine(width, [
|
|
245
|
-
{ label: 'Ready', value: status.ready ? 'yes' : 'no', valueColor: status.ready ? C.good : C.warn },
|
|
246
|
-
{ label: 'Sources', value: String(status.sourceCount), valueColor: status.sourceCount > 0 ? C.info : C.dim },
|
|
247
|
-
{ label: 'Nodes', value: String(status.nodeCount), valueColor: status.nodeCount > 0 ? C.info : C.dim },
|
|
248
|
-
{ label: 'Issues', value: String(status.issueCount), valueColor: status.issueCount > 0 ? C.warn : C.good },
|
|
249
|
-
], C));
|
|
250
|
-
const note = status.note ? ` note: ${status.note}` : '';
|
|
251
|
-
lines.push(buildPanelLine(width, [[' storage: ', C.label], [status.storagePath, C.dim], [note, C.dim]]));
|
|
252
|
-
} else {
|
|
253
|
-
lines.push(buildPanelLine(width, [[' status: not loaded; /knowledge status uses the same isolated route.', C.dim]]));
|
|
254
|
-
}
|
|
255
|
-
if (this.agentKnowledgeError) {
|
|
256
|
-
lines.push(...buildBodyText(width, `Agent Knowledge status warning: ${this.agentKnowledgeError}`, C, C.warn));
|
|
257
|
-
}
|
|
258
|
-
lines.push(buildPanelLine(width, [
|
|
259
|
-
[' actions ', C.label],
|
|
260
|
-
['/knowledge status', C.value],
|
|
261
|
-
[' | ', C.dim],
|
|
262
|
-
['/knowledge ingest-url <url> --yes', C.value],
|
|
263
|
-
[' | ', C.dim],
|
|
264
|
-
['/knowledge search <query>', C.value],
|
|
265
|
-
[' | ', C.dim],
|
|
266
|
-
['/knowledge queue', C.value],
|
|
267
|
-
]));
|
|
268
|
-
return lines;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
public render(width: number, height: number): Line[] {
|
|
272
|
-
this.clampSelection();
|
|
273
|
-
|
|
274
|
-
// I1: show confirm dialog in place of normal content
|
|
275
|
-
if (this.confirm) {
|
|
276
|
-
return buildPanelWorkspace(width, height, {
|
|
277
|
-
title: 'Knowledge Control Room',
|
|
278
|
-
intro: '',
|
|
279
|
-
sections: [{ title: 'Confirmation', lines: renderConfirmLines(width, this.confirm) }],
|
|
280
|
-
footerLines: [buildPanelLine(width, [[' y confirm n / Esc cancel', C.dim]])],
|
|
281
|
-
palette: C,
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (this.records.length === 0) this.refresh();
|
|
286
|
-
|
|
287
|
-
const intro = 'Isolated Agent Knowledge plus local non-secret memory review. This surface never falls back to default knowledge or non-Agent knowledge segments.';
|
|
288
|
-
const records = this.registry.search({ limit: 200 });
|
|
289
|
-
const agentKnowledgeHeader = this.buildAgentKnowledgeHeader(width);
|
|
290
|
-
|
|
291
|
-
if (records.length === 0) {
|
|
292
|
-
return this.renderList(width, height, {
|
|
293
|
-
title: 'Knowledge Control Room',
|
|
294
|
-
header: agentKnowledgeHeader,
|
|
295
|
-
footer: [buildPanelLine(width, [[' Review keys: Up/Down move r/Enter review s stale c contradicted f fresh', C.dim]])],
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const queue = this.registry.reviewQueue(24);
|
|
300
|
-
const byClass = new Map<MemoryClass, number>();
|
|
301
|
-
const byReview = new Map<MemoryReviewState, number>();
|
|
302
|
-
const byScope = new Map<string, number>();
|
|
303
|
-
for (const record of records) {
|
|
304
|
-
byClass.set(record.cls, (byClass.get(record.cls) ?? 0) + 1);
|
|
305
|
-
byReview.set(record.reviewState, (byReview.get(record.reviewState) ?? 0) + 1);
|
|
306
|
-
byScope.set(record.scope, (byScope.get(record.scope) ?? 0) + 1);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const classLines: Line[] = [
|
|
310
|
-
buildPanelLine(width, [
|
|
311
|
-
[' facts ', C.label], [String(byClass.get('fact') ?? 0), C.good],
|
|
312
|
-
[' risks ', C.label], [String(byClass.get('risk') ?? 0), (byClass.get('risk') ?? 0) > 0 ? C.warn : C.good],
|
|
313
|
-
[' runbooks ', C.label], [String(byClass.get('runbook') ?? 0), C.info],
|
|
314
|
-
[' architecture ', C.label], [String(byClass.get('architecture') ?? 0), C.info],
|
|
315
|
-
[' incidents ', C.label], [String(byClass.get('incident') ?? 0), (byClass.get('incident') ?? 0) > 0 ? C.bad : C.good],
|
|
316
|
-
]),
|
|
317
|
-
buildPanelLine(width, [
|
|
318
|
-
[' decisions ', C.label], [String(byClass.get('decision') ?? 0), C.value],
|
|
319
|
-
[' constraints ', C.label], [String(byClass.get('constraint') ?? 0), C.value],
|
|
320
|
-
[' ownership ', C.label], [String(byClass.get('ownership') ?? 0), C.value],
|
|
321
|
-
[' patterns ', C.label], [String(byClass.get('pattern') ?? 0), C.value],
|
|
322
|
-
[' total ', C.label], [String(records.length), C.value],
|
|
323
|
-
]),
|
|
324
|
-
];
|
|
325
|
-
|
|
326
|
-
const reviewLines: Line[] = [
|
|
327
|
-
buildPanelLine(width, [
|
|
328
|
-
[' reviewed ', C.label], [String(byReview.get('reviewed') ?? 0), C.good],
|
|
329
|
-
[' fresh ', C.label], [String(byReview.get('fresh') ?? 0), C.info],
|
|
330
|
-
[' stale ', C.label], [String(byReview.get('stale') ?? 0), C.warn],
|
|
331
|
-
[' contradicted ', C.label], [String(byReview.get('contradicted') ?? 0), C.bad],
|
|
332
|
-
[' Review Queue ', C.label], [String(queue.length), queue.length > 0 ? C.warn : C.good],
|
|
333
|
-
]),
|
|
334
|
-
buildPanelLine(width, [
|
|
335
|
-
[' session ', C.label], [String(byScope.get('session') ?? 0), C.info],
|
|
336
|
-
[' project ', C.label], [String(byScope.get('project') ?? 0), C.value],
|
|
337
|
-
[' team ', C.label], [String(byScope.get('team') ?? 0), C.good],
|
|
338
|
-
]),
|
|
339
|
-
buildGuidanceLine(width, '/memory review', 'work the stale and contradicted queue from the command workspace', C),
|
|
340
|
-
];
|
|
341
|
-
|
|
342
|
-
const recentSummaryLines: Line[] = [];
|
|
343
|
-
for (const [title, items, color] of [
|
|
344
|
-
['Recent Risks', summarize(records, 'risk'), C.warn],
|
|
345
|
-
['Runbooks', summarize(records, 'runbook'), C.info],
|
|
346
|
-
['Architecture Notes', summarize(records, 'architecture'), C.info],
|
|
347
|
-
['Recent Incidents', summarize(records, 'incident'), C.bad],
|
|
348
|
-
] as const) {
|
|
349
|
-
if (recentSummaryLines.length > 0) {
|
|
350
|
-
recentSummaryLines.push(buildPanelLine(width, [['', C.dim]]));
|
|
351
|
-
}
|
|
352
|
-
recentSummaryLines.push(buildPanelLine(width, [[` ${title}`, C.label]]));
|
|
353
|
-
if (items.length === 0) {
|
|
354
|
-
recentSummaryLines.push(buildPanelLine(width, [[' none recorded', C.dim]]));
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
for (const item of items) {
|
|
358
|
-
recentSummaryLines.push(buildPanelLine(width, [
|
|
359
|
-
[' ', C.label],
|
|
360
|
-
[item.summary.slice(0, Math.max(0, width - 2)), color],
|
|
361
|
-
]));
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const selectedRecord = this.records[this.selectedIndex];
|
|
366
|
-
const selectedLines: Line[] = [];
|
|
367
|
-
if (selectedRecord) {
|
|
368
|
-
selectedLines.push(buildPanelLine(width, [[' Selected', C.label]]));
|
|
369
|
-
selectedLines.push(buildKeyValueLine(width, [
|
|
370
|
-
{ label: 'Class', value: selectedRecord.cls, valueColor: C.value },
|
|
371
|
-
{ label: 'Scope', value: selectedRecord.scope, valueColor: C.info },
|
|
372
|
-
{ label: 'Review', value: selectedRecord.reviewState, valueColor: reviewStateColor(selectedRecord.reviewState) },
|
|
373
|
-
{ label: 'Confidence', value: formatConfidence(selectedRecord.confidence), valueColor: C.value },
|
|
374
|
-
], C));
|
|
375
|
-
selectedLines.push(...buildBodyText(width, `Summary: ${selectedRecord.summary}`, C, C.value));
|
|
376
|
-
if (selectedRecord.detail) selectedLines.push(...buildBodyText(width, `Detail: ${selectedRecord.detail}`, C, C.dim));
|
|
377
|
-
if (selectedRecord.provenance.length) {
|
|
378
|
-
selectedLines.push(...buildBodyText(
|
|
379
|
-
width,
|
|
380
|
-
`Origin: ${formatAgentRecordReferences(selectedRecord.provenance)}`,
|
|
381
|
-
C,
|
|
382
|
-
C.dim,
|
|
383
|
-
));
|
|
384
|
-
}
|
|
385
|
-
if (selectedRecord.staleReason) {
|
|
386
|
-
selectedLines.push(...buildBodyText(
|
|
387
|
-
width,
|
|
388
|
-
`Stale reason: ${selectedRecord.staleReason}`,
|
|
389
|
-
C,
|
|
390
|
-
selectedRecord.reviewState === 'contradicted' ? C.bad : C.warn,
|
|
391
|
-
));
|
|
392
|
-
}
|
|
393
|
-
if (selectedRecord.reviewedAt) {
|
|
394
|
-
selectedLines.push(buildPanelLine(width, [
|
|
395
|
-
[' Reviewed: ', C.label],
|
|
396
|
-
[new Date(selectedRecord.reviewedAt).toLocaleString(), C.dim],
|
|
397
|
-
]));
|
|
398
|
-
if (selectedRecord.reviewedBy) {
|
|
399
|
-
selectedLines.push(buildPanelLine(width, [
|
|
400
|
-
[' Reviewer: ', C.label],
|
|
401
|
-
[selectedRecord.reviewedBy, C.dim],
|
|
402
|
-
]));
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
return this.renderList(width, height, {
|
|
408
|
-
title: 'Knowledge Control Room',
|
|
409
|
-
header: [...agentKnowledgeHeader, ...classLines, ...reviewLines],
|
|
410
|
-
footer: [
|
|
411
|
-
buildPanelLine(width, [[' Up/Down move r/Enter reviewed s stale c contradicted f fresh', C.dim]]),
|
|
412
|
-
...(selectedLines.length > 0 ? selectedLines : []),
|
|
413
|
-
...recentSummaryLines,
|
|
414
|
-
],
|
|
415
|
-
});
|
|
416
|
-
}
|
|
417
|
-
}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MemoryPanel — project memory substrate TUI panel.
|
|
3
|
-
*
|
|
4
|
-
* Migrated to SearchableListPanel<MemoryRecord> (Wave B1).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { Line } from '../types/grid.ts';
|
|
8
|
-
import type { MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
|
|
9
|
-
import type { MemoryRecord, MemoryClass } from '@pellux/goodvibes-sdk/platform/state';
|
|
10
|
-
import { formatAgentRecordReferences } from '../agent/record-labels.ts';
|
|
11
|
-
import { SearchableListPanel } from './scrollable-list-panel.ts';
|
|
12
|
-
import {
|
|
13
|
-
buildBodyText,
|
|
14
|
-
buildGuidanceLine,
|
|
15
|
-
buildKeyValueLine,
|
|
16
|
-
buildPanelLine,
|
|
17
|
-
extendPalette,
|
|
18
|
-
DEFAULT_PANEL_PALETTE,
|
|
19
|
-
} from './polish.ts';
|
|
20
|
-
import {
|
|
21
|
-
getPanelSearchFocusTransition,
|
|
22
|
-
isPanelSearchCancel,
|
|
23
|
-
} from './search-focus.ts';
|
|
24
|
-
|
|
25
|
-
const C = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
26
|
-
header: '#94a3b8',
|
|
27
|
-
headerBg: '#1e293b',
|
|
28
|
-
decision: '#38bdf8',
|
|
29
|
-
constraint: '#f97316',
|
|
30
|
-
incident: '#ef4444',
|
|
31
|
-
pattern: '#a78bfa',
|
|
32
|
-
fact: '#22c55e',
|
|
33
|
-
risk: '#f43f5e',
|
|
34
|
-
runbook: '#eab308',
|
|
35
|
-
architecture: '#60a5fa',
|
|
36
|
-
ownership: '#14b8a6',
|
|
37
|
-
selected: '#1e3a5f',
|
|
38
|
-
searchBg: '#0f172a',
|
|
39
|
-
searchFg: '#e2e8f0',
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
function fmtTime(ts: number): string {
|
|
43
|
-
const d = new Date(ts);
|
|
44
|
-
return d.toISOString().slice(0, 16).replace('T', ' ');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function classColor(cls: MemoryClass): string {
|
|
48
|
-
switch (cls) {
|
|
49
|
-
case 'decision': return C.decision;
|
|
50
|
-
case 'constraint': return C.constraint;
|
|
51
|
-
case 'incident': return C.incident;
|
|
52
|
-
case 'pattern': return C.pattern;
|
|
53
|
-
case 'fact': return C.fact;
|
|
54
|
-
case 'risk': return C.risk;
|
|
55
|
-
case 'runbook': return C.runbook;
|
|
56
|
-
case 'architecture': return C.architecture;
|
|
57
|
-
case 'ownership': return C.ownership;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
|
|
62
|
-
private registry: MemoryRegistry;
|
|
63
|
-
private filterFocused = false;
|
|
64
|
-
private unsubscribe?: () => void;
|
|
65
|
-
|
|
66
|
-
constructor(registry: MemoryRegistry) {
|
|
67
|
-
super('memory', 'Memory', 'M', 'agent');
|
|
68
|
-
this.registry = registry;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
onActivate(): void {
|
|
72
|
-
super.onActivate();
|
|
73
|
-
this.searchQuery = '';
|
|
74
|
-
this.invalidateFilter();
|
|
75
|
-
this.filterFocused = false;
|
|
76
|
-
this.unsubscribe = this.registry.subscribe(() => {
|
|
77
|
-
this.invalidateFilter();
|
|
78
|
-
this.markDirty();
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
onDeactivate(): void {
|
|
83
|
-
super.onDeactivate();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
onDestroy(): void {
|
|
87
|
-
this.unsubscribe?.();
|
|
88
|
-
this.unsubscribe = undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ---------------------------------------------------------------------------
|
|
92
|
-
// SearchableListPanel implementation
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
|
|
95
|
-
protected getAllItems(): readonly MemoryRecord[] {
|
|
96
|
-
return this.registry.search({ limit: 100 });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
protected matchesSearch(record: MemoryRecord, query: string): boolean {
|
|
100
|
-
const q = query.trim().toLowerCase();
|
|
101
|
-
if (!q) return true;
|
|
102
|
-
const haystack = [
|
|
103
|
-
record.summary,
|
|
104
|
-
record.detail ?? '',
|
|
105
|
-
record.cls,
|
|
106
|
-
record.scope,
|
|
107
|
-
record.tags.join(' '),
|
|
108
|
-
].join(' ').toLowerCase();
|
|
109
|
-
return haystack.includes(q);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
protected renderItem(record: MemoryRecord, index: number, selected: boolean, width: number): Line {
|
|
113
|
-
const bg = selected ? C.selected : undefined;
|
|
114
|
-
return buildPanelLine(width, [
|
|
115
|
-
[' ', C.label, bg],
|
|
116
|
-
[`[${record.scope.slice(0, 1).toUpperCase()}/${record.cls.slice(0, 3).toUpperCase()}] `, classColor(record.cls), bg],
|
|
117
|
-
[record.id.slice(-8), C.dim, bg],
|
|
118
|
-
[' ', C.label, bg],
|
|
119
|
-
[fmtTime(record.createdAt), C.dim, bg],
|
|
120
|
-
[' ', C.label, bg],
|
|
121
|
-
[record.summary.slice(0, Math.max(0, width - 33)), C.value, bg],
|
|
122
|
-
]);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
protected override getPalette() { return C; }
|
|
126
|
-
protected override getEmptyStateMessage() {
|
|
127
|
-
return this.searchQuery
|
|
128
|
-
? ` No records matching "${this.searchQuery}"`
|
|
129
|
-
: ' No memory records. Use /memory add <class> <summary> to create one.';
|
|
130
|
-
}
|
|
131
|
-
protected override getEmptyStateActions() {
|
|
132
|
-
return [
|
|
133
|
-
{ command: '/memory add fact <summary>', summary: 'capture a durable fact directly' },
|
|
134
|
-
{ command: '/memory capture incident latest', summary: 'promote the latest incident into memory' },
|
|
135
|
-
];
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
handleInput(key: string): boolean {
|
|
139
|
-
// Filter-focus mode: typing goes into the search query
|
|
140
|
-
if (this.filterFocused) {
|
|
141
|
-
const items = this.getItems();
|
|
142
|
-
const transition = getPanelSearchFocusTransition(key, { selectedIndex: this.selectedIndex, itemCount: items.length });
|
|
143
|
-
if (transition === 'focus-list') {
|
|
144
|
-
this.filterFocused = false;
|
|
145
|
-
this.markDirty();
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
if (isPanelSearchCancel(key)) {
|
|
149
|
-
this.filterFocused = false;
|
|
150
|
-
return super.handleInput(key);
|
|
151
|
-
}
|
|
152
|
-
return super.handleInput(key);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const items = this.getItems();
|
|
156
|
-
const transition = getPanelSearchFocusTransition(key, { selectedIndex: this.selectedIndex, itemCount: items.length });
|
|
157
|
-
if (transition === 'focus-search') {
|
|
158
|
-
this.filterFocused = true;
|
|
159
|
-
this.markDirty();
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (key === 'r') {
|
|
164
|
-
this.invalidateFilter();
|
|
165
|
-
this.markDirty();
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return super.handleInput(key);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
render(width: number, height: number): Line[] {
|
|
173
|
-
this.clampSelection();
|
|
174
|
-
const intro = 'Durable project memory across decisions, constraints, incidents, patterns, risks, runbooks, and related origin links.';
|
|
175
|
-
|
|
176
|
-
const records = this.getItems();
|
|
177
|
-
const byClass = new Map<MemoryClass, number>();
|
|
178
|
-
for (const record of records) {
|
|
179
|
-
byClass.set(record.cls, (byClass.get(record.cls) ?? 0) + 1);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const filterLine = this.buildFilterInputLine(width, 'Filter', this.filterFocused);
|
|
183
|
-
|
|
184
|
-
const summaryLines: Line[] = [
|
|
185
|
-
buildKeyValueLine(width, [
|
|
186
|
-
{ label: 'records', value: String(records.length), valueColor: C.value },
|
|
187
|
-
{ label: 'facts', value: String(byClass.get('fact') ?? 0), valueColor: C.fact },
|
|
188
|
-
{ label: 'decisions', value: String(byClass.get('decision') ?? 0), valueColor: C.decision },
|
|
189
|
-
{ label: 'incidents', value: String(byClass.get('incident') ?? 0), valueColor: C.incident },
|
|
190
|
-
{ label: 'runbooks', value: String(byClass.get('runbook') ?? 0), valueColor: C.runbook },
|
|
191
|
-
], C),
|
|
192
|
-
filterLine,
|
|
193
|
-
buildGuidanceLine(width, '/memory review', 'review durable memory and queue posture from the command workspace', C),
|
|
194
|
-
];
|
|
195
|
-
|
|
196
|
-
const selected = records[this.selectedIndex];
|
|
197
|
-
const selectedLines: Line[] = [];
|
|
198
|
-
if (selected) {
|
|
199
|
-
selectedLines.push(buildKeyValueLine(width, [
|
|
200
|
-
{ label: 'scope', value: selected.scope, valueColor: C.info },
|
|
201
|
-
{ label: 'class', value: selected.cls, valueColor: classColor(selected.cls) },
|
|
202
|
-
{ label: 'created', value: fmtTime(selected.createdAt), valueColor: C.dim },
|
|
203
|
-
], C));
|
|
204
|
-
selectedLines.push(...buildBodyText(width, selected.summary, C, C.value));
|
|
205
|
-
if (selected.detail) selectedLines.push(...buildBodyText(width, `Detail: ${selected.detail}`, C, C.dim));
|
|
206
|
-
if (selected.tags.length) selectedLines.push(buildPanelLine(width, [[` Tags: ${selected.tags.join(', ')}`, C.good]]));
|
|
207
|
-
if (selected.provenance.length) {
|
|
208
|
-
selectedLines.push(...buildBodyText(
|
|
209
|
-
width,
|
|
210
|
-
`Origin: ${formatAgentRecordReferences(selected.provenance)}`,
|
|
211
|
-
C,
|
|
212
|
-
C.dim,
|
|
213
|
-
));
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return this.renderList(width, height, {
|
|
218
|
-
title: 'Memory',
|
|
219
|
-
header: summaryLines,
|
|
220
|
-
footer: [
|
|
221
|
-
...selectedLines,
|
|
222
|
-
buildPanelLine(width, [[' / search j/k or Up/Down move r reload Esc clear search', C.dim]]),
|
|
223
|
-
],
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
}
|