@codyswann/lisa 4.80.1 → 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 (335) hide show
  1. package/README.md +6 -3
  2. package/all/copy-contents/gitignore +5 -0
  3. package/cdk/package-lisa/package.lisa.json +0 -2
  4. package/dist/cli/doctor-cdk-preset-adoption.js +2 -2
  5. package/dist/cli/doctor-cdk-preset-adoption.js.map +1 -1
  6. package/dist/cli/doctor-session-manager-trust.d.ts +62 -0
  7. package/dist/cli/doctor-session-manager-trust.d.ts.map +1 -0
  8. package/dist/cli/doctor-session-manager-trust.js +174 -0
  9. package/dist/cli/doctor-session-manager-trust.js.map +1 -0
  10. package/dist/cli/doctor-session-manager.d.ts +35 -0
  11. package/dist/cli/doctor-session-manager.d.ts.map +1 -0
  12. package/dist/cli/doctor-session-manager.js +209 -0
  13. package/dist/cli/doctor-session-manager.js.map +1 -0
  14. package/dist/cli/doctor.d.ts.map +1 -1
  15. package/dist/cli/doctor.js +3 -0
  16. package/dist/cli/doctor.js.map +1 -1
  17. package/dist/cli/lisa-test-run-bootstrap.js +28 -0
  18. package/dist/cli/lisa-test-run-bootstrap.js.map +1 -1
  19. package/dist/cli/session-manager-config-merge.d.ts +25 -0
  20. package/dist/cli/session-manager-config-merge.d.ts.map +1 -0
  21. package/dist/cli/session-manager-config-merge.js +54 -0
  22. package/dist/cli/session-manager-config-merge.js.map +1 -0
  23. package/dist/core/cdk-preset-adoption.d.ts +7 -2
  24. package/dist/core/cdk-preset-adoption.d.ts.map +1 -1
  25. package/dist/core/cdk-preset-adoption.js +10 -2
  26. package/dist/core/cdk-preset-adoption.js.map +1 -1
  27. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  28. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  29. package/dist/core/upstream-evidence-manifest.js +258 -13
  30. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  31. package/package.json +4 -4
  32. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  34. package/plugins/lisa/.codex-plugin/skills/lisa-automation-status/SKILL.md +10 -0
  35. package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +17 -2
  36. package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  37. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +287 -0
  38. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/agents/openai.yaml +4 -0
  39. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/references/runbook-template.md +66 -0
  40. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  41. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  42. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  43. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  44. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  45. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  46. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  47. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +265 -0
  48. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  49. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  50. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  51. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  52. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  53. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  54. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  55. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +191 -0
  56. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  57. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  58. package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +158 -0
  59. package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
  60. package/plugins/lisa/.codex-plugin/skills/lisa-tear-down-automations/SKILL.md +10 -0
  61. package/plugins/lisa/commands/implement.md +2 -2
  62. package/plugins/lisa/commands/session-manager.md +7 -0
  63. package/plugins/lisa/commands/setup/session-manager.md +6 -0
  64. package/plugins/lisa/rules/reference/automation-runbook-contract.md +14 -0
  65. package/plugins/lisa/rules/reference/config-resolution.md +58 -0
  66. package/plugins/lisa/scripts/automation-status-claude-adapter.mjs +4 -1
  67. package/plugins/lisa/scripts/automation-status-codex-adapter.mjs +4 -1
  68. package/plugins/lisa/scripts/automation-status-expected-fleet.mjs +98 -0
  69. package/plugins/lisa/scripts/automation-status-run-history.mjs +29 -7
  70. package/plugins/lisa/scripts/ready-queue-order.mjs +352 -0
  71. package/plugins/lisa/skills/lisa-automation-status/SKILL.md +10 -0
  72. package/plugins/lisa/skills/lisa-implement/SKILL.md +17 -2
  73. package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  74. package/plugins/lisa/skills/lisa-session-manager/SKILL.md +285 -0
  75. package/plugins/lisa/skills/lisa-session-manager/agents/openai.yaml +4 -0
  76. package/plugins/lisa/skills/lisa-session-manager/references/runbook-template.md +66 -0
  77. package/plugins/lisa/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  78. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  79. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  80. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  81. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  82. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  83. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  84. package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +265 -0
  85. package/plugins/lisa/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  86. package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  87. package/plugins/lisa/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  88. package/plugins/lisa/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  89. package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  90. package/plugins/lisa/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  91. package/plugins/lisa/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  92. package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +191 -0
  93. package/plugins/lisa/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  94. package/plugins/lisa/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  95. package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +156 -0
  96. package/plugins/lisa/skills/lisa-setup-session-manager/agents/openai.yaml +4 -0
  97. package/plugins/lisa/skills/lisa-tear-down-automations/SKILL.md +10 -0
  98. package/plugins/lisa-agy/commands/lisa/implement.md +2 -2
  99. package/plugins/lisa-agy/commands/lisa/session-manager.md +7 -0
  100. package/plugins/lisa-agy/commands/lisa/setup/session-manager.md +6 -0
  101. package/plugins/lisa-agy/plugin.json +1 -1
  102. package/plugins/lisa-agy/scripts/automation-status-claude-adapter.mjs +4 -1
  103. package/plugins/lisa-agy/scripts/automation-status-codex-adapter.mjs +4 -1
  104. package/plugins/lisa-agy/scripts/automation-status-expected-fleet.mjs +98 -0
  105. package/plugins/lisa-agy/scripts/automation-status-run-history.mjs +29 -7
  106. package/plugins/lisa-agy/scripts/ready-queue-order.mjs +352 -0
  107. package/plugins/lisa-agy/skills/lisa-automation-status/SKILL.md +10 -0
  108. package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +17 -2
  109. package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  110. package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +285 -0
  111. package/plugins/lisa-agy/skills/lisa-session-manager/references/runbook-template.md +66 -0
  112. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  113. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  114. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  115. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  116. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  117. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  118. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  119. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +265 -0
  120. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  121. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  122. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  123. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  124. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  125. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  126. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  127. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +191 -0
  128. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  129. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  130. package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +156 -0
  131. package/plugins/lisa-agy/skills/lisa-tear-down-automations/SKILL.md +10 -0
  132. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  133. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  134. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  135. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  136. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  137. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  138. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  139. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  140. package/plugins/lisa-copilot/commands/lisa/implement.md +2 -2
  141. package/plugins/lisa-copilot/commands/lisa/session-manager.md +7 -0
  142. package/plugins/lisa-copilot/commands/lisa/setup/session-manager.md +6 -0
  143. package/plugins/lisa-copilot/rules/reference/automation-runbook-contract.md +14 -0
  144. package/plugins/lisa-copilot/rules/reference/config-resolution.md +58 -0
  145. package/plugins/lisa-copilot/scripts/automation-status-claude-adapter.mjs +4 -1
  146. package/plugins/lisa-copilot/scripts/automation-status-codex-adapter.mjs +4 -1
  147. package/plugins/lisa-copilot/scripts/automation-status-expected-fleet.mjs +98 -0
  148. package/plugins/lisa-copilot/scripts/automation-status-run-history.mjs +29 -7
  149. package/plugins/lisa-copilot/scripts/ready-queue-order.mjs +352 -0
  150. package/plugins/lisa-copilot/skills/lisa-automation-status/SKILL.md +10 -0
  151. package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +17 -2
  152. package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  153. package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +285 -0
  154. package/plugins/lisa-copilot/skills/lisa-session-manager/references/runbook-template.md +66 -0
  155. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  156. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  157. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  158. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  159. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  160. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  161. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  162. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +265 -0
  163. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  164. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  165. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  166. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  167. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  168. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  169. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  170. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +191 -0
  171. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  172. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  173. package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +156 -0
  174. package/plugins/lisa-copilot/skills/lisa-tear-down-automations/SKILL.md +10 -0
  175. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  176. package/plugins/lisa-cursor/commands/lisa/implement.md +2 -2
  177. package/plugins/lisa-cursor/commands/lisa/session-manager.md +7 -0
  178. package/plugins/lisa-cursor/commands/lisa/setup/session-manager.md +6 -0
  179. package/plugins/lisa-cursor/rules/automation-runbook-contract-reference.mdc +14 -0
  180. package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +58 -0
  181. package/plugins/lisa-cursor/scripts/automation-status-claude-adapter.mjs +4 -1
  182. package/plugins/lisa-cursor/scripts/automation-status-codex-adapter.mjs +4 -1
  183. package/plugins/lisa-cursor/scripts/automation-status-expected-fleet.mjs +98 -0
  184. package/plugins/lisa-cursor/scripts/automation-status-run-history.mjs +29 -7
  185. package/plugins/lisa-cursor/scripts/ready-queue-order.mjs +352 -0
  186. package/plugins/lisa-cursor/skills/lisa-automation-status/SKILL.md +10 -0
  187. package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +17 -2
  188. package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  189. package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +285 -0
  190. package/plugins/lisa-cursor/skills/lisa-session-manager/references/runbook-template.md +66 -0
  191. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  192. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  193. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  194. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  195. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  196. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  197. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  198. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +265 -0
  199. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  200. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  201. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  202. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  203. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  204. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  205. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  206. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +191 -0
  207. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  208. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  209. package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +156 -0
  210. package/plugins/lisa-cursor/skills/lisa-tear-down-automations/SKILL.md +10 -0
  211. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  212. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  213. package/plugins/lisa-devin/rules/reference/automation-runbook-contract.md +14 -0
  214. package/plugins/lisa-devin/rules/reference/config-resolution.md +58 -0
  215. package/plugins/lisa-devin/scripts/automation-status-claude-adapter.mjs +4 -1
  216. package/plugins/lisa-devin/scripts/automation-status-codex-adapter.mjs +4 -1
  217. package/plugins/lisa-devin/scripts/automation-status-expected-fleet.mjs +98 -0
  218. package/plugins/lisa-devin/scripts/automation-status-run-history.mjs +29 -7
  219. package/plugins/lisa-devin/scripts/ready-queue-order.mjs +352 -0
  220. package/plugins/lisa-devin/skills/lisa-automation-status/SKILL.md +10 -0
  221. package/plugins/lisa-devin/skills/lisa-implement/SKILL.md +17 -2
  222. package/plugins/lisa-devin/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  223. package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +285 -0
  224. package/plugins/lisa-devin/skills/lisa-session-manager/references/runbook-template.md +66 -0
  225. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  226. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  227. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  228. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  229. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  230. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  231. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  232. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +265 -0
  233. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  234. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  235. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  236. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  237. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  238. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  239. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  240. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +191 -0
  241. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  242. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  243. package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +156 -0
  244. package/plugins/lisa-devin/skills/lisa-tear-down-automations/SKILL.md +10 -0
  245. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  246. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  247. package/plugins/lisa-expo-agy/plugin.json +1 -1
  248. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  249. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  250. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  251. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  252. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  253. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  254. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  255. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  256. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  257. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  258. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  259. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  260. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  261. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  262. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  263. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  264. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  265. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  266. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  267. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  268. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  269. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  270. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  271. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  272. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  273. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  274. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  275. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  276. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  277. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  278. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  279. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  280. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  281. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  282. package/plugins/lisa-rails-agy/plugin.json +1 -1
  283. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  284. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  285. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  286. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  287. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  288. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  289. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  290. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  291. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  292. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  293. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  294. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  295. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  296. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  297. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  298. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  299. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  300. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  301. package/plugins/src/base/commands/implement.md +2 -2
  302. package/plugins/src/base/commands/session-manager.md +7 -0
  303. package/plugins/src/base/commands/setup/session-manager.md +6 -0
  304. package/plugins/src/base/rules/reference/automation-runbook-contract.md +14 -0
  305. package/plugins/src/base/rules/reference/config-resolution.md +58 -0
  306. package/plugins/src/base/scripts/automation-status-claude-adapter.mjs +4 -1
  307. package/plugins/src/base/scripts/automation-status-codex-adapter.mjs +4 -1
  308. package/plugins/src/base/scripts/automation-status-expected-fleet.mjs +98 -0
  309. package/plugins/src/base/scripts/automation-status-run-history.mjs +29 -7
  310. package/plugins/src/base/scripts/ready-queue-order.mjs +352 -0
  311. package/plugins/src/base/skills/lisa-automation-status/SKILL.md +10 -0
  312. package/plugins/src/base/skills/lisa-implement/SKILL.md +17 -2
  313. package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +377 -5
  314. package/plugins/src/base/skills/lisa-session-manager/SKILL.md +285 -0
  315. package/plugins/src/base/skills/lisa-session-manager/references/runbook-template.md +66 -0
  316. package/plugins/src/base/skills/lisa-session-manager/scripts/capacity.mjs +186 -0
  317. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-cycle.mjs +262 -0
  318. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +308 -0
  319. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-queue.mjs +148 -0
  320. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-runtime.mjs +326 -0
  321. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +114 -0
  322. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-workers.mjs +295 -0
  323. package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +265 -0
  324. package/plugins/src/base/skills/lisa-session-manager/scripts/isolation.mjs +130 -0
  325. package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +234 -0
  326. package/plugins/src/base/skills/lisa-session-manager/scripts/ledger.mjs +445 -0
  327. package/plugins/src/base/skills/lisa-session-manager/scripts/reconcile.mjs +134 -0
  328. package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +274 -0
  329. package/plugins/src/base/skills/lisa-session-manager/scripts/state-dir.mjs +112 -0
  330. package/plugins/src/base/skills/lisa-session-manager/scripts/telegram-route.mjs +210 -0
  331. package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +191 -0
  332. package/plugins/src/base/skills/lisa-session-manager/scripts/worker-launch.mjs +92 -0
  333. package/plugins/src/base/skills/lisa-session-manager/scripts/workers.mjs +406 -0
  334. package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +156 -0
  335. package/plugins/src/base/skills/lisa-tear-down-automations/SKILL.md +10 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Decide which worker an inbound Telegram message is for, or whether it is an
