Conversation
Add support for Cmux backends (Claude and Codex) by updating the backend availability checks to include BackendClaudeCmux and BackendCodexCmux cases. These backends require the same dependencies as their CLI counterparts (tsx for Claude, codex for Codex). Also add a new embedded file system for prompt examples.
Add Claude cmux and Codex cmux backend options to the CLI configuration. Expands the available backend choices for users to include the new cmux variants for both Claude and Codex AI providers.
Remove the SpecRuntimeEditor component from the RuntimeControls section of the PromptWorkbench. This component is no longer needed in the UI flow and simplifies the runtime configuration interface. BREAKING CHANGE: The SpecRuntimeEditor is removed from the prompt workbench interface, which may affect users who relied on inline spec editing in the runtime controls. Gavel-Issue-Id: a182d83e3bf3d7f8296f147b41241c6b Claude-Session-Id: 2e0945b6-c54c-4a18-9d99-745d0b03cc18
Replace the fixed 150ms settle between paste and Enter with an adaptive wait (waitForPasteLanded) that polls the surface until the pasted text renders and holds stable for the settle window. A slow claude/codex input event queue no longer gets Enter pressed into a half-filled buffer, which was a no-op that left the prompt typed but unsent. Route the codex initial prompt through submitAndConfirm so a dropped Enter is recovered by re-pressing, closing the silent no-submit where waitForScreenIdle would otherwise settle on the typed-but-unsent screen and report success. Bump default settle/quiesce to 2s; add PasteLandTimeout (60s) and PasteLandPollInterval (500ms) config knobs with accessors.
Model the interactive claude/codex CLI flag surface as typed option
structs (ClaudeCmuxOptions/CodexCmuxOptions) with clicky struct tags,
covering only flags with no api.Spec home ("extra cmux args"). Flags that
map to the spec (model, effort, permission mode, allow/deny tools, memory
toggles) are emitted by the cmux provider from the spec instead.
- api: add CLIOptionsFor + shared CodexSafety (also used by the codex
app-server), Spec.CLIArgs carrier, and PermissionDontAsk
- cmux: build the launch command from the option structs; emit the
previously-dropped --effort/--bare/--disable-slash-commands/
--setting-sources and codex --sandbox/--ask-for-approval
- serve: GET /api/captain/ai/cli-options/catalog via rpc.SchemaForStruct
- webapp: render an Extra CLI args JsonSchemaForm for cmux backends
Gavel-Issue-Id: 14871f369705ce4d3cb65f6e307f7bff Claude-Session-Id: cb8eb1dc-3978-4640-88f0-d0161f7516f5
… schemas Add api.Prompt.SchemaJSON (a verbatim JSON Schema carrier) and ai.SchemaJSONFor so structured output works from either a reflected Go struct or a pre-built schema. genkit/claude-agent/codex prefer SchemaJSON (lossless, no JSONSchema round-trip); cmux honors it via internal schema-in-prompt + reply extraction. dotprompt Render marshals frontmatter output.schema onto SchemaJSON. Move SchemaInstruction + tolerant ParseStructured + JSON extractors into pkg/ai/parse.go and dedupe provider/json_utils onto them. Add middleware.NewProvider/NewAgent for batteries-included construction (logging always, cache when configured). Committed on the feat/captain-subagent-history branch, so it also carries in-progress subagent-history work in shared files.
Add pkg/session, a canonical session aggregate (agent hierarchy, cost, changed files, plan events, approvals) built from a new claude.ParseSessions seam. Messages extend the AI SDK v6 UIMessage/UIPart shape with a transcript provenance extension; cost is the canonical api.Cost (converters from TokenSummary/ModelUsage). Fix history review findings: - delete the dead --debug flag (never read) - report unhandled stream types on every history path, not just --file - Warn instead of silently skipping unreadable transcripts - ParseError rows inherit the surrounding timestamp so the row limit no longer discards them first - --cost computes per-row cost regardless of Codex scope (was blank unless --claude was passed) Build the multi-level agent hierarchy from parentUuid (grandchildren were previously flattened onto the root session).
Project the unified Session into the two consumer shapes: the Vercel AI SDK v6 chat form (ToUIMessages — merges tool_result into its originating call and attaches usage/cost as ChatMessageMetadata) and the JSONL-replay row form (ToReplayEntries — one message entry plus one tool entry per call, with the result merged in as Response) that the session viewer and clicky-ui consume.
The un-parseable-line-type diagnostic now routes through the cli logger at Warn so it is suppressible via log level while still visible by default, instead of unconditional stderr.
Now that unhandled stream types are reported on every history path, the diagnostic was dominated by benign operational lines. Classify mode, bridge-session, progress, and queue-operation as known state-tracking types (no unique row content — the real content surfaces via actual messages), and handle pr-link as a PrLink synthetic row carrying the PR number/url/repo (workflow state, not noise). The unhandled-types Warn now lists only the content-bearing system/* subtypes that still need real handling.
Handle system/compact_boundary, system/local_command, system/scheduled_task_fire, and system/informational as synthetic rows carrying their content (and compaction metadata / level where present), instead of dropping them as unhandled. Combined with the earlier operational classification, the unhandled-stream-types diagnostic is now clean across real session history — every line type is either surfaced or a known no-op.
Move the clicky table/pretty provider — the ScanResultRow/ScanResultRowSingle rows (Columns/Row/RowDetail/MarshalJSON), the HistoryResult(All) views, the row-detail/cost-section builders, and the token/cost formatters — out of pkg/cli into pkg/session/render.go, so the unified model owns its rendering. history.go now constructs session.ScanResultRow and calls session.BuildRowDetail/RowCost via a RowOptions struct (decoupled from the CLI flag struct). Pure relocation: cost/summary/history callers use session.FormatTokens/FormatCost; behavior and output are unchanged (all existing history/marshal/detail tests pass, now colocated in pkg/session).
Add session_cache.go: an mtime/size-keyed summary cache (summarizeSessionRefs / summarizeSessionFileCached) so repeated session-list/live scans skip re-parsing unchanged transcripts. sessions.go and session_live_summary.go build sessionFileRefs and go through it. Register sessions list/live/get with AddNamedCommandWithContext and thread context.Context into RunSessionList/Get/Live (and discoverSessionCandidates), instrumenting the find/parse/enrich phases via rpchttp.Track so the serve endpoint reports Server-Timing. serve.go wraps the mux in rpchttp.TimingMiddleware and passes r.Context() to RunSessionLive. Removes the now-unused eager summarizeClaudeSessionFile in favor of the bounded fast path.
Add GET /api/captain/sessions/{id}, returning the same *session.Session the CLI
`sessions get` produces, so the web UI can render the unified model (agent
hierarchy, cost, changed files, plan, approvals, message stream with
provenance). Go's mux prefers the exact /sessions/live over the /{id} wildcard,
so the live route is unaffected.
Replace the in-memory summary sync.Map with a persistent store (commons-db embedded postgres, or an external CAPTAIN_SESSION_DB_URL) so one-shot CLI runs reuse summaries across invocations. One row per transcript — sub-agents are their own rows linked to a parent — invalidated by the file's mtime+size, with the rich unified-model metadata (git, provider, cost/usage, changed files, plan/approvals, context occupancy, counts, prompt refs) stored as jsonb. - pkg/session/rows.go: Row + Rows(ParsedSession) per-transcript projection and RowsFromFile/CodexRow single-file builders; claude.ParseTranscript parses one transcript (root prefers the in-file sessionId). - pkg/cli/session_store.go: StoredSession/StoredPrompt/ProviderInfo models, lazy sync.Once open (external DSN | embedded shared daemon under ~/Library/Caches captain/session-db, reused via postmaster.pid), upsert/ lookup helpers. Unavailable DB degrades to uncached with one Warn (user-approved). CAPTAIN_SESSION_DB_URL=off disables it. - session_cache.go boundary hits the store; prompt-run persists the realized prompt (captain-launched sessions) linked by session id; sessions get attaches it and populates the parent-linked rows. Retires the fast sampled summarizers. Verified end-to-end: cold run boots the daemon and persists rich rows (~9s), warm run reuses the postmaster + rows (~0.5s). TestMain disables the store so unit tests stay fast; the DB round-trip is a gated integration test.
`captain prompt run|render` now accepts a registry id, a .prompt filepath, --prompt/-p text, or stdin (positional made optional via WithOptionalID), with the full ai-prompt flag surface (--system, --allowed-tools, --permission-mode, --no-stream, …). The run action runs synchronously on the CLI (prints the answer + tokens/cost) and asynchronously over HTTP (returns a run handle for the web UI's SSE), branching on clickyrpc.RequestFromContext; one PromptRunResult carries both shapes. A shared prompt_source.go core (loadPromptContent + renderLoadedContent + actionFlagsToOptions) backs render/run and the alias. The HTTP path keeps the rich api.Spec overlay (overlayRuntimeSpec) as a transport adapter; the CLI path uses overlayCLI. `captain ai prompt` stays as a thin deprecated alias routed through the same core. Verified: run -p executes synchronously; render from file/-p/stdin; a missing .prompt path errors clearly (not "invalid id"); ai prompt warns and still works.
loadPromptContent now emits a debug line identifying the resolved prompt source — file path, registry id + kind, inline --prompt/-p, or stdin — so a run/render's source resolution is observable with -v.
Two paths now surface a "did you mean" for a mistyped model: - InferBackend failures (no explicit backend) carry the closest catalog names via ai.NewProvider, using a new api.ErrInferBackend sentinel + fuzzy match over catalog base names. - buildProvider pre-flight (any backend, incl. a configured one) warns when the model is an edit-distance ≤ 2 match to a catalog model — catching e.g. "claud-sonnet-4" → "claude-sonnet-5". Non-blocking (an unrecognized model may still be a valid provider/OpenRouter id), and quiet on exact or far-off names.
finalizeRenderResult runs the same catalog typo check as the execute path, so `captain prompt render -m claud-opus-4-8` surfaces the 'did you mean claude-opus-4-8?' hint without needing to run the model.
Model typo detection now falls through to the OpenRouter pricing registry after the catalog misses, so a mistyped registry-only model (e.g. 'gpt-4o-minii' -> 'gpt-4o-mini') is caught too. Adds pricing.Contains for strict membership (GetModelInfo prices any claude-ish id via the static table, so it can't gate a typo). Catalog is still checked first, so valid catalog models never load the registry.
The `sessions get` action (/api/v1/sessions/{id}) now returns the unified
session.Session; the webapp detail view was still decoding the old
SessionRecord.entries (SessionEntry[]), which no longer exists on the response.
Switch fetchSession to a UnifiedSession type and feed session.messages
(SessionUIMessage[]) to the clicky-ui SessionViewer; remap SessionHeader to the
unified fields (tool count + message count derived from messages, cost summed
from the per-bucket cost object). The prompt-run live SSE view still uses the
legacy SessionEntry path (converged separately).
Depends on the clicky-ui SessionViewer accepting SessionUIMessage[] (local
source via the link: dep; lands separately).
Converge the live prompt-run stream onto the same unified model as the session detail view, retiring the legacy SessionEntry shape: - promptEventAccumulator maps ai.Events to session.Message frames (text → text part, thinking → reasoning part, tool call → dynamic-tool part with the result merged into output/state), keyed by a stable message id. - runStream + the SSE handler carry session.Message frames. - webapp usePromptRunStream accumulates SessionUIMessage[] (keyed by id) and PromptRunStream feeds it to SessionViewer. - remove the now-dead SessionEntryWire/*Wire types and the webapp's SessionRecord.entries; both consumers are on the unified model. Go build/tests/lint + webapp tsc/vite build all green.
Add a serializable api.Spec.Workflow (verify commands/scope/maxIterations + finalize) and route the streaming prompt-run through agent.Runner built from it. Verify commands re-run on failure with feedback up to maxIterations; a body-less spec verifies only (skipping generation); schema validation now runs on the streaming path so an empty/unparseable response fails under SchemaStrictness=error.
Replace the inline RuntimeControls + variables form with clicky-ui's PromptRunEditor and collapse RuntimeForm's parallel spec fields onto a single runtime.spec, removing the dual-write and ~350 lines of duplicated controls/helpers.
Extract PluginsForWorkflow / MaxIterationsForWorkflow / ScopeForWorkflow into pkg/ai/agent/verify so captain prompt-run and gavel build the generate→verify loop identically from an api.Spec.Workflow. prompt-run now delegates to them.
Replace SetupPlugin/VerifyPlugin/FinalizePlugin/Verdict with PreRun/Verify/PostRun/Output[T] hooks over a HookContext, driven by a generic Runner[T]. Verify hooks return {Valid, Retry, Output} — a failing hook supplies the exact next request (feedback baked in). Add api.Workspace (cwd/git/changed/commits/plan) nested on api.Response, reconciling the old run-context + worktree.Result. Migrate the worktree (PreRun+PostRun), CmdVerifier/LLMJudge (Verify), captain ai agent + prompt-run, and their tests.
Verify drops Gavel (superseded by Fixture, a clicky-FixtureEditor markdown string captain declares/reflects but does not execute) and gains a doc comment clarifying Commands as the only part of Verify captain itself runs. Finalize is renamed to PostRun (matching the pkg/ai/agent hook name it serializes). Workflow gains an optional Output.SchemaJSON declaring the run's typed result schema. Spec.IsVerifyOnly and Workflow.Validate are unaffected.
pkg/ai/agent/worktree.Plugin no longer shells out to pkg/git; it wraps the wt (worktrunk.dev) CLI via clicky/exec, the same pattern agent/verify.CmdVerifier uses. PreRun runs 'wt switch --create' and points Workspace at the new worktree; PostRun decides whether to 'wt merge' and/or 'wt remove' via two new enums, WorktreeMerge and WorktreeCleanup (always/never/onSuccess|onMerge/onVerify), replacing the old CommitMsg string and KeepOnExit bool. HookContext gains Verified/Failed so PostRun hooks can read the run's outcome; Runner.Run sets both right before invoking PostRun hooks. pkg/cli/ai_agent.go's --commit flag now maps to Merge=OnSuccess + Cleanup=OnMerge; --worktree alone defaults to Cleanup=OnVerify instead of always discarding uncommitted worktree changes. commitSubject is removed (wt merge has no --message flag; it derives its own commit message).
Add model fallback support with CSV expansion and schema strictness validation. Models can now specify fallback alternatives that inherit primary's temperature/effort/noCache settings. Introduces SchemaStrictness enum for JSON schema validation control (none/warning/error/retry). Also updates clicky and commons-db dependencies, removes local dev replace directives.
|
Important Review skippedToo many files! This PR contains 250 files, which is 100 over the limit of 150. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (150)
📒 Files selected for processing (250)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
| m = m.ExpandCSV() | ||
| primary := m | ||
| primary.Fallbacks = nil | ||
| out := make([]Model, 0, 1+len(m.Fallbacks)) |
| } | ||
|
|
||
| func addMCPServers(out map[string]api.PermissionCatalogItem, path, source string) { | ||
| data, err := os.ReadFile(path) |
|
|
||
| func addWorkspaceSkills(out map[string]api.PermissionCatalogItem, dir string) { | ||
| path := filepath.Join(dir, ".skills") | ||
| if info, err := os.Stat(path); err == nil && info.IsDir() { |
| if len(overlay) == 0 { | ||
| return base | ||
| } | ||
| out := make(map[string]string, len(base)+len(overlay)) |
| if len(overlay) == 0 { | ||
| return base | ||
| } | ||
| out := make(map[string]string, len(base)+len(overlay)) |
| if len(overlay) == 0 { | ||
| return base | ||
| } | ||
| out := make(map[string]api.ToolMode, len(base)+len(overlay)) |
| if len(overlay) == 0 { | ||
| return base | ||
| } | ||
| seen := make(map[api.Preset]bool, len(base)+len(overlay)) |
| if len(overlay) == 0 { | ||
| return base | ||
| } | ||
| seen := make(map[api.Preset]bool, len(base)+len(overlay)) |
| return base | ||
| } | ||
| seen := make(map[api.Preset]bool, len(base)+len(overlay)) | ||
| out := make([]api.Preset, 0, len(base)+len(overlay)) |
| return base | ||
| } | ||
| seen := make(map[api.Preset]bool, len(base)+len(overlay)) | ||
| out := make([]api.Preset, 0, len(base)+len(overlay)) |
The webapp pinned @types/react@^18 while its linked dependency @flanksource/clicky-ui (and gavel) resolve @types/react@19, causing tsc -b to fail with ~23 TS2786 'cannot be used as a JSX component' errors (React 19's ReactNode adds bigint). Bump @types/react to 19.2.17 and @types/react-dom to 19.2.3 (matching the versions clicky-ui resolves), and pin them via a pnpm.overrides block so only one version resolves across the workspace. No webapp source changes were required - the type bump alone fixed all TS2786 errors, and vite build still succeeds.
Add support for model fallbacks and schema validation. Implements fallbackProvider to try candidate models in order on retryable failures, with lazy provider construction and streaming support. Adds SchemaStrictness to PromptRequest for runtime validation policies (warning/error/retry). Extracts IsRetryable into pkg/ai for reuse across fallback and retry middleware. Supports templated frontmatter in .prompt files for parametrizing output schema constraints like maxItems. Updates logging to render pre-built SchemaJSON verbatim, preserving full JSON Schema vocabulary.
…l session store Add model fallback support and refactor prompt schema infrastructure. Key changes: - Model CSV expansion now populates fallbacks (e.g. "claude,gpt-4o" → Model.Name="claude", Fallbacks=["gpt-4o"]) - CLI --fallback flags override frontmatter fallback lists with precedence - Replace monolithic cli_options_catalog with modular prompt_schema (spec/prompt/action/backends/models/examples) - Move latestCodexPlan logic to pkg/session.CodexPlanFromToolUses - Add gavel session store support (env GAVEL_GITHUB_CACHE_DSN) with embedded postgres fallback - Store inline plans in StoredSession.Plan as JSON blob - Frontend: add defaultToolMode, normalize ToolMeta.defaultMode → defaultPermission, improve formatting - Cached adapter probing (60s TTL) feeds both whoami and prompt --schema
A stray `pkg/cli/webapp/dist/` line in .gitignore re-ignored the directory after the negations meant to keep it tracked (last-match-wins), so nothing under dist was committed. CI's `go build ./cmd/captain` then failed with `pattern all:webapp/dist: no matching files found`, which also broke the test and lint jobs' compilation. The webapp can't be built in CI or the goreleaser release job (its vite build uses a local clicky-ui link: dependency), so the built dist must be committed and embedded. Remove the contradictory ignore line, commit the production dist, and correct serve.go's stale comment.
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 34631439 | Triggered | Generic High Entropy Secret | 599a300 | pkg/cli/webapp/dist/assets/index-BGAlGVCv.js | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secret safely. Learn here the best practices.
- Revoke and rotate this secret.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
No description provided.