@jungjaehoon/mama-os 0.77.0 → 0.79.0

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 (338) hide show
  1. package/CHANGELOG.md +446 -0
  2. package/README.md +59 -30
  3. package/db/migrations/002-model-runs-parent-index.sql +11 -0
  4. package/db/migrations/003-upkeep-settled.sql +15 -0
  5. package/dist/api/google-read-actions.d.ts +30 -0
  6. package/dist/api/google-read-actions.d.ts.map +1 -0
  7. package/dist/api/google-read-actions.js +256 -0
  8. package/dist/api/google-read-actions.js.map +1 -0
  9. package/dist/api/record-actions.d.ts.map +1 -1
  10. package/dist/api/record-actions.js +23 -5
  11. package/dist/api/record-actions.js.map +1 -1
  12. package/dist/api/report-handler.d.ts +0 -6
  13. package/dist/api/report-handler.d.ts.map +1 -1
  14. package/dist/api/report-handler.js +0 -54
  15. package/dist/api/report-handler.js.map +1 -1
  16. package/dist/api/security-events.d.ts +1 -1
  17. package/dist/api/security-events.d.ts.map +1 -1
  18. package/dist/api/security-events.js +10 -1
  19. package/dist/api/security-events.js.map +1 -1
  20. package/dist/api/source-actions.d.ts +9 -0
  21. package/dist/api/source-actions.d.ts.map +1 -1
  22. package/dist/api/source-actions.js +49 -0
  23. package/dist/api/source-actions.js.map +1 -1
  24. package/dist/api/viewer-request-security.d.ts +14 -1
  25. package/dist/api/viewer-request-security.d.ts.map +1 -1
  26. package/dist/api/viewer-request-security.js +26 -3
  27. package/dist/api/viewer-request-security.js.map +1 -1
  28. package/dist/api/viewer-server.d.ts +8 -0
  29. package/dist/api/viewer-server.d.ts.map +1 -1
  30. package/dist/api/viewer-server.js +66 -0
  31. package/dist/api/viewer-server.js.map +1 -1
  32. package/dist/api/work-actions.d.ts +19 -2
  33. package/dist/api/work-actions.d.ts.map +1 -1
  34. package/dist/api/work-actions.js +42 -2
  35. package/dist/api/work-actions.js.map +1 -1
  36. package/dist/cli/commands/backfill.d.ts.map +1 -1
  37. package/dist/cli/commands/backfill.js +1 -0
  38. package/dist/cli/commands/backfill.js.map +1 -1
  39. package/dist/cli/commands/backup.d.ts +14 -0
  40. package/dist/cli/commands/backup.d.ts.map +1 -0
  41. package/dist/cli/commands/backup.js +174 -0
  42. package/dist/cli/commands/backup.js.map +1 -0
  43. package/dist/cli/commands/control.d.ts +44 -0
  44. package/dist/cli/commands/control.d.ts.map +1 -0
  45. package/dist/cli/commands/control.js +354 -0
  46. package/dist/cli/commands/control.js.map +1 -0
  47. package/dist/cli/commands/daemon.d.ts +60 -3
  48. package/dist/cli/commands/daemon.d.ts.map +1 -1
  49. package/dist/cli/commands/daemon.js +454 -78
  50. package/dist/cli/commands/daemon.js.map +1 -1
  51. package/dist/cli/commands/doctor.d.ts +13 -0
  52. package/dist/cli/commands/doctor.d.ts.map +1 -0
  53. package/dist/cli/commands/doctor.js +216 -0
  54. package/dist/cli/commands/doctor.js.map +1 -0
  55. package/dist/cli/commands/init-questions.d.ts +98 -0
  56. package/dist/cli/commands/init-questions.d.ts.map +1 -0
  57. package/dist/cli/commands/init-questions.js +420 -0
  58. package/dist/cli/commands/init-questions.js.map +1 -0
  59. package/dist/cli/commands/init-section.d.ts +13 -0
  60. package/dist/cli/commands/init-section.d.ts.map +1 -0
  61. package/dist/cli/commands/init-section.js +375 -0
  62. package/dist/cli/commands/init-section.js.map +1 -0
  63. package/dist/cli/commands/init.d.ts +56 -2
  64. package/dist/cli/commands/init.d.ts.map +1 -1
  65. package/dist/cli/commands/init.js +562 -350
  66. package/dist/cli/commands/init.js.map +1 -1
  67. package/dist/cli/commands/logs.d.ts +9 -0
  68. package/dist/cli/commands/logs.d.ts.map +1 -0
  69. package/dist/cli/commands/logs.js +109 -0
  70. package/dist/cli/commands/logs.js.map +1 -0
  71. package/dist/cli/commands/node-move.d.ts +20 -0
  72. package/dist/cli/commands/node-move.d.ts.map +1 -0
  73. package/dist/cli/commands/node-move.js +289 -0
  74. package/dist/cli/commands/node-move.js.map +1 -0
  75. package/dist/cli/commands/register-owner.d.ts +3 -0
  76. package/dist/cli/commands/register-owner.d.ts.map +1 -1
  77. package/dist/cli/commands/register-owner.js +14 -0
  78. package/dist/cli/commands/register-owner.js.map +1 -1
  79. package/dist/cli/commands/restore.d.ts +16 -0
  80. package/dist/cli/commands/restore.d.ts.map +1 -0
  81. package/dist/cli/commands/restore.js +137 -0
  82. package/dist/cli/commands/restore.js.map +1 -0
  83. package/dist/cli/commands/service.d.ts +44 -0
  84. package/dist/cli/commands/service.d.ts.map +1 -0
  85. package/dist/cli/commands/service.js +306 -0
  86. package/dist/cli/commands/service.js.map +1 -0
  87. package/dist/cli/commands/tools.d.ts +1 -1
  88. package/dist/cli/commands/tools.d.ts.map +1 -1
  89. package/dist/cli/commands/tools.js +8 -3
  90. package/dist/cli/commands/tools.js.map +1 -1
  91. package/dist/cli/commands/uninstall.d.ts +7 -0
  92. package/dist/cli/commands/uninstall.d.ts.map +1 -0
  93. package/dist/cli/commands/uninstall.js +335 -0
  94. package/dist/cli/commands/uninstall.js.map +1 -0
  95. package/dist/cli/commands/update.d.ts +59 -0
  96. package/dist/cli/commands/update.d.ts.map +1 -0
  97. package/dist/cli/commands/update.js +948 -0
  98. package/dist/cli/commands/update.js.map +1 -0
  99. package/dist/cli/index.js +74 -23
  100. package/dist/cli/index.js.map +1 -1
  101. package/dist/cli/launch-files.d.ts +119 -0
  102. package/dist/cli/launch-files.d.ts.map +1 -1
  103. package/dist/cli/launch-files.js +324 -15
  104. package/dist/cli/launch-files.js.map +1 -1
  105. package/dist/cli/prompt.d.ts +2 -1
  106. package/dist/cli/prompt.d.ts.map +1 -1
  107. package/dist/cli/prompt.js +5 -2
  108. package/dist/cli/prompt.js.map +1 -1
  109. package/dist/cli/runtime/backend-probe.d.ts +34 -0
  110. package/dist/cli/runtime/backend-probe.d.ts.map +1 -0
  111. package/dist/cli/runtime/backend-probe.js +83 -0
  112. package/dist/cli/runtime/backend-probe.js.map +1 -0
  113. package/dist/cli/runtime/backup.d.ts +130 -0
  114. package/dist/cli/runtime/backup.d.ts.map +1 -0
  115. package/dist/cli/runtime/backup.js +592 -0
  116. package/dist/cli/runtime/backup.js.map +1 -0
  117. package/dist/cli/runtime/daemon-control.d.ts +169 -0
  118. package/dist/cli/runtime/daemon-control.d.ts.map +1 -0
  119. package/dist/cli/runtime/daemon-control.js +441 -0
  120. package/dist/cli/runtime/daemon-control.js.map +1 -0
  121. package/dist/cli/runtime/default-roots.d.ts +27 -0
  122. package/dist/cli/runtime/default-roots.d.ts.map +1 -0
  123. package/dist/cli/runtime/default-roots.js +61 -0
  124. package/dist/cli/runtime/default-roots.js.map +1 -0
  125. package/dist/cli/runtime/diagnostics.d.ts +68 -0
  126. package/dist/cli/runtime/diagnostics.d.ts.map +1 -0
  127. package/dist/cli/runtime/diagnostics.js +1139 -0
  128. package/dist/cli/runtime/diagnostics.js.map +1 -0
  129. package/dist/cli/runtime/init-state.d.ts +20 -0
  130. package/dist/cli/runtime/init-state.d.ts.map +1 -0
  131. package/dist/cli/runtime/init-state.js +51 -0
  132. package/dist/cli/runtime/init-state.js.map +1 -0
  133. package/dist/cli/runtime/install-kind.d.ts +54 -0
  134. package/dist/cli/runtime/install-kind.d.ts.map +1 -0
  135. package/dist/cli/runtime/install-kind.js +164 -0
  136. package/dist/cli/runtime/install-kind.js.map +1 -0
  137. package/dist/cli/runtime/node-move-journal.d.ts +44 -0
  138. package/dist/cli/runtime/node-move-journal.d.ts.map +1 -0
  139. package/dist/cli/runtime/node-move-journal.js +48 -0
  140. package/dist/cli/runtime/node-move-journal.js.map +1 -0
  141. package/dist/cli/runtime/release-check.d.ts +94 -0
  142. package/dist/cli/runtime/release-check.d.ts.map +1 -0
  143. package/dist/cli/runtime/release-check.js +316 -0
  144. package/dist/cli/runtime/release-check.js.map +1 -0
  145. package/dist/cli/runtime/restore.d.ts +98 -0
  146. package/dist/cli/runtime/restore.d.ts.map +1 -0
  147. package/dist/cli/runtime/restore.js +756 -0
  148. package/dist/cli/runtime/restore.js.map +1 -0
  149. package/dist/cli/runtime/setup-page.d.ts +57 -0
  150. package/dist/cli/runtime/setup-page.d.ts.map +1 -0
  151. package/dist/cli/runtime/setup-page.js +266 -0
  152. package/dist/cli/runtime/setup-page.js.map +1 -0
  153. package/dist/cli/runtime/source-tools.d.ts +60 -0
  154. package/dist/cli/runtime/source-tools.d.ts.map +1 -0
  155. package/dist/cli/runtime/source-tools.js +170 -0
  156. package/dist/cli/runtime/source-tools.js.map +1 -0
  157. package/dist/cli/runtime/systemd-service.d.ts +13 -0
  158. package/dist/cli/runtime/systemd-service.d.ts.map +1 -0
  159. package/dist/cli/runtime/systemd-service.js +32 -0
  160. package/dist/cli/runtime/systemd-service.js.map +1 -0
  161. package/dist/cli/runtime/telegram-pairing.d.ts +73 -0
  162. package/dist/cli/runtime/telegram-pairing.d.ts.map +1 -0
  163. package/dist/cli/runtime/telegram-pairing.js +184 -0
  164. package/dist/cli/runtime/telegram-pairing.js.map +1 -0
  165. package/dist/cli/runtime/update-journal.d.ts +135 -0
  166. package/dist/cli/runtime/update-journal.d.ts.map +1 -0
  167. package/dist/cli/runtime/update-journal.js +144 -0
  168. package/dist/cli/runtime/update-journal.js.map +1 -0
  169. package/dist/cli/runtime/update-progress.d.ts +95 -0
  170. package/dist/cli/runtime/update-progress.d.ts.map +1 -0
  171. package/dist/cli/runtime/update-progress.js +154 -0
  172. package/dist/cli/runtime/update-progress.js.map +1 -0
  173. package/dist/cli/runtime/update-request.d.ts +58 -0
  174. package/dist/cli/runtime/update-request.d.ts.map +1 -0
  175. package/dist/cli/runtime/update-request.js +172 -0
  176. package/dist/cli/runtime/update-request.js.map +1 -0
  177. package/dist/connectors/calendar/index.d.ts +5 -1
  178. package/dist/connectors/calendar/index.d.ts.map +1 -1
  179. package/dist/connectors/calendar/index.js +19 -28
  180. package/dist/connectors/calendar/index.js.map +1 -1
  181. package/dist/connectors/chatwork/index.d.ts.map +1 -1
  182. package/dist/connectors/chatwork/index.js +7 -3
  183. package/dist/connectors/chatwork/index.js.map +1 -1
  184. package/dist/connectors/config-loader.d.ts +7 -1
  185. package/dist/connectors/config-loader.d.ts.map +1 -1
  186. package/dist/connectors/config-loader.js +11 -2
  187. package/dist/connectors/config-loader.js.map +1 -1
  188. package/dist/connectors/discord/index.d.ts.map +1 -1
  189. package/dist/connectors/discord/index.js +4 -3
  190. package/dist/connectors/discord/index.js.map +1 -1
  191. package/dist/connectors/drive/index.d.ts.map +1 -1
  192. package/dist/connectors/drive/index.js +15 -13
  193. package/dist/connectors/drive/index.js.map +1 -1
  194. package/dist/connectors/framework/gws-utils.d.ts +62 -0
  195. package/dist/connectors/framework/gws-utils.d.ts.map +1 -1
  196. package/dist/connectors/framework/gws-utils.js +133 -3
  197. package/dist/connectors/framework/gws-utils.js.map +1 -1
  198. package/dist/connectors/framework/polling-scheduler.d.ts.map +1 -1
  199. package/dist/connectors/framework/polling-scheduler.js +12 -11
  200. package/dist/connectors/framework/polling-scheduler.js.map +1 -1
  201. package/dist/connectors/framework/types.d.ts +24 -0
  202. package/dist/connectors/framework/types.d.ts.map +1 -1
  203. package/dist/connectors/framework/types.js +9 -0
  204. package/dist/connectors/framework/types.js.map +1 -1
  205. package/dist/connectors/gmail/index.d.ts.map +1 -1
  206. package/dist/connectors/gmail/index.js +14 -13
  207. package/dist/connectors/gmail/index.js.map +1 -1
  208. package/dist/connectors/imessage/index.d.ts.map +1 -1
  209. package/dist/connectors/imessage/index.js +2 -1
  210. package/dist/connectors/imessage/index.js.map +1 -1
  211. package/dist/connectors/index.d.ts +4 -4
  212. package/dist/connectors/index.d.ts.map +1 -1
  213. package/dist/connectors/index.js +4 -10
  214. package/dist/connectors/index.js.map +1 -1
  215. package/dist/connectors/notion/index.d.ts.map +1 -1
  216. package/dist/connectors/notion/index.js +5 -3
  217. package/dist/connectors/notion/index.js.map +1 -1
  218. package/dist/connectors/sheets/index.d.ts.map +1 -1
  219. package/dist/connectors/sheets/index.js +17 -12
  220. package/dist/connectors/sheets/index.js.map +1 -1
  221. package/dist/connectors/slack/index.d.ts.map +1 -1
  222. package/dist/connectors/slack/index.js +9 -3
  223. package/dist/connectors/slack/index.js.map +1 -1
  224. package/dist/connectors/telegram/index.d.ts.map +1 -1
  225. package/dist/connectors/telegram/index.js +4 -3
  226. package/dist/connectors/telegram/index.js.map +1 -1
  227. package/dist/connectors/trello/index.d.ts +1 -1
  228. package/dist/connectors/trello/index.d.ts.map +1 -1
  229. package/dist/connectors/trello/index.js +10 -5
  230. package/dist/connectors/trello/index.js.map +1 -1
  231. package/dist/gateways/discord.d.ts +6 -0
  232. package/dist/gateways/discord.d.ts.map +1 -1
  233. package/dist/gateways/discord.js +38 -27
  234. package/dist/gateways/discord.js.map +1 -1
  235. package/dist/gateways/slack.d.ts +6 -0
  236. package/dist/gateways/slack.d.ts.map +1 -1
  237. package/dist/gateways/slack.js +38 -27
  238. package/dist/gateways/slack.js.map +1 -1
  239. package/dist/gateways/telegram-commands.d.ts +124 -0
  240. package/dist/gateways/telegram-commands.d.ts.map +1 -0
  241. package/dist/gateways/telegram-commands.js +131 -0
  242. package/dist/gateways/telegram-commands.js.map +1 -0
  243. package/dist/gateways/telegram.d.ts +20 -0
  244. package/dist/gateways/telegram.d.ts.map +1 -1
  245. package/dist/gateways/telegram.js +138 -34
  246. package/dist/gateways/telegram.js.map +1 -1
  247. package/dist/gateways/turn-contract.d.ts +11 -0
  248. package/dist/gateways/turn-contract.d.ts.map +1 -1
  249. package/dist/runtime/action-surface.d.ts +1 -1
  250. package/dist/runtime/action-surface.d.ts.map +1 -1
  251. package/dist/runtime/action-surface.js +10 -0
  252. package/dist/runtime/action-surface.js.map +1 -1
  253. package/dist/runtime/backend-security.d.ts +26 -2
  254. package/dist/runtime/backend-security.d.ts.map +1 -1
  255. package/dist/runtime/backend-security.js +63 -10
  256. package/dist/runtime/backend-security.js.map +1 -1
  257. package/dist/runtime/config.d.ts +3 -0
  258. package/dist/runtime/config.d.ts.map +1 -1
  259. package/dist/runtime/config.js +4 -4
  260. package/dist/runtime/config.js.map +1 -1
  261. package/dist/runtime/connectors.d.ts +21 -1
  262. package/dist/runtime/connectors.d.ts.map +1 -1
  263. package/dist/runtime/connectors.js +28 -17
  264. package/dist/runtime/connectors.js.map +1 -1
  265. package/dist/runtime/core-db.d.ts.map +1 -1
  266. package/dist/runtime/core-db.js +2 -0
  267. package/dist/runtime/core-db.js.map +1 -1
  268. package/dist/runtime/member-paths.d.ts +15 -7
  269. package/dist/runtime/member-paths.d.ts.map +1 -1
  270. package/dist/runtime/member-paths.js +64 -12
  271. package/dist/runtime/member-paths.js.map +1 -1
  272. package/dist/runtime/member-session.d.ts +4 -0
  273. package/dist/runtime/member-session.d.ts.map +1 -1
  274. package/dist/runtime/member-session.js +23 -2
  275. package/dist/runtime/member-session.js.map +1 -1
  276. package/dist/runtime/native-session.d.ts +2 -0
  277. package/dist/runtime/native-session.d.ts.map +1 -1
  278. package/dist/runtime/native-session.js +1 -0
  279. package/dist/runtime/native-session.js.map +1 -1
  280. package/dist/runtime/owner-runtime.d.ts +63 -1
  281. package/dist/runtime/owner-runtime.d.ts.map +1 -1
  282. package/dist/runtime/owner-runtime.js +180 -3
  283. package/dist/runtime/owner-runtime.js.map +1 -1
  284. package/dist/runtime/owner-system-prompt.d.ts.map +1 -1
  285. package/dist/runtime/owner-system-prompt.js +6 -5
  286. package/dist/runtime/owner-system-prompt.js.map +1 -1
  287. package/dist/runtime/principal-access.d.ts +2 -0
  288. package/dist/runtime/principal-access.d.ts.map +1 -1
  289. package/dist/runtime/principal-access.js +7 -0
  290. package/dist/runtime/principal-access.js.map +1 -1
  291. package/dist/runtime/principal-sessions.d.ts.map +1 -1
  292. package/dist/runtime/principal-sessions.js +8 -1
  293. package/dist/runtime/principal-sessions.js.map +1 -1
  294. package/dist/runtime/record-orders.d.ts.map +1 -1
  295. package/dist/runtime/record-orders.js +3 -29
  296. package/dist/runtime/record-orders.js.map +1 -1
  297. package/dist/runtime/report-scheduler.d.ts +9 -2
  298. package/dist/runtime/report-scheduler.d.ts.map +1 -1
  299. package/dist/runtime/report-scheduler.js +37 -10
  300. package/dist/runtime/report-scheduler.js.map +1 -1
  301. package/dist/runtime/rotating-log.d.ts +44 -0
  302. package/dist/runtime/rotating-log.d.ts.map +1 -0
  303. package/dist/runtime/rotating-log.js +124 -0
  304. package/dist/runtime/rotating-log.js.map +1 -0
  305. package/dist/runtime/stimulus-delivery.d.ts +3 -0
  306. package/dist/runtime/stimulus-delivery.d.ts.map +1 -1
  307. package/dist/runtime/stimulus-delivery.js +19 -3
  308. package/dist/runtime/stimulus-delivery.js.map +1 -1
  309. package/dist/runtime/tools-paths.d.ts +7 -0
  310. package/dist/runtime/tools-paths.d.ts.map +1 -1
  311. package/dist/runtime/tools-paths.js +11 -5
  312. package/dist/runtime/tools-paths.js.map +1 -1
  313. package/dist/runtime/upkeep-close.d.ts +17 -0
  314. package/dist/runtime/upkeep-close.d.ts.map +1 -0
  315. package/dist/runtime/upkeep-close.js +127 -0
  316. package/dist/runtime/upkeep-close.js.map +1 -0
  317. package/dist/runtime/upkeep-orders.d.ts +124 -0
  318. package/dist/runtime/upkeep-orders.d.ts.map +1 -0
  319. package/dist/runtime/upkeep-orders.js +310 -0
  320. package/dist/runtime/upkeep-orders.js.map +1 -0
  321. package/dist/runtime/upkeep-reconcile.d.ts +25 -0
  322. package/dist/runtime/upkeep-reconcile.d.ts.map +1 -0
  323. package/dist/runtime/upkeep-reconcile.js +231 -0
  324. package/dist/runtime/upkeep-reconcile.js.map +1 -0
  325. package/dist/runtime/upkeep-writeback.d.ts +22 -0
  326. package/dist/runtime/upkeep-writeback.d.ts.map +1 -0
  327. package/dist/runtime/upkeep-writeback.js +203 -0
  328. package/dist/runtime/upkeep-writeback.js.map +1 -0
  329. package/dist/tools/document-toolkit.d.ts +2 -0
  330. package/dist/tools/document-toolkit.d.ts.map +1 -1
  331. package/dist/tools/document-toolkit.js +2 -0
  332. package/dist/tools/document-toolkit.js.map +1 -1
  333. package/dist/utils/untrusted-content.d.ts.map +1 -1
  334. package/dist/utils/untrusted-content.js +8 -1
  335. package/dist/utils/untrusted-content.js.map +1 -1
  336. package/install/install.sh +457 -0
  337. package/install/node.json +9 -0
  338. package/package.json +5 -5
