@pellux/goodvibes-agent 0.1.102 → 0.1.104

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +10 -0
  3. package/docs/README.md +1 -1
  4. package/docs/getting-started.md +17 -3
  5. package/package.json +1 -1
  6. package/src/agent/memory-safety.ts +16 -0
  7. package/src/cli/help.ts +86 -0
  8. package/src/cli/local-library-command.ts +516 -0
  9. package/src/cli/management.ts +17 -0
  10. package/src/cli/memory-command.ts +630 -0
  11. package/src/cli/package-verification.ts +10 -0
  12. package/src/cli/parser.ts +8 -0
  13. package/src/cli/types.ts +3 -0
  14. package/src/input/agent-workspace-activation.ts +170 -0
  15. package/src/input/agent-workspace-categories.ts +8 -1
  16. package/src/input/agent-workspace-editors.ts +36 -0
  17. package/src/input/agent-workspace-memory-editor.ts +88 -0
  18. package/src/input/agent-workspace-setup.ts +7 -5
  19. package/src/input/agent-workspace-snapshot.ts +40 -4
  20. package/src/input/agent-workspace-token.ts +51 -0
  21. package/src/input/agent-workspace-types.ts +13 -3
  22. package/src/input/agent-workspace.ts +130 -185
  23. package/src/input/feed-context-factory.ts +1 -3
  24. package/src/input/handler-feed.ts +1 -4
  25. package/src/input/handler-interactions.ts +0 -1
  26. package/src/input/handler-modal-stack.ts +0 -1
  27. package/src/input/handler-modal-token-routes.ts +0 -11
  28. package/src/input/handler-picker-routes.ts +11 -20
  29. package/src/input/handler-ui-state.ts +0 -6
  30. package/src/input/handler.ts +1 -17
  31. package/src/main.ts +0 -6
  32. package/src/panels/builtin/agent.ts +0 -17
  33. package/src/panels/index.ts +0 -2
  34. package/src/renderer/agent-workspace.ts +8 -3
  35. package/src/renderer/conversation-overlays.ts +0 -6
  36. package/src/renderer/live-tail-modal.ts +10 -69
  37. package/src/renderer/process-modal.ts +28 -530
  38. package/src/runtime/bootstrap-core.ts +1 -1
  39. package/src/runtime/services.ts +3 -4
  40. package/src/tools/{wrfc-agent-guard.ts → agent-tool-policy-guard.ts} +0 -6
  41. package/src/version.ts +1 -1
  42. package/src/panels/agent-inspector-panel.ts +0 -521
  43. package/src/panels/agent-inspector-shared.ts +0 -94
  44. package/src/panels/agent-logs-panel.ts +0 -559
  45. package/src/panels/agent-logs-shared.ts +0 -129
  46. package/src/renderer/agent-detail-modal.ts +0 -331
  47. package/src/renderer/process-summary.ts +0 -67
@@ -21,14 +21,12 @@ import type { BlockMeta, ConversationManager } from '../core/conversation';
21
21
  import { ProcessModal } from '../renderer/process-modal.ts';
22
22
  import { LiveTailModal } from '../renderer/live-tail-modal.ts';
23
23
  import { BlockActionsMenu } from '../renderer/block-actions.ts';
24
- import { AgentDetailModal } from '../renderer/agent-detail-modal.ts';
25
24
  import { ContextInspectorModal } from '../renderer/context-inspector.ts';
26
25
  import { BookmarkModal } from './bookmark-modal.ts';
27
26
  import { SettingsModal } from './settings-modal.ts';
28
27
  import { McpWorkspace } from './mcp-workspace.ts';
29
28
  import { AgentWorkspace } from './agent-workspace.ts';
30
29
  import { SessionPickerModal } from './session-picker-modal.ts';
31
- import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
32
30
  import { ProfilePickerModal } from './profile-picker-modal.ts';
33
31
  import { OnboardingWizardController, type OnboardingWizardAction, type OnboardingWizardMode } from './onboarding/onboarding-wizard.ts';
