@raidiant/notifai 11.7.1 → 12.0.0-beta.13

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 (409) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/NOTICE +16 -0
  3. package/THIRD_PARTY_NOTICES +725 -0
  4. package/bin/notifai.mjs +11420 -0
  5. package/data/install.ps1 +318 -0
  6. package/data/release.json +1 -0
  7. package/inventory.json +1 -0
  8. package/npm-adapter-files.json +52 -0
  9. package/package.json +14 -44
  10. package/README.md +0 -168
  11. package/dist/agent-update-notice.d.ts +0 -7
  12. package/dist/agent-update-notice.js +0 -77
  13. package/dist/agent-update-notice.js.map +0 -1
  14. package/dist/atomic-file.d.ts +0 -30
  15. package/dist/atomic-file.js +0 -148
  16. package/dist/atomic-file.js.map +0 -1
  17. package/dist/attendant-update.d.ts +0 -15
  18. package/dist/attendant-update.js +0 -96
  19. package/dist/attendant-update.js.map +0 -1
  20. package/dist/claude-wake.d.ts +0 -108
  21. package/dist/claude-wake.js +0 -369
  22. package/dist/claude-wake.js.map +0 -1
  23. package/dist/cli-bin.d.ts +0 -26
  24. package/dist/cli-bin.js +0 -220
  25. package/dist/cli-bin.js.map +0 -1
  26. package/dist/cli-contract.d.ts +0 -16
  27. package/dist/cli-contract.js +0 -27
  28. package/dist/cli-contract.js.map +0 -1
  29. package/dist/cli-release.d.ts +0 -36
  30. package/dist/cli-release.js +0 -85
  31. package/dist/cli-release.js.map +0 -1
  32. package/dist/client.d.ts +0 -109
  33. package/dist/client.js +0 -217
  34. package/dist/client.js.map +0 -1
  35. package/dist/codex-answer-control.d.ts +0 -14
  36. package/dist/codex-answer-control.js +0 -41
  37. package/dist/codex-answer-control.js.map +0 -1
  38. package/dist/codex-answer-presentation.d.ts +0 -18
  39. package/dist/codex-answer-presentation.js +0 -113
  40. package/dist/codex-answer-presentation.js.map +0 -1
  41. package/dist/codex-input-lifecycle.d.ts +0 -10
  42. package/dist/codex-input-lifecycle.js +0 -63
  43. package/dist/codex-input-lifecycle.js.map +0 -1
  44. package/dist/codex-native-control.d.ts +0 -13
  45. package/dist/codex-native-control.js +0 -120
  46. package/dist/codex-native-control.js.map +0 -1
  47. package/dist/codex-native-turn.d.ts +0 -37
  48. package/dist/codex-native-turn.js +0 -155
  49. package/dist/codex-native-turn.js.map +0 -1
  50. package/dist/codex-question-bindings.d.ts +0 -76
  51. package/dist/codex-question-bindings.js +0 -151
  52. package/dist/codex-question-bindings.js.map +0 -1
  53. package/dist/codex-queue-control.d.ts +0 -21
  54. package/dist/codex-queue-control.js +0 -59
  55. package/dist/codex-queue-control.js.map +0 -1
  56. package/dist/codex-tool-messages.d.ts +0 -7
  57. package/dist/codex-tool-messages.js +0 -126
  58. package/dist/codex-tool-messages.js.map +0 -1
  59. package/dist/codex-wake.d.ts +0 -58
  60. package/dist/codex-wake.js +0 -135
  61. package/dist/codex-wake.js.map +0 -1
  62. package/dist/command-session.d.ts +0 -14
  63. package/dist/command-session.js +0 -31
  64. package/dist/command-session.js.map +0 -1
  65. package/dist/commands-acknowledge.d.ts +0 -13
  66. package/dist/commands-acknowledge.js +0 -116
  67. package/dist/commands-acknowledge.js.map +0 -1
  68. package/dist/commands-agent-sessions.d.ts +0 -6
  69. package/dist/commands-agent-sessions.js +0 -51
  70. package/dist/commands-agent-sessions.js.map +0 -1
  71. package/dist/commands-ask.d.ts +0 -62
  72. package/dist/commands-ask.js +0 -503
  73. package/dist/commands-ask.js.map +0 -1
  74. package/dist/commands-auth.d.ts +0 -76
  75. package/dist/commands-auth.js +0 -421
  76. package/dist/commands-auth.js.map +0 -1
  77. package/dist/commands-close.d.ts +0 -6
  78. package/dist/commands-close.js +0 -273
  79. package/dist/commands-close.js.map +0 -1
  80. package/dist/commands-config.d.ts +0 -31
  81. package/dist/commands-config.js +0 -272
  82. package/dist/commands-config.js.map +0 -1
  83. package/dist/commands-core.d.ts +0 -145
  84. package/dist/commands-core.js +0 -190
  85. package/dist/commands-core.js.map +0 -1
  86. package/dist/commands-devices.d.ts +0 -41
  87. package/dist/commands-devices.js +0 -144
  88. package/dist/commands-devices.js.map +0 -1
  89. package/dist/commands-doctor.d.ts +0 -25
  90. package/dist/commands-doctor.js +0 -1553
  91. package/dist/commands-doctor.js.map +0 -1
  92. package/dist/commands-guidance.d.ts +0 -27
  93. package/dist/commands-guidance.js +0 -150
  94. package/dist/commands-guidance.js.map +0 -1
  95. package/dist/commands-harness-context.d.ts +0 -57
  96. package/dist/commands-harness-context.js +0 -102
  97. package/dist/commands-harness-context.js.map +0 -1
  98. package/dist/commands-hook-attend.d.ts +0 -70
  99. package/dist/commands-hook-attend.js +0 -655
  100. package/dist/commands-hook-attend.js.map +0 -1
  101. package/dist/commands-hook-diagnostics.d.ts +0 -37
  102. package/dist/commands-hook-diagnostics.js +0 -136
  103. package/dist/commands-hook-diagnostics.js.map +0 -1
  104. package/dist/commands-hook-install.d.ts +0 -26
  105. package/dist/commands-hook-install.js +0 -784
  106. package/dist/commands-hook-install.js.map +0 -1
  107. package/dist/commands-hook-run.d.ts +0 -29
  108. package/dist/commands-hook-run.js +0 -933
  109. package/dist/commands-hook-run.js.map +0 -1
  110. package/dist/commands-hook-shape.d.ts +0 -19
  111. package/dist/commands-hook-shape.js +0 -48
  112. package/dist/commands-hook-shape.js.map +0 -1
  113. package/dist/commands-init.d.ts +0 -42
  114. package/dist/commands-init.js +0 -901
  115. package/dist/commands-init.js.map +0 -1
  116. package/dist/commands-io.d.ts +0 -2
  117. package/dist/commands-io.js +0 -117
  118. package/dist/commands-io.js.map +0 -1
  119. package/dist/commands-logs.d.ts +0 -30
  120. package/dist/commands-logs.js +0 -201
  121. package/dist/commands-logs.js.map +0 -1
  122. package/dist/commands-native-acknowledge.d.ts +0 -8
  123. package/dist/commands-native-acknowledge.js +0 -114
  124. package/dist/commands-native-acknowledge.js.map +0 -1
  125. package/dist/commands-project.d.ts +0 -4
  126. package/dist/commands-project.js +0 -41
  127. package/dist/commands-project.js.map +0 -1
  128. package/dist/commands-receive.d.ts +0 -3
  129. package/dist/commands-receive.js +0 -59
  130. package/dist/commands-receive.js.map +0 -1
  131. package/dist/commands-send-support.d.ts +0 -59
  132. package/dist/commands-send-support.js +0 -258
  133. package/dist/commands-send-support.js.map +0 -1
  134. package/dist/commands-send.d.ts +0 -34
  135. package/dist/commands-send.js +0 -900
  136. package/dist/commands-send.js.map +0 -1
  137. package/dist/commands-setup-proof.d.ts +0 -43
  138. package/dist/commands-setup-proof.js +0 -134
  139. package/dist/commands-setup-proof.js.map +0 -1
  140. package/dist/commands-skill.d.ts +0 -21
  141. package/dist/commands-skill.js +0 -199
  142. package/dist/commands-skill.js.map +0 -1
  143. package/dist/commands-sounds.d.ts +0 -12
  144. package/dist/commands-sounds.js +0 -35
  145. package/dist/commands-sounds.js.map +0 -1
  146. package/dist/commands-update-check.d.ts +0 -6
  147. package/dist/commands-update-check.js +0 -102
  148. package/dist/commands-update-check.js.map +0 -1
  149. package/dist/commands-update-resume.d.ts +0 -7
  150. package/dist/commands-update-resume.js +0 -143
  151. package/dist/commands-update-resume.js.map +0 -1
  152. package/dist/commands-update-skill.d.ts +0 -5
  153. package/dist/commands-update-skill.js +0 -36
  154. package/dist/commands-update-skill.js.map +0 -1
  155. package/dist/commands-update.d.ts +0 -11
  156. package/dist/commands-update.js +0 -286
  157. package/dist/commands-update.js.map +0 -1
  158. package/dist/commands.d.ts +0 -26
  159. package/dist/commands.js +0 -27
  160. package/dist/commands.js.map +0 -1
  161. package/dist/config-schema.d.ts +0 -62
  162. package/dist/config-schema.js +0 -302
  163. package/dist/config-schema.js.map +0 -1
  164. package/dist/config.d.ts +0 -181
  165. package/dist/config.js +0 -357
  166. package/dist/config.js.map +0 -1
  167. package/dist/credentials.d.ts +0 -96
  168. package/dist/credentials.js +0 -342
  169. package/dist/credentials.js.map +0 -1
  170. package/dist/file-lock.d.ts +0 -34
  171. package/dist/file-lock.js +0 -367
  172. package/dist/file-lock.js.map +0 -1
  173. package/dist/generated-session-label.d.ts +0 -2
  174. package/dist/generated-session-label.js +0 -152
  175. package/dist/generated-session-label.js.map +0 -1
  176. package/dist/guidance-content.d.ts +0 -53
  177. package/dist/guidance-content.js +0 -248
  178. package/dist/guidance-content.js.map +0 -1
  179. package/dist/guidance-render.d.ts +0 -25
  180. package/dist/guidance-render.js +0 -47
  181. package/dist/guidance-render.js.map +0 -1
  182. package/dist/guidance.d.ts +0 -69
  183. package/dist/guidance.js +0 -173
  184. package/dist/guidance.js.map +0 -1
  185. package/dist/harness-session-title.d.ts +0 -22
  186. package/dist/harness-session-title.js +0 -74
  187. package/dist/harness-session-title.js.map +0 -1
  188. package/dist/harnesses.d.ts +0 -65
  189. package/dist/harnesses.js +0 -159
  190. package/dist/harnesses.js.map +0 -1
  191. package/dist/hermes-attendant.d.ts +0 -38
  192. package/dist/hermes-attendant.js +0 -337
  193. package/dist/hermes-attendant.js.map +0 -1
  194. package/dist/hermes-plugin.d.ts +0 -11
  195. package/dist/hermes-plugin.js +0 -299
  196. package/dist/hermes-plugin.js.map +0 -1
  197. package/dist/hook-acknowledgements.d.ts +0 -70
  198. package/dist/hook-acknowledgements.js +0 -490
  199. package/dist/hook-acknowledgements.js.map +0 -1
  200. package/dist/hook-adapter.d.ts +0 -48
  201. package/dist/hook-adapter.js +0 -419
  202. package/dist/hook-adapter.js.map +0 -1
  203. package/dist/hook-events.d.ts +0 -105
  204. package/dist/hook-events.js +0 -130
  205. package/dist/hook-events.js.map +0 -1
  206. package/dist/hook-gates.d.ts +0 -7
  207. package/dist/hook-gates.js +0 -20
  208. package/dist/hook-gates.js.map +0 -1
  209. package/dist/hook-input.d.ts +0 -10
  210. package/dist/hook-input.js +0 -64
  211. package/dist/hook-input.js.map +0 -1
  212. package/dist/hook-lifecycle.d.ts +0 -112
  213. package/dist/hook-lifecycle.js +0 -2031
  214. package/dist/hook-lifecycle.js.map +0 -1
  215. package/dist/hook-project-sessions.d.ts +0 -28
  216. package/dist/hook-project-sessions.js +0 -141
  217. package/dist/hook-project-sessions.js.map +0 -1
  218. package/dist/hook-question-lock.d.ts +0 -29
  219. package/dist/hook-question-lock.js +0 -217
  220. package/dist/hook-question-lock.js.map +0 -1
  221. package/dist/hook-question-retirement.d.ts +0 -74
  222. package/dist/hook-question-retirement.js +0 -314
  223. package/dist/hook-question-retirement.js.map +0 -1
  224. package/dist/hook-question-state.d.ts +0 -58
  225. package/dist/hook-question-state.js +0 -239
  226. package/dist/hook-question-state.js.map +0 -1
  227. package/dist/hook-session-state.d.ts +0 -127
  228. package/dist/hook-session-state.js +0 -488
  229. package/dist/hook-session-state.js.map +0 -1
  230. package/dist/hook-types.d.ts +0 -476
  231. package/dist/hook-types.js +0 -4
  232. package/dist/hook-types.js.map +0 -1
  233. package/dist/injection-render.d.ts +0 -36
  234. package/dist/injection-render.js +0 -61
  235. package/dist/injection-render.js.map +0 -1
  236. package/dist/install-hooks.d.ts +0 -470
  237. package/dist/install-hooks.js +0 -1622
  238. package/dist/install-hooks.js.map +0 -1
  239. package/dist/integration-health.d.ts +0 -26
  240. package/dist/integration-health.js +0 -133
  241. package/dist/integration-health.js.map +0 -1
  242. package/dist/interactive.d.ts +0 -22
  243. package/dist/interactive.js +0 -644
  244. package/dist/interactive.js.map +0 -1
  245. package/dist/invocation-context.d.ts +0 -47
  246. package/dist/invocation-context.js +0 -164
  247. package/dist/invocation-context.js.map +0 -1
  248. package/dist/local-path.d.ts +0 -7
  249. package/dist/local-path.js +0 -27
  250. package/dist/local-path.js.map +0 -1
  251. package/dist/logging.d.ts +0 -157
  252. package/dist/logging.js +0 -599
  253. package/dist/logging.js.map +0 -1
  254. package/dist/main-run.d.ts +0 -1
  255. package/dist/main-run.js +0 -42
  256. package/dist/main-run.js.map +0 -1
  257. package/dist/main.d.ts +0 -2
  258. package/dist/main.js +0 -12
  259. package/dist/main.js.map +0 -1
  260. package/dist/native-answer-operation.d.ts +0 -70
  261. package/dist/native-answer-operation.js +0 -247
  262. package/dist/native-answer-operation.js.map +0 -1
  263. package/dist/native-skills.d.ts +0 -64
  264. package/dist/native-skills.js +0 -167
  265. package/dist/native-skills.js.map +0 -1
  266. package/dist/node-floor.d.ts +0 -19
  267. package/dist/node-floor.js +0 -32
  268. package/dist/node-floor.js.map +0 -1
  269. package/dist/npm-invocation.d.ts +0 -34
  270. package/dist/npm-invocation.js +0 -53
  271. package/dist/npm-invocation.js.map +0 -1
  272. package/dist/openclaw-continuation-bridge.d.ts +0 -7
  273. package/dist/openclaw-continuation-bridge.js +0 -64
  274. package/dist/openclaw-continuation-bridge.js.map +0 -1
  275. package/dist/openclaw-gateway-readiness.d.ts +0 -1
  276. package/dist/openclaw-gateway-readiness.js +0 -24
  277. package/dist/openclaw-gateway-readiness.js.map +0 -1
  278. package/dist/openclaw-generation.d.ts +0 -21
  279. package/dist/openclaw-generation.js +0 -115
  280. package/dist/openclaw-generation.js.map +0 -1
  281. package/dist/openclaw-message-bridge.d.ts +0 -8
  282. package/dist/openclaw-message-bridge.js +0 -93
  283. package/dist/openclaw-message-bridge.js.map +0 -1
  284. package/dist/openclaw-pending.d.ts +0 -8
  285. package/dist/openclaw-pending.js +0 -47
  286. package/dist/openclaw-pending.js.map +0 -1
  287. package/dist/openclaw-plugin.d.ts +0 -75
  288. package/dist/openclaw-plugin.js +0 -1353
  289. package/dist/openclaw-plugin.js.map +0 -1
  290. package/dist/openclaw-session-access.d.ts +0 -12
  291. package/dist/openclaw-session-access.js +0 -79
  292. package/dist/openclaw-session-access.js.map +0 -1
  293. package/dist/opencode-plugin.d.ts +0 -88
  294. package/dist/opencode-plugin.js +0 -286
  295. package/dist/opencode-plugin.js.map +0 -1
  296. package/dist/orca-session-title.d.ts +0 -11
  297. package/dist/orca-session-title.js +0 -111
  298. package/dist/orca-session-title.js.map +0 -1
  299. package/dist/pack-feedback-slice.d.ts +0 -35
  300. package/dist/pack-feedback-slice.js +0 -126
  301. package/dist/pack-feedback-slice.js.map +0 -1
  302. package/dist/pairing-qr.d.ts +0 -5
  303. package/dist/pairing-qr.js +0 -24
  304. package/dist/pairing-qr.js.map +0 -1
  305. package/dist/pending-pairing.d.ts +0 -38
  306. package/dist/pending-pairing.js +0 -70
  307. package/dist/pending-pairing.js.map +0 -1
  308. package/dist/platform.d.ts +0 -67
  309. package/dist/platform.js +0 -140
  310. package/dist/platform.js.map +0 -1
  311. package/dist/process-identity.d.ts +0 -26
  312. package/dist/process-identity.js +0 -82
  313. package/dist/process-identity.js.map +0 -1
  314. package/dist/program.d.ts +0 -59
  315. package/dist/program.js +0 -691
  316. package/dist/program.js.map +0 -1
  317. package/dist/project-enablement.d.ts +0 -21
  318. package/dist/project-enablement.js +0 -71
  319. package/dist/project-enablement.js.map +0 -1
  320. package/dist/question-settlement-process.d.ts +0 -17
  321. package/dist/question-settlement-process.js +0 -35
  322. package/dist/question-settlement-process.js.map +0 -1
  323. package/dist/question-timing.d.ts +0 -46
  324. package/dist/question-timing.js +0 -48
  325. package/dist/question-timing.js.map +0 -1
  326. package/dist/readiness.d.ts +0 -153
  327. package/dist/readiness.js +0 -149
  328. package/dist/readiness.js.map +0 -1
  329. package/dist/release.d.ts +0 -31
  330. package/dist/release.js +0 -52
  331. package/dist/release.js.map +0 -1
  332. package/dist/send-attempts.d.ts +0 -26
  333. package/dist/send-attempts.js +0 -139
  334. package/dist/send-attempts.js.map +0 -1
  335. package/dist/send.d.ts +0 -115
  336. package/dist/send.js +0 -421
  337. package/dist/send.js.map +0 -1
  338. package/dist/session-activation.d.ts +0 -13
  339. package/dist/session-activation.js +0 -64
  340. package/dist/session-activation.js.map +0 -1
  341. package/dist/session-attendant-probe.d.ts +0 -85
  342. package/dist/session-attendant-probe.js +0 -173
  343. package/dist/session-attendant-probe.js.map +0 -1
  344. package/dist/session-attendant-state.d.ts +0 -70
  345. package/dist/session-attendant-state.js +0 -256
  346. package/dist/session-attendant-state.js.map +0 -1
  347. package/dist/session-attendant.d.ts +0 -143
  348. package/dist/session-attendant.js +0 -520
  349. package/dist/session-attendant.js.map +0 -1
  350. package/dist/session-delivery.d.ts +0 -249
  351. package/dist/session-delivery.js +0 -576
  352. package/dist/session-delivery.js.map +0 -1
  353. package/dist/session-handoff.d.ts +0 -98
  354. package/dist/session-handoff.js +0 -118
  355. package/dist/session-handoff.js.map +0 -1
  356. package/dist/session-input-wakes.d.ts +0 -78
  357. package/dist/session-input-wakes.js +0 -239
  358. package/dist/session-input-wakes.js.map +0 -1
  359. package/dist/session-inputs.d.ts +0 -57
  360. package/dist/session-inputs.js +0 -434
  361. package/dist/session-inputs.js.map +0 -1
  362. package/dist/session-labels.d.ts +0 -56
  363. package/dist/session-labels.js +0 -396
  364. package/dist/session-labels.js.map +0 -1
  365. package/dist/session-message-handoff.d.ts +0 -39
  366. package/dist/session-message-handoff.js +0 -107
  367. package/dist/session-message-handoff.js.map +0 -1
  368. package/dist/setup-destinations.d.ts +0 -23
  369. package/dist/setup-destinations.js +0 -44
  370. package/dist/setup-destinations.js.map +0 -1
  371. package/dist/skill-integrity.d.ts +0 -54
  372. package/dist/skill-integrity.js +0 -164
  373. package/dist/skill-integrity.js.map +0 -1
  374. package/dist/skill-source/manifest.json +0 -40
  375. package/dist/skill-source/notifai/SKILL.md +0 -350
  376. package/dist/skill-source/notifai/references/diagnostics.md +0 -36
  377. package/dist/skill-source/notifai/references/harness-setup.md +0 -384
  378. package/dist/skill-source/notifai/references/native-questions.md +0 -51
  379. package/dist/skill-source/notifai/references/notes-and-edits.md +0 -54
  380. package/dist/skill-source/notifai/references/send-details.md +0 -14
  381. package/dist/skill-source/notifai/references/updates.md +0 -116
  382. package/dist/skill-source/notifai/references/writing-examples.md +0 -56
  383. package/dist/sound-ref.d.ts +0 -23
  384. package/dist/sound-ref.js +0 -28
  385. package/dist/sound-ref.js.map +0 -1
  386. package/dist/ui/banner.d.ts +0 -22
  387. package/dist/ui/banner.js +0 -87
  388. package/dist/ui/banner.js.map +0 -1
  389. package/dist/ui/config-view.d.ts +0 -30
  390. package/dist/ui/config-view.js +0 -108
  391. package/dist/ui/config-view.js.map +0 -1
  392. package/dist/ui/help.d.ts +0 -44
  393. package/dist/ui/help.js +0 -107
  394. package/dist/ui/help.js.map +0 -1
  395. package/dist/ui/theme.d.ts +0 -90
  396. package/dist/ui/theme.js +0 -244
  397. package/dist/ui/theme.js.map +0 -1
  398. package/dist/update-handoff.d.ts +0 -29
  399. package/dist/update-handoff.js +0 -69
  400. package/dist/update-handoff.js.map +0 -1
  401. package/dist/url-policy.d.ts +0 -85
  402. package/dist/url-policy.js +0 -369
  403. package/dist/url-policy.js.map +0 -1
  404. package/dist/version.d.ts +0 -40
  405. package/dist/version.js +0 -132
  406. package/dist/version.js.map +0 -1
  407. package/dist/wake-support.d.ts +0 -40
  408. package/dist/wake-support.js +0 -95
  409. package/dist/wake-support.js.map +0 -1
