@aitne/daemon 0.1.2 → 0.1.4

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 (253) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/whatsapp-adapter.d.ts.map +1 -1
  3. package/dist/adapters/whatsapp-adapter.js +0 -1
  4. package/dist/adapters/whatsapp-adapter.js.map +1 -1
  5. package/dist/api/integration-route-gate.d.ts +15 -11
  6. package/dist/api/integration-route-gate.d.ts.map +1 -1
  7. package/dist/api/integration-route-gate.js +60 -23
  8. package/dist/api/integration-route-gate.js.map +1 -1
  9. package/dist/api/json-body.d.ts +22 -7
  10. package/dist/api/json-body.d.ts.map +1 -1
  11. package/dist/api/json-body.js +27 -8
  12. package/dist/api/json-body.js.map +1 -1
  13. package/dist/api/routes/agent.d.ts.map +1 -1
  14. package/dist/api/routes/agent.js +18 -0
  15. package/dist/api/routes/agent.js.map +1 -1
  16. package/dist/api/routes/backends.d.ts.map +1 -1
  17. package/dist/api/routes/backends.js +96 -1
  18. package/dist/api/routes/backends.js.map +1 -1
  19. package/dist/api/routes/books.js +1 -1
  20. package/dist/api/routes/books.js.map +1 -1
  21. package/dist/api/routes/context.d.ts.map +1 -1
  22. package/dist/api/routes/context.js +13 -1
  23. package/dist/api/routes/context.js.map +1 -1
  24. package/dist/api/routes/dashboard.d.ts.map +1 -1
  25. package/dist/api/routes/dashboard.js +75 -5
  26. package/dist/api/routes/dashboard.js.map +1 -1
  27. package/dist/api/routes/github.d.ts.map +1 -1
  28. package/dist/api/routes/github.js +38 -5
  29. package/dist/api/routes/github.js.map +1 -1
  30. package/dist/api/routes/integrations.d.ts +35 -6
  31. package/dist/api/routes/integrations.d.ts.map +1 -1
  32. package/dist/api/routes/integrations.js +191 -16
  33. package/dist/api/routes/integrations.js.map +1 -1
  34. package/dist/api/routes/mail.d.ts.map +1 -1
  35. package/dist/api/routes/mail.js +112 -46
  36. package/dist/api/routes/mail.js.map +1 -1
  37. package/dist/api/routes/observations.d.ts.map +1 -1
  38. package/dist/api/routes/observations.js +161 -8
  39. package/dist/api/routes/observations.js.map +1 -1
  40. package/dist/api/routes/setup-migrate.d.ts +9 -1
  41. package/dist/api/routes/setup-migrate.d.ts.map +1 -1
  42. package/dist/api/routes/setup-migrate.js +4 -2
  43. package/dist/api/routes/setup-migrate.js.map +1 -1
  44. package/dist/api/routes/skills.d.ts.map +1 -1
  45. package/dist/api/routes/skills.js +39 -1
  46. package/dist/api/routes/skills.js.map +1 -1
  47. package/dist/api/routes/voice.d.ts.map +1 -1
  48. package/dist/api/routes/voice.js +154 -14
  49. package/dist/api/routes/voice.js.map +1 -1
  50. package/dist/bootstrap/adapters.d.ts +109 -0
  51. package/dist/bootstrap/adapters.d.ts.map +1 -0
  52. package/dist/bootstrap/adapters.js +237 -0
  53. package/dist/bootstrap/adapters.js.map +1 -0
  54. package/dist/bootstrap/catchup.d.ts +23 -0
  55. package/dist/bootstrap/catchup.d.ts.map +1 -0
  56. package/dist/bootstrap/catchup.js +124 -0
  57. package/dist/bootstrap/catchup.js.map +1 -0
  58. package/dist/bootstrap/schedule-helpers.d.ts +18 -0
  59. package/dist/bootstrap/schedule-helpers.d.ts.map +1 -0
  60. package/dist/bootstrap/schedule-helpers.js +96 -0
  61. package/dist/bootstrap/schedule-helpers.js.map +1 -0
  62. package/dist/bootstrap/services.d.ts +60 -0
  63. package/dist/bootstrap/services.d.ts.map +1 -0
  64. package/dist/bootstrap/services.js +209 -0
  65. package/dist/bootstrap/services.js.map +1 -0
  66. package/dist/core/backends/backend-router.d.ts +23 -0
  67. package/dist/core/backends/backend-router.d.ts.map +1 -1
  68. package/dist/core/backends/backend-router.js +48 -3
  69. package/dist/core/backends/backend-router.js.map +1 -1
  70. package/dist/core/backends/claude-auth.d.ts +70 -0
  71. package/dist/core/backends/claude-auth.d.ts.map +1 -0
  72. package/dist/core/backends/claude-auth.js +198 -0
  73. package/dist/core/backends/claude-auth.js.map +1 -0
  74. package/dist/core/backends/claude-code-core.d.ts +47 -119
  75. package/dist/core/backends/claude-code-core.d.ts.map +1 -1
  76. package/dist/core/backends/claude-code-core.js +112 -1565
  77. package/dist/core/backends/claude-code-core.js.map +1 -1
  78. package/dist/core/backends/claude-delegated.d.ts +86 -0
  79. package/dist/core/backends/claude-delegated.d.ts.map +1 -0
  80. package/dist/core/backends/claude-delegated.js +801 -0
  81. package/dist/core/backends/claude-delegated.js.map +1 -0
  82. package/dist/core/backends/claude-errors.d.ts +39 -0
  83. package/dist/core/backends/claude-errors.d.ts.map +1 -0
  84. package/dist/core/backends/claude-errors.js +71 -0
  85. package/dist/core/backends/claude-errors.js.map +1 -0
  86. package/dist/core/backends/claude-probe.d.ts +103 -0
  87. package/dist/core/backends/claude-probe.d.ts.map +1 -0
  88. package/dist/core/backends/claude-probe.js +336 -0
  89. package/dist/core/backends/claude-probe.js.map +1 -0
  90. package/dist/core/backends/claude-tool-collection.d.ts +135 -0
  91. package/dist/core/backends/claude-tool-collection.d.ts.map +1 -0
  92. package/dist/core/backends/claude-tool-collection.js +831 -0
  93. package/dist/core/backends/claude-tool-collection.js.map +1 -0
  94. package/dist/core/backends/gemini-cli-core.d.ts +21 -0
  95. package/dist/core/backends/gemini-cli-core.d.ts.map +1 -1
  96. package/dist/core/backends/gemini-cli-core.js +84 -6
  97. package/dist/core/backends/gemini-cli-core.js.map +1 -1
  98. package/dist/core/backends/prompt-utils.d.ts +1 -0
  99. package/dist/core/backends/prompt-utils.d.ts.map +1 -1
  100. package/dist/core/backends/prompt-utils.js +60 -3
  101. package/dist/core/backends/prompt-utils.js.map +1 -1
  102. package/dist/core/context-builder.d.ts +36 -12
  103. package/dist/core/context-builder.d.ts.map +1 -1
  104. package/dist/core/context-builder.js +179 -89
  105. package/dist/core/context-builder.js.map +1 -1
  106. package/dist/core/dispatcher-date-utils.d.ts +49 -0
  107. package/dist/core/dispatcher-date-utils.d.ts.map +1 -0
  108. package/dist/core/dispatcher-date-utils.js +132 -0
  109. package/dist/core/dispatcher-date-utils.js.map +1 -0
  110. package/dist/core/dispatcher-error-handling.d.ts +159 -0
  111. package/dist/core/dispatcher-error-handling.d.ts.map +1 -0
  112. package/dist/core/dispatcher-error-handling.js +393 -0
  113. package/dist/core/dispatcher-error-handling.js.map +1 -0
  114. package/dist/core/dispatcher-hourly-check.d.ts +150 -0
  115. package/dist/core/dispatcher-hourly-check.d.ts.map +1 -0
  116. package/dist/core/dispatcher-hourly-check.js +665 -0
  117. package/dist/core/dispatcher-hourly-check.js.map +1 -0
  118. package/dist/core/dispatcher-message-handler.d.ts +170 -0
  119. package/dist/core/dispatcher-message-handler.d.ts.map +1 -0
  120. package/dist/core/dispatcher-message-handler.js +1054 -0
  121. package/dist/core/dispatcher-message-handler.js.map +1 -0
  122. package/dist/core/dispatcher-morning-routine.d.ts +169 -0
  123. package/dist/core/dispatcher-morning-routine.d.ts.map +1 -0
  124. package/dist/core/dispatcher-morning-routine.js +434 -0
  125. package/dist/core/dispatcher-morning-routine.js.map +1 -0
  126. package/dist/core/dispatcher-prompt.d.ts +107 -0
  127. package/dist/core/dispatcher-prompt.d.ts.map +1 -0
  128. package/dist/core/dispatcher-prompt.js +227 -0
  129. package/dist/core/dispatcher-prompt.js.map +1 -0
  130. package/dist/core/dispatcher-repository-helpers.d.ts +39 -0
  131. package/dist/core/dispatcher-repository-helpers.d.ts.map +1 -0
  132. package/dist/core/dispatcher-repository-helpers.js +86 -0
  133. package/dist/core/dispatcher-repository-helpers.js.map +1 -0
  134. package/dist/core/dispatcher-result-processor.d.ts +145 -0
  135. package/dist/core/dispatcher-result-processor.d.ts.map +1 -0
  136. package/dist/core/dispatcher-result-processor.js +414 -0
  137. package/dist/core/dispatcher-result-processor.js.map +1 -0
  138. package/dist/core/dispatcher-scheduled-tasks.d.ts +406 -0
  139. package/dist/core/dispatcher-scheduled-tasks.d.ts.map +1 -0
  140. package/dist/core/dispatcher-scheduled-tasks.js +998 -0
  141. package/dist/core/dispatcher-scheduled-tasks.js.map +1 -0
  142. package/dist/core/dispatcher-types.d.ts +296 -0
  143. package/dist/core/dispatcher-types.d.ts.map +1 -0
  144. package/dist/core/dispatcher-types.js +106 -0
  145. package/dist/core/dispatcher-types.js.map +1 -0
  146. package/dist/core/dispatcher.d.ts +86 -610
  147. package/dist/core/dispatcher.d.ts.map +1 -1
  148. package/dist/core/dispatcher.js +293 -3542
  149. package/dist/core/dispatcher.js.map +1 -1
  150. package/dist/core/integration-health.d.ts +18 -10
  151. package/dist/core/integration-health.d.ts.map +1 -1
  152. package/dist/core/integration-health.js +31 -1
  153. package/dist/core/integration-health.js.map +1 -1
  154. package/dist/core/integration-lifecycle.d.ts +65 -0
  155. package/dist/core/integration-lifecycle.d.ts.map +1 -1
  156. package/dist/core/integration-lifecycle.js +167 -16
  157. package/dist/core/integration-lifecycle.js.map +1 -1
  158. package/dist/core/integration-main-backend.d.ts +40 -0
  159. package/dist/core/integration-main-backend.d.ts.map +1 -1
  160. package/dist/core/integration-main-backend.js +89 -2
  161. package/dist/core/integration-main-backend.js.map +1 -1
  162. package/dist/core/management-md.d.ts +51 -17
  163. package/dist/core/management-md.d.ts.map +1 -1
  164. package/dist/core/management-md.js +233 -56
  165. package/dist/core/management-md.js.map +1 -1
  166. package/dist/core/output-language-policy.d.ts +74 -0
  167. package/dist/core/output-language-policy.d.ts.map +1 -0
  168. package/dist/core/output-language-policy.js +194 -0
  169. package/dist/core/output-language-policy.js.map +1 -0
  170. package/dist/core/prompts.d.ts +1 -0
  171. package/dist/core/prompts.d.ts.map +1 -1
  172. package/dist/core/prompts.js +121 -3
  173. package/dist/core/prompts.js.map +1 -1
  174. package/dist/core/repository-management-docs.d.ts +24 -0
  175. package/dist/core/repository-management-docs.d.ts.map +1 -1
  176. package/dist/core/repository-management-docs.js +210 -26
  177. package/dist/core/repository-management-docs.js.map +1 -1
  178. package/dist/core/routine-acquisition-plan.d.ts +131 -0
  179. package/dist/core/routine-acquisition-plan.d.ts.map +1 -0
  180. package/dist/core/routine-acquisition-plan.js +268 -0
  181. package/dist/core/routine-acquisition-plan.js.map +1 -0
  182. package/dist/core/routine-fetch-window-runner.d.ts +201 -0
  183. package/dist/core/routine-fetch-window-runner.d.ts.map +1 -0
  184. package/dist/core/routine-fetch-window-runner.js +661 -0
  185. package/dist/core/routine-fetch-window-runner.js.map +1 -0
  186. package/dist/core/routine-windows.d.ts +156 -0
  187. package/dist/core/routine-windows.d.ts.map +1 -0
  188. package/dist/core/routine-windows.js +330 -0
  189. package/dist/core/routine-windows.js.map +1 -0
  190. package/dist/core/skills-compiler.d.ts +11 -0
  191. package/dist/core/skills-compiler.d.ts.map +1 -1
  192. package/dist/core/skills-compiler.js +102 -13
  193. package/dist/core/skills-compiler.js.map +1 -1
  194. package/dist/core/skills-manifest.d.ts.map +1 -1
  195. package/dist/core/skills-manifest.js +26 -0
  196. package/dist/core/skills-manifest.js.map +1 -1
  197. package/dist/core/system-reset.d.ts.map +1 -1
  198. package/dist/core/system-reset.js +25 -2
  199. package/dist/core/system-reset.js.map +1 -1
  200. package/dist/db/observations.d.ts +45 -2
  201. package/dist/db/observations.d.ts.map +1 -1
  202. package/dist/db/observations.js +112 -14
  203. package/dist/db/observations.js.map +1 -1
  204. package/dist/db/schema.d.ts.map +1 -1
  205. package/dist/db/schema.js +13 -25
  206. package/dist/db/schema.js.map +1 -1
  207. package/dist/index.js +83 -610
  208. package/dist/index.js.map +1 -1
  209. package/dist/observers/delegated-sync-worker.d.ts +45 -2
  210. package/dist/observers/delegated-sync-worker.d.ts.map +1 -1
  211. package/dist/observers/delegated-sync-worker.js +71 -21
  212. package/dist/observers/delegated-sync-worker.js.map +1 -1
  213. package/dist/observers/mail-poller.d.ts +12 -5
  214. package/dist/observers/mail-poller.d.ts.map +1 -1
  215. package/dist/observers/mail-poller.js +36 -14
  216. package/dist/observers/mail-poller.js.map +1 -1
  217. package/dist/observers/manager.d.ts +37 -5
  218. package/dist/observers/manager.d.ts.map +1 -1
  219. package/dist/observers/manager.js +28 -10
  220. package/dist/observers/manager.js.map +1 -1
  221. package/dist/safety/risk-classifier.d.ts.map +1 -1
  222. package/dist/safety/risk-classifier.js +5 -0
  223. package/dist/safety/risk-classifier.js.map +1 -1
  224. package/dist/services/delegated-backend-invoker.d.ts +1 -51
  225. package/dist/services/delegated-backend-invoker.d.ts.map +1 -1
  226. package/dist/services/delegated-backend-invoker.js +41 -480
  227. package/dist/services/delegated-backend-invoker.js.map +1 -1
  228. package/dist/services/delegated-invoker-audit.d.ts +94 -0
  229. package/dist/services/delegated-invoker-audit.d.ts.map +1 -0
  230. package/dist/services/delegated-invoker-audit.js +238 -0
  231. package/dist/services/delegated-invoker-audit.js.map +1 -0
  232. package/dist/services/delegated-invoker-cache-hits.d.ts +34 -0
  233. package/dist/services/delegated-invoker-cache-hits.d.ts.map +1 -0
  234. package/dist/services/delegated-invoker-cache-hits.js +104 -0
  235. package/dist/services/delegated-invoker-cache-hits.js.map +1 -0
  236. package/dist/services/delegated-invoker-janitors.d.ts +28 -0
  237. package/dist/services/delegated-invoker-janitors.d.ts.map +1 -0
  238. package/dist/services/delegated-invoker-janitors.js +104 -0
  239. package/dist/services/delegated-invoker-janitors.js.map +1 -0
  240. package/dist/services/delegated-invoker-utils.d.ts +42 -0
  241. package/dist/services/delegated-invoker-utils.d.ts.map +1 -0
  242. package/dist/services/delegated-invoker-utils.js +100 -0
  243. package/dist/services/delegated-invoker-utils.js.map +1 -0
  244. package/dist/services/delegated-task-runtime.d.ts +1 -1
  245. package/dist/services/delegated-task-runtime.js +1 -1
  246. package/dist/services/integrations/snapshot-partitions.d.ts +5 -0
  247. package/dist/services/integrations/snapshot-partitions.d.ts.map +1 -1
  248. package/dist/services/integrations/snapshot-partitions.js +12 -0
  249. package/dist/services/integrations/snapshot-partitions.js.map +1 -1
  250. package/dist/services/voice/transcriber-impl.d.ts.map +1 -1
  251. package/dist/services/voice/transcriber-impl.js +46 -0
  252. package/dist/services/voice/transcriber-impl.js.map +1 -1
  253. package/package.json +12 -12
@@ -0,0 +1,393 @@
1
+ /**
2
+ * `DispatcherErrorRouter` — owns the dispatcher's failure-path
3
+ * machinery: the shallow retry wrapper around BackendRouter
4
+ * (`executeWithRetry`), the post-throw cleanup + DM/dashboard
5
+ * notification (`handleError`), the quota-error introspection +
6
+ * formatting helpers, and the §4.5 delegated-connector health
7
+ * warning consult/dispatch pair.
8
+ *
9
+ * Extracted from `core/dispatcher.ts` as part of phase D-2 of
10
+ * `docs/design/appendices/file-split-plan.md`. Pattern B (stateful
11
+ * coordinator): the router owns its own logic but borrows live
12
+ * references from the dispatcher for state that must remain
13
+ * observable to the parent class — the `notifiedEvents` Set used to
14
+ * dedup outbound notifications, the `shutdownAwaiters` Set used to
15
+ * shortcut the retry sleep on SIGTERM, plus accessors for the
16
+ * dispatcher's `shutdown` flag and lazily-injected dashboard stream.
17
+ *
18
+ * Dispatcher entry points served:
19
+ * - `dispatchSafe` calls `executeWithRetry` around backend invokes
20
+ * and `handleError` on failure;
21
+ * - `handleMessage` calls the `consult/run` connector-warning pair
22
+ * before/after recording the user message;
23
+ * - the §4.5 finalize callbacks (`onRetemplateFinalize` /
24
+ * `onManagementScanFinalize`) bridge into the ResultProcessor —
25
+ * the success path uses ResultProcessor directly; the error path
26
+ * calls through these closures to keep the failure ordering
27
+ * identical to the pre-split file.
28
+ *
29
+ * Shared-state references held:
30
+ * - `notifiedEvents: Set<string>` — live reference; `handleError`
31
+ * deletes the entry as defense-in-depth cleanup when execution
32
+ * threw before reaching `processResult`.
33
+ * - `shutdownAwaiters: Set<() => void>` — live reference; the retry
34
+ * sleep races a 5-min timer against this awaiter set so SIGTERM
35
+ * resolves the wait promptly.
36
+ * - `isShutdown: () => boolean` — getter; on shutdown wakeup the
37
+ * retry loop rethrows instead of looping again.
38
+ * - `getDashboardStream: () => IDashboardStream | null` — lazy
39
+ * accessor; the dashboard stream is wired after construction.
40
+ */
41
+ import { isMessageEvent, isScheduledEvent } from "@aitne/shared";
42
+ import { BackendDecisiveFailure, BackendQuotaError, } from "./agent-core.js";
43
+ import { BackendRouterHandledError } from "./backends/backend-router.js";
44
+ import { consultDelegatedConnectorHealth, markSignoutWarned, renderSignoutDm, } from "./delegated-connector-health.js";
45
+ import { compareLocalDateParts, getLocalDateParts, localDateTimeToUtcMs, } from "./dispatcher-date-utils.js";
46
+ import { createLogger } from "../logging.js";
47
+ const logger = createLogger("dispatcher-error");
48
+ export class DispatcherErrorRouter {
49
+ db;
50
+ config;
51
+ notificationMgr;
52
+ messageRecorder;
53
+ notifiedEvents;
54
+ shutdownAwaiters;
55
+ getDashboardStream;
56
+ isShutdown;
57
+ onRetemplateFinalize;
58
+ onManagementScanFinalize;
59
+ constructor(deps) {
60
+ this.db = deps.db;
61
+ this.config = deps.config;
62
+ this.notificationMgr = deps.notificationMgr;
63
+ this.messageRecorder = deps.messageRecorder;
64
+ this.notifiedEvents = deps.notifiedEvents;
65
+ this.shutdownAwaiters = deps.shutdownAwaiters;
66
+ this.getDashboardStream = deps.getDashboardStream;
67
+ this.isShutdown = deps.isShutdown;
68
+ this.onRetemplateFinalize = deps.onRetemplateFinalize;
69
+ this.onManagementScanFinalize = deps.onManagementScanFinalize;
70
+ }
71
+ isRetryable(error) {
72
+ // BackendCore implementations wrap all errors into BackendQuotaError or
73
+ // BackendDecisiveFailure before they reach the dispatcher. Both are
74
+ // decisive (no retry). BackendRouterHandledError is also decisive.
75
+ if (error instanceof BackendQuotaError ||
76
+ error instanceof BackendDecisiveFailure ||
77
+ error instanceof BackendRouterHandledError) {
78
+ return false;
79
+ }
80
+ // Raw 5xx from an unclassified path — retry once.
81
+ const status = typeof error === "object" && error !== null && "status" in error
82
+ ? error.status
83
+ : undefined;
84
+ return typeof status === "number" && status >= 500;
85
+ }
86
+ /**
87
+ * Defense-in-depth retry wrapper around BackendRouter.execute().
88
+ *
89
+ * **Primary retry responsibility lives inside each BackendCore** (§12/§13).
90
+ * Quota errors, timeouts, and auth failures are all normalized into
91
+ * BackendDecisiveFailure / BackendQuotaError before they reach this layer.
92
+ * The BackendRouter handles fallback on decisive failures.
93
+ *
94
+ * This outer loop exists solely as a safety net for raw 5xx errors that
95
+ * somehow escape the BackendCore → Router chain (e.g., an unexpected HTTP
96
+ * error from the SDK transport layer). In practice it almost never fires.
97
+ */
98
+ async executeWithRetry(fn, event) {
99
+ const maxRetries = 1;
100
+ let lastError;
101
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
102
+ try {
103
+ return await fn();
104
+ }
105
+ catch (error) {
106
+ lastError = error;
107
+ if (attempt < maxRetries && this.isRetryable(error)) {
108
+ logger.warn({
109
+ eventType: event.type,
110
+ attempt: attempt + 1,
111
+ error: error instanceof Error ? error.message : String(error),
112
+ }, "Retrying agent execution after backoff");
113
+ // Shutdown-aware sleep: race a single 5-minute timer against the
114
+ // shutdown signal so SIGTERM unwinds the retry loop promptly
115
+ // instead of blocking for up to 5 minutes.
116
+ await new Promise((resolve) => {
117
+ const onShutdown = () => {
118
+ clearTimeout(timer);
119
+ this.shutdownAwaiters.delete(onShutdown);
120
+ resolve();
121
+ };
122
+ const timer = setTimeout(() => {
123
+ this.shutdownAwaiters.delete(onShutdown);
124
+ resolve();
125
+ }, 5 * 60 * 1000);
126
+ this.shutdownAwaiters.add(onShutdown);
127
+ });
128
+ if (this.isShutdown()) {
129
+ throw lastError;
130
+ }
131
+ continue;
132
+ }
133
+ break;
134
+ }
135
+ }
136
+ throw lastError;
137
+ }
138
+ async handleError(event, error) {
139
+ logger.error({ event: event.type, error: error.message }, "Event processing error");
140
+ // Defense-in-depth cleanup of the notify-dedup marker — processResult
141
+ // is the primary collection point, but if execution threw before
142
+ // reaching it, drop any orphan entry here so the set cannot grow
143
+ // unbounded across error storms.
144
+ this.notifiedEvents.delete(event.correlationId);
145
+ const routerHandledError = error instanceof BackendRouterHandledError
146
+ ? error
147
+ : null;
148
+ // Mark scheduled task as failed whenever execution terminates
149
+ // without a result. Covers both scheduled.task and scheduled.dm —
150
+ // a scheduled.dm row that throws would otherwise stick in
151
+ // `running` forever.
152
+ if (isScheduledEvent(event) && event.scheduleId) {
153
+ this.db
154
+ .prepare("UPDATE agent_schedule SET status = 'failed' WHERE id = ? AND status = 'running'")
155
+ .run(event.scheduleId);
156
+ this.onRetemplateFinalize(event, { errored: true });
157
+ }
158
+ // Same rationale as the success-path call: management events have no
159
+ // `agent_schedule` row, so this hook is intentionally outside the
160
+ // scheduleId guard.
161
+ this.onManagementScanFinalize(event, { errored: true });
162
+ if (routerHandledError) {
163
+ const quotaError = this.extractQuotaError(routerHandledError.cause);
164
+ if (quotaError && isMessageEvent(event)) {
165
+ this.notifyDashboardError(event, this.formatQuotaMessage(quotaError));
166
+ }
167
+ return;
168
+ }
169
+ const quotaError = this.extractQuotaError(error);
170
+ if (quotaError && isMessageEvent(event)) {
171
+ const quotaMsg = this.formatQuotaMessage(quotaError);
172
+ this.notifyDashboardError(event, quotaMsg);
173
+ await this.notificationMgr.send(quotaMsg, event);
174
+ return;
175
+ }
176
+ if (isMessageEvent(event)) {
177
+ const errorMsg = "An error occurred during processing. Please try again.";
178
+ this.notifyDashboardError(event, errorMsg);
179
+ await this.notificationMgr.send(errorMsg, event);
180
+ }
181
+ }
182
+ /**
183
+ * Best-effort inline error to the dashboard tab whose POST triggered
184
+ * this event. `DashboardAdapter` is `notificationEligible=false`, so
185
+ * the normal `notificationMgr.send` path skips it — without this hook
186
+ * the browser sees the request accepted (200 OK), watches nothing
187
+ * happen, and hits the 120s waiting timeout with no explanation. We
188
+ * target the originating channel id; if the tab already reconnected
189
+ * with a new UUID the adapter silently drops, which matches the
190
+ * chat_error semantics.
191
+ */
192
+ notifyDashboardError(event, message) {
193
+ if (!isMessageEvent(event))
194
+ return;
195
+ if (event.platform !== "dashboard")
196
+ return;
197
+ this.getDashboardStream()?.sendError?.(event.channel, message);
198
+ }
199
+ extractQuotaError(error) {
200
+ if (error instanceof BackendQuotaError) {
201
+ return error;
202
+ }
203
+ if (error instanceof BackendDecisiveFailure &&
204
+ error.kind === "quota" &&
205
+ error.cause instanceof BackendQuotaError) {
206
+ return error.cause;
207
+ }
208
+ // All BackendCore implementations normalize quota errors before they
209
+ // reach the dispatcher, so no Claude-specific fallback is needed here.
210
+ return null;
211
+ }
212
+ formatQuotaMessage(quotaError) {
213
+ const backendLabel = this.formatBackendLabel(quotaError.backendId);
214
+ const resetHint = quotaError.resetHint;
215
+ if (quotaError.originalCode === "max_budget_usd") {
216
+ return `${backendLabel} reached the per-turn budget limit. Please try a shorter request or raise max_budget_usd in backend settings.`;
217
+ }
218
+ if (resetHint) {
219
+ const timeZone = resetHint.timeZone || this.config.timezone || undefined;
220
+ const resetAtMs = this.resolveQuotaResetAtMs(resetHint);
221
+ if (resetAtMs !== null) {
222
+ const formatted = new Intl.DateTimeFormat("en-US", {
223
+ timeZone,
224
+ year: "numeric",
225
+ month: "2-digit",
226
+ day: "2-digit",
227
+ hour: "2-digit",
228
+ minute: "2-digit",
229
+ hourCycle: "h12",
230
+ }).format(new Date(resetAtMs));
231
+ const zoneLabel = timeZone ? ` (${timeZone})` : "";
232
+ return `${backendLabel} has reached its usage limit. Resets at ${formatted}${zoneLabel}. Please try again after the reset.`;
233
+ }
234
+ try {
235
+ new Intl.DateTimeFormat("en-US", { timeZone }).format(new Date());
236
+ }
237
+ catch {
238
+ // Fall through to rawLabel/generic message if the timezone label is invalid.
239
+ }
240
+ const rawLabel = resetHint.rawLabel.trim();
241
+ if (rawLabel) {
242
+ return `${backendLabel} has reached its usage limit. Resets at ${rawLabel}. Please try again after the reset.`;
243
+ }
244
+ }
245
+ return `${backendLabel} has reached its usage limit. Please wait and try again later.`;
246
+ }
247
+ formatBackendLabel(backendId) {
248
+ switch (backendId) {
249
+ case "claude":
250
+ return "Claude Code";
251
+ case "codex":
252
+ return "Codex";
253
+ case "gemini":
254
+ return "Gemini CLI";
255
+ default:
256
+ return backendId;
257
+ }
258
+ }
259
+ resolveQuotaResetAtMs(resetHint) {
260
+ const timeZone = resetHint.timeZone || this.config.timezone || undefined;
261
+ const now = new Date();
262
+ const current = getLocalDateParts(now, timeZone);
263
+ let target = {
264
+ year: current.year,
265
+ month: current.month,
266
+ day: current.day,
267
+ hour: resetHint.hour,
268
+ minute: resetHint.minute,
269
+ };
270
+ if (compareLocalDateParts(current, target) >= 0) {
271
+ const nextDate = new Date(Date.UTC(current.year, current.month - 1, current.day + 1));
272
+ target = {
273
+ year: nextDate.getUTCFullYear(),
274
+ month: nextDate.getUTCMonth() + 1,
275
+ day: nextDate.getUTCDate(),
276
+ hour: resetHint.hour,
277
+ minute: resetHint.minute,
278
+ };
279
+ }
280
+ return localDateTimeToUtcMs(target, timeZone);
281
+ }
282
+ /**
283
+ * DELEGATED-MODE-V2-DESIGN.md §4.5 — at every DM dispatch, consult the
284
+ * cached probe for delegated integrations whose effective backend
285
+ * matches the session backend. Surfaces a one-shot DM (deduped via
286
+ * `runtime_state`) when the cached probe says required capabilities
287
+ * are no longer present.
288
+ *
289
+ * The consult itself is synchronous DB-only work (cheap on the hot
290
+ * path). The DM dispatch is fire-and-forget so the agent's response
291
+ * latency is not gated on Slack/Telegram round-trips. Per-warning
292
+ * dispatch failures are swallowed so a flaky messaging adapter never
293
+ * breaks the user's actual DM.
294
+ *
295
+ * Phase 1 of the §4.5 health check — synchronous cache consult only.
296
+ * Returns the warnings the dispatcher must surface this turn (or `[]`
297
+ * when nothing is broken / setup mode is active / the consult itself
298
+ * threw). Recovery markers are cleared inline by the consult helper, so
299
+ * the caller does not have to track them.
300
+ *
301
+ * Split from the dispatch step so the actual DM (and its messages-table
302
+ * persist) fires AFTER the dispatcher has recorded the inbound user
303
+ * message — otherwise the warning row's `CURRENT_TIMESTAMP` lands before
304
+ * the user-message row's, which makes `chat_meta` history reload reorder
305
+ * the bubbles (warning above user) and a one-time visual flicker leaks
306
+ * to the user. See `runDelegatedConnectorWarningDispatch` below.
307
+ */
308
+ consultDelegatedConnectorWarnings(sessionBackend) {
309
+ try {
310
+ const result = consultDelegatedConnectorHealth(this.db, sessionBackend);
311
+ if (result.recovered.length > 0) {
312
+ logger.info({ recovered: result.recovered, sessionBackend }, "Delegated connector(s) recovered — sign-out warning markers cleared");
313
+ }
314
+ return result.warnings;
315
+ }
316
+ catch (err) {
317
+ logger.warn({ err, sessionBackend }, "Delegated connector-health consult failed — skipping DM warning");
318
+ return [];
319
+ }
320
+ }
321
+ /**
322
+ * Phase 2 of the §4.5 health check — asynchronous DM dispatch + post-
323
+ * delivery bookkeeping (throttle marker + dashboard-channel persist).
324
+ * Caller invokes this AFTER the user message is recorded so the DM's
325
+ * messages-table row carries a strictly-later `CURRENT_TIMESTAMP`
326
+ * (preserves pre-reconcile chat order on the dashboard).
327
+ */
328
+ runDelegatedConnectorWarningDispatch(warnings, event, sessionBackend, sessionId) {
329
+ for (const warning of warnings) {
330
+ logger.warn({
331
+ integration: warning.integration,
332
+ backend: warning.backend,
333
+ missingRequired: warning.missingRequired,
334
+ }, "Delegated connector reports missing required capabilities — DM owner");
335
+ const message = renderSignoutDm(warning);
336
+ // Mark the throttle ONLY after a successful dispatch — if the
337
+ // messaging adapter is down, an absent marker keeps the next
338
+ // consult ready to re-issue the warning. The .send() promise
339
+ // resolves on adapter-acknowledged delivery; .catch() is the
340
+ // failure side, which deliberately leaves the marker unset.
341
+ //
342
+ // After delivery, persist the warning to `messages` so it survives
343
+ // dashboard chat reload + the chat_meta history-reconcile pass
344
+ // (`reconcileLiveMessagesAfterHistoryReload` drops live bubbles whose
345
+ // timestamp is before the sync started AND whose signature is not in
346
+ // the restored history; without this persist the DM bubble vanishes
347
+ // the moment the agent's reply chat_meta arrives). For non-dashboard
348
+ // platforms (Slack/Telegram) the message-store is the platform itself,
349
+ // so we deliberately persist only when `event.platform === "dashboard"`
350
+ // to avoid duplicating remote-platform messages locally.
351
+ void this.notificationMgr
352
+ .send(message, event, {
353
+ priority: "high",
354
+ category: "delegated_signout",
355
+ })
356
+ .then(() => {
357
+ try {
358
+ markSignoutWarned(this.db, warning);
359
+ }
360
+ catch (err) {
361
+ logger.warn({
362
+ err,
363
+ integration: warning.integration,
364
+ backend: warning.backend,
365
+ }, "Failed to persist delegated-signout marker — next consult may re-warn");
366
+ }
367
+ if (event.platform === "dashboard") {
368
+ try {
369
+ this.messageRecorder.recordMessage({
370
+ sessionId,
371
+ role: "assistant",
372
+ content: message,
373
+ platform: event.platform,
374
+ backend: sessionBackend,
375
+ });
376
+ }
377
+ catch (err) {
378
+ logger.warn({
379
+ err,
380
+ integration: warning.integration,
381
+ backend: warning.backend,
382
+ sessionId,
383
+ }, "Failed to persist delegated-signout DM into messages — bubble may vanish on chat reload");
384
+ }
385
+ }
386
+ })
387
+ .catch((err) => {
388
+ logger.error({ err, integration: warning.integration, backend: warning.backend }, "Failed to deliver delegated-signout DM — marker not set, will retry next dispatch");
389
+ });
390
+ }
391
+ }
392
+ }
393
+ //# sourceMappingURL=dispatcher-error-handling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher-error-handling.js","sourceRoot":"","sources":["../../src/core/dispatcher-error-handling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * `HourlyCheckCoordinator` — owns the dispatcher's
3
+ * `triggerHourlyCheck` 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
+ * `hourlyCheckInProgress` 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.triggerHourlyCheck(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
+ * - `setHourlyCheckInProgress` / `isHourlyCheckInProgress` —
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
+ * `TriggerHourlyCheckSkipReason` 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 { AgentConfig } from "../config.js";
53
+ import type { EventBus } from "./event-bus.js";
54
+ import type { TodayWriteLockManager } from "./today-write-lock.js";
55
+ import type { IAgentRouter } from "./backends/backend-router.js";
56
+ import type { IAuditLogger, IContextBuilder, TriggerHourlyCheckOptions, TriggerHourlyCheckResult, TriggerHourlyCheckSkipReason } from "./dispatcher-types.js";
57
+ import type { PromptAssembler } from "./dispatcher-prompt.js";
58
+ import type { RoutineFetchWindowRunner } from "./routine-fetch-window-runner.js";
59
+ export interface HourlyCheckCoordinatorDeps {
60
+ db: Database.Database;
61
+ config: AgentConfig;
62
+ eventBus: EventBus;
63
+ contextBuilder: IContextBuilder;
64
+ agentRouter: IAgentRouter;
65
+ audit: IAuditLogger;
66
+ todayWriteLock: TodayWriteLockManager | undefined;
67
+ prompt: PromptAssembler;
68
+ /**
69
+ * ROUTINE_DATA_ACQUISITION_DESIGN.md Phase 4 / D3 — pre-pass runner
70
+ * spawned between Stage 2 (lite-tier triage) and Stage 3 (medium-tier
71
+ * main session) on `escalate` / `failed` verdicts. The rendered
72
+ * `<fetch_report>` block rides on the Stage 3 RoutineEvent's
73
+ * `event.data.fetchReportBlock` so ContextBuilder folds it into the
74
+ * Stage 3 prompt.
75
+ */
76
+ fetchWindowRunner: RoutineFetchWindowRunner;
77
+ /** Accessor for the lazily-injected delegated-sync refresh callback. */
78
+ getDelegatedSyncRefresh: () => (() => Promise<void>) | null;
79
+ /** Setter for the dispatcher's `hourlyCheckInProgress` flag. */
80
+ setHourlyCheckInProgress: (value: boolean) => void;
81
+ /** Getter for the dispatcher's `hourlyCheckInProgress` flag. */
82
+ isHourlyCheckInProgress: () => boolean;
83
+ /** Mirrors `EventDispatcher.isMorningRoutineActive`. */
84
+ isMorningRoutineActive: () => boolean;
85
+ /**
86
+ * Mirrors `EventDispatcher.isAutonomousAllowed`. Returns the
87
+ * skip-reason when the gate must abort early (setup incomplete,
88
+ * vault degraded, user paused) or `null` when autonomous work is
89
+ * permitted to proceed.
90
+ */
91
+ isAutonomousAllowed: () => TriggerHourlyCheckSkipReason | null;
92
+ }
93
+ export declare class HourlyCheckCoordinator {
94
+ private readonly db;
95
+ private readonly config;
96
+ private readonly eventBus;
97
+ private readonly contextBuilder;
98
+ private readonly agentRouter;
99
+ private readonly audit;
100
+ private readonly todayWriteLock;
101
+ private readonly prompt;
102
+ private readonly fetchWindowRunner;
103
+ private readonly getDelegatedSyncRefresh;
104
+ private readonly setHourlyCheckInProgress;
105
+ private readonly isHourlyCheckInProgress;
106
+ private readonly isMorningRoutineActive;
107
+ private readonly isAutonomousAllowed;
108
+ constructor(deps: HourlyCheckCoordinatorDeps);
109
+ trigger(source: string, options?: TriggerHourlyCheckOptions): Promise<TriggerHourlyCheckResult>;
110
+ /**
111
+ * cost-reduction-structural §B — pull a fresh signal snapshot and run
112
+ * the deterministic gate. Helper so the dispatcher's call site stays
113
+ * compact and tests can spy on the boundary.
114
+ */
115
+ private computeHourlyCheckGateDecision;
116
+ private readTodayMdSafe;
117
+ /**
118
+ * cost-reduction-structural §B — daemon-direct silent path. Used by
119
+ * Stage 0 and Stage 2 log-only verdicts. Consumes pending user
120
+ * observations + appends a single Agent Log line + records the gate
121
+ * verdict to `agent_actions`. The flag is reset before return.
122
+ */
123
+ private runSilentHourlyCheckPath;
124
+ private enqueueStage3HourlyCheck;
125
+ private logGateAuditRow;
126
+ /**
127
+ * cost-reduction-structural §B Stage 2 — synchronous lite-tier triage.
128
+ * Builds a `routine.hourly_check.triage` RoutineEvent and runs it
129
+ * inline through the agent router (NOT the EventBus, so the result
130
+ * is available before we decide whether to silence or escalate).
131
+ *
132
+ * The agent contract is JSON-only output (`{ "action": "log_only" |
133
+ * "escalate", "reason": "..." }`); on parse failure we return
134
+ * `'failed'` and the caller treats that as cautious escalate.
135
+ *
136
+ * Tool/turn clamp (defense-in-depth):
137
+ * - `allowedToolsOverride: []` removes every tool from the SDK's
138
+ * allowlist for the spawn. Stage 2 has nothing to do but emit a
139
+ * JSON line; the design's "no write tools" rule is enforced here
140
+ * instead of relying on the prompt alone.
141
+ * - `maxTurns: 1` caps the spawn at a single assistant turn. Even
142
+ * if a future prompt change accidentally invites tool use, the
143
+ * spawn cannot loop. Codex/Gemini have no per-spawn `allowedTools`
144
+ * surface today (acknowledged gap in `agent-core.ts`); the
145
+ * `maxTurns` cap and process_backend_config envelope are the
146
+ * remaining safety floor on those backends.
147
+ */
148
+ private runStage2Triage;
149
+ }
150
+ //# sourceMappingURL=dispatcher-hourly-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher-hourly-check.d.ts","sourceRoot":"","sources":["../../src/core/dispatcher-hourly-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAe3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAgB/C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAC;AAKjF,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,cAAc,EAAE,eAAe,CAAC;IAChC,WAAW,EAAE,YAAY,CAAC;IAC1B,KAAK,EAAE,YAAY,CAAC;IACpB,cAAc,EAAE,qBAAqB,GAAG,SAAS,CAAC;IAClD,MAAM,EAAE,eAAe,CAAC;IACxB;;;;;;;OAOG;IACH,iBAAiB,EAAE,wBAAwB,CAAC;IAC5C,wEAAwE;IACxE,uBAAuB,EAAE,MAAM,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;IAC5D,gEAAgE;IAChE,wBAAwB,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,gEAAgE;IAChE,uBAAuB,EAAE,MAAM,OAAO,CAAC;IACvC,wDAAwD;IACxD,sBAAsB,EAAE,MAAM,OAAO,CAAC;IACtC;;;;;OAKG;IACH,mBAAmB,EAAE,MAAM,4BAA4B,GAAG,IAAI,CAAC;CAChE;AAED,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkB;IACjD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoC;IACnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA2B;IAC7D,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAqC;IAC7E,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA2B;IACpE,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAgB;IACxD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAgB;IACvD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAA4C;gBAEpE,IAAI,EAAE,0BAA0B;IAiBtC,OAAO,CACX,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC;IAgUpC;;;;OAIG;IACH,OAAO,CAAC,8BAA8B;IAoBtC,OAAO,CAAC,eAAe;IAcvB;;;;;OAKG;IACH,OAAO,CAAC,wBAAwB;YA0ElB,wBAAwB;IAoEtC,OAAO,CAAC,eAAe;IA8DvB;;;;;;;;;;;;;;;;;;;;;OAqBG;YACW,eAAe;CA2F9B"}