Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/harnesscli/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ func runServiceInstall(args []string) int {
fs := flag.NewFlagSet("service install", flag.ContinueOnError)
fs.SetOutput(stderr)
binary := fs.String("binary", "", "path to the harnessd binary (default: look up harnessd on PATH)")
addr := fs.String("addr", "", "listen address for harnessd (default: resolve like harnessd — HARNESS_ADDR env or :8080)")
addr := fs.String("addr", "", "listen address for harnessd (default: resolve like harnessd — HARNESS_ADDR env or 127.0.0.1:8080)")
logDir := fs.String("log-dir", "", "directory for service logs (default ~/.harness/logs)")
dryRun := fs.Bool("dry-run", false, "print the rendered unit file and target path without writing anything")
if err := fs.Parse(args); err != nil {
Expand Down
84 changes: 84 additions & 0 deletions docs/logs/engineering-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,90 @@
- Verification: `go test ./cmd/harnessd ./cmd/harnesscli/... ./internal/config
-race` and `go vet` on the same packages, all green.

## 2026-09-05 — Issue #1380 website/docs staleness correction

- Scope: `website/docs/**` reference/concept/tutorial/server pages plus the one-line
`harnesscli service install --addr` help-text default at `cmd/harnesscli/service.go:410`.
A prior read-only audit (attached to epic #1369) flagged 19 website pages with
verified-wrong claims; every claim was re-verified against the current tree with `rg`
before being corrected (the audit was treated as a lead, not gospel).
- Bind default: `HARNESS_ADDR` changed to `127.0.0.1:8080` in issue #1328
(`internal/config/config.go:254`) with a non-loopback-bind refusal
(`cmd/harnessd/bind_guard.go:29-42`); ~15 stray `:8080` claims across
reference/getting-started/concepts/server/tutorials pages were corrected to match,
and the refusal behavior is now documented next to each default.
- Resume story: `harnesscli continue` / `POST /v1/runs/{id}/continue` requires the
source run's status to be `completed` and returns 409 `run_not_completed`
otherwise (`internal/harness/runner.go:2217`, `internal/server/http_runs.go:817`);
a cancelled run cannot be resumed. Corrected in `reference/exit-codes.md`,
`cli/harnesscli.md`, `cli/go-code-wrapper.md`, `server/http-api-guide.md`, and
`concepts/runs-and-conversations.md`, which previously told operators to
`harnesscli continue` a blocked or cancelled run.
- Route inventory (`reference/http-routes.md`) was rebuilt from the actual
`mux.Handle`/`mux.HandleFunc` registrations in `internal/server/http*.go`: added
`/v1/tools`, `/v1/hooks`, `/v1/config/reload`, `/v1/model-settings*`, `/v1/tasks`,
`/v1/jobs/{id}/kill|output`, `/v1/callbacks/{id}/cancel`, `/v1/cron/runs`,
`/v1/cron/jobs/{id}/executions`, `/v1/conversations/{id}/events|rewind-points|rewind|undo`,
`/v1/runs/{id}/replay`, `/v1/providers/{name}/import-subscription`, the 6
`/v1/relay/*` control-plane routes, and `/viz`. The `RunRequest` schema block gained
`attachments`, `plan_mode`, `plan_file`, `extra_dirs`, `denied_tools`, `rules`, and
`workspace_path` (documented as the post-#1372 state — the field does not exist in
`RunRequest` yet on this branch).
- Events catalog (`reference/events-catalog.md`): `AllEventTypes()` returns 79 of the
86 event constants actually declared and emitted; the 7 gap events
(`callback.dispatching|failed|retry_wait|started`, `plan.approval_required|granted|denied`)
are genuinely emitted via a separate string-keyed bridge
(`internal/harness/tools/delayed_callback.go`, `internal/harness/plan_mode.go:75-107`) and
are now documented alongside a corrected 86-event total. Also added `todos.updated` and
`job.completed` (`EventBackgroundJobCompleted`, delivered to the conversation stream, not
the originating run), and fixed a stale `"tool": "ask_user_question"` payload example to
the real `"AskUserQuestion"` value.
- Tools catalog (`reference/tools-catalog.md`): added `list_models`, `deploy`, `goals`,
`cron_update`, `cron_history`, `message_subagent`, `notify_parent`, and `agent_swarm` —
all verified as real `Definition{Name: ...}` registrations in
`internal/harness/tools_default.go`. Rejected the audit's `compact_summary` entry after
verification: it is an internal message-tagging label used by the `compact_history` tool,
not a callable tool.
- Providers: `catalog/models.json` has 15 providers, not 10 — added `cerebras`,
`codex-subscription`, `kimi-subscription`, `lmstudio`, `ollama` to
`reference/providers-and-models-reference.md`, `reference/environment-variables.md`,
`concepts/providers-and-models.md`, and `getting-started/what-is-go-code.md`. Live model
discovery is provider-agnostic (OpenRouter, OpenAI, Anthropic, DeepSeek all refresh on a
5-minute TTL via `internal/provider/openai/discovery.go:24` and
`internal/provider/anthropic/discovery.go:24`), not OpenRouter-only as previously stated.
- `max_steps`: `0` means unlimited with no daemon-level fallback to a non-zero cap
(documented as the post-#1376 state — `cmd/harnessd/config_reload.go:44-47` on this
branch still resets a resolved `0` to `8`).
- `server/expose-as-mcp-server.md` needed a substantial rewrite beyond the audit's
findings: `/mcp` is mounted via `harnessmcp.NewHTTPHandler`
(`cmd/harnessd/runtime_container.go:348-352`), not `internal/mcpserver.NewServer` (which
has no production caller). `/mcp` and the `harness-mcp` stdio proxy share the same
25-tool, REST-backed dispatcher (`internal/harnessmcp`) — the page previously described
them as two different tool sets (10 vs. 5) and claimed `/mcp` supports `GET` SSE
notifications via `subscribe_run`; the handler is POST-only and 405s on GET
(`internal/harnessmcp/httptransport.go:29-31`). Same "five tools" and unmounted-SSE
staleness also existed in `tutorials/claude-desktop-mcp.md`, corrected there too.
`list_mcp_resources`/`read_mcp_resource` are implemented (`cmd/harnessd/mcp_setup.go:68-90`,
`internal/mcp/mcp.go:231,246`), contradicting the "not yet implemented" claim in
`integrations/mcp-consume.md`.
- Added missing `harnesscli` subcommand documentation (dispatch table in
`cmd/harnesscli/auth.go`): `steer`, `viz`, `acp`, `plugin`, `mcp`, `hooks`, `service`,
`auth kimi`, `auth codex`, and `input` (documented as the post-#1374 CLI addition; the
route `POST /v1/runs/{id}/input` it calls already exists today).
- One-line code fix: `cmd/harnesscli/service.go:410`'s `--addr` flag help text said the
resolved default was `:8080`; corrected to `127.0.0.1:8080` to match
`internal/config/config.go:254`. No test asserted the old string;
`go test ./cmd/harnesscli -run Service -race` passes unchanged.
- Verification: `rg` re-check of every corrected route/flag/env/tool/event against the
current tree; `npm run build` in `website/` (Docusaurus) completed with no broken
internal links or MDX errors.
- New finding, not fixed here (outside `website/docs` + the one help string): the VM
workspace cloud-init template (`internal/workspace/bootstrap.go:36`) sets
`HARNESS_ADDR=:8080` (a non-loopback bind) with no `HARNESS_AUTH_DISABLED` and no
configured auth, so `bind_guard.go`'s #1328 protection likely now rejects `harnessd`
startup on provisioned VM workspaces; `systemctl start harnessd || true` would swallow
the failure silently. Filed as a follow-up rather than fixed in this docs-only PR.

## 2026-08-08 — Issue #1285 attached lifecycle PTY (in implementation)

- Planned boundary: ptyrunner will accept only the typed identity returned by
Expand Down
7 changes: 4 additions & 3 deletions website/docs/cli/go-code-wrapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ go-code "summarize the diff"
case $? in
0) echo "run completed" ;;
2) echo "run failed" ;;
3) echo "run blocked on input — resume interactively" ;;
6) echo "run cancelled — resumable via go-code continue <run-id> ..." ;;
3) echo "run blocked on input — answer with harnesscli input <run-id> \"<q>=<a>\", or resume interactively with go-code --resume <run-id>" ;;
6) echo "run cancelled — not resumable; harnesscli continue requires status=completed. Start a new run instead." ;;
esac
```

Expand All @@ -154,6 +154,7 @@ The full code table (`0` completed, `1` client error, `2` failed, `3` blocked, `
| `go-code` | `--tui` | Launches the interactive BubbleTea TUI |
| `go-code "prompt"` | `-prompt "..."` | Runs a single prompt, streams events, exits |
| `go-code --server` | (server lifecycle only) | Starts `harnessd` in background and exits |
| `go-code --resume <run-id>` | `--tui -resume <run-id>` | Launches the TUI resuming an existing conversation |
| `go-code runs` | `list` | Lists known runs |
| `go-code list` | `list` | Alias for `runs` |
| `go-code show <id>` | `status` | Shows one run |
Expand All @@ -170,7 +171,7 @@ The full code table (`0` completed, `1` client error, `2` failed, `3` blocked, `

### Server address: `HARNESS_ADDR`

The `HARNESS_ADDR` environment variable controls the listen address. The default is `:8080`. The wrapper extracts the port from this value and constructs the base URL as `http://127.0.0.1:<port>`.
The `HARNESS_ADDR` environment variable controls the listen address. The default is `127.0.0.1:8080`. The wrapper extracts the port from this value and constructs the base URL as `http://127.0.0.1:<port>`.

```bash
# Run on a different port
Expand Down
167 changes: 158 additions & 9 deletions website/docs/cli/harnesscli.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ The process exit code reports the run's outcome, so scripts and CI can branch on
| `1` | Client-side error: bad flags, missing prompt, connection/HTTP/stream failure |
| `2` | `run.failed` — a turn failed server-side |
| `3` | Blocked — the run needs input it will never get headlessly (`run.waiting_for_user`, `tool.approval_required`, or `plan.approval_required` observed while stdin is non-interactive) |
| `6` | `run.cancelled` — interrupted but resumable via `harnesscli continue <run-id> <prompt>` |
| `6` | `run.cancelled` — interrupted; **not** resumable via `harnesscli continue` (that command requires the source run's status to be `completed` and returns HTTP 409 otherwise). Start a new run to continue the work. |
| `130` | SIGINT/SIGTERM while streaming |

The `run_id=` / `terminal_event=` stdout lines are unchanged by this mapping. See [Exit Codes](/docs/reference/exit-codes) for the full contract — blocked-signal details, goal-status reservations, and per-command coverage.
Expand All @@ -69,7 +69,9 @@ The `run_id=` / `terminal_event=` stdout lines are unchanged by this mapping. Se
| `-task-context` | `""` | Task context injected into the startup prompt |
| `-prompt-profile` | `""` | Prompt profile override for model routing |
| `-prompt-custom` | `""` | Custom prompt extension text |
| `-workspace` | cwd | Workspace directory for this run |
| `-workspace` | cwd | Workspace directory for this run (sent as `workspace_path`; see the callout below) |
| `-plan-mode` | `false` | Start the run in enforced read-only plan mode (`plan_mode` in the request); see [Enforced Plan Mode](/docs/concepts/configuration) |
| `-resume` | `""` | Resume an existing conversation by ID in the TUI; implies `-tui` |
| `-tui` | `false` | Launch the interactive BubbleTea TUI (requires a real terminal) |
| `-list-profiles` | `false` | List available profiles and exit |
| `-prompt-behavior` | (empty) | Behavior extension IDs — repeatable or comma-separated |
Expand Down Expand Up @@ -102,7 +104,7 @@ terminal_event=run.completed
```

<Callout type="info">
`-workspace` defaults to the current working directory via `os.Getwd()`. The value is serialized as `workspace_path` in the run creation request, but the server's `POST /v1/runs` handler decodes into `harness.RunRequest`, which has no `workspace_path` field — the value is currently silently ignored server-side. Workspace selection is controlled by `workspace_type` and profile-level runner configuration, not by this flag.
`-workspace` defaults to the current working directory via `os.Getwd()` and is sent as `workspace_path` in the run creation request. The server honors `workspace_path` when it is an absolute path to an existing directory: tools for the run are rooted there instead of the server's own working directory. `workspace_type` and profile-level runner configuration control workspace *provisioning* (local directory, git worktree, container, VM); `workspace_path` only selects which existing directory a non-provisioned (local-process) run is rooted in.
</Callout>

<Callout type="warning">
Expand Down Expand Up @@ -177,6 +179,10 @@ Output includes: ID, Status, Model, Created, Updated, Prompt (truncated at 80 ch

Send a follow-up prompt to an existing run, creating a new run in the same conversation.

<Callout type="warning">
`continue` only works when the source run's status is `completed`. It returns HTTP 409 `run_not_completed` for any other status (`waiting_for_user`, `waiting_for_approval`, `running`, `queued`, `failed`, or `cancelled`) — a cancelled run cannot be resumed at all, and a run blocked on a question or approval must be unblocked first (see `harnesscli input` below, or `POST /v1/runs/{id}/approve` / `/deny`).
</Callout>

```bash
# Stream the continuation (default):
harnesscli continue <run-id> Now explain it to a 5-year-old
Expand All @@ -198,6 +204,21 @@ When `-no-stream` is false (the default), the new run's events are streamed and

---

### input

Answer a run that is blocked on `run.waiting_for_user` (the run invoked the `AskUserQuestion` tool).

```bash
harnesscli input <run-id> "question-key=the answer"
harnesscli input <run-id> "q1=yes" "q2=no"
```

**API:** `POST /v1/runs/{id}/input` with body `{"answers": {"<question-key>": "<answer>"}}`

Each positional argument after the run ID is split on the first `=`; the part before `=` is the question key (as returned by `GET /v1/runs/{id}/input`) and the part after is the answer. The run resumes automatically once all pending questions are answered.

---

### replay

Replay a recorded rollout. The rollout can be provided as a run ID (the server locates the JSONL file) or as a direct rollout file path.
Expand Down Expand Up @@ -250,6 +271,109 @@ The query is all positional args joined with spaces. Matching is case-insensitiv

---

### steer

Inject a steering message into an active run without stopping it.

```bash
harnesscli steer <run-id> "focus on the auth module instead"
```

**API:** `POST /v1/runs/{id}/steer` with body `{"prompt": "..."}`

The server queues the message; the harness delivers it to the agent as a user message at the next step boundary, and the run keeps going. Empty or whitespace-only prompts are rejected client-side before any request is sent.

---

### viz

Print the URL for the `/viz` static visualization UI served by `harnessd`, optionally opening it in the default browser.

```bash
harnesscli viz
harnesscli viz --open
```

---

### acp

Serve the Agent Client Protocol (newline-delimited JSON-RPC 2.0) over stdin/stdout so ACP-compatible editors (Zed, JetBrains via ACP) can drive go-code as a subprocess. This is the same protocol the standalone `harness-acp` binary exposes; `harnesscli acp` is an equivalent entrypoint reached through the main CLI. See the [ACP runbook](https://github.com/dennisonbertram/go-code/blob/main/docs/runbooks/acp.md) for the manual Zed verification checklist.

```bash
harnesscli acp
harnesscli acp -server http://my-harness:9090
```

stdout is a pure protocol channel — all diagnostics go to stderr.

---

### plugin

Manage installable plugin bundles (`plugin.json` bundles under `~/.go-harness/plugins`).

```bash
harnesscli plugin install <path-or-url>
harnesscli plugin list
harnesscli plugin uninstall <name>
harnesscli plugin update <name>
harnesscli plugin trust <name>
harnesscli plugin untrust <name>
harnesscli plugin marketplace <subcommand>
```

Trusted bundles alone reach profiles, MCP validation, and hooks; enabled visibility is independent from executable trust. See `docs/design/plugins.md` for the bundle schema.

---

### mcp

Manage saved credentials for remote MCP servers configured for this CLI.

```bash
harnesscli mcp login <server-name>
harnesscli mcp status <server-name>
harnesscli mcp logout <server-name>
```

---

### hooks

Manage trust for config-driven lifecycle hook files (shell/HTTP hooks, epic #737).

```bash
harnesscli hooks trust <hook-file>
harnesscli hooks revoke <hook-file>
harnesscli hooks list
```

Hook loading itself is read-only and startup-computed; use `GET /v1/hooks` to see what a running `harnessd` actually loaded. See `docs/design/plugins.md` → "Config-driven hooks" for the hook-file schema.

---

### service

Install, manage, and check the status of `harnessd` as an OS-level background service (launchd on macOS, systemd on Linux).

```bash
harnesscli service install --binary /path/to/harnessd --addr 127.0.0.1:8080
harnesscli service start
harnesscli service stop
harnesscli service status
harnesscli service uninstall
```

| Flag (install) | Default | Description |
|---|---|---|
| `--binary` | look up `harnessd` on `PATH` | Path to the `harnessd` binary |
| `--addr` | resolve like `harnessd` — `HARNESS_ADDR` env or `127.0.0.1:8080` | Listen address for harnessd |
| `--log-dir` | `~/.harness/logs` | Directory for service logs |
| `--dry-run` | `false` | Print the rendered unit file and target path without writing anything |

---

## auth login and config files

### auth login
Expand Down Expand Up @@ -277,6 +401,32 @@ On success, `auth login`:

The generated key carries three scopes: `store.ScopeRunsRead`, `store.ScopeRunsWrite`, and `store.ScopeAdmin`.

### auth kimi

Manage Kimi Code subscription auth (epic #848). Reuses a `kimi-code`-authenticated vendor session through a harness-owned credential copy at `~/.harness/subscription-auth/kimi.json`; it never writes under `~/.kimi-code/`.

```bash
kimi-code login # vendor CLI login, done once outside harnesscli
harnesscli auth kimi login
harnesscli auth kimi status
harnesscli auth kimi logout
```

`logout` removes only `~/.harness/subscription-auth/kimi.json`.

### auth codex

Manage Codex subscription auth (epic #847). Reuses a ChatGPT-authenticated vendor Codex session through a harness-owned credential copy at `~/.harness/subscription-auth/codex.json`; it never writes under `~/.codex/` and only reads from it.

```bash
codex login # vendor CLI login, done once outside harnesscli
harnesscli auth codex login
harnesscli auth codex status
harnesscli auth codex logout
```

`logout` removes only `~/.harness/subscription-auth/codex.json`. The `openai` provider (`OPENAI_API_KEY`) remains the primary, unaffected path.

### Config file locations

`harnesscli` uses two separate config files for different purposes:
Expand Down Expand Up @@ -390,12 +540,11 @@ For reference, here are all the server routes that `harnesscli` calls:
| `POST` | `/v1/runs/{id}/continue` | continue |
| `POST` | `/v1/runs/replay` | replay |
| `GET` | `/v1/profiles` | -list-profiles |
| `GET` | `/v1/runs/{id}/input` | ask-user (non-TUI, see note) |
| `POST` | `/v1/runs/{id}/input` | ask-user (non-TUI, see note) |

<Callout type="warning">
`handleAskUserQuestion` — the function that calls `/v1/runs/{id}/input` to handle interactive `run.waiting_for_user` events — is defined in `cmd/harnesscli/askuser.go` and tested independently, but is **not wired** into the non-TUI streaming loop in `main.go`. Interactive question-answering in streaming mode is not yet available outside the TUI.
</Callout>
| `GET` | `/v1/runs/{id}/input` | `input` (reads pending questions) |
| `POST` | `/v1/runs/{id}/input` | `input` (posts answers) |
| `POST` | `/v1/runs/{id}/steer` | steer |
| `POST` | `/v1/runs/{id}/approve` | approve (TUI) |
| `POST` | `/v1/runs/{id}/deny` | deny (TUI) |

---

Expand Down
2 changes: 1 addition & 1 deletion website/docs/concepts/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ This page maps every named component and shows how a prompt travels from your te

The `go-code` shell script (`scripts/go-code.sh`) is the single user-facing entry point. When you run it, it:

1. Detects whether a healthy `harnessd` is already listening on `HARNESS_ADDR` (default `:8080`).
1. Detects whether a healthy `harnessd` is already listening on `HARNESS_ADDR` (default `127.0.0.1:8080`).
2. If not, starts one in the background.
3. Forwards your command to `harnesscli`.
4. On exit, stops the server only if it started it — a pre-existing server is always left running.
Expand Down
Loading
Loading