9.8 KiB
Technical Spec: Oz CLI Named-Agent CRUD
Context
specs/REMOTE-1696/PRODUCT.md defines the user-visible behavior.
The CLI command definitions live in crates/warp_cli/src/agent.rs. The old skill-discovery command has moved to AgentCommand::Skills(ListAgentSkillsArgs) with an optional --repo flag. The app-side dispatcher in app/src/ai/agent_sdk/mod.rs sends that variant to agent_config::list_skills.
The existing skill-discovery implementation is in app/src/ai/agent_sdk/agent_config.rs. It calls AIClient::list_skills(repo) and renders repository-discovered AgentSkillItem values.
Run listing/getting in app/src/ai/agent_sdk/ambient.rs (60-116) is the output model to follow. It accepts JsonOutput, fetches raw API JSON for JSON or --jq, and uses output::print_raw_json. The reusable output helpers are in app/src/ai/agent_sdk/output.rs (1-258).
The public API methods and types flow through app/src/server/server_api/ai.rs. The AIClient trait exposes skill listing as list_skills(repo) and named-agent CRUD as list_agents, list_agents_raw, get_agent, get_agent_raw, create_agent, create_agent_raw, update_agent, update_agent_raw, and delete_agent. The underlying ServerApi exposes authenticated GET/POST/PUT/DELETE helpers for public API commands.
The named-agent API reference is in ../warp-server/public_api/openapi.yaml:
POST /agent/identitiesandGET /agent/identitiesat../warp-server/public_api/openapi.yaml (2709-2781).GET /agent/identities/{uid},PUT /agent/identities/{uid}, andDELETE /agent/identities/{uid}at../warp-server/public_api/openapi.yaml (2783-2917).CreateAgentRequest,UpdateAgentRequest,AgentResponse, andListAgentsResponseat../warp-server/public_api/openapi.yaml (5399-5539).
Related named-agent environment work for REMOTE-1695 adds an optional environment_id field to create/update/response models. The current checked-out OpenAPI schema may lag that server behavior, so the implementation keeps optional deserialization tolerant while still accepting --environment and --remove-environment.
Implemented changes
Add a new named-agent CLI surface and move the old skill-discovery command:
- Rename the old skill-discovery command to
AgentCommand::Skills(ListAgentSkillsArgs)with#[command(name = "skills")]. - Add
AgentCommand::List(AgentListArgs),Get(AgentGetArgs),Create(AgentCreateArgs),Update(AgentUpdateArgs), andDelete(AgentDeleteArgs). - Keep the old skill-discovery behavior and
--repointact under the renamed skills command. - Keep parser tests focused on custom conflict behavior rather than retesting clap's ordinary subcommand and repeated-flag parsing.
Command arguments:
AgentListArgs:--sort-by <name|created-at>,--sort-order <asc|desc>, and flattenedJsonOutput.AgentGetArgs: positionaluidand flattenedJsonOutput.AgentCreateArgs: required--name; optional--description; repeatable--secret <NAME>; repeatable--skill <SKILL>; optional--base-model <MODEL_ID>; optional--environment <ENVIRONMENT_ID>; flattenedJsonOutput.AgentUpdateArgs: positionaluid; optional--name; optional--descriptionconflicting with--remove-description; repeatable--add-secret; repeatable--remove-secret;--remove-all-secretsconflicting with individual secret add/remove flags; repeatable--add-skill; repeatable--remove-skill;--remove-all-skillsconflicting with individual skill add/remove flags; optional--base-modelconflicting with--remove-base-model; optional--environmentconflicting with--remove-environment; flattenedJsonOutput.AgentDeleteArgs: positionaluid. It intentionally does not flattenJsonOutputbecause delete has no API response body to filter with--jq.
Add app-side named-agent command handling:
- Create a new module such as
app/src/ai/agent_sdk/agent_management.rsto avoid mixing named-agent CRUD with skill discovery. - Route the renamed skills command to the existing
agent_config::list_skills. - Route named-agent CRUD commands from
run_agentto the new module. - Update
command_requires_authso every named-agent command and the renamed skills command require authentication. - Add telemetry variants for
AgentSkills,AgentList,AgentGet,AgentCreate,AgentUpdate, andAgentDelete, or at minimum preserve the existingAgentListtelemetry for the renamed skills command and add distinct variants for new CRUD commands if the telemetry enum supports adding them.
Add API types and methods in app/src/server/server_api/ai.rs:
- Define serde types for
SecretRef,CreateAgentRequest,UpdateAgentRequest,AgentResponse,ListAgentsResponse,AgentSkillItem, and supporting skill response types. - Model
created_atasDateTime<Utc>. - Model
environment_idas an optional field to tolerate both the currently checked-in schema and the related server work. - For create requests, skip absent optional fields and omit empty lists.
- For update requests, use nested
Option/custom serde helpers as needed so omitted fields are not serialized, remove flags serialize empty string or empty array, and non-empty values serialize replacements. - Add
AIClienttrait methods for typed named-agent CRUD plus raw JSON list/get/create/update methods used by JSON output and--jq, using the nameslist_agents,list_agents_raw,get_agent,get_agent_raw,create_agent,create_agent_raw,update_agent,update_agent_raw, anddelete_agent. - Implement the methods with
GET/POST/PUT/DELETE /api/v1/agent/identities. - Add reusable
put_public_api,put_public_api_response, anddelete_public_api_unithelpers toServerApiif no existing helper covers those verbs.
Output behavior:
- Implement
TableFormatforAgentResponse. - For list, fetch typed responses for pretty/text/ndjson and raw JSON for JSON/
--jq. Apply client-side sorting before output for pretty/text/ndjson only. - Reject
--sort-byor--sort-orderwhen JSON output is requested or implied by--jq, because JSON mode should preserve raw API output rather than returning a client-mutated response. - In pretty list output, print
Looking for agent skills? Use <binary> agent skills instead.after the named-agent list, where<binary>is resolved through the existing CLI binary-name helper. - List and single-agent pretty/text output share the same fields from
AgentResponse: UID, name, created time, description, secret names, skill specs, base model, and environment. - Pretty/text list output filters out disabled agents. If any disabled agents are hidden, print
N disabled agents hiddento stderr after stdout output. JSON output and--jquse the raw API response and do not filter or print this notice. - For get/create/update, pretty output renders a single-row table; text output renders stable tabular text with headers; JSON/
--jqprocesses the raw API response. - List JSON is the raw public API response object with an
agentsarray, so list--jqfilters should address.agents[]. - For delete, pretty output says the agent was deleted, text output prints the deleted UID, and JSON/ndjson output prints
{ "uid": "<uid>", "deleted": true }. Delete does not support--jq.
Update references and naming:
- Update user-facing help strings so "agents" means named agents and "skills" means repository-discovered skills.
- Update examples only if the CLI docs/examples mention
oz agent listas skill discovery.
Testing and validation
- Keep CLI parse tests only for custom conflicts:
agent update <uid> --description <value> --remove-descriptionis rejected.agent update <uid> --add-secret <NAME> --remove-all-secretsis rejected.
- Add unit tests for update request serialization to prove omitted fields are absent, remove flags serialize empty values, and replacement values serialize correctly.
- Add unit tests for client-side secret and skill delta helpers.
- Add unit tests for list sorting by name and created time.
- Add unit tests that list sort flags are rejected for JSON and
--jqoutput. - Add output tests for named-agent detail text/pretty helpers where they can be tested without a full app context.
- Run
cargo fmt. - Run targeted Rust tests for
warp_cliand the new agent SDK module. - Run a focused clippy command if compile/test scope reveals warnings.
- Manually validate against an authenticated staging server. On macOS, invoke the app runner with
WARP_CLI_MODE=1 ./script/run -- <cli args>so the bundled app parses CLI subcommands rather than GUI URLs. For parallel manual validation, use the built binary withWARP_CLI_MODE=1to avoid concurrent./script/runrebundling and codesigning the same app path. - Manual validation should cover create/get/list/delete, update add/remove flags for skills and secrets, description/base-model/environment set and remove flags, invalid environment errors, nonexistent UID errors, update-with-no-flags errors, sort ordering by name and created time, JSON sort-flag rejection, pretty/text/ndjson/json output, and
--jqscalar output.
Parallelization
Parallel child agents are not necessary for implementation. The change is medium-sized but tightly coupled across command definitions, SDK dispatch, API methods, and output tests; parallel edits would likely collide in agent.rs, mod.rs, and ai.rs. Manual validation can be parallelized after the implementation lands because validators only run CLI commands and use isolated disposable named-agent prefixes.
Risks and mitigations
- Patch semantics are easy to break: use serialization tests for omitted, remove, and replacement cases.
- The old
agent listbehavior is being renamed: keep the renamedagent skillsbehavior otherwise unchanged and include the pretty-output hint for users looking for skills. - Raw JSON sorting can diverge from "raw API" expectations: reject sort flags when producing JSON output.