lastlight 0.15.0 → 0.16.1

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 (382) hide show
  1. package/README.md +71 -692
  2. package/dist/cli-config.js.map +1 -0
  3. package/dist/cli-format.js.map +1 -0
  4. package/dist/{cli/cli-server.d.ts → cli-server.d.ts} +26 -1
  5. package/dist/{cli/cli-server.js → cli-server.js} +40 -5
  6. package/dist/cli-server.js.map +1 -0
  7. package/dist/cli-timeline.js.map +1 -0
  8. package/dist/{cli/cli.js → cli.js} +4 -3
  9. package/dist/cli.js.map +1 -0
  10. package/dist/{cli/fork-cli.d.ts → fork-cli.d.ts} +3 -3
  11. package/dist/{cli/fork-cli.js → fork-cli.js} +28 -27
  12. package/dist/fork-cli.js.map +1 -0
  13. package/dist/{cli/oauth-cli.js → oauth-cli.js} +1 -1
  14. package/dist/oauth-cli.js.map +1 -0
  15. package/dist/{cli/setup.js → setup.js} +10 -4
  16. package/dist/setup.js.map +1 -0
  17. package/dist/{cli/skills-install.d.ts → skills-install.d.ts} +6 -0
  18. package/dist/{cli/skills-install.js → skills-install.js} +6 -2
  19. package/dist/skills-install.js.map +1 -0
  20. package/package.json +20 -65
  21. package/plugins/lastlight/.claude-plugin/plugin.json +1 -1
  22. package/agent-context/rules.md +0 -80
  23. package/agent-context/security.md +0 -69
  24. package/agent-context/soul.md +0 -21
  25. package/config/default.yaml +0 -119
  26. package/deploy/.env.production.example +0 -100
  27. package/deploy/entrypoint.sh +0 -82
  28. package/deploy/native/README.md +0 -160
  29. package/deploy/native/install.sh +0 -110
  30. package/deploy/native/lastlight.env.example +0 -95
  31. package/deploy/native/lastlight.service +0 -50
  32. package/deploy/sandbox-entrypoint.sh +0 -93
  33. package/dist/admin/auth.d.ts +0 -51
  34. package/dist/admin/auth.js +0 -121
  35. package/dist/admin/auth.js.map +0 -1
  36. package/dist/admin/chat-session-reader.d.ts +0 -39
  37. package/dist/admin/chat-session-reader.js +0 -84
  38. package/dist/admin/chat-session-reader.js.map +0 -1
  39. package/dist/admin/docker.d.ts +0 -61
  40. package/dist/admin/docker.js +0 -187
  41. package/dist/admin/docker.js.map +0 -1
  42. package/dist/admin/index.d.ts +0 -9
  43. package/dist/admin/index.js +0 -59
  44. package/dist/admin/index.js.map +0 -1
  45. package/dist/admin/routes.d.ts +0 -81
  46. package/dist/admin/routes.js +0 -1381
  47. package/dist/admin/routes.js.map +0 -1
  48. package/dist/admin/sessions.d.ts +0 -89
  49. package/dist/admin/sessions.js +0 -204
  50. package/dist/admin/sessions.js.map +0 -1
  51. package/dist/admin/tail.d.ts +0 -12
  52. package/dist/admin/tail.js +0 -145
  53. package/dist/admin/tail.js.map +0 -1
  54. package/dist/admin/version.d.ts +0 -22
  55. package/dist/admin/version.js +0 -92
  56. package/dist/admin/version.js.map +0 -1
  57. package/dist/cli/cli-config.js.map +0 -1
  58. package/dist/cli/cli-format.js.map +0 -1
  59. package/dist/cli/cli-server.js.map +0 -1
  60. package/dist/cli/cli-timeline.js.map +0 -1
  61. package/dist/cli/cli.js.map +0 -1
  62. package/dist/cli/fork-cli.js.map +0 -1
  63. package/dist/cli/oauth-cli.js.map +0 -1
  64. package/dist/cli/setup.js.map +0 -1
  65. package/dist/cli/skills-install.js.map +0 -1
  66. package/dist/config/config-resolve.d.ts +0 -28
  67. package/dist/config/config-resolve.js +0 -47
  68. package/dist/config/config-resolve.js.map +0 -1
  69. package/dist/config/config.d.ts +0 -157
  70. package/dist/config/config.js +0 -581
  71. package/dist/config/config.js.map +0 -1
  72. package/dist/config/core-pin.d.ts +0 -16
  73. package/dist/config/core-pin.js +0 -85
  74. package/dist/config/core-pin.js.map +0 -1
  75. package/dist/config/overlay-assets.d.ts +0 -20
  76. package/dist/config/overlay-assets.js +0 -114
  77. package/dist/config/overlay-assets.js.map +0 -1
  78. package/dist/config/overlay-bootstrap.d.ts +0 -44
  79. package/dist/config/overlay-bootstrap.js +0 -304
  80. package/dist/config/overlay-bootstrap.js.map +0 -1
  81. package/dist/connectors/github-webhook.d.ts +0 -45
  82. package/dist/connectors/github-webhook.js +0 -321
  83. package/dist/connectors/github-webhook.js.map +0 -1
  84. package/dist/connectors/index.d.ts +0 -18
  85. package/dist/connectors/index.js +0 -41
  86. package/dist/connectors/index.js.map +0 -1
  87. package/dist/connectors/messaging/base.d.ts +0 -42
  88. package/dist/connectors/messaging/base.js +0 -139
  89. package/dist/connectors/messaging/base.js.map +0 -1
  90. package/dist/connectors/messaging/delivery.d.ts +0 -14
  91. package/dist/connectors/messaging/delivery.js +0 -38
  92. package/dist/connectors/messaging/delivery.js.map +0 -1
  93. package/dist/connectors/messaging/index.d.ts +0 -4
  94. package/dist/connectors/messaging/index.js +0 -4
  95. package/dist/connectors/messaging/index.js.map +0 -1
  96. package/dist/connectors/messaging/session-manager.d.ts +0 -53
  97. package/dist/connectors/messaging/session-manager.js +0 -255
  98. package/dist/connectors/messaging/session-manager.js.map +0 -1
  99. package/dist/connectors/messaging/types.d.ts +0 -58
  100. package/dist/connectors/messaging/types.js +0 -7
  101. package/dist/connectors/messaging/types.js.map +0 -1
  102. package/dist/connectors/slack/connector.d.ts +0 -100
  103. package/dist/connectors/slack/connector.js +0 -341
  104. package/dist/connectors/slack/connector.js.map +0 -1
  105. package/dist/connectors/slack/index.d.ts +0 -2
  106. package/dist/connectors/slack/index.js +0 -3
  107. package/dist/connectors/slack/index.js.map +0 -1
  108. package/dist/connectors/slack/mrkdwn.d.ts +0 -8
  109. package/dist/connectors/slack/mrkdwn.js +0 -123
  110. package/dist/connectors/slack/mrkdwn.js.map +0 -1
  111. package/dist/connectors/types.d.ts +0 -61
  112. package/dist/connectors/types.js +0 -2
  113. package/dist/connectors/types.js.map +0 -1
  114. package/dist/cron/fanout.d.ts +0 -33
  115. package/dist/cron/fanout.js +0 -55
  116. package/dist/cron/fanout.js.map +0 -1
  117. package/dist/cron/jobs.d.ts +0 -18
  118. package/dist/cron/jobs.js +0 -37
  119. package/dist/cron/jobs.js.map +0 -1
  120. package/dist/cron/scheduler.d.ts +0 -45
  121. package/dist/cron/scheduler.js +0 -105
  122. package/dist/cron/scheduler.js.map +0 -1
  123. package/dist/engine/agent-executor.d.ts +0 -34
  124. package/dist/engine/agent-executor.js +0 -246
  125. package/dist/engine/agent-executor.js.map +0 -1
  126. package/dist/engine/chat/chat-runner.d.ts +0 -132
  127. package/dist/engine/chat/chat-runner.js +0 -424
  128. package/dist/engine/chat/chat-runner.js.map +0 -1
  129. package/dist/engine/chat/chat-skills.d.ts +0 -45
  130. package/dist/engine/chat/chat-skills.js +0 -184
  131. package/dist/engine/chat/chat-skills.js.map +0 -1
  132. package/dist/engine/chat/chat.d.ts +0 -54
  133. package/dist/engine/chat/chat.js +0 -316
  134. package/dist/engine/chat/chat.js.map +0 -1
  135. package/dist/engine/chat/message-batcher.d.ts +0 -32
  136. package/dist/engine/chat/message-batcher.js +0 -109
  137. package/dist/engine/chat/message-batcher.js.map +0 -1
  138. package/dist/engine/dispatcher.d.ts +0 -73
  139. package/dist/engine/dispatcher.js +0 -663
  140. package/dist/engine/dispatcher.js.map +0 -1
  141. package/dist/engine/event-shim.d.ts +0 -117
  142. package/dist/engine/event-shim.js +0 -435
  143. package/dist/engine/event-shim.js.map +0 -1
  144. package/dist/engine/executors/orchestrator.d.ts +0 -81
  145. package/dist/engine/executors/orchestrator.js +0 -365
  146. package/dist/engine/executors/orchestrator.js.map +0 -1
  147. package/dist/engine/executors/shared.d.ts +0 -189
  148. package/dist/engine/executors/shared.js +0 -616
  149. package/dist/engine/executors/shared.js.map +0 -1
  150. package/dist/engine/github/git-auth.d.ts +0 -62
  151. package/dist/engine/github/git-auth.js +0 -161
  152. package/dist/engine/github/git-auth.js.map +0 -1
  153. package/dist/engine/github/github-app-client.d.ts +0 -23
  154. package/dist/engine/github/github-app-client.js +0 -33
  155. package/dist/engine/github/github-app-client.js.map +0 -1
  156. package/dist/engine/github/github-tools.d.ts +0 -18
  157. package/dist/engine/github/github-tools.js +0 -263
  158. package/dist/engine/github/github-tools.js.map +0 -1
  159. package/dist/engine/github/github.d.ts +0 -1193
  160. package/dist/engine/github/github.js +0 -357
  161. package/dist/engine/github/github.js.map +0 -1
  162. package/dist/engine/github/profiles.d.ts +0 -279
  163. package/dist/engine/github/profiles.js +0 -42
  164. package/dist/engine/github/profiles.js.map +0 -1
  165. package/dist/engine/github/review-poster.d.ts +0 -98
  166. package/dist/engine/github/review-poster.js +0 -177
  167. package/dist/engine/github/review-poster.js.map +0 -1
  168. package/dist/engine/llm.d.ts +0 -79
  169. package/dist/engine/llm.js +0 -269
  170. package/dist/engine/llm.js.map +0 -1
  171. package/dist/engine/oauth.d.ts +0 -43
  172. package/dist/engine/oauth.js +0 -103
  173. package/dist/engine/oauth.js.map +0 -1
  174. package/dist/engine/router.d.ts +0 -29
  175. package/dist/engine/router.js +0 -680
  176. package/dist/engine/router.js.map +0 -1
  177. package/dist/engine/screen/classifier.d.ts +0 -93
  178. package/dist/engine/screen/classifier.js +0 -215
  179. package/dist/engine/screen/classifier.js.map +0 -1
  180. package/dist/engine/screen/screen.d.ts +0 -41
  181. package/dist/engine/screen/screen.js +0 -93
  182. package/dist/engine/screen/screen.js.map +0 -1
  183. package/dist/evals-api.d.ts +0 -24
  184. package/dist/evals-api.js +0 -22
  185. package/dist/evals-api.js.map +0 -1
  186. package/dist/index.d.ts +0 -1
  187. package/dist/index.js +0 -923
  188. package/dist/index.js.map +0 -1
  189. package/dist/managed-repos.d.ts +0 -30
  190. package/dist/managed-repos.js +0 -72
  191. package/dist/managed-repos.js.map +0 -1
  192. package/dist/notify/index.d.ts +0 -15
  193. package/dist/notify/index.js +0 -6
  194. package/dist/notify/index.js.map +0 -1
  195. package/dist/notify/model.d.ts +0 -57
  196. package/dist/notify/model.js +0 -89
  197. package/dist/notify/model.js.map +0 -1
  198. package/dist/notify/notifier.d.ts +0 -17
  199. package/dist/notify/notifier.js +0 -87
  200. package/dist/notify/notifier.js.map +0 -1
  201. package/dist/notify/render.d.ts +0 -21
  202. package/dist/notify/render.js +0 -59
  203. package/dist/notify/render.js.map +0 -1
  204. package/dist/notify/transports/github.d.ts +0 -26
  205. package/dist/notify/transports/github.js +0 -26
  206. package/dist/notify/transports/github.js.map +0 -1
  207. package/dist/notify/transports/slack.d.ts +0 -26
  208. package/dist/notify/transports/slack.js +0 -28
  209. package/dist/notify/transports/slack.js.map +0 -1
  210. package/dist/notify/types.d.ts +0 -95
  211. package/dist/notify/types.js +0 -16
  212. package/dist/notify/types.js.map +0 -1
  213. package/dist/providers.d.ts +0 -125
  214. package/dist/providers.js +0 -279
  215. package/dist/providers.js.map +0 -1
  216. package/dist/sandbox/docker.d.ts +0 -180
  217. package/dist/sandbox/docker.js +0 -517
  218. package/dist/sandbox/docker.js.map +0 -1
  219. package/dist/sandbox/egress-allowlist.d.ts +0 -74
  220. package/dist/sandbox/egress-allowlist.js +0 -164
  221. package/dist/sandbox/egress-allowlist.js.map +0 -1
  222. package/dist/sandbox/egress-firewall-config.d.ts +0 -127
  223. package/dist/sandbox/egress-firewall-config.js +0 -434
  224. package/dist/sandbox/egress-firewall-config.js.map +0 -1
  225. package/dist/sandbox/images.d.ts +0 -23
  226. package/dist/sandbox/images.js +0 -55
  227. package/dist/sandbox/images.js.map +0 -1
  228. package/dist/sandbox/index.d.ts +0 -112
  229. package/dist/sandbox/index.js +0 -332
  230. package/dist/sandbox/index.js.map +0 -1
  231. package/dist/sandbox/sandbox.d.ts +0 -214
  232. package/dist/sandbox/sandbox.js +0 -453
  233. package/dist/sandbox/sandbox.js.map +0 -1
  234. package/dist/sandbox/smol.d.ts +0 -162
  235. package/dist/sandbox/smol.js +0 -485
  236. package/dist/sandbox/smol.js.map +0 -1
  237. package/dist/session-log.d.ts +0 -63
  238. package/dist/session-log.js +0 -290
  239. package/dist/session-log.js.map +0 -1
  240. package/dist/state/approval-store.d.ts +0 -88
  241. package/dist/state/approval-store.js +0 -149
  242. package/dist/state/approval-store.js.map +0 -1
  243. package/dist/state/build-assets.d.ts +0 -50
  244. package/dist/state/build-assets.js +0 -154
  245. package/dist/state/build-assets.js.map +0 -1
  246. package/dist/state/db.d.ts +0 -81
  247. package/dist/state/db.js +0 -155
  248. package/dist/state/db.js.map +0 -1
  249. package/dist/state/execution-store.d.ts +0 -257
  250. package/dist/state/execution-store.js +0 -527
  251. package/dist/state/execution-store.js.map +0 -1
  252. package/dist/state/migrate.d.ts +0 -15
  253. package/dist/state/migrate.js +0 -175
  254. package/dist/state/migrate.js.map +0 -1
  255. package/dist/state/workflow-run-store.d.ts +0 -216
  256. package/dist/state/workflow-run-store.js +0 -403
  257. package/dist/state/workflow-run-store.js.map +0 -1
  258. package/dist/telemetry/index.d.ts +0 -38
  259. package/dist/telemetry/index.js +0 -253
  260. package/dist/telemetry/index.js.map +0 -1
  261. package/dist/telemetry/pi-events.d.ts +0 -12
  262. package/dist/telemetry/pi-events.js +0 -134
  263. package/dist/telemetry/pi-events.js.map +0 -1
  264. package/dist/workflows/dag.d.ts +0 -37
  265. package/dist/workflows/dag.js +0 -118
  266. package/dist/workflows/dag.js.map +0 -1
  267. package/dist/workflows/loader.d.ts +0 -53
  268. package/dist/workflows/loader.js +0 -421
  269. package/dist/workflows/loader.js.map +0 -1
  270. package/dist/workflows/loop-eval.d.ts +0 -28
  271. package/dist/workflows/loop-eval.js +0 -74
  272. package/dist/workflows/loop-eval.js.map +0 -1
  273. package/dist/workflows/phase-executor.d.ts +0 -215
  274. package/dist/workflows/phase-executor.js +0 -1144
  275. package/dist/workflows/phase-executor.js.map +0 -1
  276. package/dist/workflows/phase-ref.d.ts +0 -50
  277. package/dist/workflows/phase-ref.js +0 -91
  278. package/dist/workflows/phase-ref.js.map +0 -1
  279. package/dist/workflows/resume.d.ts +0 -58
  280. package/dist/workflows/resume.js +0 -313
  281. package/dist/workflows/resume.js.map +0 -1
  282. package/dist/workflows/runner.d.ts +0 -63
  283. package/dist/workflows/runner.js +0 -364
  284. package/dist/workflows/runner.js.map +0 -1
  285. package/dist/workflows/schema.d.ts +0 -344
  286. package/dist/workflows/schema.js +0 -460
  287. package/dist/workflows/schema.js.map +0 -1
  288. package/dist/workflows/simple.d.ts +0 -90
  289. package/dist/workflows/simple.js +0 -393
  290. package/dist/workflows/simple.js.map +0 -1
  291. package/dist/workflows/target-policy.d.ts +0 -74
  292. package/dist/workflows/target-policy.js +0 -80
  293. package/dist/workflows/target-policy.js.map +0 -1
  294. package/dist/workflows/templates.d.ts +0 -88
  295. package/dist/workflows/templates.js +0 -127
  296. package/dist/workflows/templates.js.map +0 -1
  297. package/dist/workflows/triggers.d.ts +0 -21
  298. package/dist/workflows/triggers.js +0 -45
  299. package/dist/workflows/triggers.js.map +0 -1
  300. package/dist/workflows/verdict.d.ts +0 -19
  301. package/dist/workflows/verdict.js +0 -30
  302. package/dist/workflows/verdict.js.map +0 -1
  303. package/dist/worktree/manager.d.ts +0 -43
  304. package/dist/worktree/manager.js +0 -127
  305. package/dist/worktree/manager.js.map +0 -1
  306. package/docker-compose.yml +0 -274
  307. package/sandbox.Dockerfile +0 -68
  308. package/skills/README.md +0 -42
  309. package/skills/browser-qa/SKILL.md +0 -208
  310. package/skills/browser-qa/scripts/agent-browser.mjs +0 -531
  311. package/skills/building/SKILL.md +0 -95
  312. package/skills/chat/SKILL.md +0 -36
  313. package/skills/code-review/SKILL.md +0 -82
  314. package/skills/debug-production/SKILL.md +0 -108
  315. package/skills/demo/SKILL.md +0 -228
  316. package/skills/demo/scripts/compose-demo.sh +0 -243
  317. package/skills/issue-answer/SKILL.md +0 -91
  318. package/skills/issue-comment/SKILL.md +0 -49
  319. package/skills/issue-triage/SKILL.md +0 -149
  320. package/skills/issue-triage/references/AGENT-BRIEF.md +0 -65
  321. package/skills/pr-comment/SKILL.md +0 -64
  322. package/skills/pr-review/SKILL.md +0 -148
  323. package/skills/pr-review/references/findings-schema.md +0 -89
  324. package/skills/qa-test/SKILL.md +0 -97
  325. package/skills/repo-health/SKILL.md +0 -67
  326. package/skills/security-feedback/SKILL.md +0 -173
  327. package/skills/security-feedback/references/templates.md +0 -77
  328. package/skills/security-review/SKILL.md +0 -215
  329. package/skills/security-review/references/issue-format.md +0 -302
  330. package/skills/verify/SKILL.md +0 -118
  331. package/workflows/answer.yaml +0 -44
  332. package/workflows/build.yaml +0 -129
  333. package/workflows/cron-health.yaml +0 -6
  334. package/workflows/cron-review.yaml +0 -8
  335. package/workflows/cron-security.yaml +0 -6
  336. package/workflows/cron-triage.yaml +0 -8
  337. package/workflows/demo.yaml +0 -62
  338. package/workflows/examples/bash-smoke.yaml +0 -41
  339. package/workflows/examples/parallel-review.yaml +0 -36
  340. package/workflows/examples/tdd-loop.yaml +0 -17
  341. package/workflows/explore.yaml +0 -80
  342. package/workflows/issue-comment.yaml +0 -15
  343. package/workflows/issue-triage.yaml +0 -22
  344. package/workflows/pr-comment.yaml +0 -16
  345. package/workflows/pr-fix.yaml +0 -17
  346. package/workflows/pr-review.yaml +0 -50
  347. package/workflows/prompts/answer.md +0 -68
  348. package/workflows/prompts/architect.md +0 -44
  349. package/workflows/prompts/classifier.md +0 -60
  350. package/workflows/prompts/classify-adds-info.md +0 -17
  351. package/workflows/prompts/demo.md +0 -125
  352. package/workflows/prompts/executor.md +0 -42
  353. package/workflows/prompts/explore-ask.md +0 -99
  354. package/workflows/prompts/explore-publish.md +0 -31
  355. package/workflows/prompts/explore-read.md +0 -120
  356. package/workflows/prompts/explore-synthesize.md +0 -88
  357. package/workflows/prompts/fix.md +0 -18
  358. package/workflows/prompts/guardrails.md +0 -96
  359. package/workflows/prompts/pr-fix.md +0 -26
  360. package/workflows/prompts/pr.md +0 -39
  361. package/workflows/prompts/qa-browser.md +0 -95
  362. package/workflows/prompts/qa-synth.md +0 -44
  363. package/workflows/prompts/qa-test.md +0 -60
  364. package/workflows/prompts/re-reviewer.md +0 -33
  365. package/workflows/prompts/reviewer.md +0 -60
  366. package/workflows/prompts/verify-browser.md +0 -93
  367. package/workflows/prompts/verify-synth.md +0 -44
  368. package/workflows/prompts/verify.md +0 -63
  369. package/workflows/qa-test.yaml +0 -94
  370. package/workflows/repo-health.yaml +0 -15
  371. package/workflows/security-feedback.yaml +0 -25
  372. package/workflows/security-review.yaml +0 -37
  373. package/workflows/verify.yaml +0 -93
  374. /package/dist/{cli/cli-config.d.ts → cli-config.d.ts} +0 -0
  375. /package/dist/{cli/cli-config.js → cli-config.js} +0 -0
  376. /package/dist/{cli/cli-format.d.ts → cli-format.d.ts} +0 -0
  377. /package/dist/{cli/cli-format.js → cli-format.js} +0 -0
  378. /package/dist/{cli/cli-timeline.d.ts → cli-timeline.d.ts} +0 -0
  379. /package/dist/{cli/cli-timeline.js → cli-timeline.js} +0 -0
  380. /package/dist/{cli/cli.d.ts → cli.d.ts} +0 -0
  381. /package/dist/{cli/oauth-cli.d.ts → oauth-cli.d.ts} +0 -0
  382. /package/dist/{cli/setup.d.ts → setup.d.ts} +0 -0
