Repository navigation
feat(agent): scripting parity for agent list/status - #151
Conversation
Close competitor gaps vs claude agents --json: dedicated --json output with normalized state, --all for terminal workers, --cwd filtering, and cwd plumbed through TUI/worker agent records.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e092ca846c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| cwd: std::env::current_dir() | ||
| .ok() | ||
| .map(|p| p.to_string_lossy().into_owned()), |
There was a problem hiding this comment.
Propagate current-directory lookup failures
When the worker starts from a deleted or otherwise inaccessible directory, .ok() silently records no cwd, so the worker launches successfully but is always excluded from agent list --cwd. Propagate the lookup error or use an explicitly logged fallback instead of discarding it.
AGENTS.md reference: AGENTS.md:L60-L63
Useful? React with 👍 / 👎.
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub session_id: Option<String>, | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub cwd: Option<String>, |
There was a problem hiding this comment.
Preserve output in the status scripting view
For a live TUI session with assistant output, status_value_to_record populates AgentRecord.output, but AgentScriptView::from_record drops it because the view has no output field. Consequently n00n agent status <id> --json cannot provide the output that this change's spec and commit message designate as the temporary alternative to a logs command.
Useful? React with 👍 / 👎.
| all, | ||
| cwd, | ||
| } => { | ||
| agent::list_client(json, all, cwd, state_dir)?; |
There was a problem hiding this comment.
Honor the existing global JSON output flag
When an existing caller runs n00n --output-format json agent list, the top-level flag still parses but this dispatch now passes only the new subcommand-local json boolean, which is false, so the command unexpectedly emits text. The parallel status path has the same regression; combine the local flag with cli.output_format so existing scripts receive the new stable JSON shape rather than silently changing formats.
Useful? React with 👍 / 👎.
Let the control plane try TUI before worker so pause returns typed unsupported for live sessions and resume can reach paused-team paths. Drain worker message streams until done when proxying through daemon.
Close competitor gaps vs claude agents --json: dedicated --json output with normalized state, --all for terminal workers, --cwd filtering, and cwd plumbed through TUI/worker agent records.
* feat(openai): split Codex plan into its own provider Codex (ChatGPT Coding Plan) OAuth and the full OpenAI API key flow were both exposed under the `openai` provider, causing identical model IDs to appear with different context windows and auth requirements. Introduce a dedicated `codex` provider so: - Codex plan models (`codex/gpt-5.6-luna`, `codex/gpt-5.3-codex`, etc.) resolve to the 272K plan context and OAuth auth. - Full OpenAI API models (`openai/gpt-5.5`, `openai/gpt-5.6-luna`, etc.) keep their larger context and API-key auth. - Auth commands (`n00n auth login/logout codex`) and the status table treat Codex separately while sharing OAuth state with OpenAI. * chore(changelog): add fragment for Codex provider split
…xd/n00n into feat/agent-scripting-parity
Add changelog fragment, token-profile baseline for 27 tools, and fix missing BackendKind import in agent command unit tests.
* fix(agent-control): route message/resume as steering interrupts, not queued prompts
* fix(agent-control): distinguish control messages and resume paused team runs
- Add `control` flag to `Message`, `AgentInput`, and `QueuedMessage`
so agent-control messages are tagged independently of user prompts.
- Propagate the flag through `AgentEvent::QueueItemConsumed` and the
UI display pipeline, adding `DisplayRole::Control` with its own theme
style so control messages render distinctly from user messages.
- Wire `n00n.session.prompt` `control` option and update `agent_control`
`message`/`resume` to use both `steer` and `control`.
- Surface `last_user` from `SessionRequest::Status` so `agent_control`
resume can retrieve the paused `team` `run_id` from the target session
history and build a continuation prompt.
- Include `run_id` in `team` `run_waves` pause payload for consistency.
Tests: cargo nextest run --workspace (3873 passed), cargo clippy --all --tests -- -D warnings.
* fix(agent-control): harden paused team resume
* feat(agent): add structured output helper and agent CLI command
Phase 2 implementation:
- Create plugins/lib/n00n/structured_output.lua with constants and helper functions for schema validation
- Create plugins/lib/n00n/subagent.lua with unified subagent launch API
- Migrate plugins/task/init.lua to use n00n.structured_output helper
- Migrate plugins/workflow/init.lua to use n00n.structured_output helper
- Add n00n agent run CLI command with stubs for future daemon commands
- Add tests for structured_output helper in plugins/lib/tests/spec.lua
- Keep team/roles.lua unchanged due to custom patterns (hardcoded audience, budget handling, preview support)
All tests pass. Lua syntax validated with luac.
* fix(agent-orchestration): repair route_tier and structured_output helpers
* feat(agent): add background agent server and management CLI
Implement `n00n agent run --background` Unix-socket server plus
`message`, `stop`, and `list` client commands. Background agents keep
state in `state_dir/agents/<id>/agent.json` and a `control.sock`.
Server streams `TextDelta`/`ToolOutput`/`Done` events as NDJSON, and
supports an initial `--prompt` before entering the command loop. The
`--mode` flag selects the same workflow/team/etc modes used by the
one-shot runner.
- Add `AgentMode` CLI enum (General, Research, Task, Team, Workflow)
- Add `async-lock` for per-agent message serialization
- Add unit tests for CLI parsing, state serde, and state listing
Phase 3 stubs (status, pause, resume, policy) remain unimplemented.
* feat(agent): implement status, pause, and resume commands
Add `ClientCommand::Pause` and `Resume` to the background agent
protocol. The server stores a shared `paused` flag and rejects new
messages while paused, updating `agent.json` status accordingly.
Implement `n00n agent status`, `pause`, and `resume` client commands.
`status` reads the persisted agent state; `pause`/`resume` send a
control command over the Unix socket. The `policy` subcommand remains
a stub.
* fix(agent): avoid duplicate text when message run ends with error
The text-mode client was streaming TextDelta chunks and then printing
the full Done.text again when the run ended with an error. Only print
the error in that case; the streamed text is already on the terminal.
* refactor(team): migrate roles and supervisor to n00n.subagent
Extend `plugins/lib/n00n/subagent.lua` with options the team plugin needs:
- `system` override for role-specific prompts
- `preview` and `activity_label` for ActivityPreview integration
- `budget` with `:consume()` for agent-call budgets
- `fail_on_pricing_error` to keep the existing team semantics
- return the resolved `model_spec` as a fifth value
Migrate `plugins/team/roles.lua` to launch subagents through the shared
helper, preserving its `{ok, text, cost, model, usage, error}` return
shape and all existing tests.
Simplify `plugins/team/init.lua` `run_supervisor` by delegating the
structured-output plan generation to `n00n.subagent.launch` with
`output_schema = PLANNER_OUTPUT`.
* fix(agent): log cleanup warnings in stop handler
Surface failed socket/directory removals in the background agent stop
handler instead of silently ignoring them. The process still exits, but
the warning gives operators a signal that stale state may remain.
* fix(agent): validate agent ids and lock down socket permissions
Prevent path traversal from user-supplied agent ids by validating them
before any filesystem operation, and reject ids that contain path
separators, control characters, or other unsafe content. Limit ids to
64 ASCII alphanumeric/hyphen/underscore characters.
Set the agent state directory to 0o700 and the Unix control socket to
0o600 so only the owning user can read or connect to background agent
state.
* refactor(agent): share one-shot and background agent setup
Extract `prepare_agent_env` and `PreparedEnv` to remove the duplicated
plugin/model/MCP initialization between `run` and `server`. Both paths now
build their `HeadlessParams`/`InteractiveParams` from the same prepared
environment, making the two entry points easier to keep in sync.
* docs(changelog): add fragments for agent CLI and team refactor
Record the user-facing additions, refactor, and security hardening from
PR #134 in changelog.d.
* fix(agent): await task completion before stop exits
The background agent Stop command now waits for the agent task to finish
after sending the cancel signal, instead of exiting immediately. This
prevents dropping active tool calls and unfinished MCP sessions and
ensures state is persisted before cleanup.
* refactor(cli): remove unused goal arg and stub policy commands
The `goal` flag on `n00n agent run` was parsed but never used, and the
`policy` subcommands were stubs. Remove both until they are wired to
actual behavior.
* fix(task,workflow): pass thinking config to subagent sessions
Thread the `thinking` option through `n00n.agent.session` in both the
task and workflow plugins, and include it in the workflow journal key so
cached results honor the thinking setting.
* fix(n00n-agent): make yolo enable rather than toggle in spawn_interactive
`headless::spawn_interactive` was calling `permissions.toggle_yolo()` when
`params.yolo` was true, which flipped an already-true yolo state back to
false. This broke `--yolo` in background agent mode (and TUI/ACP when
always_yolo was set). Add `PermissionManager::set_yolo` and use it to
reliably enable yolo when requested, leaving the config-derived default
otherwise.
* feat(agent): add --goal and mode-aware team/workflow/task prompts
Re-add --goal to n00n agent run and wire it into mode-specific prompts.
Team mode now emits a team tool call with goal, mode, max_agents,
waves, and auto-tier flags. Workflow mode emits a workflow tool call
with the prompt as the script and goal merged into inputs. Task mode
emits a task tool call with description/prompt. General/Research
modes prepend the goal to the prompt.
Also introduce AgentRunOptions to keep run/server signatures tidy and
avoid excessive boolean parameters.
* feat(agent): configurable agent-call limits and runaway guard
Remove the hard-coded 24/32 agent-call ceilings in team and workflow.
- team `max_agents` is now configurable with no hard maximum and an
optional `timeout_secs`.
- workflow `max_agents_per_run` is configurable with no hard maximum.
- Add `plugins/lib/n00n/guard.lua` to enforce call budgets, wall-clock
timeouts, repeated-prompt loops, and consecutive subagent errors.
- Wire the guard through `n00n.subagent.launch`, `team`, and `workflow`.
* fix(guard): reserve call budget atomically in check and guard consecutive errors
- Move the used counter increment from record() into check() so the
budget is reserved before any async yield; this prevents concurrent
subagent calls from overshooting max_calls.
- Check consecutive_errors in check() and use > in record() so the
guard blocks the next call after the threshold instead of wasting
a token on the failing call.
- Add lib spec coverage for budget, repeated-prompt, consecutive-error,
and legacy consume() behavior.
* docs: regenerate after main merge
* docs: regenerate lua-api from branch sources
* feat(daemon): add n00n-daemon control plane and scoped agent tools
Introduce an on-device registry/proxy over TUI session APIs and PR#134
worker socks, with sonic-rs NDJSON wire encoding, a thin `n00n agent`
CLI, and split agent_list/agent_status/agent_control tools that render
human cards and tooned structured output instead of raw JSON dumps.
* feat(daemon): register live TUI sessions on daemon.sock
Wire PluginHost ui_action_tx into an in-process daemon listener so CLI
agent list unions TUI sessions while the UI is up. Document stacked
follow-ups for worker absorb (#134) and steer/control (#129).
* docs(daemon): mark #129/#134 absorb status in followups plan
* feat(daemon): add lockfile transport, peercred auth, and headless registration
Advertise daemon listeners via daemon.lock, route clients through UDS or
Windows loopback TCP, reject mismatched UDS peers on Linux, and register
print/ACP sessions for remote list/status/control.
* test(daemon): add smoke gate, integration tests, and --state-dir
Automate manual verification with scripts/smoke-daemon.sh, cover worker
pause roundtrip and TUI+worker UDS list, fall back to disk when daemon
sock is absent, and add --state-dir to agent control verbs.
* feat(daemon): complete sprint 2 polish for agent control
Add stale daemon.lock recovery, TUI paused-team resume via daemon,
agent_control Lua spec tests with shared helpers, Windows TCP smoke
test, and user docs for the n00n agent CLI.
* fix(daemon): route pause/resume via backend resolution
Let the control plane try TUI before worker so pause returns typed
unsupported for live sessions and resume can reach paused-team paths.
Drain worker message streams until done when proxying through daemon.
* fix(token-profile): update cold_start baseline for new tool
* feat(agent): scripting parity for agent list/status (#151)
* feat(agent): add scripting parity for agent list and status
Close competitor gaps vs claude agents --json: dedicated --json output
with normalized state, --all for terminal workers, --cwd filtering,
and cwd plumbed through TUI/worker agent records.
* fix(daemon): route pause/resume via backend resolution
Let the control plane try TUI before worker so pause returns typed
unsupported for live sessions and resume can reach paused-team paths.
Drain worker message streams until done when proxying through daemon.
* feat(agent): add scripting parity for agent list and status
Close competitor gaps vs claude agents --json: dedicated --json output
with normalized state, --all for terminal workers, --cwd filtering,
and cwd plumbed through TUI/worker agent records.
* feat(openai): split Codex plan into its own provider (#136)
* feat(openai): split Codex plan into its own provider
Codex (ChatGPT Coding Plan) OAuth and the full OpenAI API key flow were
both exposed under the `openai` provider, causing identical model IDs to
appear with different context windows and auth requirements. Introduce a
dedicated `codex` provider so:
- Codex plan models (`codex/gpt-5.6-luna`, `codex/gpt-5.3-codex`, etc.)
resolve to the 272K plan context and OAuth auth.
- Full OpenAI API models (`openai/gpt-5.5`, `openai/gpt-5.6-luna`, etc.)
keep their larger context and API-key auth.
- Auth commands (`n00n auth login/logout codex`) and the status table
treat Codex separately while sharing OAuth state with OpenAI.
* chore(changelog): add fragment for Codex provider split
* fix(agent): import BackendKind in tests and sync CI artifacts
Add changelog fragment, token-profile baseline for 27 tools, and fix
missing BackendKind import in agent command unit tests.
Summary
Researched competitor agent-control surfaces (Claude Code
claude agents --json, OpenCode HTTP session API, Cursoragent ls) and closed the highest-value scripting gaps on top of #149:n00n agent list --json/status --jsonemit a stableAgentScriptViewschema (not raw daemon NDJSON)statefield (working,needs_input,idle,running,stopped,done,failed, …)--allincludes stopped/completed background workers (default hides terminal worker rows)--cwdfilters by session working directorycwdonAgentRecord, plumbed from TUI live/status and workeragent.jsonSpec:
specs/006-agent-scripting-parity/spec.mddocuments the gap analysis and deferred items (agent view TUI, attach/respawn, HTTP API, push events).Deferred (not in this PR)
n00n agentdashboard (Claudeclaude agents/ requestedopencode agents)attach/respawncommandslogstail command (usestatus --jsonoutputfor now)Test plan
./scripts/smoke-daemon.shcargo test -p n00n-daemon scriptingcargo test -p n00n --bins -- agent::filter_n00n agent list --json --state-dir /tmp/...smoke in script