Skip to main content

kind: Agent

What it is

kind: Agent is the per-record CRUD entry point for a single agent — the standalone counterpart to the agents: list nested under a kind: Crew or kind: Workspace document. Operators reach for the standalone form when they want to author or patch one agent in isolation (e.g. add a skill, repoint the model) without re-shipping the whole crew bundle. Every agent belongs to exactly one crew. The manifest references its parent crew by slug (spec.crew_slug); Plan resolves that slug to the crew’s id at apply time — the Create handler only accepts a crew_id, never a slug. The kind is implemented in internal/manifest/kinds/agent.go.

Bindings are separate POSTs

The Create handler does not accept inline skills or credential references in its body. Each binding is its own POST. So the Create Exec runs three phases in order:
  1. POST /api/v1/agents — create the agent, returns the new id.
  2. POST /api/v1/agents/{id}/skills — one call per spec.skills entry.
  3. POST /api/v1/agents/{id}/credentials — one call per spec.env_refs entry.
Binding POSTs are idempotent on the server (a re-bind returns “already assigned”), so re-applying is safe. A skill/env-ref typo fails loud at apply with the offending slug named.

YAML schema

apiVersion: crewship/v1      # required — always crewship/v1 for now
kind: Agent                  # required — the literal string "Agent"
metadata:
  name: Daniel               # required — display name
  slug: daniel               # required — workspace-unique idempotency key
  description: ""            # optional — mapped to agents.description
spec:
  crew_slug: code-review     # required — parent crew (resolved to crew_id)
  role_title: Code Reviewer  # optional — human-facing UI title
  agent_role: LEAD           # optional — LEAD | AGENT | COORDINATOR (server default: AGENT)
  cli_adapter: CLAUDE_CODE   # optional — runtime adapter (see enum below)
  llm:
    provider: ANTHROPIC      # optional — ANTHROPIC | OPENAI | GOOGLE | NONE
    model: claude-haiku-4-5  # optional — adapter-specific model id (free-form)
  tool_profile: MINIMAL      # optional — FULL | CODING | MINIMAL
  timeout_seconds: 1800      # optional — single-turn cap (default 1800)
  memory_enabled: true       # optional — per-agent memory tier (default true)
  prompt: |                  # one of prompt / prompt_file (exactly one)
    You are Daniel, a senior code reviewer.
  skills: [house-style]      # optional — skill slugs to bind
  env_refs: [ANTHROPIC_API_KEY, GH_TOKEN]  # optional — credential names to bind

Field reference

FieldTypeRequiredNotes
metadata.namestringyesDisplay name.
metadata.slugstringyesWorkspace-unique idempotency key.
metadata.descriptionstringnoMapped to agents.description.
spec.crew_slugstringyesParent crew slug. Resolved to crew_id at Plan time. The manifest requires it for every agent even though the server allows crewless agents — so cross-document references stay unambiguous.
spec.role_titlestringnoHuman-facing UI title (e.g. “Technical Architect”).
spec.agent_roleenumnoOne of LEAD | AGENT | COORDINATOR. Empty → server default AGENT. LEAD requires a crew_slug. COORDINATOR is effectively unsupported — prefer AGENT/LEAD (see the note below).
spec.cli_adapterenumnoOne of CLAUDE_CODE | OPENCODE | CODEX_CLI | GEMINI_CLI | CURSOR_CLI | FACTORY_DROID.
spec.llm.providerenumnoOne of ANTHROPIC | OPENAI | GOOGLE | NONE. NONE = explicitly no LLM (for adapters that pin their own).
spec.llm.modelstringnoAdapter-specific model id. Free-form by design.
spec.tool_profileenumnoOne of FULL | CODING | MINIMAL.
spec.timeout_secondsintnoSingle-turn execution cap. Defaults to 1800; must be non-negative.
spec.memory_enabledboolnoPer-agent memory tier. Defaults to true. Pointer-typed internally so an absent field is distinguishable from false.
spec.promptstringone-ofInline system prompt body.
spec.prompt_filestringone-ofManifest-relative path to a prompt body. Folded into prompt at parse time.
spec.skills[]stringnoSkill slugs to bind (one POST each).
spec.env_refs[]stringnoCredential names to bind as env vars (one POST each). The credential’s name must equal the env-var (e.g. ANTHROPIC_API_KEY).
Exactly one of prompt / prompt_file must be set. prompt_file is resolved relative to the manifest file and folded into prompt before Validate runs, so a hand-built document that never went through the loader must set prompt directly.
COORDINATOR is asymmetric — and effectively unsupported. The standalone kind: Agent validator (validAgentRoles, internal/manifest/kinds/agent.go) still admits COORDINATOR, but:
  • The nested form — an agent inside a kind: Crew or kind: Workspace bundle — rejects it outright. Its validator (validAgentRole, internal/manifest/validate.go) accepts only AGENT and LEAD.
  • Even via the standalone kind, the server’s agent-role enum was trimmed to AGENT/LEAD in v0.1, so apply can still fail with a 400 at the POST /api/v1/agents call.
In practice use AGENT or LEAD. COORDINATOR survives in the standalone front-end validator only so a future server rollback stays a one-line change; treat it as unsupported today.