@@ -1,13 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.OWNER_PRINCIPAL_ID = void 0;
3
+ exports.MESSENGER_SECRETS = exports.BOOT_STAGES = exports.StopDuringBootError = exports.OWNER_PRINCIPAL_ID = void 0;
4
+ exports.pathsOverlap = pathsOverlap;
4
5
  exports.validateDeliveryRoutes = validateDeliveryRoutes;
6
+ exports.upkeepCloseSince = upkeepCloseSince;
7
+ exports.pathsFor = pathsFor;
5
8
  exports.ensureDaemonIsolation = ensureDaemonIsolation;
6
9
  exports.loggedOwnerIntake = loggedOwnerIntake;
7
10
  exports.bootDaemon = bootDaemon;
8
11
  exports.runDaemon = runDaemon;
9
- exports.daemonStatus = daemonStatus;
10
- exports.requestDaemonStop = requestDaemonStop;
12
+ exports.installControlPaths = installControlPaths;
13
+ exports.askDaemon = askDaemon;
14
+ exports.launchdService = launchdService;
15
+ exports.bootoutLaunchdService = bootoutLaunchdService;
11
16
  const mama_core_1 = require("@jungjaehoon/mama-core");
12
17
  const document_toolkit_js_1 = require("../../tools/document-toolkit.js");
