aiden-runtime 3.18.0 → 3.19.4

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.
@@ -0,0 +1,344 @@
1
+ "use strict";
2
+ // ── cli/commandCatalog.ts ─────────────────────────────────────────────────────
3
+ // Single source of truth for all slash commands — descriptions, usage, subs,
4
+ // examples, section groupings, and (C2+) runtime dispatch handlers.
5
+ //
6
+ // Consumed by:
7
+ // • cli/commandPalette.ts (interactive arrow-key palette)
8
+ // • cli/aiden.ts (Tab completer + /help handler + dispatch)
9
+ // • core/pluginLoader.ts (plugin command registration — C4)
10
+ // ─────────────────────────────────────────────────────────────────────────────
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.COMMANDS = exports.COMMAND_DETAIL = void 0;
13
+ exports.register = register;
14
+ exports.unregister = unregister;
15
+ exports.list = list;
16
+ exports.get = get;
17
+ exports.generation = generation;
18
+ exports.getCatalog = getCatalog;
19
+ // ── Full command registry (static metadata) ───────────────────────────────────
20
+ // C2 adds the 6 commands that existed in /help display but were missing here.
21
+ // Handlers are NOT stored here — they are registered at runtime via
22
+ // registerCoreCommands() in cli/aiden.ts, which closes over module-scope state.
23
+ exports.COMMAND_DETAIL = {
24
+ '/new': { section: 'Session', desc: 'Start a fresh session (clears local history).', usage: '/new' },
25
+ '/reset': { section: 'Session', desc: 'Alias for /new.', usage: '/reset' },
26
+ '/clear': { section: 'Session', desc: 'Clear the terminal screen.', usage: '/clear' },
27
+ '/history': { section: 'Session', desc: 'Show conversation history for this session.', usage: '/history' },
28
+ '/stop': { section: 'Session', desc: 'Interrupt an in-flight LLM call.', usage: '/stop' },
29
+ '/export': { section: 'Session', desc: 'Export conversation as Markdown or JSON.', usage: '/export md|json' },
30
+ '/fork': { section: 'Session', desc: 'Branch current session under a new name.', usage: '/fork <name>' },
31
+ '/checkpoint': { section: 'Session', desc: 'Save a state snapshot.', usage: '/checkpoint' },
32
+ '/timing': { section: 'Info', desc: 'Show timing breakdown for the last response.', usage: '/timing' },
33
+ '/version': { section: 'Info', desc: 'Show current version and check for updates.', usage: '/version' },
34
+ '/status': { section: 'Info', desc: 'Show server health, uptime, RAM.', usage: '/status' },
35
+ '/tools': { section: 'Info', desc: 'All registered tools grouped by category.', usage: '/tools' },
36
+ '/kit': { section: 'Info', desc: 'Toolkit categories — enable or disable tool groups.', usage: '/kit' },
37
+ '/providers': { section: 'Info', desc: 'Provider chain with rate-limit status.', usage: '/providers' },
38
+ '/models': { section: 'Info', desc: 'Model assignments for each task type.', usage: '/models' },
39
+ '/memory': { section: 'Info', desc: 'Conversation memory stats.', usage: '/memory' },
40
+ '/memsearch': { section: 'Info', desc: 'Search memories by keyword — Layer 1 progressive disclosure.',
41
+ usage: '/memsearch <query>',
42
+ examples: ['/memsearch archon provider routing', '/memsearch phase 14 lazy loading'],
43
+ },
44
+ '/memtimeline': { section: 'Info', desc: 'Chronological context around a memory ID — Layer 2.',
45
+ usage: '/memtimeline <mem_id>',
46
+ examples: ['/memtimeline mem_000001'],
47
+ },
48
+ '/memget': { section: 'Info', desc: 'Full detail for specific memory IDs — Layer 3.',
49
+ usage: '/memget <id1,id2,...>',
50
+ examples: ['/memget mem_000001', '/memget mem_000001,mem_000002'],
51
+ },
52
+ '/goals': { section: 'Info', desc: 'Active goals queue.', usage: '/goals' },
53
+ '/skills': { section: 'Info', desc: 'Skill lifecycle manager.',
54
+ subs: ['search <q>', 'install <name>', 'list', 'check', 'update <name>', 'audit', 'remove <name>',
55
+ 'publish <name>', 'export <name>', 'import <source>', 'import-repo <owner/repo>',
56
+ 'validate [id]', 'source <name>', 'stats', 'recommend', 'test <name>',
57
+ 'enable <id>', 'disable <id>', 'approve <id>', 'reject <id>', 'explore <topic>',
58
+ 'review [<id>]'],
59
+ examples: ['/skills list', '/skills search http', '/skills validate', '/skills import owner/repo',
60
+ '/skills import-repo anthropics/skills', '/skills enable algorithmic-art'],
61
+ usage: '/skills [list|search|install|enable|disable|validate|import|...] [args]',
62
+ },
63
+ // ── Gap 1 (C2): 3 Info commands previously missing from catalog ──────────────
64
+ '/plugins': { section: 'Info', desc: 'Plugin manager — list loaded plugins or force reload.',
65
+ subs: ['list', 'reload'],
66
+ examples: ['/plugins', '/plugins reload'],
67
+ usage: '/plugins [list|reload]',
68
+ },
69
+ '/profile': { section: 'Info', desc: 'View, edit, or clear the structured user profile (Honcho model).',
70
+ subs: ['edit', 'clear'],
71
+ examples: ['/profile', '/profile edit', '/profile clear'],
72
+ usage: '/profile [edit|clear]',
73
+ },
74
+ '/failed': { section: 'Info', desc: 'Signal last exchange failed — triggers failure trace analysis + lesson.',
75
+ usage: '/failed [reason]',
76
+ examples: ['/failed', '/failed wrong file was edited'],
77
+ },
78
+ '/lessons': { section: 'Info', desc: 'Browse permanent failure rules stored in LESSONS.md.',
79
+ subs: ['search <q>', 'web|shell|files|planning|provider|memory|skills|errors|general'],
80
+ examples: ['/lessons', '/lessons search rate limit', '/lessons errors'],
81
+ usage: '/lessons [search <q>|<category>]',
82
+ },
83
+ '/teach': { section: 'Info', desc: 'Append a manual rule to LESSONS.md.',
84
+ usage: '/teach <rule text>',
85
+ examples: ['/teach If rate limit hit, wait 60s before retry'],
86
+ },
87
+ '/focus': { section: 'Info', desc: 'Toggle zen mode — suppress tool traces and status output.', usage: '/focus' },
88
+ '/explore': { section: 'Info', desc: 'Capability browser.',
89
+ subs: ['tools', 'skills', 'providers'],
90
+ examples: ['/explore tools', '/explore skills'],
91
+ usage: '/explore [tools|skills|providers]',
92
+ },
93
+ '/pulse': { section: 'Info', desc: 'Live system dashboard — uptime, RAM, providers, async tasks.', usage: '/pulse' },
94
+ '/rewind': { section: 'Info', desc: 'Time-travel undo — mark a restore point, then jump back.',
95
+ subs: ['mark [label]', 'undo', '<n>'],
96
+ examples: ['/rewind mark safe', '/rewind undo', '/rewind 2'],
97
+ usage: '/rewind [mark <label>|undo|<n>]',
98
+ },
99
+ '/pin': { section: 'Info', desc: 'Pin an exchange so it is never compacted.',
100
+ subs: ['[label]', 'list', 'unpin <idx>'],
101
+ examples: ['/pin important fact', '/pin list', '/pin unpin 0'],
102
+ usage: '/pin [<label>|list|unpin <idx>]',
103
+ },
104
+ '/diff': { section: 'Info', desc: 'Filesystem changes since last commit (git status).', usage: '/diff' },
105
+ '/trust': { section: 'Info', desc: 'Per-tool approval levels.',
106
+ subs: ['list', 'set <tool> <0|1|2|3>', 'reset <tool>'],
107
+ examples: ['/trust list', '/trust set web_search 2', '/trust reset web_search'],
108
+ usage: '/trust [list|set <tool> <level>|reset <tool>]',
109
+ },
110
+ '/timeline': { section: 'Info', desc: 'ASCII tree of all sessions with branching and message counts.', usage: '/timeline' },
111
+ '/garden': { section: 'Info', desc: 'Memory layer explorer — all 9 memory stores at a glance.',
112
+ subs: ['semantic', 'entities', 'learning', 'facts'],
113
+ examples: ['/garden', '/garden semantic', '/garden entities'],
114
+ usage: '/garden [semantic|entities|learning|facts]',
115
+ },
116
+ '/decision': { section: 'Info', desc: 'Per-turn reasoning trace from the decision log.',
117
+ subs: ['[N]', 'last', 'clear'],
118
+ examples: ['/decision', '/decision 50', '/decision last', '/decision clear'],
119
+ usage: '/decision [<N>|last|clear]',
120
+ },
121
+ '/log': { section: 'Core', desc: 'Show recent log buffer entries.',
122
+ usage: '/log [N] [level]',
123
+ examples: ['/log', '/log 50', '/log 20 error'],
124
+ },
125
+ '/save': { section: 'Core', desc: 'Save conversation to workspace/exports/ as Markdown.',
126
+ usage: '/save [filename]',
127
+ examples: ['/save', '/save my-session'],
128
+ },
129
+ '/rerun': { section: 'Core', desc: 'Re-send the last user message.', usage: '/rerun' },
130
+ '/name': { section: 'Core', desc: 'Give the current session a human-readable name.',
131
+ usage: '/name <label>',
132
+ examples: ['/name debugging-auth-bug', '/name'],
133
+ },
134
+ '/stack': { section: 'Core', desc: 'Show active async task stack.', usage: '/stack' },
135
+ '/halt': { section: 'Core', desc: 'Hard-stop all execution and LLM calls immediately.', usage: '/halt' },
136
+ '/yolo': { section: 'Core', desc: 'Toggle YOLO mode — auto-approve all tool calls.', usage: '/yolo' },
137
+ '/attach': { section: 'Core', desc: 'Queue a file as context prepended to the next message.',
138
+ subs: ['<path>', 'list', 'clear'],
139
+ examples: ['/attach ./notes.txt', '/attach list', '/attach clear'],
140
+ usage: '/attach [<path>|list|clear]',
141
+ },
142
+ '/changelog': { section: 'Core', desc: 'Show recent git commits or workspace file changes.',
143
+ usage: '/changelog [N]',
144
+ examples: ['/changelog', '/changelog 50'],
145
+ },
146
+ '/sessions': { section: 'Info', desc: 'List recent sessions.', usage: '/sessions' },
147
+ '/recipes': { section: 'Info', desc: 'List saved YAML prompt recipes.', usage: '/recipes' },
148
+ '/analytics': { section: 'Info', desc: 'Token usage analytics over time.', usage: '/analytics' },
149
+ '/budget': { section: 'Info', desc: 'Estimated token cost for this session.', usage: '/budget' },
150
+ '/workspace': { section: 'Info', desc: 'Show current workspace path and contents.', usage: '/workspace' },
151
+ '/model': { section: 'Config', desc: 'Switch the active LLM model.', usage: '/model <name>' },
152
+ '/provider': { section: 'Config', desc: 'Manage API providers — list, add, remove, test.',
153
+ usage: '/provider [list|add|add-custom|remove|test]',
154
+ subs: ['list', 'add <type> <key>', 'add-custom <name> <baseUrl> [key]', 'remove <id>', 'test <id>'],
155
+ examples: ['/provider list', '/provider add groq sk-xxx', '/provider test groq-1'],
156
+ },
157
+ '/primary': { section: 'Config', desc: 'Pin a provider to front of the chain.', usage: '/primary [list|<name>|reset]' },
158
+ '/switch': { section: 'Config', desc: 'Alias for /primary — pin a provider to front of chain.', usage: '/switch [list|<name>|reset]' },
159
+ '/theme': { section: 'Config', desc: 'Change color theme.',
160
+ usage: '/theme <name>',
161
+ examples: ['/theme default', '/theme mono', '/theme slate', '/theme ember'],
162
+ },
163
+ '/persona': { section: 'Config', desc: 'Switch response persona.',
164
+ usage: '/persona <name>',
165
+ examples: ['/persona default', '/persona concise', '/persona technical'],
166
+ },
167
+ '/detail': { section: 'Config', desc: 'Cycle tool-trace detail level: off → tools → verbose.', usage: '/detail' },
168
+ '/depth': { section: 'Config', desc: 'Cycle reasoning depth: low → medium → high.', usage: '/depth' },
169
+ '/config': { section: 'Config', desc: 'Show current configuration snapshot.', usage: '/config' },
170
+ '/quick': { section: 'Power', desc: 'Quick side question — no history, no tools.', usage: '/quick <question>' },
171
+ '/compact': { section: 'Power', desc: 'Manual context compression.', usage: '/compact' },
172
+ '/async': { section: 'Power', desc: 'Run a task in the background.', usage: '/async <task>' },
173
+ '/run': { section: 'Power', desc: 'Execute JS in the Aiden VM sandbox with full SDK access.',
174
+ subs: ['<file.js>', '- [desc]', 'examples', 'help', 'help <ns>'],
175
+ examples: ['/run scripts/port_checker.js', '/run -', '/run examples', '/run help'],
176
+ usage: '/run <file.js> | - | examples | help [ns]',
177
+ },
178
+ '/spawn': { section: 'Power', desc: 'Delegate a sub-task to an isolated subagent.',
179
+ subs: ['<task>', 'list', 'kill <id>'],
180
+ examples: ['/spawn summarise the logs', '/spawn list', '/spawn kill spawn_1234_abc'],
181
+ usage: '/spawn <task> | list | kill <id>',
182
+ },
183
+ '/swarm': { section: 'Power', desc: 'Run N parallel subagents and aggregate results.',
184
+ subs: ['<task>', '<task> --n=<N>', '<task> --strategy=vote|merge|best'],
185
+ examples: ['/swarm research quantum computing', '/swarm write a haiku --n=5 --strategy=vote'],
186
+ usage: '/swarm <task> [--n=3] [--strategy=vote|merge|best]',
187
+ },
188
+ '/search': { section: 'Power', desc: 'Hybrid BM25 + semantic search over sessions and memory.',
189
+ subs: ['<query>', '<query> --top=N'],
190
+ examples: ['/search market research', '/search authentication bug --top=10'],
191
+ usage: '/search <query> [--top=N]',
192
+ },
193
+ '/permissions': { section: 'Power', desc: 'Permission system — view mode, reload config, browse audit log.',
194
+ subs: ['status', 'reload', 'audit', 'edit'],
195
+ examples: ['/permissions', '/permissions reload', '/permissions audit', '/permissions edit'],
196
+ usage: '/permissions [status|reload|audit|edit]',
197
+ },
198
+ '/uninstall': { section: 'Power', desc: 'Uninstall Aiden from this system.',
199
+ subs: ['--keep-workspace', '--keep-config', '--yes'],
200
+ examples: ['/uninstall', '/uninstall --keep-workspace', '/uninstall --yes'],
201
+ usage: '/uninstall [--keep-workspace] [--keep-config] [--yes]',
202
+ },
203
+ '/mcp': { section: 'Power', desc: 'Manage MCP (Model Context Protocol) server connections.',
204
+ subs: ['list', 'tools [server]', 'connect <name> <cmd>', 'disconnect <name>', 'call <server:tool> [json]'],
205
+ examples: ['/mcp list', '/mcp tools github', '/mcp call github:list_issues {}'],
206
+ usage: '/mcp <subcommand> [args]',
207
+ },
208
+ '/cmd': { section: 'Power', desc: 'Run a Windows cmd.exe command and show stdout/exitCode.',
209
+ examples: ['/cmd dir', '/cmd ipconfig /all'],
210
+ usage: '/cmd <command>',
211
+ },
212
+ '/ps': { section: 'Power', desc: 'Run a PowerShell command directly (no temp file).',
213
+ examples: ['/ps Get-Process | Select-Object -First 5', '/ps $PSVersionTable'],
214
+ usage: '/ps <command>',
215
+ },
216
+ '/wsl': { section: 'Power', desc: 'Run a bash command inside WSL (auto-translates paths).',
217
+ examples: ['/wsl uname -a', '/wsl ls -la /mnt/c/Users'],
218
+ usage: '/wsl <command> [--distro=<name>]',
219
+ },
220
+ '/refresh': { section: 'Power', desc: 'Check for Aiden updates and reload config.', usage: '/refresh' },
221
+ '/channels': { section: 'Power', desc: 'Show all channel adapters and their status.',
222
+ subs: ['restart <name>', 'test <name>'],
223
+ usage: '/channels [restart <name>|test <name>]',
224
+ examples: ['/channels', '/channels restart discord', '/channels test slack'],
225
+ },
226
+ '/voice': { section: 'Voice', desc: 'Toggle voice mode — TTS reads every AI reply aloud.',
227
+ subs: ['on', 'off', 'status', 'providers', 'design', 'clone', 'reset'],
228
+ usage: '/voice [on|off|status|providers|design <desc>|clone <path>|reset]',
229
+ examples: ['/voice', '/voice on', '/voice off', '/voice providers'],
230
+ },
231
+ '/speak': { section: 'Voice', desc: 'Speak text immediately using the TTS engine.',
232
+ usage: '/speak <text>',
233
+ examples: ['/speak Hello, how can I help you today?'],
234
+ },
235
+ '/listen': { section: 'Voice', desc: 'Record microphone input, transcribe via STT, and send as message.',
236
+ usage: '/listen [seconds]',
237
+ examples: ['/listen', '/listen 10'],
238
+ },
239
+ '/todo': { section: 'Power', desc: 'Manage a per-session task list.',
240
+ subs: ['add <text>', 'done <id>', 'remove <id>', 'list [all|pending|done]', 'clear'],
241
+ usage: '/todo [add <text>|done <id>|remove <id>|list [filter]|clear]',
242
+ examples: ['/todo add Write unit tests', '/todo done 1', '/todo list pending', '/todo clear'],
243
+ },
244
+ '/cron': { section: 'Power', desc: 'Schedule recurring shell commands. Persists across sessions.',
245
+ subs: ['add <schedule> <command>', 'list', 'pause <id>', 'resume <id>', 'delete <id>', 'run <id>'],
246
+ usage: '/cron [add <schedule> -- <command>|list|pause|resume|delete|run <id>]',
247
+ examples: ['/cron add every 5 minutes -- echo heartbeat', '/cron list', '/cron pause 1'],
248
+ },
249
+ '/vision': { section: 'Power', desc: 'Analyze an image with AI vision.',
250
+ usage: '/vision <path|url> [prompt]',
251
+ examples: ['/vision screenshot.png', '/vision /tmp/photo.jpg What text is visible?'],
252
+ },
253
+ '/security': { section: 'Power', desc: 'Run AgentShield security scan.', usage: '/security' },
254
+ '/debug': { section: 'Power', desc: 'Recent server log entries.', usage: '/debug' },
255
+ '/private': { section: 'Power', desc: 'Toggle private mode — suppresses memory writes.', usage: '/private' },
256
+ '/learn': { section: 'Power', desc: 'Trigger active skill learning for a topic.',
257
+ usage: '/learn [<name>] [<description>]',
258
+ examples: ['/learn TypeScript generics advanced patterns'],
259
+ },
260
+ // ── Gap 1 (C2): 3 Power commands previously missing from catalog ─────────────
261
+ '/install': { section: 'Power', desc: 'Install a skill from the public registry (skills.taracod.com).',
262
+ usage: '/install <skill_name>',
263
+ examples: ['/install http-client', '/install code-review'],
264
+ },
265
+ '/publish': { section: 'Power', desc: 'Publish a skill to the public registry (Pro — requires license key).',
266
+ usage: '/publish <skill_name> [--key=<license>]',
267
+ examples: ['/publish my-skill', '/publish my-skill --key=abc123'],
268
+ },
269
+ '/sandbox': { section: 'Power', desc: 'Manage Docker sandbox mode (status / off / auto / strict / build).',
270
+ subs: ['status', 'off', 'auto', 'strict', 'build'],
271
+ examples: ['/sandbox', '/sandbox strict', '/sandbox build'],
272
+ usage: '/sandbox [status|off|auto|strict|build]',
273
+ },
274
+ '/help': { section: 'Meta', desc: 'Show help overview, or search / get detail on a command.',
275
+ subs: ['search <q>', '<command>'],
276
+ examples: ['/help', '/help search memory', '/help /skills'],
277
+ usage: '/help [search <q>|<command>]',
278
+ },
279
+ '/quit': { section: 'Exit', desc: 'Exit Aiden.', usage: '/quit' },
280
+ '/exit': { section: 'Exit', desc: 'Alias for /quit.', usage: '/exit' },
281
+ '/q': { section: 'Exit', desc: 'Alias for /quit.', usage: '/q' },
282
+ };
283
+ // ── Runtime registry (C2) ─────────────────────────────────────────────────────
284
+ // Seeded from COMMAND_DETAIL at module load. register() / unregister() mutate
285
+ // this map and bump _generation so downstream caches can invalidate.
286
+ let _generation = 0;
287
+ /** Internal registry — DO NOT access directly; use list() / get(). */
288
+ const _registry = new Map(Object.entries(exports.COMMAND_DETAIL));
289
+ /**
290
+ * Register (or replace) a slash command at runtime.
291
+ * Bumps _generation so dropdown / palette caches know to rebuild.
292
+ */
293
+ function register(name, detail) {
294
+ _registry.set(name, detail);
295
+ _generation++;
296
+ }
297
+ /**
298
+ * Unregister a slash command at runtime.
299
+ * No-ops silently if the name is not registered.
300
+ */
301
+ function unregister(name) {
302
+ if (_registry.delete(name))
303
+ _generation++;
304
+ }
305
+ /** All registered commands as [name, detail] pairs (includes plugins). */
306
+ function list() {
307
+ return Array.from(_registry.entries());
308
+ }
309
+ /** Look up a single command by exact name (e.g. '/skills'). */
310
+ function get(name) {
311
+ return _registry.get(name);
312
+ }
313
+ /** Monotonic counter — incremented on every register() / unregister() call.
314
+ * Consumers can cache against this value and rebuild when it changes. */
315
+ function generation() {
316
+ return _generation;
317
+ }
318
+ // ── Flat list of command strings (used by Tab completer) ──────────────────────
319
+ // Derived from the static COMMAND_DETAIL so it is available synchronously
320
+ // before registerCoreCommands() runs. Prefer commandCatalog.list() for
321
+ // anything that needs runtime-registered plugin commands.
322
+ exports.COMMANDS = Object.keys(exports.COMMAND_DETAIL);
323
+ // ── Catalog builder ───────────────────────────────────────────────────────────
324
+ /** Build a PaletteCommand array for the command palette.
325
+ * Uses the live _registry so plugin commands are included. */
326
+ function getCatalog() {
327
+ return list().map(([cmd, d]) => {
328
+ const subcommands = d.subs?.map(sub => {
329
+ const subcmd = sub.split(' ')[0]; // first token is the subcommand keyword
330
+ return {
331
+ command: `${cmd} ${subcmd}`,
332
+ description: sub,
333
+ section: d.section,
334
+ };
335
+ });
336
+ return {
337
+ command: cmd,
338
+ description: d.desc,
339
+ usage: d.usage,
340
+ subcommands,
341
+ section: d.section,
342
+ };
343
+ });
344
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ // Detects whether a user message expresses an action intent (vs. a question or
3
+ // conversational turn). Used by the PlannerGuard to reject respond-only plans
4
+ // that were generated in response to clear action requests.
5
+ //
6
+ // Anchored at start of message, allows optional polite prefixes and leading
7
+ // whitespace. Add new verbs here — keep sorted for readability.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isActionIntent = isActionIntent;
10
+ exports.isMemoryIntent = isMemoryIntent;
11
+ exports.extractMemoryFact = extractMemoryFact;
12
+ exports.isForgetIntent = isForgetIntent;
13
+ exports.detectActionVerb = detectActionVerb;
14
+ const ACTION_VERB_RE = /^\s*(?:please\s+|can\s+you\s+|could\s+you\s+)?(append|capture|close|copy|delete|download|edit|execute|fetch|forget|get|kill|launch|lock|move|mute|note|open|pause|play|prepend|read|reboot|record|remember|remind|remove|rename|replace|restart|resume|run|save|schedule|screenshot|send|set\s+timer|shutdown|skip|speak|start|stop|store|track|transcribe|unlock|unmute|upload|volume|write)\b/i;
15
+ function isActionIntent(message) {
16
+ return ACTION_VERB_RE.test(message);
17
+ }
18
+ // Narrower set: verbs that specifically mean "persist to Aiden's memory system".
19
+ // Used by MemoryGuard to override wrong-tool plans (e.g. file_write) for memory intents.
20
+ const MEMORY_VERB_RE = /^\s*(?:please\s+|can\s+you\s+|could\s+you\s+)?(remember|track|note|store|keep\s+track|forget|remove\s+from\s+memory|delete\s+from\s+memory)\b/i;
21
+ function isMemoryIntent(message) {
22
+ return MEMORY_VERB_RE.test(message);
23
+ }
24
+ /** Extract the fact payload from a memory-intent message.
25
+ * Strips the leading verb+prefix and returns the remainder, trimmed.
26
+ * Falls back to the full message if no verb is stripped. */
27
+ function extractMemoryFact(message) {
28
+ return message
29
+ .replace(/^\s*(?:please\s+|can\s+you\s+|could\s+you\s+)?(?:remember|track|note|store|keep\s+track\s+of?|forget|forget\s+about|remove\s+from\s+memory|delete\s+from\s+memory)\b[:—\s]*/i, '')
30
+ .trim() || message.trim();
31
+ }
32
+ // C11: Narrow subset of memory verbs that mean "delete from memory".
33
+ const FORGET_VERB_RE = /^\s*(?:please\s+|can\s+you\s+|could\s+you\s+)?(forget|remove\s+from\s+memory|delete\s+from\s+memory)\b/i;
34
+ function isForgetIntent(message) {
35
+ return FORGET_VERB_RE.test(message);
36
+ }
37
+ function detectActionVerb(message) {
38
+ const m = message.match(ACTION_VERB_RE);
39
+ return m ? m[1].replace(/\s+/g, ' ').toLowerCase() : '';
40
+ }
41
+ /*
42
+ * Unit assertions (run manually: npx tsx core/actionVerbDetector.ts)
43
+ *
44
+ * Expect true:
45
+ * isActionIntent('open notepad') // bare verb
46
+ * isActionIntent('please open notepad') // polite prefix
47
+ * isActionIntent('can you launch chrome') // can you
48
+ * isActionIntent('could you mute') // could you
49
+ * isActionIntent('screenshot') // single word
50
+ * isActionIntent('set timer for 5 minutes') // two-word verb
51
+ * isActionIntent(' volume up') // leading whitespace
52
+ * isActionIntent('Close all windows') // capitalised
53
+ * isActionIntent('remember my color is purple') // C5: memory verb
54
+ * isActionIntent('track my water intake') // C5: memory verb
55
+ * isActionIntent('note that my name is shiva') // C5: memory verb
56
+ * isActionIntent('store this fact') // C5: memory verb
57
+ *
58
+ * Expect false:
59
+ * isActionIntent('what can you do') // question — no action verb at start
60
+ * isActionIntent("what's 2+2") // math question
61
+ * isActionIntent('tell me about open source') // 'open' not at intent position
62
+ * isActionIntent('how do I start a project') // 'start' after 'how do I'
63
+ * isActionIntent('') // empty
64
+ * isActionIntent('remembered yesterday I bought milk') // C5: past-tense narrative — anchor prevents match
65
+ */