lastlight 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/admin/routes.js +101 -0
- package/dist/admin/routes.js.map +1 -1
- package/dist/cli/cli-server.d.ts +51 -9
- package/dist/cli/cli-server.js +101 -18
- package/dist/cli/cli-server.js.map +1 -1
- package/dist/cli/cli.js +9 -6
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/setup.js +2 -2
- package/dist/cli/setup.js.map +1 -1
- package/dist/engine/agent-executor.js +1 -0
- package/dist/engine/agent-executor.js.map +1 -1
- package/dist/engine/github/profiles.d.ts +15 -5
- package/dist/engine/github/profiles.js.map +1 -1
- package/dist/sandbox/index.d.ts +13 -6
- package/dist/sandbox/index.js +69 -29
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/sandbox.d.ts +9 -0
- package/dist/sandbox/sandbox.js.map +1 -1
- package/dist/state/approval-store.d.ts +2 -0
- package/dist/state/approval-store.js +9 -0
- package/dist/state/approval-store.js.map +1 -1
- package/dist/workflows/resume.js +1 -6
- package/dist/workflows/resume.js.map +1 -1
- package/dist/workflows/runner.js +4 -0
- package/dist/workflows/runner.js.map +1 -1
- package/dist/workflows/simple.d.ts +2 -48
- package/dist/workflows/simple.js +12 -55
- package/dist/workflows/simple.js.map +1 -1
- package/dist/workflows/target-policy.d.ts +74 -0
- package/dist/workflows/target-policy.js +80 -0
- package/dist/workflows/target-policy.js.map +1 -0
- package/docker-compose.yml +18 -3
- package/package.json +2 -2
- package/plugins/lastlight/.claude-plugin/plugin.json +1 -1
- package/plugins/lastlight/skills/lastlight-server/SKILL.md +18 -3
- package/plugins/lastlight/skills/lastlight-server/references/env-schema.md +16 -3
- package/sandbox.Dockerfile +21 -89
- package/skills/issue-answer/SKILL.md +5 -4
- package/skills/issue-comment/SKILL.md +3 -1
- package/skills/issue-triage/SKILL.md +5 -3
- package/skills/security-review/SKILL.md +3 -1
- package/workflows/prompts/answer.md +2 -1
package/sandbox.Dockerfile
CHANGED
|
@@ -2,96 +2,28 @@
|
|
|
2
2
|
# fallback only — the default production sandbox is gondolin, invoked
|
|
3
3
|
# in-process by agentic-pi without docker).
|
|
4
4
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# package.json#engines just work. System node from the base image stays at
|
|
16
|
-
# /usr/local/bin; fnm-managed versions are pre-installed under FNM_DIR and
|
|
17
|
-
# selected per-shell by the bashrc hook below.
|
|
18
|
-
ENV FNM_DIR=/usr/local/share/fnm
|
|
19
|
-
ENV PATH=$FNM_DIR/aliases/default/bin:$PATH
|
|
20
|
-
RUN curl -fsSL https://fnm.vercel.app/install \
|
|
21
|
-
| bash -s -- --install-dir "$FNM_DIR" --skip-shell \
|
|
22
|
-
&& ln -s "$FNM_DIR/fnm" /usr/local/bin/fnm \
|
|
23
|
-
&& fnm install 22 \
|
|
24
|
-
&& fnm install 24 \
|
|
25
|
-
&& fnm default 22 \
|
|
26
|
-
&& chmod -R a+rX "$FNM_DIR" \
|
|
27
|
-
&& mkdir -p "$FNM_DIR/multishells" \
|
|
28
|
-
&& chmod 1777 "$FNM_DIR/multishells"
|
|
29
|
-
|
|
30
|
-
# Source fnm in every bash invocation (interactive or not). BASH_ENV makes
|
|
31
|
-
# non-interactive `bash -c` read this file — that's how opencode's bash tool
|
|
32
|
-
# inherits the right node version when it runs `npm ci` inside a repo with
|
|
33
|
-
# an .nvmrc pinning Node 24.
|
|
34
|
-
RUN printf '%s\n' \
|
|
35
|
-
'export FNM_DIR=/usr/local/share/fnm' \
|
|
36
|
-
'export PATH="$FNM_DIR:$PATH"' \
|
|
37
|
-
'# --shell bash is required: when sourced via BASH_ENV the parent process' \
|
|
38
|
-
'# is not a shell so fnm cannot auto-detect.' \
|
|
39
|
-
'eval "$(fnm env --shell bash --use-on-cd --version-file-strategy=recursive)"' \
|
|
40
|
-
'# The cd hook fires only on cd. Tools often launch `bash -c "..."` with' \
|
|
41
|
-
'# cwd already set via the spawn options — no cd happens — so also' \
|
|
42
|
-
'# auto-switch on shell start when the cwd has a version file.' \
|
|
43
|
-
'if [ -f "$PWD/.nvmrc" ] || [ -f "$PWD/.node-version" ]; then' \
|
|
44
|
-
' fnm use --silent-if-unchanged 2>/dev/null \' \
|
|
45
|
-
' || { fnm install 2>/dev/null && fnm use --silent-if-unchanged 2>/dev/null; } \' \
|
|
46
|
-
' || true' \
|
|
47
|
-
'fi' \
|
|
48
|
-
> /etc/bash.bashrc.fnm \
|
|
49
|
-
&& printf '\n[ -r /etc/bash.bashrc.fnm ] && . /etc/bash.bashrc.fnm\n' >> /etc/bash.bashrc \
|
|
50
|
-
&& ln -s /etc/bash.bashrc.fnm /etc/profile.d/fnm.sh
|
|
51
|
-
# Source the fnm file directly — Debian's /etc/bash.bashrc bails early on
|
|
52
|
-
# non-interactive shells (`[ -z "$PS1" ] && return`), so pointing BASH_ENV
|
|
53
|
-
# at it would skip our setup for `bash -c` invocations (which is the path
|
|
54
|
-
# the agent's bash tool uses).
|
|
55
|
-
ENV BASH_ENV=/etc/bash.bashrc.fnm
|
|
56
|
-
|
|
57
|
-
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
58
|
-
python3 python3-pip pipx \
|
|
59
|
-
&& PIPX_HOME=/opt/pipx PIPX_BIN_DIR=/usr/local/bin pipx install semgrep \
|
|
60
|
-
&& curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz \
|
|
61
|
-
| tar -xz -C /usr/local/bin gitleaks \
|
|
62
|
-
&& apt-get purge -y python3-pip \
|
|
63
|
-
&& rm -rf /var/lib/apt/lists/*
|
|
64
|
-
|
|
65
|
-
# uv — fast, isolated Python runner for `type: script` (runtime: python) phases.
|
|
66
|
-
# Single static binary; `uv run script.py` honours PEP 723 inline dependency
|
|
67
|
-
# blocks and resolves them into a cached venv (UV_CACHE_DIR is pointed at the
|
|
68
|
-
# shared /cache volume at runtime, mirroring the npm/pnpm/yarn cache wiring).
|
|
69
|
-
RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh \
|
|
70
|
-
&& uv --version
|
|
71
|
-
|
|
72
|
-
# Create non-root agent user, UID/GID pinned to 10001 to MATCH the harness
|
|
73
|
-
# `lastlight` user (see Dockerfile: `useradd -u 10001 lastlight`). The
|
|
74
|
-
# entrypoint chowns the bind-mounted workspace to `agent`; per-PR workspaces
|
|
75
|
-
# are persistent and reused across runs (issue #107), so the harness re-stages
|
|
76
|
-
# AGENTS.md + skills into them on the next run. If `agent` lands on the default
|
|
77
|
-
# uid (1001) those files become unwritable by the harness (10001) on reuse —
|
|
78
|
-
# EACCES, and the review silently runs with no skill/AGENTS.md. Sharing the uid
|
|
79
|
-
# keeps the reused workspace mutually writable by both the agent (in-container)
|
|
80
|
-
# and the harness (host). Also self-heals dirs left at 1001 by older images:
|
|
81
|
-
# the entrypoint's `chown -R agent:agent` re-owns them to 10001 on next reuse.
|
|
82
|
-
RUN groupadd -g 10001 agent && useradd -m -s /bin/bash -u 10001 -g 10001 agent
|
|
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
|
+
FROM lastlight-sandbox-base:latest
|
|
83
15
|
|
|
84
16
|
# Install agentic-pi globally so the harness can `docker exec ...
|
|
85
|
-
# agentic-pi run ...` against this container. The version + integrity
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
17
|
+
# agentic-pi run ...` against this container. The version + integrity come from
|
|
18
|
+
# `sandbox/agentic-pi.pin` (regenerated from lastlight's package-lock.json by
|
|
19
|
+
# scripts/agentic-pi-pin.sh, drift-guarded by tests/agentic-pi-pin.test.ts). We
|
|
20
|
+
# COPY that tiny two-line file rather than the whole package-lock.json on
|
|
21
|
+
# purpose: the lockfile's hash changes on every release and would rebust this
|
|
22
|
+
# layer (and sandbox-qa's Chromium) on every version bump; the pin changes only
|
|
23
|
+
# when agentic-pi actually does. `npm install -g <tarball>` doesn't consult a
|
|
24
|
+
# lockfile, hence the explicit integrity verification.
|
|
25
|
+
COPY sandbox/agentic-pi.pin /tmp/agentic-pi.pin
|
|
26
|
+
RUN version=$(sed -n '1p' /tmp/agentic-pi.pin) \
|
|
95
27
|
&& expected=$(sed -n '2p' /tmp/agentic-pi.pin) \
|
|
96
28
|
&& echo "Installing agentic-pi@${version} (${expected})" \
|
|
97
29
|
&& curl -fsSL "https://registry.npmjs.org/agentic-pi/-/agentic-pi-${version}.tgz" -o /tmp/agentic-pi.tgz \
|
|
@@ -103,7 +35,7 @@ RUN node -e "const p=require('/tmp/lastlight-lock.json').packages['node_modules/
|
|
|
103
35
|
exit 1; \
|
|
104
36
|
fi \
|
|
105
37
|
&& npm install -g --no-audit --no-fund /tmp/agentic-pi.tgz \
|
|
106
|
-
&& rm /tmp/agentic-pi.tgz /tmp/agentic-pi.pin
|
|
38
|
+
&& rm /tmp/agentic-pi.tgz /tmp/agentic-pi.pin
|
|
107
39
|
|
|
108
40
|
# Agent context (baked at /app/ — entrypoint cats into workspace/AGENTS.md)
|
|
109
41
|
COPY agent-context/ /app/agent-context/
|
|
@@ -67,10 +67,11 @@ maintainer to `@last-light build` (or `explore`) it — let triage own work item
|
|
|
67
67
|
For broad/open-ended questions (e.g. "what's missing vs tool X"), gather a
|
|
68
68
|
representative sample and answer from it — explicitly noting it's a
|
|
69
69
|
sample, not an exhaustive audit — rather than enumerating everything.
|
|
70
|
-
3. **Label (GitHub issue only).**
|
|
71
|
-
(
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
3. **Label (GitHub issue only).** Ensure `question` exists idempotently with
|
|
71
|
+
`github_ensure_labels` (`[{name: "question", color: "d876e3"}]` — one call,
|
|
72
|
+
no 422 to worry about), then apply it with `github_add_labels`. If ensuring
|
|
73
|
+
or adding the label is denied, skip it — the answer is the deliverable. For a
|
|
74
|
+
Slack-initiated question there is no issue to label.
|
|
74
75
|
4. **Write the answer as your final message** (the harness delivers it — see
|
|
75
76
|
above; do not post it yourself):
|
|
76
77
|
- Direct and structured. Lead with the answer; use short sections or a
|
|
@@ -23,7 +23,9 @@ outgoing comment.** Never make code changes, create branches, or push.
|
|
|
23
23
|
2. **Do the bounded action:**
|
|
24
24
|
- Close / reopen → `github_update_issue`
|
|
25
25
|
- Label → `github_add_labels` / `github_remove_label` (use the canonical
|
|
26
|
-
triage roles from `docs/agents/triage-labels.md` where relevant
|
|
26
|
+
triage roles from `docs/agents/triage-labels.md` where relevant; if you're
|
|
27
|
+
applying a canonical label that may not exist yet, ensure it first with one
|
|
28
|
+
idempotent `github_ensure_labels` call so it lands with the right color)
|
|
27
29
|
- Duplicate check → search similar issues, link the original in one comment
|
|
28
30
|
- Answer a direct question → ≤5 sentences, ≤2 file reads. Don't survey the
|
|
29
31
|
codebase or compile a report.
|
|
@@ -29,8 +29,10 @@ downstream build agent, which has full source access.
|
|
|
29
29
|
|
|
30
30
|
## 0. Ensure the labels exist
|
|
31
31
|
|
|
32
|
-
Before applying any label, create the canonical set
|
|
33
|
-
`
|
|
32
|
+
Before applying any label, create the canonical set in a **single** idempotent
|
|
33
|
+
`github_ensure_labels` call — pass every row below as one `labels` array
|
|
34
|
+
(`{name, color}`). It lists once and creates only the missing ones, so it never
|
|
35
|
+
errors on labels that already exist:
|
|
34
36
|
|
|
35
37
|
| Label | Color | Role |
|
|
36
38
|
|-------|-------|------|
|
|
@@ -44,7 +46,7 @@ Before applying any label, create the canonical set idempotently with
|
|
|
44
46
|
| `duplicate` | `cfd3d7` | dedupe |
|
|
45
47
|
| `question` | `d876e3` | question |
|
|
46
48
|
|
|
47
|
-
If
|
|
49
|
+
If the call is denied (the token lacks the permission), fall back to using
|
|
48
50
|
only the labels that already exist on the repo and skip the rest. Done when the
|
|
49
51
|
labels you need are present or you've confirmed you can't create them.
|
|
50
52
|
|
|
@@ -68,7 +68,9 @@ recording "no relevant changes since prior scan" and return.
|
|
|
68
68
|
|
|
69
69
|
### 2. Ensure labels exist
|
|
70
70
|
|
|
71
|
-
`
|
|
71
|
+
One idempotent `github_ensure_labels` call — pass every row below as a single
|
|
72
|
+
`labels` array of `{name, color}` (it creates only the missing ones, never
|
|
73
|
+
errors on existing labels):
|
|
72
74
|
|
|
73
75
|
| Label | Color | Purpose |
|
|
74
76
|
|-------|-------|---------|
|
|
@@ -58,7 +58,8 @@ double-post. The only GitHub write you make is the label:
|
|
|
58
58
|
|
|
59
59
|
{{#if issueNumber}}
|
|
60
60
|
- Apply the `question` label to issue #{{issueNumber}} with `github_add_labels`
|
|
61
|
-
(
|
|
61
|
+
(ensure it exists first with one `github_ensure_labels` call —
|
|
62
|
+
`[{name: "question", color: "d876e3"}]`; it's idempotent, no 422 to handle).
|
|
62
63
|
Leave the issue open.
|
|
63
64
|
{{/if}}
|
|
64
65
|
{{#if !issueNumber}}
|