@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,86 @@
1
+ /**
2
+ * Stage-1 deterministic gate for the three-stage activity_scan funnel
3
+ * (cost-reduction-structural §B). Pure function over a `ActivityScanSignals`
4
+ * snapshot — no DB handle, no clock, no I/O. The dispatcher computes the
5
+ * snapshot via `computeActivityScanSignals`, then asks this module which
6
+ * stage to enter.
7
+ *
8
+ * The four possible decisions:
9
+ * - `stage0_silent`: consume observations, append a single Agent Log
10
+ * line via the daemon-direct writer, return. No LLM call.
11
+ * - `stage2`: lite-tier triage (only when `stage2Enabled`). Strict
12
+ * JSON-only output decides log_only vs escalate.
13
+ * - `stage3`: existing full activity_scan session.
14
+ *
15
+ * HOURLY_CHECK_GATE_REDESIGN_PLAN.md Phase 4 collapsed the gateMode
16
+ * enum (`off`/`shadow`/`live`) into a single execution path. The gate's
17
+ * verdict is always honoured.
18
+ */
19
+ import type { ActivityScanSignals } from "../db/activity-scan-signals.js";
20
+ export type ActivityScanGateStage = "stage0_silent" | "stage2" | "stage3";
21
+ export interface ActivityScanGateConfig {
22
+ /**
23
+ * Hours since the last Stage 3 run after which the gate force-runs at
24
+ * least Stage 2 (or Stage 3 if novelty is high). Bounds the worst case
25
+ * where Stage 0/1 keeps short-circuiting on a quiet day.
26
+ */
27
+ heartbeatHours: number;
28
+ /**
29
+ * When `false`, low-signal cases bypass Stage 2 entirely and route to
30
+ * Stage 3. The cautious default — Stage 2 only takes effect after
31
+ * shadow telemetry validates the decision boundary.
32
+ */
33
+ stage2Enabled: boolean;
34
+ /**
35
+ * Below this threshold, `pendingObsCount` alone does not imply
36
+ * Stage 3. Default 0 — any pending observation hits the low-signal
37
+ * branch which routes to Stage 2 or Stage 3 depending on the flag.
38
+ */
39
+ pendingObsLowSignalCeiling?: number;
40
+ }
41
+ export interface ActivityScanGateDecision {
42
+ stage: ActivityScanGateStage;
43
+ /** Short, telemetry-friendly explanation. */
44
+ reason: string;
45
+ /** Snapshot at decision time (echoed for the audit row). */
46
+ signals: ActivityScanSignals;
47
+ }
48
+ export declare function decideStage(signals: ActivityScanSignals, config: ActivityScanGateConfig): ActivityScanGateDecision;
49
+ /**
50
+ * The dispatcher logs every cron tick to `agent_actions` regardless of
51
+ * which stage runs. Helper that builds the JSON payload from a decision
52
+ * so the schema stays consistent across stages and call-sites.
53
+ */
54
+ export declare function buildGateAuditDetail(decision: ActivityScanGateDecision, extra: {
55
+ appliedDecision: ActivityScanGateStage;
56
+ /** Stage-2 LLM verdict, when Stage 2 ran. */
57
+ stage2Verdict?: "log_only" | "escalate" | "failed";
58
+ /** Forced-run flag from `/api/agent/run-now` etc. */
59
+ forced?: boolean;
60
+ /**
61
+ * HOURLY_CHECK_GATE_REDESIGN_PLAN.md §3.5 — true when pre-pass for
62
+ * any non-direct integration failed in `harvestForGate` and the
63
+ * gate force-escalated to `stage3` regardless of the signal
64
+ * verdict. Surfaced in the audit row so dashboards can flag the
65
+ * cautious-escalate path.
66
+ */
67
+ cautiousEscalate?: boolean;
68
+ /**
69
+ * Original gate verdict captured BEFORE cautious-escalate
70
+ * overwrote it. Persisted as `pre_escalate_gate_*` so dashboards
71
+ * can answer "what would the gate have said if pre-pass had
72
+ * succeeded?" — distinguishes a tick that was structurally
73
+ * stage3 anyway from one that was forced up from stage0_silent
74
+ * by a transient fetch outage.
75
+ */
76
+ preEscalateGateStage?: ActivityScanGateStage;
77
+ preEscalateGateReason?: string;
78
+ }): Record<string, unknown>;
79
+ /**
80
+ * Build the `<gate_decision>` block injected into Stage 3's prompt so
81
+ * the routine knows *why* it was escalated and can prioritize.
82
+ */
83
+ export declare function renderGateDecisionBlock(decision: ActivityScanGateDecision, extra: {
84
+ forced?: boolean;
85
+ cautiousEscalate?: boolean;
86
+ }): string;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Stage-1 deterministic gate for the three-stage activity_scan funnel
3
+ * (cost-reduction-structural §B). Pure function over a `ActivityScanSignals`
4
+ * snapshot — no DB handle, no clock, no I/O. The dispatcher computes the
5
+ * snapshot via `computeActivityScanSignals`, then asks this module which
6
+ * stage to enter.
7
+ *
8
+ * The four possible decisions:
9
+ * - `stage0_silent`: consume observations, append a single Agent Log
10
+ * line via the daemon-direct writer, return. No LLM call.
11
+ * - `stage2`: lite-tier triage (only when `stage2Enabled`). Strict
12
+ * JSON-only output decides log_only vs escalate.
13
+ * - `stage3`: existing full activity_scan session.
14
+ *
15
+ * HOURLY_CHECK_GATE_REDESIGN_PLAN.md Phase 4 collapsed the gateMode
16
+ * enum (`off`/`shadow`/`live`) into a single execution path. The gate's
17
+ * verdict is always honoured.
18
+ */
19
+ const HIGH_NOVELTY_FLOOR = 3;
20
+ const ESCALATE_NOVELTY_FLOOR = 2;
21
+ export function decideStage(signals, config) {
22
+ // Heartbeat: even on quiet days, exercise Stage 2/3 every N hours so
23
+ // the gate's signal compute is exercised end-to-end. We pick Stage 3
24
+ // when there is *some* novelty pending, Stage 2 otherwise — Stage 2
25
+ // is the cheaper lite-tier shape and Stage 3 wastes context if there
26
+ // is nothing to act on. If Stage 2 is disabled, the cautious fallback
27
+ // is Stage 3.
28
+ if (signals.hoursSinceLastStage3Run >= config.heartbeatHours) {
29
+ if (numericNovelty(signals.maxNoveltyScore) >= ESCALATE_NOVELTY_FLOOR) {
30
+ return decision("stage3", "heartbeat_due_with_novelty", signals);
31
+ }
32
+ return decision(config.stage2Enabled ? "stage2" : "stage3", "heartbeat_due", signals);
33
+ }
34
+ // Hard escalate: any high-priority signal goes straight to Stage 3.
35
+ if (numericNovelty(signals.maxNoveltyScore) >= HIGH_NOVELTY_FLOOR) {
36
+ return decision("stage3", "high_novelty", signals);
37
+ }
38
+ if (signals.calendarHasConflict) {
39
+ return decision("stage3", "calendar_conflict", signals);
40
+ }
41
+ if (signals.vipMailUnreadCount > 0) {
42
+ return decision("stage3", "vip_mail_unread", signals);
43
+ }
44
+ if (signals.agentPlanOverdueCount > 0) {
45
+ return decision("stage3", "agent_plan_overdue", signals);
46
+ }
47
+ if (signals.scheduleApproachingCount > 0) {
48
+ return decision("stage3", "schedule_approaching", signals);
49
+ }
50
+ // No signals at all → Stage 0 silent.
51
+ if (signals.pendingObsCount === 0 && !signals.calendarHas24hChange) {
52
+ return decision("stage0_silent", "no_signals", signals);
53
+ }
54
+ // Low signals only → Stage 2 if enabled, else Stage 3 (cautious default).
55
+ // The pendingObsLowSignalCeiling lets an operator widen the silent-skip
56
+ // band ("at most N noise observations is still nothing to act on"); the
57
+ // default 0 leaves the design's conservative posture intact.
58
+ const ceiling = Math.max(0, config.pendingObsLowSignalCeiling ?? 0);
59
+ if (signals.pendingObsCount <= ceiling
60
+ && !signals.calendarHas24hChange) {
61
+ return decision("stage0_silent", "low_signal_under_ceiling", signals);
62
+ }
63
+ return decision(config.stage2Enabled ? "stage2" : "stage3", "low_signal_default", signals);
64
+ }
65
+ /**
66
+ * The dispatcher logs every cron tick to `agent_actions` regardless of
67
+ * which stage runs. Helper that builds the JSON payload from a decision
68
+ * so the schema stays consistent across stages and call-sites.
69
+ */
70
+ export function buildGateAuditDetail(decision, extra) {
71
+ return {
72
+ stage_reached: extra.appliedDecision,
73
+ gate_stage: decision.stage,
74
+ gate_reason: decision.reason,
75
+ forced: extra.forced ?? false,
76
+ ...(extra.stage2Verdict ? { stage2_verdict: extra.stage2Verdict } : {}),
77
+ ...(extra.cautiousEscalate ? { cautious_escalate: true } : {}),
78
+ ...(extra.preEscalateGateStage
79
+ ? { pre_escalate_gate_stage: extra.preEscalateGateStage }
80
+ : {}),
81
+ ...(extra.preEscalateGateReason
82
+ ? { pre_escalate_gate_reason: extra.preEscalateGateReason }
83
+ : {}),
84
+ signal_snapshot: {
85
+ pendingObsCount: decision.signals.pendingObsCount,
86
+ maxNoveltyScore: decision.signals.maxNoveltyScore,
87
+ noveltyDistribution: decision.signals.noveltyDistribution,
88
+ vipMailUnreadCount: decision.signals.vipMailUnreadCount,
89
+ calendarHas24hChange: decision.signals.calendarHas24hChange,
90
+ calendarHasConflict: decision.signals.calendarHasConflict,
91
+ agentPlanOverdueCount: decision.signals.agentPlanOverdueCount,
92
+ scheduleApproachingCount: decision.signals.scheduleApproachingCount,
93
+ hoursSinceLastStage3Run: serializeHours(decision.signals.hoursSinceLastStage3Run),
94
+ },
95
+ };
96
+ }
97
+ /**
98
+ * Build the `<gate_decision>` block injected into Stage 3's prompt so
99
+ * the routine knows *why* it was escalated and can prioritize.
100
+ */
101
+ export function renderGateDecisionBlock(decision, extra) {
102
+ return [
103
+ "<gate_decision>",
104
+ ` triggered_by: ${decision.stage === "stage3" ? "stage1" : "stage2_escalation"}`,
105
+ ` reason: ${decision.reason}`,
106
+ ` forced: ${extra.forced ? "true" : "false"}`,
107
+ ...(extra.cautiousEscalate ? [" cautious_escalate: true"] : []),
108
+ ` signals_snapshot: ${JSON.stringify({
109
+ maxNovelty: decision.signals.maxNoveltyScore,
110
+ pendingObs: decision.signals.pendingObsCount,
111
+ vipMail: decision.signals.vipMailUnreadCount,
112
+ calConflict: decision.signals.calendarHasConflict,
113
+ agentPlanOverdue: decision.signals.agentPlanOverdueCount,
114
+ scheduleApproaching: decision.signals.scheduleApproachingCount,
115
+ })}`,
116
+ "</gate_decision>",
117
+ ].join("\n");
118
+ }
119
+ function numericNovelty(score) {
120
+ // Cautious default — null (no summary done yet) is treated as 2 so a
121
+ // backlog of unsummarized observations does NOT silently skip the
122
+ // routine. The design doc calls this out explicitly under "edge cases".
123
+ return score ?? ESCALATE_NOVELTY_FLOOR;
124
+ }
125
+ function decision(stage, reason, signals) {
126
+ return { stage, reason, signals };
127
+ }
128
+ function serializeHours(value) {
129
+ if (!Number.isFinite(value))
130
+ return null;
131
+ return Number(value.toFixed(2));
132
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Background-task budget envelope — BACKGROUND_TASK_RUNNER_DESIGN.md §6 / §10.1.
3
+ *
4
+ * Pure resolution of the `(modelId, maxTurns, maxBudgetUsd,
5
+ * executeTimeoutMinutes)` envelope for a background-task worker from
6
+ * three inputs:
7
+ *
8
+ * 1. The per-task `tier` (`lite` | `medium` | `high`) — selects the
9
+ * base turn/budget/timeout envelope.
10
+ * 2. The optional per-task `maxBudgetUsd` override (POST body).
11
+ * 3. The operator-editable `process_backend_config` row for
12
+ * `process_key='background_task'` (model + caps) — same chokepoint
13
+ * as browser-task's `loadBrowserTaskBackendBinding`.
14
+ *
15
+ * The worker is Claude-only (it drives the Claude Agent SDK `query()`
16
+ * loop directly, like browser_task). A `process_backend_config` row that
17
+ * pins a non-Claude backend is refused with `backend_misconfigured` so a
18
+ * mis-set `/settings/models` row fails fast rather than silently doing
19
+ * nothing.
20
+ *
21
+ * 100% coverage gate — the I/O (DB read) lives in
22
+ * `loadBackgroundTaskBinding`; this module is the pure arithmetic.
23
+ */
24
+ import type { BackgroundTaskTier } from "../../db/background-task-store.js";
25
+ /** Fallback model when the `process_backend_config` row is missing or
26
+ * carries an empty `main_model`. Mirrors the seed default. */
27
+ export declare const BACKGROUND_TASK_FALLBACK_CLAUDE_MODEL = "claude-sonnet-4-6";
28
+ export declare const BACKGROUND_TASK_DEFAULT_TIER: BackgroundTaskTier;
29
+ /** Hard upper bounds. The seed sits well below; the caps give the
30
+ * operator room to relax via `/settings/models` while pinning a ceiling
31
+ * no per-task override can blow past. Background tasks are the
32
+ * long-running surface, so the turn / timeout ceilings are far higher
33
+ * than browser-task's (60 turns / 5 min) — a deep research or
34
+ * multi-repo audit legitimately runs for many turns over many minutes. */
35
+ export declare const BACKGROUND_TASK_MAX_TURNS_CAP = 120;
36
+ export declare const BACKGROUND_TASK_MAX_BUDGET_USD_CAP = 15;
37
+ export declare const BACKGROUND_TASK_MAX_EXECUTE_TIMEOUT_MINUTES = 120;
38
+ export interface BackgroundTaskEnvelope {
39
+ modelId: string;
40
+ maxTurns: number;
41
+ maxBudgetUsd: number;
42
+ executeTimeoutMinutes: number;
43
+ }
44
+ interface TierEnvelope {
45
+ maxTurns: number;
46
+ maxBudgetUsd: number;
47
+ executeTimeoutMinutes: number;
48
+ }
49
+ export declare function tierEnvelope(tier: BackgroundTaskTier): TierEnvelope;
50
+ /** Shape of the operator-editable `process_backend_config` row, already
51
+ * read from the DB (or null when absent). */
52
+ export interface BackgroundTaskProcessConfig {
53
+ mainBackend: string;
54
+ mainModel: string | null;
55
+ maxTurns: number | null;
56
+ maxBudgetUsd: number | null;
57
+ }
58
+ export interface ResolveEnvelopeInput {
59
+ tier: BackgroundTaskTier | null;
60
+ /** Per-task budget override (POST body). Clamped to the hard cap. */
61
+ maxBudgetUsd: number | null;
62
+ processConfig: BackgroundTaskProcessConfig | null;
63
+ }
64
+ export type ResolveEnvelopeResult = {
65
+ ok: true;
66
+ envelope: BackgroundTaskEnvelope;
67
+ } | {
68
+ ok: false;
69
+ reason: "backend_misconfigured";
70
+ detail: string;
71
+ };
72
+ /**
73
+ * Pure envelope resolution. Branches:
74
+ * - processConfig present + `mainBackend !== 'claude'` → REFUSE.
75
+ * - otherwise: model from the config (or fallback); turns from the
76
+ * config (or tier base), clamped; budget = per-task override ?? config
77
+ * budget ?? tier base, clamped; timeout from the tier base, clamped.
78
+ */
79
+ export declare function resolveBackgroundTaskEnvelope(input: ResolveEnvelopeInput): ResolveEnvelopeResult;
80
+ export {};
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Background-task budget envelope — BACKGROUND_TASK_RUNNER_DESIGN.md §6 / §10.1.
3
+ *
4
+ * Pure resolution of the `(modelId, maxTurns, maxBudgetUsd,
5
+ * executeTimeoutMinutes)` envelope for a background-task worker from
6
+ * three inputs:
7
+ *
8
+ * 1. The per-task `tier` (`lite` | `medium` | `high`) — selects the
9
+ * base turn/budget/timeout envelope.
10
+ * 2. The optional per-task `maxBudgetUsd` override (POST body).
11
+ * 3. The operator-editable `process_backend_config` row for
12
+ * `process_key='background_task'` (model + caps) — same chokepoint
13
+ * as browser-task's `loadBrowserTaskBackendBinding`.
14
+ *
15
+ * The worker is Claude-only (it drives the Claude Agent SDK `query()`
16
+ * loop directly, like browser_task). A `process_backend_config` row that
17
+ * pins a non-Claude backend is refused with `backend_misconfigured` so a
18
+ * mis-set `/settings/models` row fails fast rather than silently doing
19
+ * nothing.
20
+ *
21
+ * 100% coverage gate — the I/O (DB read) lives in
22
+ * `loadBackgroundTaskBinding`; this module is the pure arithmetic.
23
+ */
24
+ /** Fallback model when the `process_backend_config` row is missing or
25
+ * carries an empty `main_model`. Mirrors the seed default. */
26
+ export const BACKGROUND_TASK_FALLBACK_CLAUDE_MODEL = "claude-sonnet-4-6";
27
+ export const BACKGROUND_TASK_DEFAULT_TIER = "medium";
28
+ /** Hard upper bounds. The seed sits well below; the caps give the
29
+ * operator room to relax via `/settings/models` while pinning a ceiling
30
+ * no per-task override can blow past. Background tasks are the
31
+ * long-running surface, so the turn / timeout ceilings are far higher
32
+ * than browser-task's (60 turns / 5 min) — a deep research or
33
+ * multi-repo audit legitimately runs for many turns over many minutes. */
34
+ export const BACKGROUND_TASK_MAX_TURNS_CAP = 120;
35
+ export const BACKGROUND_TASK_MAX_BUDGET_USD_CAP = 15.0;
36
+ export const BACKGROUND_TASK_MAX_EXECUTE_TIMEOUT_MINUTES = 120;
37
+ /** Per-tier base envelope. `process_backend_config` overrides
38
+ * model/turns/budget; the tier is the source of the execute-timeout
39
+ * (which has no `process_backend_config` column) and the fallback when
40
+ * the config row is absent. */
41
+ const TIER_ENVELOPES = {
42
+ lite: { maxTurns: 15, maxBudgetUsd: 0.5, executeTimeoutMinutes: 10 },
43
+ medium: { maxTurns: 40, maxBudgetUsd: 2.0, executeTimeoutMinutes: 30 },
44
+ high: { maxTurns: 80, maxBudgetUsd: 8.0, executeTimeoutMinutes: 60 },
45
+ };
46
+ export function tierEnvelope(tier) {
47
+ return TIER_ENVELOPES[tier];
48
+ }
49
+ function clampPositive(value, cap, fallback) {
50
+ if (!Number.isFinite(value) || value <= 0)
51
+ return fallback;
52
+ return Math.min(value, cap);
53
+ }
54
+ /**
55
+ * Pure envelope resolution. Branches:
56
+ * - processConfig present + `mainBackend !== 'claude'` → REFUSE.
57
+ * - otherwise: model from the config (or fallback); turns from the
58
+ * config (or tier base), clamped; budget = per-task override ?? config
59
+ * budget ?? tier base, clamped; timeout from the tier base, clamped.
60
+ */
61
+ export function resolveBackgroundTaskEnvelope(input) {
62
+ const tier = input.tier ?? BACKGROUND_TASK_DEFAULT_TIER;
63
+ const base = TIER_ENVELOPES[tier];
64
+ const cfg = input.processConfig;
65
+ if (cfg && cfg.mainBackend !== "claude") {
66
+ return {
67
+ ok: false,
68
+ reason: "backend_misconfigured",
69
+ detail: `process_backend_config.main_backend='${cfg.mainBackend}' — background_task drives the Claude Agent SDK directly; refusing to dispatch. Set background_task back to claude in /settings/models.`,
70
+ };
71
+ }
72
+ const modelId = cfg && typeof cfg.mainModel === "string" && cfg.mainModel.length > 0
73
+ ? cfg.mainModel
74
+ : BACKGROUND_TASK_FALLBACK_CLAUDE_MODEL;
75
+ const rawTurns = cfg && typeof cfg.maxTurns === "number" && Number.isFinite(cfg.maxTurns)
76
+ ? Math.max(1, Math.floor(cfg.maxTurns))
77
+ : base.maxTurns;
78
+ const maxTurns = Math.min(rawTurns, BACKGROUND_TASK_MAX_TURNS_CAP);
79
+ // Per-task override wins, then the operator config, then the tier base.
80
+ const budgetSource = input.maxBudgetUsd != null
81
+ ? input.maxBudgetUsd
82
+ : cfg && cfg.maxBudgetUsd != null
83
+ ? cfg.maxBudgetUsd
84
+ : base.maxBudgetUsd;
85
+ const maxBudgetUsd = clampPositive(budgetSource, BACKGROUND_TASK_MAX_BUDGET_USD_CAP, base.maxBudgetUsd);
86
+ const executeTimeoutMinutes = Math.min(base.executeTimeoutMinutes, BACKGROUND_TASK_MAX_EXECUTE_TIMEOUT_MINUTES);
87
+ return {
88
+ ok: true,
89
+ envelope: { modelId, maxTurns, maxBudgetUsd, executeTimeoutMinutes },
90
+ };
91
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Background-task driver — generic Claude Agent SDK glue for the
3
+ * per-task worker loop. BACKGROUND_TASK_RUNNER_DESIGN.md §4.1.
4
+ *
5
+ * The browser-task driver's analogue, with the entire Playwright /
6
+ * managed-Chromium / allowlist / final-confirm plane removed. A worker
7
+ * is a plain `query()` session seeded with a SELF-CONTAINED brief and a
8
+ * three-tool MCP envelope (`read_memory`, `ask_user`, `finish`) plus the
9
+ * SDK's `WebSearch` / `WebFetch` for research-type work. It opts out of
10
+ * the `<user>` / `<management_rules>` injection (`settingSources:
11
+ * ["project"]`, as browser-task does) — the brief carries the context,
12
+ * the output-language directive, persona hints for the `draft`, and the
13
+ * notification policy / criteria.
14
+ *
15
+ * Responsibilities:
16
+ * 1. Resolve the `(model, maxTurns, maxBudgetUsd, executeTimeout)`
17
+ * envelope from `process_backend_config` + the row's tier/budget
18
+ * (`background-task-budget.ts`), pinned for the task's lifetime.
19
+ * 2. Render a per-task workdir (empty dir + CLAUDE.md agent profile).
20
+ * 3. Drive `query()` until terminal, honouring the AbortController
21
+ * (cancel + timeout) — no Playwright resources to release.
22
+ * 4. Hand back a `DriverRunResult` the runner maps to terminal state /
23
+ * park. On the death paths the artifact is NULL — the runner
24
+ * synthesizes the fail-loud artifact (§4.3).
25
+ *
26
+ * Excluded from the 100% coverage gate — SDK stream consumer. The pure
27
+ * sub-pieces (budget envelope) live in the covered set.
28
+ */
29
+ import type Database from "better-sqlite3";
30
+ import { type BackgroundTaskRow } from "../../db/background-task-store.js";
31
+ import { type BackgroundTaskEnvelope, type BackgroundTaskProcessConfig } from "./background-task-budget.js";
32
+ import { type BackgroundTaskRuntime } from "./background-task-tools.js";
33
+ import { type BackgroundTaskTransitionEmitter } from "./background-task-transition-events.js";
34
+ /** Read the operator-editable envelope row for `background_task`. */
35
+ export declare function loadBackgroundTaskProcessConfig(db: Database.Database): BackgroundTaskProcessConfig | null;
36
+ export interface DriverDeps {
37
+ db: Database.Database;
38
+ paDataDir: string;
39
+ /** Workspace dir root — resolves the agent-profile MD. */
40
+ workspaceDir: string;
41
+ transitionEmitter?: BackgroundTaskTransitionEmitter;
42
+ /** Vault root for the worker's `read_memory` tool. */
43
+ contextDir: string;
44
+ /** Clarification TTL in ms (`backgroundTaskClarificationTtlMinutes`). */
45
+ clarificationTtlMs: number;
46
+ /** Override for tests; production wires `() => Date.now()`. */
47
+ nowFn?: () => number;
48
+ }
49
+ export interface DriverHandle {
50
+ abortController: AbortController;
51
+ cwd: string;
52
+ runtime: BackgroundTaskRuntime;
53
+ sdkSessionId: string | null;
54
+ binding: BackgroundTaskEnvelope;
55
+ }
56
+ export interface DriverRunResult {
57
+ outcome: "completed" | "yielded_for_clarification" | "no_finish" | "max_turns_exceeded" | "budget_exceeded" | "timeout" | "cancelled" | "sdk_error" | "resume_unavailable" | "backend_misconfigured";
58
+ sdkSessionId: string | null;
59
+ detail?: string | null;
60
+ costUsd: number;
61
+ numTurns: number;
62
+ durationMs: number;
63
+ }
64
+ /**
65
+ * Acquire a fresh workdir + runtime + binding for `row`. The runner
66
+ * calls this BEFORE `runDriver` so it can stash the handle in its parked
67
+ * map before any turn fires (an ask_user on the first turn must find the
68
+ * handle already there).
69
+ */
70
+ export declare function prepareDriverHandle(input: {
71
+ deps: DriverDeps;
72
+ row: BackgroundTaskRow;
73
+ }): Promise<{
74
+ ok: true;
75
+ handle: DriverHandle;
76
+ } | {
77
+ ok: false;
78
+ reason: DriverRunResult["outcome"];
79
+ detail?: string;
80
+ }>;
81
+ /** Drive the initial turn — the worker reads the brief and works. */
82
+ export declare function runDriver(deps: DriverDeps, row: BackgroundTaskRow, handle: DriverHandle): Promise<DriverRunResult>;
83
+ /** Resume a parked task after `/clarify` lands the owner's answer. Uses
84
+ * the persisted SDK session id so the prompt cache stays warm. Works both
85
+ * in-process (warm parked handle) and across a daemon restart (the runner
86
+ * reconstructs the handle from the persisted `backend_session_id`); in the
87
+ * cross-restart case a session the SDK can no longer load surfaces as
88
+ * `resume_unavailable`. */
89
+ export declare function resumeDriver(deps: DriverDeps, row: BackgroundTaskRow, handle: DriverHandle, userAnswer: string): Promise<DriverRunResult>;
90
+ /**
91
+ * BACKGROUND_TASK_RUNNER_DESIGN.md §10.2 / Phase 4 — resume a task that was
92
+ * mid-execution when the daemon restarted, using the persisted SDK session
93
+ * id (`backend_session_id`) so the warm transcript + prompt cache survive
94
+ * the restart instead of re-running the brief from scratch. The runner
95
+ * reconstructs the handle (`prepareDriverHandle` recreates the per-task
96
+ * workdir + sets `sdkSessionId` from the row). When the SDK can no longer
97
+ * load the session, this returns `resume_unavailable` and the runner falls
98
+ * back to re-dispatch-from-brief — so resume is a pure optimization with no
99
+ * regression.
100
+ */
101
+ export declare function resumeFromBootDriver(deps: DriverDeps, row: BackgroundTaskRow, handle: DriverHandle): Promise<DriverRunResult>;
102
+ /** Remove the per-task workdir. Idempotent. Parked tasks (awaiting_user)
103
+ * do NOT call this — the runner keeps the handle in its parked map so
104
+ * /clarify can resume the warm SDK session. */
105
+ export declare function releaseDriverHandle(deps: DriverDeps, handle: DriverHandle): Promise<void>;