@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,152 @@
1
+ /**
2
+ * Background-task clarifications — BACKGROUND_TASK_RUNNER_DESIGN.md §6.
3
+ *
4
+ * One row per `ask_user` round-trip. The worker writes the row when it
5
+ * calls `ask_user`, the runner transitions the task to `awaiting_user`,
6
+ * and the delivery boundary surfaces the question to the owner (active
7
+ * delivery turn or idle draft send). The owner replies via DM; the DM
8
+ * agent forwards the answer through `POST /api/background-task/:id/clarify`
9
+ * which calls `resolveClarification` here.
10
+ *
11
+ * Deadline enforcement: a daemon housekeeping tick sweeps
12
+ * `WHERE resolved = 0 AND deadline_at < now()` via
13
+ * `listOverdueClarifications` and transitions the parent task to
14
+ * `timeout` (releasing the slot). The TTL is CONFIGURABLE
15
+ * (`backgroundTaskClarificationTtlMinutes`) and longer than
16
+ * browser-task's fixed 5 min — no browser resource is held while parked.
17
+ *
18
+ * I/O-bound. Excluded from the coverage gate.
19
+ */
20
+ const SELECT_COLUMNS = `id, task_id, question, context_summary,
21
+ asked_at, deadline_at, delivered_at, answer, answered_at, resolved`;
22
+ function fromDbRow(row) {
23
+ return {
24
+ id: row.id,
25
+ taskId: row.task_id,
26
+ question: row.question,
27
+ contextSummary: row.context_summary,
28
+ askedAt: row.asked_at,
29
+ deadlineAt: row.deadline_at,
30
+ deliveredAt: row.delivered_at,
31
+ answer: row.answer,
32
+ answeredAt: row.answered_at,
33
+ resolved: row.resolved === 1,
34
+ };
35
+ }
36
+ export function createClarification(db, input) {
37
+ const deadline = input.askedAt + input.ttlMs;
38
+ db.prepare(`INSERT INTO background_task_clarifications
39
+ (id, task_id, question, context_summary,
40
+ asked_at, deadline_at, delivered_at, answer, answered_at, resolved)
41
+ VALUES (?, ?, ?, ?, ?, ?, NULL, NULL, NULL, 0)`).run(input.id, input.taskId, input.question, input.contextSummary, input.askedAt, deadline);
42
+ const row = getClarification(db, input.id);
43
+ if (!row) {
44
+ throw new Error(`createClarification: post-insert row for ${input.id} missing`);
45
+ }
46
+ return row;
47
+ }
48
+ export function getClarification(db, id) {
49
+ const row = db
50
+ .prepare(`SELECT ${SELECT_COLUMNS} FROM background_task_clarifications WHERE id = ?`)
51
+ .get(id);
52
+ return row ? fromDbRow(row) : null;
53
+ }
54
+ export function listClarificationsForTask(db, taskId) {
55
+ const rows = db
56
+ .prepare(`SELECT ${SELECT_COLUMNS}
57
+ FROM background_task_clarifications
58
+ WHERE task_id = ?
59
+ ORDER BY asked_at ASC`)
60
+ .all(taskId);
61
+ return rows.map(fromDbRow);
62
+ }
63
+ /** The most-recent unresolved clarification for a task — the one a
64
+ * /clarify reply without an explicit id resolves against. */
65
+ export function getOpenClarificationForTask(db, taskId) {
66
+ const row = db
67
+ .prepare(`SELECT ${SELECT_COLUMNS}
68
+ FROM background_task_clarifications
69
+ WHERE task_id = ? AND resolved = 0
70
+ ORDER BY asked_at DESC
71
+ LIMIT 1`)
72
+ .get(taskId);
73
+ return row ? fromDbRow(row) : null;
74
+ }
75
+ /** CAS-resolve a clarification with the owner's answer. Refuses if
76
+ * already resolved (idempotent forwarding) or past deadline (the
77
+ * deadline scanner owns the timeout transition). */
78
+ export function resolveClarification(db, input) {
79
+ const existing = getClarification(db, input.id);
80
+ if (!existing)
81
+ return { ok: false, row: null, reason: "not_found" };
82
+ if (existing.resolved) {
83
+ return { ok: false, row: existing, reason: "already_resolved" };
84
+ }
85
+ if (input.answeredAt > existing.deadlineAt) {
86
+ return { ok: false, row: existing, reason: "expired" };
87
+ }
88
+ const result = db
89
+ .prepare(`UPDATE background_task_clarifications
90
+ SET answer = ?, answered_at = ?, resolved = 1
91
+ WHERE id = ? AND resolved = 0`)
92
+ .run(input.answer, input.answeredAt, input.id);
93
+ if (result.changes === 0) {
94
+ const after = getClarification(db, input.id);
95
+ return { ok: false, row: after, reason: "already_resolved" };
96
+ }
97
+ return { ok: true, row: getClarification(db, input.id) };
98
+ }
99
+ /** Sweep target — every unresolved clarification whose deadline has
100
+ * passed. The scanner transitions each parent task to `timeout`. */
101
+ export function listOverdueClarifications(db, nowMs) {
102
+ const rows = db
103
+ .prepare(`SELECT ${SELECT_COLUMNS}
104
+ FROM background_task_clarifications
105
+ WHERE resolved = 0 AND deadline_at < ?
106
+ ORDER BY deadline_at ASC`)
107
+ .all(nowMs);
108
+ return rows.map(fromDbRow);
109
+ }
110
+ /** Mark a clarification resolved without an answer (deadline path). */
111
+ export function expireClarification(db, id, nowMs) {
112
+ const result = db
113
+ .prepare(`UPDATE background_task_clarifications
114
+ SET resolved = 1, answered_at = ?
115
+ WHERE id = ? AND resolved = 0`)
116
+ .run(nowMs, id);
117
+ if (result.changes === 0)
118
+ return null;
119
+ return getClarification(db, id);
120
+ }
121
+ export function markClarificationDelivered(db, id, deliveredAt) {
122
+ const result = db
123
+ .prepare(`UPDATE background_task_clarifications
124
+ SET delivered_at = COALESCE(delivered_at, ?)
125
+ WHERE id = ?`)
126
+ .run(deliveredAt, id);
127
+ return result.changes > 0 ? getClarification(db, id) : null;
128
+ }
129
+ /** Delivery recovery target — undelivered, still-open clarifications
130
+ * whose parent task is parked, joined with that task's delivery fields. */
131
+ export function listUndeliveredClarifications(db, nowMs, limit = 20) {
132
+ const rows = db
133
+ .prepare(`SELECT c.id, c.task_id, c.question, c.context_summary,
134
+ c.asked_at, c.deadline_at, c.delivered_at,
135
+ c.answer, c.answered_at, c.resolved,
136
+ t.originating_channel, t.title, t.brief
137
+ FROM background_task_clarifications c
138
+ JOIN background_task t ON t.id = c.task_id
139
+ WHERE c.resolved = 0
140
+ AND c.delivered_at IS NULL
141
+ AND c.deadline_at >= ?
142
+ AND t.state = 'awaiting_user'
143
+ ORDER BY c.asked_at ASC
144
+ LIMIT ?`)
145
+ .all(nowMs, limit);
146
+ return rows.map((row) => ({
147
+ ...fromDbRow(row),
148
+ taskOriginatingChannel: row.originating_channel,
149
+ taskTitle: row.title,
150
+ taskBrief: row.brief,
151
+ }));
152
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Background-task row store — BACKGROUND_TASK_RUNNER_DESIGN.md §6.
3
+ *
4
+ * I/O-bound CRUD over `background_task`. The state machine is enforced
5
+ * at this layer via the CHECK constraint on the `state` column (closed
6
+ * set) plus the per-transition CAS helpers below — `markRunning`,
7
+ * `markAwaitingUser`, `markRunningFromParked`, `markTerminal` — each
8
+ * one's WHERE clause refuses an out-of-order write so a race between two
9
+ * writers cannot flip a row backwards.
10
+ *
11
+ * The genuinely new shape vs `browser_task` is the ARTIFACT: `report`
12
+ * (verbatim result — the fidelity anchor), `draft` (worker-authored
13
+ * summary), `notify` (the worker's disposition vs the spawn-time policy),
14
+ * `significance`, and `artifact_path`. `markTerminal` writes them in the
15
+ * same transition as the terminal state so a worker's `finish` (or the
16
+ * runner's fail-loud synthesis) is atomic.
17
+ *
18
+ * Pure decision logic (slot arithmetic) is reused from
19
+ * `services/browser-task/browser-task-slots.ts`; this module is the SQL
20
+ * wrapper and is excluded from the coverage gate (same posture as
21
+ * `browser-task-store.ts`).
22
+ */
23
+ import type Database from "better-sqlite3";
24
+ export type BackgroundTaskState = "pending" | "running" | "awaiting_user" | "completed" | "failed" | "timeout" | "cancelled";
25
+ export type BackgroundTaskNotificationPolicy = "always" | "if_significant" | "silent";
26
+ export type BackgroundTaskTier = "lite" | "medium" | "high";
27
+ export declare const BACKGROUND_TASK_TERMINAL_STATES: ReadonlySet<BackgroundTaskState>;
28
+ export declare const BACKGROUND_TASK_NON_TERMINAL_STATES: ReadonlySet<BackgroundTaskState>;
29
+ export interface BackgroundTaskRow {
30
+ id: string;
31
+ brief: string;
32
+ title: string | null;
33
+ state: BackgroundTaskState;
34
+ notificationPolicy: BackgroundTaskNotificationPolicy;
35
+ /** Phase 4 if_significant criteria DSL (§4.3) — concrete atomic
36
+ * conditions the worker checks one-by-one. Empty/null ⇒ the worker
37
+ * falls back to the prose criteria in the brief. */
38
+ significanceCriteria: string[] | null;
39
+ report: string | null;
40
+ draft: string | null;
41
+ /** null until finished; true ⇒ surface, false ⇒ file only. */
42
+ notify: boolean | null;
43
+ significance: string | null;
44
+ artifactPath: string | null;
45
+ outcomeDetail: string | null;
46
+ originatingChannel: string | null;
47
+ correlationId: string | null;
48
+ scheduleRowId: number | null;
49
+ tier: BackgroundTaskTier | null;
50
+ maxBudgetUsd: number | null;
51
+ backendSessionId: string | null;
52
+ createdAt: number;
53
+ startedAt: number | null;
54
+ finishedAt: number | null;
55
+ deliveredAt: number | null;
56
+ }
57
+ export interface CreateBackgroundTaskInput {
58
+ id: string;
59
+ brief: string;
60
+ title: string | null;
61
+ notificationPolicy: BackgroundTaskNotificationPolicy;
62
+ /** Phase 4 if_significant criteria DSL (§4.3) — optional structured
63
+ * conditions. Persisted as a JSON array; null/empty stores SQL NULL. */
64
+ significanceCriteria?: readonly string[] | null;
65
+ originatingChannel: string | null;
66
+ correlationId: string | null;
67
+ scheduleRowId: number | null;
68
+ tier: BackgroundTaskTier | null;
69
+ maxBudgetUsd: number | null;
70
+ createdAt: number;
71
+ }
72
+ /** Insert a fresh row in state=pending. The slot manager promotes it to
73
+ * `running` once a slot frees. */
74
+ export declare function createBackgroundTask(db: Database.Database, input: CreateBackgroundTaskInput): BackgroundTaskRow;
75
+ /**
76
+ * §10.3 brief-dedup — find a still-relevant task with an IDENTICAL brief
77
+ * spawned inside the dedup window, so a runaway fan-out (the
78
+ * RESEARCH_CLUSTER_COST_FIX_PLAN class: a replayed trigger POSTing the
79
+ * same brief many times in minutes) collapses onto the first task instead
80
+ * of spawning N workers. Matches on `brief` + `tier` (the two inputs that
81
+ * define "the same work" — `notificationPolicy` only affects delivery, not
82
+ * the work done) within `sinceMs`, and excludes the FAIL terminals
83
+ * (`failed`/`timeout`/`cancelled`) so a prior failure is retryable rather
84
+ * than sticky. A `completed` duplicate inside the window IS returned — the
85
+ * answer already exists, re-running would just re-spend. Newest first.
86
+ */
87
+ export declare function findRecentDuplicateBackgroundTask(db: Database.Database, input: {
88
+ brief: string;
89
+ tier: BackgroundTaskTier | null;
90
+ sinceMs: number;
91
+ }): BackgroundTaskRow | null;
92
+ export declare function getBackgroundTask(db: Database.Database, id: string): BackgroundTaskRow | null;
93
+ export interface ListBackgroundTasksOptions {
94
+ states?: readonly BackgroundTaskState[];
95
+ /** §10.5 — filter the worker disposition: `false` = the filed
96
+ * (notify=false) digest pull, `true` = already-surfaced. Rows whose
97
+ * `notify` is still NULL (unfinished) are excluded when this is set. */
98
+ notify?: boolean;
99
+ /** Keep only rows finished at/after this epoch-ms — the digest "since
100
+ * yesterday" window. */
101
+ finishedSinceMs?: number;
102
+ limit?: number;
103
+ offset?: number;
104
+ }
105
+ export declare function listBackgroundTasks(db: Database.Database, options?: ListBackgroundTasksOptions): readonly BackgroundTaskRow[];
106
+ export declare function countBackgroundTasks(db: Database.Database, options?: ListBackgroundTasksOptions): number;
107
+ /** Pending → running. CAS on prior state so a concurrent terminal
108
+ * transition (cancel-while-pending) does not get clobbered. */
109
+ export declare function markRunning(db: Database.Database, id: string, startedAt: number): BackgroundTaskRow | null;
110
+ /** Running → awaiting_user. Slot stays held; resume via /clarify. */
111
+ export declare function markAwaitingUser(db: Database.Database, id: string): BackgroundTaskRow | null;
112
+ /** awaiting_user → running. Used by /clarify resume. */
113
+ export declare function markRunningFromParked(db: Database.Database, id: string): BackgroundTaskRow | null;
114
+ export interface TerminalTransitionInput {
115
+ id: string;
116
+ state: "completed" | "failed" | "timeout" | "cancelled";
117
+ outcomeDetail: string | null;
118
+ finishedAt: number;
119
+ /** Artifact fields. Passing `undefined`/`null` leaves the existing
120
+ * column unchanged (COALESCE). `finish` writes all of them on the
121
+ * `completed` path; the runner's fail-loud synthesis writes
122
+ * `report`/`draft`/`notify` on the `failed`/`timeout` path. */
123
+ report?: string | null;
124
+ draft?: string | null;
125
+ /** `true`/`false` set explicitly; `undefined` leaves it NULL. */
126
+ notify?: boolean;
127
+ significance?: string | null;
128
+ artifactPath?: string | null;
129
+ }
130
+ /** Any non-terminal state → terminal, writing the artifact atomically.
131
+ * Idempotent — re-running on an already-terminal row CAS-misses and
132
+ * returns null. */
133
+ export declare function markTerminal(db: Database.Database, input: TerminalTransitionInput): BackgroundTaskRow | null;
134
+ /** Capture the SDK session id once the first turn streams it, so a
135
+ * /clarify resume can `query({resume})` the warm session. */
136
+ export declare function setBackendSessionId(db: Database.Database, id: string, sessionId: string): void;
137
+ export declare function markBackgroundTaskDelivered(db: Database.Database, id: string, deliveredAt: number): BackgroundTaskRow | null;
138
+ /** Delivery recovery target — completed rows whose worker stored a
139
+ * notify=true artifact but whose DM was never sent/recorded (§10.2). */
140
+ export declare function listUndeliveredBackgroundTaskReports(db: Database.Database, limit?: number): readonly BackgroundTaskRow[];
141
+ /** §10.5 — filed (notify=false) results, for the periodic digest +
142
+ * owner pull ("did that monitor ever run?"). */
143
+ export declare function listFiledBackgroundTaskResults(db: Database.Database, sinceMs: number, limit?: number): readonly BackgroundTaskRow[];
144
+ /**
145
+ * §10.2 boot re-dispatch-from-brief — reset every non-terminal row to
146
+ * `pending` (clearing the lost in-memory session) so the event-pipeline
147
+ * boot hook can re-run each one's brief through the runner. Returns the
148
+ * ids reset so the caller can fan out the re-dispatch.
149
+ *
150
+ * Unlike browser_task's `sweepNonTerminalRowsForBootRecovery` (which
151
+ * force-fails), background tasks are re-dispatchable because the brief is
152
+ * self-contained. `backend_session_id` is cleared since the prior SDK
153
+ * session is unreachable after a restart.
154
+ *
155
+ * Open clarifications belonging to the reset tasks are resolved in the
156
+ * SAME transaction. The pre-restart run that raised an `ask_user` is
157
+ * gone, so its clarification row is orphaned: a surviving `resolved = 0`
158
+ * row would later trip the deadline scanner (`listOverdueClarifications`
159
+ * → `expireForDeadline`) into transitioning the FRESH re-dispatched run
160
+ * to `timeout` — and because re-dispatch makes the task ACTIVE again
161
+ * (pending→running), that `expireForDeadline` is NOT a no-op the way it
162
+ * is for a terminal row. Clearing them here closes that window.
163
+ */
164
+ export declare function resetNonTerminalForBootRedispatch(db: Database.Database, nowMs?: number): readonly {
165
+ id: string;
166
+ }[];
167
+ /**
168
+ * Phase 4 resume-across-restart (§10.2) — the non-terminal rows the boot
169
+ * recovery path partitions into "resume the SDK session" vs "re-dispatch
170
+ * from brief". Returns just the discriminators (id, state, session id) so
171
+ * the caller can decide without loading the full artifact.
172
+ */
173
+ export declare function listNonTerminalBackgroundTasks(db: Database.Database): readonly {
174
+ id: string;
175
+ state: BackgroundTaskState;
176
+ backendSessionId: string | null;
177
+ }[];
178
+ /**
179
+ * Phase 4 resume-across-restart (§10.2) — reset ONE non-terminal row back
180
+ * to `pending` for re-dispatch-from-brief, clearing its (now unreachable)
181
+ * SDK session id and resolving its orphaned open clarifications in the same
182
+ * transaction (same rationale as the bulk
183
+ * `resetNonTerminalForBootRedispatch`: a surviving `resolved = 0` row would
184
+ * trip the deadline scanner into timing out the FRESH re-dispatched run).
185
+ * Used by the boot path for the rows it re-dispatches and by the runner's
186
+ * resume-failure fallback. Returns the row id when it was non-terminal,
187
+ * else null (idempotent on an already-terminal / missing row).
188
+ */
189
+ export declare function resetSingleForBootRedispatch(db: Database.Database, id: string, nowMs?: number): string | null;
190
+ /**
191
+ * Fold a just-answered clarification into the task's brief so a COLD
192
+ * re-dispatch (when the warm SDK session can't be resumed across a restart,
193
+ * §10.2) still carries the owner's answer and doesn't re-ask the same
194
+ * question. The worker only ever sees the brief, so appending the resolved
195
+ * Q&A is the only way to thread the answer into a fresh run. Idempotency is
196
+ * not required — this runs at most once per clarification per re-dispatch,
197
+ * and the clarify route has already CAS-resolved the row.
198
+ */
199
+ export declare function appendResolvedClarificationToBrief(db: Database.Database, id: string, question: string | null, answer: string): void;
200
+ /**
201
+ * Retention prune for terminal rows older than `cutoffMs`. Children in
202
+ * `background_task_clarifications` go with the parent via ON DELETE
203
+ * CASCADE. Non-terminal rows are never deleted (the boot re-dispatch
204
+ * sweep owns them). `finished_at` is the lifetime anchor with a
205
+ * `created_at` fallback for the rare unset-finished_at terminal.
206
+ */
207
+ export declare function deleteTerminalBackgroundTasksOlderThan(db: Database.Database, cutoffMs: number): number;