@@ -1,143 +0,0 @@
1
- /**
2
- * The Session Attendant: one long-lived CLI process per Agent Session.
3
- *
4
- * It starts as that session's own asynchronous hook child, proves the harness
5
- * process alive every two seconds by local evidence only, and holds the
6
- * session's Session Presence lease with the service. It is resident from its
7
- * start but makes no network call until the session had a Notification
8
- * Request accepted, so sessions that never notified are never reported.
9
- *
10
- * Nothing ends it from outside. Claude Code neither signals nor waits for a
11
- * resident async hook on `/exit` or when the harness is killed, so the local
12
- * probe is the one universal end signal; SessionEnd's marker is a fast path,
13
- * and a SIGTERM (terminal close) is treated as "ending, report once".
14
- *
15
- * Local only: PIDs, process start times, descriptor paths, and harness homes
16
- * never leave this machine. The service sees an opaque incarnation id.
17
- */
18
- import type { AttendanceMessage, SessionActivity } from '@raidiant/notifai-protocol';
19
- import { type ApiClient } from './client.js';
20
- import type { Logger } from './logging.js';
21
- /** Local probe cadence. */
22
- export declare const ATTENDANT_PROBE_INTERVAL_MS = 2000;
23
- /** Harness writes stop this long before the lease ends by the attendant's own clock. */
24
- export declare const ATTENDANT_WRITE_MARGIN_MS = 15000;
25
- /** A wall-clock move this far from the monotonic clock means sleep or a clock jump. */
26
- export declare const ATTENDANT_CLOCK_JUMP_MS = 5000;
27
- /** Best-effort bound on the final `ended` or `withdrawn` report. */
28
- export declare const ATTENDANT_FINAL_REPORT_MS = 3000;
29
- /** How often an unsupported service is asked again. */
30
- export declare const ATTENDANT_UNSUPPORTED_RECHECK_MS: number;
31
- /** How long to wait before asking again about a session the service does not know yet. */
32
- export declare const ATTENDANT_UNKNOWN_SESSION_RETRY_MS = 60000;
33
- /** First pause before re-asking for a message that waits on something brief; doubles to the hold. */
34
- export declare const ATTENDANT_MESSAGE_RETRY_MS = 2000;
35
- export type HarnessProbe = {
36
- state: 'running';
37
- activity: SessionActivity;
38
- }
39
- /** Evidence is missing or ambiguous: no renewal, no deliveries, never `running`. */
40
- | {
41
- state: 'uncertain';
42
- reason: string;
43
- } | {
44
- state: 'ended';
45
- reason: 'harness-gone' | 'session-replaced' | 'session-end-hook';
46
- };
47
- export type GateResult = {
48
- ok: true;
49
- } | {
50
- ok: false;
51
- reason: string;
52
- };
53
- export type AttendantPhase =
54
- /** Resident, no network: the session has not notified yet. */
55
- 'dormant'
56
- /** The service does not offer Session Attendance; resident without reporting. */
57
- | 'unsupported' | 'acquiring' | 'attending'
58
- /** Another incarnation holds the lease. */
59
- | 'waiting-for-lease' | 'uncertain' | 'exited';
60
- export type AttendantExitReason = 'harness-gone' | 'session-replaced' | 'session-end-hook' | 'signal' | 'superseded' | 'claim-lost' | 'withdrawn-by-service' | 'unauthorized' | `gate:${string}`;
61
- export interface AttendantStatus {
62
- phase: AttendantPhase;
63
- incarnation: string;
64
- generation: number | null;
65
- activity: SessionActivity | null;
66
- reason: string | null;
67
- /** Whether this attendant hands Session Messages in, so the service accepts notes. */
68
- accepts_messages: boolean;
69
- /** Fresh in-process proof that a harness writer still targets this session. */
70
- writer_ready?: boolean;
71
- updated_at: number;
72
- }
73
- export interface AttendantClock {
74
- /** Monotonic milliseconds; never moves with the wall clock. */
75
- monotonic(): number;
76
- wall(): number;
77
- sleep(milliseconds: number, signal: AbortSignal): Promise<void>;
78
- }
79
- export interface SessionAttendantOptions {
80
- sessionId: string;
81
- /** The opaque incarnation this attendant serves; see `incarnationNow`. */
82
- incarnation: string;
83
- /** The incarnation currently recorded for the session, read fresh each probe. */
84
- incarnationNow(): string | null;
85
- /** Replace a refused incarnation id; null when the record moved on. */
86
- rotateIncarnation(expected: string): string | null;
87
- probe(): HarnessProbe;
88
- /** Whether this attendant still holds its exclusive local claim. */
89
- claimHeld(): boolean;
90
- /** Whether this session had a Notification Request accepted on this machine. */
91
- notified(): boolean;
92
- /** Project Enablement and the installed CLI/hook contract. */
93
- gates(): GateResult;
94
- /** Authenticated client, or null when this machine is not paired. */
95
- client(): ApiClient | null;
96
- /** Whether the service advertises Session Attendance. */
97
- serverSupportsAttendance(client: ApiClient): Promise<boolean>;
98
- /** Whether this attendant can hand Session Messages into the session. */
99
- acceptsMessages: boolean;
100
- /**
101
- * Hands claimable Session Messages into the session. `retry-soon` asks for
102
- * the next exchange without holding it open: a message is waiting on
103
- * something that resolves in seconds.
104
- */
105
- onMessages?: (messages: AttendanceMessage[], attendant: AttendantHandle) => Promise<'done' | 'retry-soon'>;
106
- clock: AttendantClock;
107
- logger: Logger;
108
- /** Staged local input needs a fresh exchange rather than a quiet long-poll. */
109
- localInputPending?: () => boolean;
110
- /** Persist the local status `notifai doctor` reads. */
111
- writeStatus(status: AttendantStatus): void;
112
- /** Periodic housekeeping (e.g. keep markers younger than pruning). */
113
- heartbeat?: () => void;
114
- /** Harness-owned work that needs the fresh probe while attendance continues. */
115
- onProbe?: (probe: HarnessProbe) => void;
116
- /** Publish fresh writer evidence for ask admission. */
117
- publishProbeStatus?: boolean;
118
- /** Resolves when a termination signal arrived. */
119
- signalled?: Promise<void>;
120
- probeIntervalMs?: number;
121
- waitSeconds?: number;
122
- }
123
- /** What a harness writer asks before each write. */
124
- export interface AttendantHandle {
125
- /**
126
- * True only while, right now: this attendant still owns its claim and
127
- * incarnation, fresh local evidence shows the session running, every gate
128
- * passes, no clock discontinuity happened since the lease was granted, and
129
- * the lease cannot lapse before the write ends by the monotonic clock.
130
- */
131
- mayWrite(): boolean;
132
- generation(): number | null;
133
- incarnation(): string;
134
- }
135
- export interface AttendantResult {
136
- reason: AttendantExitReason;
137
- /** Whether a final report was attempted. */
138
- reported: 'ended' | 'withdrawn' | null;
139
- }
140
- /** Runs one Session Attendant until its session ends or a gate fails. */
141
- export declare function runSessionAttendant(options: SessionAttendantOptions): Promise<AttendantResult>;
142
- /** Production clocks: `performance.now()` pauses with the machine; `Date.now()` does not. */
143
- export declare const systemAttendantClock: AttendantClock;
@@ -1,520 +0,0 @@
1
- import { ATTENDANCE_MAX_WAIT_SECONDS } from '@raidiant/notifai-protocol';
2
- import { ApiCallError } from './client.js';
3
- /** Local probe cadence. */
4
- export const ATTENDANT_PROBE_INTERVAL_MS = 2_000;
5
- /** Harness writes stop this long before the lease ends by the attendant's own clock. */
6
- export const ATTENDANT_WRITE_MARGIN_MS = 15_000;
7
- /** A wall-clock move this far from the monotonic clock means sleep or a clock jump. */
8
- export const ATTENDANT_CLOCK_JUMP_MS = 5_000;
9
- /** Best-effort bound on the final `ended` or `withdrawn` report. */
10
- export const ATTENDANT_FINAL_REPORT_MS = 3_000;
11
- /** How often an unsupported service is asked again. */
12
- export const ATTENDANT_UNSUPPORTED_RECHECK_MS = 30 * 60_000;
13
- /** How long to wait before asking again about a session the service does not know yet. */
14
- export const ATTENDANT_UNKNOWN_SESSION_RETRY_MS = 60_000;
15
- /** First pause before re-asking for a message that waits on something brief; doubles to the hold. */
16
- export const ATTENDANT_MESSAGE_RETRY_MS = 2_000;
17
- const MAX_NETWORK_BACKOFF_MS = 30_000;
18
- /** Runs one Session Attendant until its session ends or a gate fails. */
19
- export async function runSessionAttendant(options) {
20
- const { clock, logger } = options;
21
- const probeInterval = options.probeIntervalMs ?? ATTENDANT_PROBE_INTERVAL_MS;
22
- const waitSeconds = Math.min(options.waitSeconds ?? ATTENDANCE_MAX_WAIT_SECONDS, ATTENDANCE_MAX_WAIT_SECONDS);
23
- const stopping = new AbortController();
24
- let incarnation = options.incarnation;
25
- let generation = null;
26
- let cursor;
27
- /** Monotonic time after which the lease may have lapsed at the service. */
28
- let leaseEndsAt = null;
29
- let probe = options.probe();
30
- let activity = probe.state === 'running' ? probe.activity : null;
31
- let phase = 'dormant';
32
- let phaseReason = null;
33
- let networkStarted = false;
34
- let exit = probe.state === 'ended' ? probe.reason : null;
35
- let exchange = null;
36
- let exchangeHeld = false;
37
- let wake = null;
38
- const setPhase = (next, reason = null) => {
39
- if (next === phase && reason === phaseReason)
40
- return;
41
- phase = next;
42
- phaseReason = reason;
43
- logger.info('attendant.state', {
44
- phase,
45
- ...(reason === null ? {} : { reason }),
46
- ...(generation === null ? {} : { generation }),
47
- });
48
- writeStatus();
49
- };
50
- const writeStatus = () => {
51
- try {
52
- options.writeStatus({
53
- phase,
54
- incarnation,
55
- generation,
56
- activity,
57
- reason: phaseReason,
58
- accepts_messages: options.acceptsMessages,
59
- ...(options.publishProbeStatus ? { writer_ready: probe.state === 'running' } : {}),
60
- updated_at: clock.wall(),
61
- });
62
- }
63
- catch {
64
- // Status is diagnostic; the attendant keeps attending without it.
65
- }
66
- };
67
- const nudge = () => {
68
- const resolve = wake;
69
- wake = null;
70
- resolve?.();
71
- };
72
- const abortExchange = () => {
73
- exchange?.abort();
74
- };
75
- const stop = (reason) => {
76
- if (exit === null)
77
- exit = reason;
78
- exchange?.abort();
79
- stopping.abort();
80
- nudge();
81
- };
82
- /** Bumped by every clock discontinuity; a lease granted in an older epoch is void. */
83
- let epoch = 0;
84
- let reacquireNow = false;
85
- const loseLease = (reason) => {
86
- if (leaseEndsAt !== null || generation !== null) {
87
- logger.info('attendant.lease', { event: 'lost', reason, ...(generation === null ? {} : { generation }) });
88
- }
89
- leaseEndsAt = null;
90
- };
91
- let lastWall = clock.wall();
92
- let lastMono = clock.monotonic();
93
- /**
94
- * Sleep pauses the monotonic clock while the service's lease keeps running;
95
- * a wall-clock step says the same. Either way nothing may be written until
96
- * a fresh exchange, sent after the jump, re-acquires.
97
- */
98
- const checkClock = () => {
99
- const wall = clock.wall();
100
- const mono = clock.monotonic();
101
- const jump = Math.abs(wall - lastWall - (mono - lastMono));
102
- lastWall = wall;
103
- lastMono = mono;
104
- if (jump > ATTENDANT_CLOCK_JUMP_MS) {
105
- epoch += 1;
106
- reacquireNow = true;
107
- loseLease('clock-jump');
108
- exchange?.abort();
109
- }
110
- };
111
- /**
112
- * Re-establish everything this attendant relies on, now: clock continuity,
113
- * its claim, its incarnation, and fresh session evidence. Returns whether
114
- * the session is running and this attendant may still act for it.
115
- */
116
- const refresh = () => {
117
- if (exit !== null)
118
- return false;
119
- checkClock();
120
- if (!options.claimHeld()) {
121
- stop('claim-lost');
122
- return false;
123
- }
124
- const recorded = options.incarnationNow();
125
- if (recorded !== null && recorded !== incarnation) {
126
- stop('superseded');
127
- return false;
128
- }
129
- const previous = probe;
130
- probe = options.probe();
131
- if (probe.state === 'ended') {
132
- stop(probe.reason);
133
- return false;
134
- }
135
- if (probe.state === 'uncertain') {
136
- if (previous.state !== 'uncertain') {
137
- exchange?.abort();
138
- if (networkStarted)
139
- setPhase('uncertain', probe.reason);
140
- }
141
- return false;
142
- }
143
- const changed = activity !== probe.activity;
144
- activity = probe.activity;
145
- if (changed) {
146
- logger.info('attendant.state', { phase, activity, reason: 'activity-changed', generation });
147
- writeStatus();
148
- }
149
- if (previous.state !== 'running' || changed) {
150
- // A held exchange carries the old activity; cut it so the next one reports this.
151
- exchange?.abort();
152
- nudge();
153
- }
154
- return true;
155
- };
156
- const handle = {
157
- mayWrite: () => {
158
- if (!refresh())
159
- return false;
160
- const gate = options.gates();
161
- if (!gate.ok) {
162
- stop(`gate:${gate.reason}`);
163
- return false;
164
- }
165
- return (generation !== null &&
166
- leaseEndsAt !== null &&
167
- clock.monotonic() < leaseEndsAt - ATTENDANT_WRITE_MARGIN_MS);
168
- },
169
- generation: () => generation,
170
- incarnation: () => incarnation,
171
- };
172
- // -- local probe -------------------------------------------------------
173
- let lastHeartbeat = lastMono;
174
- const tick = () => {
175
- refresh();
176
- if (exit !== null)
177
- return;
178
- if (options.publishProbeStatus)
179
- writeStatus();
180
- try {
181
- options.onProbe?.(probe);
182
- }
183
- catch (err) {
184
- logger.error('attendant.state', {
185
- phase, reason: 'probe-work-failed',
186
- message: err instanceof Error ? err.message : String(err),
187
- });
188
- }
189
- if (!networkStarted && options.notified())
190
- nudge();
191
- if (probe.state === 'running' && exchangeHeld && exchange !== null && options.localInputPending?.()) {
192
- // A reply can arrive locally while attendance is holding for server notes.
193
- // Refresh the lease and invoke the genuine resident writer on the next
194
- // exchange, without waiting the server's entire quiet-poll interval.
195
- reacquireNow = true;
196
- exchange.abort();
197
- nudge();
198
- }
199
- const mono = clock.monotonic();
200
- if (mono - lastHeartbeat >= 60 * 60_000) {
201
- lastHeartbeat = mono;
202
- try {
203
- options.heartbeat?.();
204
- }
205
- catch {
206
- // Housekeeping only.
207
- }
208
- }
209
- };
210
- const probeLoop = (async () => {
211
- while (!stopping.signal.aborted) {
212
- await clock.sleep(probeInterval, stopping.signal).catch(() => undefined);
213
- if (stopping.signal.aborted)
214
- break;
215
- tick();
216
- }
217
- })();
218
- void options.signalled?.then(() => stop('signal'));
219
- /** Wait until a probe tick changes something this loop waits on, or a stop. */
220
- const nextChange = async () => {
221
- if (stopping.signal.aborted)
222
- return;
223
- await new Promise((resolve) => {
224
- wake = resolve;
225
- });
226
- };
227
- const pause = async (ms) => {
228
- await clock.sleep(ms, stopping.signal).catch(() => undefined);
229
- };
230
- writeStatus();
231
- logger.info('attendant.start', { notified: options.notified(), probe: probe.state });
232
- // -- exchange loop -----------------------------------------------------
233
- let backoff = 1_000;
234
- let rotated = false;
235
- let serverChecked = false;
236
- /** Consecutive `retry-soon` answers from message hand-off. */
237
- let messageRetries = 0;
238
- while (exit === null) {
239
- if (!networkStarted) {
240
- if (!options.notified()) {
241
- await nextChange();
242
- continue;
243
- }
244
- networkStarted = true;
245
- }
246
- // Fresh evidence before every exchange, not the last tick's.
247
- if (!refresh()) {
248
- if (exit !== null)
249
- break;
250
- if (probe.state === 'uncertain')
251
- setPhase('uncertain', probe.reason);
252
- await nextChange();
253
- continue;
254
- }
255
- const gate = options.gates();
256
- if (!gate.ok) {
257
- stop(`gate:${gate.reason}`);
258
- break;
259
- }
260
- const client = options.client();
261
- if (client === null) {
262
- stop('gate:not-paired');
263
- break;
264
- }
265
- if (!serverChecked) {
266
- // Discovery is an exchange like any other: a stop, an uncertain probe,
267
- // or an activity change cuts it, and whatever it answers, every check
268
- // above runs again before anything is sent.
269
- const discovery = new AbortController();
270
- exchange = discovery;
271
- exchangeHeld = false;
272
- let supported;
273
- try {
274
- supported = await abortable(options.serverSupportsAttendance(client), discovery.signal);
275
- }
276
- catch {
277
- exchange = null;
278
- if (exit !== null || discovery.signal.aborted)
279
- continue;
280
- await pause(backoff);
281
- backoff = Math.min(backoff * 2, MAX_NETWORK_BACKOFF_MS);
282
- continue;
283
- }
284
- exchange = null;
285
- if (!supported) {
286
- setPhase('unsupported', 'service-has-no-session-attendance');
287
- await pause(ATTENDANT_UNSUPPORTED_RECHECK_MS);
288
- continue;
289
- }
290
- serverChecked = true;
291
- continue;
292
- }
293
- if (generation === null)
294
- setPhase('acquiring');
295
- const body = {
296
- incarnation,
297
- ...(generation === null ? {} : { generation }),
298
- state: 'running',
299
- // Set by the successful refresh above.
300
- activity: activity ?? 'idle',
301
- accepts_messages: options.acceptsMessages,
302
- ...(cursor === undefined ? {} : { message_cursor: cursor }),
303
- };
304
- const current = new AbortController();
305
- exchange = current;
306
- const sentAt = clock.monotonic();
307
- const sentEpoch = epoch;
308
- const hold = cursor !== undefined && !reacquireNow && messageRetries === 0;
309
- exchangeHeld = hold;
310
- reacquireNow = false;
311
- let response;
312
- try {
313
- response = await client.attend(options.sessionId, body, {
314
- waitSeconds: hold ? waitSeconds : 0,
315
- signal: current.signal,
316
- });
317
- }
318
- catch (err) {
319
- exchange = null;
320
- if (exit !== null)
321
- break;
322
- if (current.signal.aborted)
323
- continue;
324
- if (err instanceof ApiCallError) {
325
- if (err.status === 401 || err.status === 403) {
326
- stop('unauthorized');
327
- break;
328
- }
329
- if (err.status === 404) {
330
- setPhase('acquiring', 'session-unknown-to-service');
331
- await pause(ATTENDANT_UNKNOWN_SESSION_RETRY_MS);
332
- continue;
333
- }
334
- if (err.status === 409 && err.code === 'feature_unavailable') {
335
- serverChecked = false;
336
- setPhase('unsupported', 'service-refused-session-attendance');
337
- await pause(ATTENDANT_UNSUPPORTED_RECHECK_MS);
338
- continue;
339
- }
340
- }
341
- await pause(backoff);
342
- backoff = Math.min(backoff * 2, MAX_NETWORK_BACKOFF_MS);
343
- continue;
344
- }
345
- exchange = null;
346
- backoff = 1_000;
347
- if (exit !== null)
348
- break;
349
- if (response.status === 'attending') {
350
- const acquired = generation !== response.generation;
351
- if (acquired) {
352
- logger.info('attendant.lease', { event: 'acquired', generation: response.generation });
353
- }
354
- generation = response.generation;
355
- // A grant sent before a clock discontinuity says nothing about now.
356
- leaseEndsAt = sentEpoch === epoch ? sentAt + response.lease_remaining_ms : null;
357
- cursor = response.message_cursor;
358
- rotated = false;
359
- // The answer waiter claims under the generation this status names.
360
- if (acquired)
361
- writeStatus();
362
- setPhase('attending');
363
- let handedOff = 'done';
364
- if (options.acceptsMessages && options.onMessages !== undefined) {
365
- try {
366
- handedOff = await options.onMessages(response.messages, handle);
367
- }
368
- catch (err) {
369
- logger.error('attendant.state', {
370
- phase,
371
- reason: 'message-handoff-failed',
372
- message: err instanceof Error ? err.message : String(err),
373
- });
374
- }
375
- }
376
- if (handedOff === 'retry-soon' && exit === null) {
377
- // Ask again without holding the exchange, backing off to the ordinary
378
- // hold so a message that keeps waiting cannot spin the attendant.
379
- const pauseMs = ATTENDANT_MESSAGE_RETRY_MS * 2 ** Math.min(messageRetries, 4);
380
- messageRetries += 1;
381
- await pause(Math.min(pauseMs, waitSeconds * 1_000));
382
- }
383
- else {
384
- messageRetries = 0;
385
- }
386
- continue;
387
- }
388
- if (response.status === 'owned') {
389
- if (generation !== null)
390
- loseLease('owned-by-another-incarnation');
391
- generation = null;
392
- cursor = undefined;
393
- setPhase('waiting-for-lease');
394
- await pause(Math.min(Math.max(response.retry_after_ms, 1_000), 120_000));
395
- continue;
396
- }
397
- // withdrawn: the service holds no lease for this incarnation.
398
- if (generation !== null) {
399
- loseLease('withdrawn-by-service');
400
- generation = null;
401
- stop('withdrawn-by-service');
402
- break;
403
- }
404
- // Refused while acquiring: this incarnation id already released its lease
405
- // (a report made before a restart). A fresh id may acquire once.
406
- const next = rotated ? null : options.rotateIncarnation(incarnation);
407
- if (next === null) {
408
- stop('withdrawn-by-service');
409
- break;
410
- }
411
- logger.info('attendant.lease', { event: 'incarnation-rotated' });
412
- incarnation = next;
413
- rotated = true;
414
- cursor = undefined;
415
- }
416
- // -- end -----------------------------------------------------------------
417
- stopping.abort();
418
- abortExchange();
419
- await probeLoop;
420
- const reason = exit ?? 'signal';
421
- let reported = null;
422
- const final = finalReport(reason);
423
- if (networkStarted && final !== null && (final === 'ended' || generation !== null)) {
424
- const client = safeClient(options);
425
- if (client !== null) {
426
- reported = final;
427
- const bound = new AbortController();
428
- const timer = setTimeout(() => bound.abort(), ATTENDANT_FINAL_REPORT_MS);
429
- try {
430
- await client.attend(options.sessionId, {
431
- incarnation,
432
- ...(generation === null ? {} : { generation }),
433
- state: final,
434
- }, { waitSeconds: 0, signal: bound.signal });
435
- }
436
- catch {
437
- // Without the report the lease lapses and presence reads out of reach.
438
- }
439
- finally {
440
- clearTimeout(timer);
441
- }
442
- }
443
- }
444
- leaseEndsAt = null;
445
- phase = 'exited';
446
- phaseReason = reason;
447
- writeStatus();
448
- logger.info('attendant.exit', {
449
- reason,
450
- ...(reported === null ? {} : { reported }),
451
- ...(generation === null ? {} : { generation }),
452
- });
453
- return { reason, reported };
454
- }
455
- /** Which final state a way of ending reports, if any. */
456
- function finalReport(reason) {
457
- switch (reason) {
458
- case 'harness-gone':
459
- case 'session-replaced':
460
- case 'session-end-hook':
461
- case 'signal':
462
- return 'ended';
463
- case 'superseded':
464
- // A newer start of the same session attends now; ending would end it.
465
- return 'withdrawn';
466
- case 'withdrawn-by-service':
467
- case 'unauthorized':
468
- case 'claim-lost':
469
- // Another attendant may hold this incarnation's lease now; any report
470
- // from here could release it.
471
- return null;
472
- default:
473
- return reason.startsWith('gate:') && reason !== 'gate:not-paired' ? 'withdrawn' : null;
474
- }
475
- }
476
- function safeClient(options) {
477
- try {
478
- return options.client();
479
- }
480
- catch {
481
- return null;
482
- }
483
- }
484
- /** Rejects when `signal` aborts, so an awaited call cannot outlive a stop. */
485
- function abortable(work, signal) {
486
- if (signal.aborted)
487
- return Promise.reject(new Error('aborted'));
488
- return new Promise((resolve, reject) => {
489
- const onAbort = () => reject(new Error('aborted'));
490
- signal.addEventListener('abort', onAbort, { once: true });
491
- work.then((value) => {
492
- signal.removeEventListener('abort', onAbort);
493
- resolve(value);
494
- }, (err) => {
495
- signal.removeEventListener('abort', onAbort);
496
- reject(err);
497
- });
498
- });
499
- }
500
- /** Production clocks: `performance.now()` pauses with the machine; `Date.now()` does not. */
501
- export const systemAttendantClock = {
502
- monotonic: () => performance.now(),
503
- wall: () => Date.now(),
504
- sleep: (milliseconds, signal) => new Promise((resolve, reject) => {
505
- if (signal.aborted) {
506
- reject(signal.reason);
507
- return;
508
- }
509
- const timer = setTimeout(() => {
510
- signal.removeEventListener('abort', onAbort);
511
- resolve();
512
- }, milliseconds);
513
- const onAbort = () => {
514
- clearTimeout(timer);
515
- reject(signal.reason);
516
- };
517
- signal.addEventListener('abort', onAbort, { once: true });
518
- }),
519
- };
520
- //# sourceMappingURL=session-attendant.js.map