@pellux/goodvibes-agent 0.1.6 → 0.1.8
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 +31 -0
- package/LICENSE +21 -0
- package/README.md +3 -1
- package/docs/README.md +2 -2
- package/docs/deployment-and-services.md +1 -1
- package/docs/getting-started.md +6 -4
- package/docs/release-and-publishing.md +1 -1
- package/package.json +2 -2
- package/src/agent/routine-registry.ts +389 -0
- package/src/cli/management-commands.ts +8 -12
- package/src/cli/package-verification.ts +2 -2
- package/src/input/agent-workspace.ts +30 -3
- package/src/input/commands/control-room-runtime.ts +7 -28
- package/src/input/commands/health-runtime.ts +4 -4
- package/src/input/commands/operator-runtime.ts +17 -45
- package/src/input/commands/remote-runtime.ts +7 -22
- package/src/input/commands/routines-runtime.ts +232 -0
- package/src/input/commands/session-content.ts +3 -16
- package/src/input/commands/session-workflow.ts +1 -1
- package/src/input/commands/session.ts +19 -26
- package/src/input/commands/tasks-runtime.ts +28 -102
- package/src/input/commands.ts +2 -0
- package/src/input/handler-picker-routes.ts +2 -3
- package/src/panels/builtin/shared.ts +4 -4
- package/src/panels/provider-health-domains.ts +3 -3
- package/src/planning/project-planning-coordinator.ts +3 -3
- package/src/renderer/agent-workspace.ts +2 -1
- package/src/renderer/live-tail-modal.ts +7 -7
- package/src/renderer/process-indicator.ts +8 -8
- package/src/renderer/process-modal.ts +9 -9
- package/src/runtime/bootstrap.ts +2 -0
- package/src/runtime/services.ts +2 -20
- package/src/tools/wrfc-agent-guard.ts +37 -1
- package/src/version.ts +1 -1
- package/.goodvibes/agents/reviewer.md +0 -48
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* /session command handler — Multi-session Orchestration.
|
|
3
3
|
*
|
|
4
|
-
* Implements session
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* — Register a task as a global cross-session ref, optionally linking it to a
|
|
8
|
-
* dependency.
|
|
9
|
-
*
|
|
10
|
-
* /session handoff <taskId> --to <sessionId> [--session <sessionId>] [--reason <reason>]
|
|
11
|
-
* — Initiate a task handoff from the current session to another.
|
|
12
|
-
*
|
|
13
|
-
* /session graph [--session <sessionId>] [--format text|json]
|
|
14
|
-
* — Display the cross-session task dependency graph.
|
|
15
|
-
*
|
|
16
|
-
* /session cancel <taskId|--scope session> [--session <sessionId>] [--scope task|subtree|session]
|
|
17
|
-
* — Cancel tasks with configurable scope semantics.
|
|
4
|
+
* Implements read-only session graph inspection plus session continuity commands.
|
|
5
|
+
* Copied local task graph mutation commands are blocked in Agent; explicit
|
|
6
|
+
* build/fix/review handoff must use `/delegate` so GoodVibes TUI owns execution.
|
|
18
7
|
*/
|
|
19
8
|
|
|
20
9
|
import type { SlashCommand, CommandContext } from '../command-registry.ts';
|
|
@@ -72,6 +61,14 @@ function fmtRef(ref: CrossSessionTaskRef): string {
|
|
|
72
61
|
return `${statusBadge(ref.status)} ${key}${label} "${ref.title}"`;
|
|
73
62
|
}
|
|
74
63
|
|
|
64
|
+
function printSessionGraphMutationBlocked(context: CommandContext): void {
|
|
65
|
+
context.print([
|
|
66
|
+
'[session] Local cross-session task graph mutation is blocked in GoodVibes Agent.',
|
|
67
|
+
'[session] Use /session graph for read-only inspection.',
|
|
68
|
+
'[session] Use /delegate <task> for explicit build/fix/review handoff to GoodVibes TUI.',
|
|
69
|
+
].join('\n'));
|
|
70
|
+
}
|
|
71
|
+
|
|
75
72
|
// ── /session link-task ────────────────────────────────────────────────────────
|
|
76
73
|
|
|
77
74
|
function handleLinkTask(args: string[], context: CommandContext): void {
|
|
@@ -192,7 +189,7 @@ function handleGraph(args: string[], context: CommandContext): void {
|
|
|
192
189
|
if (filterSession) {
|
|
193
190
|
context.print(`[session] No tasks registered for session ${filterSession.slice(0, 8)}...`);
|
|
194
191
|
} else {
|
|
195
|
-
context.print('[session] Task graph is empty.
|
|
192
|
+
context.print('[session] Task graph is empty. Local task graph mutation is blocked in Agent; use /delegate for explicit build/fix/review handoff.');
|
|
196
193
|
}
|
|
197
194
|
return;
|
|
198
195
|
}
|
|
@@ -324,21 +321,21 @@ function handleCancel(args: string[], context: CommandContext): void {
|
|
|
324
321
|
export const sessionCommand: SlashCommand = {
|
|
325
322
|
name: 'session',
|
|
326
323
|
aliases: ['sess'],
|
|
327
|
-
description: '
|
|
324
|
+
description: 'Session continuity and read-only cross-session graph inspection.',
|
|
328
325
|
usage: '<subcommand> [args]',
|
|
329
|
-
argsHint: '
|
|
326
|
+
argsHint: 'list|resume|save|graph',
|
|
330
327
|
handler: async (args: string[], context: CommandContext): Promise<void> => {
|
|
331
328
|
const [sub, ...rest] = args;
|
|
332
329
|
|
|
333
330
|
switch (sub) {
|
|
334
331
|
case 'link-task':
|
|
335
332
|
case 'link':
|
|
336
|
-
|
|
333
|
+
printSessionGraphMutationBlocked(context);
|
|
337
334
|
break;
|
|
338
335
|
|
|
339
336
|
case 'handoff':
|
|
340
337
|
case 'ho':
|
|
341
|
-
|
|
338
|
+
printSessionGraphMutationBlocked(context);
|
|
342
339
|
break;
|
|
343
340
|
|
|
344
341
|
case 'graph':
|
|
@@ -347,7 +344,7 @@ export const sessionCommand: SlashCommand = {
|
|
|
347
344
|
break;
|
|
348
345
|
|
|
349
346
|
case 'cancel':
|
|
350
|
-
|
|
347
|
+
printSessionGraphMutationBlocked(context);
|
|
351
348
|
break;
|
|
352
349
|
|
|
353
350
|
default: {
|
|
@@ -357,14 +354,10 @@ export const sessionCommand: SlashCommand = {
|
|
|
357
354
|
'Usage: /session <subcommand>',
|
|
358
355
|
' list | rename <name> | resume <id|name> | fork [name] | save [name] | info [id] | export <id> [format] | search <query> | delete <id>',
|
|
359
356
|
' — Session continuity, export, resume, and pruning',
|
|
360
|
-
' link-task <taskId> [--session <sid>] [--depends-on <sid:taskId>] [--label <label>]',
|
|
361
|
-
' — Register a task in the cross-session graph',
|
|
362
|
-
' handoff <taskId> --to <sid> [--session <sid>] [--reason <reason>]',
|
|
363
|
-
' — Hand a task off to another session',
|
|
364
357
|
' graph [--session <sid>] [--format text|json]',
|
|
365
358
|
' — Display the cross-session task dependency graph',
|
|
366
|
-
'
|
|
367
|
-
' —
|
|
359
|
+
' link-task | handoff | cancel',
|
|
360
|
+
' — Blocked in Agent; use /delegate for explicit build/fix/review handoff',
|
|
368
361
|
].join('\n');
|
|
369
362
|
context.print(usage);
|
|
370
363
|
}
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
2
2
|
import type { RuntimeTask, TaskLifecycleState } from '@/runtime/index.ts';
|
|
3
3
|
import { reviewWorktreeAttachments } from '@/runtime/index.ts';
|
|
4
|
-
import { requireOperatorClient,
|
|
5
|
-
|
|
4
|
+
import { requireOperatorClient, requirePanelManager, requireShellPaths } from './runtime-services.ts';
|
|
5
|
+
|
|
6
|
+
const BLOCKED_TASK_MUTATIONS: ReadonlySet<string> = new Set([
|
|
7
|
+
'create',
|
|
8
|
+
'update',
|
|
9
|
+
'complete',
|
|
10
|
+
'fail',
|
|
11
|
+
'cancel',
|
|
12
|
+
'pause',
|
|
13
|
+
'resume',
|
|
14
|
+
'retry',
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
function printTaskMutationBlocked(print: (text: string) => void, subcommand: string): void {
|
|
18
|
+
print([
|
|
19
|
+
`Task mutation "${subcommand}" is blocked in GoodVibes Agent.`,
|
|
20
|
+
' policy: runtime tasks are read-only from the Agent surface; normal work stays in the main conversation.',
|
|
21
|
+
' durable tasks: use /workplan for visible planning and task tracking.',
|
|
22
|
+
' build/fix/review: use /delegate <task> to hand explicit implementation work to GoodVibes TUI.',
|
|
23
|
+
' result: no local runtime task state was changed.',
|
|
24
|
+
].join('\n'));
|
|
25
|
+
}
|
|
6
26
|
|
|
7
27
|
function sortRuntimeTasks(tasks: RuntimeTask[]): RuntimeTask[] {
|
|
8
28
|
const statusOrder: TaskLifecycleState[] = ['running', 'queued', 'blocked', 'failed', 'completed', 'cancelled'];
|
|
@@ -32,8 +52,8 @@ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
32
52
|
registry.register({
|
|
33
53
|
name: 'tasks',
|
|
34
54
|
aliases: ['task'],
|
|
35
|
-
description: 'Inspect
|
|
36
|
-
usage: '[list [status|kind] | show <taskId> | output <taskId>
|
|
55
|
+
description: 'Inspect runtime tasks without starting or mutating local background work',
|
|
56
|
+
usage: '[list [status|kind] | show <taskId> | output <taskId>]',
|
|
37
57
|
handler(args, ctx) {
|
|
38
58
|
if (args.length === 0) {
|
|
39
59
|
if (ctx.showPanel) ctx.showPanel('tasks');
|
|
@@ -96,7 +116,7 @@ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
96
116
|
return worktrees.total > 0
|
|
97
117
|
? [
|
|
98
118
|
` worktrees: ${worktrees.total} tracked (${worktrees.active} active / ${worktrees.paused} paused / ${worktrees.pendingCleanup} cleanup)`,
|
|
99
|
-
|
|
119
|
+
' worktree next: open GoodVibes TUI in the target workspace for recovery.',
|
|
100
120
|
]
|
|
101
121
|
: [];
|
|
102
122
|
})(),
|
|
@@ -125,106 +145,12 @@ export function registerTasksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
125
145
|
return;
|
|
126
146
|
}
|
|
127
147
|
|
|
128
|
-
if (subcommand
|
|
129
|
-
|
|
130
|
-
const kind = args[1];
|
|
131
|
-
const owner = args[2];
|
|
132
|
-
const title = args.slice(3).join(' ').trim();
|
|
133
|
-
if (!kind || !owner || !title) {
|
|
134
|
-
ctx.print('Usage: /tasks create <kind> <owner> <title...>');
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const validKinds = new Set(['exec', 'agent', 'acp', 'scheduler', 'daemon', 'mcp', 'plugin', 'integration']);
|
|
138
|
-
if (!validKinds.has(kind)) {
|
|
139
|
-
ctx.print(`Unknown task kind: ${kind}`);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
const task = opsApi.tasks.create({
|
|
143
|
-
kind: kind as import('@/runtime/index.ts').TaskKind,
|
|
144
|
-
owner,
|
|
145
|
-
title,
|
|
146
|
-
description: title,
|
|
147
|
-
});
|
|
148
|
-
ctx.print(`Created task ${task.id} (${task.kind}) for ${task.owner}.`);
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (subcommand === 'update') {
|
|
153
|
-
const opsApi = requireOpsApi(ctx);
|
|
154
|
-
const taskId = args[1];
|
|
155
|
-
const field = args[2];
|
|
156
|
-
const value = args.slice(3).join(' ').trim();
|
|
157
|
-
if (!taskId || !field || !value) {
|
|
158
|
-
ctx.print('Usage: /tasks update <taskId> <title|description|result> <value...>');
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
if (field !== 'title' && field !== 'description' && field !== 'result') {
|
|
162
|
-
ctx.print(`Unsupported task update field: ${field}`);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
opsApi.tasks.update(taskId, field === 'result' ? { result: value } : { [field]: value });
|
|
166
|
-
ctx.print(`Updated task ${taskId} field ${field}.`);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (subcommand === 'complete') {
|
|
171
|
-
const opsApi = requireOpsApi(ctx);
|
|
172
|
-
const taskId = args[1];
|
|
173
|
-
if (!taskId) {
|
|
174
|
-
ctx.print('Usage: /tasks complete <taskId> [result]');
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
const result = args.slice(2).join(' ').trim() || undefined;
|
|
178
|
-
opsApi.tasks.complete(taskId, result);
|
|
179
|
-
ctx.print(`Completed task ${taskId}.`);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (subcommand === 'fail') {
|
|
184
|
-
const opsApi = requireOpsApi(ctx);
|
|
185
|
-
const taskId = args[1];
|
|
186
|
-
const errorText = args.slice(2).join(' ').trim();
|
|
187
|
-
if (!taskId || !errorText) {
|
|
188
|
-
ctx.print('Usage: /tasks fail <taskId> <error...>');
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
opsApi.tasks.fail(taskId, { error: errorText });
|
|
192
|
-
ctx.print(`Failed task ${taskId}.`);
|
|
148
|
+
if (BLOCKED_TASK_MUTATIONS.has(subcommand)) {
|
|
149
|
+
printTaskMutationBlocked(ctx.print, subcommand);
|
|
193
150
|
return;
|
|
194
151
|
}
|
|
195
152
|
|
|
196
|
-
|
|
197
|
-
const note = args.slice(2).join(' ').trim() || undefined;
|
|
198
|
-
if (!taskId) {
|
|
199
|
-
ctx.print(`Usage: /tasks ${subcommand} <taskId> [note]`);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
const opsApi = requireOpsApi(ctx);
|
|
203
|
-
try {
|
|
204
|
-
switch (subcommand) {
|
|
205
|
-
case 'cancel':
|
|
206
|
-
opsApi.tasks.cancel(taskId, note);
|
|
207
|
-
ctx.print(`Cancelled task ${taskId}.`);
|
|
208
|
-
return;
|
|
209
|
-
case 'pause':
|
|
210
|
-
opsApi.tasks.pause(taskId, note);
|
|
211
|
-
ctx.print(`Paused task ${taskId}.`);
|
|
212
|
-
return;
|
|
213
|
-
case 'resume':
|
|
214
|
-
opsApi.tasks.resume(taskId, note);
|
|
215
|
-
ctx.print(`Resumed task ${taskId}.`);
|
|
216
|
-
return;
|
|
217
|
-
case 'retry':
|
|
218
|
-
opsApi.tasks.retry(taskId, note);
|
|
219
|
-
ctx.print(`Re-queued task ${taskId}.`);
|
|
220
|
-
return;
|
|
221
|
-
default:
|
|
222
|
-
ctx.print(`Unknown tasks subcommand: ${subcommand}`);
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
} catch (error) {
|
|
226
|
-
ctx.print(summarizeError(error));
|
|
227
|
-
}
|
|
153
|
+
ctx.print(`Unknown tasks subcommand: ${subcommand}`);
|
|
228
154
|
},
|
|
229
155
|
});
|
|
230
156
|
}
|
package/src/input/commands.ts
CHANGED
|
@@ -57,6 +57,7 @@ import { registerAgentExternalizedTuiCommands } from './commands/agent-externali
|
|
|
57
57
|
import { registerDelegationRuntimeCommands } from './commands/delegation-runtime.ts';
|
|
58
58
|
import { registerPersonasRuntimeCommands } from './commands/personas-runtime.ts';
|
|
59
59
|
import { registerAgentSkillsRuntimeCommands } from './commands/agent-skills-runtime.ts';
|
|
60
|
+
import { registerRoutinesRuntimeCommands } from './commands/routines-runtime.ts';
|
|
60
61
|
|
|
61
62
|
/**
|
|
62
63
|
* registerBuiltinCommands - Register all built-in slash commands into the registry.
|
|
@@ -67,6 +68,7 @@ export function registerBuiltinCommands(registry: CommandRegistry): void {
|
|
|
67
68
|
registerAgentWorkspaceRuntimeCommands(registry);
|
|
68
69
|
registerPersonasRuntimeCommands(registry);
|
|
69
70
|
registerAgentSkillsRuntimeCommands(registry);
|
|
71
|
+
registerRoutinesRuntimeCommands(registry);
|
|
70
72
|
registerDelegationRuntimeCommands(registry);
|
|
71
73
|
registerConfigCommand(registry);
|
|
72
74
|
registerOperatorRuntimeCommands(registry);
|
|
@@ -256,7 +256,7 @@ type LiveTailRouteState = {
|
|
|
256
256
|
active: boolean;
|
|
257
257
|
scrollUp: () => void;
|
|
258
258
|
scrollDown: () => void;
|
|
259
|
-
killProcess: () =>
|
|
259
|
+
killProcess: () => boolean;
|
|
260
260
|
close: () => void;
|
|
261
261
|
};
|
|
262
262
|
processModal: {
|
|
@@ -270,8 +270,7 @@ export function handleLiveTailToken(state: LiveTailRouteState, token: InputToken
|
|
|
270
270
|
if (!state.liveTailModal.active) return false;
|
|
271
271
|
|
|
272
272
|
const killAndReturn = (): void => {
|
|
273
|
-
state.liveTailModal.killProcess();
|
|
274
|
-
state.handleEscape();
|
|
273
|
+
if (state.liveTailModal.killProcess()) state.handleEscape();
|
|
275
274
|
};
|
|
276
275
|
|
|
277
276
|
if (token.type === 'key') {
|
|
@@ -55,9 +55,9 @@ export interface BuiltinPanelDeps {
|
|
|
55
55
|
resumeSession?: (sessionId: string) => void;
|
|
56
56
|
/** Request a shell repaint directly rather than routing through a retired event path. */
|
|
57
57
|
requestRender?: () => void;
|
|
58
|
-
/** Submit a Planning panel answer through the normal
|
|
58
|
+
/** Submit a Planning panel answer through the normal Agent chat/planning coordinator path. */
|
|
59
59
|
submitPlanningAnswer?: (answer: string) => void;
|
|
60
|
-
/** Pause the
|
|
60
|
+
/** Pause the Agent-owned planning loop and return focus to normal prompt input. */
|
|
61
61
|
dismissPlanning?: () => void;
|
|
62
62
|
/** ForensicsRegistry for the Forensics panel. */
|
|
63
63
|
forensicsRegistry?: import('@/runtime/index.ts').ForensicsRegistry;
|
|
@@ -96,9 +96,9 @@ export interface BuiltinPanelDeps {
|
|
|
96
96
|
adaptivePlanner?: AdaptivePlanner;
|
|
97
97
|
/** Passive SDK-backed project planning artifact service. */
|
|
98
98
|
projectPlanningService?: ProjectPlanningService;
|
|
99
|
-
/** Stable workspace project id for project:<projectId> planning
|
|
99
|
+
/** Stable workspace project id for project:<projectId> planning namespaces. */
|
|
100
100
|
projectPlanningProjectId?: string;
|
|
101
|
-
/**
|
|
101
|
+
/** Agent-owned persistent work plan store. */
|
|
102
102
|
workPlanStore?: import('../../work-plans/work-plan-store.ts').WorkPlanStore;
|
|
103
103
|
/** Shared system-messages panel instance attached from boot so low-priority chatter stays out of conversation. */
|
|
104
104
|
systemMessagesPanel?: import('../system-messages-panel.ts').SystemMessagesPanel;
|
|
@@ -200,15 +200,15 @@ export function buildProviderHealthDomainSummaries(
|
|
|
200
200
|
summary: worktreeSummary.total === 0
|
|
201
201
|
? 'no persisted worktrees'
|
|
202
202
|
: `${worktreeSummary.total} tracked / ${worktreeIssues} need review`,
|
|
203
|
-
next:
|
|
203
|
+
next: 'externalized to GoodVibes TUI',
|
|
204
204
|
details: [
|
|
205
205
|
worktreeSummary.paused > 0 ? `${worktreeSummary.paused} paused worktree(s)` : '',
|
|
206
206
|
worktreeSummary.pendingCleanup > 0 ? `${worktreeSummary.pendingCleanup} cleanup pending` : '',
|
|
207
207
|
worktreeSummary.discard > 0 ? `${worktreeSummary.discard} marked discard` : '',
|
|
208
208
|
].filter(Boolean),
|
|
209
209
|
nextSteps: worktreeIssues > 0
|
|
210
|
-
? ['
|
|
211
|
-
: ['
|
|
210
|
+
? ['Open GoodVibes TUI in the target workspace for recovery', '/delegate <task> for explicit build/fix/review recovery']
|
|
211
|
+
: ['No Agent worktree action available; use GoodVibes TUI when repository recovery is needed'],
|
|
212
212
|
});
|
|
213
213
|
|
|
214
214
|
return summaries;
|
|
@@ -198,7 +198,7 @@ export class ProjectPlanningCoordinator {
|
|
|
198
198
|
metadata: {
|
|
199
199
|
...(existing?.metadata ?? {}),
|
|
200
200
|
active: true,
|
|
201
|
-
owner: '
|
|
201
|
+
owner: 'agent',
|
|
202
202
|
source: 'conversation',
|
|
203
203
|
lastPromptAt: now,
|
|
204
204
|
},
|
|
@@ -471,13 +471,13 @@ export class ProjectPlanningCoordinator {
|
|
|
471
471
|
.join('\n') || '- none recorded yet';
|
|
472
472
|
|
|
473
473
|
return [
|
|
474
|
-
'
|
|
474
|
+
'Agent-owned planning loop is active for this turn.',
|
|
475
475
|
'Do not execute code changes, spawn agents, or claim implementation is complete unless the user explicitly approves execution after the plan is structurally ready.',
|
|
476
476
|
'Be relentless and thorough: challenge vague wording, inspect relevant context before proposing execution, and ask exactly one focused question when information is missing.',
|
|
477
477
|
'Do not ask broad questions like "what is in scope?" without examples. Break broad planning gaps into concrete choices, explain tradeoffs, and recommend a default the user can accept or correct.',
|
|
478
478
|
'',
|
|
479
479
|
`Project id: ${this.projectId}`,
|
|
480
|
-
`
|
|
480
|
+
`Planning namespace: ${state.knowledgeSpaceId}`,
|
|
481
481
|
`Readiness: ${evaluation.readiness}`,
|
|
482
482
|
`Execution approved: ${state.executionApproved ? 'yes' : 'no'}`,
|
|
483
483
|
`Goal: ${state.goal || '(missing)'}`,
|
|
@@ -97,9 +97,10 @@ function snapshotLines(category: AgentWorkspaceCategory, snapshot: AgentWorkspac
|
|
|
97
97
|
} else if (category.id === 'memory') {
|
|
98
98
|
base.push(
|
|
99
99
|
{ text: `Session memories: ${snapshot.sessionMemoryCount}`, fg: PALETTE.info },
|
|
100
|
+
{ text: `Local routines: ${snapshot.localRoutineCount}; enabled: ${snapshot.enabledRoutineCount}`, fg: PALETTE.info },
|
|
100
101
|
{ text: `Local skills: ${snapshot.localSkillCount}; enabled: ${snapshot.enabledSkillCount}`, fg: PALETTE.info },
|
|
101
102
|
{ text: `Local personas: ${snapshot.localPersonaCount}; active: ${snapshot.activePersonaName}`, fg: PALETTE.info },
|
|
102
|
-
{ text: 'Durable memory, skills, and personas remain Agent-local until shared registry contracts exist.', fg: PALETTE.good },
|
|
103
|
+
{ text: 'Durable memory, routines, skills, and personas remain Agent-local until shared registry contracts exist.', fg: PALETTE.good },
|
|
103
104
|
{ text: 'Secrets are rejected/redacted; store secret references instead of secret values.', fg: PALETTE.warn },
|
|
104
105
|
);
|
|
105
106
|
} else if (category.id === 'work') {
|
|
@@ -6,7 +6,7 @@ import type { ProcessEntry } from './process-modal.ts';
|
|
|
6
6
|
import { getOverlaySurfaceMetrics, getStableOverlayContentRows } from './overlay-viewport.ts';
|
|
7
7
|
|
|
8
8
|
export interface LiveTailModalDeps {
|
|
9
|
-
readonly agentManager: Pick<AgentManager, '
|
|
9
|
+
readonly agentManager: Pick<AgentManager, 'getStatus'>;
|
|
10
10
|
readonly processManager: Pick<ProcessManager, 'stop' | 'getOutput'>;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ export interface LiveTailModalDeps {
|
|
|
15
15
|
/**
|
|
16
16
|
* LiveTailModal — manages state for the live output peek modal.
|
|
17
17
|
*
|
|
18
|
-
* Shows streaming stdout/stderr from a selected
|
|
18
|
+
* Shows streaming stdout/stderr from a selected shell process or agent
|
|
19
19
|
* progress notes. Auto-scrolls to the bottom unless the user scrolled up.
|
|
20
20
|
*/
|
|
21
21
|
export class LiveTailModal {
|
|
@@ -48,17 +48,17 @@ export class LiveTailModal {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
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.
|
|
53
54
|
*/
|
|
54
55
|
killProcess(): boolean {
|
|
55
56
|
if (!this.entry) return false;
|
|
56
57
|
|
|
57
58
|
if (this.entry.type === 'exec') {
|
|
58
59
|
return this.deps.processManager.stop(this.entry.id);
|
|
59
|
-
} else {
|
|
60
|
-
return this.deps.agentManager.cancel(this.entry.id);
|
|
61
60
|
}
|
|
61
|
+
return false;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/** Retrieve the current output text for the watched process. */
|
|
@@ -151,6 +151,6 @@ export function renderLiveTailModal(
|
|
|
151
151
|
margin: 2,
|
|
152
152
|
targetContentRows,
|
|
153
153
|
sections,
|
|
154
|
-
hints: ['[Up/Down] Scroll', '[k]
|
|
154
|
+
hints: ['[Up/Down] Scroll', '[k] Stop exec only', '[Esc] Back'],
|
|
155
155
|
}, width);
|
|
156
156
|
}
|
|
@@ -17,12 +17,12 @@ function truncateToWidth(text: string, maxWidth: number): string {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* renderProcessIndicator — shows a one-line summary of active
|
|
21
|
-
*
|
|
20
|
+
* renderProcessIndicator — shows a one-line summary of active runtime
|
|
21
|
+
* activity below the input area.
|
|
22
22
|
*
|
|
23
|
-
* Dimmed when no
|
|
24
|
-
*
|
|
25
|
-
* when active.
|
|
23
|
+
* Dimmed when no entries are active, highlighted (cyan) when delegated agent
|
|
24
|
+
* records or shell exec processes are running. Includes an `Enter to view`
|
|
25
|
+
* hint when active.
|
|
26
26
|
*/
|
|
27
27
|
export function renderProcessIndicator(
|
|
28
28
|
width: number,
|
|
@@ -62,16 +62,16 @@ export function renderProcessIndicator(
|
|
|
62
62
|
if (agentCount > 0) parts.push(`${agentCount} agent${agentCount !== 1 ? 's' : ''}`);
|
|
63
63
|
if (toolCount > 0) parts.push(`${toolCount} tool${toolCount !== 1 ? 's' : ''} running`);
|
|
64
64
|
const label = total === 0
|
|
65
|
-
? `No
|
|
65
|
+
? `No runtime activity ${GLYPHS.status.pending} back to input`
|
|
66
66
|
: `${parts.join(` ${GLYPHS.navigation.pipeSeparator} `)} ${GLYPHS.status.pending} Enter to open ${GLYPHS.status.pending} back to input`;
|
|
67
67
|
return renderFocusedStatus(label);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
if (total === 0) {
|
|
71
|
-
return renderPlainStatus('No
|
|
71
|
+
return renderPlainStatus('No runtime activity', { fg: '238', dim: true });
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
// Build the label: "
|
|
74
|
+
// Build the label: "2 agents | Turn 3 | write - src/foo.ts"
|
|
75
75
|
const parts: string[] = [];
|
|
76
76
|
if (agentCount > 0) {
|
|
77
77
|
parts.push(`${agentCount} agent${agentCount !== 1 ? 's' : ''}`);
|
|
@@ -42,7 +42,7 @@ const WRFC_ROLE_ORDER: Record<string, number> = {
|
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
export interface ProcessModalDeps {
|
|
45
|
-
readonly agentManager: Pick<AgentManager, 'list' | 'getStatus'
|
|
45
|
+
readonly agentManager: Pick<AgentManager, 'list' | 'getStatus'>;
|
|
46
46
|
readonly processManager: Pick<ProcessManager, 'list' | 'getStatus' | 'stop'>;
|
|
47
47
|
readonly wrfcController: Pick<WrfcController, 'getChain'> & Partial<Pick<WrfcController, 'listChains'>>;
|
|
48
48
|
}
|
|
@@ -558,8 +558,9 @@ export class ProcessModal {
|
|
|
558
558
|
}
|
|
559
559
|
|
|
560
560
|
/**
|
|
561
|
-
*
|
|
562
|
-
*
|
|
561
|
+
* Stop the selected shell process.
|
|
562
|
+
* Agent entries are read-only in GoodVibes Agent; build execution and
|
|
563
|
+
* cancellation belong to GoodVibes TUI/shared-session owners.
|
|
563
564
|
*/
|
|
564
565
|
killSelected(): boolean {
|
|
565
566
|
const entry = this.getSelected();
|
|
@@ -567,9 +568,8 @@ export class ProcessModal {
|
|
|
567
568
|
|
|
568
569
|
if (entry.type === 'exec') {
|
|
569
570
|
return this.deps.processManager.stop(entry.id);
|
|
570
|
-
} else {
|
|
571
|
-
return this.deps.agentManager.cancel(entry.id);
|
|
572
571
|
}
|
|
572
|
+
return false;
|
|
573
573
|
}
|
|
574
574
|
}
|
|
575
575
|
|
|
@@ -598,12 +598,12 @@ export function renderProcessModal(modal: ProcessModal, width: number, viewportH
|
|
|
598
598
|
|
|
599
599
|
if (modal.entries.length === 0) {
|
|
600
600
|
return ModalFactory.createModal({
|
|
601
|
-
title: '
|
|
601
|
+
title: 'Runtime Activity',
|
|
602
602
|
width: boxW,
|
|
603
603
|
margin: boxMargin,
|
|
604
604
|
targetContentRows,
|
|
605
605
|
sections: [
|
|
606
|
-
{ type: 'text', content: 'No
|
|
606
|
+
{ type: 'text', content: 'No runtime activity.' },
|
|
607
607
|
],
|
|
608
608
|
hints: ['[Esc] Close'],
|
|
609
609
|
}, width);
|
|
@@ -643,7 +643,7 @@ export function renderProcessModal(modal: ProcessModal, width: number, viewportH
|
|
|
643
643
|
}
|
|
644
644
|
|
|
645
645
|
return ModalFactory.createModal({
|
|
646
|
-
title: '
|
|
646
|
+
title: 'Runtime Activity',
|
|
647
647
|
width: boxW,
|
|
648
648
|
margin: boxMargin,
|
|
649
649
|
targetContentRows,
|
|
@@ -651,6 +651,6 @@ export function renderProcessModal(modal: ProcessModal, width: number, viewportH
|
|
|
651
651
|
helpers: modal.entries.length > maxVisibleRows
|
|
652
652
|
? [{ content: `[${window.start + 1}-${window.end} of ${modal.entries.length}]` }]
|
|
653
653
|
: undefined,
|
|
654
|
-
hints: ['[Up/Down] Navigate', '[Enter]
|
|
654
|
+
hints: ['[Up/Down] Navigate', '[Enter] Details/output', '[k] Stop exec only', '[Esc] Close'],
|
|
655
655
|
}, width);
|
|
656
656
|
}
|
package/src/runtime/bootstrap.ts
CHANGED
|
@@ -44,6 +44,7 @@ import { startMcpConfigAutoReload } from '../mcp/runtime-reload.ts';
|
|
|
44
44
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../config/surface.ts';
|
|
45
45
|
import { buildActivePersonaPrompt } from '../agent/persona-registry.ts';
|
|
46
46
|
import { buildEnabledSkillsPrompt } from '../agent/skill-registry.ts';
|
|
47
|
+
import { buildEnabledRoutinesPrompt } from '../agent/routine-registry.ts';
|
|
47
48
|
|
|
48
49
|
const GOODVIBES_AGENT_OPERATOR_POLICY = [
|
|
49
50
|
'## GoodVibes Agent Operator Policy',
|
|
@@ -210,6 +211,7 @@ export async function bootstrapRuntime(
|
|
|
210
211
|
return joinPromptParts(
|
|
211
212
|
runtime.systemPrompt,
|
|
212
213
|
GOODVIBES_AGENT_OPERATOR_POLICY,
|
|
214
|
+
buildEnabledRoutinesPrompt(services.shellPaths),
|
|
213
215
|
buildEnabledSkillsPrompt(services.shellPaths),
|
|
214
216
|
buildActivePersonaPrompt(services.shellPaths),
|
|
215
217
|
supplement,
|
package/src/runtime/services.ts
CHANGED
|
@@ -88,7 +88,6 @@ import {
|
|
|
88
88
|
} from '@pellux/goodvibes-sdk/platform/tools';
|
|
89
89
|
import { WorkPlanStore } from '../work-plans/work-plan-store.ts';
|
|
90
90
|
|
|
91
|
-
const REGULAR_KNOWLEDGE_DB_FILE = 'knowledge-wiki.sqlite';
|
|
92
91
|
const HOME_GRAPH_KNOWLEDGE_DB_FILE = 'knowledge-home-graph.sqlite';
|
|
93
92
|
|
|
94
93
|
function buildFallbackModelDefinition(provider: string, modelId: string): ModelDefinition {
|
|
@@ -170,6 +169,7 @@ export interface RuntimeServices {
|
|
|
170
169
|
readonly automationManager: AutomationManager;
|
|
171
170
|
readonly gatewayMethods: GatewayMethodCatalog;
|
|
172
171
|
readonly artifactStore: ArtifactStore;
|
|
172
|
+
/** Compatibility alias that intentionally points at the isolated Agent Knowledge service, not default Knowledge/Wiki. */
|
|
173
173
|
readonly knowledgeService: KnowledgeService;
|
|
174
174
|
readonly agentKnowledgeService: KnowledgeService;
|
|
175
175
|
readonly homeGraphService: HomeGraphService;
|
|
@@ -395,10 +395,6 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
395
395
|
].join(' '));
|
|
396
396
|
},
|
|
397
397
|
});
|
|
398
|
-
const knowledgeStore = new KnowledgeStore({
|
|
399
|
-
configManager,
|
|
400
|
-
dbFileName: REGULAR_KNOWLEDGE_DB_FILE,
|
|
401
|
-
});
|
|
402
398
|
const agentKnowledgeStore = new KnowledgeStore({
|
|
403
399
|
configManager,
|
|
404
400
|
dbFileName: GOODVIBES_AGENT_KNOWLEDGE_DB_FILE,
|
|
@@ -411,10 +407,6 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
411
407
|
timeoutMs: 20_000,
|
|
412
408
|
maxConcurrent: 1,
|
|
413
409
|
});
|
|
414
|
-
const knowledgeSemanticService = new KnowledgeSemanticService(knowledgeStore, {
|
|
415
|
-
llm: knowledgeSemanticLlm,
|
|
416
|
-
maxLlmSourcesPerReindex: 3,
|
|
417
|
-
});
|
|
418
410
|
const homeGraphSemanticService = new KnowledgeSemanticService(homeGraphKnowledgeStore, {
|
|
419
411
|
llm: knowledgeSemanticLlm,
|
|
420
412
|
maxLlmSourcesPerReindex: 3,
|
|
@@ -424,12 +416,6 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
424
416
|
llm: knowledgeSemanticLlm,
|
|
425
417
|
maxLlmSourcesPerReindex: 3,
|
|
426
418
|
});
|
|
427
|
-
const knowledgeService = new KnowledgeService(knowledgeStore, artifactStore, undefined, {
|
|
428
|
-
memoryRegistry,
|
|
429
|
-
runtimeBus: options.runtimeBus,
|
|
430
|
-
semanticService: knowledgeSemanticService,
|
|
431
|
-
});
|
|
432
|
-
knowledgeService.attachRuntimeBus(options.runtimeBus);
|
|
433
419
|
const agentKnowledgeService = new KnowledgeService(agentKnowledgeStore, artifactStore, undefined, {
|
|
434
420
|
memoryRegistry,
|
|
435
421
|
runtimeBus: options.runtimeBus,
|
|
@@ -459,10 +445,6 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
459
445
|
serviceRegistry,
|
|
460
446
|
featureFlags,
|
|
461
447
|
});
|
|
462
|
-
knowledgeSemanticService.setGapRepairer(createWebKnowledgeGapRepairer({
|
|
463
|
-
searchService: webSearchService,
|
|
464
|
-
ingestService: knowledgeService,
|
|
465
|
-
}));
|
|
466
448
|
agentKnowledgeSemanticService.setGapRepairer(createWebKnowledgeGapRepairer({
|
|
467
449
|
searchService: webSearchService,
|
|
468
450
|
ingestService: agentKnowledgeService,
|
|
@@ -597,7 +579,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
|
|
|
597
579
|
automationManager,
|
|
598
580
|
gatewayMethods,
|
|
599
581
|
artifactStore,
|
|
600
|
-
knowledgeService,
|
|
582
|
+
knowledgeService: agentKnowledgeService,
|
|
601
583
|
agentKnowledgeService,
|
|
602
584
|
homeGraphService,
|
|
603
585
|
projectPlanningService,
|