13
18
  const node_fs_1 = require("node:fs");
@@ -24,6 +29,7 @@ const action_surface_js_1 = require("../../runtime/action-surface.js");
24
29
  const connectors_js_1 = require("../../runtime/connectors.js");
25
30
  const config_js_1 = require("../../runtime/config.js");
26
31
  const model_cache_js_1 = require("../../runtime/model-cache.js");
32
+ const member_paths_js_1 = require("../../runtime/member-paths.js");
27
33
  const backend_security_js_1 = require("../../runtime/backend-security.js");
28
34
  const session_credential_js_1 = require("../../runtime/session-credential.js");
29
35
  const action_mcp_config_js_1 = require("../runtime/action-mcp-config.js");
@@ -35,10 +41,16 @@ const viewer_data_js_1 = require("../../api/viewer-data.js");
35
41
  const owner_authority_js_1 = require("../../runtime/owner-authority.js");
36
42
  const principal_access_js_1 = require("../../runtime/principal-access.js");
37
43
  const report_scheduler_js_1 = require("../../runtime/report-scheduler.js");
44
+ const release_check_js_1 = require("../runtime/release-check.js");
45
+ const install_kind_js_1 = require("../runtime/install-kind.js");
46
+ const update_request_js_1 = require("../runtime/update-request.js");
47
+ const systemd_service_js_1 = require("../runtime/systemd-service.js");
38
48
  const wiki_read_js_1 = require("../../wiki/wiki-read.js");
