@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
@@ -0,0 +1,99 @@
1
+ import { customRoutineSlugFromKey } from "@aitne/shared";
2
+ /**
3
+ * LEGACY custom-routine file format (B-007 §5.8 Q3) — parsing/enumeration
4
+ * helpers only.
5
+ *
6
+ * The subsystem that FIRED these files (`CustomRoutineScheduler`, a per-file
7
+ * node-cron job emitting `routine.custom.<slug>` events) was retired at the
8
+ * Agents-hub redesign (AGENTS_HUB_REDESIGN_PLAN.md §3): user-defined recurring
9
+ * work is a user Agent now (`agents` + `recurring_schedules`, visible on
10
+ * `/agents` with metrics and execution history). What remains here serves two
11
+ * callers:
12
+ *
13
+ * 1. `core/agents/custom-routine-migration.ts` — the one-time boot converter
14
+ * that turns each valid `policies/routines/custom/<slug>.md` into a user
15
+ * Agent definition.
16
+ * 2. `core/context-validation/` — writes under the legacy path are still
17
+ * validated against this format so existing files stay well-formed
18
+ * (they are inert post-migration; the prompt-injection branch in
19
+ * `policy-files.ts` remains for one release).
20
+ */
21
+ export interface CustomRoutineSpec {
22
+ slug: string;
23
+ cron: string;
24
+ enabled: boolean;
25
+ /**
26
+ * Canonical model tier. Normalized from frontmatter — both legacy
27
+ * `light`/`heavy` and current `lite`/`medium`/`high` strings are
28
+ * accepted at parse time (`light → medium`, `heavy → high`).
29
+ */
30
+ backendTier: "lite" | "medium" | "high";
31
+ maxBudgetUsd: number;
32
+ processKey: string;
33
+ }
34
+ export type CustomRoutineParseError = {
35
+ kind: "missing_field";
36
+ field: string;
37
+ } | {
38
+ kind: "invalid_cron";
39
+ value: string;
40
+ } | {
41
+ kind: "invalid_slug";
42
+ value: string;
43
+ } | {
44
+ kind: "invalid_type";
45
+ value: string;
46
+ } | {
47
+ kind: "invalid_process_key";
48
+ value: string;
49
+ } | {
50
+ kind: "invalid_enabled";
51
+ value: string;
52
+ } | {
53
+ kind: "invalid_tier";
54
+ value: string;
55
+ } | {
56
+ kind: "invalid_budget";
57
+ value: string;
58
+ } | {
59
+ kind: "missing_checks_section";
60
+ } | {
61
+ kind: "no_frontmatter";
62
+ };
63
+ export interface CustomRoutineEnumerationResult {
64
+ specs: CustomRoutineSpec[];
65
+ errors: {
66
+ slug: string;
67
+ error: CustomRoutineParseError;
68
+ }[];
69
+ }
70
+ /**
71
+ * Parse a `policies/routines/custom/<slug>.md` file body into a validated spec.
72
+ * Pure function — safe to unit-test exhaustively. Returns a discriminated
73
+ * result so callers can log structured errors without throwing.
74
+ */
75
+ export declare function parseCustomRoutineSpec(slug: string, body: string): {
76
+ ok: true;
77
+ spec: CustomRoutineSpec;
78
+ } | {
79
+ ok: false;
80
+ error: CustomRoutineParseError;
81
+ };
82
+ /**
83
+ * Enumerate every `policies/routines/custom/*.md` file under `contextDir` and
84
+ * parse each into a spec. Errors are returned alongside the successful
85
+ * specs so callers can log them without aborting.
86
+ *
87
+ * The readers are injectable for tests — by default they read from disk; a
88
+ * missing directory yields empty results.
89
+ */
90
+ export declare function enumerateCustomRoutines(contextDir: string, options?: {
91
+ readDir?: (dir: string) => string[];
92
+ readFile?: (path: string) => string;
93
+ }): CustomRoutineEnumerationResult;
94
+ /**
95
+ * Convenience: extract the slug from a `policies/routines/custom/<slug>.md` path.
96
+ * Returns null if the path is outside the custom-routine directory.
97
+ */
98
+ export declare function slugFromCustomRoutinePath(relativePath: string): string | null;
99
+ export { customRoutineSlugFromKey };
@@ -0,0 +1,187 @@
1
+ import { existsSync, readFileSync, readdirSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import cron from "node-cron";
4
+ import { customRoutineKey, customRoutineSlugFromKey } from "@aitne/shared";
5
+ import { CONTEXT_RELATIVE_PATHS } from "./context-paths.js";
6
+ /**
7
+ * Extract the frontmatter body between the opening and closing `---`
8
+ * delimiters. Returns null when the file has no YAML frontmatter.
9
+ */
10
+ function extractFrontmatter(content) {
11
+ if (!content.startsWith("---\n") && !content.startsWith("---\r\n")) {
12
+ return null;
13
+ }
14
+ const afterOpen = content.startsWith("---\r\n") ? 5 : 4;
15
+ const endIdx = content.indexOf("\n---", afterOpen - 1);
16
+ if (endIdx < 0)
17
+ return null;
18
+ return content.slice(afterOpen, endIdx);
19
+ }
20
+ function readScalar(frontmatter, field) {
21
+ const re = new RegExp(`^${field}\\s*:\\s*(.+?)\\s*$`, "m");
22
+ const m = frontmatter.match(re);
23
+ if (!m)
24
+ return null;
25
+ let v = m[1].trim();
26
+ // Strip surrounding quotes (single or double).
27
+ if ((v.startsWith('"') && v.endsWith('"')) ||
28
+ (v.startsWith("'") && v.endsWith("'"))) {
29
+ v = v.slice(1, -1);
30
+ }
31
+ return v;
32
+ }
33
+ function hasChecksSection(content) {
34
+ return /^##\s+Checks\s*$/m.test(content);
35
+ }
36
+ /**
37
+ * Parse a `policies/routines/custom/<slug>.md` file body into a validated spec.
38
+ * Pure function — safe to unit-test exhaustively. Returns a discriminated
39
+ * result so callers can log structured errors without throwing.
40
+ */
41
+ export function parseCustomRoutineSpec(slug, body) {
42
+ if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(slug) || slug.length > 64) {
43
+ return { ok: false, error: { kind: "invalid_slug", value: slug } };
44
+ }
45
+ const fm = extractFrontmatter(body);
46
+ if (fm === null) {
47
+ return { ok: false, error: { kind: "no_frontmatter" } };
48
+ }
49
+ const typeRaw = readScalar(fm, "type");
50
+ if (!typeRaw) {
51
+ return { ok: false, error: { kind: "missing_field", field: "type" } };
52
+ }
53
+ if (typeRaw !== "rule") {
54
+ return { ok: false, error: { kind: "invalid_type", value: typeRaw } };
55
+ }
56
+ const slugRaw = readScalar(fm, "slug");
57
+ if (!slugRaw) {
58
+ return { ok: false, error: { kind: "missing_field", field: "slug" } };
59
+ }
60
+ if (slugRaw !== slug) {
61
+ return { ok: false, error: { kind: "invalid_slug", value: slugRaw } };
62
+ }
63
+ const processKeyRaw = readScalar(fm, "process_key");
64
+ if (!processKeyRaw) {
65
+ return { ok: false, error: { kind: "missing_field", field: "process_key" } };
66
+ }
67
+ if (processKeyRaw !== customRoutineKey(slug)) {
68
+ return { ok: false, error: { kind: "invalid_process_key", value: processKeyRaw } };
69
+ }
70
+ const cronExpr = readScalar(fm, "cron");
71
+ if (!cronExpr) {
72
+ return { ok: false, error: { kind: "missing_field", field: "cron" } };
73
+ }
74
+ if (!cron.validate(cronExpr)) {
75
+ return { ok: false, error: { kind: "invalid_cron", value: cronExpr } };
76
+ }
77
+ const tierRaw = readScalar(fm, "backend_tier");
78
+ if (!tierRaw) {
79
+ return { ok: false, error: { kind: "missing_field", field: "backend_tier" } };
80
+ }
81
+ // Accept the legacy two-tier names ("light" / "heavy") and the canonical
82
+ // three-tier names ("lite" / "medium" / "high"). Legacy "light" maps to
83
+ // Sonnet (medium) and "heavy" to Opus (high), preserving behavior of
84
+ // user-authored routine files written before the rename.
85
+ const tierAliasMap = {
86
+ "lite": "lite",
87
+ "medium": "medium",
88
+ "high": "high",
89
+ "light": "medium",
90
+ "heavy": "high",
91
+ };
92
+ const normalizedTier = tierAliasMap[tierRaw];
93
+ if (!normalizedTier) {
94
+ return { ok: false, error: { kind: "invalid_tier", value: tierRaw } };
95
+ }
96
+ const budgetRaw = readScalar(fm, "max_budget_usd");
97
+ if (!budgetRaw) {
98
+ return { ok: false, error: { kind: "missing_field", field: "max_budget_usd" } };
99
+ }
100
+ const budget = Number(budgetRaw);
101
+ if (!Number.isFinite(budget) || budget <= 0) {
102
+ return { ok: false, error: { kind: "invalid_budget", value: budgetRaw } };
103
+ }
104
+ const enabledRaw = readScalar(fm, "enabled");
105
+ if (!enabledRaw) {
106
+ return { ok: false, error: { kind: "missing_field", field: "enabled" } };
107
+ }
108
+ if (enabledRaw !== "true" && enabledRaw !== "false") {
109
+ return { ok: false, error: { kind: "invalid_enabled", value: enabledRaw } };
110
+ }
111
+ const enabled = enabledRaw === "true";
112
+ if (!hasChecksSection(body)) {
113
+ return { ok: false, error: { kind: "missing_checks_section" } };
114
+ }
115
+ return {
116
+ ok: true,
117
+ spec: {
118
+ slug,
119
+ cron: cronExpr,
120
+ enabled,
121
+ backendTier: normalizedTier,
122
+ maxBudgetUsd: budget,
123
+ processKey: customRoutineKey(slug),
124
+ },
125
+ };
126
+ }
127
+ /**
128
+ * Enumerate every `policies/routines/custom/*.md` file under `contextDir` and
129
+ * parse each into a spec. Errors are returned alongside the successful
130
+ * specs so callers can log them without aborting.
131
+ *
132
+ * The readers are injectable for tests — by default they read from disk; a
133
+ * missing directory yields empty results.
134
+ */
135
+ export function enumerateCustomRoutines(contextDir, options) {
136
+ const dir = join(contextDir, CONTEXT_RELATIVE_PATHS.routines.customDir);
137
+ const readDir = options?.readDir ?? defaultReadDir;
138
+ const readFile = options?.readFile ?? defaultReadFile;
139
+ const files = readDir(dir);
140
+ const specs = [];
141
+ const errors = [];
142
+ for (const fileName of files) {
143
+ if (!fileName.endsWith(".md"))
144
+ continue;
145
+ const slug = fileName.slice(0, -3);
146
+ let body;
147
+ try {
148
+ body = readFile(join(dir, fileName));
149
+ }
150
+ catch {
151
+ continue;
152
+ }
153
+ const result = parseCustomRoutineSpec(slug, body);
154
+ if (result.ok) {
155
+ specs.push(result.spec);
156
+ }
157
+ else {
158
+ errors.push({ slug, error: result.error });
159
+ }
160
+ }
161
+ return { specs, errors };
162
+ }
163
+ function defaultReadDir(dir) {
164
+ if (!existsSync(dir))
165
+ return [];
166
+ return readdirSync(dir);
167
+ }
168
+ function defaultReadFile(path) {
169
+ return readFileSync(path, "utf-8");
170
+ }
171
+ /**
172
+ * Convenience: extract the slug from a `policies/routines/custom/<slug>.md` path.
173
+ * Returns null if the path is outside the custom-routine directory.
174
+ */
175
+ export function slugFromCustomRoutinePath(relativePath) {
176
+ const prefix = `${CONTEXT_RELATIVE_PATHS.routines.customDir}/`;
177
+ if (!relativePath.startsWith(prefix))
178
+ return null;
179
+ const rest = relativePath.slice(prefix.length);
180
+ if (!rest.endsWith(".md"))
181
+ return null;
182
+ const slug = rest.slice(0, -3);
183
+ if (slug.includes("/"))
184
+ return null;
185
+ return slug;
186
+ }
187
+ export { customRoutineSlugFromKey };
@@ -1,5 +1,6 @@
1
1
  import { chmodSync, mkdirSync, writeFileSync } from "node:fs";
