@agentmemory/agentmemory 0.9.30 → 0.9.31

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 (331) hide show
  1. package/.env.example +39 -33
  2. package/AGENTS.md +8 -8
  3. package/README.md +173 -558
  4. package/dist/.env.example +39 -33
  5. package/dist/cli.d.mts.map +1 -1
  6. package/dist/cli.mjs +1741 -966
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/compression-recovery-CnhLQC6N.mjs +5912 -0
  9. package/dist/compression-recovery-CnhLQC6N.mjs.map +1 -0
  10. package/dist/{connect-B4_ca6BD.mjs → connect-BSLIAMpw.mjs} +874 -267
  11. package/dist/connect-BSLIAMpw.mjs.map +1 -0
  12. package/dist/hooks/_capture.d.mts +5 -2
  13. package/dist/hooks/_capture.d.mts.map +1 -1
  14. package/dist/hooks/_capture.mjs +55 -19
  15. package/dist/hooks/_capture.mjs.map +1 -1
  16. package/dist/hooks/notification.mjs +20 -6
  17. package/dist/hooks/notification.mjs.map +1 -1
  18. package/dist/hooks/post-commit.mjs +25 -5
  19. package/dist/hooks/post-commit.mjs.map +1 -1
  20. package/dist/hooks/post-tool-failure.mjs +12 -4
  21. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  22. package/dist/hooks/post-tool-use.mjs +12 -4
  23. package/dist/hooks/post-tool-use.mjs.map +1 -1
  24. package/dist/hooks/pre-compact.mjs +37 -10
  25. package/dist/hooks/pre-compact.mjs.map +1 -1
  26. package/dist/hooks/pre-tool-use.mjs +25 -5
  27. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  28. package/dist/hooks/prompt-submit.mjs +12 -4
  29. package/dist/hooks/prompt-submit.mjs.map +1 -1
  30. package/dist/hooks/session-end.mjs +47 -16
  31. package/dist/hooks/session-end.mjs.map +1 -1
  32. package/dist/hooks/session-start.mjs +37 -10
  33. package/dist/hooks/session-start.mjs.map +1 -1
  34. package/dist/hooks/stop.mjs +74 -72
  35. package/dist/hooks/stop.mjs.map +1 -1
  36. package/dist/hooks/subagent-start.mjs +13 -5
  37. package/dist/hooks/subagent-start.mjs.map +1 -1
  38. package/dist/hooks/subagent-stop.mjs +18 -6
  39. package/dist/hooks/subagent-stop.mjs.map +1 -1
  40. package/dist/hooks/task-completed.mjs +13 -5
  41. package/dist/hooks/task-completed.mjs.map +1 -1
  42. package/dist/{image-refs-T_urbwHM.mjs → image-refs-Cyta4Lf9.mjs} +2 -2
  43. package/dist/{image-refs-T_urbwHM.mjs.map → image-refs-Cyta4Lf9.mjs.map} +1 -1
  44. package/dist/index.mjs +8539 -4203
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/integrations/dsh.d.mts +55 -0
  47. package/dist/integrations/dsh.d.mts.map +1 -0
  48. package/dist/integrations/dsh.mjs +1145 -0
  49. package/dist/integrations/dsh.mjs.map +1 -0
  50. package/dist/{keyed-mutex-DchXsyfP.mjs → keyed-mutex-DCUh7PG-.mjs} +58 -9
  51. package/dist/keyed-mutex-DCUh7PG-.mjs.map +1 -0
  52. package/dist/{secret-store-BnY3A50C.mjs → secret-store-D9GwO0xp.mjs} +535 -434
  53. package/dist/secret-store-D9GwO0xp.mjs.map +1 -0
  54. package/dist/session-arguments-zxsbbd8y.mjs +113 -0
  55. package/dist/session-arguments-zxsbbd8y.mjs.map +1 -0
  56. package/dist/{src-Jsq9LCeH.mjs → src-DaMtDzt_.mjs} +4087 -3788
  57. package/dist/src-DaMtDzt_.mjs.map +1 -0
  58. package/dist/{standalone-DKCdrf1d.mjs → standalone-BlqshyZ5.mjs} +213 -56
  59. package/dist/standalone-BlqshyZ5.mjs.map +1 -0
  60. package/dist/standalone.d.mts +1 -1
  61. package/dist/standalone.d.mts.map +1 -1
  62. package/dist/standalone.mjs +626 -73
  63. package/dist/standalone.mjs.map +1 -1
  64. package/dist/viewer/index.html +288 -140
  65. package/integrations/hermes/README.md +84 -0
  66. package/integrations/hermes/__init__.py +669 -0
  67. package/integrations/hermes/plugin.yaml +27 -0
  68. package/integrations/omp/LICENSE +190 -0
  69. package/integrations/omp/README.md +76 -0
  70. package/integrations/omp/capture.ts +1 -0
  71. package/integrations/omp/dist/index.mjs +1450 -0
  72. package/integrations/omp/index.ts +488 -0
  73. package/integrations/omp/package.json +13 -0
  74. package/integrations/omp/security.ts +67 -0
  75. package/integrations/omp/skills/agentmemory-agents/REFERENCE.md +32 -0
  76. package/integrations/omp/skills/agentmemory-agents/SKILL.md +48 -0
  77. package/integrations/omp/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  78. package/integrations/omp/skills/agentmemory-architecture/SKILL.md +33 -0
  79. package/integrations/omp/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  80. package/integrations/omp/skills/agentmemory-config/REFERENCE.md +90 -0
  81. package/integrations/omp/skills/agentmemory-config/SKILL.md +78 -0
  82. package/integrations/omp/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  83. package/integrations/omp/skills/agentmemory-hooks/REFERENCE.md +20 -0
  84. package/integrations/omp/skills/agentmemory-hooks/SKILL.md +49 -0
  85. package/integrations/omp/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  86. package/integrations/omp/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  87. package/integrations/omp/skills/agentmemory-mcp-tools/SKILL.md +42 -0
  88. package/integrations/omp/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  89. package/integrations/omp/skills/agentmemory-rest-api/REFERENCE.md +158 -0
  90. package/integrations/omp/skills/agentmemory-rest-api/SKILL.md +49 -0
  91. package/integrations/omp/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  92. package/integrations/omp/skills/commit-context/EXAMPLES.md +71 -0
  93. package/integrations/omp/skills/commit-context/SKILL.md +64 -0
  94. package/integrations/omp/skills/commit-context/TROUBLESHOOTING.md +70 -0
  95. package/integrations/omp/skills/commit-history/EXAMPLES.md +71 -0
  96. package/integrations/omp/skills/commit-history/SKILL.md +62 -0
  97. package/integrations/omp/skills/commit-history/TROUBLESHOOTING.md +70 -0
  98. package/integrations/omp/skills/forget/EXAMPLES.md +40 -0
  99. package/integrations/omp/skills/forget/SKILL.md +73 -0
  100. package/integrations/omp/skills/forget/TROUBLESHOOTING.md +70 -0
  101. package/integrations/omp/skills/handoff/EXAMPLES.md +57 -0
  102. package/integrations/omp/skills/handoff/SKILL.md +75 -0
  103. package/integrations/omp/skills/handoff/TROUBLESHOOTING.md +70 -0
  104. package/integrations/omp/skills/lesson/SKILL.md +64 -0
  105. package/integrations/omp/skills/lesson/TROUBLESHOOTING.md +70 -0
  106. package/integrations/omp/skills/memory-discipline/SKILL.md +65 -0
  107. package/integrations/omp/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  108. package/integrations/omp/skills/recall/EXAMPLES.md +74 -0
  109. package/integrations/omp/skills/recall/SKILL.md +74 -0
  110. package/integrations/omp/skills/recall/TROUBLESHOOTING.md +70 -0
  111. package/integrations/omp/skills/recap/EXAMPLES.md +62 -0
  112. package/integrations/omp/skills/recap/SKILL.md +68 -0
  113. package/integrations/omp/skills/recap/TROUBLESHOOTING.md +70 -0
  114. package/integrations/omp/skills/remember/EXAMPLES.md +67 -0
  115. package/integrations/omp/skills/remember/SKILL.md +66 -0
  116. package/integrations/omp/skills/remember/TROUBLESHOOTING.md +70 -0
  117. package/integrations/omp/skills/session-history/EXAMPLES.md +59 -0
  118. package/integrations/omp/skills/session-history/SKILL.md +67 -0
  119. package/integrations/omp/skills/session-history/TROUBLESHOOTING.md +70 -0
  120. package/integrations/omp/skills/write-agentmemory-skill/SKILL.md +41 -0
  121. package/integrations/omp/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  122. package/integrations/openclaw/LICENSE +190 -0
  123. package/integrations/openclaw/README.md +88 -0
  124. package/integrations/openclaw/openclaw.plugin.json +71 -0
  125. package/integrations/openclaw/package.json +38 -0
  126. package/integrations/openclaw/plugin.mjs +425 -0
  127. package/integrations/openclaw/plugin.yaml +26 -0
  128. package/integrations/openclaw/skills/agentmemory-agents/REFERENCE.md +32 -0
  129. package/integrations/openclaw/skills/agentmemory-agents/SKILL.md +48 -0
  130. package/integrations/openclaw/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  131. package/integrations/openclaw/skills/agentmemory-architecture/SKILL.md +33 -0
  132. package/integrations/openclaw/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  133. package/integrations/openclaw/skills/agentmemory-config/REFERENCE.md +90 -0
  134. package/integrations/openclaw/skills/agentmemory-config/SKILL.md +78 -0
  135. package/integrations/openclaw/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  136. package/integrations/openclaw/skills/agentmemory-hooks/REFERENCE.md +20 -0
  137. package/integrations/openclaw/skills/agentmemory-hooks/SKILL.md +49 -0
  138. package/integrations/openclaw/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  139. package/integrations/openclaw/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  140. package/integrations/openclaw/skills/agentmemory-mcp-tools/SKILL.md +42 -0
  141. package/integrations/openclaw/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  142. package/integrations/openclaw/skills/agentmemory-rest-api/REFERENCE.md +158 -0
  143. package/integrations/openclaw/skills/agentmemory-rest-api/SKILL.md +49 -0
  144. package/integrations/openclaw/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  145. package/integrations/openclaw/skills/commit-context/EXAMPLES.md +71 -0
  146. package/integrations/openclaw/skills/commit-context/SKILL.md +64 -0
  147. package/integrations/openclaw/skills/commit-context/TROUBLESHOOTING.md +70 -0
  148. package/integrations/openclaw/skills/commit-history/EXAMPLES.md +71 -0
  149. package/integrations/openclaw/skills/commit-history/SKILL.md +62 -0
  150. package/integrations/openclaw/skills/commit-history/TROUBLESHOOTING.md +70 -0
  151. package/integrations/openclaw/skills/forget/EXAMPLES.md +40 -0
  152. package/integrations/openclaw/skills/forget/SKILL.md +73 -0
  153. package/integrations/openclaw/skills/forget/TROUBLESHOOTING.md +70 -0
  154. package/integrations/openclaw/skills/handoff/EXAMPLES.md +57 -0
  155. package/integrations/openclaw/skills/handoff/SKILL.md +75 -0
  156. package/integrations/openclaw/skills/handoff/TROUBLESHOOTING.md +70 -0
  157. package/integrations/openclaw/skills/lesson/SKILL.md +64 -0
  158. package/integrations/openclaw/skills/lesson/TROUBLESHOOTING.md +70 -0
  159. package/integrations/openclaw/skills/memory-discipline/SKILL.md +65 -0
  160. package/integrations/openclaw/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  161. package/integrations/openclaw/skills/recall/EXAMPLES.md +74 -0
  162. package/integrations/openclaw/skills/recall/SKILL.md +74 -0
  163. package/integrations/openclaw/skills/recall/TROUBLESHOOTING.md +70 -0
  164. package/integrations/openclaw/skills/recap/EXAMPLES.md +62 -0
  165. package/integrations/openclaw/skills/recap/SKILL.md +68 -0
  166. package/integrations/openclaw/skills/recap/TROUBLESHOOTING.md +70 -0
  167. package/integrations/openclaw/skills/remember/EXAMPLES.md +67 -0
  168. package/integrations/openclaw/skills/remember/SKILL.md +66 -0
  169. package/integrations/openclaw/skills/remember/TROUBLESHOOTING.md +70 -0
  170. package/integrations/openclaw/skills/session-history/EXAMPLES.md +59 -0
  171. package/integrations/openclaw/skills/session-history/SKILL.md +67 -0
  172. package/integrations/openclaw/skills/session-history/TROUBLESHOOTING.md +70 -0
  173. package/integrations/openclaw/skills/write-agentmemory-skill/SKILL.md +41 -0
  174. package/integrations/openclaw/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  175. package/integrations/pi/LICENSE +190 -0
  176. package/integrations/pi/README.md +45 -51
  177. package/integrations/pi/dist/index.mjs +1379 -0
  178. package/integrations/pi/index.ts +138 -171
  179. package/integrations/pi/package.json +16 -6
  180. package/integrations/pi/security.ts +6 -26
  181. package/integrations/pi/skills/agentmemory-agents/REFERENCE.md +32 -0
  182. package/integrations/pi/skills/agentmemory-agents/SKILL.md +48 -0
  183. package/integrations/pi/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  184. package/integrations/pi/skills/agentmemory-architecture/SKILL.md +33 -0
  185. package/integrations/pi/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  186. package/integrations/pi/skills/agentmemory-config/REFERENCE.md +90 -0
  187. package/integrations/pi/skills/agentmemory-config/SKILL.md +78 -0
  188. package/integrations/pi/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  189. package/integrations/pi/skills/agentmemory-hooks/REFERENCE.md +20 -0
  190. package/integrations/pi/skills/agentmemory-hooks/SKILL.md +49 -0
  191. package/integrations/pi/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  192. package/integrations/pi/skills/agentmemory-mcp-tools/REFERENCE.md +66 -0
  193. package/integrations/pi/skills/agentmemory-mcp-tools/SKILL.md +42 -0
  194. package/integrations/pi/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  195. package/integrations/pi/skills/agentmemory-rest-api/REFERENCE.md +158 -0
  196. package/integrations/pi/skills/agentmemory-rest-api/SKILL.md +49 -0
  197. package/integrations/pi/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  198. package/integrations/pi/skills/commit-context/EXAMPLES.md +71 -0
  199. package/integrations/pi/skills/commit-context/SKILL.md +64 -0
  200. package/integrations/pi/skills/commit-context/TROUBLESHOOTING.md +70 -0
  201. package/integrations/pi/skills/commit-history/EXAMPLES.md +71 -0
  202. package/integrations/pi/skills/commit-history/SKILL.md +62 -0
  203. package/integrations/pi/skills/commit-history/TROUBLESHOOTING.md +70 -0
  204. package/integrations/pi/skills/forget/EXAMPLES.md +40 -0
  205. package/integrations/pi/skills/forget/SKILL.md +73 -0
  206. package/integrations/pi/skills/forget/TROUBLESHOOTING.md +70 -0
  207. package/integrations/pi/skills/handoff/EXAMPLES.md +57 -0
  208. package/integrations/pi/skills/handoff/SKILL.md +75 -0
  209. package/integrations/pi/skills/handoff/TROUBLESHOOTING.md +70 -0
  210. package/integrations/pi/skills/lesson/SKILL.md +64 -0
  211. package/integrations/pi/skills/lesson/TROUBLESHOOTING.md +70 -0
  212. package/integrations/pi/skills/memory-discipline/SKILL.md +65 -0
  213. package/integrations/pi/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  214. package/integrations/pi/skills/recall/EXAMPLES.md +74 -0
  215. package/integrations/pi/skills/recall/SKILL.md +74 -0
  216. package/integrations/pi/skills/recall/TROUBLESHOOTING.md +70 -0
  217. package/integrations/pi/skills/recap/EXAMPLES.md +62 -0
  218. package/integrations/pi/skills/recap/SKILL.md +68 -0
  219. package/integrations/pi/skills/recap/TROUBLESHOOTING.md +70 -0
  220. package/integrations/pi/skills/remember/EXAMPLES.md +67 -0
  221. package/integrations/pi/skills/remember/SKILL.md +66 -0
  222. package/integrations/pi/skills/remember/TROUBLESHOOTING.md +70 -0
  223. package/integrations/pi/skills/session-history/EXAMPLES.md +59 -0
  224. package/integrations/pi/skills/session-history/SKILL.md +67 -0
  225. package/integrations/pi/skills/session-history/TROUBLESHOOTING.md +70 -0
  226. package/integrations/pi/skills/write-agentmemory-skill/SKILL.md +41 -0
  227. package/integrations/pi/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  228. package/package.json +32 -11
  229. package/plugin/.claude-plugin/plugin.json +1 -1
  230. package/plugin/.codex-plugin/plugin.json +4 -3
  231. package/plugin/.devin-plugin/plugin.json +5 -3
  232. package/plugin/cursor/hooks.json +23 -8
  233. package/plugin/hooks.json +67 -0
  234. package/plugin/opencode/README.md +1 -1
  235. package/plugin/opencode/agentmemory-capture.ts +25 -11
  236. package/plugin/opencode/plugin.json +1 -1
  237. package/plugin/plugin.json +1 -1
  238. package/plugin/scripts/_capture.d.mts +5 -2
  239. package/plugin/scripts/_capture.d.mts.map +1 -1
  240. package/plugin/scripts/_capture.mjs +55 -19
  241. package/plugin/scripts/_capture.mjs.map +1 -1
  242. package/plugin/scripts/cursor/run-detached.d.mts +1 -0
  243. package/plugin/scripts/cursor/run-detached.mjs +786 -0
  244. package/plugin/scripts/cursor/run-detached.mjs.map +1 -0
  245. package/plugin/scripts/cursor/run-hook.d.mts +1 -0
  246. package/plugin/scripts/cursor/run-hook.mjs +704 -0
  247. package/plugin/scripts/cursor/run-hook.mjs.map +1 -0
  248. package/plugin/scripts/notification.mjs +20 -6
  249. package/plugin/scripts/notification.mjs.map +1 -1
  250. package/plugin/scripts/plugin-bridge.mjs +37 -6
  251. package/plugin/scripts/post-commit.mjs +25 -5
  252. package/plugin/scripts/post-commit.mjs.map +1 -1
  253. package/plugin/scripts/post-tool-failure.mjs +12 -4
  254. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  255. package/plugin/scripts/post-tool-use.mjs +12 -4
  256. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  257. package/plugin/scripts/pre-compact.mjs +37 -10
  258. package/plugin/scripts/pre-compact.mjs.map +1 -1
  259. package/plugin/scripts/pre-tool-use.mjs +25 -5
  260. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  261. package/plugin/scripts/prompt-submit.mjs +12 -4
  262. package/plugin/scripts/prompt-submit.mjs.map +1 -1
  263. package/plugin/scripts/session-end.mjs +47 -16
  264. package/plugin/scripts/session-end.mjs.map +1 -1
  265. package/plugin/scripts/session-start.mjs +37 -10
  266. package/plugin/scripts/session-start.mjs.map +1 -1
  267. package/plugin/scripts/stop.mjs +74 -72
  268. package/plugin/scripts/stop.mjs.map +1 -1
  269. package/plugin/scripts/subagent-start.mjs +13 -5
  270. package/plugin/scripts/subagent-start.mjs.map +1 -1
  271. package/plugin/scripts/subagent-stop.mjs +18 -6
  272. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  273. package/plugin/scripts/task-completed.mjs +13 -5
  274. package/plugin/scripts/task-completed.mjs.map +1 -1
  275. package/plugin/skills/_shared/TROUBLESHOOTING.md +21 -7
  276. package/plugin/skills/agentmemory-agents/REFERENCE.md +7 -6
  277. package/plugin/skills/agentmemory-agents/SKILL.md +19 -5
  278. package/plugin/skills/agentmemory-agents/TROUBLESHOOTING.md +70 -0
  279. package/plugin/skills/agentmemory-architecture/SKILL.md +4 -4
  280. package/plugin/skills/agentmemory-architecture/TROUBLESHOOTING.md +70 -0
  281. package/plugin/skills/agentmemory-config/REFERENCE.md +12 -15
  282. package/plugin/skills/agentmemory-config/SKILL.md +7 -3
  283. package/plugin/skills/agentmemory-config/TROUBLESHOOTING.md +70 -0
  284. package/plugin/skills/agentmemory-hooks/SKILL.md +15 -5
  285. package/plugin/skills/agentmemory-hooks/TROUBLESHOOTING.md +70 -0
  286. package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +4 -4
  287. package/plugin/skills/agentmemory-mcp-tools/SKILL.md +6 -3
  288. package/plugin/skills/agentmemory-mcp-tools/TROUBLESHOOTING.md +70 -0
  289. package/plugin/skills/agentmemory-rest-api/REFERENCE.md +9 -1
  290. package/plugin/skills/agentmemory-rest-api/SKILL.md +12 -6
  291. package/plugin/skills/agentmemory-rest-api/TROUBLESHOOTING.md +70 -0
  292. package/plugin/skills/commit-context/SKILL.md +1 -1
  293. package/plugin/skills/commit-context/TROUBLESHOOTING.md +70 -0
  294. package/plugin/skills/commit-history/SKILL.md +1 -1
  295. package/plugin/skills/commit-history/TROUBLESHOOTING.md +70 -0
  296. package/plugin/skills/forget/EXAMPLES.md +19 -54
  297. package/plugin/skills/forget/SKILL.md +16 -12
  298. package/plugin/skills/forget/TROUBLESHOOTING.md +70 -0
  299. package/plugin/skills/handoff/EXAMPLES.md +5 -4
  300. package/plugin/skills/handoff/SKILL.md +10 -5
  301. package/plugin/skills/handoff/TROUBLESHOOTING.md +70 -0
  302. package/plugin/skills/lesson/SKILL.md +1 -1
  303. package/plugin/skills/lesson/TROUBLESHOOTING.md +70 -0
  304. package/plugin/skills/memory-discipline/SKILL.md +1 -1
  305. package/plugin/skills/memory-discipline/TROUBLESHOOTING.md +70 -0
  306. package/plugin/skills/recall/SKILL.md +18 -7
  307. package/plugin/skills/recall/TROUBLESHOOTING.md +70 -0
  308. package/plugin/skills/recap/EXAMPLES.md +5 -3
  309. package/plugin/skills/recap/SKILL.md +11 -6
  310. package/plugin/skills/recap/TROUBLESHOOTING.md +70 -0
  311. package/plugin/skills/remember/EXAMPLES.md +3 -3
  312. package/plugin/skills/remember/SKILL.md +1 -1
  313. package/plugin/skills/remember/TROUBLESHOOTING.md +70 -0
  314. package/plugin/skills/session-history/EXAMPLES.md +2 -2
  315. package/plugin/skills/session-history/SKILL.md +10 -4
  316. package/plugin/skills/session-history/TROUBLESHOOTING.md +70 -0
  317. package/plugin/skills/write-agentmemory-skill/SKILL.md +2 -1
  318. package/plugin/skills/write-agentmemory-skill/TROUBLESHOOTING.md +70 -0
  319. package/dist/connect-B4_ca6BD.mjs.map +0 -1
  320. package/dist/docker-compose.yml +0 -73
  321. package/dist/iii-config.docker.yaml +0 -53
  322. package/dist/iii-config.yaml +0 -63
  323. package/dist/keyed-mutex-DchXsyfP.mjs.map +0 -1
  324. package/dist/secret-store-BnY3A50C.mjs.map +0 -1
  325. package/dist/src-Jsq9LCeH.mjs.map +0 -1
  326. package/dist/standalone-DKCdrf1d.mjs.map +0 -1
  327. package/dist/status-IRz7ta8-.mjs +0 -2344
  328. package/dist/status-IRz7ta8-.mjs.map +0 -1
  329. package/docker-compose.yml +0 -73
  330. package/iii-config.docker.yaml +0 -53
  331. package/iii-config.yaml +0 -63
