aiden-runtime 4.1.1 → 4.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +78 -26
  2. package/dist/cli/v4/aidenCLI.js +159 -9
  3. package/dist/cli/v4/callbacks.js +5 -2
  4. package/dist/cli/v4/chatSession.js +525 -15
  5. package/dist/cli/v4/commands/auth.js +6 -3
  6. package/dist/cli/v4/commands/help.js +4 -0
  7. package/dist/cli/v4/commands/index.js +10 -1
  8. package/dist/cli/v4/commands/reloadSoul.js +37 -0
  9. package/dist/cli/v4/commands/update.js +102 -0
  10. package/dist/cli/v4/defaultSoul.js +68 -2
  11. package/dist/cli/v4/display.js +28 -10
  12. package/dist/cli/v4/doctor.js +112 -0
  13. package/dist/cli/v4/doctorLiveness.js +65 -10
  14. package/dist/cli/v4/promotionPrompt.js +202 -0
  15. package/dist/cli/v4/providerBootSelector.js +144 -0
  16. package/dist/cli/v4/sessionSummaryGate.js +66 -0
  17. package/dist/cli/v4/toolPreview.js +139 -0
  18. package/dist/core/v4/aidenAgent.js +91 -29
  19. package/dist/core/v4/capabilities.js +89 -0
  20. package/dist/core/v4/contextCompressor.js +25 -8
  21. package/dist/core/v4/distillationIndex.js +167 -0
  22. package/dist/core/v4/distillationStore.js +98 -0
  23. package/dist/core/v4/logger/logger.js +40 -9
  24. package/dist/core/v4/promotionCandidates.js +234 -0
  25. package/dist/core/v4/promptBuilder.js +145 -1
  26. package/dist/core/v4/sessionDistiller.js +405 -0
  27. package/dist/core/v4/skillMining/skillMiner.js +43 -6
  28. package/dist/core/v4/skillOutcomeTracker.js +323 -0
  29. package/dist/core/v4/subsystemHealth.js +143 -0
  30. package/dist/core/v4/update/executeInstall.js +233 -0
  31. package/dist/core/version.js +1 -1
  32. package/dist/moat/memoryGuard.js +111 -0
  33. package/dist/moat/skillTeacher.js +14 -5
  34. package/dist/providers/v4/chatCompletionsAdapter.js +9 -0
  35. package/dist/providers/v4/errors.js +20 -4
  36. package/dist/providers/v4/modelDefaults.js +65 -0
  37. package/dist/providers/v4/registry.js +9 -2
  38. package/dist/providers/v4/runtimeResolver.js +6 -0
  39. package/dist/tools/v4/index.js +57 -1
  40. package/dist/tools/v4/memory/memoryRemove.js +57 -2
  41. package/dist/tools/v4/memory/sessionSummary.js +151 -0
  42. package/dist/tools/v4/sessions/recallSession.js +163 -0
  43. package/dist/tools/v4/sessions/sessionSearch.js +5 -1
  44. package/dist/tools/v4/system/_psHelpers.js +55 -0
  45. package/dist/tools/v4/system/aidenSelfUpdate.js +162 -0
  46. package/dist/tools/v4/system/appClose.js +79 -0
  47. package/dist/tools/v4/system/appLaunch.js +92 -0
  48. package/dist/tools/v4/system/clipboardRead.js +54 -0
  49. package/dist/tools/v4/system/clipboardWrite.js +84 -0
  50. package/dist/tools/v4/system/mediaKey.js +78 -0
  51. package/dist/tools/v4/system/osProcessList.js +99 -0
  52. package/dist/tools/v4/system/screenshot.js +106 -0
  53. package/dist/tools/v4/system/volumeSet.js +157 -0
  54. package/package.json +4 -1
  55. package/skills/system_control.md +135 -69
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
4
  // AUTO-GENERATED by scripts/inject-version.js — do not edit by hand
5
- exports.VERSION = '4.1.1';
5
+ exports.VERSION = '4.1.2';
@@ -31,6 +31,7 @@
31
31
  */
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
33
  exports.MemoryGuard = void 0;
