@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.
- package/CHANGELOG.md +14 -0
- package/README.md +10 -0
- package/docs/README.md +1 -1
- package/docs/getting-started.md +17 -3
- package/package.json +1 -1
- package/src/agent/memory-safety.ts +16 -0
- package/src/cli/help.ts +86 -0
- package/src/cli/local-library-command.ts +516 -0
- package/src/cli/management.ts +17 -0
- package/src/cli/memory-command.ts +630 -0
- package/src/cli/package-verification.ts +10 -0
- package/src/cli/parser.ts +8 -0
- package/src/cli/types.ts +3 -0
- package/src/input/agent-workspace-activation.ts +170 -0
- package/src/input/agent-workspace-categories.ts +8 -1
- package/src/input/agent-workspace-editors.ts +36 -0
- package/src/input/agent-workspace-memory-editor.ts +88 -0
- package/src/input/agent-workspace-setup.ts +7 -5
- package/src/input/agent-workspace-snapshot.ts +40 -4
- package/src/input/agent-workspace-token.ts +51 -0
- package/src/input/agent-workspace-types.ts +13 -3
- package/src/input/agent-workspace.ts +130 -185
- package/src/input/feed-context-factory.ts +1 -3
- package/src/input/handler-feed.ts +1 -4
- package/src/input/handler-interactions.ts +0 -1
- package/src/input/handler-modal-stack.ts +0 -1
- package/src/input/handler-modal-token-routes.ts +0 -11
- package/src/input/handler-picker-routes.ts +11 -20
- package/src/input/handler-ui-state.ts +0 -6
- package/src/input/handler.ts +1 -17
- package/src/main.ts +0 -6
- package/src/panels/builtin/agent.ts +0 -17
- package/src/panels/index.ts +0 -2
- package/src/renderer/agent-workspace.ts +8 -3
- package/src/renderer/conversation-overlays.ts +0 -6
- package/src/renderer/live-tail-modal.ts +10 -69
- package/src/renderer/process-modal.ts +28 -530
- package/src/runtime/bootstrap-core.ts +1 -1
- package/src/runtime/services.ts +3 -4
- package/src/tools/{wrfc-agent-guard.ts → agent-tool-policy-guard.ts} +0 -6
- package/src/version.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +0 -521
- package/src/panels/agent-inspector-shared.ts +0 -94
- package/src/panels/agent-logs-panel.ts +0 -559
- package/src/panels/agent-logs-shared.ts +0 -129
- package/src/renderer/agent-detail-modal.ts +0 -331
- package/src/renderer/process-summary.ts +0 -67
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { MemoryApi } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
2
|
+
import type { MemoryRecord } from '@pellux/goodvibes-sdk/platform/state';
|
|
2
3
|
import type { ShellPathService } from '@/runtime/index.ts';
|
|
3
4
|
import type { CommandContext } from './command-registry.ts';
|
|
4
5
|
import { AgentPersonaRegistry } from '../agent/persona-registry.ts';
|
|
5
6
|
import { AgentRoutineRegistry } from '../agent/routine-registry.ts';
|
|
6
7
|
import { createAgentRuntimeProfile, type AgentRuntimeProfileInfo } from '../agent/runtime-profile.ts';
|
|
7
8
|
import { AgentSkillRegistry } from '../agent/skill-registry.ts';
|
|
9
|
+
import { activateAgentWorkspaceSelection } from './agent-workspace-activation.ts';
|
|
8
10
|
import { AGENT_WORKSPACE_CATEGORIES } from './agent-workspace-categories.ts';
|
|
9
|
-
import { createDeleteEditor,
|
|
11
|
+
import { createDeleteEditor, createMemoryUpdateEditor, createPersonaUpdateEditor, createRoutineUpdateEditor, createSkillUpdateEditor, editorCategoryId, isAffirmative, splitList } from './agent-workspace-editors.ts';
|
|
12
|
+
import { deleteAgentWorkspaceMemoryEditor, submitAgentWorkspaceMemoryEditor } from './agent-workspace-memory-editor.ts';
|
|
10
13
|
import { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
|
|
11
14
|
import type { AgentWorkspaceAction, AgentWorkspaceActionResult, AgentWorkspaceCategory, AgentWorkspaceCommandDispatcher, AgentWorkspaceEditorField, AgentWorkspaceFocusPane, AgentWorkspaceLocalEditor, AgentWorkspaceLocalEditorKind, AgentWorkspaceLocalLibraryItem, AgentWorkspaceLocalOperation, AgentWorkspaceRuntimeSnapshot } from './agent-workspace-types.ts';
|
|
12
15
|
|
|
@@ -26,12 +29,7 @@ export type {
|
|
|
26
29
|
} from './agent-workspace-types.ts';
|
|
27
30
|
export { AGENT_WORKSPACE_MODAL_NAME } from './agent-workspace-types.ts';
|
|
28
31
|
export { buildAgentWorkspaceRuntimeSnapshot } from './agent-workspace-snapshot.ts';
|
|
29
|
-
|
|
30
|
-
const trimmed = command.trim().replace(/^\//, '');
|
|
31
|
-
if (!trimmed) return { name: '', args: [] };
|
|
32
|
-
const parts = trimmed.split(/\s+/);
|
|
33
|
-
return { name: parts[0] ?? '', args: parts.slice(1) };
|
|
34
|
-
}
|
|
32
|
+
export { handleAgentWorkspaceToken } from './agent-workspace-token.ts';
|
|
35
33
|
|
|
36
34
|
export class AgentWorkspace {
|
|
37
35
|
public active = false;
|
|
@@ -43,6 +41,7 @@ export class AgentWorkspace {
|
|
|
43
41
|
public lastActionResult: AgentWorkspaceActionResult | null = null;
|
|
44
42
|
public localEditor: AgentWorkspaceLocalEditor | null = null;
|
|
45
43
|
private readonly selectedLibraryItemIndexes: Record<AgentWorkspaceLocalEditorKind, number> = {
|
|
44
|
+
memory: 0,
|
|
46
45
|
persona: 0,
|
|
47
46
|
skill: 0,
|
|
48
47
|
routine: 0,
|
|
@@ -207,147 +206,39 @@ export class AgentWorkspace {
|
|
|
207
206
|
this.replaceEditorField(editor.selectedFieldIndex, characters.join(''), editor.message);
|
|
208
207
|
}
|
|
209
208
|
|
|
210
|
-
submitEditorFieldOrForm(): void {
|
|
209
|
+
submitEditorFieldOrForm(requestRender?: () => void): void {
|
|
211
210
|
const editor = this.localEditor;
|
|
212
211
|
if (!editor) return;
|
|
213
212
|
if (editor.selectedFieldIndex < editor.fields.length - 1) {
|
|
214
213
|
this.moveEditorField(1);
|
|
215
214
|
return;
|
|
216
215
|
}
|
|
217
|
-
this.submitLocalEditor();
|
|
216
|
+
this.submitLocalEditor(requestRender);
|
|
218
217
|
}
|
|
219
218
|
|
|
220
|
-
activateSelected(): void {
|
|
221
|
-
|
|
222
|
-
this.submitEditorFieldOrForm();
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
if (this.focusPane === 'categories') {
|
|
226
|
-
this.focusActions();
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const action = this.selectedAction;
|
|
230
|
-
if (!action) return;
|
|
231
|
-
if (action.kind === 'editor' && action.editorKind) {
|
|
232
|
-
this.localEditor = action.editorKind === 'profile'
|
|
233
|
-
? createProfileEditor(this.runtimeSnapshot?.runtimeStarterTemplates ?? [])
|
|
234
|
-
: createLocalEditor(action.editorKind);
|
|
235
|
-
this.status = `Editing ${this.localEditor.title}.`;
|
|
236
|
-
this.lastActionResult = {
|
|
237
|
-
kind: 'guidance',
|
|
238
|
-
title: this.localEditor.title,
|
|
239
|
-
detail: this.localEditor.message,
|
|
240
|
-
safety: action.safety,
|
|
241
|
-
};
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
if (action.kind === 'local-selection' && action.localKind) {
|
|
245
|
-
this.moveLocalLibraryItemSelection(action.localKind, action.selectionDelta ?? 0);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
248
|
-
if (action.kind === 'local-operation' && action.localOperation) {
|
|
249
|
-
this.applyLocalLibraryOperation(action.localOperation);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (action.kind === 'guidance' || !action.command) {
|
|
253
|
-
if (action.kind === 'workspace' && action.targetCategoryId) {
|
|
254
|
-
const targetIndex = this.categories.findIndex((category) => category.id === action.targetCategoryId);
|
|
255
|
-
if (targetIndex >= 0) {
|
|
256
|
-
this.selectedCategoryIndex = targetIndex;
|
|
257
|
-
this.selectedActionIndex = 0;
|
|
258
|
-
this.focusActions();
|
|
259
|
-
this.status = `Opened ${this.selectedCategory.label}.`;
|
|
260
|
-
this.lastActionResult = {
|
|
261
|
-
kind: 'refreshed',
|
|
262
|
-
title: `Opened ${this.selectedCategory.label}`,
|
|
263
|
-
detail: action.detail,
|
|
264
|
-
safety: action.safety,
|
|
265
|
-
};
|
|
266
|
-
this.clampSelection();
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
this.status = `Workspace area unavailable: ${action.targetCategoryId}.`;
|
|
270
|
-
this.lastActionResult = {
|
|
271
|
-
kind: 'error',
|
|
272
|
-
title: 'Workspace area unavailable',
|
|
273
|
-
detail: `No Agent workspace category exists for ${action.targetCategoryId}.`,
|
|
274
|
-
safety: action.safety,
|
|
275
|
-
};
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
this.status = action.detail;
|
|
279
|
-
this.lastActionResult = {
|
|
280
|
-
kind: 'guidance',
|
|
281
|
-
title: action.label,
|
|
282
|
-
detail: action.detail,
|
|
283
|
-
safety: action.safety,
|
|
284
|
-
};
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
if (action.safety === 'blocked') {
|
|
288
|
-
this.status = `Blocked here: ${action.label}.`;
|
|
289
|
-
this.lastActionResult = {
|
|
290
|
-
kind: 'blocked',
|
|
291
|
-
title: `${action.label} is blocked in Agent`,
|
|
292
|
-
detail: action.detail,
|
|
293
|
-
command: action.command,
|
|
294
|
-
safety: action.safety,
|
|
295
|
-
};
|
|
296
|
-
return;
|
|
297
|
-
}
|
|
298
|
-
const parsed = parseCommand(action.command);
|
|
299
|
-
if (!parsed.name) {
|
|
300
|
-
this.status = `No command is configured for ${action.label}.`;
|
|
301
|
-
this.lastActionResult = {
|
|
302
|
-
kind: 'error',
|
|
303
|
-
title: 'Command unavailable',
|
|
304
|
-
detail: `No command is configured for ${action.label}.`,
|
|
305
|
-
safety: action.safety,
|
|
306
|
-
};
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
if (/<[^>\s]+(?:\s+[^>]*)?>/.test(action.command)) {
|
|
310
|
-
this.status = `Placeholder command not dispatched: ${action.command}.`;
|
|
311
|
-
this.lastActionResult = {
|
|
312
|
-
kind: 'guidance',
|
|
313
|
-
title: `${action.label} needs details`,
|
|
314
|
-
detail: 'This action is a command template. Close the workspace and run it with real task text instead of placeholder values.',
|
|
315
|
-
command: action.command,
|
|
316
|
-
safety: action.safety,
|
|
317
|
-
};
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
if (!this.context?.executeCommand || !this.dispatchCommand) {
|
|
321
|
-
this.status = `Command dispatch is not available for ${action.command}.`;
|
|
322
|
-
this.lastActionResult = {
|
|
323
|
-
kind: 'error',
|
|
324
|
-
title: 'Command dispatch unavailable',
|
|
325
|
-
detail: `The command ${action.command} cannot be opened from this runtime.`,
|
|
326
|
-
command: action.command,
|
|
327
|
-
safety: action.safety,
|
|
328
|
-
};
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
|
-
this.status = `Opening ${action.command}.`;
|
|
332
|
-
this.lastActionResult = {
|
|
333
|
-
kind: 'dispatched',
|
|
334
|
-
title: `Opening ${action.label}`,
|
|
335
|
-
detail: 'The workspace handed this safe or read-only command to the shell-owned command router.',
|
|
336
|
-
command: action.command,
|
|
337
|
-
safety: action.safety,
|
|
338
|
-
};
|
|
339
|
-
this.dispatchCommand(action.command);
|
|
219
|
+
activateSelected(requestRender?: () => void): void {
|
|
220
|
+
activateAgentWorkspaceSelection(this, requestRender);
|
|
340
221
|
}
|
|
341
222
|
|
|
342
|
-
|
|
223
|
+
hasCommandDispatch(): boolean {
|
|
224
|
+
return Boolean(this.context?.executeCommand && this.dispatchCommand);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
dispatchWorkspaceCommand(command: string): void {
|
|
228
|
+
this.dispatchCommand?.(command);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
clampSelection(): void {
|
|
343
232
|
this.selectedCategoryIndex = Math.max(0, Math.min(this.selectedCategoryIndex, this.categories.length - 1));
|
|
344
233
|
this.selectedActionIndex = Math.max(0, Math.min(this.selectedActionIndex, this.actions.length - 1));
|
|
234
|
+
this.clampLocalLibrarySelection('memory');
|
|
345
235
|
this.clampLocalLibrarySelection('persona');
|
|
346
236
|
this.clampLocalLibrarySelection('skill');
|
|
347
237
|
this.clampLocalLibrarySelection('routine');
|
|
348
238
|
}
|
|
349
239
|
|
|
350
240
|
private localLibraryItems(kind: AgentWorkspaceLocalEditorKind): readonly AgentWorkspaceLocalLibraryItem[] {
|
|
241
|
+
if (kind === 'memory') return this.runtimeSnapshot?.localMemories ?? [];
|
|
351
242
|
if (kind === 'persona') return this.runtimeSnapshot?.localPersonas ?? [];
|
|
352
243
|
if (kind === 'skill') return this.runtimeSnapshot?.localSkills ?? [];
|
|
353
244
|
if (kind === 'profile') return [];
|
|
@@ -361,7 +252,7 @@ export class AgentWorkspace {
|
|
|
361
252
|
: Math.max(0, Math.min(this.selectedLibraryItemIndexes[kind], length - 1));
|
|
362
253
|
}
|
|
363
254
|
|
|
364
|
-
|
|
255
|
+
moveLocalLibraryItemSelection(kind: AgentWorkspaceLocalEditorKind, delta: number): void {
|
|
365
256
|
const items = this.localLibraryItems(kind);
|
|
366
257
|
if (items.length === 0) {
|
|
367
258
|
this.status = `No local ${kind} records to select.`;
|
|
@@ -384,7 +275,7 @@ export class AgentWorkspace {
|
|
|
384
275
|
};
|
|
385
276
|
}
|
|
386
277
|
|
|
387
|
-
|
|
278
|
+
applyLocalLibraryOperation(operation: AgentWorkspaceLocalOperation): void {
|
|
388
279
|
const shellPaths = this.context?.workspace?.shellPaths;
|
|
389
280
|
if (!shellPaths) {
|
|
390
281
|
this.status = 'Local Agent registry files are unavailable.';
|
|
@@ -412,7 +303,29 @@ export class AgentWorkspace {
|
|
|
412
303
|
};
|
|
413
304
|
return;
|
|
414
305
|
}
|
|
415
|
-
if (operation === '
|
|
306
|
+
if (operation === 'memory-edit') {
|
|
307
|
+
const memory = this.memoryApi();
|
|
308
|
+
const record = memory.get(selected.id);
|
|
309
|
+
if (!record) throw new Error(`Unknown Agent memory: ${selected.id}`);
|
|
310
|
+
this.localEditor = createMemoryUpdateEditor(record);
|
|
311
|
+
this.status = `Editing memory: ${record.id}.`;
|
|
312
|
+
this.lastActionResult = {
|
|
313
|
+
kind: 'guidance',
|
|
314
|
+
title: this.localEditor.title,
|
|
315
|
+
detail: this.localEditor.message,
|
|
316
|
+
safety: 'safe',
|
|
317
|
+
};
|
|
318
|
+
} else if (operation === 'memory-review') {
|
|
319
|
+
const record = this.memoryApi().review(selected.id, { state: 'reviewed', confidence: selected.confidence ?? 100, reviewedBy: 'operator' });
|
|
320
|
+
if (!record) throw new Error(`Unknown Agent memory: ${selected.id}`);
|
|
321
|
+
this.finishLocalOperation('memory', `Reviewed memory ${record.id}`, `${record.summary} is marked reviewed.`);
|
|
322
|
+
} else if (operation === 'memory-stale') {
|
|
323
|
+
const record = this.memoryApi().review(selected.id, { state: 'stale', staleReason: 'Marked stale from Agent workspace', reviewedBy: 'operator' });
|
|
324
|
+
if (!record) throw new Error(`Unknown Agent memory: ${selected.id}`);
|
|
325
|
+
this.finishLocalOperation('memory', `Marked memory stale ${record.id}`, `${record.summary} needs review before reuse.`);
|
|
326
|
+
} else if (operation === 'memory-delete') {
|
|
327
|
+
this.openDeleteEditor('memory', selected);
|
|
328
|
+
} else if (operation === 'persona-edit') {
|
|
416
329
|
const registry = AgentPersonaRegistry.fromShellPaths(shellPaths);
|
|
417
330
|
const persona = registry.get(selected.id);
|
|
418
331
|
if (!persona) throw new Error(`Unknown persona: ${selected.id}`);
|
|
@@ -492,11 +405,18 @@ export class AgentWorkspace {
|
|
|
492
405
|
}
|
|
493
406
|
|
|
494
407
|
private selectedItemForOperation(operation: AgentWorkspaceLocalOperation): AgentWorkspaceLocalLibraryItem | null {
|
|
408
|
+
if (operation.startsWith('memory-')) return this.selectedLocalLibraryItem('memory');
|
|
495
409
|
if (operation.startsWith('persona-')) return this.selectedLocalLibraryItem('persona');
|
|
496
410
|
if (operation.startsWith('skill-')) return this.selectedLocalLibraryItem('skill');
|
|
497
411
|
return this.selectedLocalLibraryItem('routine');
|
|
498
412
|
}
|
|
499
413
|
|
|
414
|
+
private memoryApi(): MemoryApi {
|
|
415
|
+
const memory = this.context?.clients?.agentKnowledgeApi?.memory;
|
|
416
|
+
if (!memory) throw new Error('Agent Memory API is unavailable; refusing default Knowledge/Wiki or non-Agent fallback.');
|
|
417
|
+
return memory;
|
|
418
|
+
}
|
|
419
|
+
|
|
500
420
|
private finishLocalOperation(kind: AgentWorkspaceLocalEditorKind, title: string, detail: string): void {
|
|
501
421
|
this.runtimeSnapshot = this.context ? buildAgentWorkspaceRuntimeSnapshot(this.context) : this.runtimeSnapshot;
|
|
502
422
|
this.clampLocalLibrarySelection(kind);
|
|
@@ -538,7 +458,7 @@ export class AgentWorkspace {
|
|
|
538
458
|
return editor.fields.find((field) => field.required && field.value.trim().length === 0) ?? null;
|
|
539
459
|
}
|
|
540
460
|
|
|
541
|
-
private submitLocalEditor(): void {
|
|
461
|
+
private submitLocalEditor(requestRender?: () => void): void {
|
|
542
462
|
const editor = this.localEditor;
|
|
543
463
|
if (!editor) return;
|
|
544
464
|
const missing = this.missingEditorField();
|
|
@@ -552,6 +472,26 @@ export class AgentWorkspace {
|
|
|
552
472
|
this.status = `${missing.label} is required.`;
|
|
553
473
|
return;
|
|
554
474
|
}
|
|
475
|
+
if (editor.kind === 'memory') {
|
|
476
|
+
if (editor.mode === 'delete') {
|
|
477
|
+
try {
|
|
478
|
+
this.submitMemoryDeleteEditor(editor);
|
|
479
|
+
} catch (error) {
|
|
480
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
481
|
+
this.localEditor = { ...editor, message: detail };
|
|
482
|
+
this.status = detail;
|
|
483
|
+
this.lastActionResult = {
|
|
484
|
+
kind: 'error',
|
|
485
|
+
title: `${editor.title} failed`,
|
|
486
|
+
detail,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
requestRender?.();
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
void this.submitMemoryEditor(editor).finally(() => requestRender?.());
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
555
495
|
const shellPaths = this.context?.workspace?.shellPaths;
|
|
556
496
|
if (!shellPaths) {
|
|
557
497
|
this.localEditor = { ...editor, message: 'Cannot save because Agent shell paths are unavailable.' };
|
|
@@ -679,7 +619,11 @@ export class AgentWorkspace {
|
|
|
679
619
|
this.status = 'Deletion not confirmed.';
|
|
680
620
|
return;
|
|
681
621
|
}
|
|
682
|
-
if (editor.kind === '
|
|
622
|
+
if (editor.kind === 'memory') {
|
|
623
|
+
const removed = this.memoryApi().delete(expectedId);
|
|
624
|
+
if (!removed) throw new Error(`Unknown Agent memory: ${expectedId}`);
|
|
625
|
+
this.finishLocalDelete(editor.kind, expectedId, expectedId);
|
|
626
|
+
} else if (editor.kind === 'persona') {
|
|
683
627
|
const removed = AgentPersonaRegistry.fromShellPaths(shellPaths).deletePersona(expectedId);
|
|
684
628
|
this.finishLocalDelete(editor.kind, removed.id, removed.name);
|
|
685
629
|
} else if (editor.kind === 'skill') {
|
|
@@ -691,6 +635,56 @@ export class AgentWorkspace {
|
|
|
691
635
|
}
|
|
692
636
|
}
|
|
693
637
|
|
|
638
|
+
private submitMemoryDeleteEditor(editor: AgentWorkspaceLocalEditor): void {
|
|
639
|
+
const expectedId = editor.recordId ?? '';
|
|
640
|
+
const confirmedId = this.editorField('confirm');
|
|
641
|
+
const removed = deleteAgentWorkspaceMemoryEditor(editor, confirmedId, this.memoryApi());
|
|
642
|
+
if (!removed) {
|
|
643
|
+
this.localEditor = {
|
|
644
|
+
...editor,
|
|
645
|
+
message: `Deletion not confirmed. Type ${expectedId} exactly, then press Enter.`,
|
|
646
|
+
};
|
|
647
|
+
this.status = 'Deletion not confirmed.';
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
this.finishLocalDelete(editor.kind, removed.id, removed.name);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
private async submitMemoryEditor(editor: AgentWorkspaceLocalEditor): Promise<void> {
|
|
654
|
+
try {
|
|
655
|
+
this.status = 'Saving Agent memory...';
|
|
656
|
+
const result = await submitAgentWorkspaceMemoryEditor(editor, this.memoryApi(), (id) => this.editorField(id));
|
|
657
|
+
this.finishMemoryEditor(result.record, result.verb);
|
|
658
|
+
} catch (error) {
|
|
659
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
660
|
+
this.localEditor = { ...editor, message: detail };
|
|
661
|
+
this.status = detail;
|
|
662
|
+
this.lastActionResult = {
|
|
663
|
+
kind: 'error',
|
|
664
|
+
title: `${editor.title} failed`,
|
|
665
|
+
detail,
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
private finishMemoryEditor(record: MemoryRecord, verb: 'Created' | 'Updated'): void {
|
|
671
|
+
this.localEditor = null;
|
|
672
|
+
this.runtimeSnapshot = this.context ? buildAgentWorkspaceRuntimeSnapshot(this.context) : this.runtimeSnapshot;
|
|
673
|
+
const categoryIndex = this.categories.findIndex((category) => category.id === 'memory');
|
|
674
|
+
if (categoryIndex >= 0) {
|
|
675
|
+
this.selectedCategoryIndex = categoryIndex;
|
|
676
|
+
this.selectedActionIndex = 0;
|
|
677
|
+
}
|
|
678
|
+
this.status = `${verb} memory: ${record.summary}.`;
|
|
679
|
+
this.lastActionResult = {
|
|
680
|
+
kind: 'refreshed',
|
|
681
|
+
title: `${verb} memory`,
|
|
682
|
+
detail: `${record.summary} (${record.id}) was saved to Agent-owned memory only.`,
|
|
683
|
+
safety: 'safe',
|
|
684
|
+
};
|
|
685
|
+
this.clampSelection();
|
|
686
|
+
}
|
|
687
|
+
|
|
694
688
|
private finishLocalEditor(kind: AgentWorkspaceLocalEditorKind, id: string, name: string, verb: 'Created' | 'Updated'): void {
|
|
695
689
|
this.localEditor = null;
|
|
696
690
|
const categoryId = editorCategoryId(kind);
|
|
@@ -749,52 +743,3 @@ export class AgentWorkspace {
|
|
|
749
743
|
this.clampSelection();
|
|
750
744
|
}
|
|
751
745
|
}
|
|
752
|
-
|
|
753
|
-
export function handleAgentWorkspaceToken(
|
|
754
|
-
workspace: AgentWorkspace,
|
|
755
|
-
token: InputToken,
|
|
756
|
-
handleEscape: () => void,
|
|
757
|
-
requestRender: () => void,
|
|
758
|
-
): boolean {
|
|
759
|
-
if (!workspace.active) return false;
|
|
760
|
-
|
|
761
|
-
if (workspace.localEditor) {
|
|
762
|
-
if (token.type === 'text') {
|
|
763
|
-
workspace.appendEditorText(token.value);
|
|
764
|
-
} else if (token.type === 'key') {
|
|
765
|
-
if (token.logicalName === 'escape') workspace.cancelLocalEditor();
|
|
766
|
-
else if (token.logicalName === 'enter') workspace.submitEditorFieldOrForm();
|
|
767
|
-
else if (token.logicalName === 'tab' || token.logicalName === 'down') workspace.moveEditorField(1);
|
|
768
|
-
else if (token.logicalName === 'up') workspace.moveEditorField(-1);
|
|
769
|
-
else if (token.logicalName === 'backspace' || token.logicalName === 'delete') workspace.editorBackspace();
|
|
770
|
-
else if (token.logicalName === 'j' && token.ctrl === true) workspace.appendEditorNewline();
|
|
771
|
-
}
|
|
772
|
-
requestRender();
|
|
773
|
-
return true;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
if (token.type === 'key') {
|
|
777
|
-
if (token.logicalName === 'escape') {
|
|
778
|
-
handleEscape();
|
|
779
|
-
return true;
|
|
780
|
-
}
|
|
781
|
-
if (token.logicalName === 'enter' || token.logicalName === 'space') workspace.activateSelected();
|
|
782
|
-
else if (token.logicalName === 'left') workspace.focusCategories();
|
|
783
|
-
else if (token.logicalName === 'right') workspace.focusActions();
|
|
784
|
-
else if (token.logicalName === 'up') workspace.moveUp();
|
|
785
|
-
else if (token.logicalName === 'down') workspace.moveDown();
|
|
786
|
-
else if (token.logicalName === 'tab') workspace.toggleFocusPane();
|
|
787
|
-
else if (token.logicalName === 'home') workspace.jumpHome();
|
|
788
|
-
else if (token.logicalName === 'end') workspace.jumpEnd();
|
|
789
|
-
} else if (token.type === 'text') {
|
|
790
|
-
if (token.value === 'h') workspace.focusCategories();
|
|
791
|
-
else if (token.value === 'l') workspace.focusActions();
|
|
792
|
-
else if (token.value === 'j') workspace.moveDown();
|
|
793
|
-
else if (token.value === 'k') workspace.moveUp();
|
|
794
|
-
else if (token.value === 'r' || token.value === 'R') workspace.refreshRuntimeSnapshot();
|
|
795
|
-
else if (token.value === ' ') workspace.activateSelected();
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
requestRender();
|
|
799
|
-
return true;
|
|
800
|
-
}
|
|
@@ -25,7 +25,6 @@ import type { ConversationManager } from '../core/conversation';
|
|
|
25
25
|
import type { ProcessModal } from '../renderer/process-modal.ts';
|
|
26
26
|
import type { LiveTailModal } from '../renderer/live-tail-modal.ts';
|
|
27
27
|
import type { BlockActionsMenu } from '../renderer/block-actions.ts';
|
|
28
|
-
import type { AgentDetailModal } from '../renderer/agent-detail-modal.ts';
|
|
29
28
|
import type { ContextInspectorModal } from '../renderer/context-inspector.ts';
|
|
30
29
|
import type { BookmarkModal } from './bookmark-modal.ts';
|
|
31
30
|
import type { SettingsModal } from './settings-modal.ts';
|
|
@@ -83,7 +82,7 @@ export interface FeedContextMutableInit {
|
|
|
83
82
|
* - `selectionModal`, `bookmarkModal`, `settingsModal`, `sessionPickerModal`,
|
|
84
83
|
* `profilePickerModal` — modal objects constructed once
|
|
85
84
|
* - `filePicker`, `modelPicker`, `processModal`, `liveTailModal`,
|
|
86
|
-
* `
|
|
85
|
+
* `contextInspectorModal`, `blockActionsMenu`,
|
|
87
86
|
* `searchManager`, `historySearch`, `onboardingWizard` — service objects constructed once
|
|
88
87
|
* - `panelManager`, `keybindingsManager` — from uiServices, stable
|
|
89
88
|
* - `modalStack` — reference to the handler's shared array
|
|
@@ -117,7 +116,6 @@ export interface FeedContextStableRefs {
|
|
|
117
116
|
onboardingWizard: OnboardingWizardController;
|
|
118
117
|
processModal: ProcessModal;
|
|
119
118
|
liveTailModal: LiveTailModal;
|
|
120
|
-
agentDetailModal: AgentDetailModal;
|
|
121
119
|
contextInspectorModal: ContextInspectorModal;
|
|
122
120
|
blockActionsMenu: BlockActionsMenu;
|
|
123
121
|
searchManager: SearchManager;
|
|
@@ -12,7 +12,6 @@ import type { BlockMeta, ConversationManager } from '../core/conversation';
|
|
|
12
12
|
import { ProcessModal } from '../renderer/process-modal.ts';
|
|
13
13
|
import { LiveTailModal } from '../renderer/live-tail-modal.ts';
|
|
14
14
|
import { BlockActionsMenu } from '../renderer/block-actions.ts';
|
|
15
|
-
import { AgentDetailModal } from '../renderer/agent-detail-modal.ts';
|
|
16
15
|
import { ContextInspectorModal } from '../renderer/context-inspector.ts';
|
|
17
16
|
import { BookmarkModal } from './bookmark-modal.ts';
|
|
18
17
|
import { SettingsModal } from './settings-modal.ts';
|
|
@@ -73,7 +72,7 @@ import type { ModelPickerTarget } from './model-picker.ts';
|
|
|
73
72
|
* - `selectionModal`, `bookmarkModal`, `settingsModal`, `sessionPickerModal`,
|
|
74
73
|
* `profilePickerModal` — modal objects constructed once in InputHandler constructor
|
|
75
74
|
* - `filePicker`, `modelPicker`, `onboardingWizard`, `processModal`, `liveTailModal`,
|
|
76
|
-
* `
|
|
75
|
+
* `contextInspectorModal`, `blockActionsMenu`, `searchManager`, `historySearch` —
|
|
77
76
|
* service objects constructed once
|
|
78
77
|
* - `panelManager`, `keybindingsManager` — from uiServices, stable for app lifetime
|
|
79
78
|
* - `modalStack` — reference to the handler's shared array (mutated in place)
|
|
@@ -121,7 +120,6 @@ export interface InputFeedContext {
|
|
|
121
120
|
readonly onboardingWizard: OnboardingWizardController;
|
|
122
121
|
readonly processModal: ProcessModal;
|
|
123
122
|
readonly liveTailModal: LiveTailModal;
|
|
124
|
-
readonly agentDetailModal: AgentDetailModal;
|
|
125
123
|
readonly contextInspectorModal: ContextInspectorModal;
|
|
126
124
|
readonly blockActionsMenu: BlockActionsMenu;
|
|
127
125
|
readonly searchManager: SearchManager;
|
|
@@ -211,7 +209,6 @@ export function feedInputTokens(context: InputFeedContext, tokens: readonly Inpu
|
|
|
211
209
|
handleEscape: context.handleEscape,
|
|
212
210
|
liveTailModal: context.liveTailModal,
|
|
213
211
|
processModal: context.processModal,
|
|
214
|
-
agentDetailModal: context.agentDetailModal,
|
|
215
212
|
contextInspectorModal: context.contextInspectorModal,
|
|
216
213
|
modalOpened: context.modalOpened,
|
|
217
214
|
filePicker: context.filePicker,
|
|
@@ -235,7 +235,6 @@ export function handleEscapeForHandler(handler: InputHandler): void {
|
|
|
235
235
|
helpOverlayActive: handler.helpOverlayActive,
|
|
236
236
|
shortcutsOverlayActive: handler.shortcutsOverlayActive,
|
|
237
237
|
bookmarkModal: handler.bookmarkModal,
|
|
238
|
-
agentDetailModal: handler.agentDetailModal,
|
|
239
238
|
liveTailModal: handler.liveTailModal,
|
|
240
239
|
settingsModal: handler.settingsModal,
|
|
241
240
|
mcpWorkspace: handler.mcpWorkspace,
|
|
@@ -121,7 +121,6 @@ export function handleEscape(state: EscapeState): {
|
|
|
121
121
|
shortcutsScrollOffset = 0;
|
|
122
122
|
},
|
|
123
123
|
closeBookmark: () => state.bookmarkModal.close(),
|
|
124
|
-
closeAgentDetail: () => state.agentDetailModal.close(),
|
|
125
124
|
closeLiveTail: () => state.liveTailModal.close(),
|
|
126
125
|
closeSettings: () => state.settingsModal.close(),
|
|
127
126
|
closeMcpWorkspace: () => state.mcpWorkspace?.close(),
|
|
@@ -12,7 +12,6 @@ import { handleMcpWorkspaceToken, type McpWorkspace } from './mcp-workspace.ts';
|
|
|
12
12
|
import type { CommandContext } from './command-registry.ts';
|
|
13
13
|
import type { LiveTailModal } from '../renderer/live-tail-modal.ts';
|
|
14
14
|
import type { ProcessModal } from '../renderer/process-modal.ts';
|
|
15
|
-
import type { AgentDetailModal } from '../renderer/agent-detail-modal.ts';
|
|
16
15
|
import type { ContextInspectorModal } from '../renderer/context-inspector.ts';
|
|
17
16
|
import type { FilePickerModal } from './file-picker.ts';
|
|
18
17
|
import type { BlockActionsMenu, BlockActionId } from '../renderer/block-actions.ts';
|
|
@@ -65,7 +64,6 @@ export type ModalTokenRouteState = {
|
|
|
65
64
|
handleEscape: () => void;
|
|
66
65
|
liveTailModal: LiveTailModal;
|
|
67
66
|
processModal: ProcessModal;
|
|
68
|
-
agentDetailModal: AgentDetailModal;
|
|
69
67
|
contextInspectorModal: ContextInspectorModal;
|
|
70
68
|
modalOpened: (name: string) => void;
|
|
71
69
|
filePicker: FilePickerModal;
|
|
@@ -240,14 +238,6 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
|
|
|
240
238
|
return withState(state, true);
|
|
241
239
|
}
|
|
242
240
|
|
|
243
|
-
if (handleEscapeOnlyModalToken({
|
|
244
|
-
active: state.agentDetailModal.active,
|
|
245
|
-
requestRender: state.requestRender,
|
|
246
|
-
handleEscape: state.handleEscape,
|
|
247
|
-
}, token)) {
|
|
248
|
-
return withState(state, true);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
241
|
if (handleEscapeOnlyModalToken({
|
|
252
242
|
active: state.contextInspectorModal.active,
|
|
253
243
|
requestRender: state.requestRender,
|
|
@@ -259,7 +249,6 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
|
|
|
259
249
|
if (handleProcessModalToken({
|
|
260
250
|
processModal: state.processModal,
|
|
261
251
|
liveTailModal: state.liveTailModal,
|
|
262
|
-
agentDetailModal: state.agentDetailModal,
|
|
263
252
|
modalOpened: state.modalOpened,
|
|
264
253
|
requestRender: state.requestRender,
|
|
265
254
|
handleEscape: state.handleEscape,
|
|
@@ -204,15 +204,12 @@ type ProcessRouteState = {
|
|
|
204
204
|
getSelected: () => ProcessEntry | undefined;
|
|
205
205
|
close: () => void;
|
|
206
206
|
open: () => void;
|
|
207
|
-
|
|
207
|
+
stopSelected: () => boolean;
|
|
208
208
|
refresh: () => void;
|
|
209
209
|
};
|
|
210
210
|
liveTailModal: {
|
|
211
211
|
open: (entry: ProcessEntry) => void;
|
|
212
212
|
};
|
|
213
|
-
agentDetailModal: {
|
|
214
|
-
open: (id: string) => void;
|
|
215
|
-
};
|
|
216
213
|
modalOpened: (name: string) => void;
|
|
217
214
|
requestRender: () => void;
|
|
218
215
|
handleEscape: () => void;
|
|
@@ -231,20 +228,14 @@ export function handleProcessModalToken(state: ProcessRouteState, token: InputTo
|
|
|
231
228
|
else if (token.logicalName === 'enter') {
|
|
232
229
|
const entry = state.processModal.getSelected();
|
|
233
230
|
if (entry) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
state.agentDetailModal.open(entry.id);
|
|
238
|
-
} else {
|
|
239
|
-
state.modalOpened('liveTail');
|
|
240
|
-
state.processModal.close();
|
|
241
|
-
state.liveTailModal.open(entry);
|
|
242
|
-
}
|
|
231
|
+
state.modalOpened('liveTail');
|
|
232
|
+
state.processModal.close();
|
|
233
|
+
state.liveTailModal.open(entry);
|
|
243
234
|
}
|
|
244
235
|
}
|
|
245
236
|
} else if (token.type === 'text' && token.value === 'k') {
|
|
246
|
-
const
|
|
247
|
-
if (
|
|
237
|
+
const stopped = state.processModal.stopSelected();
|
|
238
|
+
if (stopped) state.processModal.refresh();
|
|
248
239
|
}
|
|
249
240
|
|
|
250
241
|
state.requestRender();
|
|
@@ -256,7 +247,7 @@ type LiveTailRouteState = {
|
|
|
256
247
|
active: boolean;
|
|
257
248
|
scrollUp: () => void;
|
|
258
249
|
scrollDown: () => void;
|
|
259
|
-
|
|
250
|
+
stopProcess: () => boolean;
|
|
260
251
|
close: () => void;
|
|
261
252
|
};
|
|
262
253
|
processModal: {
|
|
@@ -269,8 +260,8 @@ type LiveTailRouteState = {
|
|
|
269
260
|
export function handleLiveTailToken(state: LiveTailRouteState, token: InputToken): boolean {
|
|
270
261
|
if (!state.liveTailModal.active) return false;
|
|
271
262
|
|
|
272
|
-
const
|
|
273
|
-
if (state.liveTailModal.
|
|
263
|
+
const stopAndReturn = (): void => {
|
|
264
|
+
if (state.liveTailModal.stopProcess()) state.handleEscape();
|
|
274
265
|
};
|
|
275
266
|
|
|
276
267
|
if (token.type === 'key') {
|
|
@@ -280,9 +271,9 @@ export function handleLiveTailToken(state: LiveTailRouteState, token: InputToken
|
|
|
280
271
|
}
|
|
281
272
|
if (token.logicalName === 'up') state.liveTailModal.scrollUp();
|
|
282
273
|
else if (token.logicalName === 'down') state.liveTailModal.scrollDown();
|
|
283
|
-
else if (token.logicalName === 'k')
|
|
274
|
+
else if (token.logicalName === 'k') stopAndReturn();
|
|
284
275
|
} else if (token.type === 'text' && token.value === 'k') {
|
|
285
|
-
|
|
276
|
+
stopAndReturn();
|
|
286
277
|
}
|
|
287
278
|
|
|
288
279
|
state.requestRender();
|
|
@@ -55,7 +55,6 @@ export type ActiveModalState = {
|
|
|
55
55
|
helpOverlayActive: boolean;
|
|
56
56
|
shortcutsOverlayActive: boolean;
|
|
57
57
|
bookmarkModal: { active: boolean; close: () => void };
|
|
58
|
-
agentDetailModal: { active: boolean; close: () => void };
|
|
59
58
|
liveTailModal: { active: boolean; close: () => void };
|
|
60
59
|
settingsModal: { active: boolean; close: () => void };
|
|
61
60
|
mcpWorkspace?: { active: boolean; close: () => void; reopen: () => void };
|
|
@@ -76,7 +75,6 @@ export function getActiveModalName(state: ActiveModalState): string | null {
|
|
|
76
75
|
if (state.helpOverlayActive) return 'help';
|
|
77
76
|
if (state.shortcutsOverlayActive) return 'shortcuts';
|
|
78
77
|
if (state.bookmarkModal.active) return 'bookmark';
|
|
79
|
-
if (state.agentDetailModal.active) return 'agentDetail';
|
|
80
78
|
if (state.liveTailModal.active) return 'liveTail';
|
|
81
79
|
if (state.settingsModal.active) return 'settings';
|
|
82
80
|
if (state.mcpWorkspace?.active) return 'mcpWorkspace';
|
|
@@ -98,7 +96,6 @@ export type ModalCloseOps = {
|
|
|
98
96
|
resetHelp: () => void;
|
|
99
97
|
resetShortcuts: () => void;
|
|
100
98
|
closeBookmark: () => void;
|
|
101
|
-
closeAgentDetail: () => void;
|
|
102
99
|
closeLiveTail: () => void;
|
|
103
100
|
closeSettings: () => void;
|
|
104
101
|
closeMcpWorkspace: () => void;
|
|
@@ -126,9 +123,6 @@ export function closeModalByName(name: string, ops: ModalCloseOps): void {
|
|
|
126
123
|
case 'bookmark':
|
|
127
124
|
ops.closeBookmark();
|
|
128
125
|
break;
|
|
129
|
-
case 'agentDetail':
|
|
130
|
-
ops.closeAgentDetail();
|
|
131
|
-
break;
|
|
132
126
|
case 'liveTail':
|
|
133
127
|
ops.closeLiveTail();
|
|
134
128
|
break;
|