@@ -1521,28 +1521,17 @@
1521
1521
  var params = new URLSearchParams(window.location.search);
1522
1522
  var paramPort = params.get('port');
1523
1523
  var locPort = window.location.port;
1524
- var wsProto = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
1525
- var REST, WS_URL, WS_DIRECT_URL, wsPort;
1524
+ var REST;
1526
1525
  if (paramPort) {
1527
1526
  var resolvedPort = parseInt(paramPort) === 3111 ? '3113' : paramPort;
1528
1527
  REST = window.location.protocol + '//' + window.location.hostname + ':' + resolvedPort;
1529
- wsPort = params.get('wsPort') || String(parseInt(resolvedPort) - 1);
1530
- WS_URL = wsProto + '//' + window.location.hostname + ':' + wsPort;
1531
- WS_DIRECT_URL = WS_URL + '/stream/mem-live/viewer';
1532
1528
  } else if (locPort) {
1533
1529
  var resolvedPort = parseInt(locPort) === 3111 ? '3113' : locPort;
1534
1530
  REST = window.location.protocol + '//' + window.location.hostname + ':' + resolvedPort;
1535
- wsPort = params.get('wsPort') || String(parseInt(resolvedPort) - 1);
1536
- WS_URL = wsProto + '//' + window.location.hostname + ':' + wsPort;
1537
- WS_DIRECT_URL = WS_URL + '/stream/mem-live/viewer';
1538
1531
  } else {
1539
1532
  REST = window.location.origin;
1540
- wsPort = params.get('wsPort');
1541
- WS_URL = wsPort
1542
- ? wsProto + '//' + window.location.hostname + ':' + wsPort
1543
- : wsProto + '//' + window.location.host;
1544
- WS_DIRECT_URL = WS_URL + '/stream/mem-live/viewer';
1545
1533
  }