39
49
  const timezone_js_1 = require("../../runtime/timezone.js");
40
50
  const security_events_js_1 = require("../../api/security-events.js");
41
51
  const egress_proxy_js_1 = require("../../runtime/egress-proxy.js");
52
+ const daemon_control_js_1 = require("../runtime/daemon-control.js");
53
+ const update_journal_js_1 = require("../runtime/update-journal.js");
42
54
  exports.OWNER_PRINCIPAL_ID = 'owner';
43
55
  const OWNER_AGENT_ID = 'owner-agent';
44
56
  const OWNER_MEMORY_SCOPES = (0, action_surface_js_1.ownerMemoryScopes)(exports.OWNER_PRINCIPAL_ID, index_js_1.LOADABLE_CONNECTORS);
@@ -56,9 +68,49 @@ function errorName(error) {
56
68
  const name = error.name.trim() !== '' ? error.name : 'Error';
57
69
  return `${name}: ${error.message}`;
58
70
  }
59
- function stage(logger, name) {
71
+ class StopDuringBootError extends Error {
72
+ constructor() {
73
+ super('Stopped during boot');
74
+ this.name = 'StopDuringBootError';
75
+ }
76
+ }
77
+ exports.StopDuringBootError = StopDuringBootError;
78
+ /** Every stage boot logs as `boot stage=<name>`; `mama doctor` reports only these names. */
79
+ exports.BOOT_STAGES = [
80
+ 'config',
81
+ 'isolation',
82
+ 'owner_runtime',
83
+ 'viewer',
84
+ 'replay',
85
+ 'connectors',
86
+ 'messengers',
87
+ 'telegram',
88
+ 'telegram:disabled',
89
+ 'discord',
90
+ 'slack',
91
+ 'report_scheduler',
92
+ 'report_scheduler:route_unavailable',
93
+ 'personal_job_scheduler',
94
+ ];
95
+ function stage(logger, name, signal) {
96
+ if (signal?.aborted)
97
+ throw new StopDuringBootError();
60
98
  logger.info(`boot stage=${name}`);
61
99
  }
100
+ /** The auth.env names each messenger needs before it starts; `mama doctor` checks the same. */
101
+ exports.MESSENGER_SECRETS = {
102
+ telegram: ['MAMA_TELEGRAM_TOKEN'],
103
+ discord: ['MAMA_DISCORD_TOKEN'],
104
+ slack: ['MAMA_SLACK_TOKEN', 'MAMA_SLACK_APP_TOKEN'],
105
+ };
106
+ function messengerSecrets(messenger) {
107
+ return exports.MESSENGER_SECRETS[messenger].map((name) => {
108
+ const value = process.env[name];
109
+ if (!value?.trim())
110
+ throw new Error(`${name} is required`);
111
+ return value;
112
+ });
113
+ }
62
114
  function stageFailed(logger, name, error) {
63
115
  logger.error(`boot stage=${name} failed error=${errorName(error)}`);
64
116
  }
@@ -107,6 +159,24 @@ function validateDeliveryRoutes(config) {
107
159
  throw new Error(`delivery.${purpose} targets ${messenger}, which is disabled or has no allowlisted owner channel`);
108
160
  }
109
161
  }
