Getting started

What do I need to run Last Light?

Three things:

  1. An API key for your chosen model provider — OPENAI_API_KEY, ANTHROPIC_API_KEY, or OPENROUTER_API_KEY (the agent runtime is agentic-pi, provider-agnostic; one OpenRouter key gets you Claude / GPT / Gemini / Llama / etc. through a single endpoint)
  2. A GitHub App with Issues, PRs, and Contents permissions
  3. Node.js 20+. Docker is required only when LASTLIGHT_SANDBOX=docker; the default gondolin sandbox uses HVF/KVM with no Docker dependency.

See the Get Started guide for step-by-step instructions.

Which LLM does it use?

The agent runtime is agentic-pi (workflow phases) plus @earendil-works/pi-ai (in-process chat) — both provider-agnostic. The default model is anthropic/claude-sonnet-4-6, configurable via the LASTLIGHT_MODEL environment variable. Any provider/model string pi-ai supports works (openai/…, anthropic/…, openrouter/<vendor>/<model>, etc.).

Per-phase overrides go through LASTLIGHT_MODELS as JSON — e.g. {"chat":"openai/gpt-5.1-mini","architect":"openai/gpt-5.5"}. Set OPENAI_API_KEY, ANTHROPIC_API_KEY, and/or OPENROUTER_API_KEY to match — one OpenRouter key covers most providers. Legacy OPENCODE_* env vars are still read as fallbacks.

Does it work with GitHub Enterprise?

Not yet tested, but it should work with GitHub Enterprise Cloud since it uses the same API. GitHub Enterprise Server (self-hosted) would need base URL configuration in the GitHub tooling layer — contributions welcome.

How it works

How does the bot authenticate with GitHub?

Last Light authenticates as a GitHub App, not a personal account. It generates short-lived installation tokens (expire in ~1 hour) using your App's private key. Every action — comments, labels, PR reviews — shows up as coming from the bot, not you.

The harness handles token generation, refresh, and per-workflow downscoping automatically (see permission profiles below). For git operations inside sandboxes, a credential file is synced before each command.

What's the difference between webhook mode and cron mode?

Webhooks react in real-time — GitHub sends events (issue opened, PR created, comment posted) to your endpoint, and the agent handles them immediately. Requires a publicly reachable URL.

Cron polls on a schedule — the agent checks for new issues, PRs, and stale items periodically (e.g. every 15 minutes). No public URL needed, but responses are delayed.

Use one or the other — not both. Webhooks are better for responsive repos; cron is simpler to deploy.

Can the bot actually fix bugs and open PRs?

Yes. When a maintainer @mentions the bot on an issue, it clones the repo into an isolated sandbox (a gondolin micro-VM by default, or a Docker container if LASTLIGHT_SANDBOX=docker), reads the code, makes changes, runs tests, and opens a PR. It's not magic — it works best for well-scoped issues with clear descriptions.

The bot always links back to the original issue and posts validation results so you can review before merging.

What happens when it doesn't know what to do?

It does nothing. Silence is built into the design — the agent only acts on events it has explicit rules for. Unknown event types, ambiguous requests, and bot-generated events are all ignored. For build requests, if it can't push code, it posts a detailed comment explaining what it tried and what needs human attention.

What's the difference between Sandbox Sessions and Chat Sessions?

Sandbox sessions are agent runs that executed inside a sandbox — triage, PR review, build, health report, anything driven by a YAML workflow. Each phase runs agentic-pi run in a gondolin micro-VM (or Docker container when LASTLIGHT_SANDBOX=docker); events are streamed into a Claude-SDK-style JSONL envelope on disk and listed on the dashboard's Sandbox Sessions tab.

Chat sessions are conversations from Slack threads. They run an in-process @earendil-works/pi-ai chat loop inside the harness — one pi-ai conversation per Slack thread, history rehydrated from the messaging_messages SQLite table on every turn, so context holds for hours or days. Every turn is persisted (executions table) and one thread becomes one growing, coherent JSONL. They live on the dashboard's Chat Sessions tab.