1534
+ var LIVE_EVENTS_URL = REST + '/agentmemory/viewer/events';
1546
1535
 
1547
1536
  var dateEl = document.getElementById('dateline');
1548
1537
  if (dateEl) dateEl.textContent = new Date().toLocaleDateString('en-US', { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric' });
@@ -1654,9 +1643,9 @@
1654
1643
  eventLoop: { term: 'Event loop delay', text: 'How far behind the worker is in handling work. Under 20 ms is healthy; a large value means it is busy or blocked.' },
1655
1644
  uptime: { term: 'Uptime', text: 'How long the agentmemory worker has been running since it last started.' },
1656
1645
  health: { term: 'Health status', text: 'Healthy means everything is normal. Degraded means something needs attention but memory still works. Critical means a core part is failing.' },
1657
- stateBackend: { term: 'State store', text: 'Where the engine keeps every record: file (local files on this machine, the default) or redis (a Redis server set by AGENTMEMORY_STATE_BACKEND and AGENTMEMORY_REDIS_URL). ok means a test write and read just succeeded; the number is how long it took.' },
1658
- engineConnection: { term: 'Engine connection', text: 'Whether the agentmemory worker is connected to the iii engine that stores its data and runs its functions.' },
1659
- live: { term: 'Live updates', text: 'The viewer keeps one streaming connection open and updates every page the moment data changes. Nothing is refreshed on a timer.' },
1646
+ stateBackend: { term: 'State store', text: 'Where the state worker keeps every record: file (local files on this machine, the default) or redis (a Redis server set by AGENTMEMORY_STATE_BACKEND and AGENTMEMORY_REDIS_URL). ok means a test write and read just succeeded; the number is how long it took.' },
1647
+ engineConnection: { term: 'Engine connection', text: 'Whether the agentmemory worker is connected to the iii engine that routes calls between workers.' },
1648
+ live: { term: 'Live updates', text: 'The viewer keeps one SSE connection open and updates every page the moment data changes. Nothing is refreshed on a timer.' },
1660
1649
  flags: { term: 'Feature flags', text: 'Optional features that are on or off in this install. Off features leave some pages empty; each one lists the setting that turns it on.' },
1661
1650
  audit: { term: 'Audit log', text: 'A record of every change agentmemory made to stored data: saves, deletes, imports, consolidation runs. Use it to see who changed what and when.' },
1662
1651
  workers: { term: 'Workers', text: 'Processes connected to the iii engine. agentmemory runs as one worker.' },
@@ -1670,20 +1659,19 @@
1670
1659
  var state = {
1671
1660
  activeTab: 'dashboard',
1672
1661
  dashboard: { loaded: false },
1673
- graph: { loaded: false, loading: false, dirty: false, nodeMap: {}, edgeMap: {}, degrees: {}, rev: 0, localDegree: null, localDegreeRev: -1, stats: null, hidden: {}, search: '', searchOpen: false, searchIndex: 0, selectedId: null, detail: null, queryError: null, disabledInfo: null, warning: '', truncated: false, totalNodes: 0, totalEdges: 0, requestSeq: 0, fetches: 0, patches: 0, lastPatchAt: 0, fresh: {}, simStale: false, rebuildResult: '' },
1662
+ graph: { loaded: false, loading: false, dirty: false, nodeMap: {}, edgeMap: {}, degrees: {}, rev: 0, localDegree: null, localDegreeRev: -1, stats: null, hidden: {}, search: '', searchOpen: false, searchIndex: 0, selectedId: null, detail: null, queryError: null, disabledInfo: null, warning: '', truncated: false, totalNodes: 0, totalEdges: 0, requestSeq: 0, searchRequestSeq: 0, resetSeq: 0, generation: undefined, fetches: 0, patches: 0, lastPatchAt: 0, fresh: {}, simStale: false, rebuildResult: '' },
1674
1663
  memories: { search: '', typeFilter: '', projectFilter: '', agentFilter: '', selectedId: null, nextCursor: null, loadingMore: false, searchIds: null, searchMode: '', searching: false, query: null, facets: null, checked: {}, editing: null, saving: false, pendingId: null, fetches: {}, diffId: null, error: '' },
1675
1664
  sessions: { selectedId: null, autoSelected: false, closed: false, nextCursor: null, loadingMore: false, filters: { q: '', project: '', agent: '', status: '' }, query: null, facets: null, detail: null, pendingObs: null, notice: null, summarizing: '' },
1676
1665
  audit: { opFilter: '', selectedIdx: null, from: '', to: '', q: '', query: null, loadingMore: false, exhausted: {}, fresh: {} },
1677
1666
  activity: { loaded: false, observations: [], sessionCount: 0, typeFilter: '', fileFilter: '', day: '' },
1678
1667
  lessons: { search: '', selectedId: null, projectFilter: '', notice: null, busy: '' },
1679
- actions: { frontier: [], statusFilter: '', search: '', selectedId: null, notice: null, busy: '' },
1668
+ actions: { frontier: [], statusFilter: '', search: '', selectedId: null, notice: null, busy: '', detailLoading: '', detailAttemptedId: '', detailRequestFailedId: '', loadError: false },
1680
1669
  crystals: { search: '', selectedId: null },
1681
1670
  profile: { loaded: false, selectedProject: '', data: null, dataProject: '', userPicked: false, notice: null },
1682
1671
  health: { error: null, checking: false, consolidating: false, reveal: '' },
1683
1672
  replay: { loaded: false, selectedId: '', timeline: null, cursor: 0, playing: false, speed: 1, timer: null, startAt: 0, offsetAt: 0, loadingTimeline: false, loadError: '', importPath: '', importing: false, importOpen: false, importStatus: null },
1684
1673
  flagsOpen: false,
1685
1674
  freshRows: {},
1686
- ws: null
1687
1675
  };
1688
1676
 
1689
1677
  var store = {
@@ -1714,7 +1702,7 @@
1714
1702
  memory: { path: 'memories?latest=true&limit=100&facets=true', key: 'memories' },
1715
1703
  session: { path: 'sessions?limit=100&facets=true', key: 'sessions' },
1716
1704
  lesson: { path: 'lessons', key: 'lessons' },
1717
- action: { path: 'actions', key: 'actions' },
1705
+ action: { path: 'actions', key: 'actions', allPages: true },
1718
1706
  crystal: { path: 'crystals', key: 'crystals' },
1719
1707
  semantic: { path: 'semantic', key: 'facts', fallbackKey: 'semantic' },
1720
1708
  procedural: { path: 'procedural', key: 'procedures', fallbackKey: 'procedural' },
@@ -2314,6 +2302,20 @@
2314
2302
  store.entities[kind] = bucket;
2315
2303
  }
2316
2304
 
2305
+ async function loadAllPages(spec, apiGet) {
2306
+ var rows = [];
2307
+ var cursor = null;
2308
+ var result;
2309
+ do {
2310
+ var path = spec.path + '?limit=500' + (cursor ? '&cursor=' + encodeURIComponent(cursor) : '');
2311
+ result = await apiGet(path);
2312
+ if (result === null) return null;
2313
+ rows.push.apply(rows, listOr([], result, spec.key, spec.fallbackKey));
2314
+ cursor = result.nextCursor || null;
2315
+ } while (cursor);
2316
+ return Object.assign({}, result, { [spec.key]: rows });
2317
+ }
2318
+
2317
2319
  async function ensureLoaded(kind) {
2318
2320
  if (store.loaded[kind]) return;
2319
2321
  if (store.loading[kind]) return store.loading[kind];
@@ -2323,8 +2325,16 @@
2323
2325
  try {
2324
2326
  await bootGate;
2325
2327
  var seqAtStart = eventSeq;
2326
- var result = await apiGet(spec.path);
2327
- if (result === null) return;
2328
+ var result;
2329
+ if (spec.allPages) {
2330
+ result = await loadAllPages(spec, apiGet);
2331
+ } else {
2332
+ result = await apiGet(spec.path);
2333
+ }
2334
+ if (result === null) {
2335
+ if (kind === 'action') state.actions.loadError = true;
2336
+ return;
2337
+ }
2328
2338
  replaceBucket(kind, listOr([], result, spec.key, spec.fallbackKey));
2329
2339
  if (kind === 'memory') {
2330
2340
  state.memories.nextCursor = result.nextCursor || null;
@@ -2343,6 +2353,9 @@
2343
2353
  }
2344
2354
  replayEventsSince(seqAtStart, kind);
2345
2355
  store.loaded[kind] = true;
2356
+ if (kind === 'action') state.actions.loadError = false;
2357
+ } catch {
2358
+ if (kind === 'action') state.actions.loadError = true;
2346
2359
  } finally {
2347
2360
  store.loading[kind] = null;
2348
2361
  }
@@ -2358,11 +2371,14 @@
2358
2371
  });
2359
2372
  }