162
+ /**
163
+ * The instant closing summaries start being ordered: written once, at the first start that runs
164
+ * upkeep, so items closed before it are left to the one-time summary script.
165
+ */
166
+ function upkeepCloseSince(path, now = Date.now()) {
167
+ if ((0, node_fs_1.existsSync)(path)) {
168
+ const stored = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8')).closeSince;
169
+ if (typeof stored !== 'number' || !Number.isSafeInteger(stored))
170
+ throw new Error(`${path} has no closeSince epoch`);
171
+ return stored;
172
+ }
173
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true });
174
+ // Written once and read at every start: a partial file would stop each boot, so it appears whole.
175
+ const temporary = `${path}.${process.pid}.tmp`;
176
+ (0, node_fs_1.writeFileSync)(temporary, `${JSON.stringify({ closeSince: now }, null, 2)}\n`, { mode: 0o600 });
177
+ (0, node_fs_1.renameSync)(temporary, path);
178
+ return now;
179
+ }
110
180
  function pathsFor(configPath, config) {
111
181
  const mamaRoot = (0, node_path_1.dirname)(configPath);
112
182
  const runtimeRoot = (0, node_path_1.join)(mamaRoot, 'runtime');
@@ -129,6 +199,11 @@ function pathsFor(configPath, config) {
129
199
  if (pathsOverlap(workspaceDir, runtimeRoot)) {
130
200
  throw new Error(`agent.codex_cwd must not contain or sit inside ${runtimeRoot}`);
131
201
  }
202
+ // Backups are denied to the agents; a workspace there could not be read by its own agent.
203
+ const backupsRoot = (0, node_path_1.join)(mamaRoot, 'backups');
204
+ if (pathsOverlap(workspaceDir, backupsRoot)) {
205
+ throw new Error(`agent.codex_cwd must not contain or sit inside ${backupsRoot}`);
206
+ }
132
207
  return {
133
208
  mamaRoot,
134
209
  runtimeRoot,
@@ -148,6 +223,8 @@ function pathsFor(configPath, config) {
148
223
  /** Create only the native-process isolation files; never remove product state. */
149
224
  function ensureDaemonIsolation(options) {
150
225
  const { config, paths } = options;
226
+ // Member temp data under the old machine-wide parents is no longer under any session deny.
227
+ (0, member_paths_js_1.removeRetiredMemberTemp)();
151
228
  (0, node_fs_1.mkdirSync)(paths.downloadsDir, { recursive: true, mode: 0o700 });
152
229
  (0, node_fs_1.mkdirSync)(paths.workspaceDir, { recursive: true });
153
230
  const gitDir = (0, node_path_1.join)(paths.workspaceDir, '.git');
@@ -196,7 +273,15 @@ async function stopOne(logger, name, stop, errors) {
196
273
  }
197
274
  /** Open the W1 owner subject, then activate producers in their dependency order. */
198
275
  async function bootDaemon(options = {}) {
199
- const logger = options.logger ?? defaultLogger;
276
+ const baseLogger = options.logger ?? defaultLogger;
277
+ let lastError = null;
278
+ const logger = {
279
+ info: (line) => baseLogger.info(line),
280
+ error: (line) => {
281
+ lastError = { at: Date.now(), line: line.slice(0, 500) };
282
+ baseLogger.error(line);
283
+ },
284
+ };
200
285
  const dependencies = options.dependencies ?? {};
201
286
  const configPath = options.configPath ?? (0, config_js_1.defaultConfigPath)(options.home ?? (0, node_os_1.homedir)());
202
287
  let config;
@@ -208,15 +293,94 @@ async function bootDaemon(options = {}) {
208
293
  let gateway = null;
209
294
  const gateways = new Map();
210
295
  let reportScheduler;
296
+ let releaseCheck;
211
297
  let egressProxy;
212
298
  let stopped = false;
213
299
  let deliveryReady = options.mode === 'replay';
214
300
  const startedAt = Date.now();
215
301
  let currentStage = 'config';
216
- const stopResources = async () => {
302
+ let booted = false;
303
+ const command = options.command ?? (options.mode === 'replay' ? 'replay' : 'daemon');
304
+ // The install's one process: taken before anything opens the database or rewrites settings.
305
+ const control = (0, daemon_control_js_1.daemonControlPaths)((0, node_path_1.dirname)(configPath));
306
+ const lock = (0, daemon_control_js_1.acquireInstallLock)(control, command);
307
+ let controlServer = null;
308
+ // One per open drain connection; while any is open, nothing new is claimed.
309
+ const holds = new Set();
310
+ // An update candidate's hold, from boot until the updater's `release` (update.md, steps 13-14).
311
+ let updateHold = null;
312
+ const claimsHeld = () => holds.size > 0 || updateHold !== null;
313
+ // What a drain waits for: this process's own work. Runs and claims of an earlier boot are left
314
+ // out: their process is gone, and such a claim (a dispatch the crash cut off) resolves only in
315
+ // reconciliation, which the hold suspends. A claim is stamped when it is taken and renewed only
316
+ // by this process's deliveries, so its time tells which boot owns it. A claimed input whose
317
+ // native dispatch is uncertain is parked until reconciliation too; nothing starts from it.
318
+ const drainCounts = () => {
217
319
  if (stopped)
218
- return;
219
- stopped = true;
320
+ throw new Error('MAMA is stopping');
321
+ if (!owner)
322
+ return { running: 0, claimed: 0, turns: 0 };
323
+ const adapter = owner.database.adapter;
324
+ const { running, unclaimed } = adapter
325
+ .prepare(`SELECT COUNT(*) AS running,
326
+ COUNT(CASE WHEN parent_model_run_id IS NULL
327
+ AND json_extract(input_refs_json, '$.nativeInputId') IS NULL
328
+ THEN 1 END) AS unclaimed
329
+ FROM model_runs WHERE status = 'running' AND created_at >= ?`)
330
+ .get(startedAt);
331
+ const { claimed } = adapter
332
+ .prepare(`SELECT COUNT(*) AS claimed FROM mailbox_inputs m
333
+ WHERE m.status = 'claimed' AND m.claimed_at >= ?
334
+ AND NOT EXISTS (SELECT 1 FROM native_input_deliveries n
335
+ WHERE n.input_id = m.id AND n.state = 'uncertain')`)
336
+ .get(startedAt);
337
+ // A turn either runs a claimed input or is a top-level run without one (a probe); subagent
338
+ // runs belong to their parent's turn.
339
+ return { running, claimed, turns: claimed + unclaimed };
340
+ };
341
+ const statusReport = async () => {
342
+ const report = {
343
+ pid: process.pid,
344
+ command,
345
+ version: (0, package_version_js_1.resolvePackageVersion)(),
346
+ // What `mama update --node` confirms after it moves the managed Node.
347
+ node: process.version,
348
+ startedAt,
349
+ stage: stopped ? 'stopping' : booted ? 'running' : currentStage,
350
+ lastError,
351
+ drain: holds.size === 0
352
+ ? null
353
+ : { holds: holds.size, since: Math.min(...[...holds].map((hold) => hold.since)) },
354
+ update: updateHold,
355
+ };
356
+ // Stores close during a stop; what it reports then is that it is stopping.
357
+ if (stopped)
358
+ return report;
359
+ if (owner) {
360
+ const adapter = owner.database.adapter;
361
+ report.runningTurns = adapter
362
+ .prepare(`SELECT COALESCE(json_extract(input_refs_json, '$.sessionKey'), 'unknown') AS session,
363
+ COUNT(*) AS count
364
+ FROM model_runs WHERE status = 'running' AND created_at >= ? GROUP BY 1 ORDER BY 1`)
365
+ .all(startedAt);
366
+ report.inputs = adapter
367
+ .prepare(`SELECT status, COUNT(*) AS count FROM mailbox_inputs
368
+ WHERE status IN ('pending', 'claimed') GROUP BY status ORDER BY status`)
369
+ .all();
370
+ }
371
+ if (connectors) {
372
+ const connectorRuntime = connectors;
373
+ const health = await connectorRuntime.registry.healthCheckAll();
374
+ report.connectors = connectorRuntime.enabledConnectorNames.map((name) => ({
375
+ name,
376
+ healthy: health[name]?.healthy === true,
377
+ lastPoll: health[name]?.lastPollTime?.toISOString() ?? null,
378
+ error: health[name]?.error ?? null,
379
+ }));
380
+ }
381
+ return report;
382
+ };
383
+ const stopOwned = async () => {
220
384
  const errors = [];
221
385
  owner?.beginStop?.();
222
386
  // Quiesce every producer before awaiting any one of them. Transports and the database
@@ -224,6 +388,8 @@ async function bootDaemon(options = {}) {
224
388
  const producerStops = [...gateways].map(([name, active]) => stopOne(logger, `${name}_intake`, () => active.beginStop?.(), errors));
225
389
  if (reportScheduler)
226
390
  producerStops.push(stopOne(logger, 'report_scheduler', () => reportScheduler.stop(), errors));
391
+ if (releaseCheck)
392
+ producerStops.push(stopOne(logger, 'release_check', () => releaseCheck.stop(), errors));
227
393
  if (connectors)
228
394
  producerStops.push(stopOne(logger, 'connectors', () => connectors.stop(), errors));
229
395
  if (viewer)
@@ -243,22 +409,101 @@ async function bootDaemon(options = {}) {
243
409
  if (errors.length > 0)
244
410
  throw new AggregateError(errors, 'Daemon shutdown failed');
245
411
  };
412
+ const stopResources = async () => {
413
+ if (stopped)
414
+ return;
415
+ stopped = true;
416
+ let closed = false;
417
+ try {
418
+ await stopOwned();
419
+ closed = true;
420
+ }
421
+ finally {
422
+ if (controlServer) {
423
+ await controlServer
424
+ .close()
425
+ .catch((error) => logger.error(`shutdown stage=control failed error=${errorName(error)}`));
426
+ }
427
+ // Released only once every store closed: after a failed stop something may still run, so
428
+ // the install stays held until this process ends and the operating system lets it go.
429
+ if (closed)
430
+ lock.release();
431
+ }
432
+ };
246
433
  try {
247
434
  currentStage = 'config';
248
- stage(logger, 'config');
435
+ // A half-switched install is neither the old one nor the restored one.
436
+ (0, daemon_control_js_1.refuseWhileRestoring)((0, node_path_1.dirname)(configPath));
437
+ controlServer = await (0, daemon_control_js_1.startDaemonControl)(control, {
438
+ status: statusReport,
439
+ stop: () => {
440
+ if (!options.requestStop)
441
+ throw new Error(`${command} stops when it finishes`);
442
+ options.requestStop();
443
+ },
444
+ probe: async () => {
445
+ if (!booted || stopped || !owner)
446
+ throw new Error('MAMA has not finished starting');
447
+ // After the drain is idle the stop comes next, and it would cut the probe's turn off.
448
+ if (holds.size > 0)
449
+ throw new Error('A drain holds new work; probe after the restart');
450
+ try {
451
+ const probed = await owner.probeBackend();
452
+ logger.info(`backend probe: ${config.agent.backend} answered in ${probed.ms} ms (model run ${probed.modelRunId ?? 'none'})`);
453
+ return { backend: config.agent.backend, model: config.agent.model, ...probed };
454
+ }
455
+ catch (error) {
456
+ // The turn's own failure is wrapped by the dispatch layer; its causes name what failed.
457
+ const reason = (0, stimulus_delivery_js_1.stimulusFailureReason)(error);
458
+ logger.error(`backend probe: ${config.agent.backend} failed: ${reason}`);
459
+ throw new Error(reason);
460
+ }
461
+ },
462
+ hold: () => {
463
+ const at = drainCounts();
464
+ // Each turn that may still run takes at most max_turn_ms; nothing new starts.
465
+ const perTurn = owner ? config.agent.max_turn_ms : 0;
466
+ const hold = { since: Date.now() };
467
+ holds.add(hold);
468
+ logger.info(`drain: claims held running=${at.running} claimed=${at.claimed}`);
469
+ return {
470
+ boundMs: at.turns * perTurn + 60_000,
471
+ counts: () => {
472
+ const { running, claimed } = drainCounts();
473
+ return { running, claimed };
474
+ },
475
+ release: () => {
476
+ holds.delete(hold);
477
+ logger.info('drain: claims released');
478
+ },
479
+ };
480
+ },
481
+ release: () => {
482
+ const held = updateHold !== null;
483
+ if (held)
484
+ logger.info(`update: ${updateHold.to} confirmed; claims released`);
485
+ updateHold = null;
486
+ return { held };
487
+ },
488
+ });
489
+ if (options.signal?.aborted)
490
+ throw new StopDuringBootError();
249
491
  config = options.config ?? (0, config_js_1.loadConfig)({ path: configPath, home: options.home });
492
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(config.logging.file), { recursive: true });
493
+ // The service manager redirects only what comes before this point, to the crash log. From here
494
+ // on the daemon's own log holds everything, from the first boot stage on.
495
+ options.captureOutput?.(config.logging.file);
496
+ stage(logger, 'config', options.signal);
497
+ // An update candidate runs nothing until the updater has seen it boot and confirmed it: its
498
+ // boot recovery claims nothing, so a rollback finds every input as it was (update.md step 13).
499
+ const update = (0, update_journal_js_1.readUpdateJournal)((0, update_journal_js_1.updatePaths)((0, node_path_1.dirname)(configPath)).journal);
500
+ if (update?.phase === 'candidate') {
501
+ updateHold = { since: Date.now(), to: update.to };
502
+ logger.info(`update: booted as the candidate for ${update.to}; claims held until confirmed`);
503
+ }
250
504
  logger.info(`document toolkit: ${(0, document_toolkit_js_1.readDocumentToolkit)(config.tools_root, (error) => logger.error(error.message)) ? 'present' : 'absent (run mama tools install)'}`);
251
505
  const timeZone = (0, timezone_js_1.createTimeZoneSetting)(config.timezone);
252
506
  validateDeliveryRoutes(config);
253
- // launchd may have created the redirected log already; preserve its contents and tighten it.
254
- (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(config.logging.file), { recursive: true });
255
- const logDescriptor = (0, node_fs_1.openSync)(config.logging.file, 'a', 0o600);
256
- try {
257
- (0, node_fs_1.fchmodSync)(logDescriptor, 0o600);
258
- }
259
- finally {
260
- (0, node_fs_1.closeSync)(logDescriptor);
261
- }
262
507
  (0, model_cache_js_1.declareModelCache)();
263
508
  paths = pathsFor(configPath, config);
264
509
  currentStage = 'isolation';
@@ -268,7 +513,7 @@ async function bootDaemon(options = {}) {
268
513
  paths,
269
514
  ...(options.mcpServerPath ? { mcpServerPath: options.mcpServerPath } : {}),
270
515
  });
271
- stage(logger, 'isolation');
516
+ stage(logger, 'isolation', options.signal);
272
517
  currentStage = 'owner_runtime';
273
518
  const ownerPolicyProvider = (0, owner_policy_js_1.createOwnerPolicyProvider)(paths.mamaRoot);
274
519
  const ownerPolicy = ownerPolicyProvider();
@@ -445,6 +690,7 @@ async function bootDaemon(options = {}) {
445
690
  },
446
691
  ownerPolicyProvider,
447
692
  deliveryReady: () => deliveryReady,
693
+ claimsHeld,
448
694
  onPersonalJobError: (error) => logger.error(`personal job scheduler failed reason=${(0, stimulus_delivery_js_1.stimulusFailureReason)(error)}`),
449
695
  ...(options.mode === 'replay'
450
696
  ? {}
@@ -458,7 +704,9 @@ async function bootDaemon(options = {}) {
458
704
  const selected = gateways.get(sourceForRef(row.stimulusId));
459
705
  if (!selected)
460
706
  throw new Error('Owner messenger is not available for an interrupted response');
461
- await selected.recoverPendingResponses();
707
+ // Only this message: a sweep would tell messages still in flight that they were
708
+ // interrupted, and their answers would then be dropped as already delivered.
709
+ await selected.recoverResponse(row.stimulusId);
462
710
  },
463
711
  closeUncertain: {
464
712
  ownerAnswered: (row) => gateways.get(sourceForRef(row.stimulusId))?.answered(row.stimulusId) ?? false,
@@ -491,10 +739,26 @@ async function bootDaemon(options = {}) {
491
739
  else
492
740
  logger.info(line);
493
741
  },
742
+ // Upkeep orders write the ledger from live state: a replay places none, but still serves an
743
+ // order a live run left in the mailbox.
744
+ upkeep: {
745
+ closeSince: upkeepCloseSince((0, node_path_1.join)(paths.runtimeRoot, 'upkeep-state.json')),
746
+ fullReportHours: () => config.reports.full_report_hours,
747
+ scan: options.mode !== 'replay',
748
+ onEvent: (event) => {
749
+ const line = `upkeep ${event.kind} ${event.type}${event.type === 'ordered'
750
+ ? ` order=${event.order} subjects=${event.subjects}`
751
+ : ` subject=${event.subject}`}${'attempt' in event ? ` attempt=${event.attempt}` : ''}${'reason' in event ? ` reason=${(0, stimulus_delivery_js_1.stimulusFailureReason)(event.reason)}` : ''}`;
752
+ if (event.type === 'lost')
753
+ logger.error(line);
754
+ else
755
+ logger.info(line);
756
+ },
757
+ },
494
758
  onStimulusDelivered: (row, modelRunId) => stimulusDelivered(logger, row, modelRunId),
495
759
  onStimulusFailed: (row, reason, modelRunId) => stimulusFailed(logger, row.kind ?? 'unknown', row.stimulusId, reason, modelRunId),
496
760
  });
497
- stage(logger, 'owner_runtime');
761
+ stage(logger, 'owner_runtime', options.signal);
498
762
  currentStage = 'viewer';
499
763
  const viewerFactory = dependencies.createViewerServer ?? viewer_server_js_1.createViewerServer;
500
764
  viewer = viewerFactory({
@@ -545,14 +809,14 @@ async function bootDaemon(options = {}) {
545
809
  });
546
810
  await viewer.start();
547
811
  logger.info(`viewer server listening on port=${String(viewer.port)}`);
548
- stage(logger, 'viewer');
812
+ stage(logger, 'viewer', options.signal);
549
813
  if (options.mode === 'replay') {
550
814
  logger.info('replay collectors: disabled');
551
815
  currentStage = 'replay';
552
816
  if (!options.replay)
553
817
  throw new Error('Replay mode requires a replay feeder');
554
818
  await options.replay({ config, paths, owner, logger, timeZone });
555
- stage(logger, 'replay');
819
+ stage(logger, 'replay', options.signal);
556
820
  return {
557
821
  config,
558
822
  paths,
@@ -589,16 +853,14 @@ async function bootDaemon(options = {}) {
589
853
  coreAdapter: owner.database?.adapter,
590
854
  acceptSourceDelta,
591
855
  });
592
- stage(logger, 'connectors');
856
+ stage(logger, 'connectors', options.signal);
593
857
  currentStage = 'messengers';
594
858
  const intake = loggedOwnerIntake(owner.intake, logger);
595
859
  const ledgerPath = paths.ownerMessageLedgerPath;
596
860
  messageLedger = new telegram_message_ledger_js_1.OwnerMessageLedger(ledgerPath, { log: (line) => logger.info(line) });
597
861
  const filesRoot = (0, node_path_1.join)(paths.workspaceDir, 'files');
598
862
  if (config.telegram.enabled) {
599
- const token = process.env.MAMA_TELEGRAM_TOKEN;
600
- if (!token?.trim())
601
- throw new Error('MAMA_TELEGRAM_TOKEN is required');
863
+ const [token] = messengerSecrets('telegram');
602
864
  const factory = dependencies.createTelegramGateway ?? ((options) => new telegram_js_1.TelegramGateway(options));
603
865
  gateway = factory({
604
866
  token,
@@ -612,6 +874,59 @@ async function bootDaemon(options = {}) {
612
874
  principalPaths: (principalId, room) => owner.principalPaths(principalId, room),
613
875
  onMemberSelection: (selection) => owner.completeMemberEnrollment(selection),
614
876
  recordMemberSelection: (exchange) => owner.recordHostExchange(exchange),
877
+ hostCommands: {
878
+ status: async (target, ownerView) => {
879
+ const report = await statusReport();
880
+ const adapter = owner.database.adapter;
881
+ // A group reads the reply, so a member's count there is that group's inputs only.
882
+ const waiting = (0, owner_runtime_js_1.countInputsNotDone)(adapter, ownerView
883
+ ? {}
884
+ : target.kind === 'common'
885
+ ? { principalId: exports.OWNER_PRINCIPAL_ID }
886
+ : {
887
+ principalId: target.principalId,
888
+ ...(target.room === undefined ? {} : { channel: target.room }),
889
+ });
890
+ const turns = (report.runningTurns ?? []);
891
+ const sources = (report.connectors ?? []);
892
+ return {
893
+ version: String(report.version),
894
+ since: (0, timezone_js_1.localNow)(startedAt, timeZone.get()),
895
+ conversationRunning: owner.turnRunning(target),
896
+ inputsWaiting: waiting,
897
+ ...(ownerView
898
+ ? {
899
+ owner: {
900
+ turnsRunning: turns.reduce((sum, turn) => sum + turn.count, 0),
901
+ sources: sources.map(({ name, healthy }) => ({ name, healthy })),
902
+ },
903
+ }
904
+ : {}),
905
+ };
906
+ },
907
+ startOver: (target) => owner.startOver(target),
908
+ stopTurn: (target) => owner.stopTurn(target),
909
+ update: async (origin) => {
910
+ const result = (0, update_request_js_1.requestUpdate)({
911
+ mamaRoot: paths.mamaRoot,
912
+ home: options.home ?? (0, node_os_1.homedir)(),
913
+ origin,
914
+ version: (0, package_version_js_1.resolvePackageVersion)(),
915
+ platform: process.platform,
916
+ run: systemd_service_js_1.defaultServiceRun,
917
+ uid: process.getuid(),
918
+ log: (line) => logger.info(line),
919
+ });
920
+ logger.info(`update requested result=${result.kind}`);
921
+ if (result.kind !== 'busy')
922
+ return result;
923
+ return {
924
+ kind: 'busy',
925
+ source: result.source,
926
+ since: result.since === null ? 'an unknown time' : (0, timezone_js_1.localNow)(result.since, timeZone.get()),
927
+ };
928
+ },
929
+ },
615
930
  config: {
616
931
  enabled: true,
617
932
  allowedChats: config.telegram.allowed_chats,
@@ -633,14 +948,12 @@ async function bootDaemon(options = {}) {
633
948
  });
634
949
  gateways.set('telegram', gateway);
635
950
  await gateway.start();
636
- stage(logger, 'telegram');
951
+ stage(logger, 'telegram', options.signal);
637
952
  }
638
953
  else
639
- stage(logger, 'telegram:disabled');
954
+ stage(logger, 'telegram:disabled', options.signal);
640
955
  if (config.discord?.enabled) {
641
- const token = process.env.MAMA_DISCORD_TOKEN;
642
- if (!token?.trim())
643
- throw new Error('MAMA_DISCORD_TOKEN is required');
956
+ const [token] = messengerSecrets('discord');
644
957
  const factory = dependencies.createDiscordGateway ?? ((options) => new discord_js_1.DiscordGateway(options));
645
958
  const active = factory({
646
959
  token,
@@ -662,15 +975,10 @@ async function bootDaemon(options = {}) {
662
975
  });
663
976
  gateways.set('discord', active);
664
977
  await active.start();
665
- stage(logger, 'discord');
978
+ stage(logger, 'discord', options.signal);
666
979
  }
667
980
  if (config.slack?.enabled) {
668
- const token = process.env.MAMA_SLACK_TOKEN;
669
- const appToken = process.env.MAMA_SLACK_APP_TOKEN;
670
- if (!token?.trim())
671
- throw new Error('MAMA_SLACK_TOKEN is required');
672
- if (!appToken?.trim())
673
- throw new Error('MAMA_SLACK_APP_TOKEN is required');
981
+ const [token, appToken] = messengerSecrets('slack');
674
982
  const factory = dependencies.createSlackGateway ?? ((options) => new slack_js_1.SlackGateway(options));
675
983
  const active = factory({
676
984
  token,
@@ -693,7 +1001,7 @@ async function bootDaemon(options = {}) {
693
1001
  });
694
1002
  gateways.set('slack', active);
695
1003
  await active.start();
696
- stage(logger, 'slack');
1004
+ stage(logger, 'slack', options.signal);
697
1005
  }
698
1006
  const reportRoute = config.delivery?.reports ?? 'telegram';
699
1007
  if (gateways.has(reportRoute)) {
@@ -717,25 +1025,40 @@ async function bootDaemon(options = {}) {
717
1025
  return receipt;
718
1026
  },
719
1027
  },
720
- hasPendingReport: () => Boolean(owner.database.adapter
721
- .prepare(`SELECT 1 FROM mailbox_inputs m LEFT JOIN native_input_deliveries n ON n.input_id = m.id WHERE m.principal_id = ? AND m.kind = 'scheduled' AND m.channel_key = 'schedule' AND m.status IN ('pending', 'claimed') AND (n.state IS NULL OR n.state != 'uncertain') LIMIT 1`)
722
- .get(exports.OWNER_PRINCIPAL_ID)),
1028
+ hasPendingReport: (report, hourKey) => Boolean(owner.database.adapter
1029
+ .prepare(`SELECT 1 FROM mailbox_inputs m LEFT JOIN native_input_deliveries n ON n.input_id = m.id WHERE m.principal_id = ? AND m.kind = 'scheduled' AND m.channel_key = 'schedule' AND m.status IN ('pending', 'claimed') AND (n.state IS NULL OR n.state != 'uncertain' OR (json_extract(m.payload_json, '$.report') = ? AND json_extract(m.payload_json, '$.hourKey') = ? AND EXISTS (SELECT 1 FROM native_turn_results r WHERE r.receipt_json = n.receipt_json AND r.principal_id = m.principal_id))) LIMIT 1`)
1030
+ .get(exports.OWNER_PRINCIPAL_ID, report, hourKey)),
723
1031
  sendToOwner: (text, key) => gateways.get(reportRoute).sendToOwner(text, key),
1032
+ // Every messenger keys outbound text the same way in the one shared ledger.
1033
+ retireUndelivered: (key) => messageLedger.retireUndelivered((0, telegram_message_ledger_js_1.outboundLedgerKey)(key)),
1034
+ log: (line) => logger.info(line),
724
1035
  onError: (error) => logger.error(`report scheduler failed reason=${(0, stimulus_delivery_js_1.stimulusFailureReason)(error)}`),
725
1036
  });
726
1037
  reportScheduler.start();
727
- stage(logger, 'report_scheduler');
1038
+ stage(logger, 'report_scheduler', options.signal);
728
1039
  }
729
1040
  else
730
- stage(logger, 'report_scheduler:route_unavailable');
1041
+ stage(logger, 'report_scheduler:route_unavailable', options.signal);
731
1042
  if (gateway?.sendMemberText) {
732
1043
  owner.personalJobs.start();
733
1044
  await owner.personalJobs
734
1045
  .tick()
735
1046
  .catch((error) => logger.error(`personal job scheduler failed reason=${(0, stimulus_delivery_js_1.stimulusFailureReason)(error)}`));
736
- stage(logger, 'personal_job_scheduler');
1047
+ stage(logger, 'personal_job_scheduler', options.signal);
737
1048
  }
1049
+ // Live only: a replay or backfill returned above, and never asks the registry.
1050
+ releaseCheck = (dependencies.createReleaseCheck ?? release_check_js_1.createReleaseCheck)({
1051
+ statePath: (0, release_check_js_1.releaseCheckPath)(paths.mamaRoot),
1052
+ current: (0, package_version_js_1.resolvePackageVersion)(),
1053
+ packageKind: (0, install_kind_js_1.packageKind)((0, install_kind_js_1.runningPackageRoot)()),
1054
+ npmPath: (0, release_check_js_1.npmBesideNode)(),
1055
+ timeZone,
1056
+ log: (line) => logger.info(line),
1057
+ onError: (error) => logger.error(`release check failed error=${errorName(error)}`),
1058
+ });
1059
+ releaseCheck.start();
738
1060
  deliveryReady = true;
1061
+ booted = true;
739
1062
  return {
740
1063
  config,
741
1064
  paths,
@@ -748,7 +1071,16 @@ async function bootDaemon(options = {}) {
748
1071
  };
749
1072
  }
750
1073
  catch (error) {
751
- stageFailed(logger, currentStage, error);
1074
+ if (error instanceof StopDuringBootError) {
1075
+ logger.info(`boot stopped before stage=${currentStage}`);
1076
+ }
1077
+ else {
1078
+ stageFailed(logger, currentStage, error);
1079
+ // A frozen error keeps its own shape; the stage is still in the log line above.
1080
+ if (error !== null && typeof error === 'object' && Object.isExtensible(error)) {
1081
+ Object.defineProperty(error, 'bootStage', { value: currentStage, enumerable: false });
1082
+ }
1083
+ }
752
1084
  try {
753
1085
  await stopResources();
754
1086
  }
@@ -758,48 +1090,92 @@ async function bootDaemon(options = {}) {
758
1090
  throw error;
759
1091
  }
760
1092
  }
761
- /** Foreground launchd entry: wait until SIGTERM/SIGINT, then close reverse-order stages. */
1093
+ /**
1094
+ * Foreground entry for launchd, systemd, Docker or a terminal. Signals are taken before boot, so a
1095
+ * stop during boot unwinds what has started; `mama stop` asks through the control socket.
1096
+ */
762
1097
  async function runDaemon(options = {}) {
763
- const daemon = await bootDaemon(options);
764
- await new Promise((resolve, reject) => {
765
- let finished = false;
766
- const finish = async () => {
767
- if (finished)
1098
+ const controller = new AbortController();
1099
+ // A stop asked for through `mama stop` exits 0; one by a signal exits 128 + its number, as an
1100
+ // unhandled signal would, so a service that restarts only after a failure brings back a daemon
1101
+ // that was killed (a service manager's own stop does not restart it whatever the code).
1102
+ let signalExit;
1103
+ const requestStop = () => controller.abort();
1104
+ const onSigterm = () => {
1105
+ signalExit = 143;
1106
+ controller.abort();
1107
+ };
1108
+ const onSigint = () => {
1109
+ signalExit = 130;
1110
+ controller.abort();
1111
+ };
1112
+ // Once each: a second signal falls to Node's default and ends the process at once, a force stop;
1113
+ // the operating system releases the install lock with it.
1114
+ process.once('SIGTERM', onSigterm);
1115
+ process.once('SIGINT', onSigint);
1116
+ try {
1117
+ let daemon;
1118
+ try {
1119
+ daemon = await bootDaemon({ ...options, signal: controller.signal, requestStop });
1120
+ }
1121
+ catch (error) {
1122
+ if (error instanceof StopDuringBootError) {
1123
+ if (signalExit !== undefined)
1124
+ process.exitCode = signalExit;
768
1125
  return;
769
- finished = true;
770
- try {
771
- await daemon.stop();
772
- resolve();
773
- }
774
- catch (error) {
775
- reject(error);
776
1126
  }
777
- };
778
- process.once('SIGTERM', () => void finish());
779
- process.once('SIGINT', () => void finish());
780
- });
1127
+ throw error;
1128
+ }
1129
+ if (!controller.signal.aborted) {
1130
+ await new Promise((resolve) => controller.signal.addEventListener('abort', () => resolve(), { once: true }));
1131
+ }
1132
+ await daemon.stop();
1133
+ if (signalExit !== undefined)
1134
+ process.exitCode = signalExit;
1135
+ }
1136
+ finally {
1137
+ process.off('SIGTERM', onSigterm);
1138
+ process.off('SIGINT', onSigint);
1139
+ }
781
1140
  }
782
- function launchdService(command) {
783
- const result = (0, node_child_process_1.spawnSync)('launchctl', [command, `gui/${process.getuid()}/com.mama.server`], {
1141
+ /** The install's control paths from its config path, as boot derives them. */
1142
+ function installControlPaths(options = {}) {
1143
+ const configPath = options.configPath ?? (0, config_js_1.defaultConfigPath)(options.home ?? (0, node_os_1.homedir)());
1144
+ return (0, daemon_control_js_1.daemonControlPaths)((0, node_path_1.dirname)(configPath));
1145
+ }
1146
+ async function askDaemon(op, options = {}) {
1147
+ return await (0, daemon_control_js_1.requestDaemonControl)(installControlPaths(options), op, options.timeoutMs);
1148
+ }
1149
+ /** macOS only: this user's LaunchAgent (`launchctl print` exits 113 when it is not loaded). */
1150
+ function launchdService() {
1151
+ if (process.platform !== 'darwin')
1152
+ return { loaded: false, pid: null };
1153
+ const result = (0, node_child_process_1.spawnSync)('launchctl', ['print', `gui/${process.getuid()}/com.mama.server`], {
784
1154
  encoding: 'utf8',
785
1155
  });
786
1156
  if (result.error)
787
1157
  throw result.error;
788
- // launchctl print exits 113 when this user's service is not registered.
789
- if (result.status !== 0 && !(command === 'print' && result.status === 113)) {
790
- throw Object.assign(new Error(`launchctl ${command} failed`), {
1158
+ if (result.status === 113)
1159
+ return { loaded: false, pid: null };
1160
+ if (result.status !== 0) {
1161
+ throw Object.assign(new Error('launchctl print failed'), {
791
1162
  code: `LAUNCHCTL_${result.status ?? result.signal ?? 'UNKNOWN'}`,
792
1163
  });
793
1164
  }
794
- return result;
795
- }
796
- function daemonStatus() {
797
- const result = launchdService('print');
798
- return result.status === 0 && /^\s*state = running\s*$/m.test(result.stdout)
799
- ? 'running'
800
- : 'stopped';
1165
+ const pid = /^\s*pid = (\d+)\s*$/m.exec(result.stdout)?.[1];
1166
+ return { loaded: true, pid: pid === undefined ? null : Number(pid) };
801
1167
  }
802
- function requestDaemonStop() {
803
- launchdService('bootout');
1168
+ /** Unload the LaunchAgent so KeepAlive cannot restart it; launchd sends the stop signal. */
1169
+ function bootoutLaunchdService() {
1170
+ const result = (0, node_child_process_1.spawnSync)('launchctl', ['bootout', `gui/${process.getuid()}/com.mama.server`], {
1171
+ encoding: 'utf8',
1172
+ });
1173
+ if (result.error)
1174
+ throw result.error;
1175
+ if (result.status !== 0) {
1176
+ throw Object.assign(new Error('launchctl bootout failed'), {
1177
+ code: `LAUNCHCTL_${result.status ?? result.signal ?? 'UNKNOWN'}`,
1178
+ });
1179
+ }
804
1180
  }
805
1181
  //# sourceMappingURL=daemon.js.map