diff --git a/api-reference/endpoint/agent-stream.mdx b/api-reference/endpoint/agent-stream.mdx deleted file mode 100644 index 386d130..0000000 --- a/api-reference/endpoint/agent-stream.mdx +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Agent stream -description: Stateless agent relay for spec init and impact preview. ---- - -## `POST /v1/agent/stream` - -Stateless relay between the CLI and the workspace's LLM provider. `zombied` adds the system prompt and API key, forwards to the provider, and streams the response back as SSE. - -The CLI manages conversation history and resends the full message array with each request. `zombied` holds no state between requests. - -### Request body - -```json -{ - "mode": "spec_init", - "messages": [ - { - "role": "user", - "content": "Generate a spec template for: Add rate limiting per API key with Redis backend" - } - ], - "tools": [ - { - "name": "read_file", - "description": "Read a file from the user's repo", - "input_schema": { - "type": "object", - "properties": { "path": { "type": "string" } }, - "required": ["path"] - } - }, - { - "name": "list_dir", - "description": "List directory contents", - "input_schema": { - "type": "object", - "properties": { "path": { "type": "string" } }, - "required": ["path"] - } - }, - { - "name": "glob", - "description": "Find files matching a glob pattern", - "input_schema": { - "type": "object", - "properties": { "pattern": { "type": "string" } }, - "required": ["pattern"] - } - } - ] -} -``` - -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `mode` | string | yes | System prompt selector. One of: `spec_init`, `preview` | -| `messages` | array | yes | Conversation history in Messages API format. CLI accumulates and resends with each request. | -| `tools` | array | yes | Tool definitions. CLI sends read-only tools (read_file, list_dir, glob). | - -### Response - -`200 text/event-stream` — SSE stream with the following event types: - -**`tool_use`** — The model wants to call a tool. CLI should execute locally and send the result back. - -``` -event: tool_use -data: {"id":"tu_01","name":"read_file","input":{"path":"go.mod"}} -``` - -**`text_delta`** — Streaming text from the model's response. - -``` -event: text_delta -data: {"text":"# M5_001: Rate Limiting\n\n**Prototype:** v1.0.0\n"} -``` - -**`done`** — Stream complete. Includes usage and cost. - -``` -event: done -data: {"usage":{"input_tokens":12450,"output_tokens":3200,"cost_usd":0.085,"provider":"anthropic","model":"claude-sonnet-4-6","round_trips":4}} -``` - -**`error`** — Provider error or timeout. - -``` -event: error -data: {"message":"provider timeout after 30s"} -``` - -### Tool call round trip - -When the CLI receives a `tool_use` event: - -1. Execute the tool locally (read file from laptop, list directory, etc.) -2. Append the assistant's `tool_use` message and the user's `tool_result` to the message history -3. POST the updated messages to the same endpoint - -The loop continues until the model returns text (no more tool calls) and a `done` event. - -### Modes - -| Mode | System prompt behavior | Typical tool calls | -|------|----------------------|-------------------| -| `spec_init` | Explore the repo, detect language/ecosystem, generate a milestone spec | 3-5 (list root, read manifest, read Makefile, list src/) | -| `preview` | Read the spec, explore the repo, predict which files will be touched | 4-8 (read spec, list directories, read key files, grep patterns) | - -### Provider resolution - -`zombied` resolves the LLM provider from workspace configuration. The CLI never specifies or sees the provider. Supported providers include Anthropic, OpenAI, Google, and user-supplied keys. - -### Security - -- Tool calls are executed by the CLI on the user's machine, not by `zombied` -- The CLI validates all paths against the repo root before reading (prevents path traversal) -- `zombied` has no filesystem awareness and never sees file contents directly -- Files only leave the laptop one at a time when the model explicitly requests them - -### Errors - -| Status | Code | Meaning | -|--------|------|---------| -| 400 | `INVALID_MODE` | Unknown mode value | -| 401 | `UNAUTHORIZED` | Missing or invalid auth token | -| 403 | `FORBIDDEN` | Insufficient role for workspace | -| 500 | — | SSE `event: error` emitted before stream closes | diff --git a/api-reference/endpoint/list-specs.mdx b/api-reference/endpoint/list-specs.mdx deleted file mode 100644 index ebc7c7d..0000000 --- a/api-reference/endpoint/list-specs.mdx +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: "List specs" -description: "List specs in a workspace." -api: "GET /v1/specs" ---- - -## Query parameters - - - The workspace to list specs for. - - -## Response - -Returns an array of spec objects belonging to the workspace. - - - Array of spec objects. - - - -```json -{ - "specs": [ - { - "spec_id": "spec_01J8XW2A1K", - "name": "M4_001_CLI_RUNTIME", - "version": "v1.0.0", - "milestone": "M4", - "workstream": "001", - "status": "ACTIVE", - "created_at": "2026-03-20T09:00:00Z", - "updated_at": "2026-03-28T11:30:00Z" - }, - { - "spec_id": "spec_01J8XW2B3L", - "name": "M4_002_NPM_PUBLISH", - "version": "v1.0.0", - "milestone": "M4", - "workstream": "002", - "status": "ACTIVE", - "created_at": "2026-03-21T10:15:00Z", - "updated_at": "2026-03-27T14:00:00Z" - } - ] -} -``` - diff --git a/api-reference/error-codes.mdx b/api-reference/error-codes.mdx new file mode 100644 index 0000000..b5f94c2 --- /dev/null +++ b/api-reference/error-codes.mdx @@ -0,0 +1,212 @@ +--- +title: 'Error Codes' +description: 'All error responses from zombied use RFC 7807 (application/problem+json). This page lists every error code, its HTTP status, and common causes.' +--- + +## Response format + +Every `4xx` and `5xx` response uses `Content-Type: application/problem+json`: + +```json +{ + "docs_uri": "https://docs.usezombie.com/api-reference/error-codes#UZ-ZMB-009", + "title": "Zombie not found", + "detail": "No zombie with id 'abc123' in this workspace.", + "error_code": "UZ-ZMB-009", + "request_id": "req_a1b2c3d4e5f6" +} +``` + +| Field | Description | +|---|---| +| `docs_uri` | Stable link to this page for the specific code | +| `title` | Short label — identical for every occurrence of a given code | +| `detail` | Instance-specific context (varies per call) | +| `error_code` | Machine-readable code. Use this for programmatic handling. | +| `request_id` | Correlation ID for support and log tracing | + +--- + +## UUID validation + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-UUIDV7-003` | 400 | Invalid UUID canonical format | ID passed is not a valid canonical UUIDv7 string | +| `UZ-UUIDV7-005` | 500 | ID generation failed | Internal failure generating a new UUIDv7 | +| `UZ-UUIDV7-009` | 400 | Invalid ID shape | Path or body ID does not match UUIDv7 format | +| `UZ-UUIDV7-010` | 409 | UUID backfill conflict | Duplicate ID detected during backfill | +| `UZ-UUIDV7-011` | 500 | Rollback blocked | Rollback cannot proceed due to existing state | +| `UZ-UUIDV7-012` | 500 | Error response linking failed | Internal error linking error response to trace | + +## Internal errors + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-INTERNAL-001` | 503 | Database unavailable | Database server unreachable. Check `DATABASE_URL`. | +| `UZ-INTERNAL-002` | 500 | Database error | Query failed. Check DB logs. | +| `UZ-INTERNAL-003` | 500 | Internal operation failed | Unexpected internal failure. Check `err=` in logs. | + +## Request validation + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-REQ-001` | 400 | Invalid request | Missing or malformed field in request body or query | +| `UZ-REQ-002` | 413 | Payload too large | Request body exceeds 2MB limit | + +## Authentication / authorization + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-AUTH-001` | 403 | Forbidden | Token valid but lacks permission for this resource | +| `UZ-AUTH-002` | 401 | Unauthorized | Missing or invalid Bearer token | +| `UZ-AUTH-003` | 401 | Token expired | JWT has passed its expiry time. Re-authenticate. | +| `UZ-AUTH-004` | 503 | Authentication service unavailable | OIDC provider unreachable | +| `UZ-AUTH-005` | 404 | Session not found | Auth session ID not found or already expired | +| `UZ-AUTH-006` | 401 | Session expired | Auth session timed out before completion | +| `UZ-AUTH-007` | 409 | Session already complete | Auth session was already resolved | +| `UZ-AUTH-008` | 503 | Session limit reached | Too many concurrent auth sessions. Retry shortly. | +| `UZ-AUTH-009` | 403 | Insufficient role | Token role is too low for this endpoint | +| `UZ-AUTH-010` | 403 | Unsupported role | Token contains an unrecognized role claim | + +## API / queue + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-API-001` | 503 | API saturated | Too many in-flight requests. Back off and retry. | +| `UZ-API-002` | 503 | Queue unavailable | Redis queue is unreachable | + +## Workspace + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-WORKSPACE-001` | 404 | Workspace not found | No workspace with this ID exists | +| `UZ-WORKSPACE-002` | 402 | Workspace paused | Workspace billing is paused. Update payment. | +| `UZ-WORKSPACE-003` | 402 | Workspace free limit reached | Free-tier execution limit reached. Upgrade plan. | + +## Billing + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-BILLING-001` | 400 | Invalid subscription ID | Subscription ID format is invalid | +| `UZ-BILLING-002` | 500 | Billing state missing | Workspace has no billing record | +| `UZ-BILLING-003` | 500 | Billing state invalid | Workspace billing record is in an inconsistent state | +| `UZ-BILLING-004` | 400 | Invalid billing event | Billing webhook payload is malformed or unknown | +| `UZ-BILLING-005` | 402 | Credit exhausted | Workspace has no remaining execution credit | + +## Entitlement + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-ENTL-001` | 503 | Entitlement service unavailable | Could not verify plan entitlements | +| `UZ-ENTL-003` | 402 | Stage limit reached | Plan does not allow more pipeline stages | +| `UZ-ENTL-004` | 403 | Skill not allowed | Plan does not include this skill | + +## Spec + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-SPEC-001` | 404 | Spec not found | No spec for this agent/run combination | +| `UZ-SPEC-002` | 400 | Spec is empty | SKILL.md or TRIGGER.md has no content | +| `UZ-SPEC-003` | 422 | Spec has no actionable content | Spec parsed but no runnable instructions found | +| `UZ-SPEC-004` | 422 | Spec has unresolved file ref | Spec references a file that could not be fetched | + +## Run + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-RUN-001` | 404 | Run not found | No run with this ID in this workspace | +| `UZ-RUN-002` | 409 | Invalid state transition | Run cannot move to requested state from current state | +| `UZ-RUN-003` | 429 | Run token budget exceeded | Run hit `max_tokens` limit. Increase in agent profile. | +| `UZ-RUN-004` | 408 | Run wall time exceeded | Run hit `max_wall_time_seconds`. Increase in profile. | +| `UZ-RUN-005` | 429 | Workspace monthly budget exceeded | Monthly token budget exhausted. Resets next month. | +| `UZ-RUN-006` | 409 | Run already in terminal state | Run is DONE/BLOCKED/CANCELLED; no further changes allowed | +| `UZ-RUN-007` | 500 | Run cancel signal failed | Redis publish failed. Retry the cancel request. | +| `UZ-RUN-008` | 500 | Run interrupt signal failed | Redis interrupt write failed. Check Redis. | +| `UZ-RUN-009` | 409 | Run not interruptible | Run state does not support interrupts | + +## Agent + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-AGENT-001` | 404 | Agent not found | No agent profile with this ID | + +## Proposal + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-PROPOSAL-001` | 400 | Invalid proposal JSON | Proposal body is not valid JSON | +| `UZ-PROPOSAL-002` | 400 | Proposal not an array | Top-level proposal value must be a JSON array | +| `UZ-PROPOSAL-003` | 400 | Proposal change not an object | Each change in the array must be a JSON object | +| `UZ-PROPOSAL-004` | 400 | Missing target field | Change object lacks required `target` field | +| `UZ-PROPOSAL-005` | 400 | Unsupported target field | `target` value is not a supported stage field | +| `UZ-PROPOSAL-006` | 400 | Missing stage ID | Change object lacks required `stage_id` | +| `UZ-PROPOSAL-007` | 400 | Missing role | Stage change lacks required `role` field | +| `UZ-PROPOSAL-008` | 400 | Missing insert-before stage ID | Insert operation lacks `insert_before_stage_id` | +| `UZ-PROPOSAL-009` | 400 | Disallowed field | Change contains a field not allowed in proposals | +| `UZ-PROPOSAL-010` | 400 | Unregistered agent reference | Proposal references an unknown agent ID | +| `UZ-PROPOSAL-011` | 400 | Invalid skill reference | Skill reference does not match registry format | +| `UZ-PROPOSAL-012` | 400 | Unknown stage reference | Stage ID in proposal does not exist in pipeline | +| `UZ-PROPOSAL-013` | 409 | Duplicate stage reference | Same stage ID appears twice in proposal | +| `UZ-PROPOSAL-014` | 422 | Proposal would not compile | Applying proposal produces an invalid pipeline | +| `UZ-PROPOSAL-015` | 422 | No valid proposal template | No template matches for AI proposal generation | +| `UZ-PROPOSAL-016` | 500 | Proposal generation failed | AI proposal generation encountered an error | +| `UZ-PROPOSAL-017` | 404 | Proposal not found | No pending proposal with this ID | + +## Webhook + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-WH-001` | 404 | Zombie not found for webhook | Webhook routing found no matching zombie | +| `UZ-WH-002` | 400 | Malformed webhook | Webhook body is missing required fields | +| `UZ-WH-003` | 403 | Zombie paused | Zombie exists but is not active | +| `UZ-WH-010` | 401 | Invalid webhook signature | Slack signature verification failed | +| `UZ-WH-011` | 401 | Stale webhook timestamp | Slack timestamp is >5 min old (replay protection) | + +## Tool + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-TOOL-001` | 424 | Tool credential missing | Required vault credential not found for skill | +| `UZ-TOOL-002` | 502 | Tool API call failed | External API returned an error | +| `UZ-TOOL-003` | 502 | Tool git operation failed | Git operation failed. Check repo URL and credentials. | +| `UZ-TOOL-004` | 400 | Tool not attached | Tool name not in zombie's TRIGGER.md skills list | +| `UZ-TOOL-005` | 400 | Unknown tool | Tool name not recognized | +| `UZ-TOOL-006` | 504 | Tool call timed out | External tool did not respond within timeout | + +## Zombie + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-ZMB-001` | 402 | Zombie budget exceeded | Daily dollar budget hit. Raise via `zombiectl config set`. | +| `UZ-ZMB-002` | 500 | Zombie agent timeout | Agent timed out processing an event. Check logs. | +| `UZ-ZMB-003` | 424 | Zombie credential missing | Required vault credential absent. Use `zombiectl credential add`. | +| `UZ-ZMB-004` | 500 | Zombie claim failed | Could not claim zombie from DB. Verify zombie status. | +| `UZ-ZMB-005` | 500 | Zombie checkpoint failed | Session checkpoint write to Postgres failed | +| `UZ-ZMB-006` | 409 | Zombie name already exists | Name taken. Kill existing zombie first. | +| `UZ-ZMB-007` | 400 | Zombie credential value too long | Credential value exceeds 4KB limit | +| `UZ-ZMB-008` | 400 | Invalid zombie config | TRIGGER.md config_json fails schema validation | +| `UZ-ZMB-009` | 404 | Zombie not found | No zombie with this ID in the workspace | + +## Approval gate + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-APPROVAL-001` | 400 | Approval parse failed | `gates` in TRIGGER.md config_json has invalid JSON | +| `UZ-APPROVAL-002` | 404 | Approval not found | Approval action not found or already resolved | +| `UZ-APPROVAL-003` | 401 | Approval invalid signature | Slack signature or timestamp verification failed | +| `UZ-APPROVAL-004` | 503 | Approval Redis unavailable | Gate service down; default-deny applied | +| `UZ-APPROVAL-005` | 400 | Approval condition invalid | Gate condition expression is invalid | + +## Credentials + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-CRED-001` | 503 | Anthropic API key missing | Workspace `anthropic_api_key` not in vault. Set via credentials API. | +| `UZ-CRED-002` | 503 | GitHub token failed | GitHub App token request failed. Check `GITHUB_APP_ID`. | +| `UZ-CRED-003` | 503 | Platform LLM key missing | No active platform LLM key. Admin must set via platform-keys API. | + +## Relay + +| Code | HTTP | Title | Common Causes | +|---|---|---|---| +| `UZ-RELAY-001` | 400 | No LLM provider configured | Workspace has no LLM credentials configured | diff --git a/changelog.mdx b/changelog.mdx index edbad8b..e8077e3 100644 --- a/changelog.mdx +++ b/changelog.mdx @@ -7,6 +7,222 @@ description: "Stay up to date with UseZombie product updates, new features, and UseZombie is in **Early Access Preview**. Features below are live in the current release. APIs and agent behavior may evolve before GA. + + ## Persistent Zombie Memory (M14_001) + + Zombies now remember facts across runs. Memory persists in Postgres even after a + workspace is destroyed, so a Lead-Collector zombie doesn't re-research every lead + and a Customer-Support zombie doesn't re-ask customers their plan. + + **How it works:** + - NullClaw's `memory_store` / `memory_recall` / `memory_list` / `memory_forget` tools + route to a dedicated `memory` Postgres schema, isolated by the `memory_runtime` role. + - Each zombie's memory is row-scoped by `instance_id = "zmb:{zombie_uuid}"` — two + concurrent zombies cannot read each other's entries. + - `conversation` category remains ephemeral (workspace SQLite), as intended. + - The executor receives `memory_connection` + `memory_namespace` in the RPC payload + and bypasses NullClaw's instance_id propagation gap directly in `zombie_memory.zig`. + + **External-agent memory API (Path B):** + - `POST /v1/memory/store` — store or upsert a key-value entry for a zombie + - `POST /v1/memory/recall` — keyword search across key and content + - `POST /v1/memory/list` — list entries, optionally filtered by category + - `POST /v1/memory/forget` — delete a specific entry by key (idempotent) + - All endpoints enforce workspace scope: a caller can only access zombies in their workspace. + + + + + ## Integration Grant & Execute API — credentialed proxy with human approval (M9_001) + + Zombies (both internal and external LangGraph/CrewAI agents) can now call external + services through UseZombie's credentialed proxy. The credential never leaves UseZombie — + it is injected server-side and stripped from any accidental echo in the response. + + **Integration Grant system:** + - A zombie requests a grant for a service (`POST /v1/zombies/{id}/integration-requests`). + UseZombie fans out an Approve/Deny notification to Slack, Discord, and/or the dashboard + simultaneously. Human clicks Approve once — grant is durable; no per-call approval needed. + - High-risk endpoints still run through the existing M4 per-request approval gate independently. + - Revoke at any time via `DELETE /v1/zombies/{id}/integration-grants/{grant_id}` or + `zombiectl grant revoke`. + + **`POST /v1/execute`** — the new proxy endpoint: + - Auth: zombie session (internal Path A) or `Authorization: Bearer zmb_xxx` (external Path B). + - Pipeline: grant check → firewall domain/endpoint/injection policy → approval gate → credential + inject → outbound HTTP → credential echo strip → activity log. + - Returns the service response with `X-UseZombie-Action-Id` and `X-UseZombie-Firewall-Decision` headers. + + **External agent keys (`zmb_` prefix):** + - Create via `POST /v1/workspaces/{ws}/external-agents` (Clerk-protected). Raw key shown once; only + SHA-256 hash stored. Key resolves to a full zombie identity (workspace_id + zombie_id). + - CLI: `zombiectl agent create / list / delete`, `zombiectl grant list / revoke`. + + **Services supported:** Slack, Gmail/AgentMail, Discord, Grafana. + **New error codes:** `UZ-APIKEY-001/002`, `UZ-GRANT-001/002/003`, `UZ-PROXY-001`. + **Schema:** `core.integration_grants` (slot 026), `core.external_agents` (slot 027). + + + + ## Zombie execution telemetry — per-delivery metrics store and dual API (M18_001) + + UseZombie now records `token_count`, `time_to_first_token_ms`, `wall_seconds`, and + `credit_deducted_cents` for every zombie event delivery, with two query surfaces: + + - **`GET /v1/workspaces/{ws}/zombies/{id}/telemetry?limit=50&cursor=`** — workspace-scoped + cursor-paginated list of the last N deliveries for a single zombie. Returns newest-first + with an opaque cursor for subsequent pages. + - **`GET /internal/v1/telemetry?workspace_id=&zombie_id=&after=&limit=100`** — operator + cross-workspace query. All params optional; `after` accepts epoch ms for time-window queries. + + Writes are idempotent on `event_id` — crash-recovery redelivery of the same event does not + create duplicate rows. A write failure in the telemetry path is logged but does not affect + credit deduction or event acknowledgement. + + Additionally, each delivery now emits an OTel span (`zombie.delivery`) with `zombie_id`, + `workspace_id`, `event_id`, `token_count`, and `plan_tier` attributes. The span uses the + actual wall-clock delivery window so it appears at the correct position in Grafana Tempo. + + + + ## Slack plugin — OAuth install, event routing, and approval interactions (M8_001) + + UseZombie workspaces can now connect Slack via the "Add to Slack" OAuth flow or + the `zombiectl credential add slack` CLI path: + + - **`GET /v1/slack/install`** — redirects workspace owners to Slack OAuth with an + HMAC-signed CSRF state and a 10-minute Redis nonce for replay protection. + - **`GET /v1/slack/callback`** — exchanges the OAuth code for a bot token, stores it + in the zombie vault (`vault.secrets(workspace_id, "slack")`), creates a + `core.workspace_integrations` routing record, and posts a one-time confirmation + message to the workspace. + - **`POST /v1/slack/events`** — verifies Slack signing secret (HMAC-SHA256, RULE CTM), + filters bot-loop events, resolves the workspace via `workspace_integrations`, finds + the zombie configured with a `slack_event` trigger, and enqueues the event to Redis. + - **`POST /v1/slack/interactions`** — verifies signing, URL-decodes the Slack + `payload=` form field, parses `gate_*` action IDs, and relays approve/deny decisions + to the existing approval gate (`resolveApproval`). + + The zombie vault holds the credential; `workspace_integrations` holds only routing + metadata (no credentials). OAuth and CLI paths converge at the same vault slot. + Schema: `core.workspace_integrations` (migration slot 028). + + + + ## Harness & agent-profile scope removed (M17_001) + + The legacy harness control plane — config compilation pipeline, `/v1/harness/*` + endpoints, `agent.agent_profiles` / `agent_config_versions` / `workspace_active_config` / + `config_compile_jobs` / `config_linkage_audit_artifacts` tables, and the + `agent_profile_version` audit linkage — had zero runtime consumers after the + zombie execution model replaced it. All of it is gone: + + - Server handlers (`harness_http.zig`, `harness_control_plane/*`, `profile_linkage.zig`) + and the `src/harness/` module removed. `/v1/workspaces/{id}/harness/*` routes no + longer exist. + - Five agent-schema tables removed via full teardown (pre-v2.0 policy): SQL files + `schema/008_harness_control_plane.sql` and `schema/011_profile_linkage_audit.sql` + deleted outright; `schema/009_rls_tenant_isolation.sql` stripped of agent.* + RLS policies; canonical migration array shrunk from 21 to 19 entries. + - `zombiectl`: `harness`, `harness source`, `harness compile`, `harness activate`, + `harness active`, and `agent harness revert` subcommands removed. The CLI no + longer references dropped endpoints. + - `/v1/agents/{id}` handler was a dead client of `agent_profiles` and is removed. + - `workspace_entitlements.max_profiles` column dropped and `UZ-ENTL-002` + (Profile limit reached) error retired — both were harness-only. + - Website marketing copy no longer advertises "harness checks"; the step is + now described as "validation" in the flow. + + ~4,200 lines of dead infrastructure removed. No behavior change for zombie + workflows. No API breaking changes visible to any live client. + + + + ## Zombie observability (M15_002) + + Zombie triggers and event deliveries now emit PostHog events and increment + Prometheus counters, making zombie throughput visible in Grafana and product + analytics. Previously, `/metrics` contained no zombie counters and PostHog + dashboards showed no zombie activity. + + - **Prometheus:** `zombies_triggered_total`, `zombies_completed_total`, + `zombies_failed_total`, `zombie_tokens_total`, and a `zombie_execution_seconds` + wall-time histogram. + - **PostHog:** `zombie_triggered` fired from the webhook receiver; `zombie_completed` + fired after each delivery attempt with tokens, wall-time, and exit status. + - Graceful no-ops when the PostHog client is unavailable — metrics always record. + + + + ## Zombie credit metering (M15_001) + + Free-plan zombies now deduct from `consumed_credit_cents` after each successful event + delivery, at 1 cent per agent-second. Previously, the pre-execution credit gate blocked + exhausted workspaces but the balance never decremented — free-plan zombies effectively + consumed unlimited quota. Now: + + - **Free plan:** Each delivery records a `CREDIT_DEDUCTED` audit row and updates + `consumed_credit_cents` / `remaining_credit_cents` atomically. + - **Scale plan:** Short-circuits with no DB write — Scale is unlimited. + - **Exhausted credit:** Still writes a zero-delta audit row for observability, so + operators can see post-exhaustion usage events. + - **Crash recovery:** Replay of the same `event_id` is idempotent — no double-charge. + - **DB failure:** Logged and the event is still acknowledged, so a transient Postgres + outage never causes message loss or redelivery storms. + + No API changes. + + + + ## Scoring infrastructure removed (M10_004) + + The legacy agent scoring pipeline had no callers after the zombie execution model replaced + it. All dead code has been removed: scoring Prometheus gauges, PostHog scoring events, + scoring atomic counters, and the scoring duration histogram. The billing summary endpoint + (`GET /v1/workspaces/:id/billing/summary`) now returns zeros correctly instead of 500-ing + against dropped tables. No user-visible API changes. + + + + ## Zombie directory format, AI Firewall, error standardization, pipeline v1 removal + + ### Zombie directory format + Zombies are now two-file directories (`SKILL.md` + `TRIGGER.md`) instead of a single `.md` file. + `SKILL.md` follows the ClaHub registry format — the same file you upload to the CLI is publishable to the skill registry. + `TRIGGER.md` carries deployment config: trigger, chain, budget, network policy, credentials. + `zombiectl install` scaffolds both files; `zombiectl up` sends them raw to the API. + + ### Dynamic skills (no compiled Zig per skill) + Skills are now config-driven. The NullCraw executor reads `SKILL.md` instructions and uses + built-in tools (`shell`, `http`, `file_read`) to call external APIs. Adding a new skill requires + only a new directory — no rebuild of the server binary. + + ### AI Firewall — 4-layer outbound inspection + Every outbound request from a Zombie now passes through an AI Firewall before reaching external APIs: + - **Domain allowlist** — only domains declared in `TRIGGER.md` `network.allow` can be reached + - **Endpoint policy** — per-endpoint rules in `TRIGGER.md` `firewall:` section (e.g., allow GET, deny POST) + - **Prompt injection detection** — scans outbound bodies for instruction override, role hijacking, and jailbreak patterns + - **Content scanning** — inspects response bodies for credential leakage and PII (credit cards, SSNs, API keys) + All firewall decisions are logged as activity events. Fails closed on errors. + + ### API error format standardized (RFC 7807) + All error responses now use `application/problem+json` with `UZ-` prefixed error codes. + Every error code has a stable HTTP status — callers no longer need to parse HTTP status codes independently. + + ### Pipeline v1 removed + The v1 GitHub PR-solver pipeline has been removed. All `/v1/runs/*` and `/v1/specs` endpoints + return **HTTP 410 Gone** with error code `ERR_PIPELINE_V1_REMOVED`. Use zombie-native SSE stream + and chat-inject API instead (see v0.5.0 release notes). + + ### Webhook auth — URL-embedded secret + Preferred webhook URL format: `POST /v1/webhooks/{zombie_id}/{secret}`. + Bearer token remains supported as fallback. + + ### Handler context layer (internal) + All HTTP handler boilerplate (arena setup, request ID, Bearer auth) is now handled by a shared + `hx.zig` wrapper. Handlers contain only business logic. No user-visible behavior change. + + ## Lead Zombie — v2 core ships @@ -35,6 +251,14 @@ description: "Stay up to date with UseZombie product updates, new features, and skill invoked, response returned — is timestamped and queryable. `zombiectl logs` streams the activity log. Cursor-based pagination for replay. + ### Credential injection + Credentials are resolved from the vault at runtime and injected into the sandbox. + No credentials in config files. Add credentials with `zombiectl credential add`. + + ### Session checkpoint + The zombie's conversation context is checkpointed to Postgres after each event. + On crash and restart, the zombie resumes from the last checkpoint — no lost context. + ### New CLI commands `zombiectl install`, `zombiectl up`, `zombiectl status`, `zombiectl kill`, `zombiectl logs`, `zombiectl credential add`, `zombiectl credential list`. @@ -52,6 +276,10 @@ description: "Stay up to date with UseZombie product updates, new features, and ### Version tooling `make sync-version` / `make check-version` prevent VERSION drift across `build.zig.zon` and `zombiectl/package.json`. + + ### Bug fixes + - Fixed YAML parser silently dropping array items in CLI config upload + - Fixed UTF-8 truncation splitting multi-byte characters in session context diff --git a/docs.json b/docs.json index e10ffec..5b1bf20 100644 --- a/docs.json +++ b/docs.json @@ -78,7 +78,8 @@ { "group": "Overview", "pages": [ - "api-reference/introduction" + "api-reference/introduction", + "api-reference/error-codes" ] }, { @@ -106,12 +107,6 @@ "POST /v1/workspaces/{workspace_id}:sync" ] }, - { - "group": "Specs", - "pages": [ - "GET /v1/specs" - ] - }, { "group": "Runs", "pages": [ @@ -124,28 +119,6 @@ "POST /v1/runs/{run_id}:cancel" ] }, - { - "group": "Agents", - "pages": [ - "GET /v1/agents/{agent_id}", - "GET /v1/agents/{agent_id}/scores", - "GET /v1/agents/{agent_id}/improvement-report", - "GET /v1/agents/{agent_id}/proposals", - "POST /v1/agents/{agent_id}/proposals/{proposal_id}:approve", - "POST /v1/agents/{agent_id}/proposals/{proposal_id}:reject", - "POST /v1/agents/{agent_id}/proposals/{proposal_id}:veto", - "POST /v1/agents/{agent_id}/harness/changes/{change_id}:revert" - ] - }, - { - "group": "Harness", - "pages": [ - "PUT /v1/workspaces/{workspace_id}/harness/source", - "POST /v1/workspaces/{workspace_id}/harness/compile", - "POST /v1/workspaces/{workspace_id}/harness/activate", - "GET /v1/workspaces/{workspace_id}/harness/active" - ] - }, { "group": "Credentials", "pages": [ @@ -161,13 +134,6 @@ "DELETE /v1/workspaces/{workspace_id}/skills/{skill_ref}/secrets/{key_name}" ] }, - { - "group": "Agent Relay", - "pages": [ - "POST /v1/workspaces/{workspace_id}/spec/template", - "POST /v1/workspaces/{workspace_id}/spec/preview" - ] - }, { "group": "Admin", "pages": [ @@ -180,8 +146,7 @@ "group": "Billing", "pages": [ "POST /v1/workspaces/{workspace_id}/billing/scale", - "POST /v1/workspaces/{workspace_id}/billing/events", - "POST /v1/workspaces/{workspace_id}/scoring/config" + "POST /v1/workspaces/{workspace_id}/billing/events" ] } ]