2360
2373
 
2361
- async function loadSnapshot() {
2374
+ async function loadSnapshot(retried) {
2375
+ var resetSeq = state.graph.resetSeq;
2362
2376
  var snap = await apiGet('viewer/snapshot');
2377
+ if (resetSeq !== state.graph.resetSeq) return retried ? false : loadSnapshot(true);
2363
2378
  if (!snap) return false;
2364
2379
  var graphBefore = store.graph && store.graph.updatedAt;
2365
2380
  var reconnect = !!store.snapshotAt;
2381
+ noteGraphGeneration(snap.graph);
2366
2382
  applySnapshot(store, snap);
2367
2383
  renderFlagsChip();
2368
2384
  renderFlagsPanel();
@@ -2806,8 +2822,54 @@
2806
2822
  return graphNodeList().filter(function(n) { return !deg[n.id]; }).length;
2807
2823
  }
2808
2824
 
2825
+ function clearGraphView() {
2826
+ var g = state.graph;
2827
+ g.resetSeq++;
2828
+ g.requestSeq++;
2829
+ g.searchRequestSeq++;
2830
+ g.selectedId = null;
2831
+ g.detail = null;
2832
+ g.nodeMap = {};
2833
+ g.edgeMap = {};
2834
+ g.degrees = {};
2835
+ g.fresh = {};
2836
+ g.stats = null;
2837
+ g.totalNodes = 0;
2838
+ g.totalEdges = 0;
2839
+ g.truncated = false;
2840
+ g.loading = false;
2841
+ g.loaded = false;
2842
+ g.dirty = false;
2843
+ g.queryError = null;
2844
+ g.disabledInfo = null;
2845
+ g.warning = '';
2846
+ g.searchOpen = false;
2847
+ g.rev++;
2848
+ g.simStale = true;
2849
+ if (graphReloadTimer) clearTimeout(graphReloadTimer);
2850
+ graphReloadTimer = null;
2851
+ graphSim.hoverId = null;
2852
+ graphSim.drag = null;
2853
+ graphSim.nodes = [];
2854
+ graphSim.byId = {};
2855
+ graphSim.edges = [];
2856
+ graphSim.isolatedBox = null;
2857
+ setRouteEntity('graph', '');
2858
+ renderGraphDetail();
2859
+ requestGraphDraw();
2860
+ }
2861
+
2862
+ function noteGraphGeneration(data) {
2863
+ if (!data) return;
2864
+ var g = state.graph;
2865
+ if (data.reset === true && data.source === 'items') store.graph = null;
2866
+ if (data.reset === true || ('generation' in data && g.generation !== undefined && g.generation !== data.generation)) clearGraphView();
2867
+ if ('generation' in data) g.generation = data.generation;
2868
+ }
2869
+
2809
2870
  function setGraphData(result) {
2810
2871
  var g = state.graph;
2872
+ if (!(result.nodes || []).length && !result.truncated && !result.totalNodes && (g.selectedId || g.detail || Object.keys(g.nodeMap).length)) clearGraphView();
2811
2873
  var nodeMap = {};
2812
2874
  var edgeMap = {};
2813
2875
  (result.nodes || []).forEach(function(n) { if (n && n.id) nodeMap[n.id] = n; });
@@ -2841,6 +2903,7 @@
2841
2903
  g.loading = false;
2842
2904
  if (result === null) {
2843
2905
  var stats = await api('graph/stats', { readErrorBody: true });
2906
+ if (seq !== g.requestSeq) return;
2844
2907
  if (stats && stats.error && stats.flag) {
2845
2908
  g.disabledInfo = stats;
2846
2909
  g.queryError = null;
@@ -2865,8 +2928,9 @@
2865
2928
 
2866
2929
  function applyGraphDelta(delta) {
2867
2930
  var g = state.graph;
2868
- if (!g.loaded || !delta) return false;
2931
+ if (!delta || (!g.loaded && !delta.full)) return false;
2869
2932
  if (delta.full) {
2933
+ if (!(delta.nodes || []).length && (g.selectedId || g.detail || Object.keys(g.nodeMap).length)) clearGraphView();
2870
2934
  var keepNodes = {};
2871
2935
  (delta.nodes || []).forEach(function(n) { keepNodes[n.id] = true; });
2872
2936
  Object.keys(g.nodeMap).forEach(function(id) { if (!keepNodes[id] && id !== g.selectedId) delete g.nodeMap[id]; });
@@ -2893,13 +2957,14 @@
2893
2957
  g.lastPatchAt = Date.now();
2894
2958
  Object.keys(touched).forEach(function(id) { g.fresh[id] = Date.now(); });
2895
2959
  if (g.selectedId && touched[g.selectedId] && g.detail && g.detail.id === g.selectedId) g.detail.stale = true;
2960
+ g.loaded = true;
2896
2961
  return true;
2897
2962
  }
2898
2963
 
2899
2964
  function handleGraphChange(data) {
2900
2965
  var g = state.graph;
2901
2966
  if (!data) return;
2902
- if (!g.loaded) { g.dirty = true; return; }
2967
+ noteGraphGeneration(data);
2903
2968
  if (data.source === 'snapshot') {
2904
2969
  if (data.delta && applyGraphDelta(data.delta)) {
2905
2970
  if (graphSim.canvas && state.activeTab === 'graph') { syncGraphSim(false); requestGraphDraw(); setTimeout(requestGraphDraw, 4200); }
@@ -2925,10 +2990,10 @@
2925
2990
 
2926
2991
  async function fetchGraphDetail(id) {
2927
2992
  var g = state.graph;
2928
- g.detail = { id: id, loading: true, data: null, error: '', stale: false };
2993
+ var detail = g.detail = { id: id, loading: true, data: null, error: '', stale: false };
2929
2994
  renderGraphDetail();
2930
2995
  var res = await api('graph/node?id=' + encodeURIComponent(id), { readErrorBody: true });
2931
- if (!g.detail || g.detail.id !== id) return;
2996
+ if (g.detail !== detail) return;
2932
2997
  if (!res || res.error) {
2933
2998
  g.detail = { id: id, loading: false, data: null, error: res && res.error ? res.error : 'request failed', stale: false };
2934
2999
  } else {
@@ -3671,7 +3736,7 @@
3671
3736
  html += copyBtn('memory_graph_query { "startNodeId": ' + JSON.stringify(id) + ', "maxDepth": 1 }', 'MCP call').replace('>copy<', '>copy MCP call<');
3672
3737
  html += copyBtn(curlGet('graph/node?id=' + encodeURIComponent(id)), 'curl command').replace('>copy<', '>copy curl<');
3673
3738
  html += '</div>';
3674
- if (d && d.stale) html += '<div class="mem-note">This node changed on the live stream. <button type="button" class="linkish" data-action="graph-refresh-detail" data-node-id="' + esc(id) + '">Reload sources</button></div>';
3739
+ if (d && d.stale) html += '<div class="mem-note">This node changed on the SSE feed. <button type="button" class="linkish" data-action="graph-refresh-detail" data-node-id="' + esc(id) + '">Reload sources</button></div>';
3675
3740
 
3676
3741
  var rels = graphDetailRelations(id);
3677
3742
  var relTotal = data && typeof data.relationsTotal === 'number' ? Math.max(data.relationsTotal, rels.length) : rels.length;
@@ -3796,7 +3861,7 @@
3796
3861
  html += '<div class="empty-step-label" style="margin-top:8px;">Query it from an agent:</div>' + cmdBlock('memory_graph_query { "query": "checkout" }');
3797
3862
  html += '</div>';
3798
3863
  if (g.rebuildResult) html += '<div class="mem-note">' + esc(g.rebuildResult) + '</div>';
3799
- html += '<div class="go-live">' + (g.patches ? 'Patched ' + fmtNum(g.patches) + ' time' + (g.patches === 1 ? '' : 's') + ' from the live stream, last ' + relTime(new Date(g.lastPatchAt).toISOString()) + '.' : 'Updates arrive on the live stream; nothing refreshes on a timer.') + ' ' + help('live') + '</div>';
3864
+ html += '<div class="go-live">' + (g.patches ? 'Patched ' + fmtNum(g.patches) + ' time' + (g.patches === 1 ? '' : 's') + ' from the SSE feed, last ' + relTime(new Date(g.lastPatchAt).toISOString()) + '.' : 'Updates arrive on the SSE feed; nothing refreshes on a timer.') + ' ' + help('live') + '</div>';
3800
3865
  html += '</div>';
3801
3866
  setViewHtml(host, html);
3802
3867
  }
@@ -4021,9 +4086,11 @@
4021
4086
 
4022
4087
  async function searchGraphRemote(q) {
4023
4088
  var g = state.graph;
4089
+ var seq = ++g.searchRequestSeq;
4024
4090
  var box = document.getElementById('graph-search-results');
4025
4091
  if (box) { box.hidden = false; box.innerHTML = '<div class="gs-empty">Searching all nodes…</div>'; }
4026
4092
  var res = await apiPost('graph/query', { query: q, limit: 50 });
4093
+ if (seq !== g.searchRequestSeq) return;
4027
4094
  if (!res) {
4028
4095
  if (box) box.innerHTML = '<div class="gs-empty">The server search failed.</div>';
4029
4096
  return;
@@ -4097,7 +4164,7 @@
4097
4164
  try {
4098
4165
  var result = await apiPost('graph/build', {});
4099
4166
  g.rebuildResult = result && result.success
4100
- ? 'Rebuilt from ' + (result.sessions || 0) + ' sessions: ' + (result.nodes || 0) + ' nodes and ' + (result.edges || 0) + ' relations extracted. New ones arrive on the live stream.'
4167
+ ? 'Rebuilt from ' + (result.sessions || 0) + ' sessions: ' + (result.nodes || 0) + ' nodes and ' + (result.edges || 0) + ' relations extracted. New ones arrive on the SSE feed.'
4101
4168
  : 'The rebuild failed. Check the server log.';
4102
4169
  } finally {
4103
4170
  graphRebuilding = false;
@@ -4505,7 +4572,7 @@
4505
4572
  var mem = store.entities.memory[id];
4506
4573
  if (!mem) {
4507
4574
  if (m.pendingId === id) {
4508
- setViewHtml(panel, '<div class="detail-panel mem-detail"><div class="mem-detail-head"><span>Saving the new version</span>' + memoryCloseBtn() + '</div><div class="mem-note">Saved. Waiting for the new version to arrive on the live stream…</div>' + skeleton(4) + '</div>');
4575
+ setViewHtml(panel, '<div class="detail-panel mem-detail"><div class="mem-detail-head"><span>Saving the new version</span>' + memoryCloseBtn() + '</div><div class="mem-note">Saved. Waiting for the new version to arrive on the SSE feed…</div>' + skeleton(4) + '</div>');
4509
4576
  return;
4510
4577
  }
4511
4578
  if (!m.fetches[id]) fetchMemoryVersion(id);
@@ -6404,7 +6471,7 @@
6404
6471
  var res = await apiPost('lessons/strengthen', { lessonId: id });
6405
6472
  l.busy = '';
6406
6473
  l.notice = res && res.success !== false
6407
- ? { id: id, html: 'Recorded one more use. Confidence and uses update here from the live stream.' }
6474
+ ? { id: id, html: 'Recorded one more use. Confidence and uses update here from the SSE feed.' }
6408
6475
  : { id: id, err: true, html: 'Strengthen failed' + (res && res.error ? ': ' + esc(res.error) : '. Check that agentmemory is running.') };
6409
6476
  renderLessons();
6410
6477
  }
@@ -6497,12 +6564,15 @@
6497
6564
  function renderActions() {
6498
6565
  var el = document.getElementById('view-actions');
6499
6566
  var a = state.actions;
6500
- if (!store.loaded.action) {
6567
+ var all = entityList('action');
6568
+ if (!store.loaded.action && !all.length) {
6501
6569
  listDetailShell(el, 'actions', '');
6502
- setViewHtml(document.getElementById('actions-list'), skeleton(8));
6570
+ setViewHtml(document.getElementById('actions-list'), a.loadError
6571
+ ? '<div class="empty-state compact"><div class="empty-title">Could not load actions</div><div class="empty-what">Check that agentmemory is running, then try again.</div><button type="button" class="btn" data-action="action-list-retry">Retry</button></div>'
6572
+ : skeleton(8));
6573
+ renderActionDetail({});
6503
6574
  return;
6504
6575
  }
6505
- var all = entityList('action');
6506
6576
  var rows = actionRows();
6507
6577
  var frontierIds = {};
6508
6578
  (a.frontier || []).forEach(function(f) {
@@ -6560,24 +6630,82 @@
6560
6630
  html += '</tbody></table></div>';
6561
6631
  if (all.length < 8) html += usageCard('action');
6562
6632
  }
6633
+ if (a.loadError) {
6634
+ html = '<div class="inline-note"><strong>Could not refresh actions.</strong> Showing previously loaded actions. <button type="button" class="btn" data-action="action-list-retry">Retry</button></div>' + html;
6635
+ }
6563
6636
  setViewHtml(document.getElementById('actions-list'), html);
6637
+ renderActionDetail(frontierIds);
6638
+ }
6639
+
6640
+ function renderActionDetail(frontierIds) {
6641
+ var a = state.actions;
6564
6642
  var detail = document.getElementById('actions-detail');
6565
6643
  var sel = a.selectedId ? store.entities.action[a.selectedId] : null;
6566
6644
  if (a.selectedId && !sel) {
6567
- setViewHtml(detail, '<div id="action-detail" class="detail-panel mem-detail"><div class="mem-detail-head"><span>Action</span><button type="button" class="icon-btn icon-close" data-action="action-close" aria-label="Close action details">close</button></div>' + emptyState({
6645
+ ensureActionById(a.selectedId);
6646
+ var loadingAction = a.detailLoading === a.selectedId;
6647
+ var actionBody = loadingAction ? '<div class="empty-state compact"><div class="empty-title">Loading action…</div></div>' : a.detailRequestFailedId === a.selectedId
6648
+ ? '<div class="empty-state compact"><div class="empty-title">Could not load action</div><div class="empty-what">Check that agentmemory is running, then try again.</div><button type="button" class="btn" data-action="action-retry">Retry</button></div>'
6649
+ : emptyState({
6568
6650
  compact: true,
6569
6651
  icon: '&#128269;',
6570
6652
  title: 'Action not found',
6571
6653
  what: 'Action links anywhere in the viewer open this panel by id.',
6572
6654
  why: 'No action has the id <code>' + esc(a.selectedId) + '</code>. It was removed, or the link comes from another install.',
6573
6655
  steps: [{ label: 'Check from a terminal:', cmd: curlGet('actions/get?actionId=' + encodeURIComponent(a.selectedId)) }]
6574
- }) + '</div>');
6656
+ });
6657
+ setViewHtml(detail, '<div id="action-detail" class="detail-panel mem-detail"><div class="mem-detail-head"><span>Action</span><button type="button" class="icon-btn icon-close" data-action="action-close" aria-label="Close action details">close</button></div>' + actionBody + '</div>');
6575
6658
  } else {
6576
6659
  setViewHtml(detail, sel ? actionDetailHtml(sel, !!frontierIds[sel.id]) : '');
6577
6660
  }
6578
6661
  document.getElementById('actions-layout').classList.toggle('has-detail', !!detail.firstElementChild);
6579
6662
  }
6580
6663
 
6664
+ async function ensureActionById(id) {
6665
+ var a = state.actions;
6666
+ if (!id || store.entities.action[id] || a.detailLoading === id || a.detailAttemptedId === id) return;
6667
+ a.detailAttemptedId = id;
6668
+ a.detailLoading = id;
6669
+ try {
6670
+ var result = await apiGet('actions/get?actionId=' + encodeURIComponent(id));
6671
+ if (result && result.success && result.action && result.action.id) {
6672
+ store.entities.action[result.action.id] = result.action;
6673
+ a.detailRequestFailedId = '';
6674
+ renderActions();
6675
+ } else if (result === null) {
6676
+ a.detailRequestFailedId = id;
6677
+ }
6678
+ } catch (_) {
6679
+ a.detailRequestFailedId = id;
6680
+ } finally {
6681
+ if (a.detailLoading === id) {
6682
+ a.detailLoading = '';
6683
+ if (!store.entities.action[id]) renderActions();
6684
+ }
6685
+ }
6686
+ }
6687
+
6688
+ function retryActionById(id) {
6689
+ var a = state.actions;
6690
+ if (a.detailLoading === id) return;
6691
+ a.detailAttemptedId = '';
6692
+ a.detailRequestFailedId = '';
6693
+ return ensureActionById(id);
6694
+ }
6695
+
6696
+ async function retryActionList() {
6697
+ if (store.loading.action) return store.loading.action;
6698
+ state.actions.loadError = false;
6699
+ store.loaded.action = false;
6700
+ renderActions();
6701
+ await ensureLoaded('action');
6702
+ if (state.actions.selectedId && !store.entities.action[state.actions.selectedId]) {
6703
+ state.actions.detailAttemptedId = '';
6704
+ state.actions.detailRequestFailedId = '';
6705
+ }
6706
+ renderActions();
6707
+ }
6708
+
6581
6709
  async function setActionStatus(id, status) {
6582
6710
  var a = state.actions;
6583
6711
  a.busy = id;
@@ -6585,7 +6713,7 @@
6585
6713
  var res = await apiPost('actions/update', { actionId: id, status: status });
6586
6714
  a.busy = '';
6587
6715
  a.notice = res && res.success !== false
6588
- ? { id: id, html: 'Status set to <b>' + esc(status) + '</b>. Ready markers update from the live stream.' }
6716
+ ? { id: id, html: 'Status set to <b>' + esc(status) + '</b>. Ready markers update from the SSE feed.' }
6589
6717
  : { id: id, err: true, html: 'Update failed' + (res && res.error ? ': ' + esc(res.error) : '. Check that agentmemory is running.') };
6590
6718
  renderActions();
6591
6719
  }
@@ -6679,6 +6807,8 @@
6679
6807
  return true;
6680
6808
  }
6681
6809
  if (action === 'action-close') { state.actions.selectedId = null; setRouteEntity('actions', ''); renderActions(); return true; }
6810
+ if (action === 'action-retry') { retryActionById(state.actions.selectedId); return true; }
6811
+ if (action === 'action-list-retry') { retryActionList(); return true; }
6682
6812
  if (action === 'action-set-status') { setActionStatus(target.getAttribute('data-action-id'), target.getAttribute('data-status')); return true; }
6683
6813
  if (action === 'action-clear-filters') { state.actions.search = ''; state.actions.statusFilter = ''; var ai = document.getElementById('actions-search'); if (ai) ai.value = ''; renderActions(); return true; }
6684
6814
  if (action === 'activity-type' || action === 'activity-file' || action === 'activity-day' || action === 'activity-clear') { handleActivityAction(action, target); return true; }
@@ -7079,7 +7209,7 @@
7079
7209
  }
7080
7210
  if (store.lastHealthAt && Date.now() - store.lastHealthAt > HEALTH_STALE_MS) {
7081
7211
  var secs = Math.round((Date.now() - store.lastHealthAt) / 1000);
7082
- return '<div class="hstale" id="health-live-state"><strong>Stale: no health report for ' + secs + 's.</strong> Reports normally arrive every 30 seconds. The stream is connected, so the worker is busy or the iii engine is paused' + (r && r.service && r.service.stateBackend === 'redis' ? ', or Redis restarted: the engine stops relaying live events after a Redis restart until agentmemory restarts' : '') + '. The numbers below are from ' + esc(last) + '.' +
7212
+ return '<div class="hstale" id="health-live-state"><strong>Stale: no health report for ' + secs + 's.</strong> Reports normally arrive every 30 seconds. The SSE feed is connected, but the worker may be busy or unable to publish a health report. The numbers below are from ' + esc(last) + '.' +
7083
7213
  '<div style="margin-top:6px;">Fix: wait a minute; if it stays stale, press Re-check now for the full report, check the server log and restart agentmemory.</div></div>';
7084
7214
  }
7085
7215
  return '';
@@ -7105,7 +7235,7 @@
7105
7235
  }
7106
7236
  html += '<div class="verdict"><span class="verdict-badge ' + esc(level) + '">' + esc(badgeText) + '</span><div style="min-width:0;flex:1;">' +
7107
7237
  '<div class="verdict-text">' + esc(text) + '</div>' +
7108
- '<div class="verdict-meta">' + (r ? 'Report ' + relTime(r.checkedAt) + ' · ' : '') + 'updates every 30 s over the live stream, no refresh needed ' + help('live') + '</div></div></div>';
7238
+ '<div class="verdict-meta">' + (r ? 'Report ' + relTime(r.checkedAt) + ' · ' : '') + 'updates every 30 s over SSE, no refresh needed ' + help('live') + '</div></div></div>';
7109
7239
  html += healthStaleHtml(r);
7110
7240
  if (state.health.error) html += '<div class="mem-note err" style="margin-top:10px;">' + esc(state.health.error) + '</div>';
7111
7241
  if (r && r.problems.length) {
@@ -7126,13 +7256,12 @@
7126
7256
  var backend = r && r.service ? r.service.stateBackend : null;
7127
7257
  var streamText = conn.state === 'live' ? 'live' : conn.state;
7128
7258
  return '<div class="card"><div class="card-title">Engine and connection ' + help('engineConnection') + '</div>' +
7129
- hrow('Engine connection', esc(snap.connectionState || 'unknown'), 'engineConnection', 'The worker\'s link to the iii engine, which stores the data and runs the functions.') +
7130
- ((kv || backend) ? hrow('State store', esc((backend ? backend + (kv ? ' · ' : '') : '') + (kv ? kv.status + (kv.latencyMs !== undefined ? ' · ' + kv.latencyMs + ' ms' : '') + (kv.error ? ' · ' + kv.error : '') : '')), 'stateBackend', backend === 'redis' ? 'Stored in Redis (AGENTMEMORY_STATE_BACKEND=redis). If Redis restarts, restart agentmemory so the live stream resumes.' : 'Stored in the engine\'s local files (the default). Set AGENTMEMORY_STATE_BACKEND=redis to use Redis.') : '') +
7131
- hrow('Viewer stream', esc(streamText), 'live', 'This page\'s own connection. Offline means the numbers here stop changing.') +
7132
- (snap.streamRelay ? hrow('Redis event relay', esc(snap.streamRelay), 'live', snap.streamRelay === 'down' ? 'The engine stopped relaying live events through Redis, usually after Redis restarted. Data is still saved. Restart agentmemory to resume live updates.' : 'A test event sent through Redis every 30 seconds came back to the worker.') : '') +
7259
+ hrow('Engine connection', esc(snap.connectionState || 'unknown'), 'engineConnection', 'The worker\'s link to the iii engine, which routes calls between workers.') +
7260
+ ((kv || backend) ? hrow('State store', esc((backend ? backend + (kv ? ' · ' : '') : '') + (kv ? kv.status + (kv.latencyMs !== undefined ? ' · ' + kv.latencyMs + ' ms' : '') + (kv.error ? ' · ' + kv.error : '') : '')), 'stateBackend', backend === 'redis' ? 'The state worker stores records in Redis (AGENTMEMORY_STATE_BACKEND=redis).' : 'The state worker stores records in local files (the default). Set AGENTMEMORY_STATE_BACKEND=redis to use Redis.') : '') +
7261
+ hrow('Viewer feed', esc(streamText), 'live', 'This page\'s own connection. Offline means the numbers here stop changing.') +
7133
7262
  hrow('Uptime', esc(formatUptime(snap.uptimeSeconds)), 'uptime') +
7134
- hrow('Version', esc('v' + (store.version || (r && r.service.version) || '?') + (r ? ' · engine ' + r.service.engineVersion : ''))) +
7135
- hrow('Ports', esc('REST ' + (ports.rest || '?') + ' · streams ' + (ports.streams || '?') + ' · viewer ' + (ports.viewer || 'off'))) +
7263
+ hrow('Version', esc('v' + (store.version || (r && r.service.version) || '?') + (r ? ' · engine pinned ' + r.service.enginePinnedVersion : ''))) +
7264
+ hrow('Ports', esc('REST ' + (ports.rest || '?') + ' · viewer ' + (ports.viewer || 'off'))) +
7136
7265
  '</div>';
7137
7266
  }
7138
7267
 
@@ -7168,17 +7297,18 @@
7168
7297
  'New and removed vectors are written here right away and replayed after a crash, so nothing is embedded twice. Each save empties it.');
7169
7298
  }
7170
7299
  }
7171
- html += hrow('Graph', esc(fmtNum(c.graphNodes) + ' nodes · ' + fmtNum(c.graphEdges) + ' edges'), 'graph', store.graph && store.graph.updatedAt ? 'Snapshot rebuilt ' + esc(timeAgo(store.graph.updatedAt)) + '.' : '');
7300
+ html += hrow('Graph', esc(fmtNum(c.graphNodes) + ' nodes · ' + fmtNum(c.graphEdges) + ' edges'), 'graph', store.graph && store.graph.updatedAt ? 'Graph index updated ' + esc(timeAgo(store.graph.updatedAt)) + '.' : '');
7172
7301
  if (r && r.graphCompaction) html += graphCompactionRowHtml(r.graphCompaction);
7173
7302
  return html + '</div>';
7174
7303
  }
7175
7304
 
7176
7305
  function graphCompactionRowHtml(g) {
7177
7306
  var where = g.phase === 'snapshot' || g.total === null || g.total === undefined ? (g.phase || 'nodes') : g.phase + ' ' + fmtNum(g.processed || 0) + ' of ' + fmtNum(g.total);
7178
- var text = g.state === 'off' ? 'off' : g.state === 'pending' ? 'pending' : g.state === 'running' ? 'running, ' + where : g.state === 'failed' ? 'failed at ' + where : 'done';
7307
+ var text = g.state === 'off' ? 'off' : g.state === 'pending' ? 'pending' : g.state === 'deferred' ? 'deferred' : g.state === 'running' ? 'running, ' + where : g.state === 'failed' ? 'failed at ' + where : 'done';
7179
7308
  var note = 'A one-time pass that trims each graph node and edge to its newest 32 source observations, so stores from older versions stop slowing graph search.';
7180
7309
  if (g.state === 'done') note = fmtNum(g.trimmed || 0) + ' records trimmed, ' + fmtNum(g.idsRemoved || 0) + ' ids removed' + (g.completedAt ? ', finished ' + timeAgo(g.completedAt) : '') + '. It does not run again.';
7181
7310
  else if (g.state === 'running') note += ' It runs in the background, resumes after a restart and stops by itself.';
7311
+ else if (g.state === 'deferred') note = 'Compaction is deferred because it reads complete legacy graph scopes. Run it only during explicit maintenance.';
7182
7312
  else if (g.state === 'off') note = 'Turned off with AGENTMEMORY_GRAPH_COMPACT_ON_BOOT=false. Run it by hand if graph search is slow:';
7183
7313
  else if (g.state === 'failed') note = esc(g.error || 'unknown error') + '. It resumes on the next start. To run it now:';
7184
7314
  var cmd = g.state === 'off' || g.state === 'failed' ? cmdBlock(curlPost('graph/compact', {})) : '';
@@ -7401,7 +7531,7 @@
7401
7531
  var snap = store.health || {};
7402
7532
  var idx = store.indexes || {};
7403
7533
  var c = store.counts || {};
7404
- var html = '<div class="inline-note"><strong>Health</strong> ' + help('health') + ' is the live version of the <a class="empty-link" href="' + esc(REST) + '/agentmemory/status" target="_blank" rel="noopener">/agentmemory/status</a> page: the same checks and verdict, pushed over the stream every 30 seconds. Every problem says what to do. Scroll down for how consolidation works.</div>';
7534
+ var html = '<div class="inline-note"><strong>Health</strong> ' + help('health') + ' is the live version of the <a class="empty-link" href="' + esc(REST) + '/agentmemory/status" target="_blank" rel="noopener">/agentmemory/status</a> page: the same checks and verdict, pushed over SSE every 30 seconds. Every problem says what to do. Scroll down for how consolidation works.</div>';
7405
7535
  html += healthVerdictHtml(r);
7406
7536
  html += '<div class="hgrid">' + engineCardHtml(r, snap) + indexCardHtml(r, idx, c) + captureCardHtml(r) + llmCardHtml(r) + flagsCardHtml(r) + processCardHtml(snap) + recordsCardHtml(c) + '</div>';
7407
7537
  html += consolidationPanelHtml(store.consolidationStatus, c);
@@ -7611,14 +7741,12 @@
7611
7741
 
7612
7742
  var wsReconnectTimer = null;
7613
7743
  var wsRetries = 0;
7614
- var directFailed = true;
7615
- var directFailures = 0;
7616
7744
  var wsHasConnected = false;
7617
7745
  var wsOpenedAt = null;
7618
- var DIRECT_FAILURE_THRESHOLD = 2;
7619
7746
  var OFFLINE_AFTER_RETRIES = 3;
7620
7747
  var HEALTH_STALE_MS = 90000;
7621
7748
  var conn = { state: 'connecting', nextRetryAt: 0, lostAt: 0 };
7749
+ var liveAbortController = null;
7622
7750
 
7623
7751
  function reconnectDelay(attempt) {
7624
7752
  var n = Math.max(1, attempt || 1);
@@ -7704,90 +7832,110 @@
7704
7832
  function reconnectNow() {
7705
7833
  if (wsReconnectTimer) clearTimeout(wsReconnectTimer);
7706
7834
  wsReconnectTimer = null;
7707
- directFailures = 0;
7708
- directFailed = true;
7709
7835
  conn.nextRetryAt = 0;
7710
- var ws = state.ws;
7711
- if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return;
7836
+ if (liveAbortController) {
7837
+ liveAbortController.abort();
7838
+ liveAbortController = null;
7839
+ }
7712
7840
  connectWs();
7713
7841
  }
7714
7842
 
7715
- var WS_CONNECT_TIMEOUT_MS = 5000;
7843
+ function parseSseFrame(frame) {
7844
+ var id = '';
7845
+ var type = '';
7846
+ var data = [];
7847
+ frame.split(/\r?\n/).forEach(function(line) {
7848
+ if (!line || line.charAt(0) === ':') return;
7849
+ var colon = line.indexOf(':');
7850
+ var field = colon < 0 ? line : line.slice(0, colon);
7851
+ var value = colon < 0 ? '' : line.slice(colon + 1).replace(/^ /, '');
7852
+ if (field === 'id') id = value;
7853
+ else if (field === 'event') type = value;
7854
+ else if (field === 'data') data.push(value);
7855
+ });
7856
+ if (!type || !data.length) return null;
7857
+ try { return { id: id, type: type, data: JSON.parse(data.join('\n')) }; } catch (_) { return null; }
7858
+ }
7859
+
7860
+ function consumeSseChunk(buffer, chunk) {
7861
+ buffer += chunk;
7862
+ var frames = [];
7863
+ var match;
7864
+ while ((match = /\r?\n\r?\n/.exec(buffer))) {
7865
+ var frame = buffer.slice(0, match.index);
7866
+ buffer = buffer.slice(match.index + match[0].length);
7867
+ var parsed = parseSseFrame(frame);
7868
+ if (parsed) frames.push(parsed);
7869
+ }
7870
+ return { buffer: buffer, frames: frames };
7871
+ }
7716
7872
 
7717
- function connectWs() {
7873
+ async function connectWs() {
7718
7874
  wsReconnectTimer = null;
7719
7875
  conn.nextRetryAt = 0;
7720
- var useDirect = !directFailed;
7721
- var ws;
7876
+ var controller = new AbortController();
7877
+ liveAbortController = controller;
7722
7878
  try {
7723
- ws = new WebSocket(useDirect ? WS_DIRECT_URL : WS_URL);
7724
- ws.__direct = useDirect;
7725
- } catch (_) {
7726
- scheduleReconnect();
7727
- return;
7728
- }
7729
- var connectTimer = setTimeout(function() {
7730
- if (ws.readyState === WebSocket.CONNECTING) {
7731
- try { ws.close(); } catch {}
7879
+ var headers = { 'Cache-Control': 'no-cache', Accept: 'text/event-stream' };
7880
+ var viewerToken = getViewerToken();
7881
+ if (viewerToken) headers.Authorization = 'Bearer ' + viewerToken;
7882
+ var response = await fetch(LIVE_EVENTS_URL, { headers: headers, signal: controller.signal });
7883
+ if (!response.ok || !response.body) {
7884
+ if (response.status === 401) showViewerAuthPrompt();
7885
+ throw new Error('SSE request failed: ' + response.status);
7732
7886
  }
7733
- }, WS_CONNECT_TIMEOUT_MS);
7734
- ws.onopen = function() {
7735
- clearTimeout(connectTimer);
7736
- if (state.ws !== ws) return;
7887
+ hideViewerAuthPrompt();
7737
7888
  wsRetries = 0;
7738
- if (!ws.__direct) {
7739
- ws.send(JSON.stringify({
7740
- type: 'join',
7741
- data: {
7742
- subscriptionId: 'viewer-' + Date.now(),
7743
- streamName: 'mem-live',
7744
- groupId: 'viewer'
7745
- }
7746
- }));
7747
- }
7748
7889
  var firstConnect = !wsHasConnected;
7749
7890
  wsHasConnected = true;
7750
7891
  wsOpenedAt = Date.now();
7751
7892
  setConnection('live');
7752
7893
  updateFooter();
7753
- resyncAfterConnect(firstConnect);
7754
- };
7755
- ws.onmessage = function(e) {
7756
- if (state.ws !== ws) return;
7757
- if (!ws.__usable) {
7758
- ws.__usable = true;
7759
- directFailures = 0;
7894
+ await resyncAfterConnect(firstConnect);
7895
+ var reader = response.body.getReader();
7896
+ var decoder = new TextDecoder();
7897
+ var buffer = '';
7898
+ while (true) {
7899
+ var result = await reader.read();
7900
+ if (result.done) break;
7901
+ buffer += decoder.decode(result.value, { stream: true });
7902
+ var parsed = consumeSseChunk(buffer, '');
7903
+ buffer = parsed.buffer;
7904
+ parsed.frames.forEach(function(frame) {
7905
+ store.lastEventAt = Date.now();
7906
+ if (frame.type === 'raw_observation') {
7907
+ var obs = frame.data && frame.data.data && (frame.data.data.observation || frame.data.data);
7908
+ if (looksLikeObservation(obs)) routeWsMessage({ observation: obs, fresh: true });
7909
+ } else if (frame.type === 'compressed_observation') {
7910
+ var compressed = frame.data && frame.data.data && (frame.data.data.observation || frame.data.data);
7911
+ if (looksLikeObservation(compressed)) routeWsMessage({ observation: compressed, fresh: true });
7912
+ } else {
7913
+ handleLiveEvent(frame.type, frame.data && frame.data.data || {});
7914
+ }
7915
+ });
7760
7916
  }
7761
- store.lastEventAt = Date.now();
7762
- try {
7763
- var msg = JSON.parse(e.data);
7764
- if (msg.type === 'stream' && msg.event) {
7765
- handleStreamEvent(msg);
7766
- } else if (msg.event_type && msg.data) {
7767
- handleStreamEvent({ event: { type: 'create', data: msg.data, event_type: msg.event_type } });
7768
- }
7769
- } catch {}
7770
- };
7771
- ws.onclose = function() {
7772
- clearTimeout(connectTimer);
7773
- if (state.ws !== ws) return;
7774
- wsOpenedAt = null;
7775
- updateFooter();
7776
- directFailures += 1;
7777
- if (directFailures >= DIRECT_FAILURE_THRESHOLD) {
7778
- directFailed = ws.__direct;
7779
- directFailures = 0;
7917
+ if (!controller.signal.aborted) throw new Error('SSE stream ended');
7918
+ } catch (err) {
7919
+ if (!controller.signal.aborted) console.warn('[viewer] SSE connection failed:', err);
7920
+ } finally {
7921
+ if (liveAbortController === controller) liveAbortController = null;
7922
+ if (!controller.signal.aborted) {
7923
+ wsOpenedAt = null;
7924
+ updateFooter();
7925
+ scheduleReconnect();
7780
7926
  }
7781
- scheduleReconnect();
7782
- };
7783
- ws.onerror = function() {
7784
- if (state.ws !== ws) return;
7785
- try { ws.close(); } catch {}
7786
- };
7787
- state.ws = ws;
7927
+ }
7788
7928
  }
7789
7929
 
7790
7930
  var resyncing = null;
7931
+ function resetLoadedKindForReload(kind) {
7932
+ store.loaded[kind] = false;
7933
+ if (kind === 'action') {
7934
+ state.actions.detailAttemptedId = '';
7935
+ state.actions.detailRequestFailedId = '';
7936
+ }
7937
+ }
7938
+
7791
7939
  async function resyncAfterConnect(firstConnect) {
7792
7940
  openBootGate();
7793
7941
  if (resyncing) return resyncing;
@@ -7795,9 +7943,10 @@
7795
7943
  resyncing = (async function() {
7796
7944
  try {
7797
7945
  await loadSnapshot();
7946
+ await loadRecentObservations();
7798
7947
  scheduleHealthStaleCheck();
7799
7948
  if (!firstConnect || reloadKinds.length) {
7800
- reloadKinds.forEach(function(k) { store.loaded[k] = false; });
7949
+ reloadKinds.forEach(resetLoadedKindForReload);
7801
7950
  await Promise.all(reloadKinds.map(ensureLoaded));
7802
7951
  state.graph.dirty = state.graph.loaded;
7803
7952
  state.activity.loaded = false;
@@ -7814,6 +7963,24 @@
7814
7963
  }
7815
7964
 
7816
7965
  var LIVE_BUFFER_MAX = 200;
7966
+ var ACTIVITY_SESSIONS = 10;
7967
+ async function loadRecentObservations() {
7968
+ var sessionResult = await apiGet('sessions?limit=' + ACTIVITY_SESSIONS);
7969
+ var sessions = sortByTimeDesc((sessionResult && sessionResult.sessions) || [], ['startedAt']).filter(function(x) { return sessionId(x); }).slice(0, ACTIVITY_SESSIONS);
7970
+ mergeSessionRows(sessions);
7971
+ var results = await Promise.all(sessions.map(function(x) {
7972
+ return apiGet('observations?sessionId=' + encodeURIComponent(sessionId(x)) + '&limit=100');
7973
+ }));
7974
+ var seen = {};
7975
+ results.forEach(function(result) {
7976
+ ((result && result.observations) || []).slice(-LIVE_BUFFER_MAX).forEach(function(observation) {
7977
+ if (!observation || !observation.id || seen[observation.id]) return;
7978
+ seen[observation.id] = true;
7979
+ routeWsMessage({ observation: observation });
7980
+ });
7981
+ });
7982
+ }
7983
+
7817
7984
 
7818
7985
  function looksLikeObservation(obj) {
7819
7986
  return !!(obj && typeof obj === 'object' && obj.id && obj.timestamp);
@@ -8055,15 +8222,6 @@
8055
8222
  if (looksLikeObservation(observation)) {
8056
8223
  routeWsMessage({ observation: observation, fresh: true });
8057
8224
  }
8058
- } else if (evt.type === 'sync') {
8059
- var items = Array.isArray(evt.data) ? evt.data.slice(-LIVE_BUFFER_MAX) : [];
8060
- items.forEach(function(item) {
8061
- var payload = item.data || item;
8062
- observation = payload.observation || payload;
8063
- if (looksLikeObservation(observation)) {
8064
- routeWsMessage({ observation: observation });
8065
- }
8066
- });
8067
8225
  }
8068
8226
  }
8069
8227
 
@@ -8226,7 +8384,7 @@
8226
8384
  var sync = document.getElementById('footer-sync');
8227
8385
  if (sync) {
8228
8386
  var syncText = store.snapshotAt ? 'full sync ' + new Date(store.snapshotAt).toLocaleTimeString() : 'waiting for first sync';
8229
- if (store.snapshotAt) syncText += wsOpenedAt ? ', streaming since ' + new Date(wsOpenedAt).toLocaleTimeString() : ', stream reconnecting';
8387
+ if (store.snapshotAt) syncText += wsOpenedAt ? ', live via SSE since ' + new Date(wsOpenedAt).toLocaleTimeString() : ', SSE reconnecting';
8230
8388
  sync.textContent = syncText;
8231
8389
  }
8232
8390
  var fbEl = document.getElementById('footer-feedback');
@@ -8323,7 +8481,7 @@
8323
8481
  var modal = document.getElementById('modal');
8324
8482
  var overlay = document.getElementById('modal-overlay');
8325
8483
  var html = '<h3>How to read this viewer</h3>';
8326
- html += '<p style="margin-bottom:8px;">Everything on these pages updates live over one streaming connection; nothing refreshes on a timer. Click any <span class="help" style="cursor:default;">?</span> for a short explanation, or use the keys below.</p>';
8484
+ html += '<p style="margin-bottom:8px;">Everything on these pages updates live over one SSE connection; nothing refreshes on a timer. Click any <span class="help" style="cursor:default;">?</span> for a short explanation, or use the keys below.</p>';
8327
8485
  html += '<div class="kbd-list">' +
8328
8486
  '<div><kbd>/</kbd> focus search on this page</div>' +
8329
8487
  '<div><kbd>?</kbd> open this help</div>' +
@@ -8894,16 +9052,7 @@
8894
9052
 
8895
9053
  var bootRoute = parseRoute(window.location.hash);
8896
9054
  switchTab(bootRoute.tab, { replaceRoute: true, skipRoute: !!bootRoute.obs && !bootRoute.redirect, id: bootRoute.id, obs: bootRoute.obs });
8897
- (async function initWs() {
8898
- try {
8899
- var live = await api('livez', { signal: AbortSignal.timeout(5000) });
8900
- if (live && typeof live.streamsPort === 'number' && live.streamsPort > 0) {
8901
- WS_URL = wsProto + '//' + window.location.hostname + ':' + live.streamsPort;
8902
- WS_DIRECT_URL = WS_URL + '/stream/mem-live/viewer';
8903
- }
8904
- } catch (_) {}
8905
- connectWs();
8906
- })();
9055
+ connectWs();
8907
9056
  setTimeout(function() {
8908
9057
  if (wsHasConnected) return;
8909
9058
  openBootGate();
@@ -8913,8 +9062,7 @@
8913
9062
 
8914
9063
  document.addEventListener('visibilitychange', function() {
8915
9064
  if (document.visibilityState !== 'visible') return;
8916
- var ws = state.ws;
8917
- if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) return;
9065
+ if (liveAbortController) return;
8918
9066
  wsRetries = 0;
8919
9067
  reconnectNow();
8920
9068
  });