@aitne/daemon 0.1.10 → 0.1.11

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 (305) hide show
  1. package/dist/adapters/adapter-watchdog.d.ts +70 -0
  2. package/dist/adapters/adapter-watchdog.js +115 -0
  3. package/dist/adapters/discord.d.ts +17 -1
  4. package/dist/adapters/discord.js +33 -0
  5. package/dist/adapters/notification-manager.d.ts +27 -1
  6. package/dist/adapters/notification-manager.js +54 -39
  7. package/dist/adapters/slack-adapter.d.ts +26 -1
  8. package/dist/adapters/slack-adapter.js +41 -0
  9. package/dist/adapters/telegram-adapter.d.ts +18 -1
  10. package/dist/adapters/telegram-adapter.js +41 -2
  11. package/dist/adapters/types.d.ts +20 -0
  12. package/dist/adapters/whatsapp-adapter.d.ts +26 -7
  13. package/dist/adapters/whatsapp-adapter.js +74 -21
  14. package/dist/api/env-writer.js +8 -5
  15. package/dist/api/helpers/agent-errors-registry.d.ts +5 -5
  16. package/dist/api/helpers/agent-errors-registry.js +5 -5
  17. package/dist/api/routes/agent.js +33 -12
  18. package/dist/api/routes/agents/index.js +75 -16
  19. package/dist/api/routes/agents/views.d.ts +37 -2
  20. package/dist/api/routes/agents/views.js +64 -2
  21. package/dist/api/routes/background-task.d.ts +22 -0
  22. package/dist/api/routes/background-task.js +338 -0
  23. package/dist/api/routes/browser-history.js +9 -1
  24. package/dist/api/routes/context/permissions.js +3 -2
  25. package/dist/api/routes/context/snapshots.js +0 -3
  26. package/dist/api/routes/context/write.js +3 -17
  27. package/dist/api/routes/dashboard/config.js +48 -12
  28. package/dist/api/routes/dashboard/cost-approvals.js +66 -0
  29. package/dist/api/routes/dashboard/notifications.js +9 -9
  30. package/dist/api/routes/integrations/crud-patch.js +5 -1
  31. package/dist/api/routes/integrations-reconcile.js +2 -2
  32. package/dist/api/routes/notion.d.ts +1 -1
  33. package/dist/api/routes/observations.js +7 -7
  34. package/dist/api/routes/obsidian.d.ts +1 -1
  35. package/dist/api/routes/receipts.js +5 -1
  36. package/dist/api/routes/setup-migrate.js +1 -1
  37. package/dist/api/routes/setup.js +1 -1
  38. package/dist/api/routes/task-flows.d.ts +1 -1
  39. package/dist/api/routes/task-flows.js +1 -1
  40. package/dist/api/routes/tuning.d.ts +29 -0
  41. package/dist/api/routes/tuning.js +304 -0
  42. package/dist/api/server.d.ts +44 -16
  43. package/dist/api/server.js +9 -0
  44. package/dist/bootstrap/adapters.d.ts +19 -0
  45. package/dist/bootstrap/adapters.js +61 -0
  46. package/dist/bootstrap/api.d.ts +5 -3
  47. package/dist/bootstrap/api.js +45 -13
  48. package/dist/bootstrap/catchup.d.ts +1 -1
  49. package/dist/bootstrap/catchup.js +11 -11
  50. package/dist/bootstrap/event-pipeline.d.ts +11 -0
  51. package/dist/bootstrap/event-pipeline.js +245 -7
  52. package/dist/bootstrap/observers.js +9 -6
  53. package/dist/bootstrap/schedule-helpers.d.ts +104 -6
  54. package/dist/bootstrap/schedule-helpers.js +172 -19
  55. package/dist/config.js +26 -12
  56. package/dist/core/agent-core.d.ts +33 -1
  57. package/dist/core/agent-core.js +36 -1
  58. package/dist/core/agents/activity-scan-cadence.d.ts +103 -0
  59. package/dist/core/agents/activity-scan-cadence.js +127 -0
  60. package/dist/core/agents/agent-route-override.d.ts +53 -0
  61. package/dist/core/agents/agent-route-override.js +69 -0
  62. package/dist/core/agents/builtin-registry.d.ts +51 -14
  63. package/dist/core/agents/builtin-registry.js +92 -15
  64. package/dist/core/agents/config-gate-reconcile.d.ts +38 -0
  65. package/dist/core/agents/config-gate-reconcile.js +51 -0
  66. package/dist/core/agents/cron-substitute.d.ts +1 -1
  67. package/dist/core/agents/cron-substitute.js +1 -1
  68. package/dist/core/agents/custom-routine-migration.d.ts +60 -0
  69. package/dist/core/agents/custom-routine-migration.js +149 -0
  70. package/dist/core/agents/firing-blocked.d.ts +1 -1
  71. package/dist/core/agents/hourly-cadence.d.ts +102 -0
  72. package/dist/core/agents/hourly-cadence.js +126 -0
  73. package/dist/core/agents/loader-boot.js +23 -0
  74. package/dist/core/agents/loader.d.ts +19 -0
  75. package/dist/core/agents/loader.js +34 -2
  76. package/dist/core/agents/override-merge.d.ts +1 -1
  77. package/dist/core/agents/override-merge.js +9 -1
  78. package/dist/core/agents/recurrence-convert.d.ts +1 -1
  79. package/dist/core/agents/recurrence-convert.js +1 -1
  80. package/dist/core/agents/recurring-schedule-adapter.js +8 -0
  81. package/dist/core/alerts.js +6 -6
  82. package/dist/core/backends/auth-health-monitor.d.ts +2 -2
  83. package/dist/core/backends/auth-health-monitor.js +1 -1
  84. package/dist/core/backends/backend-router.d.ts +27 -1
  85. package/dist/core/backends/backend-router.js +165 -1
  86. package/dist/core/backends/claude-code-core.d.ts +71 -31
  87. package/dist/core/backends/claude-code-core.js +282 -54
  88. package/dist/core/backends/cli-quota-guards.d.ts +29 -1
  89. package/dist/core/backends/cli-quota-guards.js +40 -5
  90. package/dist/core/backends/codex-core.d.ts +6 -0
  91. package/dist/core/backends/codex-core.js +22 -6
  92. package/dist/core/backends/failure-spend.d.ts +58 -0
  93. package/dist/core/backends/failure-spend.js +137 -0
  94. package/dist/core/backends/gemini-cli-core.d.ts +6 -0
  95. package/dist/core/backends/gemini-cli-core.js +25 -6
  96. package/dist/core/backends/model-registry.d.ts +1 -1
  97. package/dist/core/backends/model-registry.js +4 -4
  98. package/dist/core/backends/opencode-core.d.ts +1 -1
  99. package/dist/core/backends/opencode-core.js +5 -5
  100. package/dist/core/backends/plan-presets.js +39 -15
  101. package/dist/core/bang-commands/commands-cost.js +3 -1
  102. package/dist/core/bang-commands/commands-report.js +4 -3
  103. package/dist/core/bang-commands/commands-research.js +4 -1
  104. package/dist/core/bang-commands/commands-revert-tuning.d.ts +18 -0
  105. package/dist/core/bang-commands/commands-revert-tuning.js +63 -0
  106. package/dist/core/bang-commands/commands-stop-start.js +3 -3
  107. package/dist/core/bang-commands/commands-task-control.d.ts +19 -0
  108. package/dist/core/bang-commands/commands-task-control.js +147 -0
  109. package/dist/core/bang-commands/commands-wiki.js +5 -5
  110. package/dist/core/bang-commands/index.d.ts +2 -0
  111. package/dist/core/bang-commands/index.js +12 -0
  112. package/dist/core/bang-commands/registry.d.ts +12 -0
  113. package/dist/core/browser-history/research-cluster-fanout.d.ts +28 -14
  114. package/dist/core/browser-history/research-cluster-fanout.js +39 -16
  115. package/dist/core/channel-timeline.d.ts +5 -1
  116. package/dist/core/channel-timeline.js +13 -0
  117. package/dist/core/context/index-reconciler.js +5 -2
  118. package/dist/core/context/policy-index-reconciler.d.ts +6 -4
  119. package/dist/core/context/policy-index-runner.js +25 -6
  120. package/dist/core/context-builder-calendar.js +10 -2
  121. package/dist/core/context-builder-conversation.d.ts +8 -1
  122. package/dist/core/context-builder-conversation.js +41 -7
  123. package/dist/core/context-builder-yesterday.js +4 -3
  124. package/dist/core/context-builder.d.ts +7 -2
  125. package/dist/core/context-builder.js +62 -20
  126. package/dist/core/context-file-serializer.d.ts +1 -1
  127. package/dist/core/context-file-serializer.js +1 -1
  128. package/dist/core/context-health.js +2 -2
  129. package/dist/core/context-paths.d.ts +1 -1
  130. package/dist/core/context-paths.js +1 -1
  131. package/dist/core/context-validation/prepare-write.js +1 -1
  132. package/dist/core/context-validation/routine-rulebook.d.ts +1 -1
  133. package/dist/core/context-vault-aliases.d.ts +0 -13
  134. package/dist/core/context-vault-aliases.js +37 -0
  135. package/dist/core/custom-routines.d.ts +99 -0
  136. package/dist/core/custom-routines.js +187 -0
  137. package/dist/core/daemon-api-cli.js +49 -0
  138. package/dist/core/day-boundary.d.ts +46 -0
  139. package/dist/core/day-boundary.js +40 -0
  140. package/dist/core/dispatcher-activity-scan.d.ts +221 -0
  141. package/dist/core/dispatcher-activity-scan.js +775 -0
  142. package/dist/core/dispatcher-error-handling.d.ts +6 -11
  143. package/dist/core/dispatcher-error-handling.js +38 -62
  144. package/dist/core/dispatcher-hourly-check.js +6 -1
  145. package/dist/core/dispatcher-message-handler.d.ts +10 -0
  146. package/dist/core/dispatcher-message-handler.js +17 -0
  147. package/dist/core/dispatcher-morning-routine.d.ts +6 -6
  148. package/dist/core/dispatcher-morning-routine.js +13 -13
  149. package/dist/core/dispatcher-result-processor.d.ts +33 -0
  150. package/dist/core/dispatcher-result-processor.js +167 -11
  151. package/dist/core/dispatcher-scheduled-background-task.d.ts +42 -0
  152. package/dist/core/dispatcher-scheduled-background-task.js +89 -0
  153. package/dist/core/dispatcher-scheduled-tasks.d.ts +63 -1
  154. package/dist/core/dispatcher-scheduled-tasks.js +213 -6
  155. package/dist/core/dispatcher-task-delivery.d.ts +105 -0
  156. package/dist/core/dispatcher-task-delivery.js +555 -0
  157. package/dist/core/dispatcher-types.d.ts +48 -9
  158. package/dist/core/dispatcher-types.js +3 -3
  159. package/dist/core/dispatcher.d.ts +112 -31
  160. package/dist/core/dispatcher.js +284 -59
  161. package/dist/core/dm-freshness-metrics.d.ts +1 -1
  162. package/dist/core/drift-effects.js +2 -2
  163. package/dist/core/feedback/consolidation-prep.js +17 -5
  164. package/dist/core/feedback/eviction-scorer.js +6 -2
  165. package/dist/core/feedback/lesson-format.js +9 -4
  166. package/dist/core/feedback/lesson-injection.d.ts +1 -1
  167. package/dist/core/feedback/lesson-injection.js +17 -2
  168. package/dist/core/feedback/lesson-store-overview.d.ts +8 -4
  169. package/dist/core/feedback/lesson-store-overview.js +8 -4
  170. package/dist/core/feedback/regeneralization-prep.js +29 -16
  171. package/dist/core/feedback/self-performance-prep.d.ts +186 -0
  172. package/dist/core/feedback/self-performance-prep.js +541 -0
  173. package/dist/core/feedback/tuning-actuator.d.ts +198 -0
  174. package/dist/core/feedback/tuning-actuator.js +432 -0
  175. package/dist/core/feedback/tuning-recommender.d.ts +247 -0
  176. package/dist/core/feedback/tuning-recommender.js +580 -0
  177. package/dist/core/feedback/tuning-revert-monitor.d.ts +90 -0
  178. package/dist/core/feedback/tuning-revert-monitor.js +213 -0
  179. package/dist/core/health-monitor.d.ts +6 -0
  180. package/dist/core/health-monitor.js +1 -1
  181. package/dist/core/injection-policy.d.ts +4 -4
  182. package/dist/core/injection-policy.js +4 -4
  183. package/dist/core/integration-main-backend.js +4 -0
  184. package/dist/core/management-md.d.ts +2 -2
  185. package/dist/core/management-md.js +51 -13
  186. package/dist/core/morning/orchestrator.d.ts +2 -2
  187. package/dist/core/morning/orchestrator.js +2 -2
  188. package/dist/core/notification-gate.d.ts +64 -0
  189. package/dist/core/notification-gate.js +51 -0
  190. package/dist/core/notification-rate-limit.d.ts +40 -0
  191. package/dist/core/notification-rate-limit.js +50 -0
  192. package/dist/core/policy-files.d.ts +1 -1
  193. package/dist/core/policy-files.js +2 -2
  194. package/dist/core/pre-pass-freshness.d.ts +4 -4
  195. package/dist/core/retention.d.ts +5 -0
  196. package/dist/core/retention.js +20 -4
  197. package/dist/core/review-context.d.ts +1 -1
  198. package/dist/core/review-context.js +10 -5
  199. package/dist/core/roadmap-write-lock.d.ts +2 -1
  200. package/dist/core/roadmap-write-lock.js +15 -10
  201. package/dist/core/routine-acquisition-plan.d.ts +47 -1
  202. package/dist/core/routine-acquisition-plan.js +78 -20
  203. package/dist/core/routine-fetch-window-retry.js +7 -4
  204. package/dist/core/routine-fetch-window-runner.d.ts +39 -3
  205. package/dist/core/routine-fetch-window-runner.js +264 -13
  206. package/dist/core/routine-windows.d.ts +2 -2
  207. package/dist/core/routine-windows.js +8 -5
  208. package/dist/core/scheduler.d.ts +175 -16
  209. package/dist/core/scheduler.js +559 -102
  210. package/dist/core/signal-detector.d.ts +12 -0
  211. package/dist/core/signal-detector.js +53 -9
  212. package/dist/core/skills-compiler-denied-tools.js +2 -2
  213. package/dist/core/skills-compiler-skill-index.d.ts +2 -2
  214. package/dist/core/skills-compiler-skill-index.js +2 -2
  215. package/dist/core/skills-compiler-variants.d.ts +1 -1
  216. package/dist/core/skills-compiler-variants.js +8 -0
  217. package/dist/core/skills-compiler.d.ts +29 -26
  218. package/dist/core/skills-compiler.js +117 -81
  219. package/dist/core/skills-manifest.d.ts +37 -0
  220. package/dist/core/skills-manifest.js +73 -2
  221. package/dist/core/sleep-inhibitor.d.ts +79 -0
  222. package/dist/core/sleep-inhibitor.js +132 -0
  223. package/dist/core/slim-system-prompt-loader.d.ts +77 -0
  224. package/dist/core/slim-system-prompt-loader.js +141 -0
  225. package/dist/core/spawn-gates.d.ts +126 -0
  226. package/dist/core/spawn-gates.js +180 -0
  227. package/dist/core/today-direct-writer.d.ts +2 -2
  228. package/dist/core/today-direct-writer.js +1 -1
  229. package/dist/core/today-write-lock.d.ts +4 -2
  230. package/dist/core/today-write-lock.js +30 -20
  231. package/dist/core/wake-detector.d.ts +55 -0
  232. package/dist/core/wake-detector.js +80 -0
  233. package/dist/core/wiki/compile-lock.d.ts +1 -1
  234. package/dist/core/wiki/compile-lock.js +1 -1
  235. package/dist/core/workdir.js +15 -6
  236. package/dist/db/activity-scan-signals.d.ts +77 -0
  237. package/dist/db/activity-scan-signals.js +378 -0
  238. package/dist/db/agents-store.d.ts +28 -0
  239. package/dist/db/agents-store.js +62 -0
  240. package/dist/db/background-task-clarifications-store.d.ts +81 -0
  241. package/dist/db/background-task-clarifications-store.js +152 -0
  242. package/dist/db/background-task-store.d.ts +207 -0
  243. package/dist/db/background-task-store.js +380 -0
  244. package/dist/db/browser-history-store.d.ts +39 -6
  245. package/dist/db/browser-history-store.js +51 -7
  246. package/dist/db/browser-task-clarifications-store.d.ts +12 -0
  247. package/dist/db/browser-task-clarifications-store.js +35 -5
  248. package/dist/db/browser-task-store.d.ts +3 -0
  249. package/dist/db/browser-task-store.js +29 -4
  250. package/dist/db/deferred-dm.d.ts +86 -0
  251. package/dist/db/deferred-dm.js +199 -0
  252. package/dist/db/migrations.js +330 -0
  253. package/dist/db/observations.d.ts +2 -2
  254. package/dist/db/observations.js +3 -3
  255. package/dist/db/schema.js +217 -16
  256. package/dist/db/voice-transcripts-store.d.ts +1 -1
  257. package/dist/index.js +86 -29
  258. package/dist/messaging/browser-task-mcp-notifier.d.ts +12 -70
  259. package/dist/messaging/browser-task-mcp-notifier.js +30 -151
  260. package/dist/messaging/browser-task-screenshot-attachment.d.ts +15 -0
  261. package/dist/messaging/browser-task-screenshot-attachment.js +63 -0
  262. package/dist/observers/delegated-sync-worker.d.ts +6 -6
  263. package/dist/observers/delegated-sync-worker.js +10 -10
  264. package/dist/observers/git-delegated-cron.d.ts +1 -1
  265. package/dist/observers/git-delegated-cron.js +2 -2
  266. package/dist/observers/github-poller-classifier.d.ts +3 -3
  267. package/dist/observers/github-poller-classifier.js +3 -3
  268. package/dist/observers/imminent-event-scheduler.d.ts +1 -1
  269. package/dist/observers/imminent-event-scheduler.js +1 -1
  270. package/dist/observers/mail-poller.d.ts +1 -0
  271. package/dist/observers/mail-poller.js +42 -3
  272. package/dist/observers/observation-summarizer/summarizer-client.d.ts +2 -2
  273. package/dist/observers/observation-summarizer/summarizer-client.js +2 -2
  274. package/dist/observers/observation-summarizer/worker.d.ts +2 -2
  275. package/dist/observers/observation-summarizer/worker.js +4 -4
  276. package/dist/observers/obsidian-watcher.d.ts +1 -1
  277. package/dist/observers/obsidian-watcher.js +1 -1
  278. package/dist/safety/agent-write-tracker.d.ts +4 -4
  279. package/dist/safety/agent-write-tracker.js +4 -4
  280. package/dist/safety/audit.d.ts +43 -5
  281. package/dist/safety/audit.js +86 -18
  282. package/dist/safety/risk-classifier.d.ts +6 -0
  283. package/dist/safety/risk-classifier.js +75 -11
  284. package/dist/scheduler/activity-scan-gate.d.ts +86 -0
  285. package/dist/scheduler/activity-scan-gate.js +132 -0
  286. package/dist/services/background-task/background-task-budget.d.ts +80 -0
  287. package/dist/services/background-task/background-task-budget.js +91 -0
  288. package/dist/services/background-task/background-task-driver.d.ts +105 -0
  289. package/dist/services/background-task/background-task-driver.js +416 -0
  290. package/dist/services/background-task/background-task-runner.d.ts +96 -0
  291. package/dist/services/background-task/background-task-runner.js +673 -0
  292. package/dist/services/background-task/background-task-tools.d.ts +84 -0
  293. package/dist/services/background-task/background-task-tools.js +247 -0
  294. package/dist/services/background-task/background-task-transition-events.d.ts +43 -0
  295. package/dist/services/background-task/background-task-transition-events.js +54 -0
  296. package/dist/services/browser-history/automation/egress-denylist.d.ts +1 -1
  297. package/dist/services/browser-history/automation/egress-denylist.js +16 -6
  298. package/dist/services/browser-history/managed-chromium/sandbox-launcher.js +0 -1
  299. package/dist/services/browser-task/browser-task-runner.js +53 -8
  300. package/dist/services/observations-batch.d.ts +1 -1
  301. package/dist/services/observations-batch.js +2 -2
  302. package/dist/settings/runtime-settings.d.ts +38 -11
  303. package/dist/settings/runtime-settings.js +203 -40
  304. package/dist/settings/settings-store.js +11 -3
  305. package/package.json +4 -4
