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.
- package/README.md +71 -692
- package/dist/cli-config.js.map +1 -0
- package/dist/cli-format.js.map +1 -0
- package/dist/{cli/cli-server.d.ts → cli-server.d.ts} +26 -1
- package/dist/{cli/cli-server.js → cli-server.js} +40 -5
- package/dist/cli-server.js.map +1 -0
- package/dist/cli-timeline.js.map +1 -0
- package/dist/{cli/cli.js → cli.js} +4 -3
- package/dist/cli.js.map +1 -0
- package/dist/{cli/fork-cli.d.ts → fork-cli.d.ts} +3 -3
- package/dist/{cli/fork-cli.js → fork-cli.js} +28 -27
- package/dist/fork-cli.js.map +1 -0
- package/dist/{cli/oauth-cli.js → oauth-cli.js} +1 -1
- package/dist/oauth-cli.js.map +1 -0
- package/dist/{cli/setup.js → setup.js} +10 -4
- package/dist/setup.js.map +1 -0
- package/dist/{cli/skills-install.d.ts → skills-install.d.ts} +6 -0
- package/dist/{cli/skills-install.js → skills-install.js} +6 -2
- package/dist/skills-install.js.map +1 -0
- package/package.json +20 -65
- package/plugins/lastlight/.claude-plugin/plugin.json +1 -1
- package/agent-context/rules.md +0 -80
- package/agent-context/security.md +0 -69
- package/agent-context/soul.md +0 -21
- package/config/default.yaml +0 -119
- package/deploy/.env.production.example +0 -100
- package/deploy/entrypoint.sh +0 -82
- package/deploy/native/README.md +0 -160
- package/deploy/native/install.sh +0 -110
- package/deploy/native/lastlight.env.example +0 -95
- package/deploy/native/lastlight.service +0 -50
- package/deploy/sandbox-entrypoint.sh +0 -93
- package/dist/admin/auth.d.ts +0 -51
- package/dist/admin/auth.js +0 -121
- package/dist/admin/auth.js.map +0 -1
- package/dist/admin/chat-session-reader.d.ts +0 -39
- package/dist/admin/chat-session-reader.js +0 -84
- package/dist/admin/chat-session-reader.js.map +0 -1
- package/dist/admin/docker.d.ts +0 -61
- package/dist/admin/docker.js +0 -187
- package/dist/admin/docker.js.map +0 -1
- package/dist/admin/index.d.ts +0 -9
- package/dist/admin/index.js +0 -59
- package/dist/admin/index.js.map +0 -1
- package/dist/admin/routes.d.ts +0 -81
- package/dist/admin/routes.js +0 -1381
- package/dist/admin/routes.js.map +0 -1
- package/dist/admin/sessions.d.ts +0 -89
- package/dist/admin/sessions.js +0 -204
- package/dist/admin/sessions.js.map +0 -1
- package/dist/admin/tail.d.ts +0 -12
- package/dist/admin/tail.js +0 -145
- package/dist/admin/tail.js.map +0 -1
- package/dist/admin/version.d.ts +0 -22
- package/dist/admin/version.js +0 -92
- package/dist/admin/version.js.map +0 -1
- package/dist/cli/cli-config.js.map +0 -1
- package/dist/cli/cli-format.js.map +0 -1
- package/dist/cli/cli-server.js.map +0 -1
- package/dist/cli/cli-timeline.js.map +0 -1
- package/dist/cli/cli.js.map +0 -1
- package/dist/cli/fork-cli.js.map +0 -1
- package/dist/cli/oauth-cli.js.map +0 -1
- package/dist/cli/setup.js.map +0 -1
- package/dist/cli/skills-install.js.map +0 -1
- package/dist/config/config-resolve.d.ts +0 -28
- package/dist/config/config-resolve.js +0 -47
- package/dist/config/config-resolve.js.map +0 -1
- package/dist/config/config.d.ts +0 -157
- package/dist/config/config.js +0 -581
- package/dist/config/config.js.map +0 -1
- package/dist/config/core-pin.d.ts +0 -16
- package/dist/config/core-pin.js +0 -85
- package/dist/config/core-pin.js.map +0 -1
- package/dist/config/overlay-assets.d.ts +0 -20
- package/dist/config/overlay-assets.js +0 -114
- package/dist/config/overlay-assets.js.map +0 -1
- package/dist/config/overlay-bootstrap.d.ts +0 -44
- package/dist/config/overlay-bootstrap.js +0 -304
- package/dist/config/overlay-bootstrap.js.map +0 -1
- package/dist/connectors/github-webhook.d.ts +0 -45
- package/dist/connectors/github-webhook.js +0 -321
- package/dist/connectors/github-webhook.js.map +0 -1
- package/dist/connectors/index.d.ts +0 -18
- package/dist/connectors/index.js +0 -41
- package/dist/connectors/index.js.map +0 -1
- package/dist/connectors/messaging/base.d.ts +0 -42
- package/dist/connectors/messaging/base.js +0 -139
- package/dist/connectors/messaging/base.js.map +0 -1
- package/dist/connectors/messaging/delivery.d.ts +0 -14
- package/dist/connectors/messaging/delivery.js +0 -38
- package/dist/connectors/messaging/delivery.js.map +0 -1
- package/dist/connectors/messaging/index.d.ts +0 -4
- package/dist/connectors/messaging/index.js +0 -4
- package/dist/connectors/messaging/index.js.map +0 -1
- package/dist/connectors/messaging/session-manager.d.ts +0 -53
- package/dist/connectors/messaging/session-manager.js +0 -255
- package/dist/connectors/messaging/session-manager.js.map +0 -1
- package/dist/connectors/messaging/types.d.ts +0 -58
- package/dist/connectors/messaging/types.js +0 -7
- package/dist/connectors/messaging/types.js.map +0 -1
- package/dist/connectors/slack/connector.d.ts +0 -100
- package/dist/connectors/slack/connector.js +0 -341
- package/dist/connectors/slack/connector.js.map +0 -1
- package/dist/connectors/slack/index.d.ts +0 -2
- package/dist/connectors/slack/index.js +0 -3
- package/dist/connectors/slack/index.js.map +0 -1
- package/dist/connectors/slack/mrkdwn.d.ts +0 -8
- package/dist/connectors/slack/mrkdwn.js +0 -123
- package/dist/connectors/slack/mrkdwn.js.map +0 -1
- package/dist/connectors/types.d.ts +0 -61
- package/dist/connectors/types.js +0 -2
- package/dist/connectors/types.js.map +0 -1
- package/dist/cron/fanout.d.ts +0 -33
- package/dist/cron/fanout.js +0 -55
- package/dist/cron/fanout.js.map +0 -1
- package/dist/cron/jobs.d.ts +0 -18
- package/dist/cron/jobs.js +0 -37
- package/dist/cron/jobs.js.map +0 -1
- package/dist/cron/scheduler.d.ts +0 -45
- package/dist/cron/scheduler.js +0 -105
- package/dist/cron/scheduler.js.map +0 -1
- package/dist/engine/agent-executor.d.ts +0 -34
- package/dist/engine/agent-executor.js +0 -246
- package/dist/engine/agent-executor.js.map +0 -1
- package/dist/engine/chat/chat-runner.d.ts +0 -132
- package/dist/engine/chat/chat-runner.js +0 -424
- package/dist/engine/chat/chat-runner.js.map +0 -1
- package/dist/engine/chat/chat-skills.d.ts +0 -45
- package/dist/engine/chat/chat-skills.js +0 -184
- package/dist/engine/chat/chat-skills.js.map +0 -1
- package/dist/engine/chat/chat.d.ts +0 -54
- package/dist/engine/chat/chat.js +0 -316
- package/dist/engine/chat/chat.js.map +0 -1
- package/dist/engine/chat/message-batcher.d.ts +0 -32
- package/dist/engine/chat/message-batcher.js +0 -109
- package/dist/engine/chat/message-batcher.js.map +0 -1
- package/dist/engine/dispatcher.d.ts +0 -73
- package/dist/engine/dispatcher.js +0 -663
- package/dist/engine/dispatcher.js.map +0 -1
- package/dist/engine/event-shim.d.ts +0 -117
- package/dist/engine/event-shim.js +0 -435
- package/dist/engine/event-shim.js.map +0 -1
- package/dist/engine/executors/orchestrator.d.ts +0 -81
- package/dist/engine/executors/orchestrator.js +0 -365
- package/dist/engine/executors/orchestrator.js.map +0 -1
- package/dist/engine/executors/shared.d.ts +0 -189
- package/dist/engine/executors/shared.js +0 -616
- package/dist/engine/executors/shared.js.map +0 -1
- package/dist/engine/github/git-auth.d.ts +0 -62
- package/dist/engine/github/git-auth.js +0 -161
- package/dist/engine/github/git-auth.js.map +0 -1
- package/dist/engine/github/github-app-client.d.ts +0 -23
- package/dist/engine/github/github-app-client.js +0 -33
- package/dist/engine/github/github-app-client.js.map +0 -1
- package/dist/engine/github/github-tools.d.ts +0 -18
- package/dist/engine/github/github-tools.js +0 -263
- package/dist/engine/github/github-tools.js.map +0 -1
- package/dist/engine/github/github.d.ts +0 -1193
- package/dist/engine/github/github.js +0 -357
- package/dist/engine/github/github.js.map +0 -1
- package/dist/engine/github/profiles.d.ts +0 -279
- package/dist/engine/github/profiles.js +0 -42
- package/dist/engine/github/profiles.js.map +0 -1
- package/dist/engine/github/review-poster.d.ts +0 -98
- package/dist/engine/github/review-poster.js +0 -177
- package/dist/engine/github/review-poster.js.map +0 -1
- package/dist/engine/llm.d.ts +0 -79
- package/dist/engine/llm.js +0 -269
- package/dist/engine/llm.js.map +0 -1
- package/dist/engine/oauth.d.ts +0 -43
- package/dist/engine/oauth.js +0 -103
- package/dist/engine/oauth.js.map +0 -1
- package/dist/engine/router.d.ts +0 -29
- package/dist/engine/router.js +0 -680
- package/dist/engine/router.js.map +0 -1
- package/dist/engine/screen/classifier.d.ts +0 -93
- package/dist/engine/screen/classifier.js +0 -215
- package/dist/engine/screen/classifier.js.map +0 -1
- package/dist/engine/screen/screen.d.ts +0 -41
- package/dist/engine/screen/screen.js +0 -93
- package/dist/engine/screen/screen.js.map +0 -1
- package/dist/evals-api.d.ts +0 -24
- package/dist/evals-api.js +0 -22
- package/dist/evals-api.js.map +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -923
- package/dist/index.js.map +0 -1
- package/dist/managed-repos.d.ts +0 -30
- package/dist/managed-repos.js +0 -72
- package/dist/managed-repos.js.map +0 -1
- package/dist/notify/index.d.ts +0 -15
- package/dist/notify/index.js +0 -6
- package/dist/notify/index.js.map +0 -1
- package/dist/notify/model.d.ts +0 -57
- package/dist/notify/model.js +0 -89
- package/dist/notify/model.js.map +0 -1
- package/dist/notify/notifier.d.ts +0 -17
- package/dist/notify/notifier.js +0 -87
- package/dist/notify/notifier.js.map +0 -1
- package/dist/notify/render.d.ts +0 -21
- package/dist/notify/render.js +0 -59
- package/dist/notify/render.js.map +0 -1
- package/dist/notify/transports/github.d.ts +0 -26
- package/dist/notify/transports/github.js +0 -26
- package/dist/notify/transports/github.js.map +0 -1
- package/dist/notify/transports/slack.d.ts +0 -26
- package/dist/notify/transports/slack.js +0 -28
- package/dist/notify/transports/slack.js.map +0 -1
- package/dist/notify/types.d.ts +0 -95
- package/dist/notify/types.js +0 -16
- package/dist/notify/types.js.map +0 -1
- package/dist/providers.d.ts +0 -125
- package/dist/providers.js +0 -279
- package/dist/providers.js.map +0 -1
- package/dist/sandbox/docker.d.ts +0 -180
- package/dist/sandbox/docker.js +0 -517
- package/dist/sandbox/docker.js.map +0 -1
- package/dist/sandbox/egress-allowlist.d.ts +0 -74
- package/dist/sandbox/egress-allowlist.js +0 -164
- package/dist/sandbox/egress-allowlist.js.map +0 -1
- package/dist/sandbox/egress-firewall-config.d.ts +0 -127
- package/dist/sandbox/egress-firewall-config.js +0 -434
- package/dist/sandbox/egress-firewall-config.js.map +0 -1
- package/dist/sandbox/images.d.ts +0 -23
- package/dist/sandbox/images.js +0 -55
- package/dist/sandbox/images.js.map +0 -1
- package/dist/sandbox/index.d.ts +0 -112
- package/dist/sandbox/index.js +0 -332
- package/dist/sandbox/index.js.map +0 -1
- package/dist/sandbox/sandbox.d.ts +0 -214
- package/dist/sandbox/sandbox.js +0 -453
- package/dist/sandbox/sandbox.js.map +0 -1
- package/dist/sandbox/smol.d.ts +0 -162
- package/dist/sandbox/smol.js +0 -485
- package/dist/sandbox/smol.js.map +0 -1
- package/dist/session-log.d.ts +0 -63
- package/dist/session-log.js +0 -290
- package/dist/session-log.js.map +0 -1
- package/dist/state/approval-store.d.ts +0 -88
- package/dist/state/approval-store.js +0 -149
- package/dist/state/approval-store.js.map +0 -1
- package/dist/state/build-assets.d.ts +0 -50
- package/dist/state/build-assets.js +0 -154
- package/dist/state/build-assets.js.map +0 -1
- package/dist/state/db.d.ts +0 -81
- package/dist/state/db.js +0 -155
- package/dist/state/db.js.map +0 -1
- package/dist/state/execution-store.d.ts +0 -257
- package/dist/state/execution-store.js +0 -527
- package/dist/state/execution-store.js.map +0 -1
- package/dist/state/migrate.d.ts +0 -15
- package/dist/state/migrate.js +0 -175
- package/dist/state/migrate.js.map +0 -1
- package/dist/state/workflow-run-store.d.ts +0 -216
- package/dist/state/workflow-run-store.js +0 -403
- package/dist/state/workflow-run-store.js.map +0 -1
- package/dist/telemetry/index.d.ts +0 -38
- package/dist/telemetry/index.js +0 -253
- package/dist/telemetry/index.js.map +0 -1
- package/dist/telemetry/pi-events.d.ts +0 -12
- package/dist/telemetry/pi-events.js +0 -134
- package/dist/telemetry/pi-events.js.map +0 -1
- package/dist/workflows/dag.d.ts +0 -37
- package/dist/workflows/dag.js +0 -118
- package/dist/workflows/dag.js.map +0 -1
- package/dist/workflows/loader.d.ts +0 -53
- package/dist/workflows/loader.js +0 -421
- package/dist/workflows/loader.js.map +0 -1
- package/dist/workflows/loop-eval.d.ts +0 -28
- package/dist/workflows/loop-eval.js +0 -74
- package/dist/workflows/loop-eval.js.map +0 -1
- package/dist/workflows/phase-executor.d.ts +0 -215
- package/dist/workflows/phase-executor.js +0 -1144
- package/dist/workflows/phase-executor.js.map +0 -1
- package/dist/workflows/phase-ref.d.ts +0 -50
- package/dist/workflows/phase-ref.js +0 -91
- package/dist/workflows/phase-ref.js.map +0 -1
- package/dist/workflows/resume.d.ts +0 -58
- package/dist/workflows/resume.js +0 -313
- package/dist/workflows/resume.js.map +0 -1
- package/dist/workflows/runner.d.ts +0 -63
- package/dist/workflows/runner.js +0 -364
- package/dist/workflows/runner.js.map +0 -1
- package/dist/workflows/schema.d.ts +0 -344
- package/dist/workflows/schema.js +0 -460
- package/dist/workflows/schema.js.map +0 -1
- package/dist/workflows/simple.d.ts +0 -90
- package/dist/workflows/simple.js +0 -393
- package/dist/workflows/simple.js.map +0 -1
- package/dist/workflows/target-policy.d.ts +0 -74
- package/dist/workflows/target-policy.js +0 -80
- package/dist/workflows/target-policy.js.map +0 -1
- package/dist/workflows/templates.d.ts +0 -88
- package/dist/workflows/templates.js +0 -127
- package/dist/workflows/templates.js.map +0 -1
- package/dist/workflows/triggers.d.ts +0 -21
- package/dist/workflows/triggers.js +0 -45
- package/dist/workflows/triggers.js.map +0 -1
- package/dist/workflows/verdict.d.ts +0 -19
- package/dist/workflows/verdict.js +0 -30
- package/dist/workflows/verdict.js.map +0 -1
- package/dist/worktree/manager.d.ts +0 -43
- package/dist/worktree/manager.js +0 -127
- package/dist/worktree/manager.js.map +0 -1
- package/docker-compose.yml +0 -274
- package/sandbox.Dockerfile +0 -68
- package/skills/README.md +0 -42
- package/skills/browser-qa/SKILL.md +0 -208
- package/skills/browser-qa/scripts/agent-browser.mjs +0 -531
- package/skills/building/SKILL.md +0 -95
- package/skills/chat/SKILL.md +0 -36
- package/skills/code-review/SKILL.md +0 -82
- package/skills/debug-production/SKILL.md +0 -108
- package/skills/demo/SKILL.md +0 -228
- package/skills/demo/scripts/compose-demo.sh +0 -243
- package/skills/issue-answer/SKILL.md +0 -91
- package/skills/issue-comment/SKILL.md +0 -49
- package/skills/issue-triage/SKILL.md +0 -149
- package/skills/issue-triage/references/AGENT-BRIEF.md +0 -65
- package/skills/pr-comment/SKILL.md +0 -64
- package/skills/pr-review/SKILL.md +0 -148
- package/skills/pr-review/references/findings-schema.md +0 -89
- package/skills/qa-test/SKILL.md +0 -97
- package/skills/repo-health/SKILL.md +0 -67
- package/skills/security-feedback/SKILL.md +0 -173
- package/skills/security-feedback/references/templates.md +0 -77
- package/skills/security-review/SKILL.md +0 -215
- package/skills/security-review/references/issue-format.md +0 -302
- package/skills/verify/SKILL.md +0 -118
- package/workflows/answer.yaml +0 -44
- package/workflows/build.yaml +0 -129
- package/workflows/cron-health.yaml +0 -6
- package/workflows/cron-review.yaml +0 -8
- package/workflows/cron-security.yaml +0 -6
- package/workflows/cron-triage.yaml +0 -8
- package/workflows/demo.yaml +0 -62
- package/workflows/examples/bash-smoke.yaml +0 -41
- package/workflows/examples/parallel-review.yaml +0 -36
- package/workflows/examples/tdd-loop.yaml +0 -17
- package/workflows/explore.yaml +0 -80
- package/workflows/issue-comment.yaml +0 -15
- package/workflows/issue-triage.yaml +0 -22
- package/workflows/pr-comment.yaml +0 -16
- package/workflows/pr-fix.yaml +0 -17
- package/workflows/pr-review.yaml +0 -50
- package/workflows/prompts/answer.md +0 -68
- package/workflows/prompts/architect.md +0 -44
- package/workflows/prompts/classifier.md +0 -60
- package/workflows/prompts/classify-adds-info.md +0 -17
- package/workflows/prompts/demo.md +0 -125
- package/workflows/prompts/executor.md +0 -42
- package/workflows/prompts/explore-ask.md +0 -99
- package/workflows/prompts/explore-publish.md +0 -31
- package/workflows/prompts/explore-read.md +0 -120
- package/workflows/prompts/explore-synthesize.md +0 -88
- package/workflows/prompts/fix.md +0 -18
- package/workflows/prompts/guardrails.md +0 -96
- package/workflows/prompts/pr-fix.md +0 -26
- package/workflows/prompts/pr.md +0 -39
- package/workflows/prompts/qa-browser.md +0 -95
- package/workflows/prompts/qa-synth.md +0 -44
- package/workflows/prompts/qa-test.md +0 -60
- package/workflows/prompts/re-reviewer.md +0 -33
- package/workflows/prompts/reviewer.md +0 -60
- package/workflows/prompts/verify-browser.md +0 -93
- package/workflows/prompts/verify-synth.md +0 -44
- package/workflows/prompts/verify.md +0 -63
- package/workflows/qa-test.yaml +0 -94
- package/workflows/repo-health.yaml +0 -15
- package/workflows/security-feedback.yaml +0 -25
- package/workflows/security-review.yaml +0 -37
- package/workflows/verify.yaml +0 -93
- /package/dist/{cli/cli-config.d.ts → cli-config.d.ts} +0 -0
- /package/dist/{cli/cli-config.js → cli-config.js} +0 -0
- /package/dist/{cli/cli-format.d.ts → cli-format.d.ts} +0 -0
- /package/dist/{cli/cli-format.js → cli-format.js} +0 -0
- /package/dist/{cli/cli-timeline.d.ts → cli-timeline.d.ts} +0 -0
- /package/dist/{cli/cli-timeline.js → cli-timeline.js} +0 -0
- /package/dist/{cli/cli.d.ts → cli.d.ts} +0 -0
- /package/dist/{cli/oauth-cli.d.ts → oauth-cli.d.ts} +0 -0
- /package/dist/{cli/setup.d.ts → setup.d.ts} +0 -0
package/docker-compose.yml
DELETED
|
@@ -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:
|
package/sandbox.Dockerfile
DELETED
|
@@ -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 `` 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.
|