Examples

Minimal

apiVersion: crewship/v1
kind: Agent
metadata:
  name: Daniel
  slug: daniel
spec:
  crew_slug: code-review
  prompt: You are a senior code reviewer.

Lead with model, tools, and bindings

apiVersion: crewship/v1
kind: Agent
metadata:
  name: Daniel
  slug: daniel
spec:
  crew_slug: code-review
  role_title: Code Reviewer
  agent_role: LEAD
  cli_adapter: CLAUDE_CODE
  llm:
    provider: ANTHROPIC
    model: claude-haiku-4-5
  tool_profile: MINIMAL
  memory_enabled: true
  skills: [house-style]
  env_refs: [ANTHROPIC_API_KEY, GH_TOKEN]
  prompt: |
    You are Daniel, a senior code reviewer. Apply the house-style
    skill, then run a security and correctness pass.

Prompt from a sibling file

apiVersion: crewship/v1
kind: Agent
metadata:
  name: Alice
  slug: alice
spec:
  crew_slug: triage
  agent_role: LEAD
  prompt_file: ./prompts/alice.md

CLI reference

There is no dedicated crewship agent per-kind admin command — agents are authored through the manifest pipeline (or the UI). The relevant CLI surface is the global apply/export flow:
CommandDescription
crewship apply --file agent.yamlDeclarative upsert (Create + bind skills/credentials, or Update).
crewship apply --dir ./manifests/Walk a directory; crews + agents run before projects/labels in topo order.
crewship apply --file agent.yaml --dry-runPlan only — surfaces a dangling crew_slug before any mutation.
crewship export workspaceRound-trip — emits one kind: Agent document per agent, with bindings folded back in.

REST endpoint mapping

How each manifest field maps onto the create/update request body and binding calls:
Manifest fieldPOST/PATCH body fieldNotes
metadata.namename
metadata.slugslug
spec.crew_slugcrew_idResolved slug → id before the call.
spec.role_titlerole_title
spec.agent_roleagent_role
spec.cli_adaptercli_adapter
spec.llm.providerllm_providerOmitted when NONE.
spec.llm.modelllm_model
spec.tool_profiletool_profile
spec.timeout_secondstimeout_secondsSent explicitly (default 1800) so the round-trip diff is stable.
spec.memory_enabledmemory_enabledSent explicitly (default true).
spec.promptsystem_prompt
spec.skills[]skill_idOne POST .../skills per entry.
spec.env_refs[]credential_id + env_var_nameOne POST .../credentials per entry.
Endpoints used:
VerbPathAction
POST/api/v1/agentsCreate
PATCH/api/v1/agents/{agentId}Update
GET/api/v1/agentsList (drift + export)
GET/api/v1/crewsResolve crew_slugcrew_id
POST/api/v1/agents/{id}/skillsBind a skill
POST/api/v1/agents/{id}/credentialsBind a credential

Validation rules

AgentDocument.Validate enforces:
  • apiVersion / kind, when set, equal crewship/v1 / Agent.
  • metadata.name and metadata.slug are non-empty.
  • spec.crew_slug is non-empty (and required when agent_role: LEAD).
  • agent_role, cli_adapter, llm.provider, tool_profile, when set, are members of their allow-lists (errors spell out the legal values).
  • timeout_seconds is non-negative.
  • Exactly one of prompt / prompt_file is set.
  • skills[] and env_refs[] have no empty entries.
  • When WorkspaceContext carries crew data, crew_slug must reference a declared or remote crew. Skill/credential FK checks run at Plan time (the live client is available there).

Apply behavior

ApplyUpsert (default)

  • Remote missing → ActionCreate: POST the agent, then bind each skill + env-ref in sequence. A partial binding failure leaves the agent created; re-applying converges (the bindings are idempotent).
  • Remote present, fields drift → ActionUpdate: a sparse PATCH carrying only the fields whose declared value differs from the remote. Empty declared fields are skipped — so omitting role_title won’t blank out a title set via the UI. Declared bindings are re-asserted (idempotent).
  • Remote present, no field drift, no declared bindings → ActionUnchanged.
The diff is intentionally narrow: system_prompt, description, and memory_enabled are not touched unless explicitly declared, so the manifest never silently clobbers a prompt grown via the UI.

Crew reassignment

Declaring a different crew_slug on an existing agent emits a crew_id patch — supported but rare.

Round-trip via export

crewship export workspace calls ExportAgents, which renders one kind: Agent document per agent (sorted by slug), folding crew_id back to crew_slug and pulling each agent’s bound skill slugs + credential env-names back into spec.skills / spec.env_refs. memory_enabled is emitted explicitly so the round-trip diff doesn’t fall into the “use server default” branch. Fields the manifest doesn’t model (runtime status, run counts, timestamps) are dropped.

See also

  • Crew — the parent crew; can also declare agents inline under spec.agents.
  • Skill — bound via spec.skills (slug list).
  • Workspace — the top-level bundle that nests crews + agents.
  • Issue — references an agent via spec.assignee_slug.
  • This kind’s Go implementation: internal/manifest/kinds/agent.go.