2
2
  import { delimiter, dirname, join } from "node:path";
3
+ import { BACKEND_IDS, getManagedApiKeyEnvVars, } from "@aitne/shared";
3
4
  export const SESSION_DAEMON_API_BIN_DIR = join(".pa", "bin");
4
5
  export const SESSION_DAEMON_API_CLI_REL_PATH = join(SESSION_DAEMON_API_BIN_DIR, "pa-api");
5
6
  export const SESSION_DAEMON_CURL_SHIM_REL_PATH = join(SESSION_DAEMON_API_BIN_DIR, "curl");
@@ -708,6 +709,50 @@ export function ensureDaemonApiCli(sessionDir) {
708
709
  /* c8 ignore stop */
709
710
  return cliPath;
710
711
  }
712
+ /**
713
+ * Daemon-internal secrets that must NEVER be inherited by an agent
714
+ * subprocess. Agents run with bypassPermissions and have Bash; `env` /
715
+ * `printenv` / `process.env` cannot be blocked at the tool layer, so a
716
+ * prompt-injected session could exfiltrate anything in its environment.
717
+ * `PA_MASTER_PASSWORD` decrypts the *entire* file-fallback secret store and
718
+ * has no legitimate use in a child process.
719
+ */
720
+ const ALWAYS_STRIP_FROM_CHILD_ENV = ["PA_MASTER_PASSWORD"];
721
+ function isBackendId(value) {
722
+ return BACKEND_IDS.includes(value);
723
+ }
724
+ /**
725
+ * Strip credentials a child agent must not see from a copied env:
726
+ * - daemon-internal secrets (master password) — always.
727
+ * - inactive backends' provider API keys — when the active backend is
728
+ * known. The keychain mirror exports every configured backend's provider
729
+ * key into the daemon's `process.env` globally, so without this a Claude
730
+ * session's env also carries the user's OpenAI / Gemini / OpenCode
731
+ * credentials. Scoping the keys to the backend that actually uses them
732
+ * keeps a prompt-injected child from exfiltrating credentials it never
733
+ * needs. Only applied when `sessionBackend` is a recognised backend id;
734
+ * an absent/unknown value leaves the env untouched (no behaviour change
735
+ * for non-agent spawns).
736
+ */
737
+ function scrubSensitiveChildEnv(env, sessionBackend) {
738
+ for (const name of ALWAYS_STRIP_FROM_CHILD_ENV) {
739
+ delete env[name];
740
+ }
741
+ if (!sessionBackend || !isBackendId(sessionBackend))
742
+ return;
743
+ const activeVars = new Set(getManagedApiKeyEnvVars(sessionBackend));
744
+ for (const backendId of BACKEND_IDS) {
745
+ if (backendId === sessionBackend)
746
+ continue;
747
+ for (const name of getManagedApiKeyEnvVars(backendId)) {
748
+ // Keep any var the active backend also relies on (shared across
749
+ // providers, e.g. a common cloud credential).
750
+ if (activeVars.has(name))
751
+ continue;
752
+ delete env[name];
753
+ }
754
+ }
755
+ }
711
756
  export function buildDaemonApiCliEnv(sessionDir, apiPort, optionsOrReadToken) {
712
757
  const options = typeof optionsOrReadToken === "string"
713
758
  ? { readToken: optionsOrReadToken }
@@ -721,6 +766,10 @@ export function buildDaemonApiCliEnv(sessionDir, apiPort, optionsOrReadToken) {
721
766
  PATH: pathParts.join(delimiter),
722
767
  [DAEMON_API_BASE_URL_ENV]: `http://127.0.0.1:${apiPort}`,
723
768
  };
769
+ // Remove daemon-internal secrets and inactive-backend provider keys before
770
+ // the child ever sees them. Must run after the process.env copy and before
771
+ // the PA_* identity vars below (which the child legitimately needs).
772
+ scrubSensitiveChildEnv(env, options.sessionBackend);
724
773
  if (options.readToken) {
725
774
  env[DAEMON_API_READ_TOKEN_ENV] = options.readToken;
726
775
  }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Day-boundary task runner with a per-agent-day idempotence marker —
3
+ * RESEARCH_CLUSTER_COST_FIX_PLAN.md F2 (defense in depth on top of the
4
+ * F1 per-cluster enqueue stamp).
5
+ *
6
+ * The scheduler invokes its day-boundary callback from THREE sites: the
7
+ * 04:00 cron, wake catch-up (which fires on every detected sleep gap
8
+ * >= 5 min — every macOS maintenance DarkWake), and the morning
9
+ * self-heal missed-fire path. Before this marker existed, each replay
10
+ * re-ran the full callback body; on 2026-06-11 that re-enqueued the
11
+ * same research cluster ~25x in one morning. Wrapping the body HERE —
12
+ * the single composition point — protects every future day-boundary
13
+ * addition, not just the research fan-out.
14
+ *
15
+ * Marker semantics: `runtime_state.day_boundary_last_agent_day` is
16
+ * written AFTER the body completes, not before. A sleep-interrupted or
17
+ * failed body therefore retries on the next scheduler fire (all three
18
+ * scheduler sites catch + log callback rejections), while replay safety
19
+ * of the individual steps comes from the steps themselves — the F1
20
+ * stamp for the fan-out, summarizeDmSessions' own incremental gating.
21
+ */
22
+ import type Database from "better-sqlite3";
23
+ export declare const DAY_BOUNDARY_LAST_AGENT_DAY_KEY = "day_boundary_last_agent_day";
24
+ export interface DayBoundaryTasksDeps {
25
+ db: Database.Database;
26
+ /** Local agent-day label ('YYYY-MM-DD') the caller computes via
27
+ * `getAgentDayDateStr(config.timezone, config.dayBoundaryHour)`. */
28
+ todayAgentDay: string;
29
+ summarizeDmSessions: () => Promise<void>;
30
+ fanoutResearchClusterUpdates: () => Promise<{
31
+ enqueuedSlugs: string[];
32
+ }>;
33
+ }
34
+ export type DayBoundaryTasksResult = {
35
+ ran: false;
36
+ } | {
37
+ ran: true;
38
+ enqueuedSlugs: string[];
39
+ };
40
+ /**
41
+ * Run the day-boundary body at most once per agent-day. Returns
42
+ * `{ ran: false }` when the marker shows the body already completed for
43
+ * `todayAgentDay`. Errors propagate to the caller WITHOUT writing the
44
+ * marker, so the next scheduler fire retries the whole body.
45
+ */
46
+ export declare function runDayBoundaryTasks(deps: DayBoundaryTasksDeps): Promise<DayBoundaryTasksResult>;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Day-boundary task runner with a per-agent-day idempotence marker —
3
+ * RESEARCH_CLUSTER_COST_FIX_PLAN.md F2 (defense in depth on top of the
4
+ * F1 per-cluster enqueue stamp).
5
+ *
6
+ * The scheduler invokes its day-boundary callback from THREE sites: the
7
+ * 04:00 cron, wake catch-up (which fires on every detected sleep gap
8
+ * >= 5 min — every macOS maintenance DarkWake), and the morning
9
+ * self-heal missed-fire path. Before this marker existed, each replay
10
+ * re-ran the full callback body; on 2026-06-11 that re-enqueued the
11
+ * same research cluster ~25x in one morning. Wrapping the body HERE —
12
+ * the single composition point — protects every future day-boundary
13
+ * addition, not just the research fan-out.
14
+ *
15
+ * Marker semantics: `runtime_state.day_boundary_last_agent_day` is
16
+ * written AFTER the body completes, not before. A sleep-interrupted or
17
+ * failed body therefore retries on the next scheduler fire (all three
18
+ * scheduler sites catch + log callback rejections), while replay safety
19
+ * of the individual steps comes from the steps themselves — the F1
20
+ * stamp for the fan-out, summarizeDmSessions' own incremental gating.
21
+ */
22
+ import { readRuntimeState, writeRuntimeState } from "../db/runtime-state.js";
23
+ export const DAY_BOUNDARY_LAST_AGENT_DAY_KEY = "day_boundary_last_agent_day";
24
+ /**
25
+ * Run the day-boundary body at most once per agent-day. Returns
26
+ * `{ ran: false }` when the marker shows the body already completed for
27
+ * `todayAgentDay`. Errors propagate to the caller WITHOUT writing the
28
+ * marker, so the next scheduler fire retries the whole body.
29
+ */
30
+ export async function runDayBoundaryTasks(deps) {
31
+ const { db, todayAgentDay } = deps;
32
+ const lastRunAgentDay = readRuntimeState(db, DAY_BOUNDARY_LAST_AGENT_DAY_KEY);
33
+ if (lastRunAgentDay === todayAgentDay) {
34
+ return { ran: false };
35
+ }
36
+ await deps.summarizeDmSessions();
37
+ const fanout = await deps.fanoutResearchClusterUpdates();
38
+ writeRuntimeState(db, DAY_BOUNDARY_LAST_AGENT_DAY_KEY, todayAgentDay);
39
+ return { ran: true, enqueuedSlugs: fanout.enqueuedSlugs };
40
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * `ActivityScanCoordinator` — owns the dispatcher's
3
+ * `triggerActivityScan` entry point and the cost-reduction-structural §B
4
+ * three-stage gate that fronts it. The coordinator decides whether a
5
+ * given hourly tick:
6
+ * - skips (autonomous gate / morning routine active / already running
7
+ * / below threshold);
8
+ * - silently consumes observations + records an Agent Log line
9
+ * (Stage 0 deterministic gate or Stage 2 lite-tier `log_only`);
10
+ * - escalates to the existing Stage 3 enqueue (Stage 2 `escalate`
11
+ * verdict or `failed` cautious-escalate path).
12
+ *
13
+ * Extracted from `core/dispatcher.ts` as part of phase D-2 of
14
+ * `docs/design/appendices/file-split-plan.md`. Pattern B (stateful
15
+ * coordinator): the coordinator owns the gate logic but borrows live
16
+ * accessors for state the dispatcher continues to own — the
17
+ * `activityScanInProgress` flag (atomic check-and-set inside the
18
+ * trigger), the `morningRoutineInProgress` flag (read-only), and the
19
+ * lazily-injected delegated-sync refresh callback.
20
+ *
21
+ * Dispatcher entry points served:
22
+ * - `EventDispatcher.triggerActivityScan(source, options)` is now a
23
+ * thin one-liner that delegates to `trigger(source, options)`.
24
+ *
25
+ * Invariants preserved bit-for-bit from
26
+ * `docs/design/02-event-pipeline.md` §2:
27
+ * - skip-if-morning-routine-in-progress;
28
+ * - skip-if-hourly-already-running (atomic flag flip BEFORE any
29
+ * await boundary — the C1 race fix from before the split);
30
+ * - skip-if-pending-observations-below-threshold (legacy
31
+ * min-observations floor honoured only when the gate would have
32
+ * proceeded to Stage 3 anyway);
33
+ * - skip-if-setup-incomplete / vault-degraded / user-paused via
34
+ * `isAutonomousAllowed`.
35
+ *
36
+ * Shared-state references held:
37
+ * - `setActivityScanInProgress` / `isActivityScanInProgress` —
38
+ * getter/setter pair around the dispatcher's flag. The flag is
39
+ * left `true` when an enqueue actually happens (the EventBus
40
+ * consumer's `dispatchSafe` finally clears it on routine
41
+ * completion); it is reset inline when the coordinator owns the
42
+ * turn (silent gate paths) or when the trigger is skipping.
43
+ * - `isMorningRoutineActive` — read-only mirror of the dispatcher
44
+ * method so the gate stays single-sourced.
45
+ * - `isAutonomousAllowed` — same; returns the
46
+ * `TriggerActivityScanSkipReason` the gate should surface.
47
+ * - `getDelegatedSyncRefresh` — accessor; null when no delegated
48
+ * integration is wired, in which case the gate proceeds without
49
+ * a refresh, matching pre-injection behaviour.
50
+ */
51
+ import type Database from "better-sqlite3";
52
+ import type { IntegrationKey } from "@aitne/shared";
53
+ import type { AgentConfig } from "../config.js";
54
+ import type { EventBus } from "./event-bus.js";
55
+ import type { TodayWriteLockManager } from "./today-write-lock.js";
56
+ import type { IAgentRouter } from "./backends/backend-router.js";
57
+ import type { IAuditLogger, IContextBuilder, TriggerActivityScanOptions, TriggerActivityScanResult, TriggerActivityScanSkipReason } from "./dispatcher-types.js";
58
+ import type { PromptAssembler } from "./dispatcher-prompt.js";
59
+ import type { RoutineFetchWindowRunner } from "./routine-fetch-window-runner.js";
60
+ export interface ActivityScanCoordinatorDeps {
61
+ db: Database.Database;
62
+ config: AgentConfig;
63
+ eventBus: EventBus;
64
+ contextBuilder: IContextBuilder;
65
+ agentRouter: IAgentRouter;
66
+ audit: IAuditLogger;
67
+ todayWriteLock: TodayWriteLockManager | undefined;
68
+ prompt: PromptAssembler;
69
+ /**
70
+ * docs/design/appendices/routine-data-acquisition.md Phase 4 / D3 — pre-pass runner
71
+ * spawned between Stage 2 (lite-tier triage) and Stage 3 (medium-tier
72
+ * main session) on `escalate` / `failed` verdicts. The rendered
73
+ * `<fetch_report>` block rides on the Stage 3 RoutineEvent's
74
+ * `event.data.fetchReportBlock` so ContextBuilder folds it into the
75
+ * Stage 3 prompt.
76
+ */
77
+ fetchWindowRunner: RoutineFetchWindowRunner;
78
+ /** Accessor for the lazily-injected delegated-sync refresh callback. */
79
+ getDelegatedSyncRefresh: () => (() => Promise<void>) | null;
80
+ /** Setter for the dispatcher's `activityScanInProgress` flag. */
81
+ setActivityScanInProgress: (value: boolean) => void;
82
+ /** Getter for the dispatcher's `activityScanInProgress` flag. */
83
+ isActivityScanInProgress: () => boolean;
84
+ /** Mirrors `EventDispatcher.isMorningRoutineActive`. */
85
+ isMorningRoutineActive: () => boolean;
86
+ /**
87
+ * Mirrors `EventDispatcher.isAutonomousAllowed`. Returns the
88
+ * skip-reason when the gate must abort early (setup incomplete,
89
+ * vault degraded, user paused) or `null` when autonomous work is
90
+ * permitted to proceed.
91
+ */
92
+ isAutonomousAllowed: () => TriggerActivityScanSkipReason | null;
93
+ /**
94
+ * Accessor for the scheduler's `queueMorningRoutineWake`. Returns the
95
+ * bound function once wiring has completed, or `null` early in startup
96
+ * before the scheduler is constructed. The pre-routine gate calls this
97
+ * when it detects the current agent-day's morning_routine has not run
98
+ * yet (typical cause: Mac slept through the 04:00 cron tick). The
99
+ * wake row carries the dedup guarantee — multiple back-to-back hourly
100
+ * ticks all see the same in-flight row instead of stacking up.
101
+ */
102
+ getQueueMorningRoutineWake: () => QueueMorningRoutineWake | null;
103
+ }
104
+ /** Signature of `AgentScheduler.queueMorningRoutineWake`, narrowed to the
105
+ * surface the dispatcher gate consumes. Kept structural so we don't pull
106
+ * the scheduler class into this module just for a type reference. */
107
+ export type QueueMorningRoutineWake = (source: string, options?: {
108
+ postCatchupRoutines?: string[];
109
+ postCatchupActivityScan?: boolean;
110
+ }) => {
111
+ inserted: boolean;
112
+ existingId?: number;
113
+ };
114
+ /**
115
+ * HOURLY_CHECK_GATE_REDESIGN_PLAN.md §3.3 + §7.2 — outcome of the
116
+ * Layer-1 pre-pass harvest that runs at the top of `trigger()` for
117
+ * delegated/native integrations. Surfaced both in the audit row (per-
118
+ * tick observability) and on the Stage 3 event (the rendered
119
+ * `<fetch_report>` block).
120
+ */
121
+ export interface HarvestResult {
122
+ /** True when at least one integration was eligible and the runner ran. */
123
+ ran: boolean;
124
+ /** Integrations whose sub-session completed successfully or partially. */
125
+ integrations: IntegrationKey[];
126
+ /** Eligible integrations suppressed by the freshness window. */
127
+ skippedIntegrations: IntegrationKey[];
128
+ /** Eligible integrations whose sub-session ended in `failed`. */
129
+ failedIntegrations: IntegrationKey[];
130
+ /** Wall-clock time spent on the harvest (ms). */
131
+ durationMs: number;
132
+ /**
133
+ * True when any eligible integration failed. Triggers §3.5 cautious-
134
+ * escalate: gate decision is forced to `stage3` regardless of the
135
+ * signal verdict so a fetch outage doesn't manifest as silent
136
+ * stage0.
137
+ */
138
+ failed: boolean;
139
+ /**
140
+ * Rendered `<fetch_report>` block from the runner, ready to plumb
141
+ * onto `stage3Event.data.fetchReportBlock`. `null` when the runner
142
+ * was not spawned (no eligible integrations) — in that case the
143
+ * Stage 3 prompt simply omits the block.
144
+ */
145
+ fetchReportBlock: string | null;
146
+ }
147
+ export declare class ActivityScanCoordinator {
148
+ private readonly db;
149
+ private readonly config;
150
+ private readonly eventBus;
151
+ private readonly contextBuilder;
152
+ private readonly agentRouter;
153
+ private readonly audit;
154
+ private readonly todayWriteLock;
155
+ private readonly prompt;
156
+ private readonly fetchWindowRunner;
157
+ private readonly getDelegatedSyncRefresh;
158
+ private readonly setActivityScanInProgress;
159
+ private readonly isActivityScanInProgress;
160
+ private readonly isMorningRoutineActive;
161
+ private readonly isAutonomousAllowed;
162
+ private readonly getQueueMorningRoutineWake;
163
+ constructor(deps: ActivityScanCoordinatorDeps);
164
+ trigger(source: string, options?: TriggerActivityScanOptions): Promise<TriggerActivityScanResult>;
165
+ /**
166
+ * HOURLY_CHECK_GATE_REDESIGN_PLAN.md §3.3 Layer 1 — pre-pass harvest
167
+ * for active non-direct integrations. Reads the per-integration
168
+ * `pre_pass_last_run:<key>` freshness key; integrations whose last
169
+ * successful run is within `activityScanPrePassFreshnessMinutes` are
170
+ * skipped this tick. Forced runs (`/api/agent/run-now`) bypass the
171
+ * freshness gate.
172
+ *
173
+ * Returns a `HarvestResult` so the caller can:
174
+ * - emit telemetry (which integrations fetched, which skipped on
175
+ * freshness, which failed),
176
+ * - cautious-escalate when any non-direct integration failed
177
+ * (§3.5 — prevents silent stage0 from masking a fetch outage),
178
+ * - plumb the rendered `<fetch_report>` block onto the Stage 3
179
+ * event so ContextBuilder folds it into the prompt.
180
+ */
181
+ private harvestForGate;
182
+ /**
183
+ * cost-reduction-structural §B — pull a fresh signal snapshot and run
184
+ * the deterministic gate. Helper so the dispatcher's call site stays
185
+ * compact and tests can spy on the boundary.
186
+ */
187
+ private computeActivityScanGateDecision;
188
+ private readTodayMdSafe;
189
+ /**
190
+ * cost-reduction-structural §B — daemon-direct silent path. Used by
191
+ * Stage 0 and Stage 2 log-only verdicts. Consumes pending user
192
+ * observations + appends a single Agent Log line + records the gate
193
+ * verdict to `agent_actions`. The flag is reset before return.
194
+ */
195
+ private runSilentActivityScanPath;
196
+ private enqueueStage3ActivityScan;
197
+ private logGateAuditRow;
198
+ /**
199
+ * cost-reduction-structural §B Stage 2 — synchronous lite-tier triage.
200
+ * Builds a `routine.activity_scan.triage` RoutineEvent and runs it
201
+ * inline through the agent router (NOT the EventBus, so the result
202
+ * is available before we decide whether to silence or escalate).
203
+ *
204
+ * The agent contract is JSON-only output (`{ "action": "log_only" |
205
+ * "escalate", "reason": "..." }`); on parse failure we return
206
+ * `'failed'` and the caller treats that as cautious escalate.
207
+ *
208
+ * Tool/turn clamp (defense-in-depth):
209
+ * - `allowedToolsOverride: []` removes every tool from the SDK's
210
+ * allowlist for the spawn. Stage 2 has nothing to do but emit a
211
+ * JSON line; the design's "no write tools" rule is enforced here
212
+ * instead of relying on the prompt alone.
213
+ * - `maxTurns: 1` caps the spawn at a single assistant turn. Even
214
+ * if a future prompt change accidentally invites tool use, the
215
+ * spawn cannot loop. Codex/Gemini have no per-spawn `allowedTools`
216
+ * surface today (acknowledged gap in `agent-core.ts`); the
217
+ * `maxTurns` cap and process_backend_config envelope are the
218
+ * remaining safety floor on those backends.
219
+ */
220
+ private runStage2Triage;
221
+ }