docs: add Sessions, Code Mode, headless/CI guide, and expand the CLI reference#3694
Open
aheritier wants to merge 14 commits into
Open
docs: add Sessions, Code Mode, headless/CI guide, and expand the CLI reference#3694aheritier wants to merge 14 commits into
aheritier wants to merge 14 commits into
Conversation
Documents session storage (session.db, --session-db), resuming (--session with relative offsets or explicit IDs), read-only sessions, the TUI session browser and tabs (restore_tabs), session titles (title_model, docker agent debug title), and usage/cost tracking (track_usage, cost:, and the fact that cumulative cost never decreases across compaction). Wires the page into nav.yml under Features.
Documents code_mode_tools: running an agent's tools as JavaScript functions callable from a single run_tools_with_javascript tool instead of one model round-trip per tool call, when it helps, its script/security limits, and its coarser tool-approval granularity (individual tool calls made from inside the script bypass the runtime's normal per-call permission checks). Fixes an inaccurate one-liner for code_mode_tools in the Agent Config reference (it did not format tool responses; it swaps in the JS-orchestration tool) and turns it into a cross-link. Wires the page into nav.yml under Features.
Adds command sections for `docker agent getting-started` (the interactive tour), `docker agent debug` and its subcommands (config, toolsets, skills, title, auth, oauth list/remove/login) as a user-facing troubleshooting surface, and `completion`. Adds a Self-update subsection cross-linking the existing installation-guide walkthrough. Documents --remote's mutual exclusivity with the other local-only run flags (session, session-db, sandbox, worktree*, record, fake) as a normal flag, per maintainer decision. Verified every flag against cmd/root/*.go on current main; the run flag table already covered every other visible, non-hidden flag.
Task-oriented guide covering --exec basics, structured output for machines (--json vs. structured_output), reacting to events via --on-event, choosing an auto-approval strategy for unattended runs (--yolo vs. permission allow-lists vs. the safe-auto shell policy, with a security warning against blanket --yolo in CI), providing secrets and disabling telemetry in CI (cross-linked to their existing guides), and a GitHub Actions example that pulls an agent from an OCI reference and gates its shell tool calls with a pre_tool_use hook. Wires the page into nav.yml under Resources > Guides, after Go SDK.
…uidance Local review of docs/p1-coverage flagged the headless guide as the most broken page in the branch: - --on-event silently no-ops under --exec: handleExecMode() returns before event hooks are parsed/installed (cmd/root/run.go), so the --exec examples never fired. Move the flag to the interactive/--lean path where hooks actually install, and show the --json/jq equivalent for a real --exec run. - turn_end is not a real event type (pkg/runtime/event.go only emits stream_stopped on normal completion); message.delta doesn't exist either (assistant output is agent_choice). Fixed both. - The 'hooks may finish after process exit' claim is unsafe: main.go calls os.Exit without waiting on any detached hook goroutine. Corrected to say hooks are killed with the process. - The permission example used a top-level toolsets: sequence with no agents: block, which the schema rejects (toolsets is a named map at that level; at least one agent is required). Rebuilt as a valid, load-tested config. - The GitHub Actions example's prefix-only bash regex claimed a read-only policy but let destructive suffixes through (e.g. 'ls; rm -rf /tmp'), and claimed a CLI pre_tool_use hook overrides a pulled config 'regardless of what it declares' -- false: an explicit permission allow/deny returns before pre_tool_use hooks run (dispatcher.go), and CLI hooks are appended after config hooks, not layered on top of them (pkg/config/hooks.go). Replaced the hand-rolled regex with the runtime's own permissions + safer_shell(safe-auto) mechanism, corrected the precedence explanation, and added an honest caveat that command-string matching is not a sandbox. - 'safer: true' only registers the safer_shell classifier; the policy it enforces still defaults to strict (asks on everything) unless safe-auto is explicitly pinned via a pre_tool_use hook entry (pkg/hooks/builtins/safer_shell.go, safer_shell_test.go). Documented the required pin alongside a new, load-tested example. Adds examples/ci_safe_permissions.yaml (validated with 'docker agent run --dry-run') backing both the auto-approval section and the GitHub Actions example, and lists it in examples/README.md.
- debug auth --json emits the full, unredacted JWT in its token field (cmd/root/debug_auth.go); only the text output truncates it. Add an explicit warning against pasting --json output into logs or bug reports. - --mcp-oauth-redirect-uri (added by addRuntimeConfigFlags, cmd/root/flags.go) was missing from the runtime-flags table. - --listen on 'docker agent run' is registered as MarkHidden (run.go) but was documented as an ordinary user-facing flag; note its hidden status, matching the treatment already given to the 'debug' command. - --session's relative-ref description said '-1 = last'; sessions are ordered by creation time (ORDER BY created_at DESC in pkg/session/store.go, see store_test.go), not last-used, so resuming an older session never makes it the new -1. Reworded for accuracy.
- Relative session refs (-1, -2, ...) are creation-ordered, not
most-recently-used: GetSessionSummaries ORDER BY created_at DESC
(pkg/session/store.go, store_test.go). Resuming an older conversation
does not make it the new -1; the next -1 still resolves to whichever
session was created most recently. Reworded throughout.
- Automatic title generation is seeded with exactly the first message
(pkg/app/app.go, generateTitle(ctx, []string{message})), not 'the
first message or two', and the generator does not truncate to two
messages (pkg/sessiontitle/generator.go). Manual /title regeneration
does send every user message so far -- documented that distinction.
The page recommended keeping a destructive toolset on a separate, non-Code-Mode agent 'reached via handoff or transfer_task'. Both tools are handlerless (pkg/tools/builtin/handoff, .../transfertask) and, once wrapped by Code Mode, are rejected at call time with 'tool "handoff" is not available in code mode' (pkg/tools/codemode/exec.go, pkg/teamloader/teamloader.go). A Code-Mode agent cannot delegate to another agent at all, so the advice was unworkable. Replaced it with the actual limitation: keep code_mode_tools off any agent that needs to hand off or transfer to one holding a destructive toolset. The core, already-correct claim that nested Code Mode calls bypass per-call permission dispatch is unchanged.
The headless guide's CI security section previously centered on a "safe-auto" shell auto-approval recipe (safer: true + a pinned safer_shell hook) that never actually worked from the CLI: - safer: true auto-injects its own argument-less safer_shell hook (defaults to strict); hook dedup keys on (type, command, args), so it never collapses with an explicitly pinned safer_shell entry and both run (pkg/hooks/builtins/builtins.go:203-216, pkg/hooks/executor.go:275-308). Aggregation then keeps the more restrictive verdict, so the pinned hook's allow never wins over the auto-injected hook's ask (executor.go:484-490,567-592), and the call is rejected outright under --exec --json (pkg/cli/runner.go:117-126). - Even a hook that does return allow only bypasses the pipeline when the session's SafetyPolicy is already safe-auto, a field the HTTP API accepts on session create but that docker agent run has no flag for (pkg/session/session.go, pkg/api/types.go) - --yolo maps to unsafe, everything else defaults to strict. - The GitHub Actions example's docker:github-official MCP toolset had no GITHUB_PERSONAL_ACCESS_TOKEN (pkg/config/gather.go:253-285, pkg/gateway/catalog_test.go) and, being unnamed, exposed tools under raw names like get_file_contents rather than the mcp:github:* prefix its own permission patterns assumed (pkg/tools/mcp/mcp.go: 46-93,617-652). - --worktree was listed alongside --sandbox as a hard boundary; it only isolates checkout/branch state, the shell toolset still runs as a native host process either way (pkg/tools/builtin/shell/ shell.go:187-195, pkg/worktree/worktree.go). - os.Exit was said to terminate in-flight --on-event hooks with the process; hooks run via exec.CommandContext(context.Background(), ...) and main.go exits without waiting on or signaling them (cmd/root/run_event_hooks.go:77-90, main.go:15-26), so a hook can outlive the process. Rework the section around an honest isolation model instead: --sandbox (docs/configuration/sandbox/index.md) is now presented as the actual isolation boundary for untrusted/autonomous CI agents, paired with --yolo when the blast radius needs containing. Permission allow-lists and safer_shell are demoted to defense-in-depth, with an explicit, verified example of how unspaced compound operators (cmd;evil, cmd&&evil, cmd|evil) bypass safer_shell's safe-list (safer_shell.go:248-275,391-418: containsShellSeparator only catches space-padded operators, and a bare "..." wildcard pattern like "echo ..." then swallows the unspaced tail). The broken safe-auto recipe and the unauthenticated GitHub MCP toolset are removed; the GitHub Actions example now uses --yolo (justified qualitatively, since a fixed shell allow-list can't cover an open-ended review task) with a callout to add --sandbox where the runner supports it. examples/ci_safe_permissions.yaml demonstrated exactly the broken safe-auto recipe, so it is deleted rather than patched, along with its examples/README.md entry.
Code Mode (docs/features/code-mode/index.md): a configured force_handoff target still runs deterministically after a natural stop regardless of Code Mode (pkg/runtime/loop.go:937-951) - it's applied by the runtime outside the tool-call dispatch path Code Mode replaces. Only the model *choosing* to call handoff/transfer_task as a tool fails in Code Mode; qualify the 'cannot delegate at all' claim accordingly. Sessions (docs/features/sessions/index.md): automatic session-title generation calls the title model directly (pkg/app/app.go:502-505, pkg/sessiontitle/generator.go:134-165) without going through the usage-tracking call sites (pkg/runtime/harness.go, pkg/runtime/streaming.go), so it is not folded into the session's cumulative cost/token totals. Clarify that 'every model call' means every tracked conversation/compaction call.
The sandbox VM matching the current workspace/mount set is retained and reused across runs (pkg/sandbox/sandbox.go Ensure(); docs configuration/sandbox/index.md #how-it-works), not torn down after each session as "disposable" implied. Also document that --sandbox already injects --yolo by default unless the user passes their own --yolo flag (cmd/root/sandbox.go dockerAgentArgs; sandbox_test.go), so the earlier example already runs unattended and --yolo=false is the opt-out. Drop the redundant paired-flag example that implied --yolo was required alongside --sandbox.
…attern
actions/checkout@v4 defaults `persist-credentials: true` and its
token defaults to `${{ github.token }}`, so write capability tracked
the repo's default GITHUB_TOKEN permissions rather than the "no
repo-write token" claim made just below the example. Add a top-level
`permissions: contents: read` block and `persist-credentials: false`
on the checkout step so the job provably never holds or persists a
write-capable token, and update the surrounding prose to match.
Also fix the optional GitHub MCP paragraph: MCP tool names are
`<toolsetname>_<tool>` with an underscore, not the colon-style
`mcp:github:get_*` this page still showed (pkg/tools/mcp/mcp.go
Tools(); mcp_test.go). A toolset named `github` exposes
`github_get_issue`, not `mcp:github:get_issue`.
…ames Same colon-style tool-name syntax as the headless guide's GitHub MCP paragraph pre-existed here. MCP tool names are `<toolsetname>_<tool>` with an underscore (pkg/tools/mcp/mcp.go Tools(); mcp_test.go), so a toolset named `github` exposes `github_get_*`/`github_list_*`/etc., never `mcp:github:*`. Fix only the pattern syntax in the examples; the rest of the page is unchanged.
The "Cost never decreases" callout still said the total updates "after every model call" and that /cost reflects "everything the session has actually spent" -- contradicting the corrected line just above it. Automatic title generation calls the model directly outside session usage accounting (pkg/app/app.go Run(); pkg/sessiontitle/ generator.go generateOnce()), so it isn't part of that total. Qualify the callout to the same tracked conversation/compaction calls, and call out that title-generation calls are excluded.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Second wave of end-user documentation (P1), closing feature-coverage gaps:
docs/features/sessions/index.md): session storage (--session-db), resuming (--session <id>, creation-ordered relative offsets like-1), read-only sessions,/sessionsbrowsing and tabs, session titles, and usage/cost tracking.docs/features/code-mode/index.md): what Code Mode is (code_mode_tools), when it helps, an example, and its security characteristics (nested tool calls bypass per-call permission dispatch; model-invokedhandoff/transfer_taskare unavailable in Code Mode while configuredforce_handoffstill runs).docs/guides/headless/index.md):--exec,--json/ structured output,--on-event, and a CI walkthrough built around--sandboxas the real isolation boundary (shell allow-listing is presented only as caveated defense-in-depth, not a security boundary), with a hardened GitHub Actions example.docs/features/cli/index.md): documentsdocker agent getting-started,docker agent debug(+ subcommands),completion, opt-in self-update, and the--remoteflag; marks--listenas hidden to match the code.<toolset>_<tool>form indocs/configuration/permissions/index.md;code_mode_toolscross-link in the Agents page; nav entries for the new pages indocs/data/nav.yml.Notes
Docs-only. Every flag/field/command/subcommand was verified against current
mainsource, and the CI guidance in particular was reworked and re-reviewed to avoid overstating what shell allow-listing provides. Passes the canonical front-matter check and markdownlint; branch is based on currentmain.