Skip to content

feat(agent): scripting parity for agent list/status - #151

Merged
w0wl0lxd merged 6 commits into
feat/n00n-daemon-session-bridgefrom
feat/agent-scripting-parity
Jul 27, 2026
Merged

w0wl0lxd merged 6 commits into
feat/n00n-daemon-session-bridgefrom
feat/agent-scripting-parity

Conversation

@w0wl0lxd

Copy link
Copy Markdown
Owner

Summary

Researched competitor agent-control surfaces (Claude Code claude agents --json, OpenCode HTTP session API, Cursor agent ls) and closed the highest-value scripting gaps on top of #149:

  • n00n agent list --json / status --json emit a stable AgentScriptView schema (not raw daemon NDJSON)
  • Normalized state field (working, needs_input, idle, running, stopped, done, failed, …)
  • --all includes stopped/completed background workers (default hides terminal worker rows)
  • --cwd filters by session working directory
  • Optional cwd on AgentRecord, plumbed from TUI live/status and worker agent.json

Spec: specs/006-agent-scripting-parity/spec.md documents the gap analysis and deferred items (agent view TUI, attach/respawn, HTTP API, push events).

Deferred (not in this PR)

  • Interactive n00n agent dashboard (Claude claude agents / requested opencode agents)
  • attach / respawn commands
  • logs tail command (use status --json output for now)
  • HTTP REST / SSE event stream

Test plan

  • ./scripts/smoke-daemon.sh
  • cargo test -p n00n-daemon scripting
  • cargo test -p n00n --bins -- agent::filter_
  • n00n agent list --json --state-dir /tmp/... smoke in script

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.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8a881cd4-1f60-4d81-b74a-b10312812fc8

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/agent-scripting-parity

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/cmd/agent.rs
Comment on lines +633 to +635
cwd: std::env::current_dir()
.ok()
.map(|p| p.to_string_lossy().into_owned()),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment on lines +75 to +78
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cwd: Option<String>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread src/cmd/mod.rs
all,
cwd,
} => {
agent::list_client(json, all, cwd, state_dir)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

w0wl0lxd added 5 commits July 27, 2026 05:15
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
Add changelog fragment, token-profile baseline for 27 tools, and fix
missing BackendKind import in agent command unit tests.
@w0wl0lxd
w0wl0lxd merged commit c6d7f47 into feat/n00n-daemon-session-bridge Jul 27, 2026
2 checks passed
w0wl0lxd added a commit that referenced this pull request Jul 27, 2026
* 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.
@linear-code

linear-code Bot commented Jul 27, 2026

Copy link
Copy Markdown

N00N-45

@w0wl0lxd
w0wl0lxd deleted the feat/agent-scripting-parity branch July 29, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant