@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,434 @@
1
+ /**
2
+ * `MorningRoutineRunner` — owns `routine.morning_routine` execution
3
+ * end-to-end: today.md write-lock acquisition, prompt-variant
4
+ * selection (`morning_routine` vs `morning_routine_initial`), tier
5
+ * inheritance for the initial variant, retry chain wrapping
6
+ * (`morningRoutineInProgress` flag), post-run today.md health check
7
+ * (missing / wrong-date), exponential-back-off retry scheduling, and
8
+ * the deferred post-morning catchup emit.
9
+ *
10
+ * Extracted from `core/dispatcher.ts` as part of phase D-2 of
11
+ * `docs/design/appendices/file-split-plan.md`. Pattern B (stateful
12
+ * coordinator): the runner has no mutable state of its own; it
13
+ * borrows the dispatcher's `morningRoutineInProgress` flag through a
14
+ * setter callback so the existing C-fix invariants survive the split,
15
+ * and bridges into a handful of dispatcher / coordinator methods that
16
+ * are still owned elsewhere (`rotateDayFiles`,
17
+ * `diagnoseTodayMdState`, `isRoadmapStale`, `emitRoadmapRefresh`,
18
+ * `triggerHourlyCheck`).
19
+ *
20
+ * Dispatcher entry points served:
21
+ * - `EventDispatcher.dispatch` routes `routine === "morning_routine"`
22
+ * into `executeMorningRoutine(event)`;
23
+ * - `ScheduledTaskRunner.handleMorningRoutineRetry` (after Task 7)
24
+ * calls back into the same entry point with a synthesized
25
+ * RoutineEvent carrying the propagated retryCount.
26
+ *
27
+ * Shared-state references held:
28
+ * - `setMorningRoutineInProgress` — setter callback. The flag is
29
+ * flipped to true at the start of the execute → executeWithRetry
30
+ * wrapper and reset in `finally` so the hourly check can resume.
31
+ */
32
+ import { EventPriority, createEvent, formatSqliteDatetime, isBackendId, } from "@aitne/shared";
33
+ import { existsSync } from "node:fs";
34
+ import { join } from "node:path";
35
+ import { getContextDir } from "../config.js";
36
+ import { findRegisteredModel } from "./backends/model-registry.js";
37
+ import { flushPendingTodayRefresh } from "./drift-effects.js";
38
+ import { maybeTriggerRoadmapRefresh } from "./schedule-insert-helper.js";
39
+ import { createLogger } from "../logging.js";
40
+ const logger = createLogger("dispatcher-morning-routine");
41
+ export class MorningRoutineRunner {
42
+ db;
43
+ config;
44
+ eventBus;
45
+ contextBuilder;
46
+ agentRouter;
47
+ notificationMgr;
48
+ todayWriteLock;
49
+ prompt;
50
+ errorRouter;
51
+ resultProcessor;
52
+ fetchWindowRunner;
53
+ setMorningRoutineInProgress;
54
+ rotateDayFiles;
55
+ diagnoseTodayMdState;
56
+ isRoadmapStale;
57
+ emitRoadmapRefresh;
58
+ triggerHourlyCheck;
59
+ constructor(deps) {
60
+ this.db = deps.db;
61
+ this.config = deps.config;
62
+ this.eventBus = deps.eventBus;
63
+ this.contextBuilder = deps.contextBuilder;
64
+ this.agentRouter = deps.agentRouter;
65
+ this.notificationMgr = deps.notificationMgr;
66
+ this.todayWriteLock = deps.todayWriteLock;
67
+ this.prompt = deps.prompt;
68
+ this.errorRouter = deps.errorRouter;
69
+ this.resultProcessor = deps.resultProcessor;
70
+ this.fetchWindowRunner = deps.fetchWindowRunner;
71
+ this.setMorningRoutineInProgress = deps.setMorningRoutineInProgress;
72
+ this.rotateDayFiles = deps.rotateDayFiles;
73
+ this.diagnoseTodayMdState = deps.diagnoseTodayMdState;
74
+ this.isRoadmapStale = deps.isRoadmapStale;
75
+ this.emitRoadmapRefresh = deps.emitRoadmapRefresh;
76
+ this.triggerHourlyCheck = deps.triggerHourlyCheck;
77
+ }
78
+ /**
79
+ * Morning routine execution with pre-processing (lock, rotateDayFiles,
80
+ * prompt variant selection). Only called for routine === "morning_routine".
81
+ * Tier is resolved by BackendRouter from process-key defaults or user config.
82
+ */
83
+ async executeMorningRoutine(event) {
84
+ let lockId = null;
85
+ let effectiveEvent = event;
86
+ if (this.todayWriteLock) {
87
+ const lock = this.todayWriteLock.acquire();
88
+ if (!lock.ok) {
89
+ logger.warn({
90
+ eventType: event.type,
91
+ source: event.source,
92
+ holder: lock.holder,
93
+ }, "today.md write lock held during morning routine — scheduling retry");
94
+ this.scheduleMorningRetry(event);
95
+ return;
96
+ }
97
+ lockId = lock.lockId;
98
+ effectiveEvent = {
99
+ ...event,
100
+ data: {
101
+ ...event.data,
102
+ todayWriteLockId: lockId,
103
+ },
104
+ };
105
+ }
106
+ this.rotateDayFiles();
107
+ // Check roadmap staleness BEFORE agent runs (agent may PATCH roadmap, updating mtime)
108
+ const roadmapStaleBeforeMorning = this.isRoadmapStale();
109
+ // Select prompt variant based on whether yesterday.md exists
110
+ const contextDir = getContextDir(this.config, this.db);
111
+ const hasYesterday = existsSync(join(contextDir, "yesterday.md"));
112
+ const promptKey = hasYesterday
113
+ ? "routine.morning_routine"
114
+ : "routine.morning_routine_initial";
115
+ // Retry runs on the light tier (Sonnet) instead of the configured heavy
116
+ // tier (Opus). Cost trade-off: a wrong-date or malformed today.md is
117
+ // cheap to regenerate — the heavy work (mail classification, journal
118
+ // synthesis, roadmap walk) was already done by the first attempt and its
119
+ // outputs persisted via /api/context/* writes that survive into the
120
+ // retry's prompt context. Sonnet at ~1/5 of Opus's per-turn cost keeps
121
+ // the worst-case retry chain (3 attempts) under $2 instead of $12,
122
+ // which is the cap the user asked for after observing $25/hour
123
+ // burn during a date-format loop. See morning-routine fix doc.
124
+ const retryCount = Number(effectiveEvent.data?.retryCount ?? 0);
125
+ const isRetry = retryCount > 0 || effectiveEvent.data?.isRetry === true;
126
+ let requestedTier = isRetry
127
+ ? "medium"
128
+ : undefined;
129
+ // `routine.morning_routine_initial` is non-configurable, so plan presets
130
+ // and operator pins on `routine.morning_routine` are silently ignored
131
+ // here without explicit inheritance — Max5/Max20/Team/Enterprise users
132
+ // pinned to Sonnet would still run Opus on the initial variant whenever
133
+ // `yesterday.md` is missing (cf. plan-presets.ts and process-key.ts §
134
+ // morning_routine_initial). Mirror morning_routine's configured tier
135
+ // onto the initial run when available.
136
+ if (!isRetry && promptKey === "routine.morning_routine_initial") {
137
+ const inheritedTier = this.inferConfiguredMorningRoutineTier();
138
+ if (inheritedTier) {
139
+ requestedTier = inheritedTier;
140
+ }
141
+ }
142
+ logger.info({ hasYesterday, promptKey, roadmapStale: roadmapStaleBeforeMorning, isRetry, retryCount, requestedTier: requestedTier ?? "default" }, "Morning routine prompt selected");
143
+ // B2 fix + ROUTINE_DATA_ACQUISITION_DESIGN.md Phase 4 / D2 race fix:
144
+ // flip `morningRoutineInProgress=true` BEFORE the pre-pass fires so
145
+ // hourly_check can't squeeze through the cold-start window
146
+ // (`hourlyCheck.trigger` skips when `isMorningRoutineActive()`
147
+ // returns true). The original B2 fix already widened the flag to
148
+ // span the whole retry chain; the pre-pass shifts the boundary
149
+ // earlier so the same guard covers context build + binding resolve
150
+ // + Haiku fetcher cold-start, all of which precede the executor.
151
+ // The flag is reset in `finally` regardless of whether the pre-pass,
152
+ // context build, or main session throws — so a partial-failure path
153
+ // cannot leave the flag stuck `true` and starve hourly_check forever.
154
+ this.setMorningRoutineInProgress(true);
155
+ let result;
156
+ try {
157
+ // ROUTINE_DATA_ACQUISITION_DESIGN.md Phase 4 / D2 — fire the
158
+ // pre-pass fetcher session BEFORE we build the parent's context.
159
+ // The fetcher POSTs to `/api/observations` so the parent's
160
+ // `pending=true` reads see the fresh rows; the rendered
161
+ // `<fetch_report>` block is grafted into `event.data.fetchReportBlock`
162
+ // so ContextBuilder injects it verbatim into the parent prompt. The
163
+ // runner never throws — failures surface as
164
+ // `<fetch_report status="failed">` and the parent routine
165
+ // continues with whatever observations the rest of the plan
166
+ // produced (cf. runner doc-comment, design §11 R5).
167
+ //
168
+ // Retry runs (`isRetry === true`) skip the pre-pass: by the time we
169
+ // retry, observations posted by the original attempt are still
170
+ // available (TTL is days; retries are minutes apart), and Haiku
171
+ // tokens spent re-confirming the same window inflate the retry
172
+ // cost cap the user explicitly set. The morning_routine_initial
173
+ // path shares morning_routine's plan via the runner's automatic
174
+ // key derivation, so no special-casing is required there.
175
+ const fetchPrepass = isRetry
176
+ ? null
177
+ : await this.fetchWindowRunner.run(effectiveEvent, promptKey);
178
+ if (fetchPrepass) {
179
+ effectiveEvent = {
180
+ ...effectiveEvent,
181
+ data: {
182
+ ...effectiveEvent.data,
183
+ fetchReportBlock: fetchPrepass.block,
184
+ },
185
+ };
186
+ }
187
+ const context = await this.contextBuilder.build(effectiveEvent);
188
+ const binding = this.agentRouter.resolveBinding(effectiveEvent, {
189
+ processKey: promptKey,
190
+ ...(requestedTier ? { requestedTier } : {}),
191
+ });
192
+ const reassemblePrompt = (bid) => this.prompt.assemble(promptKey, promptKey, bid);
193
+ const prompt = reassemblePrompt(binding.main.backendId);
194
+ result = await this.errorRouter.executeWithRetry(() => this.agentRouter.execute({
195
+ prompt,
196
+ context,
197
+ event: effectiveEvent,
198
+ processKey: promptKey,
199
+ preResolvedBinding: binding,
200
+ reassemblePrompt,
201
+ ...(requestedTier ? { requestedTier } : {}),
202
+ }), effectiveEvent);
203
+ }
204
+ finally {
205
+ this.setMorningRoutineInProgress(false);
206
+ if (lockId && this.todayWriteLock) {
207
+ this.todayWriteLock.release(lockId);
208
+ }
209
+ }
210
+ await this.resultProcessor.processResult(result, effectiveEvent);
211
+ // Post-morning-routine: verify today.md was generated, retry if not.
212
+ // This catches agent failures that don't throw (e.g., early stop, context
213
+ // building succeeded but the PUT /api/context/today call was skipped).
214
+ //
215
+ // Distinguish the two failure modes so the operator can tell from the log
216
+ // whether the agent skipped the write entirely vs. wrote with the wrong
217
+ // agent-day date. Pre-fix, both paths logged the same "does not exist"
218
+ // string and the wrong-date case looked indistinguishable from a hard
219
+ // crash, masking the date-confusion root cause.
220
+ const todayMdState = this.diagnoseTodayMdState();
221
+ if (todayMdState.kind !== "fresh") {
222
+ logger.warn({
223
+ eventType: effectiveEvent.type,
224
+ isError: result.isError,
225
+ numTurns: result.numTurns,
226
+ todayMdState: todayMdState.kind,
227
+ ...(todayMdState.kind === "wrong_date"
228
+ ? {
229
+ writtenDate: todayMdState.writtenDate,
230
+ expectedAgentDay: todayMdState.expectedAgentDay,
231
+ }
232
+ : {}),
233
+ }, todayMdState.kind === "missing"
234
+ ? "Morning routine completed but today.md does not exist — scheduling retry"
235
+ : "Morning routine completed but today.md has wrong agent-day date — scheduling retry");
236
+ this.scheduleMorningRetry(effectiveEvent);
237
+ }
238
+ else {
239
+ if (effectiveEvent.data?.deferPostMorningCatchupsUntilStartupReady === true) {
240
+ logger.info({ eventType: effectiveEvent.type, source: effectiveEvent.source }, "Deferring post-morning catchups until startup messaging is ready");
241
+ }
242
+ else {
243
+ await this.emitPostMorningCatchups(effectiveEvent);
244
+ }
245
+ const todayRefreshFlush = flushPendingTodayRefresh(this.db);
246
+ if (todayRefreshFlush.hadPending) {
247
+ logger.info({ scheduled: todayRefreshFlush.scheduled }, "Flushed pending today_refresh after morning routine");
248
+ }
249
+ }
250
+ // Post-morning-routine: trigger roadmap refresh if stale
251
+ if (roadmapStaleBeforeMorning) {
252
+ if (effectiveEvent.data?.deferPostMorningCatchupsUntilStartupReady === true) {
253
+ logger.info({ eventType: effectiveEvent.type, source: effectiveEvent.source }, "Deferring roadmap_refresh until startup messaging is ready");
254
+ }
255
+ else {
256
+ this.emitRoadmapRefresh("post_morning_routine");
257
+ }
258
+ }
259
+ }
260
+ /**
261
+ * Read the operator-or-preset configured tier for `routine.morning_routine`
262
+ * so the non-configurable `morning_routine_initial` variant can mirror it.
263
+ * Returns null when no row exists (fresh install before the setup wizard
264
+ * applies a preset) or when the pinned model isn't in the registry — the
265
+ * caller falls back to the router's default tier in those cases.
266
+ */
267
+ inferConfiguredMorningRoutineTier() {
268
+ try {
269
+ const row = this.db
270
+ .prepare("SELECT main_backend, main_model FROM process_backend_config WHERE process_key = 'routine.morning_routine'")
271
+ .get();
272
+ if (!row || !isBackendId(row.main_backend))
273
+ return null;
274
+ return findRegisteredModel(row.main_backend, row.main_model)?.tier ?? null;
275
+ }
276
+ catch (err) {
277
+ logger.debug({ err }, "Failed to infer morning_routine configured tier");
278
+ return null;
279
+ }
280
+ }
281
+ async emitPostMorningCatchups(event) {
282
+ const queuedRoutines = Array.isArray(event.data?.postCatchupRoutines)
283
+ ? event.data.postCatchupRoutines.filter((value) => typeof value === "string")
284
+ : [];
285
+ for (const routine of queuedRoutines) {
286
+ logger.info({ routine }, "Emitting deferred post-morning catchup routine");
287
+ await this.eventBus.put({
288
+ ...createEvent({
289
+ type: `routine.${routine}`,
290
+ source: "post_morning_catchup",
291
+ priority: EventPriority.HIGH,
292
+ }),
293
+ routine,
294
+ });
295
+ }
296
+ if (event.data?.postCatchupHourlyCheck === true) {
297
+ logger.info("Triggering deferred hourly_check after morning catchup");
298
+ await this.triggerHourlyCheck("post_morning_catchup");
299
+ }
300
+ }
301
+ /**
302
+ * Schedule a retry of the morning routine when today.md wasn't generated.
303
+ *
304
+ * Uses the existing agent_schedule → ScheduleWatcher path rather than
305
+ * re-enqueuing on the EventBus directly. Benefits:
306
+ * 1. Retry persists across daemon restarts.
307
+ * 2. Shares the same Opus cost-limit and concurrency gates.
308
+ * 3. Back-off delay is enforced by scheduled_for timestamp.
309
+ *
310
+ * Retry policy: exponential back-off (5 min → 10 min → 15 min), max 3
311
+ * attempts. After the 3rd failure, send a critical notification to
312
+ * the user and stop retrying.
313
+ *
314
+ * Retry count is tracked via `event.data.retryCount` on the RoutineEvent.
315
+ * On the first failure the count comes from the cron-fired RoutineEvent
316
+ * (undefined → 0). On subsequent failures handleMorningRoutineRetry
317
+ * synthesizes a new RoutineEvent carrying the previous count from the
318
+ * wake task's taskContext, so the chain propagates through a single
319
+ * code path: event.data.retryCount → +1 → task_context.retryCount
320
+ * → next event.data.retryCount → ...
321
+ *
322
+ * Dedup protects against pathological cases:
323
+ * - M1: another retry is already pending/running → skip
324
+ */
325
+ scheduleMorningRetry(event) {
326
+ const previousCount = Number(event.data?.retryCount ?? 0);
327
+ const retryCount = previousCount + 1;
328
+ const MAX_RETRIES = 3;
329
+ // Preserve the original cron morning_routine correlationId through
330
+ // the chain if present. On the first call this is the cron event's
331
+ // own id. On later calls it's propagated via event.correlationId
332
+ // (which handleMorningRoutineRetry sets from taskCtx).
333
+ const originalCorrelationId = event.data?.originalCorrelationId ??
334
+ event.correlationId;
335
+ if (retryCount > MAX_RETRIES) {
336
+ logger.error({
337
+ retryCount: previousCount,
338
+ maxRetries: MAX_RETRIES,
339
+ originalCorrelationId,
340
+ }, "Morning routine retry exhausted — sending critical notification");
341
+ void this.notificationMgr
342
+ .send(`⚠️ Morning routine failed to generate today.md after ${MAX_RETRIES} attempts. Please regenerate manually from the dashboard.`, event, { category: "critical", priority: "critical" })
343
+ .catch((err) => {
344
+ logger.error({ err }, "Failed to send morning-routine-retry-exhausted notification");
345
+ });
346
+ return;
347
+ }
348
+ // Exponential back-off: 5 / 10 / 15 minutes
349
+ const delayMinutes = retryCount * 5;
350
+ const retryTime = new Date(Date.now() + delayMinutes * 60 * 1000);
351
+ const scheduledFor = formatSqliteDatetime(retryTime);
352
+ // Encode the retry state in task_context so the wake agent (via
353
+ // executeScheduledTask → handleMorningRoutineRetry) can propagate
354
+ // retryCount into the synthesized RoutineEvent's event.data.
355
+ // `importance: "low"` keeps the retry out of roadmap.md — the
356
+ // originating morning_routine is already tracked elsewhere.
357
+ const taskContext = JSON.stringify({
358
+ routine: "morning_routine",
359
+ retryCount,
360
+ originalCorrelationId,
361
+ source: typeof event.data?.queuedSource === "string" ? event.data.queuedSource : event.source,
362
+ postCatchupRoutines: Array.isArray(event.data?.postCatchupRoutines)
363
+ ? event.data.postCatchupRoutines
364
+ : [],
365
+ postCatchupHourlyCheck: event.data?.postCatchupHourlyCheck === true,
366
+ importance: "low",
367
+ });
368
+ // M1: dedup + INSERT in a single transaction so two concurrent
369
+ // retry schedulers cannot both race past the dedup check and both
370
+ // insert new rows. better-sqlite3 is synchronous so the transaction
371
+ // callback runs atomically relative to any other DB access from
372
+ // this process.
373
+ //
374
+ // Dedup checks for 'pending' only — not 'running' — because the
375
+ // retry chain legitimately calls this method while the current
376
+ // wake task is still in 'running' state (handleMorningRoutineRetry
377
+ // → executeMorningRoutine → this). Including 'running' would break chain
378
+ // continuation.
379
+ const insertRetryTxn = this.db.transaction(() => {
380
+ // C5 fix: dedup on task_context.routine, not task_description prefix.
381
+ // Both `scheduleMorningRetry` (here) and `isMorningRoutineActive`
382
+ // (above) now use the same JSON-path check, so the detection path
383
+ // doesn't depend on the human-readable description string.
384
+ const existing = this.db
385
+ .prepare(`SELECT id FROM agent_schedule
386
+ WHERE task_type = 'wake'
387
+ AND status = 'pending'
388
+ AND json_extract(task_context, '$.routine') = 'morning_routine'
389
+ LIMIT 1`)
390
+ .get();
391
+ if (existing) {
392
+ return { inserted: false, existingId: existing.id };
393
+ }
394
+ this.db
395
+ .prepare(`INSERT INTO agent_schedule
396
+ (scheduled_for, task_type, task_description, task_context, correlation_id, model, status)
397
+ VALUES (?, 'wake', ?, ?, ?, NULL, 'pending')`)
398
+ .run(scheduledFor, `Morning routine retry (attempt ${retryCount}/${MAX_RETRIES}). Generate today.md per the morning_routine flow.`, taskContext, originalCorrelationId);
399
+ return { inserted: true };
400
+ });
401
+ try {
402
+ const outcome = insertRetryTxn();
403
+ if (!outcome.inserted) {
404
+ logger.info({
405
+ existingScheduleId: outcome.existingId,
406
+ retryCount,
407
+ originalCorrelationId,
408
+ }, "Morning routine retry dedup — another pending retry already exists");
409
+ return;
410
+ }
411
+ logger.info({
412
+ retryCount,
413
+ delayMinutes,
414
+ scheduledFor,
415
+ originalCorrelationId,
416
+ // Retries always fall back to the medium tier (Sonnet) per the
417
+ // cost-cap fix in executeMorningRoutine — surface that explicitly
418
+ // in the schedule log so the operator can confirm the
419
+ // downgrade happened without grepping the next agent-execute line.
420
+ plannedTier: "medium",
421
+ plannedTierReason: "morning_routine_retry_cost_cap",
422
+ }, "Morning routine retry scheduled (will run on Sonnet)");
423
+ // Route the INSERT through the shared roadmap-refresh gate.
424
+ // `importance:"low"` short-circuits the trigger — the morning
425
+ // routine is already represented elsewhere — but going through
426
+ // the helper keeps all five INSERT call-sites on one path.
427
+ maybeTriggerRoadmapRefresh({ scheduledFor, taskContext: { importance: "low" } }, (src) => this.emitRoadmapRefresh(src), "morning_retry");
428
+ }
429
+ catch (err) {
430
+ logger.error({ err, retryCount }, "Failed to schedule morning routine retry");
431
+ }
432
+ }
433
+ }
434
+ //# sourceMappingURL=dispatcher-morning-routine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher-morning-routine.js","sourceRoot":"","sources":["../../src/core/dispatcher-morning-routine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * `PromptAssembler` — composes the dispatcher's per-turn prompt and
3
+ * owns the inbound-attachment lifecycle (staging into the session dir,
4
+ * transcribing audio via local Whisper, and rendering the
5
+ * "[Attached files]" prompt block).
6
+ *
7
+ * Extracted from `core/dispatcher.ts` as part of phase D-2 of
8
+ * `docs/design/appendices/file-split-plan.md`. Pattern B (stateful
9
+ * coordinator): the assembler owns its own logic but borrows live
10
+ * references / accessors for state that the dispatcher continues to
11
+ * own (the `activeTurnTokens` Map, the lazily-injected
12
+ * `AttachmentStore`, and the lazily-injected `VoiceTranscriber`).
13
+ *
14
+ * Dispatcher entry points served:
15
+ * - every `dispatch.*` path that needs a task-flow prompt
16
+ * (`runStage2Triage`, `executeMorningRoutine`,
17
+ * `executeScheduledTask`, `executeDefault`, `handleMessage`);
18
+ * - `handleMessage` for the inbound attachment lifecycle
19
+ * (`stageInboundAttachments` → `transcribeAttachments` →
20
+ * `buildAttachmentPromptBlock`);
21
+ * - `validateAttachmentTurnToken` (public on `EventDispatcher`,
22
+ * reads the live `activeTurnTokens` Map this assembler issues
23
+ * into via `issueAttachmentTurnToken`).
24
+ *
25
+ * Shared-state references held:
26
+ * - `activeTurnTokens: Map<string, number>` — live reference,
27
+ * mutated in place via `issueAttachmentTurnToken` /
28
+ * `releaseAttachmentTurnToken`. The dispatcher's
29
+ * `validateAttachmentTurnToken` reads the same map.
30
+ * - `getAttachmentStore` / `getVoiceTranscriber` — getter callbacks
31
+ * because both are set lazily by `index.ts` after the dispatcher
32
+ * is constructed. The assembler must read the *current* value at
33
+ * each call, not capture it once.
34
+ */
35
+ import type Database from "better-sqlite3";
36
+ import type { MessageEvent } from "@aitne/shared";
37
+ import type { AgentConfig } from "../config.js";
38
+ import type { AttachmentStore, StoreAttachmentRow } from "../services/attachments/store.js";
39
+ import type { VoiceTranscriber, VoiceTranscriptionResult } from "../services/voice/transcriber.js";
40
+ import type { GetTaskFlow } from "./dispatcher-types.js";
41
+ export interface PromptAssemblerDeps {
42
+ db: Database.Database;
43
+ config: AgentConfig;
44
+ getTaskFlow: GetTaskFlow;
45
+ /** Live reference to the dispatcher's in-flight turn-token map. */
46
+ activeTurnTokens: Map<string, number>;
47
+ /**
48
+ * Accessor for the lazily-injected AttachmentStore. Returns null
49
+ * before `EventDispatcher.setAttachmentStore` has been called or in
50
+ * tests that don't wire the store.
51
+ */
52
+ getAttachmentStore: () => AttachmentStore | null;
53
+ /**
54
+ * Accessor for the lazily-injected VoiceTranscriber. Returns null
55
+ * when the local-Whisper layer is not wired.
56
+ */
57
+ getVoiceTranscriber: () => VoiceTranscriber | null;
58
+ }
59
+ export declare class PromptAssembler {
60
+ private readonly db;
61
+ private readonly config;
62
+ private readonly getTaskFlow;
63
+ private readonly activeTurnTokens;
64
+ private readonly getAttachmentStore;
65
+ private readonly getVoiceTranscriber;
66
+ constructor(deps: PromptAssemblerDeps);
67
+ /**
68
+ * B-007 §5.8 — compose the final prompt by loading the task-flow
69
+ * template and appending the vault policy-files block (rules/*.md,
70
+ * routines/<cadence>.md, custom routine file, etc.). Centralised
71
+ * here so every dispatch path sees the same policy bundle.
72
+ */
73
+ assemble(eventType: string, processKey: string, backendId?: string, flags?: Record<string, unknown>): string;
74
+ /**
75
+ * Policy-file prompt assembly must not fall back to `<dataDir>/context`
76
+ * while degraded. Reactive sessions still run so the user can repair the
77
+ * vault, but the prompt must not silently inject stale rulebooks from a
78
+ * legacy location.
79
+ */
80
+ private getPromptPolicyContextDir;
81
+ /** Internal — issue a turn token bound to a session. Cleared by
82
+ * `releaseAttachmentTurnToken` in a `finally`. */
83
+ issueAttachmentTurnToken(sessionId: number): string;
84
+ releaseAttachmentTurnToken(token: string): void;
85
+ /**
86
+ * Stage inbound attachments into `<sessionDir>/_attachments/` via
87
+ * hard-link (or copy on EXDEV). Returns the rows that were actually
88
+ * staged — callers feed these into the prompt-block builder.
89
+ */
90
+ stageInboundAttachments(event: MessageEvent, sessionDir: string | undefined): StoreAttachmentRow[];
91
+ /**
92
+ * Run local-Whisper transcription on every audio attachment in `rows`.
93
+ * Cached transcripts are returned without re-running inference. Returns
94
+ * an empty map when the transcriber is unset, when no rows are audio,
95
+ * or when every transcription failed — callers always render the path
96
+ * even if the transcript is missing.
97
+ */
98
+ transcribeAttachments(rows: StoreAttachmentRow[]): Promise<Map<string, VoiceTranscriptionResult>>;
99
+ /**
100
+ * Compose the "[Attached files]" prompt block that the dispatcher
101
+ * appends to the task-flow body for turns with inbound attachments.
102
+ * Kept on the assembler (not in prompts.ts) because the attachment
103
+ * rows are local state for this turn only.
104
+ */
105
+ buildAttachmentPromptBlock(rows: StoreAttachmentRow[], transcripts?: Map<string, VoiceTranscriptionResult>): string;
106
+ }
107
+ //# sourceMappingURL=dispatcher-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher-prompt.d.ts","sourceRoot":"","sources":["../../src/core/dispatcher-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAUhD,OAAO,KAAK,EACV,eAAe,EACf,kBAAkB,EACnB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EACV,gBAAgB,EAChB,wBAAwB,EACzB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKzD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC;IACtB,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,WAAW,CAAC;IACzB,mEAAmE;IACnE,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC;;;;OAIG;IACH,kBAAkB,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;IACjD;;;OAGG;IACH,mBAAmB,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;CACpD;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;IACvC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAsB;IACvD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA+B;IAClE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgC;gBAExD,IAAI,EAAE,mBAAmB;IASrC;;;;;OAKG;IACH,QAAQ,CACN,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM;IAqCT;;;;;OAKG;IACH,OAAO,CAAC,yBAAyB;IAOjC;uDACmD;IACnD,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAMnD,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/C;;;;OAIG;IACH,uBAAuB,CACrB,KAAK,EAAE,YAAY,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,kBAAkB,EAAE;IAkBvB;;;;;;OAMG;IACG,qBAAqB,CACzB,IAAI,EAAE,kBAAkB,EAAE,GACzB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;IAuBjD;;;;;OAKG;IACH,0BAA0B,CACxB,IAAI,EAAE,kBAAkB,EAAE,EAC1B,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,wBAAwB,CAAC,GAClD,MAAM;CAsDV"}