3
+ * operator command.
4
+ *
5
+ * Several workers can be waiting on the operator at once, and an answer
6
+ * delivered to the wrong one is acted on with permissions skipped. So routing
7
+ * only ever uses an explicit signal, in this order:
8
+ *
9
+ * 1. a leading `[REF]`, `REF:` or `REF ` naming a ledger ref or its worker
10
+ * token (case-insensitive, exact token — `TEAM-1` never matches `TEAM-12`).
11
+ * A GitHub ref `owner/repo#12` also answers to `repo#12` and `#12`, which
12
+ * is what a phone user types; a short form that names more than one ledger
13
+ * item names none;
14
+ * 2. a Telegram reply to the message that carried a pending question. The
15
+ * Telegram channel plugin (0.0.7) does not pass the replied-to message id
16
+ * on, so the manager can only supply `--reply-to` from a plugin that does;
17
+ * the leading ref is what operators are told to use;
18
+ * 3. an operator command (`status`, `pause`, `resume`, `cap N`, `stop REF`).
19
+ * A recognised command with bad arguments (`cap 40`, `stop A B`) is
20
+ * `invalid-command`, never an answer;
21
+ * 4. the only pending question, when exactly one exists.
22
+ *
23
+ * Anything else is `ambiguous`, and the manager asks which item was meant.
24
+ * Commands sit below an explicit ref or reply, so "pause" sent as a reply to a
25
+ * question is an answer, but above the single-pending fallback, so a bare
26
+ * "pause" is never swallowed as an answer.
27
+ *
28
+ * The message text is untrusted data. It is sliced and returned, never
29
+ * evaluated, interpolated into a shell, or used as a pattern.
30
+ * @module telegram-route
31
+ */
32
+
33
+ import { parseWorkerName, sanitizeRefToken } from "./workers.mjs";
34
+
35
+ /** Upper bound for `cap N`, matching the ledger's `control.maxWorkers`. */
36
+ const MAX_CAP = 32;
37
+
38
+ /** Commands that take no argument. */
39
+ const BARE_COMMANDS = new Set(["status", "pause", "resume"]);
40
+
41
+ /**
42
+ * Leading-ref forms, tried in order. Each captures `[ref, body]`.
43
+ * @type {RegExp[]}
44
+ */
45
+ const PREFIX_PATTERNS = [
46
+ /^\[([^\]\s]+)\]\s*([\s\S]*)$/,
47
+ /^([^\s:[\]]+)\s*:\s*([\s\S]*)$/,
48
+ /^(\S+)\s+([\s\S]*)$/,
49
+ ];
50
+
51
+ /**
52
+ * Every token that names a ledger item: the ref, its name-safe token, and the
53
+ * token in its worker's name.
54
+ * @param {string} ref - Ledger key
55
+ * @param {{worker?: string|null}} entry - Ledger item
56
+ * @returns {string[]} Lower-cased tokens
57
+ */
58
+ function namesFor(ref, entry) {
59
+ const safeToken = (() => {
60
+ try {
61
+ return sanitizeRefToken(ref);
62
+ } catch {
63
+ return null;
64
+ }
65
+ })();
66
+ return [
67
+ ref,
68
+ safeToken,
69
+ parseWorkerName(entry?.worker)?.token,
70
+ ...shortForms(ref),
71
+ ]
72
+ .filter(name => typeof name === "string")
73
+ .map(name => name.toLowerCase());
74
+ }
75
+
76
+ /**
77
+ * The phone-friendly short forms of a GitHub ref: `owner/repo#12` gives
78
+ * `repo#12` and `#12`. Jira and Linear keys have none.
79
+ * @param {string} ref - Ledger key
80
+ * @returns {string[]} Short forms, possibly empty
81
+ */
82
+ function shortForms(ref) {
83
+ const github = /^[^/\s]+\/([^#\s]+)(#\d+)$/.exec(ref);
84
+ return github ? [`${github[1]}${github[2]}`, github[2]] : [];
85
+ }
86
+
87
+ /**
88
+ * Find the ledger ref a token names. A token that names several items (a
89
+ * `#12` shared by two repos) names none, so it can never pick one at random.
90
+ * @param {Record<string, object>} items - Ledger items
91
+ * @param {string} token - Candidate from the message
92
+ * @returns {string | null} Ledger key, or null
93
+ */
94
+ function matchRef(items, token) {
95
+ const wanted = token.toLowerCase();
96
+ const matches = Object.keys(items).filter(ref =>
97
+ namesFor(ref, items[ref]).includes(wanted)
98
+ );
99
+ return matches.length === 1 ? matches[0] : null;
100
+ }
101
+
102
+ /**
103
+ * Read a leading ref and the body after it.
104
+ * @param {Record<string, object>} items - Ledger items
105
+ * @param {string} text - Trimmed message
106
+ * @returns {{ref: string, body: string} | null} Match, or null
107
+ */
108
+ function matchPrefix(items, text) {
109
+ return (
110
+ PREFIX_PATTERNS.map(pattern => pattern.exec(text))
111
+ .filter(match => match !== null)
112
+ .map(([, token, body]) => ({
113
+ ref: matchRef(items, token),
114
+ body: body.trim(),
115
+ }))
116
+ .find(({ ref, body }) => ref !== null && body !== "") ?? null
117
+ );
118
+ }
119
+
120
+ /**
121
+ * Parse an operator command.
122
+ * @param {string} text - Message text
123
+ * @returns {{kind: "command", name: "status"|"pause"|"resume"} | {kind: "command", name: "cap", value: number} | {kind: "command", name: "stop", ref: string} | {kind: "not-a-command", reason?: string}} Command, or why not
124
+ */
125
+ export function parseCommand(text) {
126
+ const words = (typeof text === "string" ? text : "")
127
+ .trim()
128
+ .replace(/^\//, "")
129
+ .split(/\s+/);
130
+ const name = words[0].toLowerCase();
131
+ const args = words.slice(1);
132
+ if (BARE_COMMANDS.has(name) && args.length === 0)
133
+ return { kind: "command", name };
134
+ if (name === "cap") {
135
+ const value =
136
+ args.length === 1 && /^\d+$/.test(args[0]) ? Number(args[0]) : Number.NaN;
137
+ return value <= MAX_CAP
138
+ ? { kind: "command", name, value }
139
+ : {
140
+ kind: "not-a-command",
141
+ reason: `cap needs one whole number from 0 to ${MAX_CAP}`,
142
+ };
143
+ }
144
+ if (name === "stop") {
145
+ return args.length === 1
146
+ ? { kind: "command", name, ref: args[0] }
147
+ : { kind: "not-a-command", reason: "stop needs exactly one ref" };
148
+ }
149
+ return { kind: "not-a-command" };
150
+ }
151
+
152
+ /**
153
+ * Route one inbound message.
154
+ * @param {{text: string, replyToMessageId?: number|null, ledger: {items?: Record<string, {worker?: string|null, pendingQuestion?: {telegramMessageId?: number|null}|null}>}}} input - Message and current ledger
155
+ * @returns {{kind: "answer", ref: string, body: string} | ReturnType<typeof parseCommand> & {kind: "command"} | {kind: "invalid-command", reason: string} | {kind: "ambiguous", candidates: string[]}} Route
156
+ */
157
+ export function routeInbound({ text, replyToMessageId, ledger }) {
158
+ const items = ledger?.items ?? {};
159
+ const message = typeof text === "string" ? text.trim() : "";
160
+ const pending = Object.keys(items)
161
+ .filter(ref => items[ref]?.pendingQuestion)
162
+ .sort();
163
+ const ambiguous = { kind: "ambiguous", candidates: pending };
164
+ if (message === "") return ambiguous;
165
+
166
+ const prefixed = matchPrefix(items, message);
167
+ if (prefixed)
168
+ return { kind: "answer", ref: prefixed.ref, body: prefixed.body };
169
+
170
+ const repliedTo =
171
+ replyToMessageId === undefined || replyToMessageId === null
172
+ ? undefined
173
+ : pending.find(
174
+ ref =>
175
+ items[ref].pendingQuestion.telegramMessageId === replyToMessageId
176
+ );
177
+ if (repliedTo) return { kind: "answer", ref: repliedTo, body: message };
178
+
179
+ const command = parseCommand(message);
180
+ if (command.kind === "command") {
181
+ return command.name === "stop"
182
+ ? { ...command, ref: matchRef(items, command.ref) ?? command.ref }
183
+ : command;
184
+ }
185
+ // A recognised command with bad arguments ("cap 40", "stop A B") is a
186
+ // mistyped command, not an answer. Falling through to the single-pending
187
+ // fallback would hand it to a worker that acts on it with permissions
188
+ // skipped, so the operator is told what was wrong instead.
189
+ if (command.reason) {
190
+ return { kind: "invalid-command", reason: command.reason };
191
+ }
192
+
193
+ return pending.length === 1
194
+ ? { kind: "answer", ref: pending[0], body: message }
195
+ : ambiguous;
196
+ }
197
+
198
+ /**
199
+ * Wrap an operator's answer for delivery to a worker, so the worker can tell
200
+ * a human answer from the manager's own messages.
201
+ * @param {string} owner - Display name of the person who answered
202
+ * @param {string} body - Answer text, passed through as data
203
+ * @returns {string} Message for the worker
204
+ */
205
+ export function formatAnswer(owner, body) {
206
+ if (typeof owner !== "string" || owner.trim() === "") {
207
+ throw new Error("owner must be a non-empty string");
208
+ }
209
+ return `${owner.trim()}'s answer: ${body}`;
210
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * The Telegram bot token the manager needs: validated, stored in
3
+ * `<stateDir>/.env` (dir 0700, file 0600), read from the operator's terminal
4
+ * with echo off, and never put in argv, a return value, an error message or a
5
+ * log line. An existing token is replaced only on request. Split out of
6
+ * launch.mjs, which re-exports everything here.
7
+ * @module token
8
+ */
9
+ import { randomUUID } from "node:crypto";
10
+ import * as nodeFs from "node:fs";
11
+ import path from "node:path";
12
+
13
+ import { LaunchError, resolveStateDir } from "./state-dir.mjs";
14
+
15
+ /** The shape of a BotFather token: `<bot id>:<secret>`. */
16
+ const BOT_TOKEN_PATTERN = /^\d+:[A-Za-z0-9_-]{30,}$/;
17
+
18
+ /** The plugin reads its token from this file in the state dir. */
19
+ const TOKEN_FILE_NAME = ".env";
20
+
21
+ /**
22
+ * Whether `token` looks like a BotFather bot token.
23
+ * @param {unknown} token - Candidate
24
+ * @returns {boolean} True for `<digits>:<30+ of [A-Za-z0-9_-]>`
25
+ */
26
+ export function validateBotToken(token) {
27
+ return typeof token === "string" && BOT_TOKEN_PATTERN.test(token);
28
+ }
29
+
30
+ /**
31
+ * Write `<stateDir>/.env` as `TELEGRAM_BOT_TOKEN=<token>`.
32
+ *
33
+ * The dir is chmod-ed to 0700 even when it already existed, because `mkdir`'s
34
+ * mode only applies to dirs it creates. The file is written to a temp name
35
+ * created 0600 and renamed over the target, so there is never a moment when a
36
+ * token sits in a file with looser permissions, and a replaced file's old
37
+ * mode never carries over.
38
+ * @param {string} stateDir - Absolute state dir (already resolved)
39
+ * @param {string} token - Bot token; validated, never echoed
40
+ * @param {{ fs?: Pick<typeof nodeFs, "mkdirSync" | "chmodSync" |
41
+ * "writeFileSync" | "renameSync"> }} [seams] - Injected fs
42
+ * @returns {{ path: string }} Where the token file is
43
+ */
44
+ export function writeTokenFile(stateDir, token, seams = {}) {
45
+ const fs = seams.fs ?? nodeFs;
46
+ if (!validateBotToken(token)) {
47
+ throw new LaunchError(
48
+ "That does not look like a Telegram bot token " +
49
+ "(expected <digits>:<30 or more letters, digits, _ or ->). Nothing was stored."
50
+ );
51
+ }
52
+ fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
53
+ fs.chmodSync(stateDir, 0o700);
54
+ const target = path.join(stateDir, TOKEN_FILE_NAME);
55
+ const temp = path.join(stateDir, `${TOKEN_FILE_NAME}.${randomUUID()}.tmp`);
56
+ fs.writeFileSync(temp, `TELEGRAM_BOT_TOKEN=${token}\n`, {
57
+ mode: 0o600,
58
+ flag: "wx",
59
+ });
60
+ fs.chmodSync(temp, 0o600);
61
+ fs.renameSync(temp, target);
62
+ return { path: target };
63
+ }
64
+
65
+ /**
66
+ * Load `promptSecret`/`canPrompt` from the secrets-access skill, by path
67
+ * relative to this file, the same way other skills reach it. Lazy so the
68
+ * argv builders never pay for it.
69
+ * @returns {Promise<{ promptSecret: Function, canPrompt: Function }>} Module
70
+ */
71
+ export function loadPromptSecret() {
72
+ return import(
73
+ new URL(
74
+ "../../lisa-secrets-access/scripts/prompt-secret.mjs",
75
+ import.meta.url
76
+ ).href
77
+ );
78
+ }
79
+
80
+ /**
81
+ * Quote one word for a POSIX shell.
82
+ * @param {string} word - Word to quote
83
+ * @returns {string} Single-quoted word
84
+ */
85
+ function shellQuote(word) {
86
+ const escaped = String(word).replaceAll("'", String.raw`'\''`);
87
+ return `'${escaped}'`;
88
+ }
89
+
90
+ /**
91
+ * The command an operator pastes into their own terminal to store the token,
92
+ * for when the caller (an agent's Bash) has no TTY to prompt on.
93
+ * @param {{ scriptsDir: string, projectRoot: string, replace?: boolean }}
94
+ * input - Script and project locations, and whether to carry `--replace`
95
+ * @returns {string} One shell line
96
+ */
97
+ export function buildStoreTokenCommand({ scriptsDir, projectRoot, replace }) {
98
+ const script = shellQuote(path.join(scriptsDir, "session-manager.mjs"));
99
+ const flag = replace ? " --replace" : "";
100
+ return `node ${script} store-token${flag} --project-root ${shellQuote(projectRoot)}`;
101
+ }
102
+
103
+ /**
104
+ * Prompt for the bot token on the operator's terminal and store it.
105
+ *
106
+ * An existing token is never overwritten without `replace`: a second project
107
+ * that shares this state dir by mistake would otherwise silently take over the
108
+ * first project's bot. With no TTY this does not prompt and does not fail: it
109
+ * returns the exact command for the operator to run themselves, because an
110
+ * agent's Bash can never answer a prompt and a hung setup looks like a slow
111
+ * one.
112
+ * @param {{ stateDir: string, scriptsDir: string, projectRoot: string,
113
+ * replace?: boolean, env?: Record<string, string | undefined>,
114
+ * homedir?: string, io?: { canPrompt?: () => boolean,
115
+ * promptSecret?: (message: string) => string, fs?: object } }} input -
116
+ * Configured state dir, locations, the `--replace` flag and seams
117
+ * @returns {Promise<{ stored: true, path: string } |
118
+ * { stored: false, reason: "no-tty", command: string } |
119
+ * { stored: false, reason: "empty" | "invalid", message: string }>} Outcome
120
+ */
121
+ export async function storeToken(input) {
122
+ const { scriptsDir, projectRoot, replace = false, io = {} } = input;
123
+ const stateDir = resolveStateDir(input.stateDir, input);
124
+ const existing = path.join(stateDir, TOKEN_FILE_NAME);
125
+ const exists = io.fs?.existsSync ?? nodeFs.existsSync;
126
+ if (!replace && exists(existing)) {
127
+ throw new LaunchError(
128
+ `A bot token already exists at ${existing}. Nothing was changed. ` +
129
+ `Re-run store-token with --replace to overwrite it.`
130
+ );
131
+ }
132
+ const loaded =
133
+ io.canPrompt && io.promptSecret ? null : await loadPromptSecret();
134
+ const canPrompt = io.canPrompt ?? (() => loaded.canPrompt());
135
+ const promptSecret = io.promptSecret ?? loaded.promptSecret;
136
+
137
+ if (!canPrompt()) {
138
+ return {
139
+ stored: false,
140
+ reason: "no-tty",
141
+ command: buildStoreTokenCommand({ scriptsDir, projectRoot, replace }),
142
+ };
143
+ }
144
+ const typed = promptSecret(
145
+ `Paste the Telegram bot token for ${stateDir} (input is hidden): `
146
+ );
147
+ if (!typed) {
148
+ return { stored: false, reason: "empty", message: "Nothing entered." };
149
+ }
150
+ if (!validateBotToken(typed)) {
151
+ return {
152
+ stored: false,
153
+ reason: "invalid",
154
+ message:
155
+ "That does not look like a Telegram bot token " +
156
+ "(expected <digits>:<30 or more letters, digits, _ or ->).",
157
+ };
158
+ }
159
+ return { stored: true, ...writeTokenFile(stateDir, typed, io) };
160
+ }
161
+
162
+ /**
163
+ * Refuse to start unless `<stateDir>/.env` exists and only its owner can read
164
+ * it. Only the file's metadata is read, never its contents.
165
+ * @param {string} stateDir - Absolute state dir (already resolved)
166
+ * @param {{ fs?: Pick<typeof nodeFs, "statSync"> }} [seams] - Injected fs
167
+ * @returns {{ path: string }} The token file
168
+ */
169
+ export function assertTokenPresent(stateDir, seams = {}) {
170
+ const fs = seams.fs ?? nodeFs;
171
+ const file = path.join(stateDir, TOKEN_FILE_NAME);
172
+ let stat;
173
+ try {
174
+ stat = fs.statSync(file);
175
+ } catch {
176
+ throw new LaunchError(
177
+ `No Telegram bot token at ${file}. Run store-token first.`
178
+ );
179
+ }
180
+ if (!stat.isFile()) {
181
+ throw new LaunchError(`${file} is not a regular file.`);
182
+ }
183
+ const bits = stat.mode & 0o777;
184
+ if ((bits & 0o077) !== 0) {
185
+ throw new LaunchError(
186
+ `${file} is readable by other users (mode ${bits.toString(8)}). ` +
187
+ `Run: chmod 600 ${shellQuote(file)}`
188
+ );
189
+ }
190
+ return { path: file };
191
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Running `claude --bg` for `spawn` and `resume`, with the one failure that
3
+ * is an operator setup step rather than a worker failure called out.
4
+ *
5
+ * Claude Code asks once per directory whether to trust the workspace. Until
6
+ * someone has accepted that prompt in the repo dir, `claude --bg` there exits
7
+ * 1 with (2.1.296) "Workspace not trusted. Run `claude` in <dir> once and
8
+ * accept the trust prompt, then retry." A background session cannot show the
9
+ * prompt, so retrying never helps: that refusal becomes exit 2 (setup) with
10
+ * the exact step, and the manager skill escalates it instead of counting a
11
+ * failed attempt. Every other failure keeps the exit-1 message `mustRun`
12
+ * gives.
13
+ * @module session-manager/worker-launch
14
+ */
15
+ import { CliError, EXIT, run } from "./cli-runtime.mjs";
16
+
17
+ /** How claude words the refusal; matched loosely across versions. */
18
+ const UNTRUSTED_WORKSPACE = /workspace (?:is )?not trusted/i;
19
+
20
+ /**
21
+ * Whether a failed `claude --bg` run was refused for workspace trust.
22
+ * @param {{ stdout: string, stderr: string }} result - Run outcome
23
+ * @returns {boolean} True for the trust refusal
24
+ */
25
+ export function isUntrustedWorkspace(result) {
26
+ return UNTRUSTED_WORKSPACE.test(`${result.stderr}\n${result.stdout}`);
27
+ }
28
+
29
+ /**
30
+ * Run a worker spawn or resume spec and return its stdout.
31
+ *
32
+ * Runs the command exactly once: a failure is reported, never retried, so a
33
+ * flaky refusal can never start a second worker.
34
+ * @param {{ command: string, args: string[], cwd: string,
35
+ * env: Record<string, string | undefined> }} spec - From
36
+ * `buildWorkerSpawn` or `buildWorkerResume`
37
+ * @returns {string} stdout
38
+ */
39
+ export function runWorkerSession(spec) {
40
+ const result = run(spec.command, spec.args, spec);
41
+ if (result.missing) {
42
+ throw new CliError(`${spec.command} is not on PATH.`, EXIT.FAILURE);
43
+ }
44
+ if (result.status === 0) return result.stdout;
45
+ if (isUntrustedWorkspace(result)) {
46
+ throw new CliError(
47
+ `Claude Code's workspace trust has not been accepted in ${spec.cwd}, ` +
48
+ `so claude --bg refused to start a worker there. This is a one-time ` +
49
+ `step per repo: run \`claude\` once in ${spec.cwd}, accept the trust ` +
50
+ `prompt, exit, then retry.`,
51
+ EXIT.SETUP
52
+ );
53
+ }
54
+ const detail = result.stderr.trim() || `exit ${result.status}`;
55
+ throw new CliError(
56
+ `${spec.command} ${spec.args[0] ?? ""} failed: ${detail}`,
57
+ EXIT.FAILURE
58
+ );
59
+ }
60
+
61
+ /** A full session UUID; the only id `--resume` continues in place. */
62
+ const FULL_SESSION_ID =
63
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
64
+
65
+ /**
66
+ * The full session id for a ledger item, for `claude --bg --resume`.
67
+ *
68
+ * `claude --bg` prints an 8-character short id, and that is what the ledger
69
+ * records. Given a short id, `--resume` (2.1.296) does not wake the session:
70
+ * it starts a copy of the conversation under a new id and names it after the
71
+ * message, which the manager cannot see. So a short id is mapped to the full
72
+ * `sessionId` of the matching `claude agents --json --all` row, by worker
73
+ * name or by short id.
74
+ * @param {Array<{name: string|null, id: string|null, sessionId: string|null}>}
75
+ * agents - Normalized rows from `listAgents(ctx, { all: true })`
76
+ * @param {{ worker?: string|null, sessionId?: string|null }} item - Ledger item
77
+ * @returns {string | null} Full lowercase session id, or null when no row
78
+ * carries one
79
+ */
80
+ export function resolveFullSessionId(agents, item) {
81
+ const recorded = String(item?.sessionId ?? "").toLowerCase();
82
+ if (FULL_SESSION_ID.test(recorded)) return recorded;
83
+ const row = agents.find(
84
+ agent =>
85
+ FULL_SESSION_ID.test(agent.sessionId ?? "") &&
86
+ ((item?.worker && agent.name === item.worker) ||
87
+ (recorded !== "" &&
88
+ (agent.id === recorded ||
89
+ agent.sessionId.toLowerCase().startsWith(`${recorded}-`))))
90
+ );
91
+ return row ? row.sessionId.toLowerCase() : null;
92
+ }