34
32
  import {
@@ -155,7 +153,6 @@ export class InputHandler {
155
153
  public searchManager = new SearchManager();
156
154
  public processModal: ProcessModal;
157
155
  public liveTailModal: LiveTailModal;
158
- public agentDetailModal: AgentDetailModal;
159
156
  public contextInspectorModal = new ContextInspectorModal();
160
157
  public bookmarkModal: BookmarkModal;
161
158
  public blockActionsMenu = new BlockActionsMenu();
@@ -220,8 +217,7 @@ export class InputHandler {
220
217
  public scroll: (delta: number) => void,
221
218
  public exitApp: () => void,
222
219
  public readonly uiServices: Pick<UiRuntimeServices,
223
- 'agents'
224
- | 'environment'
220
+ 'environment'
225
221
  | 'platform'
226
222
  | 'providers'
227
223
  | 'sessions'
@@ -235,22 +231,11 @@ export class InputHandler {
235
231
  uiServices.providers.providerRegistry,
236
232
  );
237
233
  this.processModal = new ProcessModal({
238
- agentManager: uiServices.agents.agentManager,
239
234
  processManager: uiServices.shell.processManager,
240
- wrfcController: uiServices.agents.wrfcController,
241
- agentEntries: 'hidden',
242
235
  });
243
236
  this.liveTailModal = new LiveTailModal({
244
- agentManager: uiServices.agents.agentManager,
245
237
  processManager: uiServices.shell.processManager,
246
238
  });
247
- this.agentDetailModal = new AgentDetailModal({
248
- agentManager: uiServices.agents.agentManager,
249
- agentMessageBus: uiServices.agents.agentMessageBus,
250
- sessionLogPathResolver: (agentId) => uiServices.environment.shellPaths.resolveProjectPath(GOODVIBES_AGENT_SURFACE_ROOT, 'sessions', `${agentId}.jsonl`),
251
- // SDK 0.23.0: supply wrfcController so the modal can show constraint data
252
- wrfcController: uiServices.agents.wrfcController,
253
- });
254
239
  this.bookmarkModal = new BookmarkModal(uiServices.shell.bookmarkManager);
255
240
  this.sessionPickerModal = new SessionPickerModal(uiServices.sessions.sessionManager);
256
241
  this.profilePickerModal = new ProfilePickerModal(uiServices.shell.profileManager);
@@ -294,7 +279,6 @@ export class InputHandler {
294
279
  onboardingWizard: this.onboardingWizard,
295
280
  processModal: this.processModal,
296
281
  liveTailModal: this.liveTailModal,
297
- agentDetailModal: this.agentDetailModal,
298
282
  contextInspectorModal: this.contextInspectorModal,
299
283
  blockActionsMenu: this.blockActionsMenu,
300
284
  searchManager: this.searchManager,
package/src/main.ts CHANGED
@@ -398,11 +398,6 @@ async function main() {
398
398
  scroll,
399
399
  exitApp,
400
400
  {
401
- agents: {
402
- agentManager,
403
- agentMessageBus: ctx.services.agentMessageBus,
404
- wrfcController: ctx.services.wrfcController,
405
- },
406
401
  providers: {
407
402
  benchmarkStore: ctx.services.benchmarkStore,
408
403
  favoritesStore: ctx.services.favoritesStore,
@@ -450,7 +445,6 @@ async function main() {
450
445
  input.setConversationManager(conversation);
451
446
  input.setContentWidth(getPromptContentWidth());
452
447
  input.filePicker.setOnUpdate(() => render());
453
- input.agentDetailModal.setOnRefresh(() => render());
454
448
  input.processModal.setOnRefresh(() => render());
455
449
 
456
450
  // Model picker callback is handled in bootstrap.ts — do not duplicate here.
@@ -1,5 +1,4 @@
1
1
  import type { PanelManager } from '../panel-manager.ts';
2
- import { AgentLogsPanel } from '../agent-logs-panel.ts';
3
2
  import { ContextVisualizerPanel } from '../context-visualizer-panel.ts';
4
3
  import { ThinkingPanel } from '../thinking-panel.ts';
5
4
  import { ToolInspectorPanel } from '../tool-inspector-panel.ts';
@@ -50,22 +49,6 @@ export function registerAgentPanels(manager: PanelManager, deps: ResolvedBuiltin
50
49
  ),
51
50
  });
52
51
 
53
- manager.registerType({
54
- id: 'agent-logs',
55
- name: 'Agents',
56
- icon: 'A',
57
- category: 'agent',
58
- description: 'View-only stream for explicit delegated build/review session records',
59
- preload: true,
60
- factory: () => {
61
- const ui = requireUiServices(deps);
62
- return new AgentLogsPanel(ui.events.agents, {
63
- agentManager: ui.agents.agentManager,
64
- workingDirectory: ui.environment.workingDirectory,
65
- });
66
- },
67
- });
68
-
69
52
  manager.registerType({
70
53
  id: 'work-plan',
71
54
  name: 'Work Plan',
@@ -4,8 +4,6 @@ export { BasePanel } from './base-panel.ts';
4
4
  export { PanelManager } from './panel-manager.ts';
5
5
  export { TokenBudgetPanel } from './token-budget-panel.ts';
6
6
  export { CostTrackerPanel } from './cost-tracker-panel.ts';
7
- export { AgentInspectorPanel } from './agent-inspector-panel.ts';
8
- export { AgentLogsPanel } from './agent-logs-panel.ts';
9
7
  export { ProviderHealthPanel } from './provider-health-panel.ts';
10
8
  export { ProviderHealthTracker } from './provider-health-tracker.ts';
11
9
  export type { ProviderHealth, ProviderStatus } from './provider-health-tracker.ts';
@@ -123,6 +123,8 @@ function localLibraryLines(
123
123
  selected ? 'selected' : '',
124
124
  item.active ? 'active' : '',
125
125
  item.enabled === true ? 'enabled' : item.enabled === false ? 'disabled' : '',
126
+ item.scope && item.cls ? `${item.scope}/${item.cls}` : '',
127
+ item.confidence !== undefined ? `${item.confidence}%` : '',
126
128
  item.reviewState,
127
129
  item.startCount !== undefined ? `starts ${item.startCount}` : '',
128
130
  ].filter(Boolean).join(' / ');
@@ -194,8 +196,8 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
194
196
  );
195
197
  } else if (category.id === 'setup') {
196
198
  base.push(
197
- { text: `GoodVibes runtime: ${snapshot.daemonBaseUrl}`, fg: PALETTE.info },
198
- { text: `Runtime owner: ${snapshot.daemonOwnership}; Agent connects but never starts or restarts it`, fg: PALETTE.good },
199
+ { text: `GoodVibes runtime: ${snapshot.runtimeBaseUrl}`, fg: PALETTE.info },
200
+ { text: `Runtime owner: ${snapshot.runtimeOwnership}; Agent connects but never starts or restarts it`, fg: PALETTE.good },
199
201
  ...setupChecklistLines(snapshot),
200
202
  { text: '' },
201
203
  { text: `Workspace: ${snapshot.workingDirectory}`, fg: PALETTE.muted },
@@ -216,7 +218,7 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
216
218
  ...snapshot.channels.filter((channel) => !channel.enabled),
217
219
  ].slice(0, 6);
218
220
  base.push(
219
- { text: `GoodVibes runtime: ${snapshot.daemonBaseUrl}`, fg: PALETTE.info },
221
+ { text: `GoodVibes runtime: ${snapshot.runtimeBaseUrl}`, fg: PALETTE.info },
220
222
  { text: `Readiness: ${readyCount}/${snapshot.channels.length} ready; ${enabledCount} enabled; ${configuredDefaults} default target(s) configured.`, fg: PALETTE.info },
221
223
  { text: `Ready channels: ${readyChannels.join(', ') || 'none'}.`, fg: readyChannels.length > 0 ? PALETTE.good : PALETTE.warn },
222
224
  { text: `Needs default target: ${needsTarget.map((channel) => `${channel.label} -> ${channel.defaultTargetKeys.join('|')}`).join(', ') || 'none'}.`, fg: needsTarget.length > 0 ? PALETTE.warn : PALETTE.muted },
@@ -301,11 +303,14 @@ function snapshotLines(workspace: AgentWorkspace, category: AgentWorkspaceCatego
301
303
  } else if (category.id === 'memory') {
302
304
  base.push(
303
305
  { text: `Session memories: ${snapshot.sessionMemoryCount}`, fg: PALETTE.info },
306
+ { text: `Agent memory: ${snapshot.localMemoryCount}; review queue: ${snapshot.localMemoryReviewQueueCount}`, fg: PALETTE.info },
304
307
  { text: `Local routines: ${snapshot.localRoutineCount}; enabled: ${snapshot.enabledRoutineCount}`, fg: PALETTE.info },
305
308
  { text: `Local skills: ${snapshot.localSkillCount}; enabled: ${snapshot.enabledSkillCount}; bundles: ${snapshot.localSkillBundleCount}; active skills: ${snapshot.activeSkillCount}`, fg: PALETTE.info },
306
309
  { text: `Local personas: ${snapshot.localPersonaCount}; active: ${snapshot.activePersonaName}`, fg: PALETTE.info },
307
310
  { text: 'Durable memory, routines, skills, and personas remain Agent-local until shared registry contracts exist.', fg: PALETTE.good },
308
311
  { text: 'Secrets are rejected/redacted; store secret references instead of secret values.', fg: PALETTE.warn },
312
+ { text: '' },
313
+ ...localLibraryLines('Agent Memory', snapshot.localMemories, 'No Agent memory yet. Create one here with Create memory.', workspace.selectedLocalLibraryItem('memory')?.id ?? null),
309
314
  );
310
315
  } else if (category.id === 'personas') {
311
316
  base.push(
@@ -9,7 +9,6 @@ import { renderSelectionModalOverlay } from './selection-modal-overlay.ts';
9
9
  import { renderSearchOverlay } from './search-overlay.ts';
10
10
  import { renderHistorySearchOverlay } from './history-search-overlay.ts';
11
11
  import { renderProcessModal } from './process-modal.ts';
12
- import { renderAgentDetailModal } from './agent-detail-modal.ts';
13
12
  import { renderLiveTailModal } from './live-tail-modal.ts';
14
13
  import { renderContextInspector } from './context-inspector.ts';
15
14
  import { renderSettingsModal } from './settings-modal.ts';
@@ -74,11 +73,6 @@ export function applyConversationOverlays(
74
73
  next = overlayViewportBottom(next, lines, conversationWidth, viewportHeight, bottomDockInset);
75
74
  }
76
75
 
77
- if (input.agentDetailModal.active) {
78
- const lines = renderAgentDetailModal(input.agentDetailModal, conversationWidth);
79
- next = overlayViewportBottom(next, lines, conversationWidth, viewportHeight, bottomDockInset);
80
- }
81
-
82
76
  if (input.liveTailModal.active) {
83
77
  const lines = renderLiveTailModal(input.liveTailModal, conversationWidth, viewportHeight);
84
78
  next = overlayViewportBottom(next, lines, conversationWidth, viewportHeight, bottomDockInset);
@@ -1,28 +1,16 @@
1
1
  import { type Line } from '../types/grid.ts';
2
2
  import { ModalFactory } from './modal-factory.ts';
3
3
  import type { ProcessManager } from '@pellux/goodvibes-sdk/platform/tools';
4
- import type { AgentManager } from '@pellux/goodvibes-sdk/platform/tools';
5
4
  import type { ProcessEntry } from './process-modal.ts';
6
5
  import { getOverlaySurfaceMetrics, getStableOverlayContentRows } from './overlay-viewport.ts';
7
6
 
8
7
  export interface LiveTailModalDeps {
9
- readonly agentManager: Pick<AgentManager, 'getStatus'>;
10
8
  readonly processManager: Pick<ProcessManager, 'stop' | 'getOutput'>;
11
9
  }
12
10
 
13
- // ─── LiveTailModal ────────────────────────────────────────────────────────────
14
-
15
- /**
16
- * LiveTailModal — manages state for the live output peek modal.
17
- *
18
- * Shows streaming stdout/stderr from a selected shell process or agent
19
- * progress notes. Auto-scrolls to the bottom unless the user scrolled up.
20
- */
21
11
  export class LiveTailModal {
22
12
  public active = false;
23
13
  public entry: ProcessEntry | null = null;
24
-
25
- /** Number of lines scrolled up from the bottom (0 = at bottom). */
26
14
  public scrollOffset = 0;
27
15
 
28
16
  constructor(private readonly deps: LiveTailModalDeps) {}
@@ -40,64 +28,27 @@ export class LiveTailModal {
40
28
  }
41
29
 
42
30
  scrollUp(): void {
43
- this.scrollOffset++;
31
+ this.scrollOffset += 1;
44
32
  }
45
33
 
46
34
  scrollDown(): void {
47
35
  this.scrollOffset = Math.max(0, this.scrollOffset - 1);
48
36
  }
49
37
 
50
- /**
51
- * Stop the current shell process when it is an exec entry.
52
- * Agent entries are read-only in GoodVibes Agent; build execution and
53
- * cancellation belong to GoodVibes TUI/shared-session owners.
54
- */
55
- killProcess(): boolean {
38
+ stopProcess(): boolean {
56
39
  if (!this.entry) return false;
57
-
58
- if (this.entry.type === 'exec') {
59
- return this.deps.processManager.stop(this.entry.id);
60
- }
61
- return false;
40
+ return this.deps.processManager.stop(this.entry.id);
62
41
  }
63
42
 
64
- /** Retrieve the current output text for the watched process. */
65
43
  getOutput(): string {
66
44
  if (!this.entry) return '';
67
-
68
- if (this.entry.type === 'exec') {
69
- const out = this.deps.processManager.getOutput(this.entry.id);
70
- if (!out) return '';
71
- const combined = [out.stdout, out.stderr].filter(Boolean).join('\n').trim();
72
- return combined || '(no output yet)';
73
- } else {
74
- // For agents, show progress note and status
75
- const rec = this.deps.agentManager.getStatus(this.entry.id);
76
- if (!rec) return '(process not found)';
77
- const parts: string[] = [
78
- `Task: ${rec.task}`,
79
- `Status: ${rec.status}`,
80
- `Tool calls: ${rec.toolCallCount}`,
81
- ];
82
- if (rec.progress) parts.push(`Progress: ${rec.progress}`);
83
- if (rec.error) parts.push(`Error: ${rec.error}`);
84
- return parts.join('\n');
85
- }
45
+ const output = this.deps.processManager.getOutput(this.entry.id);
46
+ if (!output) return '';
47
+ const combined = [output.stdout, output.stderr].filter(Boolean).join('\n').trim();
48
+ return combined || '(no output yet)';
86
49
  }
87
50
  }
88
51
 
89
- // ─── renderLiveTailModal ──────────────────────────────────────────────────────
90
-
91
- /**
92
- * Render the live-tail peek modal as Line[] for overlay in the viewport.
93
- *
94
- * Shows a scrollable view of the process output. scrollOffset=0 means the
95
- * bottom of the output is visible (auto-scroll behaviour).
96
- *
97
- * @param modal LiveTailModal state
98
- * @param width Terminal width
99
- * @param maxOutputLines Maximum lines to show inside the box (default: 16)
100
- */
101
52
  export function renderLiveTailModal(
102
53
  modal: LiveTailModal,
103
54
  width: number,
@@ -115,35 +66,25 @@ export function renderLiveTailModal(
115
66
 
116
67
  const output = modal.getOutput();
117
68
  const allLines = output.split('\n');
118
-
119
- // Apply scroll: scrollOffset=0 → show tail; larger → scroll toward head
120
69
  const totalLines = allLines.length;
121
- // Clamp scrollOffset so we never scroll past the top of the content
122
70
  const maxScroll = Math.max(0, totalLines - maxOutputLines);
123
71
  const clampedOffset = Math.min(modal.scrollOffset, maxScroll);
124
72
  const endIdx = Math.max(maxOutputLines, totalLines - clampedOffset);
125
73
  const startIdx = Math.max(0, endIdx - maxOutputLines);
126
74
  const visibleLines = allLines.slice(startIdx, endIdx);
127
75
 
128
- const typeTag = entry.type === 'agent' ? '[agent]' : '[exec]';
129
76
  const maxLabelW = Math.max(20, width - 30);
130
- const title = `${typeTag} ${entry.label.slice(0, maxLabelW)}`;
131
-
132
- // Build scroll indicator for text section header
77
+ const title = `[exec] ${entry.label.slice(0, maxLabelW)}`;
133
78
  const scrollInfo = totalLines > maxOutputLines
134
79
  ? ` Lines ${startIdx + 1}-${Math.min(endIdx, totalLines)} of ${totalLines} [Up/Down] Scroll`
135
80
  : '';
136
81
 
137
- const contentText = visibleLines.join('\n') || '(no output yet)';
138
-
139
82
  const sections: import('./modal-factory.ts').ModalSection[] = [];
140
-
141
83
  if (scrollInfo) {
142
84
  sections.push({ type: 'text', content: scrollInfo });
143
85
  sections.push({ type: 'separator' });
144
86
  }
145
-
146
- sections.push({ type: 'text', content: contentText });
87
+ sections.push({ type: 'text', content: visibleLines.join('\n') || '(no output yet)' });
147
88
 
148
89
  return ModalFactory.createModal({
149
90
  title,
@@ -151,6 +92,6 @@ export function renderLiveTailModal(
151
92
  margin: 2,
152
93
  targetContentRows,
153
94
  sections,
154
- hints: ['[Up/Down] Scroll', '[k] Stop exec only', '[Esc] Back'],
95
+ hints: ['[Up/Down] Scroll', '[k] Stop process', '[Esc] Back'],
155
96
  }, width);
156
97
  }