@pellux/goodvibes-agent 0.1.111 → 0.1.113
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 +3 -3
- package/dist/package/main.js +9921 -9868
- package/docs/getting-started.md +3 -3
- package/package.json +1 -1
- package/src/cli/help.ts +4 -1
- package/src/cli/local-library-command.ts +90 -1
- package/src/cli/parser.ts +0 -8
- package/src/cli/service-posture.ts +1 -8
- package/src/cli/status.ts +1 -30
- package/src/cli/types.ts +0 -1
- package/src/input/agent-workspace-categories.ts +5 -7
- package/src/input/agent-workspace-snapshot.ts +2 -10
- package/src/input/agent-workspace-types.ts +2 -3
- package/src/input/commands/brief-runtime.ts +1 -1
- package/src/input/commands/channels-runtime.ts +397 -35
- package/src/input/commands/experience-runtime.ts +4 -9
- package/src/input/commands/health-runtime.ts +26 -6
- package/src/input/commands/mcp-runtime.ts +51 -26
- package/src/input/commands/memory.ts +10 -10
- package/src/input/commands/planning-runtime.ts +4 -9
- package/src/input/commands/provider-accounts-runtime.ts +2 -3
- package/src/input/commands/qrcode-runtime.ts +48 -9
- package/src/input/commands/recall-bundle.ts +16 -16
- package/src/input/commands/recall-capture.ts +18 -18
- package/src/input/commands/recall-query.ts +22 -22
- package/src/input/commands/recall-review.ts +12 -12
- package/src/input/commands/tasks-runtime.ts +9 -15
- package/src/input/commands/work-plan-runtime.ts +5 -19
- package/src/input/commands.ts +2 -7
- package/src/renderer/agent-workspace.ts +1 -2
- package/src/renderer/ui-factory.ts +1 -1
- package/src/runtime/onboarding/derivation.ts +6 -67
- package/src/version.ts +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* /
|
|
2
|
+
* /memory command handler.
|
|
3
3
|
*
|
|
4
4
|
* Implements the Project Memory Substrate commands:
|
|
5
5
|
*
|
|
6
|
-
* /
|
|
7
|
-
* /
|
|
8
|
-
* /
|
|
9
|
-
* /
|
|
10
|
-
* /
|
|
11
|
-
* /
|
|
12
|
-
* /
|
|
13
|
-
* /
|
|
6
|
+
* /memory add <class> <summary> — Add a new memory record
|
|
7
|
+
* /memory add <class> <summary> --detail <text> --tags <tag,tag>
|
|
8
|
+
* /memory search [query] — Search memory records
|
|
9
|
+
* /memory search --cls <class> — Filter by class
|
|
10
|
+
* /memory link <fromId> <toId> <relation> --yes — Link two records
|
|
11
|
+
* /memory get <id> — Show a single record with provenance
|
|
12
|
+
* /memory list [class] — List all records (optionally by class)
|
|
13
|
+
* /memory remove <id> --yes — Delete a record
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import type { SlashCommand, CommandContext } from '../command-registry.ts';
|
|
@@ -118,7 +118,7 @@ export const recallCommand: SlashCommand = {
|
|
|
118
118
|
|
|
119
119
|
default: {
|
|
120
120
|
const usage = [
|
|
121
|
-
'Usage: /
|
|
121
|
+
'Usage: /memory <subcommand>',
|
|
122
122
|
' add <class> <summary> [--scope <session|project|team>] [--detail <text>] [--tags <t,t>] [--session <id>] [--task <id>] [--file <path>]',
|
|
123
123
|
` classes: ${VALID_CLASSES.join(', ')}`,
|
|
124
124
|
' capture incident <id|latest> — Capture a forensics incident as durable memory',
|
|
@@ -44,7 +44,7 @@ function formatNextQuestion(question: ProjectPlanningQuestion | undefined): stri
|
|
|
44
44
|
if (!question) return 'No next question recorded.';
|
|
45
45
|
const lines = [`Next question: ${question.prompt}`];
|
|
46
46
|
if (question.recommendedAnswer) lines.push(`Recommended answer: ${question.recommendedAnswer}`);
|
|
47
|
-
lines.push('Answer in the prompt
|
|
47
|
+
lines.push('Answer in the main prompt or review planning state with /plan status.');
|
|
48
48
|
return lines.join('\n');
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -52,8 +52,8 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
52
52
|
registry.register({
|
|
53
53
|
name: 'plan',
|
|
54
54
|
description: 'Inspect or seed Agent workspace planning state',
|
|
55
|
-
usage: '[
|
|
56
|
-
argsHint: '[
|
|
55
|
+
usage: '[approve --yes | list | show <id> | mode | explain | override <strategy> --yes | status | clear --yes | <planning goal>]',
|
|
56
|
+
argsHint: '[approve|status|list|<goal>]',
|
|
57
57
|
async handler(args, ctx) {
|
|
58
58
|
const planManager = requirePlanManager(ctx);
|
|
59
59
|
const sessionLineageTracker = requireSessionLineageTracker(ctx);
|
|
@@ -74,7 +74,6 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
74
74
|
|
|
75
75
|
const projectPlanningService = ctx.workspace.projectPlanningService;
|
|
76
76
|
const projectId = ctx.workspace.projectPlanningProjectId;
|
|
77
|
-
const openProjectPlanningPanel = () => ctx.showPanel?.('project-planning');
|
|
78
77
|
|
|
79
78
|
if (args.length === 0) {
|
|
80
79
|
if (projectPlanningService && projectId) {
|
|
@@ -92,7 +91,6 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
92
91
|
const planningNamespace = String(
|
|
93
92
|
status[['knowledge', 'SpaceId'].join('') as keyof typeof status] ?? `project:${status.projectId}`,
|
|
94
93
|
);
|
|
95
|
-
openProjectPlanningPanel();
|
|
96
94
|
ctx.print(
|
|
97
95
|
`Project planning: ${evaluation.readiness}\n` +
|
|
98
96
|
`Project: ${status.projectId}\n` +
|
|
@@ -113,8 +111,7 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
113
111
|
}
|
|
114
112
|
|
|
115
113
|
if (args[0] === 'panel') {
|
|
116
|
-
|
|
117
|
-
ctx.print('Opened project planning panel.');
|
|
114
|
+
ctx.print('Planning panels are not part of the Agent workspace. Use /plan status or /plan list.');
|
|
118
115
|
return;
|
|
119
116
|
}
|
|
120
117
|
|
|
@@ -146,7 +143,6 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
146
143
|
},
|
|
147
144
|
});
|
|
148
145
|
const evaluation = await projectPlanningService.evaluate({ projectId });
|
|
149
|
-
openProjectPlanningPanel();
|
|
150
146
|
ctx.print(`Project planning approved. Readiness: ${evaluation.readiness}. State: ${result.state?.id ?? 'current'}.`);
|
|
151
147
|
return;
|
|
152
148
|
}
|
|
@@ -208,7 +204,6 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
208
204
|
? await persistEvaluatedNextQuestion(projectPlanningService, projectId, result.state, initialEvaluation)
|
|
209
205
|
: { state: result.state, evaluation: initialEvaluation };
|
|
210
206
|
sessionLineageTracker.setOriginalTask(taskDescription.slice(0, 200));
|
|
211
|
-
openProjectPlanningPanel();
|
|
212
207
|
|
|
213
208
|
ctx.print(
|
|
214
209
|
`Project planning seeded: "${state?.goal ?? taskDescription}"\n` +
|
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
ProviderAccountSnapshot,
|
|
5
5
|
} from '@/runtime/index.ts';
|
|
6
6
|
import {
|
|
7
|
-
openCommandPanel,
|
|
8
7
|
requireOperatorClient,
|
|
9
8
|
} from './runtime-services.ts';
|
|
10
9
|
|
|
@@ -25,11 +24,11 @@ export function registerProviderAccountsRuntimeCommands(registry: CommandRegistr
|
|
|
25
24
|
name: 'accounts',
|
|
26
25
|
aliases: ['account'],
|
|
27
26
|
description: 'Review provider auth routes, subscription windows, and billing-path safety',
|
|
28
|
-
usage: '[review|
|
|
27
|
+
usage: '[review|show <provider>|routes <provider>|repair <provider>]',
|
|
29
28
|
async handler(args, ctx) {
|
|
30
29
|
const sub = (args[0] ?? 'review').toLowerCase();
|
|
31
30
|
if (sub === 'panel' || sub === 'open') {
|
|
32
|
-
|
|
31
|
+
ctx.print('Provider account panels are not part of the Agent workspace. Use /accounts review.');
|
|
33
32
|
return;
|
|
34
33
|
}
|
|
35
34
|
const snapshot = await loadProviderAccountSnapshot(ctx);
|
|
@@ -1,20 +1,59 @@
|
|
|
1
1
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
2
|
-
import {
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { networkInterfaces } from 'node:os';
|
|
4
|
+
import {
|
|
5
|
+
buildCompanionConnectionInfo,
|
|
6
|
+
encodeConnectionPayload,
|
|
7
|
+
formatConnectionBlock,
|
|
8
|
+
generateQrMatrix,
|
|
9
|
+
getOrCreateCompanionToken,
|
|
10
|
+
renderQrToString,
|
|
11
|
+
} from '@pellux/goodvibes-sdk/platform/pairing';
|
|
12
|
+
import { GOODVIBES_AGENT_PAIRING_SURFACE } from '../../config/surface.ts';
|
|
13
|
+
import { resolveRuntimeEndpointBinding } from '../../cli/endpoints.ts';
|
|
14
|
+
import { requirePlatform, requireShellPaths } from './runtime-services.ts';
|
|
15
|
+
|
|
16
|
+
function getLocalNetworkIp(): string {
|
|
17
|
+
try {
|
|
18
|
+
const nets = networkInterfaces();
|
|
19
|
+
for (const name of Object.keys(nets)) {
|
|
20
|
+
for (const netInfo of nets[name] ?? []) {
|
|
21
|
+
if (netInfo.family === 'IPv4' && !netInfo.internal) return netInfo.address;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
return '127.0.0.1';
|
|
26
|
+
}
|
|
27
|
+
return '127.0.0.1';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function urlHostForBindHost(host: string): string {
|
|
31
|
+
if (host === '0.0.0.0' || host === '::') return getLocalNetworkIp();
|
|
32
|
+
return host || '127.0.0.1';
|
|
33
|
+
}
|
|
3
34
|
|
|
4
|
-
/**
|
|
5
|
-
* Register the /qrcode command.
|
|
6
|
-
*
|
|
7
|
-
* Opens the QR Code panel which displays a scannable QR code for
|
|
8
|
-
* companion app pairing, along with connection URL, token, and username.
|
|
9
|
-
*/
|
|
10
35
|
export function registerQrcodeRuntimeCommands(registry: CommandRegistry): void {
|
|
11
36
|
registry.register({
|
|
12
37
|
name: 'qrcode',
|
|
13
38
|
aliases: ['qr', 'pair'],
|
|
14
|
-
description: '
|
|
39
|
+
description: 'Print companion pairing details and a QR code',
|
|
15
40
|
usage: '',
|
|
16
41
|
handler(_args, ctx) {
|
|
17
|
-
|
|
42
|
+
const shellPaths = requireShellPaths(ctx);
|
|
43
|
+
const configManager = requirePlatform(ctx).configManager;
|
|
44
|
+
const daemonHomeDir = join(shellPaths.homeDirectory, '.goodvibes', 'daemon');
|
|
45
|
+
const tokenRecord = getOrCreateCompanionToken(GOODVIBES_AGENT_PAIRING_SURFACE, { daemonHomeDir });
|
|
46
|
+
const binding = resolveRuntimeEndpointBinding(configManager, 'controlPlane');
|
|
47
|
+
const daemonUrl = `http://${urlHostForBindHost(binding.host)}:${binding.port}`;
|
|
48
|
+
const info = buildCompanionConnectionInfo({
|
|
49
|
+
daemonUrl,
|
|
50
|
+
token: tokenRecord.token,
|
|
51
|
+
username: 'admin',
|
|
52
|
+
surface: GOODVIBES_AGENT_PAIRING_SURFACE,
|
|
53
|
+
});
|
|
54
|
+
const payload = encodeConnectionPayload(info);
|
|
55
|
+
const qr = renderQrToString(generateQrMatrix(payload));
|
|
56
|
+
ctx.print([formatConnectionBlock(info, payload), '', qr].join('\n'));
|
|
18
57
|
},
|
|
19
58
|
});
|
|
20
59
|
}
|
|
@@ -18,11 +18,11 @@ export function handleRecallExport(args: string[], context: CommandContext): voi
|
|
|
18
18
|
|
|
19
19
|
const pathArg = commandArgs[0];
|
|
20
20
|
if (!pathArg) {
|
|
21
|
-
context.print('[
|
|
21
|
+
context.print('[memory] Usage: /memory export <path> [--scope <scope>] [--cls <class>] --yes');
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
if (!parsed.yes) {
|
|
25
|
-
requireYesFlag(context, `export durable memory bundle to ${pathArg}`, '/
|
|
25
|
+
requireYesFlag(context, `export durable memory bundle to ${pathArg}`, '/memory export <path> [--scope <scope>] [--cls <class>] --yes');
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -31,7 +31,7 @@ export function handleRecallExport(args: string[], context: CommandContext): voi
|
|
|
31
31
|
if (scopeIdx !== -1 && commandArgs[scopeIdx + 1]) {
|
|
32
32
|
const scope = commandArgs[scopeIdx + 1];
|
|
33
33
|
if (!isValidScope(scope)) {
|
|
34
|
-
context.print(`[
|
|
34
|
+
context.print(`[memory] Unknown scope "${scope}". Valid: ${VALID_SCOPES.join(', ')}`);
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
filter.scope = scope;
|
|
@@ -41,7 +41,7 @@ export function handleRecallExport(args: string[], context: CommandContext): voi
|
|
|
41
41
|
if (clsIdx !== -1 && commandArgs[clsIdx + 1]) {
|
|
42
42
|
const cls = commandArgs[clsIdx + 1];
|
|
43
43
|
if (!isValidClass(cls)) {
|
|
44
|
-
context.print(`[
|
|
44
|
+
context.print(`[memory] Unknown class "${cls}". Valid: ${VALID_CLASSES.join(', ')}`);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
filter.cls = cls;
|
|
@@ -51,7 +51,7 @@ export function handleRecallExport(args: string[], context: CommandContext): voi
|
|
|
51
51
|
const targetPath = resolveBundlePath(pathArg, requireShellPaths(context));
|
|
52
52
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
53
53
|
writeFileSync(targetPath, JSON.stringify(bundle, null, 2) + '\n', 'utf-8');
|
|
54
|
-
context.print(`[
|
|
54
|
+
context.print(`[memory] Exported ${bundle.recordCount} record(s) and ${bundle.linkCount} link(s) to ${targetPath}`);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export async function handleRecallImport(args: string[], context: CommandContext): Promise<void> {
|
|
@@ -64,11 +64,11 @@ export async function handleRecallImport(args: string[], context: CommandContext
|
|
|
64
64
|
|
|
65
65
|
const pathArg = commandArgs[0];
|
|
66
66
|
if (!pathArg) {
|
|
67
|
-
context.print('[
|
|
67
|
+
context.print('[memory] Usage: /memory import <path> --yes');
|
|
68
68
|
return;
|
|
69
69
|
}
|
|
70
70
|
if (!parsed.yes) {
|
|
71
|
-
requireYesFlag(context, `import durable memory bundle from ${pathArg}`, '/
|
|
71
|
+
requireYesFlag(context, `import durable memory bundle from ${pathArg}`, '/memory import <path> --yes');
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -77,12 +77,12 @@ export async function handleRecallImport(args: string[], context: CommandContext
|
|
|
77
77
|
try {
|
|
78
78
|
bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as MemoryBundle;
|
|
79
79
|
} catch (error) {
|
|
80
|
-
context.print(`[
|
|
80
|
+
context.print(`[memory] Failed to read memory bundle: ${summarizeError(error)}`);
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
const result = await memory.importBundle(bundle);
|
|
85
|
-
context.print(`[
|
|
85
|
+
context.print(`[memory] Imported bundle from ${targetPath}`);
|
|
86
86
|
context.print(` Records: imported=${result.importedRecords} skipped=${result.skippedRecords}`);
|
|
87
87
|
context.print(` Links: imported=${result.importedLinks}`);
|
|
88
88
|
}
|
|
@@ -106,30 +106,30 @@ export function handleRecallHandoffExport(args: string[], context: CommandContex
|
|
|
106
106
|
}
|
|
107
107
|
const pathArg = commandArgs[0];
|
|
108
108
|
if (!pathArg) {
|
|
109
|
-
context.print('[
|
|
109
|
+
context.print('[memory] Usage: /memory handoff-export <path> [--scope <scope>] --yes');
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
if (!parsed.yes) {
|
|
113
|
-
requireYesFlag(context, `export memory handoff bundle to ${pathArg}`, '/
|
|
113
|
+
requireYesFlag(context, `export memory handoff bundle to ${pathArg}`, '/memory handoff-export <path> [--scope <scope>] --yes');
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
const scopeIdx = commandArgs.indexOf('--scope');
|
|
117
117
|
const scopeRaw = scopeIdx !== -1 ? commandArgs[scopeIdx + 1] : 'team';
|
|
118
118
|
if (!scopeRaw || !isValidScope(scopeRaw)) {
|
|
119
|
-
context.print(`[
|
|
119
|
+
context.print(`[memory] Unknown scope "${scopeRaw ?? ''}". Valid: ${VALID_SCOPES.join(', ')}`);
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
122
|
const bundle = memory.exportBundle({ scope: scopeRaw });
|
|
123
123
|
const targetPath = resolveBundlePath(pathArg, requireShellPaths(context));
|
|
124
124
|
mkdirSync(dirname(targetPath), { recursive: true });
|
|
125
125
|
writeFileSync(targetPath, JSON.stringify(bundle, null, 2) + '\n', 'utf-8');
|
|
126
|
-
context.print(`[
|
|
126
|
+
context.print(`[memory] Exported ${scopeRaw} handoff bundle to ${targetPath}`);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
export function handleRecallHandoffInspect(args: string[], context: CommandContext): void {
|
|
130
130
|
const pathArg = args[0];
|
|
131
131
|
if (!pathArg) {
|
|
132
|
-
context.print('[
|
|
132
|
+
context.print('[memory] Usage: /memory handoff-inspect <path>');
|
|
133
133
|
return;
|
|
134
134
|
}
|
|
135
135
|
const targetPath = resolveBundlePath(pathArg, requireShellPaths(context));
|
|
@@ -137,7 +137,7 @@ export function handleRecallHandoffInspect(args: string[], context: CommandConte
|
|
|
137
137
|
const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as MemoryBundle;
|
|
138
138
|
context.print(inspectBundle(bundle));
|
|
139
139
|
} catch (error) {
|
|
140
|
-
context.print(`[
|
|
140
|
+
context.print(`[memory] Failed to inspect handoff bundle: ${summarizeError(error)}`);
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
@@ -146,7 +146,7 @@ export async function handleRecallHandoffImport(args: string[], context: Command
|
|
|
146
146
|
const commandArgs = [...parsed.rest];
|
|
147
147
|
const pathArg = commandArgs[0];
|
|
148
148
|
if (!pathArg) {
|
|
149
|
-
context.print('[
|
|
149
|
+
context.print('[memory] Usage: /memory handoff-import <path> --yes');
|
|
150
150
|
return;
|
|
151
151
|
}
|
|
152
152
|
await handleRecallImport([pathArg, ...(parsed.yes ? ['--yes'] : [])], context);
|
|
@@ -17,7 +17,7 @@ export async function handleRecallAdd(args: string[], context: CommandContext):
|
|
|
17
17
|
|
|
18
18
|
const cls = args[0];
|
|
19
19
|
if (!cls || !isValidClass(cls)) {
|
|
20
|
-
context.print(`[
|
|
20
|
+
context.print(`[memory] Invalid class "${cls ?? ''}". Valid: ${VALID_CLASSES.join(', ')}`);
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -36,7 +36,7 @@ export async function handleRecallAdd(args: string[], context: CommandContext):
|
|
|
36
36
|
const scope = scopeRaw && isValidScope(scopeRaw) ? scopeRaw : 'project';
|
|
37
37
|
|
|
38
38
|
if (scopeRaw && !isValidScope(scopeRaw)) {
|
|
39
|
-
context.print(`[
|
|
39
|
+
context.print(`[memory] Invalid scope "${scopeRaw}". Valid: ${VALID_SCOPES.join(', ')}`);
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -55,12 +55,12 @@ export async function handleRecallAdd(args: string[], context: CommandContext):
|
|
|
55
55
|
}
|
|
56
56
|
const summary = summaryTokens.join(' ');
|
|
57
57
|
if (!summary.trim()) {
|
|
58
|
-
context.print('[
|
|
58
|
+
context.print('[memory] Usage: /memory add <class> <summary> [--detail <text>] [--tags <t1,t2>]');
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const record = await memory.add({ scope, cls, summary, detail, tags, provenance });
|
|
63
|
-
context.print(`[
|
|
63
|
+
context.print(`[memory] Added ${cls}: ${record.id}`);
|
|
64
64
|
context.print(` Scope: ${record.scope}`);
|
|
65
65
|
context.print(` Summary: ${record.summary}`);
|
|
66
66
|
if (record.tags.length) context.print(` Tags: ${record.tags.join(', ')}`);
|
|
@@ -76,7 +76,7 @@ export async function handleRecallCapture(args: string[], context: CommandContex
|
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
if (!context.extensions.forensicsRegistry) {
|
|
79
|
-
context.print('[
|
|
79
|
+
context.print('[memory] Forensics registry not available.');
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
|
|
@@ -87,66 +87,66 @@ export async function handleRecallCapture(args: string[], context: CommandContex
|
|
|
87
87
|
? context.extensions.forensicsRegistry.latest()
|
|
88
88
|
: context.extensions.forensicsRegistry.getById(requestedId);
|
|
89
89
|
if (!report) {
|
|
90
|
-
context.print(`[
|
|
90
|
+
context.print(`[memory] Incident not found: ${requestedId ?? 'latest'}`);
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
const bundle = context.extensions.forensicsRegistry.buildBundle(report.id);
|
|
94
94
|
if (!bundle) {
|
|
95
|
-
context.print(`[
|
|
95
|
+
context.print(`[memory] Failed to build incident bundle: ${report.id}`);
|
|
96
96
|
return;
|
|
97
97
|
}
|
|
98
98
|
const record = await memory.add(buildIncidentMemoryAddOptions(bundle));
|
|
99
|
-
context.print(`[
|
|
99
|
+
context.print(`[memory] Captured incident ${report.id} into memory as ${record.id}`);
|
|
100
100
|
return;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
if (target === 'policy') {
|
|
104
104
|
const review = context.extensions.policyRuntimeState?.getSnapshot().lastPreflightReview;
|
|
105
105
|
if (!review) {
|
|
106
|
-
context.print('[
|
|
106
|
+
context.print('[memory] No policy preflight review is available to capture.');
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
109
|
const record = await memory.add(buildPolicyPreflightMemoryAddOptions(review));
|
|
110
|
-
context.print(`[
|
|
110
|
+
context.print(`[memory] Captured policy preflight into memory as ${record.id}`);
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
if (target === 'mcp') {
|
|
115
115
|
const serverName = args[1];
|
|
116
116
|
if (!serverName) {
|
|
117
|
-
context.print('[
|
|
117
|
+
context.print('[memory] Usage: /memory capture mcp <server>');
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
const server = context.extensions.mcpRegistry.listServerSecurity().find((entry) => entry.name === serverName);
|
|
121
121
|
if (!server) {
|
|
122
|
-
context.print(`[
|
|
122
|
+
context.print(`[memory] MCP server not found: ${serverName}`);
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
const record = await memory.add(buildMcpSecurityMemoryAddOptions(server));
|
|
126
|
-
context.print(`[
|
|
126
|
+
context.print(`[memory] Captured MCP server ${server.name} into memory as ${record.id}`);
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
if (target === 'plugin') {
|
|
131
131
|
if (!pluginManager) {
|
|
132
|
-
context.print('[
|
|
132
|
+
context.print('[memory] Plugin manager not available.');
|
|
133
133
|
return;
|
|
134
134
|
}
|
|
135
135
|
const pluginName = args[1];
|
|
136
136
|
if (!pluginName) {
|
|
137
|
-
context.print('[
|
|
137
|
+
context.print('[memory] Usage: /memory capture plugin <name>');
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
140
140
|
const plugin = pluginManager.list().find((entry) => entry.name === pluginName);
|
|
141
141
|
if (!plugin) {
|
|
142
|
-
context.print(`[
|
|
142
|
+
context.print(`[memory] Plugin not found: ${pluginName}`);
|
|
143
143
|
return;
|
|
144
144
|
}
|
|
145
145
|
const quarantineReason = pluginManager.getQuarantineRecord(plugin.name)?.reason;
|
|
146
146
|
const record = await memory.add(buildPluginSecurityMemoryAddOptions(plugin, quarantineReason));
|
|
147
|
-
context.print(`[
|
|
147
|
+
context.print(`[memory] Captured plugin ${plugin.name} into memory as ${record.id}`);
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
context.print('[
|
|
151
|
+
context.print('[memory] Usage: /memory capture incident <id|latest> | /memory capture policy | /memory capture mcp <server> | /memory capture plugin <name>');
|
|
152
152
|
}
|
|
@@ -7,8 +7,8 @@ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
|
7
7
|
export function getMemoryApi(context: CommandContext): MemoryApi | null {
|
|
8
8
|
const memoryApi = context.clients?.agentKnowledgeApi?.memory;
|
|
9
9
|
if (!memoryApi) {
|
|
10
|
-
context.print('[
|
|
11
|
-
context.print('[
|
|
10
|
+
context.print('[memory] Agent Memory API is not available in this runtime.');
|
|
11
|
+
context.print('[memory] Refusing to use default Knowledge/Wiki or non-Agent knowledge fallback.');
|
|
12
12
|
return null;
|
|
13
13
|
}
|
|
14
14
|
return memoryApi;
|
|
@@ -29,7 +29,7 @@ export function handleRecallSearch(args: string[], context: CommandContext): voi
|
|
|
29
29
|
const cls = args[clsIdx + 1];
|
|
30
30
|
if (isValidClass(cls)) filter.cls = cls;
|
|
31
31
|
else {
|
|
32
|
-
context.print(`[
|
|
32
|
+
context.print(`[memory] Unknown class "${cls}". Valid: ${VALID_CLASSES.join(', ')}`);
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -39,7 +39,7 @@ export function handleRecallSearch(args: string[], context: CommandContext): voi
|
|
|
39
39
|
const scope = args[scopeIdx + 1];
|
|
40
40
|
if (isValidScope(scope)) filter.scope = scope;
|
|
41
41
|
else {
|
|
42
|
-
context.print(`[
|
|
42
|
+
context.print(`[memory] Unknown scope "${scope}". Valid: ${VALID_SCOPES.join(', ')}`);
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -59,11 +59,11 @@ export function handleRecallSearch(args: string[], context: CommandContext): voi
|
|
|
59
59
|
const semanticResults = semantic ? memory.searchSemantic(filter) : [];
|
|
60
60
|
const results = semantic ? semanticResults.map((entry) => entry.record) : memory.search(filter);
|
|
61
61
|
if (!results.length) {
|
|
62
|
-
context.print('[
|
|
62
|
+
context.print('[memory] No records found.');
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
context.print(`[
|
|
66
|
+
context.print(`[memory] ${results.length} ${semantic ? 'semantic ' : ''}record(s):`);
|
|
67
67
|
for (const record of results) {
|
|
68
68
|
const semanticEntry = semanticResults.find((entry) => entry.record.id === record.id);
|
|
69
69
|
const ts = new Date(record.createdAt).toISOString().slice(0, 16).replace('T', ' ');
|
|
@@ -93,7 +93,7 @@ export function handleRecallVector(args: string[], context: CommandContext): voi
|
|
|
93
93
|
context.print(formatVectorStats(memory.vectorStats(), sub));
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
|
-
context.print('[
|
|
96
|
+
context.print('[memory] Usage: /memory vector [status|doctor|rebuild]');
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function formatVectorStats(
|
|
@@ -101,7 +101,7 @@ function formatVectorStats(
|
|
|
101
101
|
label: string,
|
|
102
102
|
): string {
|
|
103
103
|
return [
|
|
104
|
-
`[
|
|
104
|
+
`[memory] Vector index ${label}`,
|
|
105
105
|
` backend: ${stats.backend}`,
|
|
106
106
|
` enabled: ${stats.enabled ? 'yes' : 'no'}`,
|
|
107
107
|
` available: ${stats.available ? 'yes' : 'no'}`,
|
|
@@ -119,16 +119,16 @@ export function handleRecallGet(args: string[], context: CommandContext): void {
|
|
|
119
119
|
}
|
|
120
120
|
const id = args[0];
|
|
121
121
|
if (!id) {
|
|
122
|
-
context.print('[
|
|
122
|
+
context.print('[memory] Usage: /memory get <id>');
|
|
123
123
|
return;
|
|
124
124
|
}
|
|
125
125
|
const record = memory.get(id);
|
|
126
126
|
if (!record) {
|
|
127
|
-
context.print(`[
|
|
127
|
+
context.print(`[memory] Record not found: ${id}`);
|
|
128
128
|
return;
|
|
129
129
|
}
|
|
130
130
|
const ts = new Date(record.createdAt).toISOString().slice(0, 19).replace('T', ' ');
|
|
131
|
-
context.print(`[
|
|
131
|
+
context.print(`[memory] ${record.id}`);
|
|
132
132
|
context.print(` Scope: ${record.scope}`);
|
|
133
133
|
context.print(` Class: ${record.cls}`);
|
|
134
134
|
context.print(` Summary: ${record.summary}`);
|
|
@@ -163,19 +163,19 @@ export async function handleRecallLink(args: string[], context: CommandContext):
|
|
|
163
163
|
const parsed = stripYesFlag(args);
|
|
164
164
|
const [fromId, toId, relation] = parsed.rest;
|
|
165
165
|
if (!fromId || !toId || !relation) {
|
|
166
|
-
context.print('[
|
|
166
|
+
context.print('[memory] Usage: /memory link <fromId> <toId> <relation> --yes');
|
|
167
167
|
return;
|
|
168
168
|
}
|
|
169
169
|
if (!parsed.yes) {
|
|
170
|
-
requireYesFlag(context, `link memory records ${fromId} and ${toId}`, '/
|
|
170
|
+
requireYesFlag(context, `link memory records ${fromId} and ${toId}`, '/memory link <fromId> <toId> <relation> --yes');
|
|
171
171
|
return;
|
|
172
172
|
}
|
|
173
173
|
const link = await memory.link(fromId, toId, relation);
|
|
174
174
|
if (!link) {
|
|
175
|
-
context.print('[
|
|
175
|
+
context.print('[memory] Link failed — check that both IDs exist.');
|
|
176
176
|
return;
|
|
177
177
|
}
|
|
178
|
-
context.print(`[
|
|
178
|
+
context.print(`[memory] Linked: ${fromId} -> ${toId} [${relation}]`);
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
export function handleRecallRemove(args: string[], context: CommandContext): void {
|
|
@@ -186,19 +186,19 @@ export function handleRecallRemove(args: string[], context: CommandContext): voi
|
|
|
186
186
|
const parsed = stripYesFlag(args);
|
|
187
187
|
const id = parsed.rest[0];
|
|
188
188
|
if (!id) {
|
|
189
|
-
context.print('[
|
|
189
|
+
context.print('[memory] Usage: /memory remove <id> --yes');
|
|
190
190
|
return;
|
|
191
191
|
}
|
|
192
192
|
if (!parsed.yes) {
|
|
193
|
-
requireYesFlag(context, `delete durable memory record ${id}`, '/
|
|
193
|
+
requireYesFlag(context, `delete durable memory record ${id}`, '/memory remove <id> --yes');
|
|
194
194
|
return;
|
|
195
195
|
}
|
|
196
196
|
const removed = memory.delete(id);
|
|
197
197
|
if (!removed) {
|
|
198
|
-
context.print(`[
|
|
198
|
+
context.print(`[memory] Record not found: ${id}`);
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
|
-
context.print(`[
|
|
201
|
+
context.print(`[memory] Deleted: ${id}`);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
export function handleRecallList(args: string[], context: CommandContext): void {
|
|
@@ -213,7 +213,7 @@ export function handleRecallList(args: string[], context: CommandContext): void
|
|
|
213
213
|
if (scopeIdx !== -1 && args[scopeIdx + 1]) {
|
|
214
214
|
const scope = args[scopeIdx + 1];
|
|
215
215
|
if (!isValidScope(scope)) {
|
|
216
|
-
context.print(`[
|
|
216
|
+
context.print(`[memory] Unknown scope "${scope}". Valid: ${VALID_SCOPES.join(', ')}`);
|
|
217
217
|
return;
|
|
218
218
|
}
|
|
219
219
|
filter.scope = scope;
|
|
@@ -221,7 +221,7 @@ export function handleRecallList(args: string[], context: CommandContext): void
|
|
|
221
221
|
|
|
222
222
|
const records = memory.search(filter);
|
|
223
223
|
if (!records.length) {
|
|
224
|
-
context.print('[
|
|
224
|
+
context.print('[memory] No records.');
|
|
225
225
|
return;
|
|
226
226
|
}
|
|
227
227
|
|
|
@@ -232,7 +232,7 @@ export function handleRecallList(args: string[], context: CommandContext): void
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
for (const [clsName, group] of Object.entries(grouped)) {
|
|
235
|
-
context.print(`\n[
|
|
235
|
+
context.print(`\n[memory] ${clsName.toUpperCase()} (${group.length}):`);
|
|
236
236
|
for (const record of group) {
|
|
237
237
|
const tagStr = record.tags.length ? ` [${record.tags.join(', ')}]` : '';
|
|
238
238
|
context.print(` ${record.id} [${record.scope}]${tagStr} ${record.summary}`);
|