@codyswann/lisa 4.80.2 → 4.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/all/copy-contents/gitignore +5 -0
  2. package/dist/cli/doctor-session-manager-trust.d.ts +62 -0
  3. package/dist/cli/doctor-session-manager-trust.d.ts.map +1 -0
  4. package/dist/cli/doctor-session-manager-trust.js +174 -0
  5. package/dist/cli/doctor-session-manager-trust.js.map +1 -0
  6. package/dist/cli/doctor-session-manager.d.ts +35 -0
  7. package/dist/cli/doctor-session-manager.d.ts.map +1 -0
  8. package/dist/cli/doctor-session-manager.js +209 -0
  9. package/dist/cli/doctor-session-manager.js.map +1 -0
  10. package/dist/cli/doctor.d.ts.map +1 -1
  11. package/dist/cli/doctor.js +3 -0
  12. package/dist/cli/doctor.js.map +1 -1
  13. package/dist/cli/lisa-test-run-bootstrap.js +28 -0
  14. package/dist/cli/lisa-test-run-bootstrap.js.map +1 -1
  15. package/dist/cli/session-manager-config-merge.d.ts +25 -0
  16. package/dist/cli/session-manager-config-merge.d.ts.map +1 -0
  17. package/dist/cli/session-manager-config-merge.js +54 -0
  18. package/dist/cli/session-manager-config-merge.js.map +1 -0
  19. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  20. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  21. package/dist/core/upstream-evidence-manifest.js +257 -12
  22. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  23. package/package.json +4 -4
  24. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  25. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  26. package/plugins/lisa/.codex-plugin/skills/lisa-automation-status/SKILL.md +10 -0
  27. package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +17 -2
  28. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  29. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +287 -0
  30. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/agents/openai.yaml +4 -0
  31. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +66 -0
  32. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  33. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  34. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  35. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  36. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  37. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  38. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  39. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +265 -0
  40. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  41. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  42. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  43. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  44. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  45. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  46. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  47. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +191 -0
  48. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  49. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  50. package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +158 -0
  51. package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
  52. package/plugins/lisa/.codex-plugin/skills/lisa-tear-down-automations/SKILL.md +10 -0
  53. package/plugins/lisa/commands/implement.md +2 -2
  54. package/plugins/lisa/commands/session-manager.md +7 -0
  55. package/plugins/lisa/commands/setup/session-manager.md +6 -0
  56. package/plugins/lisa/rules/reference/automation-runbook-contract.md +14 -0
  57. package/plugins/lisa/rules/reference/config-resolution.md +58 -0
  58. package/plugins/lisa/scripts/automation-status-claude-adapter.mjs +4 -1
  59. package/plugins/lisa/scripts/automation-status-codex-adapter.mjs +4 -1
  60. package/plugins/lisa/scripts/automation-status-expected-fleet.mjs +98 -0
  61. package/plugins/lisa/scripts/automation-status-run-history.mjs +29 -7
  62. package/plugins/lisa/scripts/ready-queue-order.mjs +352 -0
  63. package/plugins/lisa/skills/lisa-automation-status/SKILL.md +10 -0
  64. package/plugins/lisa/skills/lisa-implement/SKILL.md +17 -2
  65. package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  66. package/plugins/lisa/skills/lisa-session-manager/SKILL.md +285 -0
  67. package/plugins/lisa/skills/lisa-session-manager/agents/openai.yaml +4 -0
  68. package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +66 -0
  69. package/plugins/lisa/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  70. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  71. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  72. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  73. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  74. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  75. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  76. package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +265 -0
  77. package/plugins/lisa/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  78. package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  79. package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  80. package/plugins/lisa/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  81. package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  82. package/plugins/lisa/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  83. package/plugins/lisa/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  84. package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +191 -0
  85. package/plugins/lisa/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  86. package/plugins/lisa/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  87. package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +156 -0
  88. package/plugins/lisa/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
  89. package/plugins/lisa/skills/lisa-tear-down-automations/SKILL.md +10 -0
  90. package/plugins/lisa-agy/commands/lisa/implement.md +2 -2
  91. package/plugins/lisa-agy/commands/lisa/session-manager.md +7 -0
  92. package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +6 -0
  93. package/plugins/lisa-agy/plugin.json +1 -1
  94. package/plugins/lisa-agy/scripts/automation-status-claude-adapter.mjs +4 -1
  95. package/plugins/lisa-agy/scripts/automation-status-codex-adapter.mjs +4 -1
  96. package/plugins/lisa-agy/scripts/automation-status-expected-fleet.mjs +98 -0
  97. package/plugins/lisa-agy/scripts/automation-status-run-history.mjs +29 -7
  98. package/plugins/lisa-agy/scripts/ready-queue-order.mjs +352 -0
  99. package/plugins/lisa-agy/skills/lisa-automation-status/SKILL.md +10 -0
  100. package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +17 -2
  101. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  102. package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +285 -0
  103. package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +66 -0
  104. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  105. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  106. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  107. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  108. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  109. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  110. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  111. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +265 -0
  112. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  113. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  114. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  115. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  116. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  117. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  118. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  119. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +191 -0
  120. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  121. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  122. package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +156 -0
  123. package/plugins/lisa-agy/skills/lisa-tear-down-automations/SKILL.md +10 -0
  124. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  126. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  127. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  128. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  129. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  130. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  131. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-copilot/commands/lisa/implement.md +2 -2
  133. package/plugins/lisa-copilot/commands/lisa/session-manager.md +7 -0
  134. package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +6 -0
  135. package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +14 -0
  136. package/plugins/lisa-copilot/rules/reference/config-resolution.md +58 -0
  137. package/plugins/lisa-copilot/scripts/automation-status-claude-adapter.mjs +4 -1
  138. package/plugins/lisa-copilot/scripts/automation-status-codex-adapter.mjs +4 -1
  139. package/plugins/lisa-copilot/scripts/automation-status-expected-fleet.mjs +98 -0
  140. package/plugins/lisa-copilot/scripts/automation-status-run-history.mjs +29 -7
  141. package/plugins/lisa-copilot/scripts/ready-queue-order.mjs +352 -0
  142. package/plugins/lisa-copilot/skills/lisa-automation-status/SKILL.md +10 -0
  143. package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +17 -2
  144. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  145. package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +285 -0
  146. package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +66 -0
  147. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  148. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  149. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  150. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  151. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  152. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  153. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  154. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +265 -0
  155. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  156. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  157. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  158. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  159. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  160. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  161. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  162. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +191 -0
  163. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  164. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  165. package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +156 -0
  166. package/plugins/lisa-copilot/skills/lisa-tear-down-automations/SKILL.md +10 -0
  167. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  168. package/plugins/lisa-cursor/commands/lisa/implement.md +2 -2
  169. package/plugins/lisa-cursor/commands/lisa/session-manager.md +7 -0
  170. package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +6 -0
  171. package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +14 -0
  172. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +58 -0
  173. package/plugins/lisa-cursor/scripts/automation-status-claude-adapter.mjs +4 -1
  174. package/plugins/lisa-cursor/scripts/automation-status-codex-adapter.mjs +4 -1
  175. package/plugins/lisa-cursor/scripts/automation-status-expected-fleet.mjs +98 -0
  176. package/plugins/lisa-cursor/scripts/automation-status-run-history.mjs +29 -7
  177. package/plugins/lisa-cursor/scripts/ready-queue-order.mjs +352 -0
  178. package/plugins/lisa-cursor/skills/lisa-automation-status/SKILL.md +10 -0
  179. package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +17 -2
  180. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  181. package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +285 -0
  182. package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +66 -0
  183. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  184. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  185. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  186. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  187. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  188. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  189. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  190. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +265 -0
  191. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  192. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  193. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  194. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  195. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  196. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  197. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  198. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +191 -0
  199. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  200. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  201. package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +156 -0
  202. package/plugins/lisa-cursor/skills/lisa-tear-down-automations/SKILL.md +10 -0
  203. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  204. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  205. package/plugins/lisa-devin/rules/reference/automation-runbook-contract.md +14 -0
  206. package/plugins/lisa-devin/rules/reference/config-resolution.md +58 -0
  207. package/plugins/lisa-devin/scripts/automation-status-claude-adapter.mjs +4 -1
  208. package/plugins/lisa-devin/scripts/automation-status-codex-adapter.mjs +4 -1
  209. package/plugins/lisa-devin/scripts/automation-status-expected-fleet.mjs +98 -0
  210. package/plugins/lisa-devin/scripts/automation-status-run-history.mjs +29 -7
  211. package/plugins/lisa-devin/scripts/ready-queue-order.mjs +352 -0
  212. package/plugins/lisa-devin/skills/lisa-automation-status/SKILL.md +10 -0
  213. package/plugins/lisa-devin/skills/lisa-implement/SKILL.md +17 -2
  214. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  215. package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +285 -0
  216. package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +66 -0
  217. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  218. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  219. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  220. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  221. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  222. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  223. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  224. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +265 -0
  225. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  226. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  227. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  228. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  229. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  230. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  231. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  232. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +191 -0
  233. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  234. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  235. package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +156 -0
  236. package/plugins/lisa-devin/skills/lisa-tear-down-automations/SKILL.md +10 -0
  237. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  238. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  239. package/plugins/lisa-expo-agy/plugin.json +1 -1
  240. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  241. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  242. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  243. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  244. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  245. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  246. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  247. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  248. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  249. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  250. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  251. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  252. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  253. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  254. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  255. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  256. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  257. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  258. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  259. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  260. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  261. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  262. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  263. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  264. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  265. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  266. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  267. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  268. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  269. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  270. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  271. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  272. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  273. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  274. package/plugins/lisa-rails-agy/plugin.json +1 -1
  275. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  276. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  277. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  278. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  279. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  280. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  281. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  282. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  283. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  284. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  285. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  286. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  287. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  288. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  289. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  290. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  291. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  292. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  293. package/plugins/src/base/commands/implement.md +2 -2
  294. package/plugins/src/base/commands/session-manager.md +7 -0
  295. package/plugins/src/base/commands/setup/session-manager.md +6 -0
  296. package/plugins/src/base/rules/reference/automation-runbook-contract.md +14 -0
  297. package/plugins/src/base/rules/reference/config-resolution.md +58 -0
  298. package/plugins/src/base/scripts/automation-status-claude-adapter.mjs +4 -1
  299. package/plugins/src/base/scripts/automation-status-codex-adapter.mjs +4 -1
  300. package/plugins/src/base/scripts/automation-status-expected-fleet.mjs +98 -0
  301. package/plugins/src/base/scripts/automation-status-run-history.mjs +29 -7
  302. package/plugins/src/base/scripts/ready-queue-order.mjs +352 -0
  303. package/plugins/src/base/skills/lisa-automation-status/SKILL.md +10 -0
  304. package/plugins/src/base/skills/lisa-implement/SKILL.md +17 -2
  305. package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  306. package/plugins/src/base/skills/lisa-session-manager/SKILL.md +285 -0
  307. package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +66 -0
  308. package/plugins/src/base/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  309. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  310. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  311. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  312. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  313. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  314. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  315. package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +265 -0
  316. package/plugins/src/base/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  317. package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  318. package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  319. package/plugins/src/base/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  320. package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  321. package/plugins/src/base/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  322. package/plugins/src/base/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  323. package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +191 -0
  324. package/plugins/src/base/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  325. package/plugins/src/base/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  326. package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +156 -0
  327. package/plugins/src/base/skills/lisa-tear-down-automations/SKILL.md +10 -0
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Worker bookkeeping for the session manager: names, live detection, spawn
3
+ * argv and stall classification.
4
+ *
5
+ * A worker is a background Claude Code session (`claude --bg`) running
6
+ * `/lisa:implement <REF> managedBy=<manager>` for one work item. The manager
7
+ * never keeps a handle on a worker; it rediscovers every worker each cycle
8
+ * from `claude agents --json` by name. So the name is the join key between the
9
+ * tracker item, the ledger row and the live session, and everything here is
10
+ * about keeping that join exact.
11
+ *
12
+ * Every export is pure. `session-manager.mjs` owns the real `process.env`,
13
+ * `child_process` and clock, and passes them in.
14
+ * @module workers
15
+ */
16
+
17
+ /** Settings blob every worker gets, so the manager's messages reach it. */
18
+ export const WORKER_SETTINGS = JSON.stringify({
19
+ crossSessionInbound: "accept",
20
+ });
21
+
22
+ /**
23
+ * Stall policy used when the caller passes none. Mirrors the
24
+ * `sessionManager.stall` defaults, so a missing config block still classifies.
25
+ */
26
+ export const DEFAULT_STALL = Object.freeze({
27
+ noOutputMinutes: 60,
28
+ requestionHours: 4,
29
+ maxAttempts: 3,
30
+ });
31
+
32
+ /** Every worker session name starts with this. Anything else is not ours. */
33
+ const WORKER_PREFIX = "wk-";
34
+
35
+ /**
36
+ * `wk-<token>-<MMDD>-<HHMM>[-<n>]`, read from the right. The token is greedy,
37
+ * so it is everything before the LAST stamp group: a ref whose number has four
38
+ * or more digits (`PROJ-1234`) or that itself ends in `-dddd-dddd` keeps its
39
+ * whole token. A lazy token read `wk-PROJ-1234-1012-1530` as `PROJ`, and the
40
+ * live-worker guard never fired. The collision suffix is capped at three
41
+ * digits so it can never be mistaken for the `HHMM` half of a stamp. The name
42
+ * is anchored so a token is compared exactly — never by `startsWith`, which
43
+ * would let an `ABC-12` worker answer for `ABC-1`.
44
+ */
45
+ const WORKER_NAME_PATTERN = /^wk-(.+)-(\d{4}-\d{4})(?:-([1-9]\d{0,2}))?$/;
46
+
47
+ /**
48
+ * `claude agents --json --all` states a finished row carries. Never enough on
49
+ * its own: an idle worker whose turn ended is alive but also reads `done`.
50
+ */
51
+ const TERMINAL_AGENT_STATES = new Set(["done", "stopped"]);
52
+
53
+ /** Ledger statuses that mean the worker reported its own outcome. */
54
+ const FINISHED_STATUSES = new Set(["DONE", "FAILED"]);
55
+
56
+ /** The `managedBy=` value contract `lisa-implement` enforces. */
57
+ const MANAGER_NAME_PATTERN = /^[A-Za-z0-9._-]{1,64}$/;
58
+
59
+ /** A full session UUID, which `--resume` accepts directly. */
60
+ const SESSION_UUID_PATTERN =
61
+ /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i;
62
+
63
+ /** The short id `claude --bg` prints and `claude agents` lists. */
64
+ const SHORT_ID_PATTERN = /\b[0-9a-f]{8}\b/i;
65
+
66
+ const MINUTE_MS = 60_000;
67
+ const HOUR_MS = 60 * MINUTE_MS;
68
+
69
+ /**
70
+ * Reduce a work-item ref to the `[A-Za-z0-9-]` token used in worker names.
71
+ *
72
+ * A GitHub ref `owner/repo#12` becomes `repo-12`: the owner adds nothing
73
+ * inside one project and `/` and `#` are not safe in a session name. Jira and
74
+ * Linear keys already fit and pass through unchanged.
75
+ * @param {string} ref - Tracker ref as the queue reported it
76
+ * @returns {string} Name-safe token
77
+ */
78
+ export function sanitizeRefToken(ref) {
79
+ if (typeof ref !== "string") {
80
+ throw new TypeError(`ref must be a string, got ${typeof ref}`);
81
+ }
82
+ const github = /^(?:[^/\s]+\/)?([^/#\s]*)#(\d+)$/.exec(ref.trim());
83
+ const raw = github ? [github[1], github[2]].filter(Boolean).join("-") : ref;
84
+ const token = raw
85
+ .replace(/[^A-Za-z0-9-]+/g, "-")
86
+ .replace(/-{2,}/g, "-")
87
+ .replace(/^-|-$/g, "");
88
+ if (token === "") {
89
+ throw new Error(`ref ${JSON.stringify(ref)} yields an empty worker token`);
90
+ }
91
+ return token;
92
+ }
93
+
94
+ /**
95
+ * Format a clock reading as local `MMDD-HHMM`.
96
+ * @param {Date | number} now - Clock reading
97
+ * @returns {string} Stamp
98
+ */
99
+ function formatStamp(now) {
100
+ const date = now instanceof Date ? now : new Date(now);
101
+ const pad = value => String(value).padStart(2, "0");
102
+ return `${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}`;
103
+ }
104
+
105
+ /**
106
+ * Name a new worker for `ref`, unique against every name already known.
107
+ *
108
+ * A retry in the same minute would otherwise reuse the name of the worker it
109
+ * replaces, and `claude agents` could no longer tell them apart, so a
110
+ * collision gets `-2`, `-3`, ….
111
+ * @param {string} ref - Tracker ref
112
+ * @param {Date | number} now - Clock reading
113
+ * @param {Iterable<string>} [existingNames] - Every session name in use or on record
114
+ * @returns {string} Worker session name
115
+ */
116
+ export function buildWorkerName(ref, now, existingNames = []) {
117
+ const known = new Set(existingNames);
118
+ const base = `${WORKER_PREFIX}${sanitizeRefToken(ref)}-${formatStamp(now)}`;
119
+ if (!known.has(base)) return base;
120
+ const suffix = Array.from(
121
+ { length: known.size + 1 },
122
+ (_, index) => index + 2
123
+ ).find(candidate => !known.has(`${base}-${candidate}`));
124
+ return `${base}-${suffix}`;
125
+ }
126
+
127
+ /**
128
+ * Split a worker session name into its parts.
129
+ * @param {string} name - Session name
130
+ * @returns {{token: string, stamp: string, suffix: number | null} | null} Parts, or null when the name is not a worker's
131
+ */
132
+ export function parseWorkerName(name) {
133
+ const match =
134
+ typeof name === "string" ? WORKER_NAME_PATTERN.exec(name) : null;
135
+ if (!match) return null;
136
+ return {
137
+ token: match[1],
138
+ stamp: match[2],
139
+ suffix: match[3] === undefined ? null : Number(match[3]),
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Read a value only when it is a string.
145
+ * @param {unknown} value - Candidate
146
+ * @returns {string | null} The string, or null
147
+ */
148
+ function stringOrNull(value) {
149
+ return typeof value === "string" ? value : null;
150
+ }
151
+
152
+ /**
153
+ * Normalize `claude agents --json` output (with or without `--all`).
154
+ *
155
+ * The shapes differ: an active row carries `{pid,status}`; a completed row
156
+ * from `--all` carries `{id,state}` with `state` of `done` or `stopped` and no
157
+ * `pid` or `status`. Claude Code 2.1.296 also prints hybrids: an idle worker
158
+ * whose turn ended is `{pid,status:"idle",state:"done"}` while its process is
159
+ * alive, a worker waiting on a question is `state:"blocked"`, and a worker
160
+ * that has just been spawned is `{pid:null,status:null,state:"working"}`. So a
161
+ * row is alive when it has a pid or a status (the active shape) whatever its
162
+ * `state`, or when its state is not terminal; only a row with neither, in a
163
+ * terminal state, has finished. Reading `state` alone undercounted capacity,
164
+ * let `spawn` duplicate a live worker and made `classify` say `dead`.
165
+ * Every row comes back with all fields present (null when absent) and an
166
+ * `active` flag, so callers never branch on shape.
167
+ * @param {unknown} parsed - `JSON.parse` of the command's stdout
168
+ * @returns {Array<{name: string|null, sessionId: string|null, id: string|null, pid: number|null, cwd: string|null, kind: string|null, startedAt: number|null, status: string|null, state: string|null, active: boolean}>} Rows
169
+ */
170
+ export function normalizeAgents(parsed) {
171
+ if (!Array.isArray(parsed)) {
172
+ throw new TypeError("claude agents --json must return an array");
173
+ }
174
+ return parsed
175
+ .filter(row => row !== null && typeof row === "object")
176
+ .map(row => {
177
+ const state = stringOrNull(row.state);
178
+ const pid = typeof row.pid === "number" ? row.pid : null;
179
+ const status = stringOrNull(row.status);
180
+ const activeShape = pid !== null || status !== null;
181
+ return {
182
+ name: stringOrNull(row.name),
183
+ sessionId: stringOrNull(row.sessionId),
184
+ id: stringOrNull(row.id),
185
+ pid,
186
+ cwd: stringOrNull(row.cwd),
187
+ kind: stringOrNull(row.kind),
188
+ startedAt: typeof row.startedAt === "number" ? row.startedAt : null,
189
+ status,
190
+ state,
191
+ active:
192
+ activeShape || state === null || !TERMINAL_AGENT_STATES.has(state),
193
+ };
194
+ });
195
+ }
196
+
197
+ /**
198
+ * Whether a normalized row is a running worker.
199
+ * @param {{active: boolean, name: string|null}} agent - Normalized row
200
+ * @returns {boolean} True for an active `wk-` session
201
+ */
202
+ function isRunningWorker(agent) {
203
+ return agent.active && parseWorkerName(agent.name) !== null;
204
+ }
205
+
206
+ /**
207
+ * Count running workers, the `running` input to capacity.
208
+ * @param {ReturnType<typeof normalizeAgents>} agents - Normalized rows
209
+ * @returns {number} Active `wk-` sessions
210
+ */
211
+ export function countRunningWorkers(agents) {
212
+ return agents.filter(isRunningWorker).length;
213
+ }
214
+
215
+ /**
216
+ * Find the running worker for `ref`, by exact token.
217
+ * @param {ReturnType<typeof normalizeAgents>} agents - Normalized rows
218
+ * @param {string} ref - Tracker ref
219
+ * @returns {ReturnType<typeof normalizeAgents>[number] | null} The row, or null
220
+ */
221
+ export function findLiveWorker(agents, ref) {
222
+ const token = sanitizeRefToken(ref);
223
+ return (
224
+ agents.find(
225
+ agent =>
226
+ isRunningWorker(agent) && parseWorkerName(agent.name)?.token === token
227
+ ) ?? null
228
+ );
229
+ }
230
+
231
+ /**
232
+ * Copy an environment without any `TELEGRAM_*` key.
233
+ *
234
+ * The manager runs with `TELEGRAM_STATE_DIR` (and possibly
235
+ * `TELEGRAM_BOT_TOKEN`) set. A worker that inherited either could start a
236
+ * second poller on the same bot and take the manager's messages, so every
237
+ * `TELEGRAM_*` key goes — a superset of the two that matter today.
238
+ * @param {Record<string, string | undefined>} env - Source environment
239
+ * @returns {Record<string, string | undefined>} New environment
240
+ */
241
+ export function scrubWorkerEnv(env) {
242
+ return Object.fromEntries(
243
+ Object.entries(env).filter(([key]) => !key.startsWith("TELEGRAM_"))
244
+ );
245
+ }
246
+
247
+ /**
248
+ * Throw unless `value` is a non-empty string.
249
+ * @param {string} label - Field name for the message
250
+ * @param {unknown} value - Candidate
251
+ */
252
+ function requireText(label, value) {
253
+ if (typeof value !== "string" || value.trim() === "") {
254
+ throw new Error(`${label} must be a non-empty string`);
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Build the spawn for a new worker. Passed as argv to `spawnSync`, never a
260
+ * shell string, so no ref or name is ever interpreted by a shell.
261
+ *
262
+ * The prompt is exactly `/lisa:implement <REF> managedBy=<manager>`: any
263
+ * trailing prose would make `lisa-track` classify the arguments as free text.
264
+ * @param {{ref: string, name: string, manager: string, repoDir: string, env: Record<string, string | undefined>}} input - Spawn inputs
265
+ * @returns {{command: string, args: string[], cwd: string, env: Record<string, string | undefined>}} Spawn spec
266
+ */
267
+ export function buildWorkerSpawn({ ref, name, manager, repoDir, env }) {
268
+ requireText("ref", ref);
269
+ if (/\s/.test(ref)) {
270
+ throw new Error(`ref ${JSON.stringify(ref)} must not contain whitespace`);
271
+ }
272
+ if (typeof manager !== "string" || !MANAGER_NAME_PATTERN.test(manager)) {
273
+ throw new Error(
274
+ `manager ${JSON.stringify(manager)} must match ${MANAGER_NAME_PATTERN}`
275
+ );
276
+ }
277
+ if (parseWorkerName(name) === null) {
278
+ throw new Error(`name ${JSON.stringify(name)} is not a worker name`);
279
+ }
280
+ requireText("repoDir", repoDir);
281
+ return {
282
+ command: "claude",
283
+ args: [
284
+ "--bg",
285
+ "-n",
286
+ name,
287
+ "--dangerously-skip-permissions",
288
+ "--settings",
289
+ WORKER_SETTINGS,
290
+ `/lisa:implement ${ref} managedBy=${manager}`,
291
+ ],
292
+ cwd: repoDir,
293
+ env: scrubWorkerEnv(env),
294
+ };
295
+ }
296
+
297
+ /**
298
+ * Build the spawn that resumes a worker with one message (an answer, or a
299
+ * nudge after a stall), with the same cwd and environment scrub as a spawn.
300
+ * @param {{sessionId: string, text: string, repoDir: string, env: Record<string, string | undefined>}} input - Resume inputs
301
+ * @returns {{command: string, args: string[], cwd: string, env: Record<string, string | undefined>}} Spawn spec
302
+ */
303
+ export function buildWorkerResume({ sessionId, text, repoDir, env }) {
304
+ requireText("sessionId", sessionId);
305
+ if (sessionId.startsWith("-")) {
306
+ throw new Error(
307
+ `sessionId ${JSON.stringify(sessionId)} must not look like a flag`
308
+ );
309
+ }
310
+ requireText("text", text);
311
+ requireText("repoDir", repoDir);
312
+ return {
313
+ command: "claude",
314
+ // `--` ends option parsing: an answer that starts with `--` (claude
315
+ // 2.1.296 reads `--x` there as an unknown option) stays message text.
316
+ args: ["--bg", "--resume", sessionId, "--", text],
317
+ cwd: repoDir,
318
+ env: scrubWorkerEnv(env),
319
+ };
320
+ }
321
+
322
+ /**
323
+ * Pull the session id out of `claude --bg` stdout. A full UUID wins; else the
324
+ * short id `claude --bg` prints. The manager can always map either back to
325
+ * the full `sessionId` through `claude agents --json` by worker name.
326
+ * @param {string} stdout - Command output
327
+ * @returns {string | null} Session id, or null
328
+ */
329
+ export function parseSessionId(stdout) {
330
+ const text = typeof stdout === "string" ? stdout : "";
331
+ return (
332
+ SESSION_UUID_PATTERN.exec(text)?.[0] ??
333
+ SHORT_ID_PATTERN.exec(text)?.[0] ??
334
+ null
335
+ );
336
+ }
337
+
338
+ /**
339
+ * Read a timestamp that may be an ISO string, epoch milliseconds or a Date.
340
+ * @param {unknown} value - Candidate
341
+ * @returns {number | null} Epoch milliseconds, or null
342
+ */
343
+ function toMillis(value) {
344
+ if (value instanceof Date) return value.getTime();
345
+ if (typeof value === "number") return value;
346
+ if (typeof value === "string") {
347
+ const parsed = Date.parse(value);
348
+ return Number.isNaN(parsed) ? null : parsed;
349
+ }
350
+ return null;
351
+ }
352
+
353
+ /**
354
+ * Classify one dispatched item for this cycle.
355
+ *
356
+ * Order matters: a recorded DONE/FAILED wins; a pending question means the
357
+ * worker ended its turn on purpose, so it is `waiting` even when its row
358
+ * reads `done`; only then is liveness judged. `stalled` and `dead` become
359
+ * `exhausted` at `maxAttempts`, which is the manager's cue to move the item to
360
+ * Blocked instead of retrying it again.
361
+ * @param {{agent: ReturnType<typeof normalizeAgents>[number] | null, entry: {status?: string, startedAt?: string|number, lastProgressAt?: string|number|null, logsHash?: string|null, pendingQuestion?: {askedAt?: string|number, lastPingAt?: string|number|null} | null, attempts?: number}, logsHash?: string | null | undefined, now: Date | number, stall?: {noOutputMinutes: number, requestionHours: number, maxAttempts: number}}} input - Classification inputs
362
+ * @returns {"finished"|"waiting"|"requestion"|"working"|"stalled"|"dead"|"exhausted"} Classification
363
+ */
364
+ export function classifyWorker({
365
+ agent,
366
+ entry,
367
+ logsHash,
368
+ now,
369
+ stall = DEFAULT_STALL,
370
+ }) {
371
+ if (FINISHED_STATUSES.has(entry.status ?? "")) return "finished";
372
+ const nowMs = toMillis(now) ?? 0;
373
+ const question = entry.pendingQuestion;
374
+ if (question) {
375
+ const lastPing =
376
+ toMillis(question.lastPingAt) ?? toMillis(question.askedAt) ?? nowMs;
377
+ return nowMs - lastPing > stall.requestionHours * HOUR_MS
378
+ ? "requestion"
379
+ : "waiting";
380
+ }
381
+ const outcome = agent?.active
382
+ ? judgeActive(entry, logsHash, nowMs, stall)
383
+ : "dead";
384
+ if (outcome !== "working" && (entry.attempts ?? 0) >= stall.maxAttempts) {
385
+ return "exhausted";
386
+ }
387
+ return outcome;
388
+ }
389
+
390
+ /**
391
+ * Decide whether an active worker is making progress.
392
+ * @param {{logsHash?: string|null, lastProgressAt?: string|number|null, startedAt?: string|number}} entry - Ledger row
393
+ * @param {string | null | undefined} logsHash - This cycle's hash of `claude logs <name>`
394
+ * @param {number} nowMs - Clock reading
395
+ * @param {{noOutputMinutes: number}} stall - Stall policy
396
+ * @returns {"working"|"stalled"} Classification
397
+ */
398
+ function judgeActive(entry, logsHash, nowMs, stall) {
399
+ if (typeof logsHash === "string" && logsHash !== entry.logsHash)
400
+ return "working";
401
+ const lastProgress =
402
+ toMillis(entry.lastProgressAt) ?? toMillis(entry.startedAt) ?? nowMs;
403
+ return nowMs - lastProgress <= stall.noOutputMinutes * MINUTE_MS
404
+ ? "working"
405
+ : "stalled";
406
+ }
@@ -0,0 +1,158 @@
1
+ ---
2
+ name: lisa-setup-session-manager
3
+ description: "Set up the local session…"
4
+ allowed-tools: ["Bash", "Read", "Write", "Skill", "AskUserQuestion"]
5
+ ---
6
+ Set up the local session manager for this project: writes the non-secret `sessionManager` block to `.lisa.config.json` (detecting a multi-repo `projects/*` layout), stores the project's own Telegram bot token outside the repo, checks the Telegram plugin is installed but not enabled at user scope, keeps the manager's state out of git, seeds the checked-in runbook, starts the manager in tmux, walks the operator through pairing, and finishes with doctor's session-manager check. Idempotent.
7
+
8
+
9
+ # Setup session manager
10
+
11
+ After this skill the project has a `sessionManager` block, a bot token in its own state directory, a
12
+ runbook at `.lisa/automations/session-manager.runbook.md`, and a running manager paired with the
13
+ operator's Telegram chat. Re-running is safe: every step keeps what is already there.
14
+
15
+ The manager itself always runs on Claude Code (see `lisa-session-manager` "Runtime parity"). Setup
16
+ can run from any agent that can reach the scripts; `start` launches the Claude Code manager.
17
+
18
+ ## Step 0: Preconditions
19
+
20
+ Stop with the named gap if any of these fail:
21
+
22
+ - **Tracker configured.** `.tracker` in `.lisa.config.json` is `github`, `jira` or `linear`.
23
+ Otherwise stop: "No tracker configured. Run the matching /lisa:setup:<tracker> first."
24
+ - **Tools.** `claude --version` is at least 2.1.224; `tmux -V` and `bun --version` succeed. Name the
25
+ missing or outdated tool and stop.
26
+
27
+ ## Step 1: Locate the scripts
28
+
29
+ Resolve the scripts directory with the same ladder as `lisa-session-manager`:
30
+
31
+ ```bash
32
+ SM="${SM_SCRIPTS_DIR:-}"
33
+ [ -z "$SM" ] && [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && SM="$CLAUDE_PLUGIN_ROOT/skills/lisa-session-manager/scripts"
34
+ [ -n "$SM" ] && [ -f "$SM/session-manager.mjs" ] || SM="$(claude plugin list --json 2>/dev/null | node -e 'let s="";process.stdin.on("data",d=>{s+=d}).on("end",()=>{let l=[];try{l=JSON.parse(s)}catch{}const c=l.filter(p=>String(p.id).startsWith("lisa@"));const p=c.find(p=>p.projectPath===process.cwd())||c.find(p=>p.scope==="user");process.stdout.write(p?p.installPath:"")})')/skills/lisa-session-manager/scripts"
35
+ [ -f "$SM/session-manager.mjs" ] || SM="$PWD/node_modules/@codyswann/lisa/plugins/lisa/skills/lisa-session-manager/scripts"
36
+ [ -f "$SM/session-manager.mjs" ] || { echo "lisa-session-manager scripts not found" >&2; exit 1; }
37
+ echo "$SM"
38
+ ```
39
+
40
+ Shell variables do not survive between Bash calls: use the printed absolute path wherever this skill
41
+ writes `$SM`, and pass `--project-root "$ROOT"` (the absolute project root) on every call.
42
+
43
+ ## Step 2: Write the config block
44
+
45
+ 1. Preview: `node "$SM/session-manager.mjs" init-config --project-root "$ROOT"`. It detects
46
+ `projects/*/.lisa.config.json` and proposes `name`, `interval`, `repos`, `capacity`, `stall` and a
47
+ per-project `notify.stateDir`. Show the block to the operator.
48
+ 2. Write it: the same command with `--write`. It adds only non-secret fields and never overwrites an
49
+ existing block (`reason: "exists"` means keep it).
50
+ 3. Ask the operator to replace each repo's starter `hints` with the technologies and areas that
51
+ identify it (e.g. `api`: `["NestJS","migrations"]`, `web`: `["Expo","screens"]`), and whether to
52
+ set `sessionManager.trustedAuthorIds`. Without it, any author's Ready item reaches a
53
+ bypass-permissions worker; say so plainly. The list holds stable tracker user ids as strings,
54
+ never logins: the GitHub numeric user id (`gh api users/<login> --jq .id`), the Jira
55
+ `accountId`, or the Linear user id. A login matches no one and silently excludes every item.
56
+ 4. `node "$SM/session-manager.mjs" validate --project-root "$ROOT"` must print `valid: true`.
57
+
58
+ ## Step 3: Store the bot token
59
+
60
+ The operator creates a bot with @BotFather, one per project. Then run
61
+ `node "$SM/session-manager.mjs" store-token --project-root "$ROOT"`.
62
+
63
+ - `stored: true`: done.
64
+ - Exit 2 because a token already exists: keep it (re-running setup is safe). Only when the operator
65
+ says the stored token is wrong or belongs to another project, re-run with `store-token --replace`.
66
+ - `reason: "no-tty"` (an agent's Bash has no terminal): print `command` from the output and ask the
67
+ operator to run it in their own terminal, where it prompts with echo off. Wait for them to confirm.
68
+ - `empty` / `invalid`: say what was wrong and ask again.
69
+
70
+ Never ask for the token in chat, never echo, log or commit it, and never put it in
71
+ `.lisa.config.json`. It lives only in `<notify.stateDir>/.env`.
72
+
73
+ ## Step 4: Telegram plugin scope
74
+
75
+ Read `claude plugin list --json` and look at the `telegram@claude-plugins-official` entries.
76
+
77
+ - **Not installed:** ask the operator to install it with
78
+ `claude plugin install telegram@claude-plugins-official -s user`, then re-check.
79
+ - **Enabled at user scope** (`scope: "user"` and `enabled: true`): every session on this machine
80
+ would start its own poller and take the bot away from the manager. Print the remedy and stop until
81
+ it is fixed:
82
+
83
+ ```bash
84
+ claude plugin disable telegram@claude-plugins-official -s user
85
+ ```
86
+
87
+ - **Installed and not enabled at user scope:** correct. The manager enables it for itself only.
88
+
89
+ ## Step 5: Keep manager state out of git
90
+
91
+ At the project root, when it is a git repo:
92
+ `git check-ignore -q .lisa/session-manager/ledger.json || printf '%s\n' '.lisa/session-manager/' >> "$(git rev-parse --git-common-dir)/info/exclude"`,
93
+ then re-run `git check-ignore -q .lisa/session-manager/ledger.json`. If it is still not ignored, stop
94
+ and report. A root that is not a git repo needs nothing.
95
+
96
+ ## Step 6: Seed the runbook
97
+
98
+ If `.lisa/automations/session-manager.runbook.md` does not exist, copy
99
+ `$SM/../references/runbook-template.md` there. Never overwrite an existing runbook: it is the
100
+ project's. It is checked in, like every automation runbook (`automation-runbook-contract`).
101
+
102
+ ## Step 7: Coexistence warning
103
+
104
+ Tell the operator, before starting: if this project also runs the scheduled `intake-repair` loop, it
105
+ can re-dispatch an item a local worker is still building (it treats a claimed item with no tracker
106
+ activity for `intake.repair.staleAfterHours` as stalled). `intake-tickets` competes for the same
107
+ Ready items. Recommend tearing down `intake-tickets` (`/lisa:tear-down-automations intake-tickets`)
108
+ and either raising `intake.repair.staleAfterHours` above `stall.noOutputMinutes × stall.maxAttempts`
109
+ or tearing down `intake-repair`.
110
+
111
+ ## Step 8: Accept workspace trust in each worker repo
112
+
113
+ Workers are background sessions, and a background session cannot show Claude Code's "trust this
114
+ folder?" prompt: until someone has accepted it in a directory, `claude --bg` refuses to start there
115
+ and `spawn` exits 2 saying so. Do this once per routed repo (or once for the root on a single-repo
116
+ project), on every machine that runs the manager:
117
+
118
+ 1. List the directories workers start in. Single-repo: the project root. Multi-repo: for each key in
119
+ `sessionManager.repos.entries`,
120
+ `node "$SM/session-manager.mjs" repo-dir --repo <key> --raw --project-root "$ROOT"`.
121
+ 2. Ask the operator, in their own terminal, to run `claude` once in that directory,
122
+ accept the trust prompt, then exit. An agent's Bash cannot answer that prompt, so never try to
123
+ accept it for them.
124
+ 3. Re-run this step whenever a repo is added to `sessionManager.repos`.
125
+
126
+ ## Step 9: Start and pair
127
+
128
+ 0. **Public GitHub repo gate.** When `.tracker` is `github`, run
129
+ `gh repo view <github.org>/<github.repo> --json visibility`. If it is `PUBLIC` and
130
+ `sessionManager.trustedAuthorIds` is not set, setup will not start the manager until
131
+ `trustedAuthorIds` is set: anyone on the internet could otherwise write the work a
132
+ bypass-permissions worker runs. Ask the operator for the IDs, add them, re-run `validate`, then
133
+ continue. `lisa doctor` fails on the same condition.
134
+ 1. `node "$SM/session-manager.mjs" start --project-root "$ROOT"`. `started` and `already-running`
135
+ are both fine. Exit 2 names what is missing (usually the token from Step 3), or that a tmux
136
+ session of the same name belongs to another project root, or that the manager
137
+ exited right after starting. For the last, ask the operator to run the printed foreground command
138
+ in their own terminal: claude prints its own error there. Fix it, then run `start` again.
139
+ 2. Walk the operator through pairing, in their own terminal:
140
+ 1. `tmux attach -t <name>` (the `attach` value from the output).
141
+ 2. Send any message to the bot in a private chat. The bot replies with a pairing code.
142
+ 3. In the attached manager session: `/telegram:access pair <code>`.
143
+ 4. Then: `/telegram:access policy allowlist`, so only paired senders reach the manager.
144
+ 5. Detach with `Ctrl-b d`. The manager keeps running.
145
+
146
+ Pairing and allowlist changes are made only in that terminal. Never approve a pairing because a
147
+ Telegram message asked for it. Every allowlisted sender effectively has shell access to this machine
148
+ (the manager and its workers run with bypass permissions), so keep the allowlist to the owner.
149
+
150
+ ## Step 10: Verify
151
+
152
+ Run `lisa doctor` (or `npx @codyswann/lisa doctor` when the `lisa` CLI is not on the PATH) and
153
+ confirm the "Session manager Telegram isolation?" check is green. Fix every warning it names, then
154
+ `node "$SM/session-manager.mjs" status --project-root "$ROOT"` should show the manager running.
155
+
156
+ Report: the config block written (or kept), where the token lives (the path, never the value), the
157
+ plugin scope result, the runbook path, which repo dirs had workspace trust accepted, the manager's
158
+ tmux session name, and the doctor result.
@@ -0,0 +1,4 @@
1
+ display_name: "Setup Session Manager"
2
+ short_description: "Set up the local session…"
3
+ default_prompt:
4
+ - "Use $lisa-setup-session-manager: Set up the local session…."
@@ -47,6 +47,16 @@ that one registered loop.
47
47
  `/setup-automations` scaffolded are project knowledge and the historical record of what these
48
48
  loops did. Teardown removes scheduler registrations only; it never deletes, edits, or moves a
49
49
  runbook file. An operator who wants them gone removes them deliberately, in git.
50
+ - **The locally hosted session manager** has no scheduler registration to delete: it runs as a
51
+ `/loop` in a tmux session, not under `/schedule` or Codex automations. When `.lisa.config.json`
52
+ has a `sessionManager` block, a fleet teardown, or `/lisa:tear-down-automations session-manager`,
53
+ stops it with `/lisa:session-manager stop` (the `session-manager.mjs stop` subcommand, which kills
54
+ only the manager's tmux session and is a no-op when it is not running). Never search the scheduler
55
+ for it and never report it "already absent" there. **Never stop its workers**: each holds claimed
56
+ work and finishes or hands off on its own. List them for the operator instead (run
57
+ `/lisa:session-manager status` before stopping, or `claude agents --json`). Its runbook,
58
+ `.lisa/automations/session-manager.runbook.md`, stays on disk like every other. Without a
59
+ `sessionManager` block there is nothing to stop.
50
60
 
51
61
  ## Answering a `policy-obsolete` teardown proposal
52
62
 
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  description: "Implement a single work item end-to-end. Vendor-agnostic: given a work-item URL/key (JIRA, Linear, GitHub Issues) or description, reads it, determines work type (Build/Fix/Improve/Investigate), assembles an agent team, runs the full lifecycle through PR + evidence. Pass executionEnv=codex-cloud to run the work on a remote surface instead of locally — it submits and returns without polling, so the laptop is a launcher rather than the execution substrate. For batch processing of all Status=Ready tickets in a queue, use /lisa:intake instead."
3
- argument-hint: "[executionEnv=local|codex-cloud] <single-work-item-url | key | description>"
3
+ argument-hint: "[executionEnv=local|codex-cloud] [managedBy=<session-name>] <single-work-item-url | key | description>"
4
4
  ---
5
5
 
6
6
  Use the /lisa-implement skill to take the work item from spec to shipped: read the source, determine work type, assemble an agent team, and run the full lifecycle through PR creation, code review, deploy, and empirical verification.
7
7
 
8
- If `$ARGUMENTS` carries `executionEnv=` with anything other than `local`, route the work to that surface via /lisa-remote-dispatch and report the recorded task identifier instead of running the lifecycle locally. $ARGUMENTS
8
+ If `$ARGUMENTS` carries both `managedBy=` and a non-`local` `executionEnv=`, do not dispatch: stop with "managedBy requires a local Claude Code session; remote surfaces cannot reach the manager." Otherwise, if `$ARGUMENTS` carries `executionEnv=` with anything other than `local`, route the work to that surface via /lisa-remote-dispatch and report the recorded task identifier instead of running the lifecycle locally. `managedBy=<session-name>` sends every question and the final DONE/FAILED status to that Claude Code session instead of the terminal. $ARGUMENTS
@@ -0,0 +1,7 @@
1
+ ---
2
+ description: "Run the local session manager: one dispatch-and-relay cycle (the /loop prompt), or start, stop or show the tmux-hosted manager. Dispatches Ready work items to fresh background worker sessions and relays their questions over the project's Telegram bot. Claude Code only; configure it with /lisa:setup:session-manager."
3
+ allowed-tools: ["Skill"]
4
+ argument-hint: "[cycle|start|stop|status]"
5
+ ---
6
+
7
+ Use the /lisa-session-manager skill to run one manager cycle or to start, stop or report on the manager. $ARGUMENTS
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: "Set up the local session manager for this project: write the sessionManager config block, store the project's own Telegram bot token, check the Telegram plugin scope, seed the runbook, start the manager in tmux, walk through pairing, and run doctor's session-manager check."
3
+ allowed-tools: ["Skill"]
4
+ ---
5
+
6
+ Use the /lisa-setup-session-manager skill to set up and start the session manager for this project. $ARGUMENTS
@@ -21,6 +21,20 @@ Membership is **registration, not skill-existence**: a loop is under this contra
21
21
  registered as a scheduled automation, and registering a new one pulls it in automatically. There is
22
22
  no hardcoded roster of loops anywhere.
23
23
 
24
+ ### Locally hosted loops
25
+
26
+ A loop the runtime's scheduler does not host is still a member. The session manager
27
+ (`lisa-session-manager`) is the case: its registration is the tmux session
28
+ `/lisa:setup:session-manager` starts, running a `/loop`, not a `/schedule` routine or a Codex
29
+ automation. It is a member exactly when `.lisa.config.json` has a `sessionManager` block. It keeps
30
+ the same obligations as a scheduled loop: a checked-in runbook at
31
+ `.lisa/automations/session-manager.runbook.md`, one of the six outcomes per cycle, and a run record
32
+ written through `automation-run-record.mjs` under loop id `session-manager`. Only the scheduler-facing
33
+ surfaces differ. `lisa-automation-status` lists it `UNSUPPORTED`, with a reason naming the tmux
34
+ session, because no scheduler can see it, and still shows its runbook and recorded cycles.
35
+ `lisa-tear-down-automations` stops the tmux session instead of deleting a scheduler entry, never
36
+ stops its workers, and leaves the runbook.
37
+
24
38
  ### Interactive flows are members too
25
39
 
26
40
  The outcome vocabulary is **not cron-specific** — it answers "did this need me?", which an operator