@@ -1,274 +0,0 @@
1
- services:
2
- agent:
3
- build: .
4
- # Pin the built image tag so it's independent of the compose project name
5
- # (which otherwise defaults to the checkout directory's basename). The
6
- # `egress-init` service references this exact tag, so without the pin the
7
- # stack only worked when the directory happened to be named `lastlight`.
8
- image: lastlight-agent
9
- restart: unless-stopped
10
- ports:
11
- - "127.0.0.1:8644:8644"
12
- dns:
13
- - 8.8.8.8
14
- - 1.1.1.1
15
- volumes:
16
- # Single private deployment folder: overlay config.yaml + asset overrides
17
- # plus a gitignored secrets/ subdir (.env, *.pem). LASTLIGHT_OVERLAY_DIR
18
- # points the harness at /app/instance; the entrypoint reads secrets from
19
- # /app/instance/secrets. This is the only deployment-specific input, read
20
- # at startup — no image rebuild. To APPLY an edit: `config.yaml` and
21
- # adding/changing an `.env` value apply on `docker compose restart agent`
22
- # (the entrypoint re-sources .env). REMOVING an `.env` value needs a
23
- # RECREATE — `docker compose up -d agent` (or `lastlight server start
24
- # agent`) — because compose injects env_file vars at container creation
25
- # and a restart can't unset them. Built-in assets (config/, workflows/,
26
- # skills/, agent-context/) stay baked into the image; they're lastlight
27
- # source and change via a normal rebuild.
28
- - ./instance:/app/instance:ro
29
- - agent-data:/app/data
30
- - /var/run/docker.sock:/var/run/docker.sock # spawn sandbox containers
31
- # Env comes from the consolidated overlay (instance/secrets/.env). The
32
- # entrypoint also sources it inside the container; this env_file is the
33
- # belt-and-suspenders copy. required: false so a fresh checkout (no
34
- # instance/ yet) doesn't error. No repo-root .env is needed.
35
- env_file:
36
- - path: ./instance/secrets/.env
37
- required: false
38
- environment:
39
- # V8's default old-space cap (~1.5 GB on 64-bit) is well below the 2g
40
- # cgroup limit, so Node would OOM before docker did. Raise it close to
41
- # the container limit so transient bursts have somewhere to land.
42
- - NODE_OPTIONS=--max-old-space-size=1800
43
- # Steady-state ~150 MB; bursts during webhook fan-out, the in-process
44
- # chat skill (full Agent SDK), and the dispatch screening call. 2 GB
45
- # gives headroom for those bursts while still preventing a runaway leak
46
- # from taking the host down.
47
- mem_limit: 2g
48
- memswap_limit: 2g
49
- networks:
50
- - internal
51
-
52
- # Shared heavy base for the sandbox images (fnm + Node + semgrep/gitleaks + uv
53
- # + the agent user). Both sandbox and sandbox-qa build FROM it, so it must be
54
- # built FIRST:
55
- # docker compose --profile build-only build sandbox-base sandbox sandbox-qa
56
- # Split out so the frequently-changing agentic-pi/agent-context tail (and, in
57
- # sandbox-qa, ~300 MB of Chromium) stays cached across ordinary releases.
58
- sandbox-base:
59
- build:
60
- context: .
61
- dockerfile: sandbox-base.Dockerfile
62
- image: lastlight-sandbox-base:latest
63
- profiles:
64
- - build-only # never started as a service, just built
65
-
66
- # Build the sandbox image (used by the harness to spawn per-task containers).
67
- # FROM lastlight-sandbox-base:latest — build sandbox-base first.
68
- sandbox:
69
- build:
70
- context: .
71
- dockerfile: sandbox.Dockerfile
72
- image: lastlight-sandbox:latest
73
- profiles:
74
- - build-only # never started as a service, just built
75
-
76
- # Build the enriched browser-QA sandbox image (Tier B). Separate + heavier
77
- # (Playwright + a baked Chromium), built only when QA is enabled. Its FROM
78
- # depends on lastlight-sandbox-base:latest existing first, so build in that order:
79
- # docker compose --profile build-only build sandbox-base sandbox-qa
80
- sandbox-qa:
81
- build:
82
- context: .
83
- dockerfile: sandbox-qa.Dockerfile
84
- image: lastlight-sandbox-qa:latest
85
- profiles:
86
- - build-only # never started as a service, just built
87
-
88
- # ── Egress firewall (replaces the earlier tinyproxy CONNECT-proxy) ────
89
- #
90
- # See src/sandbox/egress-firewall-config.ts for the architecture rationale.
91
- # Two pairs: (coredns-strict + nginx-egress-strict) and
92
- # (coredns-open + nginx-egress-open). Sandbox containers spawn with
93
- # `--dns <coredns-ip>` and dial allowlisted hosts directly — coredns
94
- # resolves the hostname to the nginx IP, nginx peeks SNI and tunnels.
95
- #
96
- # The sandbox sees no proxy at all. Works for any SDK regardless of
97
- # whether it honours HTTP(S)_PROXY env vars.
98
-
99
- # One-shot init that writes nginx-strict.conf / nginx-open.conf /
100
- # Corefile.strict / Corefile.open into the agent-data volume before
101
- # coredns + nginx try to read them. Without this, the firewall
102
- # services boot first, fail to find the configs, and run with empty
103
- # state (returning NXDOMAIN for everything) — even after the harness
104
- # later writes the files, the running processes never re-read them.
105
- # `condition: service_completed_successfully` on the firewalls below
106
- # makes them wait for this to exit 0.
107
- egress-init:
108
- image: lastlight-agent
109
- restart: "no"
110
- # Mounts the same overlay + secrets the agent does and runs the SAME
111
- # config loader, so the collector config it pre-writes is computed from
112
- # the effective config (env AND instance/config.yaml) — identical to what
113
- # the harness writes at boot. This is what keeps the running collector's
114
- # forwarding gate from diverging from the harness when OTEL is enabled via
115
- # YAML rather than env.
116
- env_file:
117
- - path: ./instance/secrets/.env
118
- required: false
119
- volumes:
120
- - ./instance:/app/instance:ro
121
- - agent-data:/app/data
122
- command:
123
- ["node", "-e",
124
- "const c=require('/app/dist/config/config.js').loadConfig();const m=require('/app/dist/sandbox/egress-firewall-config.js');const d=c.stateDir;m.writeEgressFirewallConfigs(d);m.writeOtelCollectorConfig(d,{active:c.otel.enabled&&c.otel.forwardToSandbox})"]
125
- depends_on:
126
- - agent # ensures the image has been built when init runs
127
-
128
- coredns-strict:
129
- image: coredns/coredns:1.11.3
130
- restart: unless-stopped
131
- command: ["-conf", "/app/data/proxy/Corefile.strict"]
132
- volumes:
133
- - agent-data:/app/data:ro
134
- depends_on:
135
- egress-init:
136
- condition: service_completed_successfully
137
- networks:
138
- sandbox-egress:
139
- ipv4_address: 172.30.0.10
140
-
141
- coredns-open:
142
- image: coredns/coredns:1.11.3
143
- restart: unless-stopped
144
- command: ["-conf", "/app/data/proxy/Corefile.open"]
145
- volumes:
146
- - agent-data:/app/data:ro
147
- depends_on:
148
- egress-init:
149
- condition: service_completed_successfully
150
- networks:
151
- sandbox-egress:
152
- ipv4_address: 172.30.0.11
153
-
154
- nginx-egress-strict:
155
- image: nginx:alpine
156
- restart: unless-stopped
157
- command:
158
- ["nginx", "-c", "/app/data/proxy/nginx-strict.conf", "-g", "daemon off;"]
159
- volumes:
160
- - agent-data:/app/data:ro
161
- depends_on:
162
- egress-init:
163
- condition: service_completed_successfully
164
- # Deliberately NOT on `internal`. Splitting ingress (sandbox-egress)
165
- # and egress (proxy-egress) onto separate networks that don't include
166
- # harness services keeps compose-internal hostnames unresolvable to
167
- # the firewall — so an `unrestricted_egress` phase can't ask nginx to
168
- # tunnel to `agent:8644` or similar.
169
- networks:
170
- sandbox-egress:
171
- ipv4_address: 172.30.0.20
172
- proxy-egress: {}
173
-
174
- nginx-egress-open:
175
- image: nginx:alpine
176
- restart: unless-stopped
177
- command:
178
- ["nginx", "-c", "/app/data/proxy/nginx-open.conf", "-g", "daemon off;"]
179
- volumes:
180
- - agent-data:/app/data:ro
181
- depends_on:
182
- egress-init:
183
- condition: service_completed_successfully
184
- networks:
185
- sandbox-egress:
186
- ipv4_address: 172.30.0.21
187
- proxy-egress: {}
188
-
189
- # In-network OTEL collector. Sandboxes export telemetry here (by IP, on
190
- # sandbox-egress) and it re-exports to the configured backend over
191
- # proxy-egress. This is why the strict SNI firewall doesn't need to know
192
- # about collector hosts or non-443 ports: the sandbox only ever dials this
193
- # fixed internal endpoint, and the backend hop runs from a trusted,
194
- # harness-controlled service that can use any port/scheme/headers. Backend
195
- # auth headers live in the generated config (mode 0600 on the host volume)
196
- # and are never forwarded into a sandbox.
197
- #
198
- # Dual-homed like the nginx firewalls — sandbox-egress (ingress) +
199
- # proxy-egress (outbound). Deliberately NOT on `internal`, so it can't
200
- # bridge a sandbox into harness services.
201
- otel-collector:
202
- image: otel/opentelemetry-collector:0.115.0
203
- restart: unless-stopped
204
- # Runs as the image's default non-root UID (10001). The harness `lastlight`
205
- # user is pinned to that same UID (Dockerfile `useradd -u 10001`), so the
206
- # collector reads the mode-0600 config the harness writes as its owner —
207
- # no root, no world-readable secret. The image tag and that pinned UID must
208
- # move together if a future image changes its user.
209
- command: ["--config=/app/data/proxy/otel-collector.yaml"]
210
- volumes:
211
- - agent-data:/app/data:ro
212
- depends_on:
213
- egress-init:
214
- condition: service_completed_successfully
215
- networks:
216
- sandbox-egress:
217
- ipv4_address: 172.30.0.30
218
- proxy-egress: {}
219
-
220
- caddy:
221
- image: caddy:2-alpine
222
- restart: unless-stopped
223
- ports:
224
- - "80:80"
225
- - "443:443"
226
- volumes:
227
- - ./Caddyfile:/etc/caddy/Caddyfile:ro
228
- - caddy-data:/data
229
- - caddy-config:/config
230
- # Caddyfile uses `{$DOMAIN:localhost}` (Caddy's own env substitution), so
231
- # DOMAIN just needs to be in the container env. Read it from the overlay
232
- # secrets file directly — no repo-root .env / ${DOMAIN} interpolation.
233
- env_file:
234
- - path: ./instance/secrets/.env
235
- required: false
236
- # Caddy's memory footprint is tiny (typically <50 MB even under load).
237
- mem_limit: 256m
238
- memswap_limit: 256m
239
- networks:
240
- - internal
241
-
242
- volumes:
243
- agent-data: # contains: lastlight.db, agent-sessions/, sandboxes/, secrets/, logs/, proxy/
244
- # Shared package-manager download cache (npm/pnpm/yarn), mounted at /cache in
245
- # every dynamically-created sandbox container by src/sandbox/docker.ts so
246
- # repeated installs reuse already-fetched tarballs (issue #107). Declared
247
- # here so the stack manages its lifecycle; the agent service does not mount
248
- # it. Override the name with LASTLIGHT_PKG_CACHE_VOLUME (defaults to the
249
- # compose-prefixed `lastlight_pkg-cache`).
250
- pkg-cache:
251
- caddy-data:
252
- caddy-config:
253
-
254
- networks:
255
- internal:
256
- # Sandbox containers (created dynamically by src/sandbox/docker.ts) attach
257
- # to this network only with `--dns 172.30.0.10` (or .11 for open mode).
258
- # `internal: true` disables direct host egress, so the only path to the
259
- # public internet is through the nginx firewalls, which receive sandbox
260
- # traffic on this network. Static subnet so the coredns + nginx IPs
261
- # baked into the generated configs are stable.
262
- sandbox-egress:
263
- internal: true
264
- ipam:
265
- config:
266
- - subnet: 172.30.0.0/24
267
- # Egress-only network for the nginx firewalls. Regular bridge (has a
268
- # default route to the host's network, so nginx can reach the public
269
- # internet). Critically: NO other compose services attach here. That way
270
- # docker's embedded DNS on this network resolves only public names — the
271
- # harness service names (`agent`, `caddy`) are not reachable from the
272
- # firewall's perspective, so an unrestricted sandbox can't ask nginx to
273
- # bridge into the compose-internal services.
274
- proxy-egress:
@@ -1,68 +0,0 @@
1
- # Sandbox image for Last Light agent tasks (LASTLIGHT_SANDBOX=docker
2
- # fallback only — the default production sandbox is gondolin, invoked
3
- # in-process by agentic-pi without docker).
4
- #
5
- # The heavy, stable toolchain (Debian deps, fnm + Node versions, semgrep/
6
- # gitleaks, uv, the `agent` user) lives in the shared sandbox-base image so it
7
- # stays cached across releases. This file adds only the THIN, frequently-changing
8
- # tail: the pinned agentic-pi install + the baked agent-context + the entrypoint.
9
- # Keep it that way — the same tail is duplicated in sandbox-qa.Dockerfile so
10
- # Chromium stays a cached child of the base rather than of this churn.
11
- #
12
- # Build order matters (sandbox-base must exist first):
13
- # docker compose --profile build-only build sandbox-base sandbox
14
- #
15
- # BASE_IMAGE defaults to the local tag (host `--local` builds + docker compose
16
- # resolve it from the local image store). CI overrides it to the just-pushed
17
- # GHCR ref (`--build-arg BASE_IMAGE=ghcr.io/nearform/lastlight-sandbox-base:<tag>`)
18
- # because the buildx docker-container driver resolves FROM from a registry, not
19
- # the local store. See .github/workflows/docker-publish.yml.
20
- ARG BASE_IMAGE=lastlight-sandbox-base:latest
21
- FROM ${BASE_IMAGE}
22
-
23
- # Install agentic-pi globally so the harness can `docker exec ...
24
- # agentic-pi run ...` against this container. The version + integrity come from
25
- # `sandbox/agentic-pi.pin` (regenerated from lastlight's package-lock.json by
26
- # scripts/agentic-pi-pin.sh, drift-guarded by tests/agentic-pi-pin.test.ts). We
27
- # COPY that tiny two-line file rather than the whole package-lock.json on
28
- # purpose: the lockfile's hash changes on every release and would rebust this
29
- # layer (and sandbox-qa's Chromium) on every version bump; the pin changes only
30
- # when agentic-pi actually does. `npm install -g <tarball>` doesn't consult a
31
- # lockfile, hence the explicit integrity verification.
32
- COPY sandbox/agentic-pi.pin /tmp/agentic-pi.pin
33
- RUN version=$(sed -n '1p' /tmp/agentic-pi.pin) \
34
- && expected=$(sed -n '2p' /tmp/agentic-pi.pin) \
35
- && echo "Installing agentic-pi@${version} (${expected})" \
36
- && curl -fsSL "https://registry.npmjs.org/agentic-pi/-/agentic-pi-${version}.tgz" -o /tmp/agentic-pi.tgz \
37
- && actual="sha512-$(node -e "const c=require('crypto'),f=require('fs');process.stdout.write(c.createHash('sha512').update(f.readFileSync('/tmp/agentic-pi.tgz')).digest('base64'))")" \
38
- && if [ "$actual" != "$expected" ]; then \
39
- echo "agentic-pi tarball integrity mismatch:" >&2; \
40
- echo " expected: $expected" >&2; \
41
- echo " actual: $actual" >&2; \
42
- exit 1; \
43
- fi \
44
- && npm install -g --no-audit --no-fund /tmp/agentic-pi.tgz \
45
- && rm /tmp/agentic-pi.tgz /tmp/agentic-pi.pin
46
-
47
- # Agent context (baked at /app/ — entrypoint cats into workspace/AGENTS.md)
48
- COPY agent-context/ /app/agent-context/
49
-
50
- # Entrypoint
51
- COPY deploy/sandbox-entrypoint.sh /app/sandbox-entrypoint.sh
52
- RUN chmod +x /app/sandbox-entrypoint.sh
53
-
54
- # Own app dir for agent user
55
- RUN chown -R agent:agent /app /home/agent
56
-
57
- WORKDIR /home/agent/workspace
58
-
59
- # Image-level env so every `docker exec` (the entrypoint just runs once at
60
- # container start) sees these. Exporting them in sandbox-entrypoint.sh only
61
- # affects PID 1 — subsequent `docker exec agentic-pi run …` calls get a
62
- # fresh environment and would otherwise miss these paths.
63
- ENV LASTLIGHT_WORKSPACE=/home/agent/workspace
64
- ENV LASTLIGHT_GIT_CREDENTIALS=/home/agent/.lastlight-git-credentials
65
-
66
- # Entrypoint runs as root, fixes permissions, then drops to agent via gosu
67
- ENTRYPOINT ["/app/sandbox-entrypoint.sh"]
68
- CMD ["sleep", "infinity"]
package/skills/README.md DELETED
@@ -1,42 +0,0 @@
1
- # Skills
2
-
3
- Skill directories staged into the sandbox per workflow phase (and a subset into
4
- chat). A phase declares `skill: <name>` / `skills: [<name>, …]`; the runner
5
- stages the whole directory into `<agentCwd>/.agents/skills/<name>/` and
6
- pi-coding-agent surfaces `name` + `description` in the system prompt. The agent
7
- reads SKILL.md (and any `references/`) on demand via its `read` tool.
8
-
9
- **Authoring guide:** [`docs/agents/writing-skills.md`](../docs/agents/writing-skills.md)
10
- — the loading contract, the craft, and house conventions. Read it before adding
11
- or editing a skill.
12
-
13
- ## Skills
14
-
15
- | Skill | Used by | Purpose |
16
- |-------|---------|---------|
17
- | [`chat`](chat/) | chat | Conversational assistant for Slack/Discord threads — answer repo/PR/issue questions, guide to natural-language triggers. |
18
- | [`issue-triage`](issue-triage/) | `issue-triage.yaml`, chat | Move issues through the canonical triage state machine — categorise, label, dedupe, manage stale, write a problem-statement summary (not a solution design), re-triage on reporter replies. |
19
- | [`issue-comment`](issue-comment/) | `issue-comment.yaml` | Handle non-build maintainer comments on issues/PRs — close, label, answer briefly, redirect build requests. |
20
- | [`pr-review`](pr-review/) | `pr-review.yaml`, chat | Review a PR with precision-first, tiered feedback and post a formal review. A pure code review — no building (CI validates the change). |
21
- | [`pr-comment`](pr-comment/) | `pr-comment.yaml` | Answer a maintainer's question about an open PR with concrete, code-cited evidence (not a full review). |
22
- | [`repo-health`](repo-health/) | `repo-health.yaml`, chat | Generate a repository health report — open issues, PR backlog, CI status, action items. |
23
- | [`security-review`](security-review/) | `security-review.yaml` | Diff-scoped SDLC security review; file one dated summary issue with a task-list of findings. |
24
- | [`security-feedback`](security-feedback/) | `security-feedback.yaml` | Process a maintainer's comment on a security scan summary — break findings into issues or record suppressions. |
25
- | [`building`](building/) | build (executor, reviewer), `pr-fix.yaml` | Shared craft: install-first + package-manager detection, the test/lint/typecheck gate, and TDD discipline in the sandbox. |
26
- | [`code-review`](code-review/) | build (reviewer), `pr-review.yaml` | Shared rubric: finding tiers (Critical/Important/Suggestions/Nits) and what-to-check. Referenced by both the branch-diff reviewer and the PR reviewer. |
27
- | [`verify`](verify/) | `verify.yaml` | Test a behaviour claim as an investigator — CONFIRMED / REFUTED / INCONCLUSIVE with bash-captured evidence. |
28
- | [`qa-test`](qa-test/) | `qa-test.yaml` | Run an automated QA flow against a CLI or locally-served app and report step-level pass/fail with evidence. |
29
- | [`browser-qa`](browser-qa/) | `verify.yaml`, `qa-test.yaml`, `demo.yaml` | Drive a real headless browser against a locally-served web UI and capture screenshot (and, for `demo`, video) evidence. Gated to the docker QA image. |
30
- | [`demo`](demo/) | `demo.yaml` | Record a short demo VIDEO of a PR/feature — drive the web UI, screen-record, and composite a titled, size-capped mp4 with ffmpeg. Gated to the docker QA image. |
31
-
32
- `building` and `code-review` are **shared building blocks** so the install
33
- gate and the review rubric each live in one place. `building` is staged by the
34
- build cycle (executor + reviewer) and `pr-fix`; `code-review` by the build
35
- reviewer and `pr-review` (which stages `code-review` but **not** `building` — a
36
- pure code review). They're staged alongside the phase's primary skill/prompt and
37
- read on demand.
38
-
39
- The live set is enforced at startup: `validateAssets()` (`src/workflows/loader.ts`)
40
- resolves every workflow `skill:` reference and `CHAT_SKILL_NAMES`
41
- (`src/engine/chat-skills.ts`). Adding a skill here does nothing until a phase or
42
- the chat list references it.
@@ -1,208 +0,0 @@
1
- ---
2
- name: browser-qa
3
- description: Drive a real headless browser against a locally-served web UI and capture screenshot evidence, reporting step-level PASS/FAIL/BLOCKED with extracted DOM text and console errors. Use when a phase on the docker QA image must exercise a rendered UI and attach screenshots — it complements the text-evidence verify/qa-test skills.
4
- version: 1.0.0
5
- tags: [browser, qa, screenshots, evidence]
6
- ---
7
-
8
- # Browser QA
9
-
10
- Drive a web UI with a real headless Chromium and report **step-level
11
- pass/fail with evidence**: extracted DOM text, assertion results, console
12
- errors, and screenshot files. The deliverable is a QA report — partial
13
- coverage with documented failures is the expected output.
14
-
15
- This skill is the **browser/screenshot path** that `verify` and `qa-test`
16
- scope out to "a separate docker-gated capability". It only works on the
17
- `lastlight-sandbox-qa:latest` docker image, which bakes in Playwright +
18
- Chromium. You have bash + file tools but **no vision** — you reason over the
19
- driver's JSON stdout (extracted text, assertion results, console errors).
20
- The PNG screenshots are **human evidence only**; you never see them.
21
-
22
- This skill uses the **building** skill for installing dependencies and
23
- starting the repo's dev-server.
24
-
25
- ## When this applies
26
-
27
- A phase running on the docker QA image whose target is a **web app** the
28
- repo serves on `localhost` and where rendered-UI behaviour or visual
29
- evidence matters. For a CLI or a curl-able API, use `qa-test`/`verify`
30
- instead — don't launch a browser you don't need.
31
-
32
- ## FIRST — probe for a browser
33
-
34
- The driver lives in this skill's bundle at
35
- `<skill-dir>/scripts/agent-browser.mjs`. Resolve `<skill-dir>` from the
36
- available-skills catalogue (it gives the absolute staged path of this
37
- skill); do not assume a relative path from `$PWD` — the agent's cwd is the
38
- checked-out repo, the bundle is a sibling.
39
-
40
- Run the runtime probe before anything else:
41
-
42
- ```bash
43
- node <skill-dir>/scripts/agent-browser.mjs doctor
44
- ```
45
-
46
- - Exit 0 with `{"ok":true,"chromium":"..."}` → Chromium is available; proceed.
47
- - Exit non-zero (e.g. `playwright not available …`, or no Chromium — the lean
48
- default image, or a gondolin run) → **DO NOT attempt browser QA.** Fall back
49
- to the text path (`verify`/`qa-test` style: curl the dev-server, capture
50
- stdout) and **say in your report that the browser path was unavailable** and
51
- you used the text path.
52
-
53
- ## Start the app
54
-
55
- Follow the **building** skill to install dependencies, then start the repo's
56
- dev-server **in the background** and wait until it is listening on
57
- `localhost:PORT` (poll with `curl` until it answers). Note the port — you
58
- pass it as `--base-url`.
59
-
60
- ## Author a flow
61
-
62
- Write a `flow.json` describing the QA flow. Shape:
63
-
64
- ```json
65
- {
66
- "baseUrl": "http://localhost:3000",
67
- "viewport": { "width": 1280, "height": 800 },
68
- "steps": [
69
- { "name": "home", "goto": "/" },
70
- { "click": "text=Login" },
71
- { "fill": ["#email", "a@b.com"] },
72
- { "type": ["#password", "hunter2"] },
73
- { "press": "Enter" },
74
- { "waitFor": "#dashboard" },
75
- { "assertText": "Welcome" },
76
- { "text": "h1" },
77
- { "screenshot": "after-login" }
78
- ]
79
- }
80
- ```
81
-
82
- Step keys (a step may combine an action **plus** a trailing `screenshot`):
83
-
84
- | Key | Value | Effect |
85
- |--------------|----------------------|------------------------------------------------------------------|
86
- | `goto` | path or URL | Navigate (resolved against `baseUrl`). **Failure is fatal.** |
87
- | `click` | selector | Click the element. |
88
- | `fill` | `[selector, value]` | Set an input's value. |
89
- | `type` | `[selector, value]` | Type key-by-key into an input. |
90
- | `press` | key (e.g. `Enter`) | Press a keyboard key. |
91
- | `waitFor` | selector | Wait until the element is visible (~10s timeout). |
92
- | `assertText` | string | Pass if the text is visible anywhere on the page; else step FAIL.|
93
- | `text` | selector | Extract `textContent` into the step result (for you to read). |
94
- | `pause` | milliseconds | Hold for N ms — a readable beat after a state change (demo videos).|
95
- | `screenshot` | basename | Write `<out-dir>/<basename>.png` (full page). |
96
-
97
- The whole flow runs in **one Chromium session** — login state, cookies, and
98
- navigation persist across steps.
99
-
100
- ### Demo mode (recording only)
101
-
102
- When the session is **recorded** (`--record-dir`, `"record": true`, or an
103
- explicit `--demo`), the driver switches on "demo mode" so the capture is
104
- watchable — headless Chromium otherwise paints no cursor and fires actions
105
- instantly. Demo mode adds a **synthetic cursor overlay** that animates to each
106
- target, types `type` steps **character-by-character**, and holds a beat
107
- **between steps**. For screenshot QA (no recording) these are all no-ops, so
108
- QA runs stay fast. Prefer `type` over `fill` in a recorded flow so the typing is
109
- visible, and use `pause` for deliberate holds. Tunables:
110
- `--step-delay MS` (default 700), `--type-delay MS` (default 70),
111
- `--move-steps N` (default 25), `--no-cursor` to drop the overlay (env:
112
- `LASTLIGHT_STEP_DELAY_MS`, `LASTLIGHT_TYPE_DELAY_MS`, `LASTLIGHT_MOVE_STEPS`).
113
-
114
- ## Run the flow
115
-
116
- ```bash
117
- node <skill-dir>/scripts/agent-browser.mjs run flow.json \
118
- --base-url http://localhost:PORT \
119
- --out-dir <artifact-dir>
120
- ```
121
-
122
- The driver prints a single JSON report:
123
-
124
- ```json
125
- { "ok": false,
126
- "baseUrl": "http://localhost:3000",
127
- "steps": [
128
- { "index": 0, "action": "goto", "ok": true, "ms": 412 },
129
- { "index": 6, "action": "assertText", "ok": false, "ms": 10003,
130
- "error": "assertText not found: \"Welcome\"" },
131
- { "index": 7, "action": "text", "ok": true, "ms": 5, "text": "Dashboard" },
132
- { "index": 8, "action": "screenshot", "ok": true, "ms": 88,
133
- "screenshot": "/…/after-login.png" }
134
- ],
135
- "consoleErrors": ["Uncaught TypeError: …"],
136
- "screenshots": ["/…/after-login.png"] }
137
- ```
138
-
139
- - A step error (selector not found, assertion miss, timeout) is recorded
140
- `ok:false` and the run **continues** — best-effort coverage. The exception
141
- is a `goto` that fails: that is fatal and the remaining steps are skipped.
142
- - `ok` at the top level is true only when **every** step passed. The process
143
- exits 0 even when steps failed — judge from the JSON, not the exit code.
144
- A non-zero exit means a fatal harness error (bad flow file, launch failure).
145
-
146
- ## Screenshots & artifacts
147
-
148
- Pass `--out-dir <artifact-dir>` pointing at the run's artifact directory the
149
- **prompt provides** (commonly the run's `.lastlight/<issueKey>/` dir,
150
- referenced as the template variable the prompt passes in). Saving there lets
151
- the harness **harvest** the PNGs into the dashboard Artifacts view. Reference
152
- each screenshot path in your report.
153
-
154
- Screenshot delivery requires **server-mode build-assets**. If the PNGs can't
155
- be persisted (no artifact dir was provided, or build-assets is repo-mode),
156
- still report the text/DOM observations and console errors, and say the
157
- screenshots were **not retained**.
158
-
159
- ## Evidence rules
160
-
161
- Mirror `verify`/`qa-test` — you are an investigator, not an advocate:
162
-
163
- - **Never fake a pass.** Don't hardcode expected text or edit the code under
164
- test to force a green step.
165
- - **Report real failures.** A step that genuinely fails is a finding — record
166
- it `FAIL` with the assertion/selector and the driver's error, and don't bury
167
- it.
168
- - **Treat `consoleErrors` and page errors as findings**, even if the visible
169
- flow "worked" — surface them in the report.
170
- - If the browser is unavailable (probe failed), fall back to text and say so —
171
- don't pretend a UI step ran.
172
-
173
- ## Report
174
-
175
- Produce your report as your **final message** — the workflow posts it for you
176
- (don't `github_add_issue_comment` yourself; that would double-post).
177
-
178
- **Be concise, and stay in the browser.** The deliverable is short: a one-line
179
- environment summary, the results table, the screenshots, console errors, and a
180
- one-line coverage note. Do **not** read or analyse the repo's source code (the
181
- text pass already did) and do **not** narrate every step or dump extracted DOM
182
- text — surface a `text`/assertion value only when it's the actual evidence for a
183
- row (e.g. a FAIL).
184
-
185
- ```
186
- ## Browser QA: <target>
187
-
188
- **Environment:** <branch / commit, package manager, how served, Chromium version>
189
-
190
- ### Results
191
- | Step | Status | Evidence |
192
- |------|--------|----------|
193
- | <step description> | PASS / FAIL / BLOCKED | <terse evidence + inline screenshot if any> |
194
-
195
- ### Console errors
196
- - <each consoleErrors / pageerror entry, or "none observed">
197
-
198
- ### Coverage
199
- <one or two lines: what was driven, and anything not tested and why>
200
- ```
201
-
202
- Reference screenshots **inline** as raw Markdown images when the prompt gives
203
- you a public artifact base URL — write `![caption](<base>/<name>.png)` directly
204
- in the report body (or table cell), **never** wrapped in backticks or a code
205
- fence (inside a code span it renders as literal text, not an image). Otherwise
206
- reference them by filename. Every step in the flow must
207
- have a row and a result. Never report a flow as "passed" with steps you didn't
208
- actually run — list them as untested instead.