In short: sandbox sessions are one-shot workflow runs; chat sessions are multi-turn conversations glued to a Slack thread.

How do approval gates work?

A workflow phase can declare approval_gate: post_architect (or any other gate name). If that gate is enabled via the APPROVAL_GATES env var, the runner pauses after that phase, persists the run state, writes a row to the workflow_approvals table, and waits for a human decision.

You resolve gates three ways: commenting @last-light approve or @last-light reject on the triggering GitHub issue; running /approve / /reject from Slack; or clicking the button on the dashboard. The run resumes exactly where it stopped — no phases re-execute.

Because gates are opt-in per environment, you can ship workflows with gates pre-declared and enable them only in production.

How do permission profiles downscope the GitHub token?

The GitHub App itself has broad permissions (contents read/write, issues read/write, PRs read/write). But every workflow declares a profile — one of read, issues-write, review-write, or repo-write. Before spawning a sandbox, the harness mints an installation token that only has the scopes that profile needs.

The practical effect: a triage run literally cannot push code, a review run cannot open issues, etc. Even if a prompt got compromised, the token handed into the sandbox doesn't have the write permission in the first place.

Customization

How do I change what the bot does?

Edit the YAML in workflows/. Every behavior is a workflow — build.yaml, issue-triage.yaml, pr-review.yaml, cron-health.yaml, etc. A workflow is a list of phases; each phase runs an agent with either an inline prompt (prompt: prompts/architect.md) or a named skill (skill: assure-guardrails).

To change an existing behavior, edit its workflow file or its referenced prompt under workflows/prompts/ or its SKILL.md under skills/. To add a new behavior, drop a new YAML file into workflows/ — the harness picks it up automatically. No TypeScript needed.

Can I add my own skills?

Yes. Create a new directory under skills/ with a SKILL.md file — name, description, and tags in the frontmatter, instructions in the body. Skills are plain Markdown that the agent follows as instructions. The agent discovers new skills automatically.

How do I add more repos?

Just install the GitHub App on additional repos — no code changes needed. The bot handles all installed repos automatically. If you want different behavior per repo, you'd need to add conditional logic to the skill files (check repository.full_name in the webhook payload).

Operations

How much does it cost to run?

The main cost is model API usage on whichever provider you've configured (OpenAI, Anthropic, OpenRouter, etc.). Each issue triage or PR review is roughly one agent session. For a repo with a few events per day, expect modest API costs. Build requests (cloning, editing, testing) use more tokens but are less frequent.

Set OPENAI_API_KEY, ANTHROPIC_API_KEY, and/or OPENROUTER_API_KEY to match your LASTLIGHT_MODEL and costs depend on usage. Cheap models like anthropic/claude-haiku-4-5-20251001 or openrouter/google/gemini-2.5-flash can be routed to high-volume phases (triage, screen) via LASTLIGHT_MODELS to keep the bill down. OpenRouter adds a small per-token markup over going direct, but in exchange you get a single key + dashboard for every model.

The agent itself is free and open-source.

What if the model provider goes down?

If the configured model provider is unavailable, agent sessions will fail and the error is logged. GitHub webhook events are retried by GitHub automatically if your endpoint doesn't respond, so the work will be picked up when service returns. Because pi-ai is provider-agnostic, you can flip LASTLIGHT_MODEL to a different provider during an outage. The execution database tracks all attempts for visibility.

Is there a risk of the bot going rogue?

The bot can only do what its GitHub App permissions allow. It can't delete repos, manage org settings, or access repos where it's not installed. Build requests only trigger when a maintainer explicitly @mentions the bot. All actions are logged as session trajectories in logs/.

For extra safety: install the App with read-only Contents permission if you don't want the bot pushing code, and only grant write access to Issues and PRs.