34
+ exports.containsInSection = containsInSection;
34
35
  class MemoryGuard {
35
36
  constructor(memory) {
36
37
  this.memory = memory;
@@ -113,6 +114,75 @@ class MemoryGuard {
113
114
  }
114
115
  return { ok: true, verified: true, fileLength: text.length };
115
116
  }
117
+ /**
118
+ * Phase v4.1.2 alive-core: section-aware write. Replaces the body of
119
+ * a markdown `## <header>` section, creating the section at file end
120
+ * if it doesn't yet exist. Body lines below the header up to the
121
+ * next `## ` (or EOF) are replaced wholesale.
122
+ *
123
+ * Preserves the standard verify-on-disk contract: the post-write read
124
+ * confirms `newBody` is present and (when applicable) the previous
125
+ * section body is gone before returning `verified: true`.
126
+ *
127
+ * Additive — does not change `guardedAdd` / `guardedReplace` /
128
+ * `guardedRemove` semantics.
129
+ */
130
+ async replaceSection(file, header, newBody) {
131
+ const headerTrim = header.trim();
132
+ if (!headerTrim.startsWith('## ')) {
133
+ return {
134
+ ok: false,
135
+ verified: false,
136
+ reason: 'header must start with "## " (markdown h2)',
137
+ };
138
+ }
139
+ const bodyTrim = newBody.trim();
140
+ if (!bodyTrim) {
141
+ return {
142
+ ok: false,
143
+ verified: false,
144
+ reason: 'newBody cannot be empty. Use guardedRemove() to drop a section.',
145
+ };
146
+ }
147
+ // Read current file state so we can compute the precise old block.
148
+ const snapBefore = await this.memory.loadSnapshot();
149
+ const before = pickFile(snapBefore, file);
150
+ // Match `<header>` and everything below it up to the next `## `
151
+ // line or EOF. No `m` flag — we want `$` to mean end-of-string;
152
+ // with `m`, the trailing-`$` lookahead would match at every line
153
+ // ending and chop off all but the first body line.
154
+ const escapedHeader = headerTrim.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
155
+ const sectionRe = new RegExp(`${escapedHeader}[^\\n]*(?:\\r?\\n[\\s\\S]*?)?(?=\\n## |$)`);
156
+ const match = before.match(sectionRe);
157
+ const newSection = `${headerTrim}\n${bodyTrim}`;
158
+ let mutation;
159
+ if (match && match[0]) {
160
+ mutation = await this.memory.replace(file, match[0], newSection);
161
+ }
162
+ else {
163
+ // Section doesn't exist — append at end with a blank-line gap.
164
+ const sep = before.length > 0 && !before.endsWith('\n') ? '\n\n' : '\n';
165
+ mutation = await this.memory.add(file, `${sep}${newSection}`);
166
+ }
167
+ if (!mutation.ok) {
168
+ return {
169
+ ok: false,
170
+ verified: false,
171
+ reason: mutation.reason ?? 'section write failed',
172
+ };
173
+ }
174
+ const snapAfter = await this.memory.loadSnapshot();
175
+ const after = pickFile(snapAfter, file);
176
+ if (!after.includes(headerTrim) || !after.includes(bodyTrim)) {
177
+ return {
178
+ ok: false,
179
+ verified: false,
180
+ reason: 'Section write claimed but header/body not found post-write',
181
+ fileLength: after.length,
182
+ };
183
+ }
184
+ return { ok: true, verified: true, fileLength: after.length };
185
+ }
116
186
  async guardedRemove(file, text) {
117
187
  const trimmed = text.trim();
118
188
  if (!trimmed) {
@@ -144,3 +214,44 @@ exports.MemoryGuard = MemoryGuard;
144
214
  function pickFile(snap, file) {
145
215
  return file === 'user' ? snap.userMd : snap.memoryMd;
146
216
  }
217
+ /**
218
+ * Phase v4.1.2-bug-X: section-aware containment check.
219
+ *
220
+ * Returns `true` if `target` appears anywhere within the body of the
221
+ * section identified by `sectionHeader` (e.g. `"## Durable facts"`).
222
+ * The section body runs from the line after the header to the line
223
+ * before the next `## ` header — or end-of-file, whichever comes
224
+ * first. Returns `false` when the section doesn't exist OR when the
225
+ * target sits outside it.
226
+ *
227
+ * Pure: no I/O, deterministic from inputs. Used by `memory_remove`
228
+ * to protect user-approved durable facts from autonomous deletion:
229
+ * the model proposed substring-match against MEMORY.md, but
230
+ * substring removal operates whole-file — partial protection would
231
+ * still nuke the durable copy as side-effect. STRICT containment
232
+ * (rejects if the substring appears ANYWHERE in the section body)
233
+ * is the honest guard.
234
+ *
235
+ * Case-sensitive: matches the existing `guardedRemove` semantics
236
+ * which use `String.prototype.includes` directly on the raw content.
237
+ *
238
+ * @param fileContent Full file content (e.g. MEMORY.md as one string).
239
+ * @param target Substring the caller intends to remove.
240
+ * @param sectionHeader Header line including the `## ` prefix.
241
+ */
242
+ function containsInSection(fileContent, target, sectionHeader) {
243
+ if (!fileContent || !target || !sectionHeader)
244
+ return false;
245
+ const headerEscaped = sectionHeader.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
246
+ // Match the header line, then capture the body until the next `## `
247
+ // (any h2) or end-of-string. No `m` flag — the trailing-`$` would
248
+ // otherwise match every line ending and chop the body at the first
249
+ // newline (the same trap the slice2 sessionSummary regex already
250
+ // documents).
251
+ const sectionRe = new RegExp(`${headerEscaped}[^\\n]*\\n([\\s\\S]*?)(?=\\n## |$)`);
252
+ const m = fileContent.match(sectionRe);
253
+ if (!m)
254
+ return false;
255
+ const body = m[1] ?? '';
256
+ return body.includes(target);
257
+ }
@@ -97,7 +97,7 @@ class SkillTeacher {
97
97
  /** Optional handler-resolver to look up toolset metadata for trace
98
98
  * entries that don't carry their own toolset. Used for the 2-toolset
99
99
  * diversity check. */
100
- resolveHandler) {
100
+ resolveHandler, healthTracker) {
101
101
  this.skillLoader = skillLoader;
102
102
  this.skillManager = skillManager;
103
103
  this.resolveHandler = resolveHandler;
@@ -107,6 +107,7 @@ class SkillTeacher {
107
107
  this.qualityPath =
108
108
  qualityFilePath ??
109
109
  node_path_1.default.join(process.cwd(), '.aiden-skill-quality.json');
110
+ this.healthTracker = healthTracker;
110
111
  }
111
112
  setTier(tier) {
112
113
  this.tier = tier;
@@ -203,9 +204,11 @@ class SkillTeacher {
203
204
  name: proposal.proposedName,
204
205
  content,
205
206
  }, {});
207
+ this.healthTracker?.recordSuccess();
206
208
  return { created: true, skillName: proposal.proposedName };
207
209
  }
208
210
  catch (e) {
211
+ this.healthTracker?.recordFailure(e);
209
212
  const msg = e instanceof Error ? e.message : String(e);
210
213
  return { created: false, reason: `create_failed: ${msg}` };
211
214
  }
@@ -336,8 +339,11 @@ ${[...new Set(proposal.toolsUsed)].map((t) => `- ${t}`).join('\n')}
336
339
  return this.qualityCache;
337
340
  }
338
341
  }
339
- catch {
340
- // ignore corrupt file is replaced on next save.
342
+ catch (e) {
343
+ // Phase v4.1.2-slice3: record corrupt-file / JSON parse / read
344
+ // failures into the health tracker. We still fall through to an
345
+ // empty cache so behavior is unchanged — telemetry is additive.
346
+ this.healthTracker?.recordFailure(e);
341
347
  }
342
348
  this.qualityCache = {};
343
349
  return this.qualityCache;
@@ -346,8 +352,11 @@ ${[...new Set(proposal.toolsUsed)].map((t) => `- ${t}`).join('\n')}
346
352
  try {
347
353
  await node_fs_1.promises.writeFile(this.qualityPath, JSON.stringify(cache, null, 2), 'utf-8');
348
354
  }
349
- catch {
350
- // ignore disk failures — quality data is best-effort.
355
+ catch (e) {
356
+ // Phase v4.1.2-slice3: surface disk-write failures (EACCES,
357
+ // ENOSPC, EROFS) instead of silently dropping. Best-effort
358
+ // semantic preserved — we still return without throwing.
359
+ this.healthTracker?.recordFailure(e);
351
360
  }
352
361
  }
353
362
  }
@@ -68,6 +68,7 @@ class ChatCompletionsAdapter {
68
68
  this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
69
69
  this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
70
70
  this.extraHeaders = opts.extraHeaders ?? {};
71
+ this.defaultExtraBody = opts.defaultExtraBody;
71
72
  }
72
73
  // ── Non-streaming ────────────────────────────────────────────────────
73
74
  async call(input) {
@@ -127,6 +128,14 @@ class ChatCompletionsAdapter {
127
128
  // the stream closes promptly and we get accurate token accounting.
128
129
  body.stream_options = { include_usage: true };
129
130
  }
131
+ // Phase v4.1.2-deepseek: merge order is base body → defaultExtraBody
132
+ // (model-mandated, from resolver lookup in MODEL_DEFAULTS) → per-call
133
+ // input.extraBody (caller). Per-call wins so a single request can
134
+ // override a default (e.g. disabling thinking on a probe). This
135
+ // matters because providers/v4/modelDefaults.ts sets thinking +
136
+ // reasoning_effort for deepseek-v4-pro on EVERY call.
137
+ if (this.defaultExtraBody)
138
+ Object.assign(body, this.defaultExtraBody);
130
139
  if (input.extraBody)
131
140
  Object.assign(body, input.extraBody);
132
141
  return body;
@@ -19,10 +19,17 @@ exports.ProviderRateLimitError = exports.ProviderTimeoutError = exports.Provider
19
19
  exports.formatRawForMessage = formatRawForMessage;
20
20
  /**
21
21
  * Format a raw response body for inclusion in the user-facing error
22
- * message. Recognises the OpenAI / Anthropic JSON envelope shape
23
- * (`{ error: { message: "..." } }`) and falls back to the raw string
24
- * for plain-text bodies. Returns null when nothing useful is available
25
- * so callers can omit the ": <detail>" tail entirely.
22
+ * message. Recognises three JSON envelope shapes and falls back to the
23
+ * raw string for plain-text bodies. Returns null when nothing useful is
24
+ * available so callers can omit the ": <detail>" tail entirely.
25
+ *
26
+ * Recognised envelopes (most-specific first):
27
+ * 1. OpenAI / Anthropic: `{ error: { message: "..." } }`
28
+ * 2. Top-level message: `{ message: "..." }`
29
+ * 3. Codex Responses: `{ detail: "..." }` (Phase v4.1.2-bug3 —
30
+ * surfaced by slice5: the Codex backend at chatgpt.com/backend-api/
31
+ * codex/responses returns 4xx bodies in this shape, e.g.
32
+ * `{"detail": "The 'gpt-5.1-codex-max' model is not supported..."}`)
26
33
  *
27
34
  * Truncates to 300 chars to keep multi-line responses from blowing
28
35
  * up the user's terminal — full body remains on `error.raw` for
@@ -45,6 +52,15 @@ function formatRawForMessage(raw) {
45
52
  if (typeof topMsg === 'string' && topMsg.length > 0) {
46
53
  return topMsg.length > 300 ? `${topMsg.slice(0, 300)}…` : topMsg;
47
54
  }
55
+ // Codex Responses envelope: { detail: "..." }. Distinct from the
56
+ // OpenAI shape — the Codex backend uses FastAPI-style validation
57
+ // errors that surface as `detail` (str) for tier/auth rejections
58
+ // and `detail: [{...}]` for schema errors. Only the string form is
59
+ // useful in the message tail; the array form is left to .raw.
60
+ const detail = raw.detail;
61
+ if (typeof detail === 'string' && detail.length > 0) {
62
+ return detail.length > 300 ? `${detail.slice(0, 300)}…` : detail;
63
+ }
48
64
  return null;
49
65
  }
50
66
  // Plain string body.
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2026 Shiva Deore (Taracod).
4
+ * Licensed under AGPL-3.0. See LICENSE for details.
5
+ *
6
+ * Aiden — local-first agent.
7
+ */
8
+ /**
9
+ * providers/v4/modelDefaults.ts — Phase v4.1.2-deepseek.
10
+ *
11
+ * Per-model default request parameters. Keyed by `${providerId}:${modelId}`
12
+ * so the same model slug appearing under multiple providers doesn't
13
+ * collide (e.g. a future shared-slug case across compat endpoints).
14
+ *
15
+ * Today's only consumer:
16
+ * deepseek:deepseek-v4-pro → always send extra_body.thinking +
17
+ * reasoning_effort, per DeepSeek's V4-Pro API guidance:
18
+ *
19
+ * client.chat.completions.create(
20
+ * model="deepseek-v4-pro",
21
+ * messages=...,
22
+ * reasoning_effort="high",
23
+ * extra_body={"thinking": {"type": "enabled"}},
24
+ * )
25
+ *
26
+ * The defaults are merged into the wire body by ChatCompletionsAdapter:
27
+ * base body → defaultExtraBody (from this map) → per-call extraBody
28
+ * (from the caller's ProviderCallInput)
29
+ *
30
+ * Per-call extraBody wins so a caller can disable thinking on a single
31
+ * request without un-registering the model default.
32
+ *
33
+ * Adding a new entry: keep this file small. Per-model knowledge lives
34
+ * here so resolver / registry / adapter stay pure (credential
35
+ * resolution / provider facts / wire-format mechanics respectively).
36
+ */
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.MODEL_DEFAULTS = void 0;
39
+ exports.getModelDefaults = getModelDefaults;
40
+ /**
41
+ * Per-`${providerId}:${modelId}` defaults. `undefined` lookup means
42
+ * the model takes no special handling.
43
+ */
44
+ exports.MODEL_DEFAULTS = Object.freeze({
45
+ // DeepSeek V4 Pro — thinking-mode flagship.
46
+ // Reference: https://api-docs.deepseek.com/ (verified 2026-05).
47
+ // deepseek-v4-flash exists too but is not wired this slice; its
48
+ // legacy aliases (deepseek-chat = v4-flash non-think,
49
+ // deepseek-reasoner = v4-flash think) stay un-defaulted to preserve
50
+ // the existing pass-through behavior for users who explicitly
51
+ // selected them.
52
+ 'deepseek:deepseek-v4-pro': {
53
+ extraBody: {
54
+ thinking: { type: 'enabled' },
55
+ reasoning_effort: 'high',
56
+ },
57
+ },
58
+ });
59
+ /**
60
+ * Look up defaults for a (provider, model) pair. Returns `undefined`
61
+ * when no entry exists — caller skips the extraBody merge.
62
+ */
63
+ function getModelDefaults(providerId, modelId) {
64
+ return exports.MODEL_DEFAULTS[`${providerId}:${modelId}`];
65
+ }
@@ -232,12 +232,19 @@ exports.PROVIDER_REGISTRY = {
232
232
  apiMode: 'chat_completions',
233
233
  baseUrl: 'https://api.deepseek.com/v1',
234
234
  apiKeyEnvVar: 'DEEPSEEK_API_KEY',
235
- description: 'DeepSeek direct API — V3 chat + R1 reasoning.',
235
+ description: 'DeepSeek direct API — V4 Pro reasoning flagship + legacy aliases.',
236
236
  tier: 'paid',
237
237
  hasFreeTier: false,
238
238
  docsUrl: 'https://api-docs.deepseek.com/',
239
239
  supportsToolCalling: true,
240
- modelIds: ['deepseek-chat', 'deepseek-reasoner'],
240
+ // Phase v4.1.2-deepseek: `deepseek-v4-pro` prepended as the new
241
+ // flagship — becomes the auto-pick default for new users via
242
+ // pickProbeModel(). Legacy `deepseek-chat` and `deepseek-reasoner`
243
+ // retained for back-compat (still functional aliases of the V4
244
+ // flash family per DeepSeek docs; deprecated-but-live). Removal
245
+ // is its own deprecation slice. Per-call thinking + reasoning
246
+ // _effort defaults for v4-pro live in providers/v4/modelDefaults.ts.
247
+ modelIds: ['deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner'],
241
248
  },
242
249
  mistral: {
243
250
  id: 'mistral',
@@ -39,6 +39,7 @@ const chatCompletionsAdapter_1 = require("./chatCompletionsAdapter");
39
39
  const anthropicAdapter_1 = require("./anthropicAdapter");
40
40
  const codexResponsesAdapter_1 = require("./codexResponsesAdapter");
41
41
  const ollamaPromptToolsAdapter_1 = require("./ollamaPromptToolsAdapter");
42
+ const modelDefaults_1 = require("./modelDefaults");
42
43
  const tokenStore_1 = require("../../core/v4/auth/tokenStore");
43
44
  class RuntimeResolver {
44
45
  constructor(credentialResolver) {
@@ -61,6 +62,11 @@ class RuntimeResolver {
61
62
  model: model.id,
62
63
  providerName: entry.id,
63
64
  extraHeaders: entry.extraHeaders,
65
+ // Phase v4.1.2-deepseek: per-model body defaults (e.g.
66
+ // DeepSeek V4-Pro's mandatory thinking + reasoning_effort).
67
+ // Undefined for models without registered defaults — adapter
68
+ // skips the merge in that case.
69
+ defaultExtraBody: (0, modelDefaults_1.getModelDefaults)(entry.id, model.id)?.extraBody,
64
70
  });
65
71
  case 'anthropic_messages':
66
72
  if (!credentials.apiKey) {
@@ -21,7 +21,7 @@
21
21
  * Status: PHASE 8.
22
22
  */
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.memoryRemoveTool = exports.memoryReplaceTool = exports.memoryAddTool = exports.processWaitTool = exports.processKillTool = exports.processLogReadTool = exports.processListTool = exports.processSpawnTool = exports.executeCodeTool = exports.shellExecTool = exports.naturalEventsTool = exports.nowPlayingTool = exports.systemInfoTool = exports.makeLookupToolSchema = exports.skillManageTool = exports.skillViewTool = exports.skillsListTool = exports.sessionListTool = exports.sessionSearchTool = exports.browserCloseTool = exports.browserScrollTool = exports.browserFillTool = exports.browserTypeTool = exports.browserClickTool = exports.browserNavigateTool = exports.browserGetUrlTool = exports.browserExtractTool = exports.browserScreenshotTool = exports.fileCopyTool = exports.fileMoveTool = exports.fileDeleteTool = exports.filePatchTool = exports.fileWriteTool = exports.fileListTool = exports.fileReadTool = exports.deepResearchTool = exports.webPageTool = exports.webFetchTool = exports.webSearchTool = exports.makeSubagentFanoutTool = void 0;
24
+ exports.sessionSummaryTool = exports.memoryRemoveTool = exports.memoryReplaceTool = exports.memoryAddTool = exports.processWaitTool = exports.processKillTool = exports.processLogReadTool = exports.processListTool = exports.processSpawnTool = exports.executeCodeTool = exports.shellExecTool = exports.clipboardWriteTool = exports.clipboardReadTool = exports.appCloseTool = exports.appLaunchTool = exports.volumeSetTool = exports.mediaKeyTool = exports.osProcessListTool = exports.screenshotTool = exports.naturalEventsTool = exports.nowPlayingTool = exports.systemInfoTool = exports.makeLookupToolSchema = exports.skillManageTool = exports.skillViewTool = exports.skillsListTool = exports.sessionListTool = exports.sessionSearchTool = exports.browserCloseTool = exports.browserScrollTool = exports.browserFillTool = exports.browserTypeTool = exports.browserClickTool = exports.browserNavigateTool = exports.browserGetUrlTool = exports.browserExtractTool = exports.browserScreenshotTool = exports.fileCopyTool = exports.fileMoveTool = exports.fileDeleteTool = exports.filePatchTool = exports.fileWriteTool = exports.fileListTool = exports.fileReadTool = exports.deepResearchTool = exports.webPageTool = exports.webFetchTool = exports.webSearchTool = exports.makeSubagentFanoutTool = void 0;
25
25
  exports.registerReadOnlyTools = registerReadOnlyTools;
26
26
  exports.registerWriteTools = registerWriteTools;
27
27
  exports.registerAllTools = registerAllTools;
@@ -49,6 +49,7 @@ const browserScroll_1 = require("./browser/browserScroll");
49
49
  const browserClose_1 = require("./browser/browserClose");
50
50
  const sessionSearch_1 = require("./sessions/sessionSearch");
51
51
  const sessionList_1 = require("./sessions/sessionList");
52
+ const recallSession_1 = require("./sessions/recallSession");
52
53
  const skillsList_1 = require("./skills/skillsList");
53
54
  const skillView_1 = require("./skills/skillView");
54
55
  const skillManage_1 = require("./skills/skillManage");
@@ -56,6 +57,18 @@ const lookupToolSchema_1 = require("./skills/lookupToolSchema");
56
57
  const systemInfo_1 = require("./system/systemInfo");
57
58
  const nowPlaying_1 = require("./system/nowPlaying");
58
59
  const naturalEvents_1 = require("./system/naturalEvents");
60
+ // Phase v4.1.2-followup-3 computer-control bundle.
61
+ const screenshot_1 = require("./system/screenshot");
62
+ const osProcessList_1 = require("./system/osProcessList");
63
+ const mediaKey_1 = require("./system/mediaKey");
64
+ const volumeSet_1 = require("./system/volumeSet");
65
+ const appLaunch_1 = require("./system/appLaunch");
66
+ const appClose_1 = require("./system/appClose");
67
+ const clipboardRead_1 = require("./system/clipboardRead");
68
+ const clipboardWrite_1 = require("./system/clipboardWrite");
69
+ // Phase v4.1.2-update — natural-language self-update entry point.
70
+ // Routes through the same shared executeInstall executor as `/update install`.
71
+ const aidenSelfUpdate_1 = require("./system/aidenSelfUpdate");
59
72
  const shellExec_1 = require("./terminal/shellExec");
60
73
  const executeCode_1 = require("./executeCode");
61
74
  const processSpawn_1 = require("./process/processSpawn");
@@ -66,6 +79,7 @@ const processWait_1 = require("./process/processWait");
66
79
  const memoryAdd_1 = require("./memory/memoryAdd");
67
80
  const memoryReplace_1 = require("./memory/memoryReplace");
68
81
  const memoryRemove_1 = require("./memory/memoryRemove");
82
+ const sessionSummary_1 = require("./memory/sessionSummary");
69
83
  const subagentFanout_1 = require("./subagent/subagentFanout");
70
84
  /**
71
85
  * Register every read-only tool into `registry`. The
@@ -94,11 +108,21 @@ function registerReadOnlyTools(registry) {
94
108
  registry.register(browserGetUrl_1.browserGetUrlTool);
95
109
  registry.register(sessionSearch_1.sessionSearchTool);
96
110
  registry.register(sessionList_1.sessionListTool);
111
+ // Phase v4.1.2-memory-C: recall_session reads SessionDistillation
112
+ // files written by Phase A+B. Sits alongside session_search — the
113
+ // two have distinct purposes (FTS5-over-messages vs ranked
114
+ // distillation summaries); descriptions force the right model
115
+ // choice.
116
+ registry.register(recallSession_1.recallSessionTool);
97
117
  registry.register(skillsList_1.skillsListTool);
98
118
  registry.register(skillView_1.skillViewTool);
99
119
  registry.register(systemInfo_1.systemInfoTool);
100
120
  registry.register(nowPlaying_1.nowPlayingTool);
101
121
  registry.register(naturalEvents_1.naturalEventsTool);
122
+ // Phase v4.1.2-followup-3 — computer-control read-only tools.
123
+ registry.register(screenshot_1.screenshotTool);
124
+ registry.register(osProcessList_1.osProcessListTool);
125
+ registry.register(clipboardRead_1.clipboardReadTool);
102
126
  registry.register((0, lookupToolSchema_1.makeLookupToolSchema)(registry));
103
127
  // Phase v4.1-subagent — register a stub for subagent_fanout so its
104
128
  // schema is visible to the agent loop, the MCP server, and the
@@ -158,9 +182,22 @@ function registerWriteTools(registry) {
158
182
  registry.register(memoryAdd_1.memoryAddTool);
159
183
  registry.register(memoryReplace_1.memoryReplaceTool);
160
184
  registry.register(memoryRemove_1.memoryRemoveTool);
185
+ // Phase v4.1.2 alive-core: cross-session continuity via /quit auto-summary.
186
+ registry.register(sessionSummary_1.sessionSummaryTool);
161
187
  // Phase 10: skill_manage — mutating, also goes through the approval
162
188
  // engine. skills_list / skill_view stay in registerReadOnlyTools.
163
189
  registry.register(skillManage_1.skillManageTool);
190
+ // Phase v4.1.2-update: natural-language entry to the same install
191
+ // executor that /update install uses. Two-step confirmation gate
192
+ // (confirm:false → status; confirm:true → install).
193
+ registry.register(aidenSelfUpdate_1.aidenSelfUpdateTool);
194
+ // Phase v4.1.2-followup-3 — computer-control mutating tools. All
195
+ // route through the approval engine like every other write.
196
+ registry.register(mediaKey_1.mediaKeyTool);
197
+ registry.register(volumeSet_1.volumeSetTool);
198
+ registry.register(appLaunch_1.appLaunchTool);
199
+ registry.register(appClose_1.appCloseTool);
200
+ registry.register(clipboardWrite_1.clipboardWriteTool);
164
201
  }
165
202
  /** Register every v4 tool. Most callers want this. */
166
203
  function registerAllTools(registry) {
@@ -227,6 +264,23 @@ var nowPlaying_2 = require("./system/nowPlaying");
227
264
  Object.defineProperty(exports, "nowPlayingTool", { enumerable: true, get: function () { return nowPlaying_2.nowPlayingTool; } });
228
265
  var naturalEvents_2 = require("./system/naturalEvents");
229
266
  Object.defineProperty(exports, "naturalEventsTool", { enumerable: true, get: function () { return naturalEvents_2.naturalEventsTool; } });
267
+ // Phase v4.1.2-followup-3 computer-control bundle exports.
268
+ var screenshot_2 = require("./system/screenshot");
269
+ Object.defineProperty(exports, "screenshotTool", { enumerable: true, get: function () { return screenshot_2.screenshotTool; } });
270
+ var osProcessList_2 = require("./system/osProcessList");
271
+ Object.defineProperty(exports, "osProcessListTool", { enumerable: true, get: function () { return osProcessList_2.osProcessListTool; } });
272
+ var mediaKey_2 = require("./system/mediaKey");
273
+ Object.defineProperty(exports, "mediaKeyTool", { enumerable: true, get: function () { return mediaKey_2.mediaKeyTool; } });
274
+ var volumeSet_2 = require("./system/volumeSet");
275
+ Object.defineProperty(exports, "volumeSetTool", { enumerable: true, get: function () { return volumeSet_2.volumeSetTool; } });
276
+ var appLaunch_2 = require("./system/appLaunch");
277
+ Object.defineProperty(exports, "appLaunchTool", { enumerable: true, get: function () { return appLaunch_2.appLaunchTool; } });
278
+ var appClose_2 = require("./system/appClose");
279
+ Object.defineProperty(exports, "appCloseTool", { enumerable: true, get: function () { return appClose_2.appCloseTool; } });
280
+ var clipboardRead_2 = require("./system/clipboardRead");
281
+ Object.defineProperty(exports, "clipboardReadTool", { enumerable: true, get: function () { return clipboardRead_2.clipboardReadTool; } });
282
+ var clipboardWrite_2 = require("./system/clipboardWrite");
283
+ Object.defineProperty(exports, "clipboardWriteTool", { enumerable: true, get: function () { return clipboardWrite_2.clipboardWriteTool; } });
230
284
  var shellExec_2 = require("./terminal/shellExec");
231
285
  Object.defineProperty(exports, "shellExecTool", { enumerable: true, get: function () { return shellExec_2.shellExecTool; } });
232
286
  var executeCode_2 = require("./executeCode");
@@ -247,3 +301,5 @@ var memoryReplace_2 = require("./memory/memoryReplace");
247
301
  Object.defineProperty(exports, "memoryReplaceTool", { enumerable: true, get: function () { return memoryReplace_2.memoryReplaceTool; } });
248
302
  var memoryRemove_2 = require("./memory/memoryRemove");
249
303
  Object.defineProperty(exports, "memoryRemoveTool", { enumerable: true, get: function () { return memoryRemove_2.memoryRemoveTool; } });
304
+ var sessionSummary_2 = require("./memory/sessionSummary");
305
+ Object.defineProperty(exports, "sessionSummaryTool", { enumerable: true, get: function () { return sessionSummary_2.sessionSummaryTool; } });
@@ -12,14 +12,37 @@
12
12
  * Returns `verified: true` only after the post-write read confirms
13
13
  * the text is gone from the file.
14
14
  *
15
- * Status: PHASE 9.
15
+ * Phase v4.1.2-bug-X: user-approved durable facts (anything in
16
+ * MEMORY.md `## Durable facts`) are protected from autonomous
17
+ * deletion. A subsequent session on a weak model (llama-3.3 in the
18
+ * smoke test) called memory_remove on a Phase-D-promoted fact with
19
+ * reasoning "outdated" — violating Phase D's opt-in trust contract.
20
+ *
21
+ * The guard is STRICT: if the requested substring appears ANYWHERE
22
+ * in the `## Durable facts` body, the call is rejected. Substring
23
+ * removal operates whole-file, so partial protection would still
24
+ * nuke the durable copy as side-effect. Hard rejection (vs propose-
25
+ * and-defer) is honest: model must surface to user; only the user
26
+ * (editing MEMORY.md directly, or via a future `/forget` slash
27
+ * command) can revoke durable content.
28
+ *
29
+ * Status: PHASE 9 (PHASE v4.1.2-bug-X: section protection).
16
30
  */
17
31
  Object.defineProperty(exports, "__esModule", { value: true });
18
32
  exports.memoryRemoveTool = void 0;
33
+ const memoryGuard_1 = require("../../../moat/memoryGuard");
34
+ /** Section in MEMORY.md that Phase D promotion writes to. */
35
+ const DURABLE_FACTS_HEADER = '## Durable facts';
19
36
  exports.memoryRemoveTool = {
20
37
  schema: {
21
38
  name: 'memory_remove',
22
- description: 'Remove an entry from MEMORY.md or USER.md by substring match. Returns verified=true only after the change is confirmed on disk.',
39
+ description: 'Remove an entry from MEMORY.md or USER.md by substring match. ' +
40
+ 'CANNOT remove entries in MEMORY.md `## Durable facts` — those are ' +
41
+ 'user-approved facts the user explicitly promoted; only the user ' +
42
+ 'can revoke them by editing MEMORY.md directly. If you think a ' +
43
+ 'durable fact is outdated, surface that to the user and ask them ' +
44
+ 'to confirm; do not propose autonomous removal. Returns ' +
45
+ 'verified=true only after the change is confirmed on disk.',
23
46
  inputSchema: {
24
47
  type: 'object',
25
48
  properties: {
@@ -42,6 +65,38 @@ exports.memoryRemoveTool = {
42
65
  }
43
66
  const file = args.file === 'user' ? 'user' : 'memory';
44
67
  const text = String(args.text ?? '');
68
+ // Phase v4.1.2-bug-X: durable-section protection. Only applies
69
+ // to MEMORY.md (USER.md has no section structure today). The
70
+ // check requires snapshot access — when ctx.memory is not
71
+ // wired (test contexts, future surface refactors), we fall
72
+ // through to the existing guardedRemove behavior. Real CLI
73
+ // sessions wire memoryManager, so production paths get the
74
+ // guard. Documented intentional fall-through.
75
+ if (file === 'memory' && ctx.memory) {
76
+ try {
77
+ const snap = await ctx.memory.loadSnapshot();
78
+ const memoryMd = snap?.memoryMd ?? '';
79
+ if (memoryMd && (0, memoryGuard_1.containsInSection)(memoryMd, text, DURABLE_FACTS_HEADER)) {
80
+ const preview = text.length > 60
81
+ ? `${text.slice(0, 60)}…`
82
+ : text;
83
+ return {
84
+ success: false,
85
+ verified: false,
86
+ error: `Cannot remove "${preview}" — it's in MEMORY.md ` +
87
+ `\`${DURABLE_FACTS_HEADER}\`, which holds user-approved facts. ` +
88
+ `Only the user can revoke these. Ask them to confirm removal ` +
89
+ `in their next message; do not propose autonomous deletion.`,
90
+ file,
91
+ protectedSection: DURABLE_FACTS_HEADER,
92
+ };
93
+ }
94
+ }
95
+ catch {
96
+ // Snapshot read failed — fall through to old behavior rather
97
+ // than block legitimate removals on transient I/O errors.
98
+ }
99
+ }
45
100
  const r = await ctx.memoryGuard.guardedRemove(file, text);
46
101
  return {
47
102
  success: r.ok,