@@ -120,6 +120,18 @@ export declare class SignalDetector {
120
120
  static normalizeDedupKey(signal: RawSignal): string;
121
121
  /** Remove expired entries from the dedup cache */
122
122
  private pruneExpiredDedup;
123
+ /**
124
+ * Precision matters more than recall here: a match marks the reply
125
+ * `corrected` (valence `correction`), and the promotion gate treats any
126
+ * correction as an authoritative directive that promotes a lesson on
127
+ * FIRST occurrence — one false positive writes a bogus standing
128
+ * directive into `policies/agent-lessons.md`. So the stop/don't pattern
129
+ * requires imperative shape (sentence-initial, modulo a couple of
130
+ * politeness tokens) paired with an agent-action verb, instead of the
131
+ * old `\b(stop|no)\b.*\b(do|that|…)\b` / bare `don't` catch-alls that
132
+ * matched benign replies like "No worries, that sounds great" or
133
+ * "I don't have anything else today".
134
+ */
123
135
  private static isCorrectionContent;
124
136
  private findPendingNotificationForReply;
125
137
  private resolvePendingNotificationId;
@@ -15,6 +15,26 @@ const MAX_RAW_SIGNALS = 20;
15
15
  * this window are suppressed.
16
16
  */
17
17
  const DEDUP_TTL_MS = 10 * 60 * 1000;
18
+ /**
19
+ * Reaction emoji that signal disapproval. A 👎 must not be recorded as a
20
+ * positive behavioral signal — the consolidation LLM only sees the row's
21
+ * summary, so a mis-valenced reaction would count as positive corroboration
22
+ * for the very notification the owner disliked. Conservative set: ambiguous
23
+ * emoji (😢, 😕, …) stay positive-by-default rather than guessing.
24
+ */
25
+ const NEGATIVE_REACTION_EMOJI = new Set([
26
+ "👎",
27
+ "❌",
28
+ "🚫",
29
+ "🛑",
30
+ "😠",
31
+ "😡",
32
+ "💢",
33
+ ]);
34
+ /** Strip skin-tone modifiers + variation selectors so 👎🏽 matches 👎. */
35
+ function normalizeReactionEmoji(emoji) {
36
+ return emoji.replace(/[\u{1F3FB}-\u{1F3FF}\u{FE0E}\u{FE0F}]/gu, "").trim();
37
+ }
18
38
  /**
19
39
  * SignalDetector — collects implicit user feedback signals (no LLM required).
20
40
  *
@@ -98,10 +118,12 @@ export class SignalDetector {
98
118
  if (params.notificationId) {
99
119
  const pendingId = this.resolvePendingNotificationId(params.notificationId);
100
120
  this.pendingNotifications.delete(pendingId);
121
+ const negative = NEGATIVE_REACTION_EMOJI.has(normalizeReactionEmoji(emoji));
101
122
  this.recordNotificationOutcome({
102
123
  notificationId: pendingId,
103
124
  reaction: "replied",
104
- valence: "positive",
125
+ valence: negative ? "negative" : "positive",
126
+ emoji,
105
127
  evidence: {
106
128
  emoji,
107
129
  responseTimeMs,
@@ -283,14 +305,28 @@ export class SignalDetector {
283
305
  }
284
306
  }
285
307
  }
308
+ /**
309
+ * Precision matters more than recall here: a match marks the reply
310
+ * `corrected` (valence `correction`), and the promotion gate treats any
311
+ * correction as an authoritative directive that promotes a lesson on
312
+ * FIRST occurrence — one false positive writes a bogus standing
313
+ * directive into `policies/agent-lessons.md`. So the stop/don't pattern
314
+ * requires imperative shape (sentence-initial, modulo a couple of
315
+ * politeness tokens) paired with an agent-action verb, instead of the
316
+ * old `\b(stop|no)\b.*\b(do|that|…)\b` / bare `don't` catch-alls that
317
+ * matched benign replies like "No worries, that sounds great" or
318
+ * "I don't have anything else today".
319
+ */
286
320
  static isCorrectionContent(content) {
287
321
  const correctionPatterns = [
288
- /shorter|brief|concise/i,
289
- /more detail|elaborate|expand/i,
322
+ /\b(shorter|brief|briefly|concise|concisely|less verbose|too long|too verbose)\b/i,
323
+ /\b(more detail|elaborate|expand)\b/i,
290
324
  /\bin (english|spanish|french|german|portuguese|italian|chinese|japanese|korean|arabic|hindi|russian)\b/i,
291
- /bullet points|bulleted list/i,
292
- /\b(stop|no)\b.*\b(notify|message|remind|send|doing|do|again|that)\b/i,
293
- /\b(do not|don't)\b/i,
325
+ /\b(bullet points|bulleted list)\b/i,
326
+ // Imperative "stop/don't/no more <agent action>" at the start of the
327
+ // reply: optional politeness tokens, a negation/stop verb, then an
328
+ // agent-action word within the same clause.
329
+ /^\W*(?:(?:please|pls|just|hey|ok|okay|can you|could you|you can|maybe)\s+){0,2}(?:stop|quit|don['’]t|do not|never|no more)\b[^.!?\n]{0,40}?\b(?:notify(?:ing)?|notifications?|messag(?:e|es|ing)|remind(?:er|ers|ing)?|send(?:ing)?|ping(?:ing)?|dm(?:s|ing)?|post(?:ing)?|repeat(?:ing)?|do(?:ing)?\s+(?:that|this))\b/i,
294
330
  ];
295
331
  return correctionPatterns.some((pattern) => pattern.test(content));
296
332
  }
@@ -389,7 +425,10 @@ export class SignalDetector {
389
425
  return;
390
426
  }
391
427
  const scopeType = metadata.agentId ? "agent_slug" : "agent";
392
- const summary = this.buildOutcomeSummary(params.reaction, metadata);
428
+ const summary = this.buildOutcomeSummary(params.reaction, metadata, {
429
+ valence: params.valence,
430
+ emoji: params.emoji,
431
+ });
393
432
  const evidence = this.sanitizeEvidence({
394
433
  ...params.evidence,
395
434
  userReaction: params.reaction,
@@ -425,20 +464,25 @@ export class SignalDetector {
425
464
  SET user_reaction = ?, reacted_at = CURRENT_TIMESTAMP
426
465
  WHERE dispatch_id = ?`).run(reaction, dispatchId);
427
466
  }
428
- buildOutcomeSummary(reaction, metadata) {
467
+ buildOutcomeSummary(reaction, metadata, opts = {}) {
429
468
  const content = metadata.contentSummary
430
469
  ? ` "${metadata.contentSummary}"`
431
470
  : "";
432
471
  const notificationType = metadata.notificationType
433
472
  ? ` (${metadata.notificationType})`
434
473
  : "";
474
+ const emojiNote = opts.emoji ? ` (${opts.emoji})` : "";
435
475
  const raw = reaction === "ignored"
436
476
  ? `Owner did not respond to notification${content}${notificationType}`
437
477
  : reaction === "corrected"
438
478
  ? `Owner corrected notification${content}${notificationType}`
439
479
  : reaction === "acted"
440
480
  ? `Owner acted on notification${content}${notificationType}`
441
- : `Owner responded to notification${content}${notificationType}`;
481
+ : opts.valence === "negative"
482
+ ? `Owner reacted negatively${emojiNote} to notification${content}${notificationType}`
483
+ : opts.emoji
484
+ ? `Owner reacted${emojiNote} to notification${content}${notificationType}`
485
+ : `Owner responded to notification${content}${notificationType}`;
442
486
  return redactSensitiveString(raw.replace(/\s+/g, " ").trim()).slice(0, 280);
443
487
  }
444
488
  sanitizeEvidence(value) {
@@ -124,7 +124,7 @@ export function buildSameBackendDenyBlock(integrations, sessionBackend) {
124
124
  "## Denied tools (per-integration)",
125
125
  "",
126
126
  "The user has restricted the following connector tools for this session.",
127
- "Do NOT invoke them in any flow — including hourly check, morning routine,",
127
+ "Do NOT invoke them in any flow — including activity scan, morning routine,",
128
128
  "or DM responses. If a workflow appears to require one, stop and tell the",
129
129
  "user the tool is denied.",
130
130
  ];
@@ -157,7 +157,7 @@ function appendCliDenyBlock(content, namespacedDenied) {
157
157
  "## Denied tools (do not invoke)",
158
158
  "",
159
159
  "The user has restricted these connector tools for this integration. Do",
160
- "NOT invoke them in any flow — including hourly check, morning routine,",
160
+ "NOT invoke them in any flow — including activity scan, morning routine,",
161
161
  "or DM responses. If a workflow appears to require one, stop and tell",
162
162
  "the user the tool is denied.",
163
163
  "",
@@ -75,8 +75,8 @@ export declare function refreshSkillIndexBlock(sessionDir: string, backendId: Ba
75
75
  * @deprecated DELEGATED-PROXY-API-DESIGN.md §11 — after Phase D no in-tree
76
76
  * skill uses `{{> base }}`: mail / external-services unified their bodies
77
77
  * during Phase D, and the lone surviving `notion/SKILL.base.md` was
78
- * removed in Phase 9 (its hourly-check read-only constraint moved to
79
- * `routine.hourly_check.md` task-flow where the rule applies in every
78
+ * removed in Phase 9 (its activity-scan read-only constraint moved to
79
+ * `routine.activity_scan.md` task-flow where the rule applies in every
80
80
  * integration mode, including same-backend delegated where the skill
81
81
  * body is dropped). The helper is retained for future composition needs;
82
82
  * remove the call sites in `materializeClaudeSession` /
@@ -203,8 +203,8 @@ export function refreshSkillIndexBlock(sessionDir, backendId) {
203
203
  * @deprecated DELEGATED-PROXY-API-DESIGN.md §11 — after Phase D no in-tree
204
204
  * skill uses `{{> base }}`: mail / external-services unified their bodies
205
205
  * during Phase D, and the lone surviving `notion/SKILL.base.md` was
206
- * removed in Phase 9 (its hourly-check read-only constraint moved to
207
- * `routine.hourly_check.md` task-flow where the rule applies in every
206
+ * removed in Phase 9 (its activity-scan read-only constraint moved to
207
+ * `routine.activity_scan.md` task-flow where the rule applies in every
208
208
  * integration mode, including same-backend delegated where the skill
209
209
  * body is dropped). The helper is retained for future composition needs;
210
210
  * remove the call sites in `materializeClaudeSession` /
@@ -113,5 +113,5 @@ export declare const SKILL_DESCRIPTION_MAX_LENGTH = 280;
113
113
  * MUST use {@link skillBodyTouchesReadSensitive} to keep the
114
114
  * "literal prefix" decision encapsulated.
115
115
  */
116
- export declare const READ_SENSITIVE_API_PREFIXES: readonly ["/api/apple-calendar", "/api/books", "/api/browser-task", "/api/calendar", "/api/context", "/api/entities", "/api/mail", "/api/mcp/servers", "/api/notion", "/api/observations", "/api/obsidian", "/api/receipts", "/api/travel-bookings"];
116
+ export declare const READ_SENSITIVE_API_PREFIXES: readonly ["/api/apple-calendar", "/api/background-task", "/api/books", "/api/browser-task", "/api/calendar", "/api/context", "/api/entities", "/api/mail", "/api/mcp/servers", "/api/notion", "/api/observations", "/api/obsidian", "/api/receipts", "/api/travel-bookings"];
117
117
  export declare function skillBodyTouchesReadSensitive(skillBody: string): boolean;
@@ -311,6 +311,14 @@ export const SKILL_DESCRIPTION_MAX_LENGTH = 280;
311
311
  */
312
312
  export const READ_SENSITIVE_API_PREFIXES = [
313
313
  "/api/apple-calendar",
314
+ // BACKGROUND_TASK_RUNNER_DESIGN.md §7 — list / detail reads on the
315
+ // generic detached-task surface. The artifact JSON (`report` / `brief`
316
+ // / `draft` / `significance`) carries the user's research / audit
317
+ // content, so the GETs are ReadSensitive (the POST spawn + clarify +
318
+ // cancel are Autonomous). A Codex DM session referencing the
319
+ // `background-task` skill body gets the read-token banner so its
320
+ // follow-up `GET /:id` read doesn't 401 silently.
321
+ "/api/background-task",
314
322
  "/api/books",
315
323
  // BROWSER_TASK_REDESIGN_PLAN.md §3 — list / detail / events / screenshot
316
324
  // reads on the open-ended browser sub-agent surface. The screenshot
@@ -2,6 +2,7 @@ import type DatabaseNs from "better-sqlite3";
2
2
  import { type BackendId, type IntegrationKey, type IntegrationState, type ProcessKey } from "@aitne/shared";
3
3
  import type { MailAccount } from "../services/mail/provider.js";
4
4
  import { type SkillCompilerFile } from "./skills-compiler-tree.js";
5
+ export declare const SLIM_CLI_SKILL_SETS: Partial<Record<ProcessKey, readonly string[]>>;
5
6
  interface SessionPromptBundleParams {
6
7
  backendId: BackendId;
7
8
  sessionDir: string;
@@ -334,39 +335,41 @@ export declare class SkillsCompiler {
334
335
  private spliceCurationAnchorsInSkill;
335
336
  /**
336
337
  * docs/design/appendices/skills-unification.md Phase 1 item 15 — the slim path does NOT
337
- * emit a `<skill-index>` block or the skill-discovery preamble. The
338
- * fetch_window system prompt is a self-contained operational contract
339
- * (one-window-one-curl, no sub-tasks, exactly-one JSON-on-stdout) and
340
- * the only skill copied (`observations`) is referenced inline by the
341
- * runner-emitted user prompt. Adding the index would mis-signal the
342
- * fetcher to scan for skills before executing the acquisition plan.
338
+ * emit a `<skill-index>` block or the skill-discovery preamble. The slim
339
+ * system prompt is a self-contained operational contract and its copied
340
+ * skills are referenced inline by the dispatched user prompt / task-flow.
341
+ * Adding the index would mis-signal the agent to scan for skills before
342
+ * executing its mechanical task.
343
343
  *
344
- * docs/design/appendices/fetch-window-cost-reduction.md Phase 1.5 slim instruction-file
345
- * materializer for `routine.fetch_window` on Codex / Gemini CLI.
344
+ * fetch-window-cost-reduction.md Phase 1.5 / RESEARCH_CLUSTER_COST_FIX_PLAN.md
345
+ * F4 — slim instruction-file materializer for a `SLIM_CLI_SKILL_SETS` process
346
+ * key on Codex / Gemini CLI.
346
347
  *
347
- * Mirrors the Claude SDK's Phase 1 systemPrompt swap (the same
348
- * `agent-assets/system-prompts/routine-fetch-window.md` template is the
349
- * single source of truth): write the slim body verbatim as AGENTS.md /
350
- * GEMINI.md and copy only the `observations` skill — the
351
- * `/api/observations/batch` POST contract is the fetcher's sole
352
- * structural assertion. The integration partial inlined by the runner
353
- * (`routine-fetch-window-runner.ts:reassemblePrompt`) covers the
354
- * per-attempt call shape, so `mail` / `notion` / `external-services` /
355
- * `attach` skill bodies are deliberately omitted.
348
+ * Mirrors the Claude SDK's slim-systemPrompt swap (the same
349
+ * `agent-assets/system-prompts/<key>.md` template is the single source of
350
+ * truth, read through the shared `loadSlimSystemPrompt` registry): write the
351
+ * slim body verbatim as AGENTS.md / GEMINI.md and copy only the key's
352
+ * `SLIM_CLI_SKILL_SETS` bundle the wide manifest's other slugs are restated
353
+ * by the dispatched user prompt / task-flow, so their bodies are omitted.
356
354
  *
357
- * No safety preamble / character / behavioral-rules / daemon-API
358
- * sections — the slim template restates the only rules the fetcher
359
- * needs (localhost-only curl, no sub-tasks, no context writes, no
360
- * notify, JSON-on-stdout-and-exit). The destructive-action policy layer
361
- * (absolute-block list, Codex sandbox, Gemini admin TOML) still applies
362
- * unchanged at runtime.
355
+ * No safety preamble / character / behavioral-rules / daemon-API sections —
356
+ * the slim template restates the only rules the session needs. The
357
+ * destructive-action policy layer (absolute-block list, Codex sandbox,
358
+ * Gemini admin TOML) still applies unchanged at runtime.
363
359
  *
364
360
  * The `<mcp-servers>` section is appended downstream by
365
361
  * `services/mcp/session-materializer.ts:appendMcpSection` exactly as on
366
- * the wide path — Phase 3's allowlist filter, when it lands, will scope
367
- * that section without further changes here.
362
+ * the wide path.
368
363
  */
369
- private materializeFetchWindowCliSession;
364
+ private materializeSlimCliSession;
365
+ /**
366
+ * Copy one built-in skill dir into a slim CLI session, applying the same
367
+ * adaptation pipeline the wide CLI path runs (brand tokens, partial /
368
+ * reference includes, integration-mode filter, tool-deny prose, curation
369
+ * anchors). Frontmatter stays intact (skills-unification.md §R6). A
370
+ * no-op when the source SKILL.md is absent.
371
+ */
372
+ private copySlimSkillDir;
370
373
  private materializeCliSession;
371
374
  }
372
375
  export {};
@@ -5,7 +5,7 @@ import { composeSkillSet, getProfileForEvent, getProfileForProcess, resolveSkill
5
5
  import { applyCharacterBlockRewrite, buildCharacterBlock } from "./character-block.js";
6
6
  import { applyOutputLanguagePointerRewrite } from "./output-language-policy.js";
7
7
  import { createLogger } from "../logging.js";
8
- import { loadFetchWindowSystemPrompt } from "./fetch-window-prompt-loader.js";
8
+ import { loadSlimSystemPrompt } from "./slim-system-prompt-loader.js";
9
9
  import { substituteIntegrationRoutingTables } from "./management-md.js";
10
10
  import { loadCurationDeclaration, } from "./skill-curation/declarations.js";
11
11
  import { resolveBuiltinSkillDir } from "./skill-source-paths.js";
@@ -21,18 +21,43 @@ import { cliInstructionFileName, cliSkillsDirName, prependCodexReadSensitiveBann
21
21
  // spliceCurationAnchorsInSkill). Not exported — sibling modules carry
22
22
  // their own peer loggers; the test suite spies on those.
23
23
  const logger = createLogger("skills-compiler");
24
- // docs/design/appendices/fetch-window-cost-reduction.md Phase 1.5 single shared constant
25
- // for the pre-pass process key. Typed as `ProcessKey` (not the inferred
26
- // string literal) so a rename in `@aitne/shared/process-key.ts` lights up
27
- // here rather than silently dead-branching the slim materializer.
28
- const FETCH_WINDOW_PROCESS_KEY = "routine.fetch_window";
29
- // Single skill kept in the slim fetch_window CLI session: the
30
- // `observations` SKILL.md is the POST contract for
31
- // `/api/observations/batch` and the fetcher's only structural assertion.
32
- // The other skills the wide path inlines (`mail`, `notion`,
33
- // `external-services`, `attach`) are already restated by the integration
34
- // partial the runner inlines into the user prompt.
35
- const FETCH_WINDOW_SLIM_SKILL = "observations";
24
+ // Per-slim-key CLI skill bundle copied into the slim Codex / Gemini session
25
+ // (RESEARCH_CLUSTER_COST_FIX_PLAN.md F4 generalizes fetch-window-cost-reduction.md
26
+ // Phase 1.5). The slim materializer writes the slim system-prompt body — the
27
+ // SAME template the Claude SDK gets, via the shared `slim-system-prompt-loader`
28
+ // registry as AGENTS.md / GEMINI.md and copies ONLY these skill dirs; the
29
+ // wide manifest's other slugs are restated by the task-flow / user prompt, so
30
+ // inlining them would re-bloat the session.
31
+ //
32
+ // Keys MUST mirror `SLIM_SYSTEM_PROMPT_LOADERS` (drift-guarded by a unit test):
33
+ // a slim system prompt with no CLI skill set — or vice versa — is a wiring bug.
34
+ // Typed `Partial<Record<ProcessKey, …>>` so a key rename in
35
+ // `@aitne/shared/process-key.ts` lights up here rather than dead-branching.
36
+ // - routine.fetch_window — the `observations` SKILL.md is the
37
+ // `/api/observations/batch` POST contract and the fetcher's only
38
+ // structural assertion; the integration partial the runner inlines covers
39
+ // the rest (`mail` / `notion` / `external-services` / `attach` dropped).
40
+ // - routine.research_cluster_update — `browser-history` (the
41
+ // `/api/browser-history/*` curl surface) + `context` (the research-journal
42
+ // write path); the task-flow inlines the endpoints + journal template.
43
+ export const SLIM_CLI_SKILL_SETS = {
44
+ "routine.fetch_window": ["observations"],
45
+ "routine.research_cluster_update": ["browser-history", "context"],
46
+ };
47
+ /**
48
+ * The slim process key for this session, or null when it is not a slim key.
49
+ * Accepts the `processKey ?? eventType` union the CLI materializer threads
50
+ * (an event-type string that is not a registered ProcessKey simply misses the
51
+ * `in` check). Keyed off `SLIM_CLI_SKILL_SETS`, which the drift guard pins to
52
+ * `SLIM_SYSTEM_PROMPT_LOADERS`.
53
+ */
54
+ function slimCliKeyFor(processKeyOrEvent) {
55
+ if (processKeyOrEvent === undefined)
56
+ return null;
57
+ return processKeyOrEvent in SLIM_CLI_SKILL_SETS
58
+ ? processKeyOrEvent
59
+ : null;
60
+ }
36
61
  /**
37
62
  * Materializes backend-specific instruction files for session workdirs
38
63
  * by reading directly from the source tree (agent-assets/agent-profiles/ and
@@ -238,15 +263,17 @@ export class SkillsCompiler {
238
263
  else {
239
264
  this.materializeCliSession(params.sessionDir, profileName, skills, params.backendId, params.processKey ?? params.eventType, profileBodyOverride, params.wikiWorkspaceName);
240
265
  }
241
- // docs/design/appendices/fetch-window-cost-reduction.md Phase 1.5 the slim
242
- // CLI materializer drops every manifest skill except `observations`.
243
- // The Claude path is wide for fetch_window today (Phase 1 swaps only
244
- // the system prompt, not the workdir layout). Report what was
245
- // actually written so log lines / callers see truth, not the
246
- // manifest's pre-narrow list.
247
- const effectiveSkills = params.backendId !== "claude"
248
- && (params.processKey ?? params.eventType) === FETCH_WINDOW_PROCESS_KEY
249
- ? [FETCH_WINDOW_SLIM_SKILL]
266
+ // fetch-window-cost-reduction.md Phase 1.5 / RESEARCH_CLUSTER_COST_FIX_PLAN.md
267
+ // F4 — the slim CLI materializer drops every manifest skill except the
268
+ // key's `SLIM_CLI_SKILL_SETS` bundle. The Claude path stays wide today
269
+ // (Phase 1/F4 swap only the system prompt + settingSources, not the
270
+ // workdir layout). Report what was actually written so log lines /
271
+ // callers see truth, not the manifest's pre-narrow list.
272
+ const slimCliKey = params.backendId !== "claude"
273
+ ? slimCliKeyFor(params.processKey ?? params.eventType)
274
+ : null;
275
+ const effectiveSkills = slimCliKey
276
+ ? [...(SLIM_CLI_SKILL_SETS[slimCliKey] ?? [])]
250
277
  : skills;
251
278
  return { profile: profileName, skills: effectiveSkills };
252
279
  }
@@ -689,95 +716,104 @@ export class SkillsCompiler {
689
716
  }
690
717
  /**
691
718
  * docs/design/appendices/skills-unification.md Phase 1 item 15 — the slim path does NOT
692
- * emit a `<skill-index>` block or the skill-discovery preamble. The
693
- * fetch_window system prompt is a self-contained operational contract
694
- * (one-window-one-curl, no sub-tasks, exactly-one JSON-on-stdout) and
695
- * the only skill copied (`observations`) is referenced inline by the
696
- * runner-emitted user prompt. Adding the index would mis-signal the
697
- * fetcher to scan for skills before executing the acquisition plan.
719
+ * emit a `<skill-index>` block or the skill-discovery preamble. The slim
720
+ * system prompt is a self-contained operational contract and its copied
721
+ * skills are referenced inline by the dispatched user prompt / task-flow.
722
+ * Adding the index would mis-signal the agent to scan for skills before
723
+ * executing its mechanical task.
698
724
  *
699
- * docs/design/appendices/fetch-window-cost-reduction.md Phase 1.5 slim instruction-file
700
- * materializer for `routine.fetch_window` on Codex / Gemini CLI.
725
+ * fetch-window-cost-reduction.md Phase 1.5 / RESEARCH_CLUSTER_COST_FIX_PLAN.md
726
+ * F4 — slim instruction-file materializer for a `SLIM_CLI_SKILL_SETS` process
727
+ * key on Codex / Gemini CLI.
701
728
  *
702
- * Mirrors the Claude SDK's Phase 1 systemPrompt swap (the same
703
- * `agent-assets/system-prompts/routine-fetch-window.md` template is the
704
- * single source of truth): write the slim body verbatim as AGENTS.md /
705
- * GEMINI.md and copy only the `observations` skill — the
706
- * `/api/observations/batch` POST contract is the fetcher's sole
707
- * structural assertion. The integration partial inlined by the runner
708
- * (`routine-fetch-window-runner.ts:reassemblePrompt`) covers the
709
- * per-attempt call shape, so `mail` / `notion` / `external-services` /
710
- * `attach` skill bodies are deliberately omitted.
729
+ * Mirrors the Claude SDK's slim-systemPrompt swap (the same
730
+ * `agent-assets/system-prompts/<key>.md` template is the single source of
731
+ * truth, read through the shared `loadSlimSystemPrompt` registry): write the
732
+ * slim body verbatim as AGENTS.md / GEMINI.md and copy only the key's
733
+ * `SLIM_CLI_SKILL_SETS` bundle the wide manifest's other slugs are restated
734
+ * by the dispatched user prompt / task-flow, so their bodies are omitted.
711
735
  *
712
- * No safety preamble / character / behavioral-rules / daemon-API
713
- * sections — the slim template restates the only rules the fetcher
714
- * needs (localhost-only curl, no sub-tasks, no context writes, no
715
- * notify, JSON-on-stdout-and-exit). The destructive-action policy layer
716
- * (absolute-block list, Codex sandbox, Gemini admin TOML) still applies
717
- * unchanged at runtime.
736
+ * No safety preamble / character / behavioral-rules / daemon-API sections —
737
+ * the slim template restates the only rules the session needs. The
738
+ * destructive-action policy layer (absolute-block list, Codex sandbox,
739
+ * Gemini admin TOML) still applies unchanged at runtime.
718
740
  *
719
741
  * The `<mcp-servers>` section is appended downstream by
720
742
  * `services/mcp/session-materializer.ts:appendMcpSection` exactly as on
721
- * the wide path — Phase 3's allowlist filter, when it lands, will scope
722
- * that section without further changes here.
743
+ * the wide path.
723
744
  */
724
- materializeFetchWindowCliSession(sessionDir, backendId) {
745
+ materializeSlimCliSession(sessionDir, backendId, processKey) {
746
+ const skillSlugs = SLIM_CLI_SKILL_SETS[processKey];
747
+ // Defensive: the caller only diverts registered slim keys, but guard so a
748
+ // future divert bug fails loud rather than writing an empty session.
749
+ if (skillSlugs === undefined)
750
+ return;
751
+ const slim = loadSlimSystemPrompt(processKey);
752
+ if (slim === null)
753
+ return; // Unreachable for registered keys; satisfies the type.
725
754
  mkdirSync(sessionDir, { recursive: true });
726
- const slim = loadFetchWindowSystemPrompt();
727
755
  writeFileSync(join(sessionDir, cliInstructionFileName(backendId)), slim, "utf-8");
728
- // Copy ONLY the `observations` skill dir. The wide path's prune step
729
- // is replaced by an explicit single-slug list — `pruneStaleBuiltinSkillDirs`
756
+ // Copy ONLY the key's slim skill dirs. The wide path's prune step is
757
+ // replaced by the explicit slug list — `pruneStaleBuiltinSkillDirs`
730
758
  // removes any other built-in skill dir that a prior re-materialization
731
759
  // (e.g. a fallback-driven wide path on the same workdir) may have left.
732
760
  const cliSkillsRoot = cliSkillsDirName(backendId);
733
761
  if (cliSkillsRoot === null)
734
- return; // Claude-only — fetch_window slim runs on Claude SDK natively.
762
+ return; // Claude-only — slim keys run on Claude SDK natively.
735
763
  const skillsRoot = this.getSourceSkillsRoot();
736
764
  const destSkillsRoot = join(sessionDir, cliSkillsRoot, "skills");
737
765
  mkdirSync(destSkillsRoot, { recursive: true });
738
- pruneStaleBuiltinSkillDirs(destSkillsRoot, skillsRoot, [FETCH_WINDOW_SLIM_SKILL]);
739
- const src = resolveBuiltinSkillDir(skillsRoot, FETCH_WINDOW_SLIM_SKILL);
766
+ pruneStaleBuiltinSkillDirs(destSkillsRoot, skillsRoot, [...skillSlugs]);
767
+ for (const slug of skillSlugs) {
768
+ this.copySlimSkillDir(destSkillsRoot, skillsRoot, slug, backendId);
769
+ }
770
+ }
771
+ /**
772
+ * Copy one built-in skill dir into a slim CLI session, applying the same
773
+ * adaptation pipeline the wide CLI path runs (brand tokens, partial /
774
+ * reference includes, integration-mode filter, tool-deny prose, curation
775
+ * anchors). Frontmatter stays intact (skills-unification.md §R6). A
776
+ * no-op when the source SKILL.md is absent.
777
+ */
778
+ copySlimSkillDir(destSkillsRoot, skillsRoot, slug, backendId) {
779
+ const src = resolveBuiltinSkillDir(skillsRoot, slug);
740
780
  const skillMdPath = join(src, "SKILL.md");
741
781
  if (!existsSync(skillMdPath))
742
782
  return;
743
- const destDir = join(destSkillsRoot, FETCH_WINDOW_SLIM_SKILL);
783
+ const destDir = join(destSkillsRoot, slug);
744
784
  cpSync(src, destDir, { recursive: true });
745
785
  substituteBrandTokensInDir(destDir);
746
- // No `substituteWikiWorkspaceTokensInDir` — fetch_window never touches
747
- // wiki workspace state, and the resolver is a no-op for non-wiki
748
- // process keys anyway.
786
+ // No `substituteWikiWorkspaceTokensInDir` — no slim key touches wiki
787
+ // workspace state, and the resolver is a no-op for non-wiki keys anyway.
749
788
  let adapted = substituteBrandTokens(readFileSync(skillMdPath, "utf-8"));
750
- // observations/SKILL.md ships no `{{> base }}` or `{{> ref:* }}`
751
- // directives today, but run the resolvers anyway so a future curation
789
+ // Run the partial / reference resolvers even on bodies that ship no
790
+ // `{{> base }}` / `{{> ref:* }}` directives today, so a future curation
752
791
  // edit cannot silently drop content. Idempotent on plain content.
753
792
  adapted = substituteBrandTokens(renderPartialIncludes(adapted, join(src, "SKILL.base.md")));
754
793
  adapted = substituteBrandTokens(renderReferenceIncludes(adapted, src));
755
- // Mode-conditional filter — observations/SKILL.md carries
756
- // `<!-- mode:<predicate>:notion -->` markers (lines 273-345 today)
757
- // because the source / consume contract differs across `direct` /
758
- // `delegated-same` / `delegated-cross` / `native` / `disabled`. The
759
- // wide path applies this filter at `materializeCliSession`; the
760
- // slim path must match so the agent doesn't get every mode's prose
761
- // for every integration. Idempotent on bodies that lack markers.
794
+ // Mode-conditional filter — skills (e.g. observations/SKILL.md) carry
795
+ // `<!-- mode:<predicate>:<integration> -->` markers because the
796
+ // source / consume contract differs across `direct` / `delegated-same` /
797
+ // `delegated-cross` / `native` / `disabled`. The wide path applies this
798
+ // at `materializeCliSession`; the slim path must match so the agent
799
+ // doesn't get every mode's prose. Idempotent on bodies that lack markers.
762
800
  adapted = applyIntegrationModeFilter(adapted, this.integrations, backendId);
763
- // Tool-deny policy stays in force — even though the fetcher is fed a
764
- // narrow allowlist, the soft-enforcement prose lands in the body
765
- // BEFORE the CLI frontmatter strip so it's not lost.
766
- adapted = applyAllDeniedToolsForSkill(adapted, FETCH_WINDOW_SLIM_SKILL, backendId, this.integrations);
767
- // docs/design/appendices/skills-unification.md Phase 1 §R6 — frontmatter stays intact
768
- // across all backends. `adaptSkillForCli` is gone; the source body's
769
- // YAML preamble flows through verbatim.
801
+ // Tool-deny policy stays in force — even with a narrow allowlist, the
802
+ // soft-enforcement prose lands in the body BEFORE the CLI frontmatter
803
+ // strip so it's not lost.
804
+ adapted = applyAllDeniedToolsForSkill(adapted, slug, backendId, this.integrations);
770
805
  writeFileSync(join(destDir, "SKILL.md"), adapted, "utf-8");
771
- this.spliceCurationAnchorsInSkill(destDir, FETCH_WINDOW_SLIM_SKILL);
806
+ this.spliceCurationAnchorsInSkill(destDir, slug);
772
807
  }
773
808
  materializeCliSession(sessionDir, profileName, skillSlugs, backendId, processKey, profileBodyOverride, wikiWorkspaceName) {
774
- // docs/design/appendices/fetch-window-cost-reduction.md Phase 1.5 divert
775
- // `routine.fetch_window` to a slim materializer that mirrors the
776
- // Claude SDK's Phase 1 systemPrompt swap. Custom bang commands cannot
777
- // reach this branch (they bind to `messaging.custom_command`), so
809
+ // fetch-window-cost-reduction.md Phase 1.5 / RESEARCH_CLUSTER_COST_FIX_PLAN.md
810
+ // F4 divert a slim process key to the slim materializer that mirrors the
811
+ // Claude SDK's slim-systemPrompt swap. Custom bang commands cannot reach
812
+ // this branch (they bind to `messaging.custom_command`), so
778
813
  // `profileBodyOverride` is intentionally NOT forwarded.
779
- if (processKey === FETCH_WINDOW_PROCESS_KEY) {
780
- this.materializeFetchWindowCliSession(sessionDir, backendId);
814
+ const slimKey = slimCliKeyFor(processKey);
815
+ if (slimKey) {
816
+ this.materializeSlimCliSession(sessionDir, backendId, slimKey);
781
817
  return;
782
818
  }
783
819
  // docs/design/appendices/skills-unification.md Phase 1 — directory-based skill delivery.
@@ -106,6 +106,43 @@ export declare function resolveSkillManifestForProcess(processKey: ProcessKey, o
106
106
  db?: Database.Database | null;
107
107
  messageText?: string | null;
108
108
  }): string[];
109
+ /**
110
+ * Event-type / process-key families whose sessions do NOT receive the
111
+ * owner's user-authored skill library (`<contextDir>/policies/skills/`).
112
+ *
113
+ * User skills are general assistant extensions the owner writes for their
114
+ * conversational agent; `syncAllUserSkills` provisions them into a session
115
+ * workdir ON TOP of the process key's built-in manifest. Narrow-persona
116
+ * sessions run under dedicated profiles with tight, purpose-built manifests
117
+ * — dumping the full user-skill library into them is pure cold-start cost +
118
+ * attention dilution and never load-bearing (their work is fully covered by
119
+ * the built-in bundle, and the wiki/research skills are themselves
120
+ * built-ins, not user-authored).
121
+ *
122
+ * Excluded families:
123
+ * - `wiki.*` — the wiki-agent persona (ingest_url / compile /
124
+ * ask / lint / trace / connect).
125
+ * - `routine.research_*` — the browser-history research routines
126
+ * (cluster_update / offer_dm / dispatch /
127
+ * wiki_summary).
128
+ *
129
+ * Everything else (the conversational DM agent, DM-tone scheduled sessions,
130
+ * the daily/weekly/monthly routines, setup, knowledge import, git/github
131
+ * observers, scheduled tasks) keeps the prior "every user skill, every
132
+ * session" behaviour.
133
+ */
134
+ export declare const USER_SKILL_EXCLUDED_PREFIXES: readonly string[];
135
+ /**
136
+ * Whether a session for `eventTypeOrProcessKey` should be provisioned with
137
+ * the owner's user-authored skills. Keyed on the SAME string the skill
138
+ * manifest resolves against (`processKey ?? eventType`) so the gate and the
139
+ * built-in manifest agree on what a session is.
140
+ *
141
+ * Gates the `syncAllUserSkills` call at every materialisation chokepoint
142
+ * (`createSessionWorkdir`, `ensureSessionWorkdir`, and the backend-fallback
143
+ * re-materialiser). See `USER_SKILL_EXCLUDED_PREFIXES`.
144
+ */
145
+ export declare function eventTypeAcceptsUserSkills(eventTypeOrProcessKey: string): boolean;
109
146
  export declare function getProfileForEvent(eventType: string): string;
110
147
  export declare function getSkillsForEvent(eventType: string): string[];
111
148
  export declare function getProfileForProcess(processKey: ProcessKey): string;