diff --git a/docs/research/agent-platform/RESEARCH_PROMPT.md b/docs/research/agent-platform/RESEARCH_PROMPT.md new file mode 100644 index 000000000..518cecec2 --- /dev/null +++ b/docs/research/agent-platform/RESEARCH_PROMPT.md @@ -0,0 +1,119 @@ +# Research Prompt: What "agent" means in {PRODUCT} + +Reusable prompt for the agent-definition study. Run once per product. Output +goes into `docs/research/agent-platform/products/{slug}.md`, following the +section skeleton below. Add the dossier to `index.md` when done. + +## Task + +Research how **{PRODUCT}** ({URLS}) defines and models an "agent". The goal is +the operational definition, not marketing copy: what the noun refers to in +their API, data model, config files, and runtime. This feeds a cross-product +synthesis on what the industry actually means by "agent". + +## Research questions + +Answer every question the sources can support. Quote primary sources; mark +gaps as gaps instead of guessing. + +### 1. The agent noun + +- What do the docs/API literally say an agent is? Capture exact quotes. +- Is the agent a persistent entity (identity, record, resource with an ID) or + an ephemeral one (a process, a run, an invocation)? +- Which conceptual model fits best: agent-as-identity, agent-as-config, + agent-as-process, agent-as-session, agent-as-workspace, other? + +### 2. Subagents + +- Does the product have subagents (child agents, delegated agents, task + agents, workers)? What are they called? +- How is a subagent created: declared ahead of time (config/registry) or + spawned dynamically at runtime by the parent? +- What does a subagent inherit from its parent (model, tools, permissions, + context, filesystem, credentials) and what is isolated? +- Can subagents nest? Is there a depth or fan-out limit? +- How do parent and subagent communicate: return value only, message passing, + shared state, shared filesystem? + +### 3. Configuration surface + +- What is configurable on an agent: model, system prompt, instructions, tools, + permissions, sandbox/environment, memory, secrets/credentials, triggers, + schedule, resource limits? +- Where does configuration live: files in a repo, API objects, dashboard, all + of the above? What format (YAML frontmatter, JSON, code)? +- **Why** does the product expose each knob? Look for the stated rationale + (safety, cost, reproducibility, multi-tenancy, team sharing) rather than + inferring one. + +### 4. Binding time + +- When is each piece of configuration bound: at definition time (static file, + registered template), at session/run creation, or mutable mid-run? +- Can an agent's definition change while instances of it are running? What + happens to in-flight work? +- Is there versioning of agent definitions? + +### 5. Relationships between nouns + +- Map the product's nouns and their cardinality: agent, subagent, session, + run, task, thread, sandbox, workspace, workflow, tool. Which contains which? + Which references which? +- Specifically: agent-to-session (one agent, many sessions? session owns the + agent?), agent-to-sandbox (does an agent imply an environment?), and + agent-to-subagent (ownership, lifetime coupling: does the parent's death + kill the child?). + +### 6. Lifecycle + +- How is an agent created, started, paused/hibernated, resumed, and destroyed? +- Who owns the loop: does the product run the agent loop (managed) or does the + customer's code drive it (bring-your-own-loop)? +- What persists across runs: memory, filesystem, conversation history? + +### 7. The implicit definition + +- Based on all the above, what makes something "an agent" in this product, + versus a plain LLM call or a script? State it in one or two sentences, in + our words, clearly marked as our inference. + +## Method + +1. Primary sources first: official docs, API reference, SDK source, config + schemas, official repos. Secondary sources only to triangulate. +2. Capture each exact quote as a checked-in source excerpt. Record its direct + URL, document section or repository symbol, source version or commit when + available, and retrieval date. Also record an archive or snapshot URL when + one exists and a content digest when the source publishes or permits one. + These evidence records must remain auditable if the live URL changes. +3. Record the retrieval date with `date +%F`; never guess it. +4. Stay on the operational definition. Ignore pricing, marketing claims, and + features unrelated to the agent model. +5. Fill the product skeleton sections in order; do not invent sections. Put + anything the sources leave unanswered under **Open questions**. + +## Output skeleton (per product file) + +```markdown +--- +title: "{PRODUCT}: what 'agent' means" +source_urls: [...] +retrieved: YYYY-MM-DD +status: done +--- + +# {PRODUCT}: what "agent" means + +Part of [Agent platform research corpus](../index.md). +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). + +## The `agent` noun (primary-source quotes) +## Subagents +## Configuration surface (what, where, why) +## Binding time +## Relationships between nouns +## Lifecycle +## What makes it "an agent" here (our inference) +## Open questions +``` diff --git a/docs/research/agent-platform/decision-record.md b/docs/research/agent-platform/decision-record.md new file mode 100644 index 000000000..27cc4cfab --- /dev/null +++ b/docs/research/agent-platform/decision-record.md @@ -0,0 +1,1082 @@ +# Agent Service: design questions and answers + +Part of Agent Definition Research. +Running record of the design discussion that followed the 15-product study. +These are directional decisions, not final commitments. Every answer is +grounded in [the synthesis](./synthesis.md). This record is frozen as +decision-time input: where a conclusion here differs from an accepted +record in the [ADR index](../../adr/index.md), the ADR is authoritative. + +## Q1: What should we build? + +**An agent registry + evolution service.** The agent is a versioned +declaration that the system itself is allowed to rewrite, through the same +proposal pipeline a human would use. "Self-correcting" is not magic; +in every product that has it, it is a loop: sessions produce evidence, a +reflection process distills evidence into proposals, verification gates them, +and activation mints rollbackable revisions. We build that loop as a +first-class product. + +The product opportunity is to connect outcomes to proposed, verified, and +safely activated revisions. Design that loop as a standalone service so it +can sit above any agent runtime. + +## Q2: What is an agent? + +The synthesis working definition: **a named, versioned declaration of +persona and capability (instructions, model, tools, limits, credentials, +delegation roster) that a runtime instantiates into pinned, resumable +sessions. Agent-ness (the loop, autonomy) is a property of the runtime, not +the record.** + +For a *self-evolving* agent, split the declaration into two layers (the +split [Hermes](./products/hermes-agent.md) and [Devin](./products/devin.md) +discovered independently): + +- **The charter** (human-owned, slow-changing): identity, purpose, trust + boundary, tool grants, credential scopes, budget limits, delegation caps. +- **The learned layer** (agent-owned, fast-changing): memory, self-authored + skills, playbooks, accumulated user/domain model. + +Governance rule that follows: the agent freely evolves its learned layer +within the charter; it may only *propose* charter changes. + +**Ownership correction:** the later Q15/Q16 audit supersedes the literal +field placement in this early working split. Memory has its own resource; grants, credential bindings, +budgets, and delegation caps live on external planes; charter and learned +layer are governance classes over one complete revision rather than separate +mutable stores. + +## Q3: Who provisions it? Who evolves it? How? + +**Provisioning:** the operator (human or another agent) creates the charter; +the system provisions everything else. Minimal-creation bar per OpenComputer: +an agent needs only a model and a prompt, on a managed default runtime. +Creation idempotent by name. Environment and memory are separate attached +resources. + +**Three evolution actors, different rights:** + +1. **The agent itself** writes scoped Memory and contributes evidence for + learned-layer proposals (Hermes: memory nudges ~10 turns, skill nudges + ~15, background curator). +2. **Fresh-context verifier agents** gate behavior changes. Cognition's + production data: fresh-context reviewers beat shared-context ones; the + proposer never approves its own change. +3. **The human operator** approves charter changes and retains the ability to + force rollback. Who else may invoke rollback is authorization policy at + command dispatch, not an Agent-stream invariant. No product in the study + lets an agent widen its own tool grants or credentials. + +**The evolution loop (five steps, all from existing patterns):** + +1. **Instrument outcomes** per session (Managed Agents `define_outcome` + rubric + separate grader). +2. **Reflect on a schedule**: a curator job reads transcripts, outcomes, and + evaluation evidence, then either records a scoped Memory update or distills + evidence into a behavior candidate (new skill, prompt edit, dependency + declaration). +3. **Propose, never mutate**: every behavior candidate opens its own proposal + with evidence and proposed content; it has no revision number yet. +4. **Verify before activation**: the proposal runs against rubric/evals with + fresh-context judges; learned-layer approvals may auto-activate, while + charter changes queue for the human. +5. **Activate with pinning**: activation mints the next immutable revision; + in-flight sessions keep their revision, only new sessions pick up the + change, and rollback is one pointer move. + +The one sanctioned live mutation: credential rotation (industry-wide). + +## Q4: Agent-as-file vs agent-as-record: where does config live? + +The framing that cuts through the abstraction: **"agent as a file" versus +"agent as a record" is just: where does the truth live, and who is allowed +to write to it safely.** + +**Option 1, agent-as-files, literally** (Claude Code, OpenClaw, eve). An +agent is nothing but a folder: + +``` +agents/code-reviewer/ +├── agent.toml # name, model, limits, tool grants +├── prompt.md # the persona/instructions +└── skills/ + └── review-prs/SKILL.md +``` + +Managing agents = edit files, git commit, ensure the folder exists where the +runtime runs. Great for humans (diffs, PRs, portability). Breaks for our +goal: when the *system* is the author, machine-writes to files/git are racy, +there is no locking, the runtime doesn't know which version a running +session started on, no per-tenant story, and (per the Claude Code dossier) +no versioning primitive at all. + +**Option 2, agent-as-record** (OpenComputer, Managed Agents, LangGraph). An +agent is a row/resource with an ID and version. +Managing agents = API calls or a dashboard; `PATCH` mints version N+1; +activation pointers roll back. Machines get optimistic locking (Managed +Agents 409 on version mismatch), audit, multi-tenancy. Humans lose diffs, +PR review, and portability. + +**Option 3, agent-as-code** (OpenAI, CrewAI, ADK, Vercel SDK). A +constructor call in the app. Managing agents = normal software deployment. +Ruled out for us: "self-configuring" means the system writes config, and +writing code + redeploying is not that. + +**Decision: hybrid, the record is the source of truth, the file layout is +the interchange format.** Mental model: **Dockerfile → image → container.** + +- The file layout is the Dockerfile: human-authorable, diffable source form. +- The revision is the image: immutable, numbered artifact minted from the + files, stored in a registry (OpenComputer: revisions "like a Vercel/Fly + deployment... never rewritten"). +- The session is the container: pinned to the image it started from; new + images never mutate running containers. + +Three products converged on exactly this shape independently: OpenComputer +(repo push of `agent.toml` + `prompt.md` + `skills/` → numbered revision), +Managed Agents (skill folder uploads into versioned agent +resources), eve (compiles an `agent/` directory into a deployed app). + +**What "managing agents" means, operation by operation:** + +| Operation | Human path | System path (self-correcting loop) | +| --- | --- | --- | +| Create | `agentctl init` scaffolds the folder, `agentctl push` registers it | `POST /agents` with the charter | +| Edit | edit files locally, push → opens a proposal | curator opens a proposal via API | +| Review | diff the active revision against proposed content | fresh-context verifier agents judge the proposal | +| Ship | activate an approved proposal → mints revision N+1 | auto-activate an approved learned-layer proposal | +| Undo | activate the previous revision (pointer move) | same, triggered by regression detection | +| Inspect | list agents, list revisions, see which sessions pin what | same API | + +**Carve-out, not everything the agent learns goes through revisions:** + +- **Revisioned (the registry):** charter + prompt + skills. Slow-changing, + behavior-defining, worth proposing, verifying, and activating. These are + "the files." +- **Not revisioned (separate memory store):** per-session learnings, user + preferences, episodic memory, domain model. Managed Agents versions + individual memories inside a store; Hermes keeps MEMORY.md living. If + every memory write minted an agent revision, version history would be + noise. + +Rule of the whole design: **you never edit the active agent. You propose new +content, and activation alone mints the next revision.** That single rule is +what makes self-correction safe. + +## Q5: Is the agent bound to a harness, the provider+model, or both? (HarnessAgent vs Agent) + +Three things get conflated and must stay separate concepts: + +1. **The brain**: provider + model (`anthropic/claude-opus-4-8`). A + swappable string. +2. **The loop engine (harness/runtime)**: assembles prompts, wires tools, + manages context, decides when to call the model again. Claude Code, + Codex, OpenComputer's `pi`, Hermes's embedded runtime, a LangGraph + graph, a hand-rolled loop, all harnesses. +3. **The agent definition**: the persona/capability declaration (prompt, + skills, tools, limits). + +The industry answers "is the harness part of the agent?" three ways: + +- **OpenComputer: yes, immutable.** Agent = prompt + model + `runtime` + (`claude` | `codex` | `pi` | `flue` | custom); "runtime is fixed once the + agent exists; to switch engines, create a new agent." Model is constrained + by runtime (`anthropic/…` for `claude`, `openai/…` for `codex`). +- **Vercel: implementation detail behind an interface.** `Agent` is just + `generate()`/`stream()`; `ToolLoopAgent` builds the loop around a raw + model; `HarnessAgent` runs "a preconfigured established harness, such as + Claude Code, Codex, or Pi, instead of building the loop yourself." +- **AgentCore: inside the container, not AWS's business.** The agent + artifact *is* harness+loop, opaque behind `POST /invocations`. + +**Decision: no separate HarnessAgent noun; one `Agent` with a `runtime` +field** whose values span the spectrum: + +- `runtime: managed-default`: we run a plain tool-loop around the model + (OpenComputer's "the only thing an agent needs is a model and a prompt"). +- `runtime: claude-code | codex | pi | ...`: an established harness + executes the definition (the HarnessAgent case). +- `runtime: custom`: customer brings the loop behind a turn contract + (`POST /turn`); we keep sessions, durability, and the event log. + +**The test for whether harness belongs in the versioned definition:** does +the artifact change meaning when the loop engine changes? It does: a prompt +and skill set tuned under Claude Code behaves differently under Codex +(OpenComputer's model-per-runtime constraint exists because the coupling is +real). It bites harder for us: **verification results are only valid for +the runtime they ran on.** A revision that passed evals under `claude-code` +proves nothing about the same prompt under `codex`. Therefore: + +- `runtime` is recorded in every revision. +- v1 adopts OpenComputer's strict rule: **runtime immutable per agent; + switching engines = a new agent.** It keeps version history meaningful + (revision 12 comparable to revision 11 only on the same engine). +- **Model stays a swappable field** within what the runtime can drive, and + may be overridden per session without minting a revision (OpenComputer + and Managed Agents both allow this): model swaps change which brain + executes the agent, not what the agent *is*. Credentials attach to the + model source (BYO key vs managed), not the harness. + +**What we are building, per se: the layer above harnesses.** The registry + +evolution service does not compete with Claude Code or Codex; it treats +them as interchangeable loop engines executing versioned agent definitions. +The agent record says who the agent is (charter, prompt, skills), which +engine runs it (runtime, fixed), and which brain powers it (model, +swappable). Sessions pin all three; the curator evolves the definition +per-runtime. + +Naming guidance so this is never re-litigated: don't name anything +"HarnessAgent." The field is `runtime`; an agent *has* a runtime the way a +container has a base image; "agent" is reserved for the declaration. + +**Field-strength vocabulary (follow-up clarification).** Fields on an agent +have one of three strengths, and the two headline fields differ: + +| Strength | Meaning | Overridable? | Example | +| --- | --- | --- | --- | +| Constraint | part of what the agent *is* | never | `runtime` | +| Default | what a session gets unless overridden at creation, within constraints | per session | `model` | +| Suggestion | advisory; influences decisions, enforces nothing | always | `description` (routing hint) | + +`runtime` is a **constraint**, not a "default runtime": no override path +exists (v1). `model` is a true **default**: per-session override is legal +within what the runtime can drive, without minting a revision. And defaults +exist only in the definition: at `SessionStarted` everything collapses to +resolved facts (pinned revision, actual runtime, actual model) that never +change for the session's life (the ledger records what actually ran, never +what was suggested). If runtime ever softens past v1, it moves down this +ladder from constraint to default, at the cost of a per-runtime dimension +on verification verdicts and version history. + +One logical persona on multiple engines is NOT plural runtime, it's +sibling agents from one template (`code-reviewer@claude-code`, +`code-reviewer@codex`), each with its own revision history and evolution +loop, tied together by steward routing (later fitness-based) and skill +portability (a skill proven under one runtime is *proposed* to the sibling +and passes that runtime's own verification). Invariant worth protecting: +**one revision binds to exactly one runtime, so a verification verdict +always has a single meaning.** + +## Q6: How do we compare runtimes if one agent cannot switch runtimes? How do we group the agents being compared? + +**Mental model:** run an A/B test between two separate agents. + +For example, create `reviewer-claude-code` and `reviewer-codex` from the same +template. Give both the same charter and rubrics, but bind one to Claude Code +and the other to Codex. Route comparable pull requests to both, score them +with the same rubric, then compare outcome quality and cost per successful +outcome. Because neither agent changes runtime, each revision history remains +clean evidence about one runtime. The conclusion is scoped: one runtime +performed better for this charter and workload, not for every agent. + +The steward can use that evidence to send later work to the stronger agent. +That is fitness-based routing. + +**Identity:** the two test subjects are separate agents. Each keeps a unique +tenant-scoped `name`, which remains an opaque idempotency key. + +**Grouping decision: use labels in v1 instead of adding an `AgentFamily` +entity.** Give each related agent's BehaviorBundle the same selectable label, +such as `family=code-reviewer`. `selectable_labels` is a bounded string map on +the BehaviorBundle. Agent read models expose it from the active revision, +while an existing Session resolves it from its pinned revision. Routing and +comparison views may recognize a small set of keys such as `family`. This +supports tenant-defined grouping without committing to a new +lifecycle-bearing entity. Precedent: Managed Agents `metadata` (16 pairs) and +OpenComputer opaque session metadata. + +If families later need their own lifecycle or permissions, promote `family` +to a first-class entity. Adding that entity later is cheaper than removing it +after clients depend on it. + +## Q7: Is the agent's content just one system prompt? Can text be replaced? + +**Authored content is a bundle, not one string.** The revision holds: +`instructions` (the persona/system prompt: OpenComputer `prompt`, Managed +Agents `system`, Claude Code markdown body, eve `instructions.md`); +`skills/` (on-demand playbooks so the prompt stays small, "without +bloating every prompt"); optionally an `initialPrompt` (Claude Code +precedent: auto-submitted first *user* turn); and a typed +`variables_schema`. +Few-shot examples belong in skills, not fake conversation turns; +multi-message preludes deferred until demanded. + +**The model receives an assembly, recorded.** At session start the runtime +assembles the real system prompt (author instructions + platform +injections: environment, memory, skill listings, following the Hermes +composition pattern). Rule: the assembly is deterministic and **ledgered**: +the session records the resolved model input (or digest), so "what did the +model actually see" is always answerable. + +**Replacing text, three cases:** +1. **Placeholders bind at StartSession** (ADK `{placeholders}`, CrewAI + kickoff interpolation): declared in the revision, resolved into session + facts, then fixed. +2. **Past messages are never edited**: transcript is events; append only. +3. **What the model *sees* can change, because the context window is a + read model over the transcript.** Compression, skill injection as user + messages (Hermes's cache-preserving trick), and elision are projection + changes, not data changes; the projection used is ledgered. + +Mid-session instruction changes stay out of v1 (revision pinning + Hermes +cache economics); sanctioned patterns are a new session or the next +revision. + +## Q8: Should tools be `required` vs not? + +Yes, split the declaration, because it determines *when* failure happens. +Two independent axes, never conflated: +1. **Declaration** (charter, authored): what the agent needs. +2. **Grant** (Grants stream, human-held): what the agent may use. +Effective toolset at session start = intersection. + +- **`tools.required`**: fail at the cheapest moment: `StartSession` + rejects if any required tool lacks a grant, before tokens burn (a PR + reviewer without `bash` just fails expensively at turn 4 otherwise). + Pod-won't-schedule-without-required-volume semantics. +- **`tools.optional`**: used if granted, silently absent otherwise; + instructions degrade gracefully; curator-observed friction becomes + `RequestGrant` (storyboard 3). + +Composition rules: +- Provisioning does NOT hard-fail on missing grants: provision + auto + grant-escalation; agent sits in derived "pending grants" state + (AgentRosterView) until the human approves; creation-by-delegation + keeps flowing; the approval is the go signal. +- Verification verdicts record the **effective toolset** they ran with + (same logic as recording the runtime): a revision verified with + web-search granted proves nothing about running without it. +- Undeclared tool attempts still ledger as `ToolCallDenied` (prompt- + injection audit, unchanged). + +Corpus check: no product has this split cleanly (Claude Code `tools` is a +restriction list; Managed Agents tools are config + permission policies; +A2A cards declare capabilities as assertions). This imports +required/optional dependency semantics into the charter. + +**Required means wait state, not error.** The tool must be wired before any +session runs; approval can wait. +`StartSession` gives a *typed* rejection (`pending-grants` + escalationId), +never a generic error. The waiting lives on the **work item**, not a +half-started session (no new "blocked" session state): the delegation +parks steward-side, or the integrator subscribes to grant events and +retries on `GrantChanged`. Event-driven, no polling. The ledger narrates +it end to end: `AgentProvisioned → GrantRequested → EscalationRaised → +EscalationResolved → GrantChanged → SessionStarted`, and the gap between +the last two timestamps is the measurable approval latency per agent. + +**Mid-session disconnection (follow-up).** Three failures, three answers, +one pinned asymmetry: **the definition pins; authorization never pins.** +Grants are evaluated live at every tool call (revocation is the human's +kill switch, the live-mutation exception, like credential rotation). +1. *Grant revoked midway:* optional tool → `ToolCallDenied(grant-revoked)`, + session continues degraded. Required tool → cooperative stop, + `SessionClosed(required-grant-revoked)`, incomplete outcome with costs + attributed, work item re-parks in the Q8 wait machinery; successor + session resumes from the persisted transcript on re-grant. No + "suspended" session state in v1 (sessions only exist when runnable); + same-session hibernate/resume is the v2 refinement if re-grant proves + frequent. +2. *Tool endpoint down (MCP outage):* availability, not authorization, + **a denial is a security fact; an error is an ops fact; the ledger + never blurs them.** Runtime retries per policy; persistent outage → + tool-error on the turn, agent adapts or stops gracefully with + `required-tool-unavailable`; charter budgets bound thrashing; curator + sees the friction. +3. *Runtime/session crash:* already covered: at-least-once turn + redelivery, per-session ordering, fail-closed ledger (unrecorded work + didn't happen and is redriven). + +## Q9: Are tools/skills suggestions? Can they be loaded mid-session and promoted later? + +**Not suggestions, enforced at every call.** Ladder placement: the +**grant is the constraint** (human-held, evaluated live), the **revision's +declared set is the default**, and **session-time additions are +overrides** (exactly like `model`). + +**Mid-session extension: yes, session-local.** Precedent: Managed Agents +("update a session's agent.tools and agent.mcp_servers mid-session... +session-local and do not propagate back"). Rules: +1. Session-local, never writes back to the agent; ledgered as a session + event. +2. Always inside the grant boundary: extension widens usage within the + constraint, never past it; widening the constraint is the grant desk. +3. Skills are looser by design: the revision pins the skill *bundle* + (versions, digest); *loading* one mid-session is a projection choice + (their whole point); handing a session an extra skill beyond the bundle + is a session-local override like a tool. + +**Promotion: yes, this is the evolution loop's food.** Repeated +session-local extensions and good outcomes are curator evidence → proposal +adds the tool/skill to the defaults → verified → activation mints a revision +for new sessions. Law: **session-local extensions are the mutations, the +curator is the selection, revisions are the heredity.** Ad-hoc +experimentation is the raw material the loop exists to consume. + +Caveat (recorded): overridden sessions weaken revision comparability: +they get marked, and the primary metric compares like-for-like; free, +since every extension is a ledger event. Event-model impact: additive, a +`SessionExtended` session event (tools/skills delta) and an override flag +on outcome joins. + +## Q10: Who owns the agent? (tenant vs project vs user vs direct allowance) + +Four concepts, never blurred: + +1. **The wall, tenant (org).** Everything lives in exactly one tenant; + adversarial isolation is a deployment boundary (decided; corpus- + unanimous: OpenComputer, Managed Agents, Devin, OpenClaw). +2. **Placement and visibility.** The initial draft used a fixed + `org | project | user` scope field. Q22 and Q23 supersede that shape: + the agent is provisioned under `parent`, hierarchy owns placement, + and visibility derives from hierarchy plus access policy. More specific + visibility still shadows broader visibility; editing from a narrower + context creates an explicit fork. +3. **Ownership, lifecycle authority.** NOT the same as scope. Owner = the + human principal/team whose flag inbox receives this agent's + escalations and who sits at its grant desk: approves charter-class + revisions, changes grants, archives, shares. Default = the human + sponsor behind the provisioner. Machine principals can never own; an + agent with no human escalation addressee is an orphan the platform + refuses to create. +4. **Direct allowance, explicit shares.** Access outside inherited visibility + is an ACL entry, ledgered (`AgentShared`: grantee, capability, by whom, + why), human-approved via the same grant-desk machinery. Capability tiers: + **use** (route work / start sessions), **observe** (outcomes, costs, + revision history), **manage** (delegated ownership powers). + +Two load-bearing consequences: +- **Cost follows the session, not the owner**: sessions carry the + initiator; a shared agent bills whoever asked (Managed Agents vaults + rationale: product at agent granularity, users at session granularity). +- **Memory remains a scoped resource, not Agent content**: a Session resolves + memory from its hierarchy and policy context and records the selection or + snapshot. Sharing an agent never allows one memory scope's contents into + another Session. + +**Corrected post-Q22/Q23:** there is no independent `scope` field on +`AgentProvisioned`. Hierarchy owns placement, the Agent record retains its +accountable owner reference, and `OwnershipTransferred` remains a dedicated +operation if needed later. +**Corrected post-Q15/Q16:** the +`AgentShared`/`AgentShareRevoked` pair does NOT live on the Agent stream: +a share is the owner's stance (access policy), so by the organizing rule +it lives on the **policy plane beside grants** (per-agent access stream, +human-only mutations; grants = what the agent may touch, shares = who may +touch the agent). + +**Topology correction:** the final stream-topology decision supersedes the +original single-stream design. The agent registry stream owns +provisioning, activation, rollback, and archive; each proposal stream owns +opening, verdict, and withdrawal. + +## Q11: Skill service vs per-agent skill management? + +**Both, split as registry vs lockfile: the skill service is the system of +record; the agent only composes, never manages.** + +- **Skill service (registry):** skills are first-class, versioned, + tenant-scoped platform entities with scope, owner, labels, and provenance + (author: human | curator | import; evidencing sessions). Access shares use + Q10's separate policy-plane bindings. Precedents: Managed Agents' Skills API + (workspace-scoped, agents reference by ID), Vercel's `skills add` + package ecosystem, the synthesis's "the registry is secretly a skill + economy." +- **Agent = composition via pinning:** the revision pins exact skill + versions; the bundle digest covers *resolved* content (the npm/lockfile + split, the revision is the lockfile). A skill update reaches an agent only + through a proposal verified under that agent's runtime; activation then + mints the revision. No floating references: a central skill edit must + never silently change agent behavior without a revision. + +Why no private per-agent skill management: portability dies (sibling +cross-pollination needs one entity both agents pin), dedup dies (N agents +evolving N private flaky-tests skills), and the fleet-level fitness signal +("which skills contribute to outcomes across all carriers") dies without +stable skill identity. + +Flows, all through existing machinery: curator authors skill → registers in +service → opens a proposal that pins the version → verify → activate. Cross- +pollination = sibling pins the same registry entity, verified under its +own runtime. Session-local loading (Q9) draws from the registry within +scope visibility; repeated good extensions promote to pins. Skill import +(repo/package) lands in the registry, never directly in an agent. + +Event-model impact: a Skill stream owns `SkillRegistered`, +`SkillVersionAdded`, and `SkillArchived`. Skill access shares are policy-plane +events, never skill lifecycle events. `ProposalOpened` evidence identifies +skill pins added or removed, each referencing registry `skillId@version`, and +the content digest commits to the proposed definition. + +## Q12: What about deterministic agents (state machines, scatter-gather, sequential)? + +Corpus dividing line (unanimous): LangGraph "workflows have predetermined +code paths; agents define their own"; Cloudflare "Workflows: linear, +deterministic; Agents: non-linear, non-deterministic"; CrewAI crews vs +flows. Cautionary tale: ADK's Sequential/Parallel/LoopAgent, deterministic +orchestrators dressed as agents, all deprecated in 2.0 for a separate +Workflow concept. Jido's `strategy` field (Direct vs ReAct) shows +determinism as a loop-engine property with the agent noun unchanged. + +**Test: who decides the next step? → three rungs, all on existing +machinery:** + +1. **No model decision → a tool, not an agent.** Fully deterministic + pipelines shouldn't burn LLM turns (Hermes `execute_code`: "collapsing + multi-step pipelines into zero-context-cost turns"). If the state + machine can be written down completely, write it down, as a tool or a + plain program. +2. **Model decides within scripted structure → an agent; the structure IS + its `runtime`.** LangGraph graph, Jido strategy, hand-rolled state + machine with LLM branch points = `runtime: custom` behind the turn + contract. Platform-invariant: sessions, pinning, outcomes, costs, + revisions apply identically; a scripted agent just gives the curator + less prompt and more structure. +3. **Deterministic coordination ACROSS agents → a workflow, deliberately + NOT our noun in v1.** Dynamic coordination = steward/coordinator agent + (already designed). Fixed coordination = external engine (Temporal or + Vercel Workflows) calling + `StartSession` as steps, the API is already workflow-shaped (idempotency + keys, durable sessions, outcomes as step results). Event-driven processors + can implement the pattern without adding a workflow DSL to the agent + model. + +**Complexity programmed into the agent noun: zero.** No strategy field, no +state-machine schema, no scatter-gather primitives in the charter (v1). +Promotion rule applies: if tenants keep rebuilding the same external +coordination, consider a Workflow noun then. + +## Q13: Agent-to-agent coordination: does one agent "require" another? + +**Coordination is charter-declared (industry-settled):** a delegation +roster (Managed Agents `multiagent` roster depth-1/max-20; OpenClaw +allowlist off-by-default; OpenAI `handoffs`). + +**"May delegate to" vs "requires": Q8's split applied to agents:** +- `delegates.required`: pure coordinators; StartSession typed `pending` + rejection if no usable delegate resolves + escalation ("provision or + share an agent matching X"); same wait machinery as required tools. +- `delegates.optional` (the common case): degrade gracefully; repeated + wished-for delegations are curator friction → proposes provisioning + (headcount from workload). + +**Roster references, default to selectors, not pins:** +- exact `agentId@revision` (Managed Agents-style pinning): reproducibility + exception only, pinned rosters rot (delegate improves, coordinator + calls the stale one; delegate archives, coordinator breaks). +- by `name`: scope-resolved (own-shadows-global). +- **by selector (`family=...` label): the default.** Resolved at + delegation time, where fitness-based routing plugs in (best sibling + gets the work); fleet improvements flow to coordinators for free. + Resolution is **ledgered**: the delegation records the concrete + agent+revision that got the work (defaults in the definition, facts in + the session). + +**Mechanics (all existing machinery):** delegation = a session on the +target agent carrying delegationId/parent ref; parent records child +session ids; results-only return; depth/fan-out caps on the policy plane; +scope+shares (Q10) gate resolution (`use` capability). Lifecycle coupling +is **session-to-session, never agent-to-agent**: parent close → +cooperative cancel of running children by default (Jido `on_parent_death` +is the policy knob if `detached` is ever needed). External delegates = +v2: a roster selector matching an A2A card, same contract, task in, +result out, internals opaque. + +## Q14: Should the rubric be part of the agent? + +**Defensible part:** the charter carries a rubric reference as a +**default** so every session has a yardstick without the caller supplying +one, and the primary metric needs consistent per-agent scoring. Per the +ladder, it's overridable per work item: outcome definitions legitimately +attach to the WORK (Managed Agents `define_outcome`, Devin's per-session +rubrics). + +**The flaw the challenge exposed:** drawn inside the bundle, the rubric +looked revisable like prompt/skills, a Goodhart hazard: **the judged +would own the yardstick.** A curator proposing "rubric → easier-rubric" as a +learned-layer change could auto-activate a lower bar and silently inflate +verification verdicts, the autonomous improvement rate, and fitness +routing. The self-improvement loop must never improve its own grades. + +**Fix (three rules):** +1. **Rubrics are first-class registry entities** (Q11 skill treatment): + versioned, scoped, owned by the operator/QA, never agent content; the + agent references `rubricId@version`. +2. **The rubric binding is charter-class, never learned-layer.** The + curator may propose a binding change (with evidence) but it always + escalates to a human. Rubric *content* versioning belongs to the + rubric's owner, not any agent's curator. +3. **Comparability is version-scoped:** `OutcomeRecorded` records + `rubricId@version`; the primary metric compares only within one rubric + version; a rubric change starts a new baseline, visibly in the ledger. + +Event-model impact retained by Q15: Rubric follows the Skill lifecycle-stream +pattern (`RubricRegistered`, `VersionAdded`, `Archived`), and +`OutcomeRecorded.rubricId` becomes `rubricId@version`. Q15 supersedes the +proposed charter binding. + +## Q15: Rubrics observe from aside: no binding on the agent at all + +**Supersedes Q14's binding location.** A corpus re-check confirms that +nobody binds rubrics on the agent. Managed Agents' +`define_outcome` is a session event; Devin's rubrics attach per session. +The charter `[rubrics]` block was invention, now removed. + +**Model: EvaluationBinding, a first-class entity aside the agent:** +`{ selector (agentId | family label | scope | task-type), rubricId@version, +precedence, owner }`, living in the evaluation registry with the rubrics, +mutable by rubric owners (humans) only, ledgered on its own stream. The +grader resolves applicable bindings at outcome time; a delegator's +per-work-item outcome definition is a higher-precedence binding source. +`OutcomeRecorded` carries all applied scores. + +**What this fixes over charter-binding:** +1. Goodhart guard becomes **structural**: the agent's revision pipeline has + no write path into evaluation config, nothing to propose, nothing to + escalate; the judged doesn't even carry a reference to the yardstick. +2. Multiple observers naturally (correctness + cost-discipline + + compliance rubrics = three bindings). +3. Selector coverage: `family=` bindings automatically observe future + siblings; "which agents are unobserved?" is a one-query fleet audit, + and unobserved agents are excluded from auto-activation (guardrail). +4. Agent history stays about the agent; evaluation churn never mints + charter revisions. + +**Survives from Q14:** rubrics as registry-owned versioned entities; +`rubricId@version` on every outcome; comparisons only within one rubric +version. Primary-metric series keyed by (agent, rubric@version) via +binding resolution; binding stability, visible in the ledger, is what +makes revision N vs N+1 comparable. + +Event-model impact: remove `rubricIds` from `AgentProvisioned.charter`; +add Rubric lifecycle events (`RubricRegistered`, `VersionAdded`, `Archived`) +on Rubric streams, plus `EvaluationBindingSet` and `Removed` on each +binding's own stream (human principals +only); verification bench resolves bindings for the agent under judgment. + +## Q16: The full "aside" audit: what else was wrongly in the agent? + +The final data-ownership decision formalizes this rule and separates +activated revision content, resolved Session facts, Memory, and reusable +Tool and Work contracts. + +Generalizing Q15's test: **is this the agent's own fact, or someone else's +stance toward the agent?** The agent stream holds the agent's facts; +every other party's stance (judging, permitting, funding, scheduling, +routing, ranking) is that party's own entity referencing the agent, +usually by selector. Projections never live inside what they observe. + +**Stays (the agent's facts):** the Agent record owns name and runtime. The +BehaviorBundle owns description, selectable labels, model default, +instructions, skill pins, variables, and required or optional tool and +delegate declarations. Labels are revision-bound because they are the +selector surface; changing them can change routing, evaluation, and +comparison semantics. + +**Moves out (was wrongly drawn in the charter):** +1. **`[limits]` → budget policy on the policy plane.** Spend/token/turn + caps are the payer's stance (DelegationReceived already carries + budgetUsd; Devin: per-session ACU caps + org hard caps). Human-owned, + selector-bindable, never mints agent revisions. Lives with grants: + both are "what the operator permits." +2. **Delegation caps (max_depth, max_concurrent, allowlists) → policy + plane.** The need to delegate stays as declaration; tolerated fan-out + is permission (OpenClaw and Hermes both put these in operator config). + +**Keep outside the agent record:** +3. Triggers/schedules/automations: the public corpus models them as separate + resources (OpenComputer schedules, Managed Agents deployments, Devin + automations), each referencing the agent. +4. Output routing: a subscription or destination binding. +5. Fitness/priority: observation data belongs in roster projections, not the + observed entity. +6. Channel/reachability bindings: separate resources. + +**Resulting charter (final shape):** identity + engine (runtime, model) + +content (instructions, skill pins, variables) + dependency declarations +(tools, delegates). Nothing economic, temporal, evaluative, or +reputational. + +Event-model impact: `AgentProvisioned.charter` +loses `limits` and delegation caps; a Policy plane joins Grants +(BudgetPolicySet, DelegationPolicySet, human principals only); trigger/ +schedule and destination entities are future streams, never charter +fields. + +## Q17: Do we need the daemon file set (SOUL/USER/MEMORY/...)? The assembly-position model + +**No, we need the layers, not the files.** The daemon file set exists because +each layer differs in **injection position, cadence, cache cost, and +visibility**; files were the +single-operator daemons' cheapest serialization of that. Mechanics from +the corpus: system prompt = once-per-session cached prefix (Hermes: +"prompt caching is sacred"); CLAUDE.md re-injected every turn *to survive +compaction*; skills = one-line index in context, bodies on demand, +injected as user messages to preserve the prefix; HEARTBEAT.md loaded +only on scheduled wakes (OpenClaw lightContext); Netclaw strips layers per +audience and gives subagents rules-without-soul. + +**Our placement (files dissolve into planes):** SOUL+AGENTS → one +`instructions` doc in the revision (no soul/rules seam needed, our +delegates are real agents with own definitions); IDENTITY → record +fields; USER/MEMORY → memory store (session-start layer; memory writes +never touch the pinned revision or its cache); TOOLS/TOOLING → tool +schemas from grants + runtime envelope + "usage wisdom" as skills; +HEARTBEAT → the scheduling trigger carries its own input; BOOTSTRAP → +template/provisioning input; project CLAUDE.md-style files → workspace +context resolved at session start (scope-layered); skills + agents/*.md → +skill registry + delegates selectors (already designed). + +**The assembly-position model (refines Q7):** every layer declares one of +four positions, and the position is part of the ledgered assembly spec: +1. **Cached stable prefix**: charter instructions + skill index; changes + only via revision. +2. **Session-start bind**: memory snapshot, variables, workspace context, + grants-derived tool list; resolved once, recorded in the input digest. +3. **Volatile tail / per-turn**: only what must be fresh (time, events, + steering); kept out of the prefix so the cache holds. +4. **On-demand loads**: skill bodies, fetched content; projection + choices, ledgered. + +Same physics as the daemons, different substrate: they serialize +positions as files in a directory; we serialize them as registry entities +plus a deterministic, ledgered assembly. + +## Q18: Per-message chunks (before/after each message)? + +**Could: yes, Q17 position 3 (volatile tail). Should: yes, as declared, +revisioned, cadence-limited turn wrappers.** Unnamed but proven in the +corpus: Netclaw wraps every input ("a message arriving at a session actor +**with context-specific instructions**"); CrewAI ships a per-task suffix +("Begin! This is VERY important to you..."); Claude Code re-injects +CLAUDE.md every request (before-chunk, survives compaction) and steers via +appended system-reminders (after-chunks), with hooks as the programmable +form; Hermes contributes the cadence refinement (nudges every ~10/15 +turns, not every message, cost). + +**Why:** recency. Prefix instructions fade over long sessions (context +rot); safety-critical rules re-asserted near the tail dominate. +After-chunks = strong form (recency works for you); before-chunks = +framing (source, audience, time). + +**Rules:** +1. Turn wrappers are **charter content**: `before_message`/`after_message` + changed through a proposal → verified → activated into a revision; the + bench can measure whether a wrapper changes outcomes. +2. Volatile tail only, never mutate history, never invalidate the cached + prefix. +3. **Cadence declared**: `every_turn | every_n_turns | on_trigger` + (tool-failure, post-compaction); every-turn is the justified exception. +4. Two wrapper sources compose in fixed order: **platform envelope** + (audience/session/time, Netclaw-style, outermost, never + agent-controlled) then the agent's declared wrappers (inner). +5. Applied wrappers are part of the ledgered input digest. + +**Refused:** runtime self-modification of wrappers mid-session, "I should +remind myself X every turn" is a curator observation → proposal +(the standard promotion pipeline), never a live mutation. + +**Who does it (clarified):** the agent never *performs* injection. The +revision **declares** wrappers; the runtime adapter **applies** them +mechanically at assembly time (same code path as the Q7 session-start +assembly, running at position 3); the model **experiences** the wrapped +input without knowing the mechanism exists; the curator **evolves** the +declarations via proposals and activation. Definition declares, platform enacts, +ledger records. + +## Q19: Extend a mutable registry or start from the event model? + +**Start from the event model.** The product requires complete provenance: +curator evidence, verification history, activation decisions, rollback, and +cost per outcome. Without events, those become bolted-on logs rather than +the projections that drive the evolution loop. + +Retrofitting provenance later would require replacing the storage model and +migrating consumers after the contracts are live. Extending a mutable CRUD +registry is defensible only if the product shrinks to bookkeeping; verified +self-evolution makes events load-bearing from the first release. + +## Q20: How does an agent get access from the beginning? + +Three paths, in order of commonality: + +1. **Placement, not shares.** "Give project B access from the start" = + provision the agent under project B's `parent` in the hierarchy. Scope + and visibility derive from hierarchy and policy, as finalized by Q22 and + Q23. Shares exist only for cross-placement exceptions. +2. **Sequential commands, deliberately non-atomic.** ProvisionAgent (Agent + stream) then ChangeGrant xN / ShareAgent (policy plane, human + principal). No cross-stream transaction, and none needed: the Q8 + pending-grants wait state makes the gap safe by design (the agent + exists, shows as pending, StartSession typed-rejects until grants + land). Human provisioners issue both back-to-back; the steward's grant + needs become GrantRequested + escalation. +3. **Templates = pre-approval at authoring time.** A human blesses a + template once, INCLUDING its grant preset and ceilings; provisioning + from it applies those grants automatically with provenance pointing at + the template's blessing. Provisioning must reject machine principals + that exceed the template's grant ceilings. The human-only invariant + survives: approval moved + from per-agent to per-template. Beyond-ceiling needs drop to path 2. + +Stream-authorship rule for implementers: **ProvisionAgent emits only +AgentProvisioned, never policy events.** Even on the template path, grant +events are appended by the policy plane's own command (template preset as +input). Streams never write each other's facts. + +## Q21: How do Google APIs do it? (API-surface conventions) + +Google's AIPs answer the Q10/scope question at scale: **placement lives in +the resource name** (AIP-122/133: `tenants/{t}/projects/{p}/agents/{a}/ +revisions/{n}`; creation is `POST /{parent}/agents`, so placement = which +collection you create into; Vertex already does this for agents: +`reasoningEngines/{re}/runtimeRevisions/{rev}`), and **ownership/access +lives aside as IAM policy** attached to the resource name and inherited +down the hierarchy, our stance-plane rule discovered independently. +Their grammar rhymes throughout: etags = our WritePrecondition; +soft-delete/undelete = archive-not-delete; custom methods (`:activate`, +`:rollback`, AIP-136) = lifecycle commands not PATCHes; the standard `labels` +map informs Q6's bounded selector syntax, not ownership; our +`selectable_labels` live on BehaviorBundle. Revisions-as-child-collection +with aliases (AIP-162) = our activation pointer. + +**Decision:** adopt AIP-style resource names for the API surface (bilbao +is ConnectRPC + protobuf; AIPs are built for proto), while hierarchy events +own placement. The resource name is a *rendering* of tenant + parent path + +name; a hierarchy move changes that rendering, and references-by-id survive, +avoiding Google's one sharp edge (name-encoded placement makes moves nearly +forbidden). The Agent ledger does not duplicate placement as `scope` data. + +## Q22: What if tenants have projects, orgs, teams, users, or whatever? + +**Don't enumerate levels, one recursive Container, tenant-drawn trees.** +Q10's `scope: {level: org|project|user}` quietly hardcoded three levels; +superseded. Industry-unanimous move for unknown tenant structure: Google +Cloud folders (untyped, nested; IAM inherits down), AWS OUs, and +Zanzibar/SpiceDB (no levels at all, only parent/member relations; +authorization resolves through whatever graph the tenant builds). The same +reason that favors open-ended labels applies to tenant-defined hierarchy. + +**Model (as first drafted):** `Container { id, parentId?, kind, name }` +on the tenancy plane; tenant root is a container; agents carry a single +`container` ref (one canonical parent, AIP-124 style). **`kind` is a +label, not a schema**: the platform interprets exactly two things: the +parent chain and membership. Consequences: +- Shadowing generalizes to *nearest ancestor wins* up the chain. +- Policy/evaluation bindings select by subtree and inherit down + (IAM/Zanzibar move). +- "User scope" = a container of kind `user-home`; org/project/team/ + whatever collapse into one mechanism. +- Q21 names render the container path; references stay by id; moves + survive. +- The placement conclusion survives the final topology's later stream + split: an agent stream with four deciders and a proposal stream with + three. Placement is still an opaque reference validated outside those + deciders. + +Q6 promotion rule guards the reverse: promote a `kind` to a real concept +only when the platform must *interpret* it (e.g. billing-per-project): +promotion cheap, demotion breaking. +**Supersedes:** Q10's fixed scope levels (the placement/ownership split +itself stands). + +**Cross-platform verification:** all three clouds +converged on this exact shape, untyped recursive middle containers +(GCP Folders, AWS OUs, Azure Management Groups; depth capped single-digit +everywhere), policies inheriting down, and **exactly one promoted +container kind** where billing/isolation anchor (GCP Project, AWS +Account, Azure Subscription). Our promoted kind is the **tenant**, same +architecture, one level rotated. Kubernetes is the cautionary tale for +skipping the tree (flat namespaces; hierarchy bolted on later via HNC); +Zanzibar is the pure-relations extreme that underlies our membership +resolution anyway. + +**Inheritance semantics fork (recorded):** GCP IAM inherits +*additively* (children gain, never lose); AWS SCPs inherit as *ceilings* +(parents bound children). We need both, and the planes already split that +way: **grants are additive down the tree; budgets/caps are ceilings down +the tree.** + +**Q22 addendum, the container dissolved (mistake chain recorded).** The +decision was worked into a formal ADR +([straw-hat-team/adr #4761776210](https://github.com/straw-hat-team/adr/pull/345), +finalized and merged 2026-07-09), and field-by-field review shrank the model +three times: +1. `Container { id, tenant, parent?, kind, name }`: first draft, five + fields (mistake: carried derivable and decorative fields). +2. `Container { id, parent? }`: tenant is the root ancestor, kind is a + label, name is an API-surface slug (mistake: the parent still drawn as + a node property). +3. **No container entity at all.** Add/move commands validate *tree* + invariants (parent exists, no cycle, depth cap), so per the + per-command-state rule the tree is the consistency unit: a per-tenant + event-sourced `Hierarchy` stream (NodeAdded/NodeMoved/NodeRemoved), + with the current tree as a projection. GCP/HNC parent pointers are + projection fields, not model. +**Final semantics: a container is an opaque id string.** Meaning accrues +around it from other planes (hierarchy events mention it, annotations decorate +it, bindings select it, resources reference it). Validity is checked +where the id is *used* (command dispatch validates against the hierarchy +projection), never inside deciders (purity rule). Deciders unaffected +throughout. + +## Q23: What do we call the placement field? + +**`parent`.** Verified against primary sources: GCP Project has field +`parent` ("A reference to a parent +Resource. eg., organizations/123 or folders/876") plus `projects.move` +("Move a project to another **place** in your resource hierarchy, under a +new resource parent"); AWS `MoveAccount(AccountId, SourceParentId, +DestinationParentId)` + `ListParents`, with docs calling the parent "the +destination container"; AIP-124 confirms "at most one canonical parent" +but does NOT prescribe the field name (earlier claim overstated). + +**Verified nuance:** the explicit `parent` field is the convention for +hierarchy nodes and for **movable** resources; plain leaf resources vary +(GCP leaves encode placement in `name`; Kubernetes leaves use +`metadata.namespace`; AWS leaves use tags, the AWS Account stores no +parent at all: parenthood is queried, which independently validates the +tree-outside-the-node ADR). Our agents are the GCP Project analog exactly +(movable registry resources), which is the case where Google chose an +explicit `parent` field + move method. + +First draft chose `place` (the ADR's prose word); it was rejected as +confusing, and convention wins. The collision worry (parent +sessions/agents in delegation) dissolves under the rule everyone already +follows: **bare `parent` on a resource always means placement; kinship is +always qualified** (`parentSessionId`, `parentAgentId`, as the event +model already spells them). Still disqualified: `container` (OCI +collision), `scope` (executed in Q22), `location` (regions). + +**Q23 addendum (spelling rule, ADR#6310044131 rule 4):** `parentId` / +`parent_id` are disallowed. Placement is spelled exactly `parent`; +kinship is `parentId` (`parentSessionId`). The spelling carries the +semantics: unsuffixed = placement, type-plus-Id = kinship, bare middle +form unwritable. Consequence in the event model: the hierarchy +stream's `NodeAdded { nodeId, parentId }` becomes +`NodeAdded { nodeId, parent }` (a node's parent IS its placement). + +## Q24: The changeClass taxonomy (governs review and activation) + +**Topology superseded and field ownership refined by the final +decisions:** +`ProposalOpened` now records the `changeClass` derived from its canonical +typed difference. After approval, the application uses it to choose automatic +learned-layer activation or human-gated charter activation. The taxonomy +otherwise remains: + +**learned-layer (auto-activate after verification):** instructions/prompt, +turn wrappers, skill pins, `tools.optional`, +`delegates.optional`, `description`, labels except well-known keys. +Rationale: behavior content the loop exists to evolve; widening a +*declaration* grants nothing (grants still gate, Q8). + +**charter (escalates to a human):** `tools.required`, +`delegates.required` (changes the runnable condition; can brick session +starts), `model` default (capability/cost shift; v1 conservative), the +`family` label (moves the agent between comparison/routing groups), and +`variables_schema` (a session-caller interface; v1 treats every schema change +as charter-class). + +**not revision changes at all:** `name` (idempotency key), `runtime` +(immutable; new sibling agent), `parent` (a move: its own audited +operation), `owner` (a transfer: its own audited operation). The Agent +stream's revisions carry behavior only; placement and accountability +changes never mint revisions. + +## Q25: Labels align with ADR#5177934677 (annotations), with a selector carve-out + +The labels decision aligns with +[straw-hat-team/adr#338](https://github.com/straw-hat-team/adr/pull/338) +(ADR 5177934677, "Annotations and Transient Annotations", **finalized and +merged 2026-07-09**, with the selector clause made conditional during +finalization). Adopt it for opaque metadata, with the selector carve-out the +merged ADR permits. + +- **Adopt the annotations spec wholesale** for all decorative/opaque + metadata we previously called labels: node `kind`, `team`, `vertical`, + display names, tenant-invented keys. Kubernetes key syntax, DNS-prefix + ownership, annotations vs transient_annotations lifetime on events. Our + Q6 rationale is that ADR's opening premise. +- **The carve-out:** ADR#5177934677 rejects the labels/annotations split + conditionally: "we do not have a platform-level selector engine." The + agent platform IS one: rosters resolve family= selectors (Q13), + evaluation bindings select by label (Q15), fitness groups by family + (Q6), the steward routes on it. And selector values need bounded, + matchable syntax, while annotation values are explicitly free + (JSON-encoded blobs). So each BehaviorBundle keeps a **minimal + `selectable_labels` map for selectable keys only** (family, + binding-matchable keys) with + label-value syntax constraints, as the downstream ADR that spec invites. + Agent projections may expose the active revision's labels, but the Agent + record never stores an independently writable copy. A label change follows + Q24 and therefore requires a Proposal and a new AgentRevision. +- Consequence: `kind` moves from "labels" to annotations (it was never + selected, only displayed); `family` stays a label (it is selected). + +## Q26: The domain layer must not know principal kinds + +A Human/Machine principal enum inside `activate_revision` would be a +layering violation, the same class as rubrics-in-the-charter one level down: +**the domain may know principal IDENTITY (an opaque value its +own events recorded, compared for equality), never principal KIND** (an +ontology only authentication holds; aauth, +[ADR#0017](../../adr/0017-aauth-agent-authentication.md)). + +Layer mapping, corrected by the final stream topology: +- Proposal-stream deciders: proposer != approver (identity equality from + stream facts), at most one verdict, a withdrawn proposal takes no verdict, + and only the author withdraws. +- Registry-stream deciders: revisions mint linearly at activation, archived + agents reject activation, the same proposal cannot activate twice, and a + rollback target must have been previously active, including revision 1 made + active by provisioning. +- App-level gates at command dispatch (where aauth knows kinds): + charter-class activation requires a human; grant/policy/evaluation + mutations are human-only (invariant 3). The decider records + activated_by as opaque and documents the upstream precondition. + +Consequence: the purity rule generalizes: deciders consume stream facts +and opaque inputs; every ontology from another plane (grants, tree +validity, principal kind) is proven upstream and arrives as data. + +## Settled design core after Q16 + +1. **The charter is minimal** (Q16 final shape): identity + engine + (runtime constraint, model default) + content (instructions, skill + pins, variables) + dependency declarations (tools/delegates, + required/optional). Nothing economic, temporal, evaluative, or + reputational. +2. **Everything else is a stance on its own plane**, selector-bound, + independently human-owned: policy (grants, budgets, delegation caps), + evaluation (rubrics + bindings, the judged never owns the yardstick), + scheduling, routing, reputation (projections only). +3. **Definitions pin; authorization never pins.** Sessions freeze their + revision; grants/policies evaluate live at every call. +4. **The loop's integrity invariants:** proposer ≠ approver; machines + never widen grants; the judged never owns the yardstick; unobserved + agents don't auto-activate. +5. **Flexibility lives in the session** (extensions, overrides, all + ledgered), and the curator promotes what works: session-local + extensions are the mutations, the curator is the selection, revisions + are the heredity. +6. **Coordination:** selector rosters (family labels) with + required/optional delegates; session-to-session coupling only; + determinism routes to tools, runtimes, or external workflows, never + the agent noun (ADK's deprecation is the documented cautionary tale). + +## Open questions (to revisit) + +Resolved since first written: multi-tenancy (Q10), rubric authorship +(Q14 to Q15: registry-owned, selector-bound), skills placement (Q11), and +the change-class taxonomy (Q24). +Still open: + +- Is runtime-immutable-per-agent too strict past v1 (per-revision runtime + with migration evals instead)? +- A2A AgentCard export: at agent level or revision level? +- The template system (Q16 identified it as the biggest undesigned + dependency). +- Evaluation binding precedence rules (default vs work-item vs multiple + bindings on one outcome). diff --git a/docs/research/agent-platform/index.md b/docs/research/agent-platform/index.md new file mode 100644 index 000000000..11c6de9f0 --- /dev/null +++ b/docs/research/agent-platform/index.md @@ -0,0 +1,45 @@ +# Agent platform research corpus + +This corpus is the frozen research input behind the platform's +agent-architecture decisions: an industry study of agent products plus the +running record of the design discussion that followed it. Where a +conclusion here differs from an accepted record in the +[ADR index](../../adr/index.md), the ADR is authoritative. + +## Method + +The [research prompt](./RESEARCH_PROMPT.md) is preserved so the shared scope +and evidence rules behind each product dossier remain reproducible. + +## Product dossiers + +- [ADK + A2A](./products/adk-a2a.md) +- [AWS AgentCore Harness](./products/aws-agentcore-harness.md) +- [Bedrock AgentCore](./products/bedrock-agentcore.md) +- [Claude Code Agent SDK](./products/claude-code-agent-sdk.md) +- [Claude Managed Agents](./products/claude-managed-agents.md) +- [Cloudflare Agents](./products/cloudflare-agents.md) +- [CrewAI](./products/crewai.md) +- [Devin](./products/devin.md) +- [Hermes Agent](./products/hermes-agent.md) +- [Jido](./products/jido.md) +- [kagent](./products/kagent.md) +- [LangGraph Platform](./products/langgraph-platform.md) +- [Netclaw](./products/netclaw.md) +- [OpenAI Agents SDK](./products/openai-agents-sdk.md) +- [OpenClaw](./products/openclaw.md) +- [OpenComputer](./products/opencomputer.md) +- [Vercel](./products/vercel.md) + +## Synthesis + +- [Synthesis: what the industry means by "agent"](./synthesis.md), the + cross-product convergence and divergence analysis drawn from the dossiers + above. + +## Decision record + +- [Agent Service: design questions and answers](./decision-record.md), the + running Q&A (Q1 through Q26) that turned the synthesis into directional + design decisions, including the mistakes surfaced along the way and where + the design is heading next. diff --git a/docs/research/agent-platform/products/adk-a2a.md b/docs/research/agent-platform/products/adk-a2a.md new file mode 100644 index 000000000..7493e3102 --- /dev/null +++ b/docs/research/agent-platform/products/adk-a2a.md @@ -0,0 +1,247 @@ +# Google ADK + A2A protocol: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence snapshot retrieved 2026-07-13. Version-sensitive claims were checked +against these authoritative anchors: + +- ADK Python 2.4.0 at commit + [`3f79243`](https://github.com/google/adk-python/tree/3f7924388361522a5d1b5e0ca1adb4d33c38abf8): + [`BaseAgent`](https://github.com/google/adk-python/blob/3f7924388361522a5d1b5e0ca1adb4d33c38abf8/src/google/adk/agents/base_agent.py#L93-L147), + [`LlmAgent` and `Agent`](https://github.com/google/adk-python/blob/3f7924388361522a5d1b5e0ca1adb4d33c38abf8/src/google/adk/agents/llm_agent.py#L223-L398), + and the [`Agent` alias](https://github.com/google/adk-python/blob/3f7924388361522a5d1b5e0ca1adb4d33c38abf8/src/google/adk/agents/llm_agent.py#L1252). +- ADK documentation, + [Agents](https://google.github.io/adk-docs/agents/), + [Multi-agent systems](https://google.github.io/adk-docs/agents/multi-agents/), + [Runtime](https://google.github.io/adk-docs/runtime/), and + [Sessions](https://google.github.io/adk-docs/sessions/). +- ADK Python 2.4.0 workflow sources: the + [`SequentialAgent`](https://github.com/google/adk-python/blob/3f7924388361522a5d1b5e0ca1adb4d33c38abf8/src/google/adk/agents/sequential_agent.py#L49-L62), + [`ParallelAgent`](https://github.com/google/adk-python/blob/3f7924388361522a5d1b5e0ca1adb4d33c38abf8/src/google/adk/agents/parallel_agent.py#L160-L176), + and [`LoopAgent`](https://github.com/google/adk-python/blob/3f7924388361522a5d1b5e0ca1adb4d33c38abf8/src/google/adk/agents/loop_agent.py#L53-L66) + deprecations, plus the replacement + [`Workflow`](https://github.com/google/adk-python/blob/3f7924388361522a5d1b5e0ca1adb4d33c38abf8/src/google/adk/workflow/_workflow.py#L15-L19). +- A2A protocol 1.0.0, specifically specification sections + [2.2, 3.4, 8, and Appendix B](https://a2a-protocol.org/v1.0.0/specification/), + and the normative [`a2a.proto`](https://github.com/a2aproject/A2A/blob/3303592588e388e62e0f69f701af531d2f4e3991/specification/a2a.proto#L361-L395) + pinned at the A2A 1.0.1 repository release commit. +- Vertex AI Agent Engine + [overview](https://cloud.google.com/vertex-ai/generative-ai/docs/agent-engine/overview), + REST v1 [`ReasoningEngineSpec`](https://docs.cloud.google.com/gemini-enterprise-agent-platform/reference/rest/v1/projects.locations.reasoningEngines#ReasoningEngineSpec), + and v1beta1 [revisions and traffic](https://docs.cloud.google.com/gemini-enterprise-agent-platform/scale/runtime/manage-revisions-and-traffic), + which lists `agentCard[]` among versioned fields. + +Three layers, three models: ADK (code object), A2A (network endpoint + +manifest), Vertex Agent Engine (cloud resource). + +## The `agent` noun (primary-source quotes) + +- **ADK:** "An **Agent**, or **LlmAgent**, in ADK is a self-contained + execution unit designed to act autonomously to achieve specific goals... + The basic components of an Agent are an AI model, task instructions, and + optionally, a set of tools." In code, `BaseAgent(BaseNode)` is a pydantic + object (`name`, `description`, `parent_agent`, `sub_agents`, callbacks); + `Agent` is literally `TypeAlias = LlmAgent`. Any custom class subclassing + `BaseAgent` with `_run_async_impl` is an agent. Same model across + Python/TS/Go/Java. +- **A2A:** "An open protocol enabling communication and interoperability + between opaque agentic applications." "Agents are autonomous + problem-solvers that act independently within their environment," and + pointedly: "encapsulating an agent as a simple tool is fundamentally + limiting, as it fails to capture the agent's full capabilities." +- **The AgentCard is the interoperable definition:** "A self-describing + manifest for an agent. It provides essential metadata including the + agent's identity, capabilities, skills, supported communication methods, + and security requirements." (a2a.proto) Required fields: `name`, + `description`, `supported_interfaces` (url + protocol binding JSONRPC/ + GRPC/HTTP+JSON + protocol version), `version`, `capabilities` (streaming, + push_notifications, extended card), `default_input/output_modes`, + `skills` (id/name/description/examples); plus security schemes + (api-key/HTTP/OAuth2/OIDC/mTLS), multi-tenant routing (`tenant`), and JWS + `signatures` for card integrity. Discovered at + `/.well-known/agent-card.json` (RFC 8615), cacheable via HTTP headers, + with an authenticated extended card for sensitive detail. +- **Vertex Agent Engine:** the deployed resource is a `ReasoningEngine` + ("a customizable runtime for models to determine which actions to take + and in which order"). Its v1 `ReasoningEngineSpec` declares the framework + in `agentFramework` (`google-adk`, `langchain`, ...); the v1beta1 revision + surface lists A2A Agent Cards in `agentCard[]` as a versioned field. +- **Conceptual models:** ADK = agent-as-code-object in a tree; A2A = + agent-as-network-endpoint defined entirely by its declared manifest; + Agent Engine = agent-as-cloud-resource bridging the two. + +## Subagents + +- **ADK's `sub_agents` is a first-class tree:** parent set automatically; + "an agent instance can only be added as a sub-agent once" (single parent, + tree not DAG; no documented depth limit). +- **Delegation is LLM-driven via description:** "This description is + primarily used by *other* LLM agents to determine if they should route a + task to this agent"; the model emits + `transfer_to_agent(agent_name=...)`, or ADK "automatically generate[s] a + delegation tool for each subagent, named after the subagent itself." + Transfer can be constrained (`disallow_transfer_to_parent/peers`). +- **Agent modes govern return-of-control:** `chat` ("manual return to + parent"), `task` ("automatic return... must be leaf agents"), and + `single_turn` ("no user interaction... can be run in parallel"). +- **Workflow agents are deterministic orchestrators:** SequentialAgent + "passes the same InvocationContext to each of its sub-agents" (shared + state including `temp:`); ParallelAgent gives "each sub-agent... its own + execution branch... **no automatic sharing of conversation history or + state between these branches**"; LoopAgent iterates until escalation or + `max_iterations`. **All three are formally deprecated** (see the + dedicated section below). +- **AgentTool** wraps an agent as a tool: runs it in an isolated Runner, + forwards state/artifact deltas back, returns the final response as the + tool result. +- **A2A inverts the hierarchy entirely: peers, not parents.** Any agent can + be client and server; the remote agent is opaque ("Agents collaborate + based on declared capabilities and exchanged information, without needing + to share their internal thoughts, plans, or tool implementations"). The + unit of delegation is the **Task**, not the agent. + +## The workflow-agent deprecation (verified in source, 2026-07-08) + +Google shipped deterministic orchestrators *as agent subclasses* and then +walked it back. All three carry a runtime `@deprecated` decorator in +`adk-python` (`src/google/adk/agents/{sequential,parallel,loop}_agent.py`), +verified verbatim: + +> "SequentialAgent is deprecated in favor of Workflow and will be removed +> in a future version. Workflow cannot yet be used as an LlmAgent +> sub-agent." + +(ParallelAgent and LoopAgent carry the identical notice with their names; +the YAML `AgentConfig` loader and per-class `config_type` are deprecated +alongside them.) + +The replacement is a separate `google.adk.workflow` package, **not an +agent subclass**: "New Workflow implementation, BaseNode with graph +orchestration. Combines user-facing graph definition with the execution +engine. Workflow(BaseNode) with `_run_impl()` as the orchestration loop." +(`workflow/_workflow.py`). The module ships a full graph engine: `_graph` +(edges), node kinds (`_function_node`, `_tool_node`, `_join_node`, +`_parallel_worker`, and notably `_llm_agent_wrapper`, the LLM agent +becomes *a node inside the workflow*, inverting the old +containment), a `DynamicNodeScheduler`, `Trigger`s, retry config, and +replay/rehydration utilities (`_replay_interceptor`, +`_rehydration_utils`, durable re-execution semantics). + +Why this matters for the study: it is the clearest industry admission that +**deterministic orchestration dressed as an agent is a category error**. +The migration inverts the relationship: before, workflow-agents contained +LLM agents as sub_agents; after, a Workflow graph contains an LLM agent as +one wrapped node among function/tool nodes. The transitional caveat +("Workflow cannot yet be used as an LlmAgent sub-agent") shows the seam +mid-migration. This is the primary evidence for our design rule (service +design Q12): determinism belongs in tools, runtimes, or external +workflows, never in the agent noun. + +## Configuration surface (what, where, why) + +- **ADK LlmAgent fields** (with doc rationales): `name` ("crucial... where + agents need to refer to or delegate tasks to each other"), `description` + (routing signal), `model` (inherits from ancestor if empty; "impacts + capabilities, cost, and performance"), `instruction` ("arguably the most + critical... core task, persona, constraints, how and when to use tools, + output format"; supports `{placeholders}` and provider functions), + `static_instruction` (context-caching prefix), `global_instruction` + (root-wide, deprecated), `tools` (functions, BaseTools, or AgentTools), + `generate_content_config`, `input_schema`/`output_schema` (structured + I/O), `output_key` ("final response... automatically saved to the + session's state dictionary"), `include_contents` (`'none'` = no history), + `planner`, `code_executor`, `mode`, transfer switches, and six + model/tool/error callbacks. Config is Python constructor calls only (the + YAML config layer is deprecated). +- **A2A AgentCard is declared capability, not behavior config:** it states + what the agent can do, where to reach it, and how to authenticate: "it + does not configure internal behavior, that remains opaque." +- **Agent Engine adds ops config:** container/source spec, framework tag, + Agent Cards, and revision traffic splitting. + +## Binding time + +- **ADK: construction time.** Pydantic `model_post_init` bakes mode-derived + tools; parents bind when `sub_agents` is assigned; no late binding. + Instruction *values* can late-bind via provider functions and state + placeholders per invocation. +- **A2A: discovery time.** The card is fetched at + `/.well-known/agent-card.json`; staleness is governed by HTTP + `Cache-Control`; JWS signatures make cards verifiable; the authenticated + `GetExtendedAgentCard` RPC gates the sensitive layer. +- **Agent Engine: deploy time with revisions.** `ReasoningEngineRuntimeRevision` + is "a specific version of the runtime related part"; traffic config + chooses `trafficSplitManual` (percentage per revision) or + `trafficSplitAlwaysLatest`, the AWS/OpenComputer-style version/endpoint + split, expressed as traffic management. + +## Relationships between nouns + +- **ADK:** Runner (binds agent + session/artifact/memory/credential + services) 1:N sessions; session = "a single, ongoing interaction... + chronological sequence of Events"; invocation = "everything that happens + in response to a *single* user query" (one `invocation_id`, possibly many + agent runs); events carry `state_delta` actions. State prefixes scope + persistence: none (session), `user:` (cross-session per user), `app:` + (cross-user), `temp:` (invocation-only). Memory is the cross-session + searchable store; artifacts are saved outputs. +- **A2A:** "**Task is the core unit of action for A2A**. It has a current + status and when results are created... they are stored in the artifact. + If there are multiple turns... these are stored in history." `contextId` + "logically groups multiple Task objects and independent Message objects, + providing continuity", 1 agent : N tasks; 1 contextId : N tasks/messages; + 1 task : N artifacts/messages; messages contain Parts (text/raw/url/data). + Three interaction styles: message-only agents, task-generating agents, + hybrid agents (messages to negotiate scope, tasks to track execution). +- **Bridge:** an ADK agent deploys as a ReasoningEngine; the preview revision + surface can carry Agent Cards: code object → cloud resource → discoverable + endpoint. + +## Lifecycle + +- **ADK invocation loop (cooperative, event-sourced):** Runner receives the + query → agent yields Events → Runner commits actions (state deltas) → + agent resumes "only *after* the Runner has processed the event." State + changes are "only **guaranteed to be persisted** after the Event carrying + the corresponding state_delta has been yielded... and processed." Partial + streaming events skip action processing; only the final event commits. + Customer code owns the process locally; Agent Engine wraps the same via + `:query`/`:streamQuery`/`:asyncQuery`. +- **A2A Task state machine** (proto): `SUBMITTED`, `WORKING` (active); + `COMPLETED`, `FAILED`, `CANCELED`, `REJECTED` (terminal); + `INPUT_REQUIRED`, `AUTH_REQUIRED` (interrupted). **Terminal tasks never + restart:** "Any subsequent interaction related to that task, such as a + refinement, must initiate a new task within the same contextId", for + task immutability, "a clean mapping of inputs to outputs," and tracing + "each artifact to a specific unit of work." +- **Delivery:** polling (`GetTask`), SSE streaming (`SendStreamingMessage`, + stream closes on terminal/interrupted state, `SubscribeToTask` to + reattach), and push notifications to registered webhooks. + +## What makes it "an agent" here (our inference) + +Our inference: Google's stack answers the definition question at two +different layers on purpose. Inside a process (ADK), an agent is a named +node in a delegation tree, a code object whose `description` is its +routing contract. Between organizations (A2A), an agent is **whatever +stands behind an endpoint and honors its card**: identity + declared +skills + protocol bindings, with internals explicitly opaque, and the +*task*, not the agent, as the durable, immutable unit of work. The +A2A/MCP line is the industry's crispest boundary statement: "A2A is about +agents *partnering* on tasks, while MCP is more about agents *using* +capabilities", tools are stateless primitives; agents reason, plan, +maintain state, and negotiate. For a service design, A2A's AgentCard is +the reference schema for agent *identity*, and its task immutability rule +is the reference semantics for agent *work*. + +## Open questions + +- ADK 2.0's `Workflow` internals are now partially read from source (see + the deprecation section); its public docs and the resolution of "Workflow + cannot yet be used as an LlmAgent sub-agent" remain to watch. +- The normative AgentCard JSON Schema is deliberately a build artifact of + the proto, consumers must track the proto. +- AGNTCY/Agent Directory (cross-vendor discovery beyond well-known URIs) + did not appear in the spec pages read. +- Extended AgentCard field differences from the public card are not + documented in the fetched pages. diff --git a/docs/research/agent-platform/products/aws-agentcore-harness.md b/docs/research/agent-platform/products/aws-agentcore-harness.md new file mode 100644 index 000000000..2aec3cbe3 --- /dev/null +++ b/docs/research/agent-platform/products/aws-agentcore-harness.md @@ -0,0 +1,353 @@ +# AWS AgentCore Harness: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Follow-up dossier to [Bedrock AgentCore](./bedrock-agentcore.md), which studies +the platform whose Runtime leaves the loop to the customer. This dossier +studies the managed harness, the AgentCore surface where AWS owns the loop. +Evidence snapshot retrieved 2026-07-15. AWS documentation is mutable, so the +sections below name the page and section used: + +- AWS Developer Guide, + [AgentCore harness](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html), + [Harness vs. Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-vs-runtime.html), + and the harness sub-pages for + [getting started](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-get-started.html), + [models](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-models.html), + [tools](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-tools.html), + [skills](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-skills.html), + [memory](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-memory.html), + [environment](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-environment.html), + [operations](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-operations.html), + [versioning](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-versioning.html), + [export](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-export.html), + [security](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-security.html), + and [quotas](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/bedrock-agentcore-limits.html). +- AWS control-plane API, + [`CreateHarness`](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateHarness.html) + and [`UpdateHarness`](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_UpdateHarness.html); + data-plane API, + [`InvokeHarness`](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeHarness.html). +- GA announcement, AWS What's New, posted Jun 17, 2026, + [AgentCore harness generally available](https://aws.amazon.com/about-aws/whats-new/2026/06/amazon-bedrock-agentcore-harness-generally-available/), + the companion [AWS ML blog post, 18 JUN 2026](https://aws.amazon.com/blogs/machine-learning/amazon-bedrock-agentcore-harness-is-now-generally-available-go-from-idea-to-production-grade-agent-in-minutes/), + and the devguide [release notes](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/release-notes.html) + (public preview April 2026, GA June 2026). +- Official samples, + [awslabs/agentcore-samples `01-features/01-harness`](https://github.com/awslabs/agentcore-samples/tree/main/01-features/01-harness), + read at commit `790838a` (main branch, 2026-07-14). +- The loop engine, Strands Agents: + [agent loop](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/) + and [retry strategies](https://strandsagents.com/docs/user-guide/concepts/agents/retry-strategies/) + docs, the [strands-agents GitHub organization](https://github.com/strands-agents), + and the [AWS Open Source Blog launch post, 2025-05-16](https://aws.amazon.com/blogs/opensource/introducing-strands-agents-an-open-source-ai-agents-sdk/). +- Secondary but AWS-authored: "What is an Agent Harness? A hands-on guide + with AgentCore harness" on + [AWS Builder Center](https://builder.aws.com/content/3D3890U2niEPp5gxlssoI9HVvWm/what-is-an-agent-harness-a-hands-on-guide-with-agentcore-harness); + builder.aws.com renders client-side, so quotes were verified against the + [dev.to cross-post under the AWS organization account](https://dev.to/aws/what-is-an-agent-harness-a-hands-on-guide-with-agentcore-harness-1h33). + +## The `agent` noun (primary-source quotes) + +- AWS's definition of the harness concept itself: "Every agent has an + orchestration layer: a loop that calls the model, picks tools, passes + results back, manages context, and handles failures. Running it in + production takes real infrastructure underneath: compute, a sandbox, + secure tool connections, filesystem, memory, identity, and observability. + Together, they form the **agent harness**, the system that lets an agent + actually run." (harness) +- The managed version: "The managed agent harness in AgentCore turns that + work into configuration. You declare what your agent does (model, tools, + skills, instructions); AgentCore handles the environment, compute, memory, + identity, networking, and observability that turn the config into a + running agent." (harness) +- The decisive loop-ownership split (harness-vs-runtime): "**AgentCore + harness** is a *managed agent harness*, the orchestration loop itself is + provided, powered by Strands Agents. You declare what the agent is (model, + system prompt, tools, memory, limits) as configuration, and AgentCore runs + the loop." Runtime is the inverse: "The orchestration loop is yours." +- The loop engine is named: "The harness is powered by + [Strands Agents](https://strandsagents.com/), the open-source agent + framework from AWS." (harness) Strands Agents is AWS-authored (announced + on the AWS Open Source Blog, built from "production systems inside + Amazon", used by Amazon Q Developer, AWS Glue, and VPC Reachability + Analyzer) but lives under its own `strands-agents` GitHub organization, + not `awslabs`. +- What that loop is, in the engine's own words: "The agent loop operates on + a simple principle: invoke the model, check if it wants to use a tool, + execute the tool if so, then invoke the model again with the result. + Repeat until the model produces a final response." (strandsagents.com, + Agent Loop) The same while-loop every harness implements; here the author + and operator is AWS. +- The AWS-official hands-on guide is more explicit about assembly (secondary + source, AWS organization account on dev.to): "Under the hood, AgentCore + harness takes your config and assembles a fully wired Strands Agents + agent: the orchestration loop, tool execution, memory management, context + handling, streaming, and error recovery are all handled." And: "The + simplest way I've seen it put is Agent = Model + Harness." +- The harness is a persistent, ARN'd, versioned resource: + `arn:...:bedrock-agentcore:region:account:harness/Name-XyZ123`, created by + `CreateHarness`, invoked by `InvokeHarness` (both names literal, confirmed + against the API references), auto-versioned on every update. The quotas + page states the layering plainly: "AgentCore harness is a logical + resource. Each harness you create is backed by a managed AgentCore Runtime + that AgentCore provisions and operates on your behalf." +- **Conceptual model: agent-as-config over a vendor-owned loop.** The exact + counterpoint to the Runtime dossier's agent-as-deployed-service: the + orchestration loop is configured rather than shipped as customer code, so + the customer artifact is a versioned configuration record, and the loop + that executes it is authored, patched, and evolved by AWS as a service. + Customer code can still run at the edges (inline-function tool + implementations execute client-side, as below), but the loop itself is not + customer-owned. The customer steers it only through configuration, or + leaves it entirely via export-to-code. + +## Subagents + +- **The harness has no subagent noun.** No child agents, no delegation + primitive, no fan-out configuration appears anywhere in the harness + config surface or API. +- Multi-agent coordination is explicitly a *graduation trigger out of* the + harness: "When you outgrow configuration and need custom orchestration + logic, specialized routing, or multi-agent coordination, you can export + the harness to Strands code and keep running on the same platform." + (AWS hands-on guide) +- Agent-to-agent shows up only at the edges: `actorId` "identifies the + entity interacting with the agent (a user, another agent, or a system)", + scoping memory per actor (harness-memory); and composition happens above + the harness, "You can drop a harness into a larger pipeline through the + AgentCore InvokeHarness state in AWS Step Functions" (harness). Peer + harnesses can also share a BYO memory resource, as in the Runtime dossier. +- The Agents Classic migration guide names the sanctioned shape: "The + managed harness supports an agent-as-tool pattern for simpler multi-agent + use cases" (agents-classic-maintenance-mode), i.e. another agent fronted + by a Gateway target and listed as one of this harness's tools, a peer + wiring rather than a parent/child record. + +## Configuration surface (what, where, why) + +`CreateHarness` requires only `harnessName` and `executionRoleArn` ("The ARN +of the IAM role that the harness assumes when running"). Everything else is +optional with managed defaults: + +- **Model:** a `HarnessModelConfiguration` union (`bedrockModelConfig` | + `openAiModelConfig` | `geminiModelConfig` | `liteLlmModelConfig`). "If you + don't specify a model, the harness defaults to Anthropic's Claude Sonnet + 4.6 on Amazon Bedrock (`global.anthropic.claude-sonnet-4-6`)." Providers + can change mid-session: "Switch providers between turns of the same + session and the conversation continues. Context carries over." + Third-party keys live in AgentCore Identity's token vault: "The harness + pulls the key at invocation time. Your agent code never sees raw + credentials." +- **System prompt:** `systemPrompt` content blocks (the CLI workflow keeps + it in a `system-prompt.md` file). +- **Tools:** "Tools are declarative. You list what the agent can call; + AgentCore handles invocation, credentials, and results." Five types + (`remote_mcp`, `agentcore_gateway`, `agentcore_browser`, + `agentcore_code_interpreter`, `inline_function`) plus built-ins: "Default + tools `shell` and `file_operations` are available in every session unless + you restrict them with `allowedTools`", which takes glob patterns ("`*` + for all tools, `@builtin` for all built-in tools, or + `@serverName/toolName` for specific MCP server tools"). Inline functions + execute client-side: "The harness pauses when the tool is called and + returns the call to your code", the human-in-the-loop pattern. +- **Skills:** bundles per the AgentSkills.io spec (`SKILL.md` with YAML + frontmatter plus optional `scripts/`, `references/`, `assets/`), sourced + from `awsSkills` | `git` | `s3` | `path`. "Skills use progressive + disclosure: metadata is injected into the system prompt upfront (~100 + tokens), and full instructions are loaded on demand via a tool call." +- **Memory:** `managedMemoryConfiguration` | `agentCoreMemoryConfiguration` + (BYO) | `disabled`. "By default, the harness provisions an AgentCore + Memory instance automatically with sensible defaults (semantic + + summarization strategies, 30-day event expiry)." +- **Context truncation:** `truncation` strategy `sliding_window` (default, + keep most recent N messages) | `summarization` | `none` ("Use only if you + manage context size yourself"). +- **Execution limits**, with the stated rationale "Set hard caps so a + runaway agent can't burn through resources": `maxIterations` + ("reasoning/action cycles per invocation. Default 75."), `timeoutSeconds` + (default 3600), `maxTokens` (per-invocation output budget, no default), + `idleRuntimeSessionTimeout` (default 900), `maxLifetime` (default 28800). +- **Environment:** default or custom container (`environmentArtifact`), + `environmentVariables` (max 50), filesystem mounts (service-managed + session storage; EFS access point; S3 Files access point, the latter two + VPC-required), `authorizerConfiguration` (IAM SigV4 default or custom JWT + authorizer), tags. + +Config lives in the control-plane API (`bedrock-agentcore-control`), the +`agentcore` CLI (`create`, `deploy`, `invoke`, `export harness`), or the +console. The stated rationale for the knob design is iteration speed and +cost control: "Trying a different model or adding a new tool is a config +change, not a code rewrite"; "You can test N model/prompt/tool combinations +in the time it would take to redeploy once." The trust boundary is blunt: +"All `InvokeHarness` and `InvokeAgentRuntimeCommand` input is trusted... +The harness does not sanitize input, filter content blocks, or enforce +behavioral constraints." + +## Binding time + +- The documented principle: "This is the core of the config-based model: + **defaults at creation time, overrides at invocation time.**" + (harness-models) +- **At CreateHarness:** all defaults, snapshotted as version 1. "When you + create a harness, AgentCore automatically creates version 1 (V1)." +- **At UpdateHarness:** "Each update to the harness creates a new version + with a complete, self-contained configuration"; "Versions are immutable + once created." `harnessVersion` is "Incremented on every successful + UpdateHarness." Array fields replace wholesale; optional fields use an + `optionalValue` wrapper to distinguish leave-unchanged, set, and clear. +- **At InvokeHarness:** per-call overrides for `model`, `systemPrompt`, + `tools`, `skills`, `allowedTools`, `maxIterations`, `maxTokens`, + `timeoutSeconds`, `actorId`. "The harness resource stays unchanged; only + that call uses the overrides." Overrides never mint a version. Invoke-time + skills "are appended after create-time skills; if both define a skill with + the same name, the invoke-time version wins." +- **Endpoints are the routing layer**, same design as Runtime: "The + `DEFAULT` endpoint automatically points to the latest version"; named + endpoints pin versions and "must be explicitly updated to point to new + versions", which is also the rollback mechanism ("roll back instantly by + pointing an endpoint at an earlier version"). +- Even the model binds late: mid-session provider switching is a headline + feature, so the brain is swappable per turn while the loop stays AWS's. + +## Relationships between nouns + +- **Harness 1:N HarnessVersion** (immutable snapshots) and **1:N + HarnessEndpoint** (named pointers; DEFAULT auto-tracks latest; quota of 10 + endpoints per agent). +- **Harness 1:1 managed AgentRuntime.** "Each harness you create is backed + by a managed AgentCore Runtime that AgentCore provisions and operates on + your behalf", and the relationship is a real schema-level reference: the + harness environment carries "The ARN of the underlying AgentCore Runtime" + (`HarnessAgentCoreRuntimeEnvironment.agentRuntimeArn`, required). + CloudTrail makes the layering visible: "harness resources appear under + the `AWS::BedrockAgentCore::Runtime` resource type rather than a + harness-specific type." IAM mirrors it: "calling `InvokeHarness` requires + both `bedrock-agentcore:InvokeHarness` and + `bedrock-agentcore:InvokeAgentRuntime` permissions on the harness ARN." + This declarative wiring is the family's inversion point: the Runtime + dossier's primitives are wired in customer code with no control-plane FK, + while the harness references Memory, Gateway, Browser, and Code + Interpreter by ARN fields in its own schema, and a harness-managed Memory + points back via `managedByResourceArn`. +- **Harness 1:N Session; Session 1:1 microVM (at a time).** "Every harness + session is stateful by default and runs in a secure, isolated microVM per + session (backed by AgentCore runtime)." Sessions are identified by a + client-supplied `runtimeSessionId` ("must be at least 33 characters... + Reuse the same session ID across invocations to continue a conversation in + the same environment"). Isolation is per session, not per invocation; many + invocations share one microVM until it expires and is replaced. +- **Harness 1:1 managed Memory (by default).** `DeleteHarness` has + `deleteManagedMemory` ("Default: true. If false, the memory is + disassociated and becomes a regular customer-owned resource"). Memory + events are scoped by `actorId` + `sessionId`. +- **Isolation guarantee:** "Every session runs in its own Firecracker + microVM in AgentCore Runtime. No shared state, no shared filesystem." + +## Lifecycle + +- **Resource states:** CREATING | CREATE_FAILED | UPDATING | UPDATE_FAILED | + READY | DELETING | DELETE_FAILED; create, then poll `get-harness` until + READY. +- **Who owns the loop: AWS.** The loop runs vendor-side per invocation: + when Memory is enabled (the default), the client sends only the new message + ("You do not need to pass previous messages yourself") and the harness + loads history from Memory; when Memory is `disabled`, only the in-microVM + state carries history, so once the microVM expires (see session + persistence tiers below) the caller must supply prior conversation context + itself. Either way the harness runs the Strands loop under the configured + limits and streams results back. + `InvokeHarness` "returns a stream of events" (`messageStart`, + `contentBlockStart`, `contentBlockDelta` carrying text, `toolUse` input, + and `reasoningContent`, `contentBlockStop`, `messageStop`, `metadata` with + token usage and latency, `runtimeClientError`). `stopReason` enumerates + the loop's exit conditions: `end_turn`, `tool_use` (paused for an inline + function), `max_tokens`, `max_iterations_exceeded`, `timeout_exceeded`, + `max_output_tokens_exceeded`. The samples describe the unit being capped + as "think → act → observe loop cycles". +- **What the managed loop owns, precisely.** Confirmed in primary docs: + context-window management ("When conversation history grows beyond the + model's context window, the harness applies a truncation strategy"), + streaming, session-state persistence ("The harness automatically persists + conversation state in AgentCore Memory... even after the underlying + microVM session has expired"), and per-session isolation. Error recovery + is the one commonly-claimed harness duty AWS does *not* document at the + harness level: the API reference tells the *caller* to "implement + exponential backoff retry logic in your application" for + `InternalServerException` and `ThrottlingException`. Automatic retries are + documented only for the Strands engine itself ("agents retry throttling + errors up to 5 times (6 total attempts) with exponential backoff starting + at 4 seconds"), and tool failures feed back into the loop rather than + killing it ("the error information goes back to the model as an error + result rather than throwing an exception that terminates the loop"). +- **A second door into the environment bypasses the loop.** + `InvokeAgentRuntimeCommand` "gives you direct shell access to the harness + microVM: deterministic command execution with no model reasoning, no token + cost, no ambiguity." Commands run as root; `allowedTools` does not apply, + only its own IAM action does. +- **Session persistence tiers:** in-microVM state is bounded by + `idleRuntimeSessionTimeout` (default 15 min) and `maxLifetime` (default + 8 hrs), after which the microVM is replaced; filesystem mounts and Memory + survive replacement; skills are re-fetched on new sessions "to guarantee + freshness." +- **Graduation path:** "Export harness to code takes a managed harness + configuration and generates the equivalent agent as editable Python source + code using the Strands framework. The generated agent is a normal + AgentCore runtime agent: you own the code, can modify it freely." + Framework support "Strands, with other frameworks like Claude Agents SDK + coming soon." Loop ownership is thus a one-way door by design: config + until you need code, then you become a Runtime customer. +- **Pricing confirms the harness is pure abstraction:** "There is no + separate harness charge. You pay only for the underlying AgentCore + capabilities you use." +- **Timeline:** public preview April 2026; GA announced Jun 17, 2026 (What's + New) with the ML blog dated 18 JUN 2026. GA added default managed memory, + more providers via LiteLLM and Bedrock Mantle, the AWS skills catalog, + evaluations, versioning and endpoints, and export to Strands code. + +## What makes it "an agent" here (our inference) + +Our inference: in the harness, "agent" is **a versioned configuration record +that a vendor-owned loop makes live**. AWS supplies and operates the loop +code (a fully wired Strands agent assembled from the config); the customer +never sees or ships the loop itself (though inline-function tools can still +execute customer code client-side) and steers it only through declared +fields and per-call overrides. This completes the spectrum inside one product family: +AgentCore Runtime is the strongest agent-as-infrastructure model (the shell +is standardized, the brain is opaque customer code), while the harness is +its inverse (the loop is standardized vendor code, the agent is pure +declaration). It is the same fundamental while-loop found in every harness +in this corpus, differing only in author and degree of control, which is +exactly the axis our +[decision record Q5](../decision-record.md#q5-is-the-agent-bound-to-a-harness-the-providermodel-or-both-harnessagent-vs-agent) +models with the `runtime` field: the AgentCore harness is a live production +example of the `managed-default` pole, with export-to-code as the escape +hatch to the customer-loop pole. + +## Open questions + +- No harness-level automatic retry or error-recovery policy is documented. + The devguide's conceptual framing says the harness "handles failures," but + the API reference delegates backoff to the calling application, and retry + specifics exist only in Strands SDK docs. Whether the managed loop applies + the Strands defaults verbatim is unstated. +- The docs are internally inconsistent about one operation name: + `ListHarnessEndpoints` (harness-versioning) vs `ListHarnessesEndpoints` + (harness-get-started API list). Unresolved against the service model. +- Behavior of in-flight sessions when the DEFAULT endpoint advances to a new + version is undocumented, the same gap flagged in the Runtime dossier. +- Whether harness sessions and raw Runtime sessions share the same microVM + pool and isolation implementation is not spelled out; only the logical + layering ("backed by a managed AgentCore Runtime") is documented. +- Strands' own monorepo is named `harness-sdk` and its docs navigation + groups the SDKs under a "Strands Harness" label, but neither side formally + ties that branding to the AgentCore harness product; treat the shared + "harness" naming as related but not proven identical. +- No numeric caps on tools, skills, or MCP servers per harness were found; + the quotas page defers wholesale to Runtime quotas. +- Harness create/get responses carry no `workloadIdentityDetails` field, + unlike Runtime and Gateway; whether the harness's underlying managed + AgentRuntime still receives an auto-created workload identity is + unstated. +- Claude Agent SDK as a second export target is "coming soon" with no + committed scope or date. diff --git a/docs/research/agent-platform/products/bedrock-agentcore.md b/docs/research/agent-platform/products/bedrock-agentcore.md new file mode 100644 index 000000000..7a71dbca9 --- /dev/null +++ b/docs/research/agent-platform/products/bedrock-agentcore.md @@ -0,0 +1,378 @@ +# AWS Bedrock AgentCore: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence snapshot retrieved 2026-07-13, refreshed and extended 2026-07-15 +against the GA-era product family. AWS documentation is mutable, so the +sections below name the page and section used. Version-sensitive SDK claims +were checked against these authoritative anchors: + +- AWS Developer Guide, + [How it works: AgentCore Runtime, Versions, Endpoints, and Sessions](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-how-it-works.html), + [Runtime session lifecycle](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-sessions.html), + [lifecycle settings](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-lifecycle-settings.html), + [versioning](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/agent-runtime-versioning.html), + [troubleshooting](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-troubleshooting.html), + [Harness vs. Runtime: Conceptual difference](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-vs-runtime.html), + and [AgentCore harness](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html); + the harness has its own follow-up dossier, + [AWS AgentCore Harness](./aws-agentcore-harness.md). +- AWS Developer Guide, + [Runtime service contract: Supported protocols and HTTP protocol contract](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-service-contract.html), + [Runtime A2A support](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-a2a.html), + and [AG-UI: deploy guide](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html) + plus [protocol contract](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui-protocol-contract.html). +- AWS control-plane API, + [`CreateAgentRuntime`](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateAgentRuntime.html) + and its [`AgentRuntimeArtifact`](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_AgentRuntimeArtifact.html) + union; data-plane API, + [`InvokeAgentRuntime`](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) + and [`InvokeAgentRuntimeCommand`](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntimeCommand.html). + The full operation indexes for both planes were enumerated on 2026-07-15: + 147 control-plane and 66 data-plane operations. +- AgentCore SDK for Python 1.18.0 at commit + [`8df87bb`](https://github.com/aws/bedrock-agentcore-sdk-python/tree/8df87bb0dd67a6046413541adfeee59d69e57f49), + specifically [`BedrockAgentCoreApp`](https://github.com/aws/bedrock-agentcore-sdk-python/blob/8df87bb0dd67a6046413541adfeee59d69e57f49/src/bedrock_agentcore/runtime/app.py#L157-L216). + Re-verified 2026-07-15: v1.18.0 (tag published 2026-07-10) is still + current; `main` differs only in dependency-lock updates. +- AWS Developer Guide pages for + [Gateway](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html) + and its [key concepts](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-core-concepts.html), + [workload identities](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/understanding-agent-identities.html), + [memory strategies](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory-strategies.html), + the [`Memory`](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_Memory.html) + data type, and [direct code deployment](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-get-started-code-deploy.html). +- Timeline and successor facts: + [release notes](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/release-notes.html) + and [Amazon Bedrock Agents Classic maintenance mode](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-classic-maintenance-mode.html). + +## The `agent` noun (primary-source quotes) + +- "An AgentCore Runtime is the foundational component that hosts your AI + agent or tool code. It represents a containerized application that + processes user inputs, maintains context, and executes actions using AI + capabilities." (runtime-how-it-works) +- The decisive split (harness-vs-runtime): for the container deployment path, + "**AgentCore Runtime** is a *serverless hosting environment*. You bring + agent code, written in any framework or no framework, wrap it with the + AgentCore SDK's `BedrockAgentCoreApp` entrypoint, package it into an ARM64 + container, push it to Amazon ECR, and deploy. *The orchestration loop is + yours.*" Runtime also supports ZIP-based direct code deployment through + `codeConfiguration`; loop ownership remains with customer code. The + `CreateAgentRuntime` schema confirms this by omission: it carries no + model, system prompt, or tool fields at all; it is pure compute, network, + and protocol configuration. +- The agent is a persistent ARN'd resource: + `arn:...:bedrock-agentcore:region:account:agent/UUID:version`, note the + resource segment is literally `agent/` (pattern confirmed in the + `CreateAgentRuntime` API reference). +- AgentCore Identity gives AWS's behavioral definition: "An AI-powered + application or automated workload that performs tasks on behalf of users... + Unlike traditional applications that run with static credentials, agents + require dynamic identity management to securely access resources across + multiple trust domains." (identity-terminology) "Agent identities are + managed as specialized workload identities with agent-specific attributes + and capabilities," a subtype, not a separate noun. +- Since June 2026 the family carries a **second, coexisting agent noun**: + the harness, an ARN'd resource under `harness/` where "the orchestration + loop itself is provided, powered by Strands Agents. You declare what the + agent is (model, system prompt, tools, memory, limits) as configuration, + and AgentCore runs the loop." (harness-vs-runtime) "Who owns the loop" is + therefore no longer one platform-wide answer but a per-surface choice. + Full study in the [AWS AgentCore Harness](./aws-agentcore-harness.md) + dossier. +- **Conceptual model: agent-as-deployed-service.** On the Runtime path the + agent's logic is an opaque customer artifact, either a container image or + source package; the platform noun (AgentRuntime) is identity + versioning + + addressing + isolation around it. Framework-agnostic by design: "Works + seamlessly with popular frameworks like LangGraph, Strands, and CrewAI." + +## Subagents + +- **AgentCore itself has no subagent concept**, delegation is left entirely + to the framework inside the container ("The orchestration loop is yours"). + The SDK source contains no occurrence of "subagent" in any spelling, and + the harness tool-type enum (`remote_mcp | agentcore_browser | + agentcore_gateway | inline_function | agentcore_code_interpreter`) has no + agent-valued member. +- Multi-agent shows up as **infrastructure between peer agents**, not + parent/child records: + - A2A protocol support: "AgentCore's A2A protocol support enables seamless + integration with A2A servers by acting as a transparent proxy layer" + (port 9000, JSON-RPC 2.0, "built-in agent discovery through Agent Cards + at `/.well-known/agent-card.json`"). (runtime-a2a) + - Agent-as-tool became a config-level pattern in June 2026: "You can add + an Amazon Bedrock AgentCore Runtime agent as a target on your gateway. + Your gateway sends traffic directly to the runtime agent without + aggregation or protocol translation." (release-notes) HTTP passthrough + targets are "ideal for fronting agent URLs, external APIs, + application-to-application (A2A) agents." + - The Agents Classic migration guide states the stance directly: "You can + build multi-agent patterns on AgentCore runtime using any supported + framework. The managed harness supports an agent-as-tool pattern for + simpler multi-agent use cases." (agents-classic-maintenance-mode) + - Shared Memory resources: "A team of AI agents managing a supply chain + shares memory to synchronize inventory levels." (memory) Memory's actor + noun covers peers: an actor "can be a human user, another agent, or a + system." (memory-terminology) +- Nothing is inherited between collaborating agents: each AgentRuntime has + its own sessions (isolated microVMs), its own workload identity, and only + explicitly wired shared memory. + +## Configuration surface (what, where, why) + +`CreateAgentRuntime` fields: `agentRuntimeArtifact` (required union of +`containerConfiguration` or `codeConfiguration`), `agentRuntimeName`, +`roleArn` (IAM, required), +`networkConfiguration` (required; VPC/subnets/security groups), +`protocolConfiguration` (`HTTP` | `MCP` | `A2A` | `AG-UI`), +`authorizerConfiguration` (inbound OAuth JWT / IAM SigV4), +`environmentVariables` (max 50), `filesystemConfigurations` (max 5; +service-managed session storage is in Preview, plus BYO EFS and S3 Files), +`lifecycleConfiguration` (`idleRuntimeSessionTimeout`, `maxLifetime`; both +documented as tunable, "Valid range: 60 to 28800 seconds"), tags. The two +deployment paths differ operationally: "Direct code deployment limits the +package size to 250MB whereas container-based packages can be up to 2 GB in +size", and direct code allows 25 new sessions/second vs 1.6 for containers. +Config lives in the control-plane API (`bedrock-agentcore-control`), the +AgentCore CLI ("a Node.js command-line tool... Under the hood, the +AgentCore CLI uses AWS CDK constructs"), or the console. + +The distinctive design is that everything else an agent needs is a +**separate ARN'd primitive**. On the Runtime path these are wired in +customer code rather than by foreign key; the harness path instead wires +the same primitives by declarative reference (see Relationships): + +- **Memory**, own resource (`CreateMemory`), with strategies at three + ownership tiers: built-in ("AgentCore handles all memory extraction and + consolidation automatically"), built-in overrides (custom prompts, managed + pipeline), self-managed ("Complete ownership of memory processing + pipeline"). An episodic strategy with cross-episode "reflection" joined + the built-ins in December 2025. +- **Gateway**, "a single, secure entry point for agentic traffic: + connecting agents to tools, to other agents, and to large language + models"; now explicitly a three-headed router: MCP targets (aggregation + into "a unified virtual MCP server", tools named + `${target_name}___${tool_name}`), HTTP targets (passthrough, including + Runtime agents and A2A), and inference targets (June 2026), which route + "LLM requests to one or more model providers through a unified, + model-based routing endpoint" with built-in connectors `bedrock-mantle`, + `openai`, and `anthropic`. Bedrock Mantle is Bedrock's OpenAI-compatible + inference endpoint ("powered by Mantle, a distributed inference engine") + and is how third-party models such as OpenAI GPT-5.5 are served on + Bedrock. +- **Identity**, workload identity auto-created per AgentRuntime and, per + the same page, per Gateway ("AgentCore Gateway also creates workload + identities automatically for agents deployed through the gateway + service"); inbound auth (who can invoke) vs outbound auth (OAuth 2LO/3LO, + API keys in a token vault keyed by agent + user). Payment credential + providers (Coinbase CDP, Stripe Privy) joined the vault in Preview, May + 2026. +- **Policy** (GA March 2026), Cedar policy engines attached to Gateways: + "the policy engine intercepts all agent requests and determines whether + to allow or deny each action based on the defined policies", and since + June 2026 also enforces Bedrock Guardrails "at the gateway layer, outside + the agent's code, where the agent cannot reason around them." +- **Built-in tools** (Browser, Code Interpreter), own resources with system + defaults `aws.browser.v1` and `aws.codeinterpreter.v1` and their own + session APIs. +- **The evaluation family** (Evaluations GA March 2026; Optimization GA + June 2026): ARN'd Evaluators scoring at tool-call, trace, or session + level; online/batch/on-demand evaluation configs; Datasets (Preview); and + Configuration bundles, "Versioned, immutable snapshots of agent + configuration (system prompts, model IDs, tool descriptions) that + decouple agent behavior from code." +- **Registry** (Preview, April 2026), "a centralized catalog for + organizing, curating, and discovering resources" covering "MCP servers, + tools, agents, agent skills, and custom resources", with an approval + workflow on records. + +Stated rationale: "The AgentCore Runtime handles scaling, session management, +security isolation, and infrastructure management, allowing you to focus on +building intelligent agent experiences rather than operational complexity"; +"Gateway eliminates weeks of custom code development"; the services "work +together or independently with any open-source framework", modularity is the +explicit design goal. + +The protocol contract for the container is fixed for custom HTTP containers: +`POST /invocations`, `GET /ping` (returns `Healthy | HealthyBusy`, +HealthyBusy keeps a session alive during async work), plus a first-class +WebSocket `/ws` route; the SDK's `BedrockAgentCoreApp` is a Starlette app +auto-wiring exactly these three routes, and `@app.entrypoint` registers the +customer's handler. AG-UI, formerly undocumented beyond a table row, now has +a dedicated contract and deploy guide: "AgentCore's AG-UI protocol support +enables integration with agent user interface servers by acting as a proxy +layer" on the same port 8080 paths, selected at deploy time via +`--protocol AGUI`. + +## Binding time + +- **At CreateAgentRuntime:** deployment artifact, protocol, network, IAM role, + authorizer, env vars, filesystem, lifecycle, snapshotted as Version 1. +- **Versioning is automatic and immutable:** "Each update to the AgentCore + Runtime creates a new version with a complete, self-contained + configuration"; "Versions are immutable once created." + (agent-runtime-versioning) +- **Endpoints are the routing layer:** "The `DEFAULT` endpoint automatically + points to the latest version"; named endpoints (e.g. `production-endpoint`) + pin specific versions and "must be explicitly updated to point to new + versions." Endpoint states: CREATING, READY, UPDATING, and failure states. +- **At InvokeAgentRuntime:** `runtimeSessionId` (session identity; new id = + new microVM), `qualifier` (which endpoint/version), `payload` (up to + 100 MB). +- **In-flight sessions are pinned, now documented:** "Each microVM session + uses the code assets (agentRuntimeArtifact) that were deployed at the time + of microVM creation. If you update your agent runtime with new code, + existing sessions will continue using the previous version until they + terminate and new sessions are created." (runtime-lifecycle-settings, with + the same behavior in runtime-troubleshooting) This resolves the prior + snapshot's inferred answer: updates never move a live session. +- **A request-scoped binding axis exists in preview:** Configuration + bundles use git-style versioning (`branchName` defaulting to `mainline`, + UUID `versionId`) and are resolved per invocation from OTEL baggage + (`aws.agentcore.configbundle_arn` + version) inside + `BedrockAgentCoreApp`, cached once per microVM process. Gateway can stamp + a routing-experiment variant into the same baggage, steering which + configuration a given request binds to without any stored FK. + +## Relationships between nouns + +- **AgentRuntime 1:N AgentRuntimeVersion** (immutable snapshots) and + **1:N AgentRuntimeEndpoint** (named pointers to versions; DEFAULT + auto-tracks latest). +- **AgentRuntime 1:N Session; Session 1:1 microVM at a time.** A session is + identified by `runtimeSessionId` and receives a dedicated execution + environment (microVM). The application can supply the ID, or Runtime can + generate it on the first invocation when omitted. Sessions are a + data-plane concept, not a control-plane resource. "AgentCore does not + enforce session-to-user mappings, your client backend should maintain the + relationship between users and their session IDs." +- **"Session" is now three distinct nouns.** The runtime session above; the + Memory conversational session (`ListSessions` is keyed by memory + actor, + "Empty sessions are automatically deleted after one day."); and Gateway + MCP Sessions (May 2026), "a unique Mcp-Session-Id on initialize... + scoped per authenticated user with configurable timeouts (default 1 hour, + up to 8 hours)." None of the three are FK-linked to each other. +- **AgentRuntime 1:1 WorkloadIdentity** (auto-created); Gateway responses + carry the same `workloadIdentityDetails`, so the 1:1 auto-creation + extends to Gateway. Manual creation can mint "multiple identities for the + same agent in different environments", so 1:1 is the automatic-path + default, not a hard invariant. +- **Memory to Agent: decoupled on the Runtime path, with one new + exception.** Memory is its own resource; binding happens in code. + Memory:Session is many-to-many via `sessionId`/`actorId` tags; Memory 1:N + strategies 1:N extracted records in namespaces. The `Memory` object now + carries `managedByResourceArn`: "ARN of the resource managing this memory + (e.g. a harness). When set, strategy modifications and deletion are only + allowed through the managing resource", the first control-plane + back-pointer in the family. +- **Gateway to Agent: decoupled**, wired by target configuration, though a + Runtime can now be locked to accept traffic only from its fronting + Gateway (`aws:SourceArn` policy for SigV4, `allowedWorkloadConfiguration` + for JWT). +- **The harness wires the same primitives by declarative FK**, inverting + the code-wiring rule: its schema requires an underlying AgentRuntime + (`agentRuntimeArn`, required), references Memory by ARN or auto-creates a + managed one, and points tools at Gateway/Browser/Code Interpreter ARNs + (built-in singletons used when omitted). See the + [harness dossier](./aws-agentcore-harness.md). +- **Isolation guarantee, platform-wide:** "Each user session receives its + own dedicated microVM with isolated CPU, memory, and filesystem + resources... After session completion, the entire microVM is terminated + and memory is sanitized... eliminating cross-session contamination + risks." The Browser and Code Interpreter session pages repeat the same + microVM-per-session, sanitize-on-termination wording verbatim, so this is + an AgentCore Tools primitive, not a Runtime-only property. + +## Lifecycle + +- **AgentRuntime:** CREATING → READY (or CREATE_FAILED); UPDATING → READY; + DELETING. Updates mint versions. +- **Session states:** Active ("processing a sync request, executing a + command, or doing background tasks"), Idle, and a third state the docs + name inconsistently: runtime-how-it-works still says Terminated, while + the dedicated sessions page says Stopped and reframes it as the same + session resuming later: "The session transitions back to Active on the + next invocation and a new compute is provisioned... The session itself + remains valid until the AgentCore Runtime ARN is deleted." Termination + triggers: inactivity (default 15 minutes), max compute lifetime (default + 8 hours), an explicit StopRuntimeSession, or unhealthy compute. +- **Who owns the loop: the customer, on this path.** The platform calls + `POST /invocations` (or the configured entry point for direct code + deployment). This is the inverse of Managed Agents/OpenComputer, and + since June 2026 also the inverse of AWS's own harness. +- **Two side doors into the session's environment** bypass the agent loop: + `InvokeAgentRuntimeCommand` "Executes a command in a runtime session + container and streams the output back to the caller", sharing the + session's container and filesystem with agent invocations; and + interactive shells (June 2026) give "persistent terminal access to their + sandboxed environment", with "up to 10 concurrent shell sessions" per + runtime session, addressed by session id + shell id over WebSocket. +- **Persistence tiers:** in-microVM state is ephemeral ("Session state is + ephemeral and should not be used for long-term durability (use AgentCore + Memory for context durability)"); managed session storage (Preview) + survives stop/resume but "Session data is deleted (reset to a clean + state)" after 14 idle days or on any runtime version update; BYO EFS and + S3 Files mounts and Memory resources persist across sessions. +- **Pricing confirms the session as the unit of execution:** "you only pay + for actual resource consumption during your session, which spans from + microVM boot... until session termination," per-second CPU + peak memory. + Default quotas were raised in June 2026: "Active session workloads per + account are now 5,000 in US East (N. Virginia) and US West (Oregon), and + 2,500 in other AWS Regions... The InvokeAgentRuntime API rate has + increased from 25 TPS to 200 TPS per agent, per account." +- **Predecessor contrast, sharpened:** "Amazon Bedrock Agents (launched + November 2023) is now Amazon Bedrock Agents Classic and will no longer be + open to new customers starting on July 30, 2026." The mechanism is + narrow: "Only CreateAgent and InvokeInlineAgent are restricted for + accounts without prior usage", every other API stays available, and + "There is no migration deadline." Agents Classic was the fully + declarative opposite, AWS owned the loop, agents were console-configured + resources with action groups and prompt overrides. AgentCore inverted the + ownership; the harness now re-adds a managed-loop option on top, and AWS + names AgentCore the recommended migration path (its docs avoid the word + successor). + +## What makes it "an agent" here (our inference) + +Our inference: in AgentCore, "agent" is **whatever the customer's deployed +artifact does**, with the platform defining the *service shell* an agent +needs: a versioned, ARN-addressed deployment with per-session microVM +isolation, plus identity, memory, tool-gateway, policy, and evaluation as +detachable primitives. The platform's operational definition of agent-ness +is infrastructural: an agent is a workload that needs dynamic multi-domain +credentials, session isolation, and long-lived context; on the Runtime path +the loop itself is out of scope. It remains the strongest +agent-as-infrastructure model in the study, and the family now demonstrates +both poles on one substrate: the same microVM, version, and endpoint +machinery serves customer-loop agents (Runtime) and AWS-loop agents (the +harness), which is the clearest statement yet that the industry can +standardize the shell without standardizing the brain, and then sell the +brain's loop as an option. + +## Open questions + +- The docs disagree on the third session state's name (Terminated on + runtime-how-it-works vs Stopped on runtime-sessions) and framing (new + execution environment vs same session resuming); unresolved by any + errata. +- The Harness (declarative config layer backed by Strands) blurs the + loop-ownership story; answered in the follow-up dossier + [AWS AgentCore Harness](./aws-agentcore-harness.md). +- Whether a harness's underlying managed AgentRuntime receives an + auto-created workload identity is not surfaced: harness create/get + responses carry no `workloadIdentityDetails` field, unlike Runtime and + Gateway. +- How `managedByResourceArn` gets set on a Memory resource is undocumented: + it appears in responses but is not a settable request field on + CreateMemory/UpdateMemory. +- Governance beyond the harness exception is still IAM, not the data + model: Runtime-path Memory/Gateway/Identity have no control-plane FK to + AgentRuntime, and resource policies attach independently per ARN + ("Resource-based policies control which principals can invoke and manage + Agent Runtime, Endpoint, and Gateway resources."). +- Registry, Policy, and Payments were confirmed as nouns with their own + CRUD surfaces, but their field-level schemas and their relationships to + the agent nouns (approval workflow targets, policy-to-tool binding, + payment session ownership) were not studied to the same depth; candidates + for a follow-up pass. diff --git a/docs/research/agent-platform/products/claude-code-agent-sdk.md b/docs/research/agent-platform/products/claude-code-agent-sdk.md new file mode 100644 index 000000000..dfbb5434e --- /dev/null +++ b/docs/research/agent-platform/products/claude-code-agent-sdk.md @@ -0,0 +1,217 @@ +# Claude Code / Claude Agent SDK: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence snapshot retrieved 2026-07-13. Version-sensitive claims were checked +against these authoritative anchors: + +- Claude Code documentation, + [How Claude Code works](https://code.claude.com/docs/en/how-claude-code-works), + [Glossary](https://code.claude.com/docs/en/glossary), and + [Create custom subagents](https://code.claude.com/docs/en/sub-agents), + specifically "Choose the subagent scope," "Write subagent files," "Spawn + nested subagents," and "Manage subagent context." +- Claude Agent SDK documentation, + [Overview](https://code.claude.com/docs/en/agent-sdk/overview) and + [Subagents in the SDK](https://code.claude.com/docs/en/agent-sdk/subagents). +- Claude Code documentation, + [Agent teams](https://code.claude.com/docs/en/agent-teams), specifically + "Compare with subagents" and "Architecture." +- Claude Agent SDK for Python 0.2.116 at commit + [`528265f`](https://github.com/anthropics/claude-agent-sdk-python/tree/528265fa09da954f0a0da1bf31e16db32b510138), + which bundles Claude Code 2.1.207. The source anchors are + [`AgentDefinition`](https://github.com/anthropics/claude-agent-sdk-python/blob/528265fa09da954f0a0da1bf31e16db32b510138/src/claude_agent_sdk/types.py#L83-L102), + [`ClaudeAgentOptions.agents`](https://github.com/anthropics/claude-agent-sdk-python/blob/528265fa09da954f0a0da1bf31e16db32b510138/src/claude_agent_sdk/types.py#L1943-L1963), + and the pinned + [CLI version](https://github.com/anthropics/claude-agent-sdk-python/blob/528265fa09da954f0a0da1bf31e16db32b510138/src/claude_agent_sdk/_cli_version.py#L1-L3). +- Anthropic's [Agent SDK to Managed Agents migration](https://platform.claude.com/docs/en/managed-agents/migration), + specifically "From the Claude Agent SDK" and "What changes," anchors the + process, persistence, and versioning comparison. + +## The `agent` noun (primary-source quotes) + +The answer is layered, three distinct uses of "agent": + +- **The harness + model is the agent.** "Claude Code serves as the **agentic + harness** around Claude: it provides the tools, context management, and + execution environment that turn a language model into a capable coding + agent." (how-claude-code-works) Glossary: "Claude Code is the harness; + Claude is the model inside it. The harness supplies file access, shell + execution, permission gating, memory loading, and the loop that chains + actions together." The "agentic loop" is "gather context, take action, + verify results, and repeat until done." +- **Subagents are markdown-file configs.** "Subagents are Markdown files with + YAML frontmatter." "Each subagent runs in its own context window with a + custom system prompt, specific tool access, and independent permissions." + (sub-agents) +- **SDK agents are per-call config objects.** The Python `AgentDefinition` + dataclass: `description`, `prompt`, `tools`, `disallowedTools`, `model`, + `skills`, `memory`, `mcpServers`, `initialPrompt`, `maxTurns`, + `background`, `effort`, `permissionMode` (`types.py`). Passed via the + `agents` dict on `query()` options; "Programmatically defined agents take + precedence over filesystem-based agents with the same name." +- **Identity is ephemeral by default:** "Each subagent invocation creates a + new instance with fresh context." Persistence is opt-in per definition via + the `memory` field (`user` | `project` | `local`), which persists a + directory across sessions, memory persists, the instance does not. +- **Conceptual model: agent-as-markdown-file (config) that instantiates as an + ephemeral process.** The definition is a file/object; the invocation is a + fresh context window. No server-side resource, no ID, no version field. + +## Subagents + +The richest subagent semantics of any product studied: + +- **Five declaration scopes with priority:** managed settings (org-wide, 1) + → `--agents` CLI JSON (session, 2) → `.claude/agents/` (project, 3) → + `~/.claude/agents/` (user, 4) → plugin `agents/` dirs (5). Project + discovery walks up to the repo root; plugin subfolders become scoped ids + (`my-plugin:review:security`). +- **Frontmatter schema:** `name`, `description` (required); `tools`, + `disallowedTools`, `model` (default `inherit`), `permissionMode`, + `maxTurns`, `skills` (preloaded), `mcpServers`, `hooks` (scoped to the + subagent), `memory`, `background`, `effort`, `isolation: worktree`, + `color`, `initialPrompt`. +- **Invocation is description-driven:** "Claude uses each subagent's + description to decide when to delegate tasks", routing is an LLM decision + over the `description` field; explicit paths are natural language, + @-mention, or `claude --agent name` (the main session adopts the + definition). +- **Inheritance rules are precise.** Isolated: context window ("Subagents + receive only this system prompt plus basic environment details... not the + full Claude Code system prompt"), no parent conversation history or tool + results, separate prompt cache. Inherited: all tools by default (minus a + hard-denied set: AskUserQuestion, plan-mode tools, ScheduleWakeup), model + (`inherit`), permission context ("bypassPermissions and acceptEdits from + parent take precedence and cannot be overridden"), CLAUDE.md at every + level, git status snapshot, extended-thinking config. Built-in Explore and + Plan skip CLAUDE.md and git status "to keep research fast and inexpensive." +- **Fork is a special subagent:** "A fork is a subagent that inherits the + entire conversation so far instead of starting fresh"; same system + prompt/tools/model as the parent, shared prompt cache. "A fork can't spawn + another fork." +- **Nesting:** "As of Claude Code v2.1.172, a subagent can spawn its own + subagents... A subagent at depth five doesn't receive the Agent tool and + can't spawn further. The limit is fixed and not configurable." +- **Foreground/background:** "As of v2.1.198, subagents run in the background + by default. Claude runs a subagent in the foreground when it needs the + result before continuing." `background: true` forces it. +- **Communication:** prompt in, final message out, "The only channel from + parent to subagent is the Agent tool's prompt string"; "The parent receives + the subagent's final message verbatim as the Agent tool result." Resumable: + the parent gets an agent ID and can continue it via SendMessage. SDK + messages carry `parent_tool_use_id` for attribution. +- **Contrast, agent teams** (experimental): "Multiple independent Claude + Code sessions coordinated by a team lead, with a shared task list and + peer-to-peer messaging. Unlike subagents, which run within a single session + and report only to the parent, teammates each have their own context + window and you can interact with any of them directly." + +## Configuration surface (what, where, why) + +- **Definition-level** (frontmatter / AgentDefinition): see schema above. + Notable knobs absent from the programmatic type: `isolation` and `color` + are frontmatter-only; TypeScript adds + `criticalSystemReminder_EXPERIMENTAL`. +- **Session/harness-level** (SDK `ClaudeAgentOptions` / `Options`): + `system_prompt` (string or `claude_code` preset), `tools`/`allowed_tools`/ + `disallowed_tools`, `mcp_servers`, `permission_mode`, `model`, `effort`, + `max_turns`, `max_budget_usd`, `cwd`, `hooks`, `agents` dict, + `setting_sources` (which filesystem config to load), `skills`, `plugins`, + `resume`/`continue`/`fork_session`, `session_store`, + `enable_file_checkpointing`. +- **Where config lives:** markdown files in scope directories; JSON via CLI + flag; code via SDK; `settings.json` (`"agent": "code-reviewer"` default + agent). Versioning is whatever git provides, no built-in version field. +- **Stated rationale** (sub-agents, SDK overview): context preservation + ("keeping exploration and implementation out of your main conversation"), + constraint enforcement ("limiting which tools a subagent can use, reducing + the risk of unintended actions"), reuse across projects, specialization + ("focused system prompts for specific domains"), cost control ("routing + tasks to faster, cheaper models like Haiku"), parallelization ("independent + subtasks finish in the time of the slowest one rather than the sum"). + +## Binding time + +- **Live reload at file level:** "Claude Code watches `~/.claude/agents/` and + `.claude/agents/`... detects the change within a few seconds and the next + delegation uses the updated definition, with no restart needed." + (Exceptions: brand-new agents directories, `--disable-slash-commands`.) + Binding is therefore **per-invocation**, not per-session: an edited .md + file affects the next delegation, unlike Managed Agents/OpenComputer where + sessions pin a snapshot. +- **SDK agents bind per `query()` call** and are ephemeral, no persistence + between calls. +- **In-flight subagents** are not restated to be affected; the new definition + applies at next invocation. +- **No versioning primitive.** Definitions are files; history is git's + problem. This is the sharpest contrast with the server-side products + (Managed Agents `version` field, OpenComputer revisions). + +## Relationships between nouns + +- **Session** = "a conversation tied to your current directory, with its own + independent context window." The main session *is* the agent (harness + + model + optional named definition via `--agent`). +- **Subagent ⊂ session:** "Subagents work within a single session", they are + context-window-scoped, not sessions of their own. Lifetime bounded by the + task; transcript persists separately + (`~/.claude/projects/{project}/{sessionId}/subagents/agent-{agentId}.jsonl`). +- **Agent teams:** each teammate is a full independent session, the + many-session shape that subagents deliberately are not. +- **Skill vs subagent:** skills run inline in the caller's context (no + isolation); subagents isolate. "Consider Skills instead when you want + reusable prompts or workflows that run in the main conversation context." +- **Hook vs subagent:** hooks are deterministic lifecycle triggers; subagent + delegation is model-discretionary. +- **Task tool → Agent tool:** "In version 2.1.63, the Task tool was renamed + to Agent", the delegation primitive is literally a tool call. +- **Built-ins:** Explore, Plan, general-purpose, statusline-setup, + claude-code-guide ship as predefined subagents. + +## Lifecycle + +- **Invocation lifecycle:** Agent tool call → fresh context window (agent + prompt + task message + CLAUDE.md + git snapshot + preloaded skills) → own + agentic loop → final text-only message returned as the tool result → + context discarded (resumable by ID while the session lives). +- **Session lifecycle:** continue (most recent), resume (by ID), fork (copy + history). "Sessions persist the **conversation**, not the filesystem." + Transcripts are JSONL under `~/.claude/projects//`; cleanup + after `cleanupPeriodDays` (default 30). +- **Who runs the loop:** the CLI harness or the embedded SDK runtime, "the + SDK runs the same execution loop that powers Claude Code." Customer infra, + customer process (the SDK's explicit contrast with Managed Agents: "Runs + in: Your process, your infrastructure" vs "Anthropic-managed + infrastructure"; "Session state: JSONL on your filesystem" vs + "Anthropic-hosted event log"). +- **What persists:** CLAUDE.md (re-injected every turn), session and subagent + transcripts, auto memory (`MEMORY.md` per repo), opt-in agent memory + directories. By default nothing persists per-subagent except its final + message in the parent transcript. + +## What makes it "an agent" here (our inference) + +Our inference: Claude Code defines an agent **behaviorally, not as a +resource**, anything running the gather-act-verify loop inside the harness +is an agent, and "an agent" as a noun is just a markdown/config overlay +(prompt + tool restrictions + model + permissions) applied to that loop. The +unit of reuse is a file, the unit of execution is a context window, and +identity/versioning are delegated to the filesystem and git. It is the purest +"agent = configured loop" model in the study, and its subagent machinery +(five scopes, description-driven routing, inheritance rules, depth-5 nesting, +fork, background) is the most elaborated answer to delegation, the exact +opposite end of the spectrum from Devin's agent-as-product. + +## Open questions + +- Agent teams are experimental and env-gated; whether the team/teammate + becomes a first-class noun is open. +- No versioning primitive: how organizations manage agent definition drift + across machines (managed settings scope exists, but no version pinning). +- The [TypeScript SDK changelog through 0.3.207](https://github.com/anthropics/claude-agent-sdk-typescript/blob/79b6350e13cf24af94a8d2e696a0883fd8cc55fe/CHANGELOG.md#L45-L55) + records workflow progress events and a phase counter in the 0.3.198 entry, + but the workflow noun and lifecycle were not deeply documented in the + sources read. +- `isolation: worktree` semantics beyond "temporary git worktree" (cleanup, + merge-back) not fully specified in the docs read. diff --git a/docs/research/agent-platform/products/claude-managed-agents.md b/docs/research/agent-platform/products/claude-managed-agents.md new file mode 100644 index 000000000..2b659ba94 --- /dev/null +++ b/docs/research/agent-platform/products/claude-managed-agents.md @@ -0,0 +1,230 @@ +# Claude Managed Agents: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from Anthropic's platform docs (beta `managed-agents-2026-04-01`) and +the official API reference skill in `anthropics/skills`. + +## Source anchors + +Sources retrieved 2026-07-13. Managed Agents was a beta API identified by +`managed-agents-2026-04-01`; no SDK package version is implied by this dossier. + +- Product definition and resource lifecycle: [Overview, sections "Core + concepts" and "Beta access"](https://platform.claude.com/docs/en/managed-agents/overview) + and [Agent setup, sections "Agent configuration fields", "Update + semantics", and "Agent lifecycle"](https://platform.claude.com/docs/en/managed-agents/agent-setup). +- Session and environment behavior: [Start a session](https://platform.claude.com/docs/en/managed-agents/sessions), + [Session operations](https://platform.claude.com/docs/en/managed-agents/session-operations), + and [Cloud environment setup](https://platform.claude.com/docs/en/managed-agents/environments). +- Delegation and durable services: [Multi-agent sessions](https://platform.claude.com/docs/en/managed-agents/multi-agent), + [Using agent memory](https://platform.claude.com/docs/en/managed-agents/memory), + [Scheduled deployments](https://platform.claude.com/docs/en/managed-agents/scheduled-deployments), + and [Self-hosted sandboxes](https://platform.claude.com/docs/en/managed-agents/self-hosted-sandboxes). +- Skills, credentials, and permissions: [Agent Skills](https://platform.claude.com/docs/en/managed-agents/skills), + [Authenticate with vaults](https://platform.claude.com/docs/en/managed-agents/vaults), + and [Permission policies](https://platform.claude.com/docs/en/managed-agents/permission-policies). +- Stable API snapshot: `anthropics/skills` commit + [`9d2f1ae`](https://github.com/anthropics/skills/commit/9d2f1ae187231d8199c64b5b762e1bdf2244733d), + especially [endpoint and lifecycle semantics](https://github.com/anthropics/skills/blob/9d2f1ae187231d8199c64b5b762e1bdf2244733d/skills/claude-api/shared/managed-agents-api-reference.md#L1-L60), + [session and agent fields](https://github.com/anthropics/skills/blob/9d2f1ae187231d8199c64b5b762e1bdf2244733d/skills/claude-api/shared/managed-agents-core.md#L79-L158), + and [multi-agent roster, thread, and limit semantics](https://github.com/anthropics/skills/blob/9d2f1ae187231d8199c64b5b762e1bdf2244733d/skills/claude-api/shared/managed-agents-multiagent.md#L1-L56). + +## The `agent` noun (primary-source quotes) + +- "An agent is a **reusable, versioned configuration** that defines persona + and capabilities. It bundles the model, system prompt, tools, MCP servers, + and skills that shape how Claude behaves during a session." + ([Agent setup](https://platform.claude.com/docs/en/managed-agents/agent-setup)) +- Concept table: "**Agent** | The model, system prompt, tools, MCP servers, + and skills" and "**Session** | A running agent instance within an + environment, performing a specific task." + ([Overview](https://platform.claude.com/docs/en/managed-agents/overview)) +- "Create the agent once as a reusable resource and reference it by ID each + time you start a session. Agents are versioned and easier to manage across + many sessions." + ([Agent setup](https://platform.claude.com/docs/en/managed-agents/agent-setup)) +- "Step one of every flow. Sessions require a pre-created agent, there is no + inline agent config under `managed-agents-2026-04-01`." + ([API reference snapshot](https://github.com/anthropics/skills/blob/9d2f1ae187231d8199c64b5b762e1bdf2244733d/skills/claude-api/shared/managed-agents-api-reference.md#L49-L60)) +- The API object is a persistent, versioned resource: `id` + (`agent_01...`), `type: "agent"`, `name`, `model` (`{id, speed}`), `system`, + `tools`, `skills`, `mcp_servers`, `metadata`, `version`, `created_at`, + `updated_at`, `archived_at`. +- **Conceptual model: agent-as-config with strong identity.** A server-side, + versioned configuration resource; the running thing is always the session. + +## Subagents + +- First-class, via the `multiagent` field: "Multi-agent orchestration lets one + agent coordinate with others... Agents can act in parallel with their own + isolated context." + ([Multi-agent sessions](https://platform.claude.com/docs/en/managed-agents/multi-agent)) +- **Declared, not ad-hoc:** "When defining your agent, set `multiagent` to + declare the roster of agents the coordinator can delegate to." Roster + entries are other agents by id (optionally pinned to a `version`) or + `{"type": "self"}` (the coordinator spawns copies of itself). The roster is + "snapshotted when the coordinator is created or updated", referenced agents + "do not automatically pick up later updates to their definitions." +- **Spawned at runtime as session threads:** "each agent runs in its own + **session thread**, a context-isolated event stream with its own + conversation history"; "additional threads are spawned at runtime when the + coordinator delegates work." +- **Inherited vs isolated:** "All agents share the same sandbox, filesystem, + and vault credentials"; "Each agent uses its own configuration: model, + system prompt, tools, MCP servers, and skills... Tools, MCP servers, and + context are not shared." Session-level overrides apply only to the + coordinator and its `self` copies. +- **Limits:** "The coordinator can only delegate to one level of agents; + depth > 1 is ignored. A maximum of 20 unique agents can be listed in + `multiagent.agents`, but the coordinator can call multiple copies of each + agent." "A maximum of 25 concurrent threads are supported." +- **Communication:** message passing between threads, visible as events + (results delivered with `from_session_thread_id`/`from_agent_name`; + follow-ups with `to_session_thread_id`). "Threads are persistent: the + coordinator can send a follow-up to an agent it called earlier, and that + agent retains everything from its previous turns." + +## Configuration surface (what, where, why) + +Agent-level fields (agent-setup): `name` (required), `model` (required, id or +`{id, speed}`), `system` (up to 100k chars), `tools` (pre-built +`agent_toolset_20260401`, MCP, custom), `mcp_servers`, `skills` (max 20 per +session), `multiagent` (coordinator roster), `description`, `metadata` (max +16 pairs). + +Session-level: `agent` (id, pinned version, or overrides object), +`environment_id`, `vault_ids` (session-scoped credentials), `resources` +(memory stores, files, GitHub repos), `title`, `metadata`. + +Environment-level (a separate resource): `type` (`cloud` | `self_hosted`), +`packages` (apt/cargo/gem/go/npm/pip), `networking` (`unrestricted` or +`limited` with `allowed_hosts`). + +Where config lives: server-side API objects (Console dashboard on top); +skills are filesystem bundles (`SKILL.md` + files) uploaded via the Skills +API. Notably there is no repo-file config, no AGENTS.md/CLAUDE.md; that is a +Claude Code concept, and branding rules explicitly separate the products +("Not permitted: 'Claude Code' or 'Claude Code Agent'"). + +Stated rationale for knobs: + +- Skills: "Unlike prompts (conversation-level instructions for one-off + tasks), skills load on demand, only impacting the context window when + needed." (managed-agents/skills) +- Environments: "You create an environment once, then reference its ID each + time you start a session." (managed-agents/environments) +- Vaults: "The vault reference is a per-session parameter, so you can manage + your product at the `agent` resource granularity and your users at the + `session` resource granularity." (managed-agents/vaults), i.e. agent = + product config, session = end-user instance. +- Permission policies: MCP tools default to `always_ask` because "new tools + added to an MCP server do not execute in your application without + approval." (managed-agents/permission-policies) + +## Binding time + +- **Agent create/update:** `model`, `system`, `tools`, `mcp_servers`, + `skills`, `multiagent` are bound to a numbered version. "Updating an agent + generates a new version when the configuration changes. The `version` field + is required and must match the agent's current version... A version + mismatch returns a 409." No-op updates create no version. The multiagent + roster snapshots referenced agents' versions at that moment. +- **Session create:** the session binds an agent (latest or pinned version) + plus per-session overrides, "Overrides apply only to the session you + create. They do not modify the agent resource or create a new agent + version." `vault_ids` and memory stores bind here; "Memory stores can only + be attached at session creation time." +- **Mid-session (partially mutable):** "You can update a session's + `agent.tools` and `agent.mcp_servers`, including permission policies, + mid-session... Updates are session-local and do not propagate back to the + underlying agent." "Only the agent's `tools` and `mcp_servers` can change + after a session is created," and "The session must be `idle` to update the + agent." The `system` field is fixed for the session's lifetime (on Opus 4.8 + a `system.message` event can replace the effective prompt between turns). + Files and GitHub repos can be added post-creation via the resources + endpoint. +- **Archive:** "Archiving makes the agent read-only and cannot be undone. + Existing sessions continue to run, but new sessions cannot reference the + agent." + +## Relationships between nouns + +- **Agent : Session, 1:many.** Session = "a running agent instance within an + environment." Sessions cannot exist without a pre-created agent. +- **Agent : Environment, orthogonal.** Independent resources, both + referenced at session creation. Environment = where; agent = who/what. +- **Environment : Session, 1:many; Session : Sandbox, 1:1.** "Multiple + sessions can share the same environment, but each session gets its own + isolated sandbox (a fresh Linux container)." +- **Session : Thread, 1:many** (multi-agent): the coordinator runs in the + primary thread; each delegation spawns a thread (`sth_…`), max 25 + concurrent. +- **Session : Events, 1:many.** Events (`sevt_…`) are the sole interaction + channel, persisted server-side, streamable over SSE. +- **Deployment : Session, 1:many.** A deployment (scheduled trigger, + `depl_…`, states active | paused | archived) creates a new session per + firing, with run records (`drun_…`). +- **Vault : Credential, 1:many** (max 20); vaults attach per-session and + apply to every thread. +- **MemoryStore : Session, many:many** (max 8 per session); a store holds up + to 2,000 path-addressed memories, each with immutable versions. +- **Self-hosted sandboxes:** "Self-hosted sandboxes keep the orchestration on + Anthropic's side but move tool execution into infrastructure you control", + the environment acts as a work queue the customer's worker polls + (`/v1/environments/{id}/work`). Providers include AWS Lambda MicroVMs, + Blaxel, Cloudflare, Daytona, E2B, GKE Agent Sandbox, Modal, Namespace, + Superserve, Vercel. +- Every noun has an ID prefix (agent_, session_, env_, vlt_, memstore_, + mem_, depl_, sth_, sevt_, outc_...), underscoring that these are all + persistent API resources. + +## Lifecycle + +- **Agent:** create (`POST /v1/agents`, version 1) → update (optimistic + concurrency on `version`, increments) → archive (read-only, permanent). No + hard delete documented. The agent has no runtime states, it is a resource, + not a process. +- **Session:** statuses `idle` (starts here), `running`, `rescheduling` + (transient error, auto-retry), `terminated` (unrecoverable). "Sessions + follow a two-step lifecycle: first create the session to provision its + sandbox, then send a user event to start work." Archive preserves history + (running sessions must be interrupted first); delete permanently removes + "its record, events, and associated sandbox." +- **Who owns the loop: Anthropic.** "Instead of building your own agent loop, + tool execution, and runtime, you get a fully managed environment where + Claude can read files, run commands, browse the web, and execute code + securely." (overview) Even self-hosted sandboxes only move tool execution; + orchestration stays on Anthropic's control plane. +- **Persistence:** conversation/event history and the session filesystem + persist for the session's life ("Stateful sessions: Persistent filesystems + and conversation history across multiple interactions"). Across sessions, + persistence is opt-in via memory stores: "Memory stores let the agent carry + information across sessions: user preferences, project conventions, prior + mistakes, and domain context." A "Dreams" research preview consolidates + memory. +- **Outcomes:** a `user.define_outcome` event turns a session into + rubric-evaluated work with a separate grader context and bounded iterations + (default 3, max 20), the platform's notion of "done." + +## What makes it "an agent" here (our inference) + +Our inference: in Claude Managed Agents, an agent is a **server-side, +versioned identity-plus-configuration resource**, persona (system prompt), +brain (model), and capabilities (tools, MCP servers, skills, delegation +roster), that the platform instantiates as sessions inside sandboxed +environments. What separates it from a plain LLM call is that Anthropic runs +the entire loop (tool execution, threads, retries, memory, scheduling) +against that resource; what separates it from OpenComputer-style "agent = +config" is that the configuration is richer (permissions, credential vaults, +declared multi-agent rosters) and the version is a first-class, +optimistically-locked field on the resource itself. + +## Open questions + +- No hard delete for agents (only archive), is deletion possible at all? +- The events-and-streaming reference was only partially readable; the full + event taxonomy (especially thread-level events) is not captured here. +- "Dreams" (memory consolidation) is a research preview with thin docs. +- How session-level `agent.tools` updates interact with a multiagent roster + (do subagent threads see coordinator tool updates?) is not documented. diff --git a/docs/research/agent-platform/products/cloudflare-agents.md b/docs/research/agent-platform/products/cloudflare-agents.md new file mode 100644 index 000000000..e44049b45 --- /dev/null +++ b/docs/research/agent-platform/products/cloudflare-agents.md @@ -0,0 +1,199 @@ +# Cloudflare Agents SDK: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from developers.cloudflare.com/agents and +`cloudflare/agents` (`packages/agents/src/index.ts`). + +## Source anchors + +Sources retrieved 2026-07-13. Source-level claims are pinned to the official +[`agents` package version 0.17.4](https://github.com/cloudflare/agents/releases/tag/agents%400.17.4) +at `cloudflare/agents` commit +[`03cdc82`](https://github.com/cloudflare/agents/commit/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea). + +- Definitions and architecture: [What are agents?](https://developers.cloudflare.com/agents/concepts/what-are-agents/), + [Agents overview](https://developers.cloudflare.com/agents/), and [Agent + class internals](https://developers.cloudflare.com/agents/runtime/lifecycle/agent-class/), + including the `DurableObject > Server > Agent > AIChatAgent` hierarchy. +- Persistence and lifecycle: [Long-running agents](https://developers.cloudflare.com/agents/concepts/agentic-patterns/long-running-agents/), + [Sub-agents](https://developers.cloudflare.com/agents/runtime/execution/sub-agents/), + [Agents with Workflows](https://developers.cloudflare.com/agents/concepts/workflows/), + and [platform limits](https://developers.cloudflare.com/agents/platform/limits/). +- Deployment configuration: [Configuration, sections "Migrations" and + "Migration best practices"](https://developers.cloudflare.com/agents/runtime/operations/configuration/). +- Stable source symbols: [package version](https://github.com/cloudflare/agents/blob/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea/packages/agents/package.json#L1-L17), + [`Agent` class](https://github.com/cloudflare/agents/blob/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea/packages/agents/src/index.ts#L1569-L1578), + [`subAgent` and `runAgentTool`](https://github.com/cloudflare/agents/blob/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea/packages/agents/src/index.ts#L7908-L8005), + [`abortSubAgent` and `deleteSubAgent`](https://github.com/cloudflare/agents/blob/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea/packages/agents/src/index.ts#L10544-L10607), + and [`destroy`](https://github.com/cloudflare/agents/blob/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea/packages/agents/src/index.ts#L10803-L10857). + +## The `agent` noun (primary-source quotes) + +- Behavioral definition: "An agent is an AI system that can autonomously + execute tasks by making decisions about tool usage and process flow." + ([What are agents?](https://developers.cloudflare.com/agents/concepts/what-are-agents/)) +- Structural definition: "Each agent session has a durable identity, local + SQL storage, real-time connections, scheduled work, and recoverable + execution." ([Agents overview](https://developers.cloudflare.com/agents/)) +- In code, `class Agent extends Server`, inheritance + chain **DurableObject > Server (partyserver) > Agent > AIChatAgent**; + docs state plainly that "agents *are* Durable Objects." + ([Agent class internals](https://developers.cloudflare.com/agents/runtime/lifecycle/agent-class/); + [`Agent` source](https://github.com/cloudflare/agents/blob/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea/packages/agents/src/index.ts#L1569-L1578)) +- Instance identity: instances are "globally unique: given the same name (or + ID), you will always get the same instance"; "Each unique name gets its + own isolated agent with its own state"; one class can have "millions of + instances functioning as independent micro-servers." +- The identity/process duality is explicit: long-running agents are "durable + identities that exist continuously but run intermittently. They wake on + demand, via HTTP requests, WebSocket connections, RPC calls, scheduled + alarms, or emails, then hibernate when idle, consuming zero compute + resources during downtime." + ([Long-running agents](https://developers.cloudflare.com/agents/concepts/agentic-patterns/long-running-agents/)) +- **Conceptual model: agent-as-durable-object (stateful actor).** The class + is code; the *instance* is simultaneously a persistent identity, a state + store (embedded SQLite), and an intermittently-running process. Uniquely + in this study, identity and state and process are the same object. + +## Subagents + +- First-class: "Spawn child agents as co-located Durable Objects with their + own isolated SQLite storage." (`Agent.subAgent(cls, name)`.) + ([documentation](https://developers.cloudflare.com/agents/runtime/execution/sub-agents/); + [source](https://github.com/cloudflare/agents/blob/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea/packages/agents/src/index.ts#L7908-L7933)) +- **Get-or-create by name:** "The first call for a given name triggers the + child's `onStart()`. Subsequent calls return the existing instance." +- **Kill vs delete distinguish process from identity:** `abortSubAgent`, + "The child stops executing immediately and restarts on the next + `subAgent()` call. Storage is preserved, only the running instance is + killed." `deleteSubAgent`, "permanently wipe its storage. The next + `subAgent()` call creates a fresh instance with empty SQLite." +- **Isolation:** "Each sub-agent has its own SQLite database, completely + isolated from the parent and from other sub-agents." Parent identity is + persisted on the child (`cf_agents_parent_path` storage key; `parentPath` + / `selfPath` accessors). +- **Communication is typed RPC:** `SubAgentStub` exposes the child's + public methods as async RPC calls; WebSocket handles stay with the root + object and bridge to children. No nesting limit documented; paths imply + arbitrary depth. +- **Agents-as-tools:** `runAgentTool()` spawns a child by class + runId, + tracks it in a `cf_agent_tool_runs` SQLite table, and re-attaches after + parent eviction, delegation that survives hibernation. +- Delegated deterministic work goes to **Workflows** instead: "Agents launch + instances with `runWorkflow()`, dispatch events, respond to approvals, + control execution, and query status." + +## Configuration surface (what, where, why) + +- **The class code is the config:** overridable handlers `onStart`, + `onRequest`, `onConnect`, `onMessage`, `onStateChanged`, `onEmail`, + `onClose`, `onError`, plus gating/recovery hooks (`validateStateChange`, + `onFiberRecovered`, `onBeforeSubAgent`). +- **Per-instance state:** `this.state`/`setState()` (persisted in a + `cf_agents_state` SQLite table, broadcast to connected clients) and + `this.sql` tagged-template queries against the instance's own SQLite. +- **Scheduling registered at runtime:** `this.schedule` with four shapes, + `scheduled` (absolute time), `delayed`, `cron`, `interval`. "Since Durable + Objects only allow one alarm at a time, the Agent class works around this + by managing multiple schedules in SQL and using a single alarm." +- **Static options:** `hibernate` (default true), `keepAliveIntervalMs` + (30s), retry, memory-limit and fiber-recovery options. +- **Platform bindings** live in `wrangler.jsonc` (DO binding + + `new_sqlite_classes` migration; "Never modify existing migrations, always + add new ones"). +- Stated rationale for state-with-agent: "This design eliminates the need + for centralized session stores, state persists with the agent instance." + Platform pitch: "Deploy once and Cloudflare runs your agents across its + global network... No infrastructure to manage, no sessions to reconstruct, + no state to externalize." + +## Binding time + +- **Deploy time:** class code and namespace binding. +- **First request:** instance creation is lazy by name, "the first request + to a unique name-pair instantiates that agent." +- **Runtime-mutable:** everything per-instance, state (`setState`, SQL), + schedules, spawned children, MCP server connections (all in the instance's + SQLite). +- **Code deploys restart running instances:** the SDK exports + `isDurableObjectCodeUpdateReset` and classifies it in scheduler retry + logic; `onStart()` refires on every wake including post-deploy restarts. + State survives (SQLite); in-flight execution does not, unless checkpointed + as fibers. +- **No user-facing version primitive.** Internally the SDK migrates its own + SQLite schema idempotently on every wake + ([`CURRENT_SCHEMA_VERSION = 11`](https://github.com/cloudflare/agents/blob/03cdc828c0bc3c6bb1d9aa636bb46ceb00a4e0ea/packages/agents/src/index.ts#L1095)). + +## Relationships between nouns + +- **Class 1:millions instances;** instance 1:1 embedded SQLite; instance 1:N + schedules, queues, workflows, fibers, tool-runs, MCP connections (each an + internal table: `cf_agents_state`, `cf_agents_schedules`, + `cf_agents_queues`, `cf_agents_workflows`, `cf_agents_runs`, + `cf_agents_fibers`, `cf_agent_tool_runs`, `cf_agents_facet_runs`, + `cf_agents_mcp_servers`). +- **Agent : session is undefined by design**, the docs list "chats, + documents, sessions, shards, or projects" as things an instance can be; + one instance can hold many concurrent WebSocket clients ("Shared rooms: + multiple users share one instance") or be one-per-user. Naming strategy = + session strategy; it's entirely application routing + (`routeAgentRequest` matches `/agents/:class/:name`). +- **Agents vs Workflows** (their sharpest concept split): Agents = + "long-lived identity that wakes on events," real-time channels, built-in + SQL, application-defined failure handling; Workflows = "run to + completion," step-level persistence, automatic retries, no real-time + communication. Docs triad: "Agents: non-linear, non-deterministic... + Workflows: linear, deterministic execution paths. Co-pilots: augmentative + AI assistance." +- Four-part architecture from the overview: communication channels; the + **agent harness** ("the loop: how the agent calls models, selects tools, + handles tool results, streams responses, and decides whether to + continue"); the Agents SDK runtime (durable infrastructure); tools. The + chat harness has been split out to `@cloudflare/ai-chat`. + +## Lifecycle + +- **Create:** lazy by name. **Wake paths (5):** HTTP, WebSocket, RPC, + alarms/schedules, email. +- **Hibernate:** default; WebSocket hibernation preserves connection state + platform-side; `keepAlive()`/`keepAliveWhile()` pin the instance during + long operations (LLM streaming). +- **Persists across hibernation/restarts:** "State stored via `setState()`, + SQLite data and tables, scheduled tasks, fiber checkpoints..., WebSocket + connection state." Does not survive: "in-memory variables and class + fields, running timers, open fetch requests, local closures." +- **Destroy is explicit and manual:** `this.destroy()` "permanently delete[s] + SQLite data, schedules, and state"; instances otherwise persist + indefinitely. +- **Who runs the loop: the customer**, in handlers, the platform provides + identity, persistence, routing, scheduling. Compute is billed + per-request/message ("30 seconds max compute per request, refreshed per + HTTP request / incoming WebSocket message"); limits page: "tens of + millions+" concurrent agents, 1 GB state per agent, ~250,000 agent + definitions per account. + +## What makes it "an agent" here (our inference) + +Our inference: Cloudflare's agent is a **named, addressable, stateful actor +that never dies**, identity, state, and (intermittent) process fused into +one Durable Object instance. There is no definition/instance split at all: +"creating an agent" is just addressing it by name, configuration is code +plus mutable per-instance state, and the LLM loop is whatever the class's +handlers do. Where every managed platform in this study separates the config +record from the running session, Cloudflare collapses them, the strongest +possible answer to binding time (everything binds at runtime, per instance) +and the weakest possible answer to fleet governance (no versions, no +roster, no central definition). + +## Open questions + +- No fleet management: how to enumerate/migrate/garbage-collect millions of + named instances isn't covered (no list-instances primitive in the docs + read). +- Code deploys vs long-lived instances: fiber checkpoints mitigate restarts, + but cross-version state compatibility is the developer's problem. +- The `Props` generic and `props` on `getAgentByName` (per-wake input) vs + persisted state deserves a closer read. +- Cloudflare's role as a Claude Managed Agents self-hosted sandbox provider + is documented on Anthropic's side but not in these docs. diff --git a/docs/research/agent-platform/products/crewai.md b/docs/research/agent-platform/products/crewai.md new file mode 100644 index 000000000..83db5088e --- /dev/null +++ b/docs/research/agent-platform/products/crewai.md @@ -0,0 +1,187 @@ +# CrewAI: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from docs.crewai.com and the `crewAIInc/crewAI` source (pydantic +models, prompt templates, delegation tools). + +## Source anchors + +Sources retrieved 2026-07-13. Source-level claims and defaults are pinned to +the official [CrewAI 1.15.2 release](https://github.com/crewAIInc/crewAI/releases/tag/1.15.2) +at commit +[`289686a`](https://github.com/crewAIInc/crewAI/commit/289686ab4960708f556ca38687e0dee2112e3e30). +Where the live documentation and release source differ, such as the documented +`max_iter` default of 20 versus the 1.15.2 source default of 25, this dossier +uses the pinned release source. + +- Product concepts: [Agents, sections "Overview of an Agent", "Agent + Attributes", and "Creating Agents"](https://docs.crewai.com/en/concepts/agents), + [Crews](https://docs.crewai.com/en/concepts/crews), + [Processes](https://docs.crewai.com/en/concepts/processes), and + [Flows](https://docs.crewai.com/en/concepts/flows). +- Persistence concepts: [Memory](https://docs.crewai.com/en/concepts/memory) + and [Knowledge](https://docs.crewai.com/en/concepts/knowledge). +- Stable agent and prompt symbols: [`BaseAgent` identity, persona, + delegation, and iteration fields](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/agents/agent_builder/base_agent.py#L251-L288), + [`Agent` implementation](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/agent/core.py#L170-L195), + [prompt substitution](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/utilities/prompts.py#L135-L180), + and [persona, ReAct, manager, and delegation prompt text](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/translations/en.json#L2-L59). +- Stable orchestration symbols: [delegation execution](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/tools/agent_tools/base_agent_tools.py#L46-L120), + [`Crew` fields](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/crew.py#L218-L300), + [hierarchical manager construction](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/crew.py#L1480-L1505), + and [`Process` values](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/process.py#L1-L11). + +## The `agent` noun (primary-source quotes) + +- [Agents, "Overview of an Agent"](https://docs.crewai.com/en/concepts/agents): + "An autonomous unit that can perform specific tasks, make decisions + based on its role and goal, use tools to accomplish objectives, communicate + and collaborate with other agents, maintain memory of interactions, and + delegate tasks when allowed." And: "Think of an agent as a specialized team + member with specific skills, expertise, and responsibilities." +- In code, `Agent` is a **pydantic model** (extends `BaseAgent`), an + in-memory object with a fresh `id: UUID4` per construction, no persistent + resource in OSS. Its own [`__repr__`](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/agent/core.py#L1274-L1275) + states the identity model: `Agent(role=..., goal=..., backstory=...)`. +- The persona *is* the prompt. The core + [prompt slice](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/translations/en.json#L7-L12): + `"You are {role}. {backstory}\nYour personal goal + is: {goal}"`, role/goal/backstory are string-substituted into the system + message verbatim + ([`Prompts._build_prompt`](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/utilities/prompts.py#L135-L180)). +- The loop is prompt-encoded ReAct (when the model lacks native tool + calling): + ["Thought / Action / Action Input / Observation... Final Answer"](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/translations/en.json#L7-L20), + ending with the pressure line: "Begin! This is VERY important to you, use + the tools available and give your best Final Answer, your job depends on + it!" +- **Conceptual model: agent-as-role (persona).** The required triad is + role/goal/backstory; everything else (llm, tools, memory, max_iter=25, + max_rpm, cache, guardrails) hangs off the persona. + +## Subagents + +- **Delegation is a peer mechanism inside a crew roster**, not parent/child + spawning. `allow_delegation: bool = False`, when enabled, two tools are + injected: DelegateWorkTool and AskQuestionTool, addressed to "coworkers" + by role name. +- The tool description encodes their context-isolation stance: "The input to + this tool should be the coworker, the task you want them to do, and ALL + necessary context to execute the task, **they know nothing about the task, + so share absolutely everything you know**, don't reference things but + instead explain them." Delegation creates a fresh `Task` on the fly and + calls `selected_agent.execute_task(task, context)` + ([prompt](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/translations/en.json#L57-L59); + [execution](https://github.com/crewAIInc/crewAI/blob/289686ab4960708f556ca38687e0dee2112e3e30/lib/crewai/src/crewai/tools/agent_tools/base_agent_tools.py#L99-L120)). +- **Hierarchical process** adds a manager: "Tasks are not pre-assigned; the + manager allocates tasks to agents based on their capabilities, reviews + outputs, and assesses task completion." Default manager persona shipped in + translations ("Crew Manager... seasoned manager with a knack for getting + the best out of your team"), or a custom `manager_agent`/`manager_llm`. +- **Roster is declared** (`Crew(agents=[...])`, fixed at kickoff); the + manager's task allocation is the dynamic part. +- Shared across the crew: crew memory, crew knowledge, cache. Isolated per + agent: own `tools`, `llm`, agent-level `knowledge_sources` ("stored in + collections named after the agent's role") and agent-level memory. +- **Nesting** happens via Flows calling crews (`crew().kickoff(...)` inside + a `@listen` step); no crews-within-crews in OSS. Remote delegation exists + via an `a2a` field (A2AConfig/Server/Client) on the agent. +- Communication between tasks is output-as-context: `Task.context = + [other_tasks]` injects their outputs as "This is the context you're + working with." + +## Configuration surface (what, where, why) + +- **Agent fields** (BaseAgent + Agent): required `role`, `goal`, `backstory`; + then `llm`, `tools`, `max_iter` (25), `max_rpm`, `max_execution_time`, + `max_retry_limit` (2), `cache`, `verbose`, `allow_delegation` (False), + `memory` (bool or Memory/scope/slice), `knowledge_sources`, `embedder`, + `system_template`/`prompt_template`/`response_template` (override the + built-in persona assembly), `use_system_prompt`, `respect_context_window`, + `inject_date`, `planning`/`planning_config`, `guardrail` + + `guardrail_max_retries`, `callbacks`, `apps` (AMP platform apps), `mcps` + (including `crewai-amp:` slugs), `skills`, `checkpoint`, `security_config`, + `from_repository` (loads a stored agent config from the CrewAI AMP + platform), `a2a`, `executor_class`. +- **Crew fields:** `agents`, `tasks`, `process` (sequential | hierarchical; + a `consensual` enum member is a TODO), `memory`, `knowledge_sources`, + `manager_llm`/`manager_agent`, `planning` + `planning_llm`, callbacks + (`before/after_kickoff`, `step_callback`, `task_callback`), `max_rpm`, + `stream`, `output_log_file`, `security_config`, `tracing`. +- **Task fields:** required `description` and `expected_output`; `agent`, + `context`, `async_execution`, `output_pydantic`/`output_json`/ + `response_model`, `output_file`, `human_input`, `markdown`, `guardrail(s)`, + `tools`. +- **Where config lives:** Python code or the YAML convention + (`config/agents.yaml` with role/goal/backstory blocks containing `{topic}` + placeholders; `config/tasks.yaml` mapping tasks to agent names). +- **Stated rationale for the triad** is definitional rather than empirical: + role "defines the agent's function and expertise within the crew"; goal is + "the individual objective that guides the agent's decision-making"; + backstory "provides context and personality to the agent, enriching + interactions." No measured claim for why persona helps is documented. + +## Binding time + +- **Kickoff is the binding moment:** `crew.kickoff(inputs={"topic": ...})` + interpolates `{placeholders}` into every agent's role/goal/backstory and + every task's description/expected_output (`interpolate_inputs`, with + originals preserved in `_original_*`). +- **No mid-run mutation mechanism** documented; persona is fixed for the + run. +- **No versioning in OSS.** The `from_repository` field implies the AMP + platform stores named agent configs server-side, but no public versioning + API is documented. + +## Relationships between nouns + +- **Crew 1:N agents, 1:N tasks; task → 0..1 agent** (or manager-assigned in + hierarchical); agent → 0..1 crew backref. +- **Run = kickoff.** No session noun in OSS; `kickoff()` returns a + `CrewOutput` (raw, structured, `tasks_output`, token usage). Variants: + `kickoff_async`, `akickoff`, `kickoff_for_each`. +- **Flow ⊃ crews:** "Flows allow you to create structured, event-driven + workflows... connect multiple tasks, manage state, and control the flow of + execution." Flow state gets a UUID; snapshots support Resume (same + `flow_uuid`) vs Fork (fresh `state.id`). Crews = autonomy; flows = + developer control. +- **Memory**, unified Memory (LanceDB at `./.crewai/memory`), crew- or + agent-scoped, persists across kickoffs. **Knowledge**, ChromaDB-backed + reference material, crew- or agent-level. **Tool**, BaseTool attached to + agents or tasks. + +## Lifecycle + +- **Agents have no lifecycle**: constructed, used during a kickoff, + garbage-collected. No daemon, no persisted record. +- **Executor loop:** `AgentExecutor` (native tool calling) or deprecated + `CrewAgentExecutor` ReAct loop, `while not AgentFinish`, bounded by + `max_iter` (25) and `max_execution_time`; retries per `max_retry_limit`. +- **The library runs the loop in-process, synchronously**, the customer's + Python process owns everything. +- **Persists across kickoffs:** long-term memory (LanceDB), knowledge + stores, flow state snapshots. Nothing else. + +## What makes it "an agent" here (our inference) + +Our inference: CrewAI defines an agent **socially**, an agent is a persona +(role/goal/backstory) occupying a seat on a team, and agency emerges from +the org chart: task assignment, delegation to "coworkers," a manager +hierarchy, shared memory. Technically it is just a pydantic config object +consumed by an in-process ReAct loop; conceptually it is the +anthropomorphic extreme of the study, and the interesting datum is that its +delegation design assumes zero shared context ("they know nothing about the +task"), the same conclusion Cognition reached from the opposite direction. + +## Open questions + +- CrewAI AMP/Enterprise deployment model (does a deployed crew get an + ID/versions/endpoints?) is undocumented publicly; `from_repository` and + `crewai-amp:` slugs are the only OSS traces. +- The `a2a` field's maturity and semantics (server vs client configs) need + a deeper read. +- `Process.consensual` has been a TODO for years, signals the roadmap + beyond sequential/hierarchical. +- No session noun: multi-turn user interaction is out of scope for crews + (flows + chat_llm hint at it). diff --git a/docs/research/agent-platform/products/devin.md b/docs/research/agent-platform/products/devin.md new file mode 100644 index 000000000..11b87327c --- /dev/null +++ b/docs/research/agent-platform/products/devin.md @@ -0,0 +1,199 @@ +# Devin (Cognition): what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence retrieved 2026-07-13 from Cognition's live documentation and blog. +The documentation is mutable, so the anchors below name the pages and sections +checked rather than implying a fixed product version. + +## Evidence anchors + +- Product identity and fleet framing: Cognition's + [launch post](https://cognition.com/blog/introducing-devin) and + [May 2026 company update](https://cognition.com/blog/series-d), plus its + [2025 performance review](https://cognition.com/blog/devin-annual-performance-review-2025). +- Managed sessions and coordination: [Devin can now Manage + Devins](https://cognition.com/blog/devin-can-now-manage-devins) and the + [Advanced Capabilities](https://docs.devin.ai/work-with-devin/advanced-capabilities) + guide. +- Multi-agent design guidance: [Don't Build + Multi-Agents](https://cognition.com/blog/dont-build-multi-agents) and + [Multi-Agents: What's Actually + Working](https://cognition.com/blog/multi-agents-working). +- Session schema and lifecycle: v3 [Create + Session](https://docs.devin.ai/api-reference/v3/sessions/post-organizations-sessions#devin-mode), + [Get + Session](https://docs.devin.ai/api-reference/v3/sessions/get-organizations-session), + [Archive + Session](https://docs.devin.ai/api-reference/v3/sessions/post-organizations-sessions-archive), + and [API release + notes](https://docs.devin.ai/api-reference/release-notes). +- Reusable configuration: [Creating + Playbooks](https://docs.devin.ai/product-guides/creating-playbooks#what-are-playbooks), + [Knowledge + Onboarding](https://docs.devin.ai/onboard-devin/knowledge-onboarding#knowledge-101), + and [Environment + configuration](https://docs.devin.ai/onboard-devin/environment#how-sessions-work). +- Managed-service operations: [Secrets and Site + Cookies](https://docs.devin.ai/product-guides/secrets), [Devin + MCP](https://docs.devin.ai/work-with-devin/devin-mcp), and + [Usage](https://docs.devin.ai/admin/billing/usage#sleep-and-idle-behavior), + plus [Enterprise + Deployment](https://docs.devin.ai/enterprise/deployment/overview). + +## The `agent` noun (primary-source quotes) + +- "Introducing Devin, the first AI software engineer." "Devin is a tireless, + skilled teammate, equally ready to build alongside you or independently + complete tasks for you to review." (cognition.com/blog/introducing-devin) +- Docs: "Devin is the AI software engineer, built to help ambitious + engineering teams crush their backlogs"; "an autonomous AI software + engineer that can write, run and test code." +- **The inversion:** there is no user-creatable "agent" object anywhere in + the API. The resources are sessions, playbooks, knowledge, snapshots, + secrets, schedules/automations, tags, repos, orgs. "Agent" never appears + as a resource type in an endpoint path. Devin *is* the agent; users create + **sessions**, even the session ID is prefixed with the agent's name + (`devin-abc123def456`). +- Series D framing shows the fleet mental model: "their army of Devins + reliably executes"; "89% of code committed by our engineers is committed + by Devin." +- **Conceptual model: agent-as-product-identity (teammate).** One agent, + org-wide configuration (knowledge, playbooks, machine), many concurrent + sessions. Customization configures *the teammate*, not new agents. + +## Subagents + +- **"Managed Devins" (March 2026):** "Devin can break down large tasks and + delegate them to a team of managed Devins working in parallel," "each + running in its own isolated VM." "The coordinator session scopes the work, + monitors progress, resolves conflicts, and compiles results." Coordinator + powers: spin up child sessions "with specific prompts, playbooks, tags, + and ACU limits," message them, put them to sleep or terminate them, + monitor their ACU consumption. Mechanically: "spawn child Devins... and + coordinate their progress through an internal MCP", described as + "experimental, requiring careful context engineering." +- API: sessions carry `parent_session_id` and `child_session_ids`; creation + accepts `child_playbook_id`. Archiving a parent cascades to children. +- Shared: org-wide knowledge, playbooks, snapshots, secrets. Isolated: each + session's VM. +- **Cognition is also the industry's loudest subagent skeptic.** "Don't + Build Multi-Agents" (Walden Yan): Principle 1, "Share context, and share + full agent traces, not just individual messages"; Principle 2, "Actions + carry implicit decisions, and conflicting decisions carry bad results." + Fragmented subagent context compounds miscommunication; recommendation was + single-threaded linear agents with context compression. +- The follow-up ("Multi-Agents: What's Actually Working") narrows the + retraction to three patterns: generator-verifier review loops (fresh + context *helps* review, "clean context leads to a notable improvement... + Shared context reduces effectiveness"), "smart friend" escalation between + frontier models, and manager-child delegation, under one constraint: + "**writes stay single-threaded and the additional agents contribute + intelligence rather than actions.**" Also names "context rot" as the + reason fresh-context reviewers beat shared-context ones. + +## Configuration surface (what, where, why) + +Configuration attaches to the org-wide teammate, in four primitives: + +- **Playbooks**, "a playbook is like a custom system prompt for a repeated + task"; sections: Procedure ("one step per line, each line written + imperatively"), Specifications, Advice, Forbidden Actions, Required from + User. `!macro` shortcuts; version history with revert. Rationale: use one + when "you or your teammates will reuse the prompt on multiple sessions" or + you keep "repeating the same reminders to Devin." +- **Knowledge**, "a collection of tips, advice, and instructions that Devin + can reference in all sessions." Explicit onboarding metaphor: "Just like + onboarding a new engineer, onboarding Devin requires an initial investment + in knowledge transfer." Each item has a `trigger_description`, "not a + keyword search, Devin uses it as a semantic cue"; retrieval is lazy + ("when relevant, not all at once"). Scopes: org, enterprise, repo-pinned. + Auto-ingests `.cursorrules`, `CLAUDE.md`, `AGENTS.md`, etc. +- **Devin's Machine / Snapshots**, "Devin's Workspace resets to a saved + machine state at the start of every session"; snapshots are "'save' + states" with startup commands (2-minute timeout each); configured via + wizard or declarative blueprints (classic deprecated June 2026). +- **Secrets**, org/enterprise-level, auto-retrieved "as needed in future + sessions," or per-session inline (`session_secrets`). + +Config lives in the web app, the v3 API, and the Devin MCP server ("full +access to session management, playbooks, knowledge, and scheduling"). + +## Binding time + +- **Session creation** (`POST /v3/organizations/{org_id}/sessions`) binds: + `prompt` (required), `playbook_id`, `child_playbook_id`, `knowledge_ids`, + `repos`, `tags`, `attachment_urls`, `platform`, `resumable` (default + true, "whether to preserve the session's VM state"), + `structured_output_schema`, `max_acu_limit`, secrets, and `devin_mode` + ("normal", "fast", "lite", "ultra", or "fusion"; preview-mode access is + feature-gated, and "fast" is documented as ~2x faster at 4x cost). +- **Mid-session:** messages resume suspended sessions automatically; the + Ask/Agent mode toggle switches "mid-session... with your next message"; + knowledge binds *dynamically* mid-session via trigger matching (new org + knowledge can affect running sessions, undocumented edge). +- **Versioning:** playbooks have version history; knowledge versioning is + undocumented; there is no agent definition to version, the "definition" + is the org's accumulated config. + +## Relationships between nouns + +- **Org 1:N sessions** (the Usage guide states that there are no concurrent + session limits); org 1:N + playbooks, knowledge items, snapshots, secrets, automations. +- **Session** → one snapshot (machine state), 0..1 playbook, dynamic + knowledge, tags/attachments/PRs, `acus_consumed`, and possibly a parent + and children. +- **Statuses (v3):** `new, claimed, running, exit, error, suspended, + resuming`; running detail: `working, waiting_for_user, + waiting_for_approval, finished`; suspension reasons include `inactivity`, + `usage_limit_exceeded`, `out_of_credits`... +- Adjacent read-side features reveal the split between understanding and + execution: Devin Wiki ("automatically indexes your repositories every + couple hours"), Devin Search, indexing/retrieval products alongside the + session loop. + +## Lifecycle + +- **Create** via API, Slack @mention, web app, automations (schedule/cron, + GitHub/Linear/Jira triggers), or MCP. +- **Sleep/wake:** "most sessions will 'sleep' instead [of ending], meaning + that you can wake Devin up and resume the session at any point"; sleeping + sessions auto-wake on PR retriggers; VM state preserved when + `resumable=true`. +- **End:** archive (cascades to children), expiry; "Anything generated + during a session that is not committed to a repository or captured in a + snapshot disappears when the session ends." +- **Who runs the loop: Cognition, entirely.** Fully managed; even the VPC + enterprise option is Cognition-managed infrastructure. +- **The pricing unit is agent labor:** enterprise customers consume ACUs + based on work performed in a session, including action complexity, VM time, + and network bandwidth. Sessions carry `acus_consumed` and a + `max_acu_limit`; Cognition's guidance is to keep prompts and sessions short + because both quality and usage degrade as work accumulates. +- **Persists across sessions:** knowledge, playbooks, snapshots, secrets, + session history/insights. Performance-review framing of the persona: + "senior-level at codebase understanding but junior at execution"; + "infinitely parallelizable and never sleeps." + +## What makes it "an agent" here (our inference) + +Our inference: Devin defines the agent as **the employee, not the config**, +a single product-identity teammate whose "definition" is the org's +accumulated onboarding (knowledge, playbooks, machine image) and whose unit +of work is the session (a VM-backed engagement billed in ACUs of labor). +It is the managed-service extreme of the spectrum: no agent CRUD at all, +configuration-as-onboarding, delegation modeled as a manager coordinating +copies of the same employee. Its second-order lesson for a service design is +the write-path rule distilled from production: parallel agents are safe for +intelligence (review, search, advice) and dangerous for actions, keep +writes single-threaded. + +## Open questions + +- Whether playbook/knowledge edits affect in-flight sessions is + undocumented (lazy knowledge recall suggests yes for knowledge). +- The internal MCP used for manager-child coordination is not publicly + specified. +- No public state machine for child-session lifecycle coupling beyond + archive cascade. diff --git a/docs/research/agent-platform/products/hermes-agent.md b/docs/research/agent-platform/products/hermes-agent.md new file mode 100644 index 000000000..69c45d0ea --- /dev/null +++ b/docs/research/agent-platform/products/hermes-agent.md @@ -0,0 +1,201 @@ +# Hermes Agent (Nous Research): what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Disambiguation: "Hermes Agent" = +[`NousResearch/hermes-agent`](https://github.com/NousResearch/hermes-agent), +not the Hermes open-weight model family or OpenClaw. At retrieval on +2026-07-13, the MIT-licensed repository declared package version `0.18.2` +and had 214,238 GitHub stars. It ships an OpenClaw migration command, +`hermes claw migrate`. + +## Evidence anchors + +The source snapshot is repository commit +[`2d71e2f1`](https://github.com/NousResearch/hermes-agent/commit/2d71e2f1e451a84239ae9d013275bf29c47ddad1), +retrieved 2026-07-13. Commit-pinned anchors keep implementation claims +auditable after `main` changes. + +- Identity, license, migration command, and package version: + [`README.md`](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/README.md#L5-L29), + [`README.md` migration command](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/README.md#L105-L117), + and [`pyproject.toml`](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/pyproject.toml#L8-L23). +- Product and cache design: [`AGENTS.md`, What Hermes + Is](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/AGENTS.md#L7-L27). +- Runtime object: [`run_agent.py`, + `AIAgent`](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/run_agent.py#L393-L489). +- Delegation contract and limits: + [`tools/delegate_tool.py`, module contract](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/tools/delegate_tool.py#L1-L16), + [`delegate_task` and batch limits](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/tools/delegate_tool.py#L2369-L2473), + [background delegation lifetime](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/tools/delegate_tool.py#L3267-L3272), + [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/cli-config.yaml.example#L1076-L1093), + and the official [Subagent Delegation](https://hermes-agent.nousresearch.com/docs/user-guide/features/delegation) + guide. +- Configuration and cache economics: + [`cli-config.yaml.example`](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/cli-config.yaml.example) + and [`AGENTS.md`, prompt caching](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/AGENTS.md#L16-L27). +- Sessions and persistence: [`docs/session-lifecycle.md`, session + model](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/docs/session-lifecycle.md#L56-L95), + [gateway agent cache](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/docs/session-lifecycle.md#L568-L578), + [`hermes_state.py`, schema](https://github.com/NousResearch/hermes-agent/blob/2d71e2f1e451a84239ae9d013275bf29c47ddad1/hermes_state.py#L141-L148), + and the official [Sessions](https://hermes-agent.nousresearch.com/docs/user-guide/sessions) + guide. + +## The `agent` noun (primary-source quotes) + +- README: "The self-improving AI agent built by Nous Research. It's the only + agent with a built-in learning loop: it creates skills from experience, + improves them during use, nudges itself to persist knowledge, searches its + own past conversations, and builds a deepening model of who you are across + sessions." +- AGENTS.md: "Hermes is a personal AI agent that runs the same agent core + across a CLI, a messaging gateway (Telegram, Discord, Slack, and ~20 other + platforms), a TUI, and an Electron desktop app. It learns across sessions + (memory + skills), delegates to subagents, runs scheduled jobs, and drives + a real terminal and browser." +- The runtime object is `class AIAgent` ("AI Agent with tool calling + capabilities... manages the conversation flow, tool execution, and + response handling"), constructed per session with identity params + (`session_id`, `parent_session_id`, `platform`, `user_id`, `thread_id`, + `gateway_session_key`) and a synchronous `run_conversation()` loop bounded + by `max_iterations` and an iteration budget. +- **Conceptual model: agent-as-personal-daemon with a split identity.** One + logical agent identity persists as *files* (`MEMORY.md`, `USER.md`, + `~/.hermes/skills/`, `SOUL.md`); the process-level agent is an `AIAgent` + instance per session, LRU-cached by the gateway (128 entries, 1h idle TTL) + to keep prompt caches warm. Like Devin, there is one agent and many + sessions, but self-hosted, and the "definition" is accumulated memory + rather than platform config. + +## Subagents + +- Dynamic, tool-spawned: `delegate_task` "spawns child AIAgent instances + with isolated context, restricted toolsets, and their own terminal + sessions. Supports single-task and batch (parallel) modes... Each child + gets: a fresh conversation (no parent history), its own task_id..., a + restricted toolset..., a focused system prompt built from the delegated + goal + context. **The parent's context only sees the delegation call and + the summary result, never the child's intermediate tool calls or + reasoning.**" +- The schema tells the model the isolation contract directly: "Be specific + and self-contained: the subagent knows nothing about your conversation + history." +- **Roles as capability tiers:** `leaf` (default, cannot delegate, no + memory/clarify/send_message/execute_code/cronjob) vs `orchestrator` + (re-granted the delegation toolset; gated by + `delegation.orchestrator_enabled` and `max_spawn_depth`). +- **Limits are explicit config:** `max_spawn_depth: 1` ("flat by default: + parent (0) → child (1); grandchild rejected unless raised", comment: each + level "multiplies API cost"), `max_concurrent_children: 3` (excess + batch tasks are rejected), child `max_iterations: 50`. +- **Lifetime coupling:** parent interrupt propagates to `_active_children`; + synchronous children close with the parent turn; deleting a parent session + cascade-deletes delegate children. With `background=true`, delegation can + outlive the current turn but remains process-local. Work that must survive a + process restart belongs in `cronjob` or `terminal(background=True, + notify_on_complete=True)`. +- A second, cheaper delegation mechanism exists: `execute_code`, "write + Python scripts that call tools via RPC, collapsing multi-step pipelines + into zero-context-cost turns." +- Subagent sessions are persisted like any session (source `subagent`, + `_delegate_from` marker) but hidden from session search/pickers. + +## Configuration surface (what, where, why) + +- **One YAML file:** `~/.hermes/config.yaml` (secrets in `~/.hermes/.env`). + Major sections: `model` (provider/key/context/timeouts), `agent` + (`max_turns` 60, reasoning effort, verify-on-stop, personalities), + `terminal` (backend: local/ssh/docker/singularity/modal/daytona, + sandboxing is a backend choice), `compression` (threshold 0.5, target + ratio 0.2, protected head/tail turns), `memory` (char limits, nudge + intervals), `delegation`, `skills` (creation nudges, external dirs), + `session_reset` (idle/daily), `mcp_servers`, `platform_toolsets` + (per-channel tool restriction), `tool_loop_guardrails`, `prompt_caching`, + `auxiliary` (per-task model overrides for vision/compression/search), + `cronjobs`, `stt`/`tts`, `profiles`, `curator` (background skill + maintenance), `honcho` (cross-session user modeling). +- **Rationales are written into the config comments:** memory limits "keep + the memory small and focused"; compression exists because context + "approaches model's context limit"; session resets because "without + resets, conversation context grows indefinitely which increases API + costs"; delegation depth because "each level multiplies API cost." +- **The overriding design constraint is prompt-cache economics** (AGENTS.md + design lens): "Per-conversation prompt caching is sacred... Anything that + mutates past context, swaps toolsets, or rebuilds the system prompt + mid-conversation invalidates that cache," and "The core is a narrow waist; + capability lives at the edges", which is why skills inject as *user + messages*, not system-prompt edits. + +## Binding time + +- **Install/config time:** provider, toolsets per platform, memory/skill + settings, terminal backend, SOUL.md persona. +- **Session creation:** `session_id` minted (`YYYYMMDD_HHMMSS_<8hex>`); + system prompt assembled once (MEMORY.md + USER.md + context files + + SOUL.md); ~60 constructor params bound. +- **Mid-run:** deliberately minimal to protect the cache; model switch + (`/model`) is the sanctioned exception; compression *splits the session* + (new session chained via `parent_session_id`, `end_reason='compression'`) + rather than mutating it; skills arrive as user messages. +- **Versioning:** DB schema (`SCHEMA_VERSION = 21`) and config migrations + are versioned; skills carry a `version` in SKILL.md frontmatter; the agent + itself has no versioned definition; identity is a living accumulation of + memory files. + +## Relationships between nouns + +- **1 agent identity → N session_keys** (deterministic routing key: + `agent:main:{platform}:{chat_type}[:{chat_id}][:{thread_id}][:{user_id}]`) + → each key maps to **N historical sessions, one active** (resets and + compression mint new session ids on the same lane). +- **Session (SQLite row)** → N messages; 0..N delegate child sessions + (FK `parent_session_id` + `_delegate_from`); `end_reason` taxonomy: + compression, branched, session_reset, agent_close, subagent, + orphaned_compression, suspended. +- **AIAgent (process object)** 1:1 with active session; holds + `_active_children`. **task_id** routes terminal/container isolation + (sibling subagents share the parent's container). **thread_id** maps + platform threads. +- State lives in `~/.hermes/state.db` (SQLite WAL + FTS5 search over own + history), memory in markdown files, skills on disk. + +## Lifecycle + +- **Run:** user-run process (`hermes` CLI or `hermes gateway start`), fully + self-hosted; no managed platform. The loop is synchronous per turn; the + gateway dispatches per-platform messages; cron scheduler fires scheduled + jobs; subagents run in a thread pool. +- **Pause/resume:** not first-class on the object; the gateway suspends and + resumes *sessions* (crash recovery marks `resume_pending`; transcripts + reload intact). +- **Stop:** `interrupt()` → loop exit → `end_session(reason)`; children + closed with the turn. +- **Persists:** everything: full transcripts + FTS index, MEMORY.md, + USER.md, skills, config. The learning loop (memory nudges every ~10 + turns, skill-creation nudges every ~15, a background curator that + archives stale skills) makes persistence the product. + +## What makes it "an agent" here (our inference) + +Our inference: Hermes defines the agent as **a continuously-learning +personal identity hosted by a disposable process**. The durable thing is not +a config record but an accumulation, memory files, a user model, and +self-authored skills, while sessions and even the process are cheap, +cache-optimized incarnations. Its subagent design is the most +cost-explicit in the study (flat depth by default because "each level +multiplies API cost"; summaries-only back to the parent), and its deepest +design lens, prompt-cache preservation dictating what may bind mid-run, +is an operational concern no managed platform in the study surfaces as a +first-class definition constraint. + +## Open questions + +- The learning loop's failure modes (bad self-written skills, memory drift) + and the curator's guarantees are not deeply documented. +- Multi-user semantics: `group_sessions_per_user` and per-user session + lanes exist, but authorization boundaries between users of one gateway + are unclear from the sources read. +- `honcho` (cross-session user modeling service) introduces an external + dependency whose data model wasn't examined. +- How the Electron app and CLI share one identity concurrently (locking on + state.db) was not covered. diff --git a/docs/research/agent-platform/products/jido.md b/docs/research/agent-platform/products/jido.md new file mode 100644 index 000000000..cd29c3b11 --- /dev/null +++ b/docs/research/agent-platform/products/jido.md @@ -0,0 +1,174 @@ +# Jido: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from the `agentjido/jido` source (v2.3.2, Apache-2.0, maintained by +Mike Hostetler) and the `jido_ai` companion. Elixir/OTP framework; README: +"Jido helps you build agent systems as ordinary Elixir and OTP software. +Agents hold state and implement `cmd/2`. Actions do work and transform that +state. Signals route events into the system. Directives describe effects for +the runtime to execute... AI is optional." + +## The `agent` noun (primary-source quotes) + +- The agent is **not the process**. From `guides/core-loop.md`: Agent = + "Immutable struct + module: defines schema, handles cmd/2, pure decision + logic"; AgentServer = "GenServer process: holds agent state, executes + directives, routes signals." +- Moduledoc (`lib/jido/agent.ex`): "An Agent is an immutable data structure + that holds state and can be updated via commands... The fundamental + operation is `cmd/2`: `{agent, directives} = MyAgent.cmd(agent, MyAction)`. + Key invariants: the returned agent is **always complete**...; directives + are **runtime-owned external effects only**, they never modify agent + state... The Agent struct is immutable. All operations return new agent + structs. Server/OTP integration is handled separately by + `Jido.AgentServer`." +- Struct fields (Zoi schema): `id`, `agent_module`, `name`, `description`, + `category`, `tags`, `vsn`, `schema` (state-validation schema), `state` + (map). +- README framing: "Functional agent state model inspired by Elm/Redux: + `cmd/2` as the core operation: actions in, updated agent + directives out." +- **Conceptual model: a split.** The *definition* is a compile-time module + (`use Jido.Agent`, agent-as-config); the *value* is an immutable struct + (agent-as-state); the *running instance* is a supervised GenServer + (`AgentServer`, agent-as-process). Jido is the only product in the study + that makes the state/process distinction formally explicit and keeps the + decision logic pure. + +## Subagents + +- First-class and **dynamic**: the `%SpawnAgent{}` directive + (`lib/jido/agent/directive.ex`): "Spawn a child agent with parent-child + hierarchy tracking... Child's parent reference points to the spawning + agent; parent monitors the child process; parent tracks child in its + children map by tag; child exit signals are delivered to parent as + `jido.agent.child.exit`; child can use `emit_to_parent/3` while attached." +- **Logical hierarchy is not the same as OTP supervision:** "The logical + relationship is independent from OTP supervisory ancestry. If the child + later becomes orphaned, the current parent ref is cleared and the child + must be explicitly reattached with `AdoptChild`." +- Fields on spawn: agent module or pre-built struct, `tag`, `opts`, `meta`, + `restart` policy (default `:transient`). +- **Orphan policy is a config knob:** `on_parent_death` on the child server: + `:stop` (default), `:continue`, `:emit_orphan`. +- **No depth or fan-out limits** found in code. +- **Communication is signals** (CloudEvents-compliant envelopes): + `emit_to_parent/3` upward; lifecycle signals (`child.started`, + `child.exit`) downward to the parent's children map. No shared state; + isolation is BEAM process isolation. +- Related machinery: `Jido.Pod` ("agent with a canonical named topology of + collaborators" persisted in state and reconciled after thaw) and + Poolboy-based pre-warmed agent worker pools. + +## Configuration surface (what, where, why) + +- **Compile-time config** (`use Jido.Agent, ...`): `name`, `description`, + `category`, `tags`, `vsn`, `schema` (NimbleOptions or Zoi state schema), + `strategy` ("Execution strategy module or {module, opts}. Default: + Jido.Agent.Strategy.Direct"), `plugins`, `signal_routes` ("Compile-time + signal route table. Each route maps signal type/pattern to an action + target"), `default_plugins` (override/disable), `schedules` ("Declarative + cron schedules as {cron_expr, signal_type}"), `jido` (instance module). +- **Server start options** (`Jido.AgentServer.start_link`): `agent` + (required), `id` (auto-generated if absent), `initial_state`, `registry`, + `default_dispatch`, `error_policy`, `max_queue_size` (default 10,000), + `parent`, `on_parent_death`, `spawn_fun`, `debug`. +- **Where config lives:** macro options in code (compile-time), keyword opts + at server start, application config for Jido instances (`use Jido, + otp_app: :my_app`, multiple isolated instances per app, "no global + singletons"). +- **Stated rationale** is the purity boundary (README): "Jido keeps agent + decisions and state transitions explicit, actions may be pure or effectful, + and directives are for effects you want the runtime to own." Directives vs + StateOps encode this: StateOps (SetState, ReplaceState, ...) are handled by + the strategy and "never reach the runtime"; directives are the inverse. +- **jido_ai** layers LLM behavior on top: `use Jido.AI.Agent` wires a ReAct + strategy, `model:` resolved at runtime, tools are ordinary Jido actions, + and each instance gets its own Task.Supervisor via a default skill plugin. + +## Binding time + +- **Compile time:** routes, plugins, merged state schema, plugin actions and + schedules are validated and baked into module attributes; plugin + requirement violations fail the build. "Cannot add new routes or plugins + at runtime." +- **Struct creation (`new/1`):** initial state assembled from schema + defaults + plugin defaults + opts; `strategy.init/2` runs. +- **Runtime-mutable via directives:** cron jobs (`%Cron{}`/`%CronCancel{}`), + sensors (`%StartSensor{}`/`%StopSensor{}`), children + (`%SpawnAgent{}`/`%StopChild{}`/`%AdoptChild{}`), and the state map itself + through `cmd/2` (functionally, new struct each time). +- **Versioning:** `vsn` is a user-supplied string, not auto-incremented. The + `Jido.Agent.Identity` default plugin separately keeps a monotonic `rev` + integer for identity-profile mutations ("Identity state is immutable: + updates produce a new struct with a bumped revision") plus + created/updated timestamps. +- Definition changes are code deploys (BEAM releases/hot upgrades); nothing + product-specific governs in-flight instances. + +## Relationships between nouns + +- **Agent (struct+module) 1:1-per-instance AgentServer (process);** many + instances of one agent module may run, each with its own id. +- **Strategy 1:1 agent module** (compile-time): owns how `cmd/2` executes + (Direct default; ReAct in jido_ai driven by `tick/2` + `%Schedule{}` + directives). +- **Plugin N:1 agent module** (compile-time): contributes actions, routes, + schedules, state defaults. +- **Sensor N:1 server** (runtime): transforms external events into signals. +- **Signal**, a CloudEvents envelope, the universal message. **Instruction**, + normalized action + params + context (jido_action package). **Directive**, + runtime-owned effect emitted by `cmd/2`. **StateOp**, internal state + change, never reaches the runtime. +- **Parent:child**, logical refs (`ParentRef` with pid/id/tag/meta; + `children` map of tag → ChildInfo) layered over a flat DynamicSupervisor + (`Jido.AgentSupervisor`); death coupling is per-child policy, not + supervision-tree structure. +- No session noun. The closest is the server instance's lifetime plus + hibernate/thaw persistence. + +## Lifecycle + +- **Create:** `MyAgent.new(id:, state:)`, pure. **Start:** + `Jido.AgentServer.start_link(agent: MyAgent)` under a DynamicSupervisor; + `post_init` builds the signal router, starts plugin children and sensors, + restores from storage if the lifecycle module says so, registers + schedules, notifies the parent, and lands in `:idle`. +- **Status machine:** `:initializing → :idle → :processing → :idle` (or + `:stopping`). +- **The loop is event-driven, not autonomous polling:** signals arrive via + `call/2` (sync) or `cast/2` (async); processing runs in a Task; a drain + loop executes queued directives; multi-step reasoning (ReAct) is driven by + the strategy's `tick/2` scheduled via directives. **The customer's BEAM + application owns the loop**; Jido is a library/framework, not a hosted + service. +- **Stop:** `%Directive.Stop{}` from inside `cmd/2`, or supervisor shutdown. + `terminate/2` hibernates via the lifecycle module, cancels crons, stops + sensors. +- **Persistence:** `Jido.Persist` hibernate/thaw (`checkpoint/2` / + `restore/2` callbacks to pluggable storage); `Jido.Agent.InstanceManager` + gives keyed singletons with idle-timeout auto-hibernate. Pods persist and + reconcile their collaborator topology after thaw. + +## What makes it "an agent" here (our inference) + +Our inference: Jido defines an agent as a **pure state machine with an +identity**, an immutable struct whose only operation is +`cmd(agent, action) → {new_agent, directives}`, and treats everything the +industry usually bundles into "agent" (process, loop, effects, scheduling, +children) as runtime concerns owned by a separate GenServer. LLM reasoning is +literally optional ("AI is optional"). It is the study's cleanest separation +of agent-as-definition / agent-as-state / agent-as-process, and its +parent-child model (logical refs with explicit orphan policy, decoupled from +supervision) is the most nuanced subagent lifetime-coupling design observed. + +## Open questions + +- No session/run noun: how multi-turn conversations map onto instances is + left to the application (jido_ai adds request tracking in state). +- `vsn` is free-form and unenforced; no migration story for state schema + changes across hibernate/thaw was found. +- No nesting/fan-out limits: supervision defaults are the only backpressure + besides `max_queue_size`. +- The 49-repo org split (jido, jido_action, jido_signal, jido_ai) spreads + the definition across packages; API stability across them is unverified. diff --git a/docs/research/agent-platform/products/kagent.md b/docs/research/agent-platform/products/kagent.md new file mode 100644 index 000000000..f8f7675ff --- /dev/null +++ b/docs/research/agent-platform/products/kagent.md @@ -0,0 +1,328 @@ +# kagent: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from kagent.dev docs and the `kagent-dev/kagent` source (CRD Go +types, controller/translator, ADK runtimes, in-repo architecture docs). +kagent is a CNCF sandbox framework for running AI agents on Kubernetes. + +## Source anchors + +Sources retrieved 2026-07-13. Source-level claims are pinned to the +[kagent v0.9.11 release](https://github.com/kagent-dev/kagent/releases/tag/v0.9.11) +(2026-07-01) at commit +[`14dcbfc`](https://github.com/kagent-dev/kagent/commit/14dcbfc49bde990a0c737da14a9d94cf7e788303). +The kagent.dev docs pages are unversioned live pages built from a separate +`kagent-dev/website` repository, so doc quotes are not release-pinned; where +docs and pinned source could diverge, this dossier prefers the pinned source. + +- Product concepts: [Agents](https://kagent.dev/docs/kagent/concepts/agents), + [Tools](https://kagent.dev/docs/kagent/concepts/tools), + [Architecture](https://kagent.dev/docs/kagent/concepts/architecture), + [Agent Memory](https://kagent.dev/docs/kagent/concepts/agent-memory), + [Agent Substrate](https://kagent.dev/docs/kagent/concepts/agent-substrate), + [Agent Harness](https://kagent.dev/docs/kagent/concepts/agent-harness), and + [What is kagent](https://kagent.dev/docs/kagent/introduction/what-is-kagent). +- Reference: [API reference](https://kagent.dev/docs/kagent/resources/api-ref), + [FAQ](https://kagent.dev/docs/kagent/resources/faq), + [A2A example](https://kagent.dev/docs/kagent/examples/a2a-agents), and + [operational considerations](https://kagent.dev/docs/kagent/operations/operational-considerations). +- Stable CRD symbols: [`Agent`, `AgentSpec`, `Tool`, `TypedReference`, + `A2AConfig`, `AgentStatus`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go), + [`ModelConfig`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/modelconfig_types.go), + and [database models](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/database/models.go). +- Stable runtime symbols: [agent-as-tool compiler](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/translator/agent/compiler.go), + [reconciler](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/reconciler/reconciler.go), + [remote A2A tool](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/adk/pkg/tools/remote_a2a_tool.go), + and [in-repo architecture docs](https://github.com/kagent-dev/kagent/tree/14dcbfc49bde990a0c737da14a9d94cf7e788303/docs/architecture). + +## The `agent` noun (primary-source quotes) + +- [Concepts, Agents](https://kagent.dev/docs/kagent/concepts/agents): "An AI + agent is an application that can interact with users in natural language. + Agents use LLMs to generate responses to user queries and can also execute + actions on behalf of the user." The same page decomposes it into exactly + three components: instructions ("A set of instructions that define the + agent's behavior and capabilities. This is also called a system prompt"), + "Tools: Functions that the agent can use to interact with its environment," + and "Skills: Descriptions of capabilities that help the agent act more + autonomously." +- **Operationally the agent is a Kubernetes custom resource**: kind `Agent`, + group `kagent.dev`, namespaced, current storage version `v1alpha2` + (v1alpha1 is retained unserved). "Agent is the Schema for the agents API" + ([`agent_types.go:664-674`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L664-L674); + [CRD manifest](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/config/crd/bases/kagent.dev_agents.yaml#L1-L17)). + A persistent, identified, GitOps-able record, not a run. +- **The spec bifurcates the noun into two agent kinds** + ([`AgentType` enum](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L31-L38)): + "Declarative configures an agent that is fully described by this resource + (model, instructions, tools) and runs on one of kagent's built-in runtimes," + versus BYO: "a 'bring your own' agent backed by a user-provided container + image. Kagent deploys the image and expects it to serve the agent over the + A2A protocol on port 8080" + ([`agent_types.go:58-68`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L58-L68)). +- Two sibling CRDs extend the noun: **SandboxAgent** ("declares an agent that + runs in an isolated sandbox on Agent Substrate", per the API reference) and + **AgentHarness**, which "provisions the execution environment itself and + runs a third-party coding agent inside it" (backend enum: + `openclaw;nemoclaw;hermes`, + [`agentharness_types.go:19-21`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agentharness_types.go#L19-L21)), + explicitly distinguished from `Agent`, which "runs a kagent-managed agent + runtime" ([Agent Harness](https://kagent.dev/docs/kagent/concepts/agent-harness)). +- **Conceptual model: agent-as-config reconciled into + agent-as-deployed-service.** The CRD spec is the complete definition + (config); the controller continuously materializes it into a running + A2A-speaking workload (one Deployment per agent, or a substrate actor). + Execution state lives in separate Session/Task/Memory records scoped to the + agent's identity. + +## Subagents + +- **Agent-as-tool, declared in the CRD roster.** The `Tool` union type on a + Declarative agent has exactly two provider kinds: `McpServer` and `Agent` + ([`ToolProviderType`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L488-L516)). + Docs: "You also have an option of using agents as tools. Any agent you + create can be referenced and used by other agents you have. You can refer + to agents in other namespaces by using the `namespace/name` format" + ([Tools, "Agents as Tools"](https://kagent.dev/docs/kagent/concepts/tools)). + The worked example is a PromQL agent used as a tool by a second agent + "whenever it needs to create a PromQL query" + ([Agents, "Agents as Tools"](https://kagent.dev/docs/kagent/concepts/agents)). +- **The subagent is not spawned; it is referenced.** The `agent` field is a + bare + [`TypedReference`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L578-L587) + (kind/apiGroup/name/namespace). The referenced agent is an independently + deployed workload with its own Service; the compiler resolves the reference + to a URL (`http://{name}.{namespace}:8080`, or a controller-proxied path for + sandbox agents, + [`toolAgentURL`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/translator/agent/compiler.go#L86-L99)). +- **Inheritance is nothing; the contract is identity-only.** The parent's + compiled config carries per child only + [`RemoteAgentConfig{Name, Url, Headers, Description}`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/adk/types.go#L377-L382); + no model, tools, memory, or approval lists cross the boundary, in either + the Go or the Python runtime. What does cross: the end user's identity + (`x-user-id` header) and conversation lineage headers + (`x-kagent-parent-context-id`, `x-kagent-root-context-id`), plus a static + `x-kagent-source: agent` marker that "hides the session from the agent's + session history sidebar" + ([`remote_a2a_tool.go`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/adk/pkg/tools/remote_a2a_tool.go#L26-L63); + [a2a-subagents.md](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/docs/architecture/a2a-subagents.md)). +- **Fresh child session per delegation edge.** Each remote-agent tool mints + its own A2A context ID at construction, independent of the parent's session: + "KAgentRemoteA2ATool.__init__ generates a UUID (`_last_context_id`) that is + used as the A2A `context_id` for every message sent to the subagent. On the + subagent side, this `context_id` becomes the session ID" + ([a2a-subagents.md:13](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/docs/architecture/a2a-subagents.md#L13)). +- **Communication is a single blocking A2A `SendMessage`** (JSON-RPC), results + only; child failures come back as an error string in the tool output rather + than aborting the parent's turn, and HITL pauses tunnel through a two-phase + resume protocol + ([`remote_a2a_tool.go:255-272`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/adk/pkg/tools/remote_a2a_tool.go#L255-L272)). +- **Nesting: a DAG with depth 10.** Reconcile-time validation recurses the + tool chain: "cycle detected in agent tool chain," "recursion limit reached + in agent tool chain," and "agent tool cannot be used to reference itself" + errors, bounded by `const MAX_DEPTH = 10` (the chain may nest up to 10 + levels) + ([`compiler.go:23`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/translator/agent/compiler.go#L23), + [`:174-180`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/translator/agent/compiler.go#L174-L180), + [`:199-201`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/translator/agent/compiler.go#L199-L201)). +- **Lifetime coupling: none, and nothing protects the edge.** No finalizer, + webhook, or reference count prevents deleting an agent that others use as a + tool; the parent's `Accepted` condition flips to False (reason + `ReconcileFailed`) on its next reconcile while its `Ready` condition, + computed only from its own Deployment, can stay stale-True + ([`reconciler.go:190-193`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/reconciler/reconciler.go#L190-L193), + [`:287-299`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/reconciler/reconciler.go#L287-L299)). + Cross-namespace referencing is gated by `allowedNamespaces`, which "follows + the Gateway API pattern for cross-namespace route attachments" + ([`agent_types.go:84-90`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L84-L90)). + +## Configuration surface (what, where, why) + +- **Declarative agent fields** + ([`DeclarativeAgentSpec`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L168-L231)): + `runtime` (`python` ADK, default, or `go` ADK), `systemMessage` / + `systemMessageFrom` (mutually exclusive by CEL rule) with Go + `promptTemplate` composition, `modelConfig` (by-name reference, default + `"default-model-config"`), `tools` (max 20), `a2aConfig` (published + `AgentSkill` list), `memory` (`modelConfig` embedder reference plus + `ttlDays`, default 15), `stream`, `executeCodeBlocks`, and `deployment`. + Agent-level extras: `skills` (OCI images or Git repos, "made available to + the agent under the `/skills` folder") and `allowedNamespaces`. +- **The environment knob is the Kubernetes pod surface**: + [`SharedDeploymentSpec`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L422-L477) + exposes replicas (default 1), resources, env, volumes, node placement, + security contexts, service accounts, and `extraContainers` ("Useful for + sidecars such as token proxies, log shippers, or security agents"). +- **Model and tools are separate CRDs referenced by name.** + [`ModelConfig`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/modelconfig_types.go#L29-L31) + enumerates nine providers (Anthropic, OpenAI, AzureOpenAI, Ollama, Gemini, + GeminiVertexAI, AnthropicVertexAI, Bedrock, SAPAICore) with per-provider + blocks gated by CEL rules; `RemoteMCPServer` (v1alpha2) carries MCP + endpoint, protocol, and header config. Credentials are always Secret + references (`apiKeySecret`, `headersFrom`), never inline values. +- **Permissions are per-tool HITL**: `requireApproval` lists tool names that + pause for human approval, CEL-enforced to be a subset of `toolNames` + ([`agent_types.go:533`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/v1alpha2/agent_types.go#L533)). + Docs: "Tools listed in `requireApproval` pause execution and present + Approve/Reject buttons in the UI." +- **Where config lives**: CRD YAML via `kubectl` is canonical; Helm values + install the platform and bundled agents; the dashboard UI has a + create/edit wizard; the CLI manages resources and invokes agents. No + triggers: there is no cron or webhook field on `Agent`; the only + trigger-adjacent surface is `AgentHarness.spec.channels` (Slack/Telegram + bindings for harness-run coding agents). +- **Stated rationale, quoted:** declarativeness is the product thesis: + "kagent is designed from the ground up to be declarative. You define the + agents, tools, and instructions and kagent will take care of the rest. Most + other frameworks are procedural and require you to write code to tell the + LLM what to do" ([FAQ](https://kagent.dev/docs/kagent/resources/faq)). + CRDs exist so you can "Define, version, and roll out agents with kubectl + and GitOps" + ([What is kagent](https://kagent.dev/docs/kagent/introduction/what-is-kagent)). + Prompt templates take ConfigMaps only: "Secret references are intentionally + excluded to avoid leaking sensitive data into prompts sent to LLM + providers." HITL exists "to keep humans in control of agent actions." + Cross-namespace references let you "share tool servers across namespaces + without duplicating them" + ([Agents](https://kagent.dev/docs/kagent/concepts/agents); + [Tools](https://kagent.dev/docs/kagent/concepts/tools)). +- **The protocol choices are asserted, not argued.** The A2A feature bullet + says only "Agents discover and invoke each other. Compose multi-agent + workflows with first-class delegation" + ([features](https://kagent.dev/docs/kagent/introduction/features)); MCP is + justified by traction: "a protocol, originally created by Anthropic, which + is meant as a flexible way to provide tools and other information to + Agents... it has begun to gain traction and more and more tools are + adopting it" ([Tools](https://kagent.dev/docs/kagent/concepts/tools)). The + interoperability rationale exists only as an outbound link to Google's A2A + announcement, never restated in kagent's own prose. + +## Binding time + +- **Reconcile time is the binding moment.** The controller resolves + ModelConfig, prompt templates, and MCP references into one fully resolved + `config.json` baked into a Secret before the pod starts: "Prompt templates + are resolved by the controller, not at runtime. The agent receives a fully + resolved string. This makes debugging easier and keeps the runtime simple" + ([architecture README](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/docs/architecture/README.md)). +- **Rebinding means a pod roll, not a mutation.** The pod reads `config.json` + once at startup; a config-hash annotation on the pod template forces a + rolling update when the serialized config or referenced Secrets change + ([`manifest_builder.go:27-30`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/core/internal/controller/translator/agent/manifest_builder.go#L27-L30)). + Docs make secret rotation the sanctioned live path: "kagent automatically + restarts agents when you update the secrets that the agents reference" + ([operational considerations](https://kagent.dev/docs/kagent/operations/operational-considerations)). +- **Per-request bindings**: session identity binds at invocation + (get-or-create by `app_name`, `user_id`, `session_id` in the ADK executor), + and each HITL approval decision binds per tool call at runtime. +- **Versioning is Kubernetes versioning.** Nothing beyond + `generation`/`resourceVersion` plus GitOps; the `version` spec field is + only "the agent's version string, surfaced on the A2A AgentCard" (API + reference). The platform database guarantees an n-1 rollback window + ([upgrade guide](https://kagent.dev/docs/kagent/operations/upgrade)). + +## Relationships between nouns + +- **CRD kinds (v1alpha2)**: Agent, AgentHarness, ModelConfig, + ModelProviderConfig, RemoteMCPServer, SandboxAgent. ToolServer and Memory + survive as v1alpha1-only leftovers never promoted. There is **no Session + CRD and no Team kind**; Team was an AutoGen-era concept that disappeared + with the v0.6 ADK migration (release notes: "The `apiVersion` field in the + kagent CRDs is now `kagent.dev/v1alpha2`"). The intro page still promises + that agents "can also be grouped into teams where a planning agent comes + up with a plan and assigns tasks to individual agents in the team," but no + CRD, controller path, or docs page backs it at the pinned commit; + agent-as-tool is the only implemented composition. +- **Agent 1:1 ModelConfig (by name), 1:N tools (max 20), each tool an MCP + server or another Agent.** Agent-as-tool edges make the agent graph + many-to-many, validated as a DAG. +- **Session is a database row, not a resource**: + `Session{ID, Name, UserID, AgentID, Source}` in the controller's + SQLite/Postgres store, where `Source` distinguishes `user` from `agent` + ("created by a parent agent's A2A call", + [`models.go:55-77`](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/go/api/database/models.go#L55-L77)). + One agent, many sessions. **Task** is the A2A unit of work, N:1 to Session. + **Memory** rows are agent-plus-user scoped vectors with TTL: "Each agent + has its own isolated memory store. You cannot share memories across + agents," and memory is "built on the Google ADK memory implementation and + cannot be swapped" + ([Agent Memory](https://kagent.dev/docs/kagent/concepts/agent-memory)). +- **The CRD is the source of truth; the DB is a cache.** "The DB provides + fast lookups for the HTTP API and UI, while the CRDs remain the source of + truth for agent configuration" + ([architecture README](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/docs/architecture/README.md)). +- **Agent implies its own environment**: one Deployment plus Service plus + config Secret per agent; SandboxAgent swaps that for a substrate actor. + "Session" is overloaded across subsystems: on Agent Substrate it means "The + execution context that tracks an actor's activity and checkpoints" + ([Agent Substrate](https://kagent.dev/docs/kagent/concepts/agent-substrate)). + +## Lifecycle + +- **Create**: `kubectl apply` fires the reconciler pipeline: translate spec + into manifests, reconcile owned resources, upsert the DB record, update + status ([controller-reconciliation.md](https://github.com/kagent-dev/kagent/blob/14dcbfc49bde990a0c737da14a9d94cf7e788303/docs/architecture/controller-reconciliation.md)). + Status carries `Accepted` (spec compiled) and `Ready` (Deployment replicas + available), plus `UnsupportedFeatures`. +- **The platform deploys; the pod owns the loop.** "The kagent engine is the + core component of kagent. It runs the agent's conversation loop and + supports two runtimes": Python ADK (default, built on Google ADK) and a Go + ADK ([Architecture](https://kagent.dev/docs/kagent/concepts/architecture)). + The controller never runs the loop; it proxies: "The controller HTTP server + proxies A2A requests to agent pods. The UI never talks directly to agent + pods" (architecture README). +- **Update**: rolling update with `MaxUnavailable=0`, `MaxSurge=1`; the old + pod drains gracefully on SIGTERM. **Delete**: standard owner-reference + garbage collection, no finalizer on the base Agent; the controller drops + the cached DB row when the Get returns NotFound. +- **Pause/resume exists only on Agent Substrate**: "Substrate decouples an + agent's lifecycle from pod infrastructure. Idle agents are snapshotted to + object storage and rehydrated on demand, so a small pool of pre-warmed + workers can host far more agents than there are pods" + ([Agent Substrate](https://kagent.dev/docs/kagent/concepts/agent-substrate)). + Regular agents are always-on Deployments. +- **Persists across runs**: sessions, tasks, and events in the controller + database; memory vectors until TTL expiry; substrate actor snapshots. +- **Invocation surfaces all speak A2A**: the dashboard POSTs A2A JSON-RPC + through the controller proxy; `kagent invoke` sends the task as an A2A + call; and "Every AI agent created with kagent implements the A2A protocol + and can be invoked by an A2A client" at + `/api/a2a/{namespace}/{agent-name}`, with the agent card at + `.well-known/agent.json` + ([A2A example](https://kagent.dev/docs/kagent/examples/a2a-agents)). + Agents double as tools for outsiders too: "A2A-enabled agents are + automatically exposed as an MCP server on the kagent controller" + ([Agents](https://kagent.dev/docs/kagent/concepts/agents)). + +## What makes it "an agent" here (our inference) + +Our inference: kagent defines an agent **infrastructurally**, as a namespaced +Kubernetes resource whose spec declares persona and capability (instructions, +model reference, tools, skills) and whose reconciler materializes it into a +long-running service that speaks A2A. Agent-ness is not a property of the +loop (that is delegated wholesale to an embedded ADK runtime); it is the +property of being a declaratively managed, addressable workload. The +distinctive datum for the study is that kagent pushes the entire multi-agent +question onto the network: a subagent is just another agent's Service called +over A2A with a fresh session, identity headers forwarded, and nothing else +shared, so delegation inherits Kubernetes semantics (references can dangle, +readiness is per-workload, and isolation is the default) rather than +framework semantics. + +## Open questions + +- Whether an explicit protocol-choice rationale for A2A/MCP exists outside + the docs and blog (maintainer talks, the CNCF sandbox proposal) is + unchecked; on kagent.dev itself the choice is asserted, not argued. +- Why ToolServer and Memory were left behind as v1alpha1-only CRDs while + their replacements (RemoteMCPServer, inline `memory`) moved to v1alpha2 is + undocumented. +- What happens to an in-flight multi-turn loop (mid tool-call) during a + rolling update is unconfirmed: graceful SIGTERM drain is implemented, but + no source states whether a mid-loop turn survives pod replacement. +- The broken agent-as-tool failure mode (Accepted=False with stale + Ready=True) is only inferable from source; no docs page describes it. +- The docs site is built from a separate unversioned repository, so doc + quotes cannot be pinned to the v0.9.11 source snapshot; treat doc-only + claims as current-site claims. diff --git a/docs/research/agent-platform/products/langgraph-platform.md b/docs/research/agent-platform/products/langgraph-platform.md new file mode 100644 index 000000000..fd9eddc66 --- /dev/null +++ b/docs/research/agent-platform/products/langgraph-platform.md @@ -0,0 +1,163 @@ +# LangGraph / LangGraph Platform: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from docs.langchain.com (OSS + LangSmith/Agent Server) and the +`langchain-ai/langgraph` source (prebuilt agent, SDK schema, CLI manifest). + +## The `agent` noun (primary-source quotes) + +LangGraph splits the noun three ways: agent (behavior), graph (deployable +code), assistant (configured instance): + +- **Agent, defined behaviorally:** "Agents are dynamic and define their own + processes and tool usage... Agents have more autonomy than workflows, and + can make decisions about the tools they use... Agents operate in + continuous feedback loops, and are used in situations where problems and + solutions are unpredictable. Workflows have predetermined code paths." + (workflows-agents) LangGraph itself is "a low-level orchestration + framework and runtime for building, managing, and deploying long-running, + stateful agents." +- **The deployable unit is a graph**, code, named in `langgraph.json` + (`"graphs": {"my_agent": "./graphs/agent.py:graph"}`), compiled at server + start. The prebuilt `create_react_agent(model, tools, prompt, ...)` + returns a `CompiledStateGraph`, **the agent literally is a graph** (and + the helper is being renamed to `create_agent` in `langchain.agents`). +- **Assistant, the persistent resource:** "In practice, an assistant is just + an *instance* of a graph with a specific configuration." "Multiple + assistants can reference identical graphs while maintaining distinct + configurations such as prompts, models, or available tools." SDK: + "assistants... are versioned configurations of your graph." Schema: + `assistant_id` (UUID), `graph_id`, `config`, `context`, `version: int`, + `name`, timestamps. +- **Conceptual model: assistant-as-config-over-graph.** Directly comparable + to OpenComputer's agent/revision split, but with the loop's *structure* + (nodes/edges) as customer code rather than a platform-owned runtime. + +## Subagents + +- Five documented multi-agent patterns (multi-agent docs): **Subagents** ("A + main agent coordinates subagents as tools. All routing passes through the + main agent"), **Handoffs** ("Tool calls update a state variable that + triggers routing... switching agents"), **Skills** ("A single agent stays + in control while loading context from skills as needed"), **Router**, and + **Custom Workflow**. +- The structural primitive is the **subgraph**: "a graph that is used as a + node in another graph." Two composition modes: shared state keys (add the + compiled subgraph as a node; communicate over shared channels) vs + different schemas (call inside a node with explicit state transforms, + "common for multi-agent systems maintaining separate message histories per + agent"). +- **Nesting:** "Subgraphs support multiple nesting levels (parent → child → + grandchild)" provided the structure is statically discoverable. +- **Checkpointer inheritance is a per-subgraph knob:** per-invocation + (default, inherits parent's checkpointer), per-thread + (`checkpointer=True`, state accumulates), stateless + (`checkpointer=False`). +- **Handoffs are a state-machine move, not a spawn:** "agent nodes can + return a `Command` object that combines control flow and state updates" + (`goto`, `update`, `resume`); cross-subgraph handoffs use + `graph=Command.PARENT` and require manual message hygiene ("you must + include both the AIMessage containing the tool call [and] a ToolMessage + acknowledging the handoff"). "Unlike single-agent middleware... you must + explicitly decide what messages pass between agents." +- Everything is **declared in graph code at compile time**; the dynamic part + is routing, not roster. + +## Configuration surface (what, where, why) + +- **Graph (compile time, code):** state schema, nodes, edges, checkpointer, + store, interrupts, cache, recursion limits; `create_react_agent` fields: + model (static or per-state dynamic callable), tools, prompt, + response_format, pre/post model hooks, context_schema, name ("used when + adding [the] agent graph to another graph as a subgraph node"). +- **Assistant (API resource):** `config` (tags, recursion_limit, + `configurable` dict), `context` (static input-shaped context, SDK + v0.6.0+), `name`, `description`, `metadata`. +- **Manifest (`langgraph.json`):** dependencies, graphs, checkpointer/store + (with TTL and semantic index), auth, http (feature switches: + `disable_assistants`, `disable_mcp`, `disable_a2a`; the platform exposes + MCP and **A2A endpoints by default**), encryption, webhooks. +- **Stated rationale for assistants:** "This architecture allows + non-engineers to manage assistant configurations through the LangSmith UI + without requiring code changes or redeployment of the underlying graph"; + "rapid iteration without modifying or redeploying your graph code." + +## Binding time + +- **Deploy time:** graph structure (from `langgraph.json`). +- **Assistant create/update:** every update creates a new integer version: + "Each update automatically creates a new version. Users can promote any + version to active status or rollback." Constraint: "you must provide the + entire configuration payload. The update endpoint creates new versions + from scratch and does not merge." +- **Run creation:** per-run `config`, `context`, `interrupt_before/after`, + `checkpoint` (resume/time-travel from any checkpoint), + `multitask_strategy`, `durability` (`sync`/`async`/`exit`). +- **Mid-run:** `interrupt()` in graph code pauses; `Command(resume=...)` + continues; `update_state` forks a new checkpoint at any point in history. +- **In-flight runs on assistant update:** not explicitly documented (runs + reference the assistant and the config they started with, implied); + flagged in Open questions. + +## Relationships between nouns + +- **Deployment 1:N graphs; graph 1:N assistants** (one default assistant + auto-created per graph at deploy); **assistant 1:N versions, 1:N crons.** +- **Thread** = the state container: "A thread maintains the state of a graph + across multiple interactions/invocations (aka runs). It accumulates and + persists the graph's state." Threads are **not owned by an assistant**: + any assistant can run on a thread. Thread 1:N checkpoints (a linked list + via `parent_config`, namespaced per subgraph), 1:N runs. Statuses: idle, + busy, interrupted, error. +- **Run** = "an invocation of an assistant"; belongs to exactly one thread + (or none, stateless runs) and references one assistant. Statuses: + pending, running, error, success, timeout, interrupted. Double-texting + handled by `multitask_strategy`: reject, interrupt, rollback, enqueue. +- **Store** = cross-thread long-term memory (namespaced key-value with + optional semantic index); **checkpoint** = thread-scoped short-term + memory. "Persistence layer gives agents short-term memory through + checkpointers and long-term memory through stores." +- **Cron** references one assistant (optionally one thread) on a schedule. + +## Lifecycle + +- **Assistant:** full CRUD + `get_versions` + `set_latest` (version + promotion/rollback); delete can cascade to threads. +- **Thread:** create (with TTL, or `supersteps` for cross-deployment + copying), get_state at any checkpoint (time travel), update_state (fork), + history, copy, prune. +- **Run:** create/stream on a thread; `stream_resumable` for reconnecting; + `DisconnectMode` cancel/continue; durability modes control checkpoint + timing. +- **Who runs the loop:** shared: the **platform executes** the graph + ("LangSmith Deployment is a workflow orchestration runtime purpose-built + for agent workloads"), but the **loop's shape is customer code** (the + ReAct prebuilt: "calls tools in a loop until a stopping condition is met... + The process repeats until no more tool_calls are present"). +- **Persists:** checkpoints (thread), store items (global), threads + themselves; assistant versions. + +## What makes it "an agent" here (our inference) + +Our inference: LangGraph refuses to make "agent" a noun at the platform +layer: an agent is any graph whose routing decisions are made by an LLM +("workflows have predetermined code paths; agents define their own"). The +durable resources are the **assistant** (versioned config over a graph) and +the **thread** (accumulated state), cleanly separating the three things +other products blur: code (graph, deploy-time), configuration (assistant, +versioned), and state (thread + checkpoints, time-travelable). For a service +design, its checkpoint/fork/time-travel model and the +full-payload-versioned assistant are the most complete binding-time answer +in the study. + +## Open questions + +- In-flight run behavior across assistant version updates is undocumented. +- The Agent Server product naming ("agent" in marketing, "assistant" in the + API) is in active flux; `create_react_agent` → `create_agent` migration + suggests further renaming. +- A2A and MCP endpoints are on by default at the platform edge; how the + assistant maps to an A2A Agent Card was not covered in the pages read. +- `context` vs `config.configurable` overlap (v0.6.0 addition) is still + settling. diff --git a/docs/research/agent-platform/products/netclaw.md b/docs/research/agent-platform/products/netclaw.md new file mode 100644 index 000000000..fdcffc161 --- /dev/null +++ b/docs/research/agent-platform/products/netclaw.md @@ -0,0 +1,170 @@ +# Netclaw (Petabridge): what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from the `netclaw-dev/netclaw` source (branch `dev`, v0.24.x), +its PRDs, and netclaw.dev docs. Disambiguation: Petabridge's Akka.NET +project, not `automateyournetwork/netclaw`. Repo topics acknowledge the +lineage: `openclaw`, `hermes-agent`. + +## The `agent` noun (primary-source quotes) + +- **The agent is the daemon, one per deployment.** "Netclaw is a + single-process .NET 10 application that runs as an always-on autonomous + agent" (architecture overview); "An AI agent that runs on your hardware, + connects to the communications tools you and your team use." The PRD + sharpens it: "Netclaw is not just a chat assistant, it is an autonomous + operations platform that can monitor, react, investigate, delegate work, + and manage its own schedule." (PRD-001) +- **Identity is data, not code, and file-shaped.** "Agent identity is + stored as data (markdown files), not code. The agent reconstructs its + personality from files on every session start." (PRD-007) The identity + directory: `~/.netclaw/identity/SOUL.md` (personality, tone, user's + name), `AGENTS.md` (purpose/mission, operating rules), `TOOLING.md` + (host capabilities), assembled by `SystemPromptAssembler`, with + project-scoped instructions resolved from `[projectDir]/.netclaw/ + AGENTS.md`, `CLAUDE.md`, `AGENTS.md`, or `CONTEXT.md` (first match). +- **Part of the charter is compiled into the binary:** the operating-rules + `AGENTS.md` ships as an embedded resource (read-only, placeholder + substitution at runtime), while SOUL.md/TOOLING.md are operator-written + disk files. Public-audience sessions get a stripped variant and no + TOOLING/project instructions. +- **Live identity binding:** at session-actor recovery, "Always read fresh + from disk, identity file edits take effect immediately" + (`LlmSessionActor`). Claude Code-style live reload, bound per + session-actor start rather than per delegation. +- **Conceptual model: agent-as-daemon** (OpenClaw/Hermes personal-daemon + family) with file-shaped identity, one process, one persona, N + event-sourced conversation sessions inside it. + +## Subagents + +- **Ephemeral child actors, spawned by tool call.** `SubAgentActor`: + "Ephemeral actor that runs a non-interactive LLM session... executes an + autonomous turn loop with tool calls, and returns the final text + response as a SubAgentResult to the caller. Then stops itself. **No + persistence, no subscribers, no streaming, no compaction.**" Spawned via + the `spawn_agent(agent, task, context)` tool; definitions are markdown + files at `~/.netclaw/agents/*.md` (or server feeds), declared roster, + dynamic invocation, the Claude Code pattern. +- **Trust inheritance is mandatory and fail-closed:** "A sub-agent + inherits the spawning session's audience. A spawn with no audience is a + programming error: defaulting to Personal would silently grant the + sub-agent broader trust than its parent." Inherited: audience, parent + CWD/session directory, operating rules, tool policy ("Agent definition + tool metadata is advisory only"). Isolated: own ephemeral history, own + system prompt (sub-agent markdown + a hardcoded "[Subagent Execution + Contract]": "You are a headless, non-interactive worker... Do not ask + the user clarifying questions... Always end by emitting a final output + for the parent session."). **SOUL.md is not inherited**, persona stays + with the main agent; children get rules, not personality. +- **Depth 1 by static denial:** `spawn_agent` is in the sub-agent's denied + tool set ("the only static sub-agent-specific filter denies recursive + spawn_agent delegation"). Iteration cap 30 per sub-agent; no explicit + fan-out cap. +- **Results-only return, supervised lifetime:** result via Akka Ask + (`SubAgentResult` → parent), then self-stop; scope id + `{parentSessionId}/subagent/{name}/{runId}`. Tool approvals **bubble up + to the parent session's human** through an `IParentApprovalBridge`. + +## Configuration surface (what, where, why) + +- **Layered file tree under `~/.netclaw/`:** `config/netclaw.json` + (schema-validated, `additionalProperties: false` throughout), + `config/secrets.json` (encrypted, `ENC:` prefix), + `config/tool-approvals.json`, `config/hard-deny-overrides.json`, + `identity/*.md`, `skills/` (native + `.system/` CDN-synced + + `.server-feeds/`), `agents/*.md` (subagent roster), + `schedules/reminders/`, SQLite at `netclaw.db`. +- **Config sections:** channels (Slack/Discord/Mattermost with per-channel + `ChannelAudiences`), `Session` (turn iteration cap 60, timeouts, + compaction tuning: threshold, snapshot interval, kept tool results/ + messages, memory distillation intervals), `Security` + (`DeploymentPosture` Public/Team/Personal, `ShellExecutionMode`), + `Tools` (per-audience profiles, "Audience tool grants are monotonic: + Public ⊆ Team ⊆ Personal", plus WebFetch allowlists, hard-deny + patterns), `Providers`/`Models` (Main/Fallback/**Compaction** as + separate model refs), `Memory` (recall timeout 300ms, max 3 injected), + `SkillSync`/`ExternalSkills`/`SkillFeeds`, `SubAgents`, `Daemon`. +- **Stated rationale:** "Secure failure mode: invalid policy/config blocks + startup" (PRD-001), fail-closed as the design center, echoed in the + security dossier's four-layer invocation stack. + +## Binding time + +- **Daemon start:** full schema validation; invalid config prevents + startup; tools statically registered. +- **Config hot-reload is validate-before-restart with drain:** "Invalid + config changes SHALL be rejected... and SHALL keep the current daemon + instance running. Valid changes SHALL trigger coordinated daemon + restart: close new ingress, drain active sessions, restart, relaunch + the sessions that were active, and resume from the last durable + checkpoint." (PRD-001), blue/green semantics inside one binary, + enabled by event-sourced sessions. +- **Skills hot-reload without restart** (500ms-debounced watcher); + identity files bind at session-actor recovery ("edits take effect + immediately" on next actor start; no mid-session re-assembly, after + compaction only the context layers re-inject, not the system prompt). +- **Audience binds per turn** from the message's channel mapping. +- **No versioning of config or persona**, files on disk, no git + integration, no revision concept. + +## Relationships between nouns + +- **Daemon 1:N sessions**; session = persistent Akka actor keyed by + channel+thread ("Session identity is Slack thread: + `{channelId}/{threadTs}`"), `PersistenceId = session-{entityId}`. +- **Session 1:0..N ephemeral subagents** (children in the supervision + tree); **daemon 1:1 skill registry and reminder manager** (shared); + session's tool view = registry filtered by the turn's audience. +- **"Everything is just input":** Slack message, webhook, timer, CLI, + all become messages to a session actor; reminders fire as + `SendUserMessage` indistinguishable from user input (dedup via + `ProcessedReminderIds` rebuilt from the journal). +- **The session is our "session"; the daemon is the agent identity**, the + same split as OpenClaw/Hermes, implemented as actors. + +## Lifecycle + +- **Session phases:** Recovering → Ready → Processing ⇄ Compacting → + Passivating (aborted by an incoming message). Idle passivation (default + 30 min): final memory distillation → snapshot → stop; next message + recreates the actor and replays. +- **Event-sourced sessions, a mini-ledger inside one daemon:** persisted + events include `TurnRecorded`, `SessionCompacted`, `SessionTitleSet`, + and a full tool audit trail: `ToolBatchStarted`, `ToolCallRecorded`, + `ToolApprovalRequested`, `ToolApprovalResolved`, `ToolBatchAbandoned`. + Snapshots at passivation and after compaction. Crash recovery = journal + replay; "if one conversation crashes, the rest keep running." +- **Who runs the loop:** the operator's own daemon (self-hosted, + systemd user service, loopback-bound, OS-level singleton lock). +- **Persists across restarts:** all session journals + snapshots, + cross-session memory (SQLite), skills, config, identity files. + +## What makes it "an agent" here (our inference) + +Our inference: Netclaw defines the agent as **a supervised, event-sourced +resident process with a file-shaped soul**, one daemon whose identity is +markdown reconstructed at every session start, whose conversations are +independently persistent actors, and whose every tool call is journaled +and policy-gated by trust audience. Its explicit anti-chatbot criteria +(always-on, durable sessions, tools, schedules, memory, trust tiers, +self-modifiable identity files) read like a checklist of the study's +convergences implemented in one binary. Three details are operationally +notable: **tool-approval events in the session journal** +(`ToolApprovalRequested/Resolved`), **fail-closed audience inheritance for +subagents** ("defaulting to Personal would silently grant broader trust than +its parent"), and **validate-before-restart config reload with session drain +and replay**. + +## Open questions + +- Multiple agents per daemon: not supported today (one identity); whether + the `agents/*.md` roster ever grows into peer personas vs staying + headless workers. +- No versioning of identity/config despite event-sourced sessions, the + definition plane is mutable files while the execution plane is + immutable events. +- Fan-out cap for concurrent subagent spawns within one tool batch is + unstated. +- Pre-1.0 (0.24.x, ~4.5 months old); details will drift. diff --git a/docs/research/agent-platform/products/openai-agents-sdk.md b/docs/research/agent-platform/products/openai-agents-sdk.md new file mode 100644 index 000000000..2c4623b95 --- /dev/null +++ b/docs/research/agent-platform/products/openai-agents-sdk.md @@ -0,0 +1,152 @@ +# OpenAI Agents SDK + AgentKit: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from the Agents SDK docs, the `openai-agents-python` source, and the +Assistants API migration guide. AgentKit/Agent Builder pages were not +publicly reachable (403); noted under Open questions. + +## The `agent` noun (primary-source quotes) + +- SDK docs: an agent is "a large language model (LLM) configured with + instructions, tools, and optional runtime behavior such as handoffs, + guardrails, and structured outputs." +- Class docstring (`src/agents/agent.py`): "An agent is an AI model + configured with instructions, tools, guardrails, handoffs and more." +- It is a **plain Python `@dataclass`**, no server-side ID, no CRUD, no + persistence. Fields: `name` (required), `instructions` (str **or a + callable** evaluated per run), `prompt` (server-side Prompt object), + `handoffs`, `model`, `model_settings`, `tools`, `mcp_servers` ("You are + expected to manage the lifecycle of these servers"), `input_guardrails`, + `output_guardrails`, `output_type`, `hooks`, `tool_use_behavior`, + `reset_tool_choice` ("ensures that the agent doesn't enter an infinite + loop of tool usage"). +- Platform guide framing: "Agents are applications that plan, call tools, + collaborate across specialists, and keep enough state to complete + multi-step work." Practical Guide PDF: "systems that independently + accomplish tasks on your behalf." +- **History, the deprecated Assistants API is the contrast case:** + "Assistants were persistent API objects that bundled model choice, + instructions, and tool declarations, created and managed entirely through + the API" (id, CRUD, Threads, polled Runs). It shuts down August 26, 2026; + the migration maps Assistants → Prompts (server-side versioned config), + Threads → Conversations, Runs → Responses. Stated rationale: Responses API + offers "better performance and new features"; "Your application code now + handles orchestration... while your prompt focuses on high-level behavior." +- **Conceptual model: agent-as-code-object.** OpenAI moved *away* from + agent-as-API-resource (Assistants) toward ephemeral code objects, while + quietly re-introducing server-side versioned config through the `prompt` + field ("Prompts allow you to dynamically configure the instructions, tools + and other config for an agent outside of your code"). + +## Subagents + +Two delegation mechanisms, distinguished precisely by the SDK itself: + +- **Handoffs**, control transfer. "Handoffs are sub-agents that the agent + can delegate to" (the `handoffs` constructor field). They surface to the + LLM as tools named `transfer_to_`. "It's as though the new + agent takes over the conversation, and gets to see the entire previous + conversation history", customizable via `input_filter` ("By default, the + new agent sees the entire conversation history"). +- **Agent-as-tool**, result return. From `Agent.as_tool()`: "In handoffs, + the new agent receives the conversation history... and takes over the + conversation. In this tool, the new agent receives generated input... and + the conversation is continued by the original agent." +- Declaration is **static in the constructor** (the roster is the `handoffs` + list), with dynamic enable/disable via `is_enabled` callables. "Handoffs + stay within a single run." No depth limit documented. +- Nothing is inherited: each target agent brings its own instructions, + model, and tools. What *is* shared run-wide is the context object: "every + agent, tool function, lifecycle etc for a given agent run must use the + same type of context." +- Guardrails scope by chain position: input guardrails run "only if the + agent is the first agent in the chain"; output guardrails "only if the + agent produces a final output." + +## Configuration surface (what, where, why) + +- **Agent constructor:** full field table above; notable design choices are + dynamic `instructions` (a function of run context), `output_type` + (structured output = loop termination condition), and + `tool_use_behavior` (`run_llm_again` default vs `stop_on_first_tool` / + custom). +- **RunConfig (per-run):** `model` ("will override the model set on every + agent"), `model_settings`, global `handoff_input_filter`, run-level + guardrails, tracing fields (`workflow_name`, `trace_id`, `group_id`), + `call_model_input_filter` ("allows editing input sent to the model e.g. to + stay within a token limit"), session settings, sandbox and tool-execution + configs. `DEFAULT_MAX_TURNS = 10`. +- **Where config lives:** pure code, except the optional `prompt` field + pulling from a server-managed, versioned Prompt resource ("snapshot, + review, diff, and roll back"), the bridge back to server-side config. +- **Stated rationale:** handoffs, "Allows for separation of concerns and + modularity"; guardrails, "you can run a guardrail with a fast/cheap + model... saving you time and money." + +## Binding time + +- Everything binds at **construction/run time in code**. Dynamic + instructions re-evaluate per invocation; `agent.clone()` is + `dataclasses.replace` (shallow copy); RunConfig overrides apply per run. +- **No versioning of agents**, except via the server-side Prompt resource + and, historically, Assistants CRUD. The industry-historical note: OpenAI + had versioned server-side agents (Assistants), deprecated them, and now + versions only the *prompt config*, not the agent. + +## Relationships between nouns + +- **Agent : Run, many-to-many.** `Runner.run(starting_agent, input)` is a + stateless classmethod; one agent can start many runs, and one run can + traverse many agents via handoffs. The loop (from `run.py`): invoke agent + → if `output_type`-matching final output, stop → if handoff, loop with new + agent → else run tools and loop. "The rule for whether the LLM output is + considered as a 'final output' is that it produces text output with the + desired type, and there are no tool calls." +- **Session**, conversation state, independent of any agent: "Session + stores conversation history for a specific session, allowing agents to + maintain context without requiring explicit manual memory management." + Implementations: SQLite (memory/file), Redis, OpenAIConversationsSession + (server-side). Keyed by `session_id`; passed to `Runner.run(session=...)`. +- **Trace/Span**, "Traces represent a single end-to-end operation of a + 'workflow'"; agent, generation, function, guardrail, and handoff spans. +- **Handoff**, a frozen dataclass wrapping the target agent as a tool + (`tool_name`, `input_filter`, `is_enabled`). +- **Thread**, legacy Assistants noun, mapped to Conversations. +- A **turn** is "one AI invocation (including any tool calls that might + occur)." + +## Lifecycle + +- **The agent has no lifecycle**, no create/start/stop/delete; it is + constructed and garbage-collected. Only sessions and traces persist. +- The run loop enforces `max_turns` (`MaxTurnsExceeded`), supports + interruption/approval via a resumable `RunState`, and streams via + `run_streamed`. +- **The customer's process owns the loop entirely** (library model), with + the Responses API as the server-side primitive ("agentic by default," + `previous_response_id` chaining, `store: true` for server-kept state). +- Assistants lifecycle (create/retrieve/modify/delete + polled runs) is the + deprecated contrast. + +## What makes it "an agent" here (our inference) + +Our inference: for OpenAI, an agent is **a configured invocation pattern, +not an entity**, a bundle of instructions + tools + termination rule +(`output_type`) that a stateless runner loops over until final output. The +noteworthy datum for a service design is directional: OpenAI *had* the +server-side agent resource everyone else is building (Assistants), and +retired it in favor of code objects + a versioned server-side Prompt + +server-side Conversations, decomposing "agent" into config, state, and loop +rather than keeping it one resource. + +## Open questions + +- AgentKit / Agent Builder / ChatKit docs were unreachable (403); the visual + workflow builder's noun model (published workflow versions?) is uncaptured. +- The `sandbox` and `tool_execution` RunConfig fields suggest an execution + environment story that the docs read did not elaborate. +- `nest_handoff_history` is "opt-in beta", handoff history semantics are + still moving. +- Practical Guide PDF quotes came via search snippets, not direct text + extraction. diff --git a/docs/research/agent-platform/products/openclaw.md b/docs/research/agent-platform/products/openclaw.md new file mode 100644 index 000000000..cc504de3f --- /dev/null +++ b/docs/research/agent-platform/products/openclaw.md @@ -0,0 +1,167 @@ +# OpenClaw: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from docs.openclaw.ai and the `openclaw/openclaw` source +(v2026.6.11). History: Warelay → Clawdbot → Moltbot → OpenClaw (Jan 2026, +after Anthropic trademark complaints); created by Peter Steinberger, now +stewarded by the OpenClaw Foundation (sponsors include OpenAI, GitHub, +NVIDIA, Vercel); ~247k stars as of March 2026. MIT, TypeScript. + +## The `agent` noun (primary-source quotes) + +- README: "OpenClaw is a *personal AI assistant* you run on your own + devices. It answers you on the channels you already use... The Gateway is + just the control plane, the product is the assistant." +- The formal definition (docs/concepts/multi-agent): "**Agent**: A fully + isolated persona consisting of workspace files, authentication profiles, + model registry, and session store. Each agent operates independently with + its own `agentId`." +- "Each configured agent has its own workspace, bootstrap files, and + session store." "The embedded agent runtime is OpenClaw-owned: model + discovery, tool wiring, prompt assembly, session management, and channel + delivery share one integrated runtime surface." (docs/concepts/agent) +- In code, `AgentConfig` (`src/config/types.agents.ts`): `id`, `default?`, + `name`, `description`, `workspace`, `agentDir`, `model`, `skills`, + `identity` (name/theme/emoji/avatar), `subagents`, `sandbox`, `tools`, + `runtime`. `DEFAULT_AGENT_ID = "main"`. +- **The persona is a directory of markdown files** (`src/agents/ + workspace.ts`): `AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, + `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md`, `MEMORY.md` ("durable user + preferences and behavior guidance" per the system prompt). The workspace + is git-initialized on creation. +- **Conceptual model: agent-as-personal-daemon persona**, a long-lived + identity (workspace + auth + sessions) hosted by a single gateway + process. Default deployment is one agent ("main"); multi-agent is + multiple isolated personas in one gateway, bound to channels by routing + rules. + +## Subagents + +- Two spawn mechanisms behind one `sessions_spawn` tool: + `runtime="subagent"` (native embedded runner) and `runtime="acp"` + (external coding agents like Codex over OpenClaw's ACP protocol). + "Sandboxed sessions cannot spawn ACP sessions because runtime='acp' runs + on the host." +- **Config-bounded spawning** (`subagents` block): `delegationMode` + ("suggest" | "prefer"), `allowAgents`, `maxConcurrent` (8), + `maxSpawnDepth` (1, "no nested spawns" by default), + `maxChildrenPerAgent` (5), `archiveAfterMinutes` (60), per-spawn model + override, timeouts. +- **Inheritance:** cross-agent spawns use the *target* agent's workspace + ("For cross-agent spawns, use the target agent's workspace instead of the + requester's"); tool allow/deny lists are inherited from the parent. +- **Agent-to-agent is off by default and allowlisted:** + `tools.agentToAgent.enabled`, "Keep off in simple deployments and enable + only when orchestration value outweighs complexity"; ping-pong chains + bounded by `maxPingPongTurns`. Cross-agent memory search requires + explicit extra collections. +- **Philosophical ceiling** (VISION.md "What We Will Not Merge"): + "Agent-hierarchy frameworks (manager-of-managers / nested planner trees) + as a default architecture" and "heavy orchestration layers" are + explicitly rejected. + +## Configuration surface (what, where, why) + +- **One JSON5 file:** `~/.openclaw/openclaw.json` (splittable via + `$include`; editable via CLI onboarding, web Control UI, or direct edit + with hot reload). Sections: `agents.defaults` + `agents.list[]` + + `agents.bindings[]` (channel/account/peer/guild/team → agentId), + `channels.*` (with `dmPolicy`/`groupPolicy`/`allowFrom`), `session` + (dmScope: "main" | "per-peer" | "per-channel-peer" | + "per-account-channel-peer"; reset: daily/idle), `gateway` + (port 18789, auth, reload mode), `cron`, `hooks`, + `heartbeat` (default every 30m; "If nothing needs attention, reply + HEARTBEAT_OK"; lightContext / isolatedSession flags), `sandbox` + (mode: "off" | "non-main" | "all"; scope: "session" | "agent" | "shared"; + docker backend), `tools.agentToAgent`, `tools.elevated`, + `models.providers` (35+ providers), `mcp.servers`. +- **Persona/behavior config is markdown in the workspace**, not JSON, the + same file-as-config pattern as Claude Code and Hermes. +- **Stated rationale, a deliberately high config bar** (repo AGENTS.md): + "Before adding a config option or env var, first prove existing product + behavior, provider selection, defaults, or doctor migration cannot solve + it. Prefer removing or consolidating config/env options." +- **Security rationale is unusually explicit:** "OpenClaw is local-first + agent infrastructure for trusted operators; it is not designed as a + shared multi-tenant boundary between adversarial users on one gateway"; + "Adversarial-user isolation needs separate gateways (and ideally separate + OS users/hosts)"; "Treat inbound DMs as **untrusted input**"; "Default: + tools run on the host for the `main` session, so the agent has full + access when it is just you." + +## Binding time + +- **Hot reload is the norm:** "The Gateway watches `~/.openclaw/ + openclaw.json` and applies changes automatically." Hot-applied: channels, + agents, tools, skills, automation, sessions. Restart-only: gateway server + settings and plugin/manifest metadata. Reload modes: hybrid (default) / + hot / restart / off. A rate-limited `config.patch` RPC does partial + merges. +- **Per-message binding:** agent selection happens at message time via + deterministic routing, "most-specific wins": exact peer > parent peer > + peer wildcard > guild+roles > guild > team > account > channel > default + agent. +- **Versioning:** none built-in; the workspace is git-initialized and docs + recommend version-controlling it. + +## Relationships between nouns + +- **Host 1:1 Gateway; Gateway 1:N agents, channels, nodes** (devices over + WebSocket). "One Gateway per host; it is the only place that opens a + WhatsApp session." +- **Agent 1:1 workspace, agentDir, auth profiles, session store; 1:N + sessions.** Session key format `agent:::` (e.g. + `agent:main:dm:+1234`, `agent:main:subagent:1`, `agent:codex:acp:1`). +- **Session 1:1 JSONL transcript + UUID;** DM scope decides whether all DMs + share one "main" session per agent or split per peer/channel/account; + groups isolate per group; cron runs get "fresh session per run"; webhooks + "isolated per hook." +- State split across per-agent SQLite (agent-scoped state/cache), a shared + gateway SQLite (global runtime state, plugin KV), markdown memory files, + and JSONL transcripts. +- **ACP** is the internal bridge protocol for hosting external coding-agent + runtimes inside the gateway's session model; OpenClaw is also both an MCP + server and MCP client registry. A `migrate-claude` extension imports + Claude Code/Desktop config, and `hermes claw migrate` exists on the + competitor's side, this niche has migration paths in both directions. + +## Lifecycle + +- **The gateway is an OS daemon** (launchd / systemd / Windows Task + Scheduler; `openclaw onboard --install-daemon`), a long-lived Node + process; OpenClaw's embedded runtime owns the loop, prompt assembly, and + tool wiring. +- **Sessions** persist until reset (daily at a configured hour by default, + idle-based, or manual `/new`); transcripts persist as JSONL regardless. +- **Heartbeat** wakes the agent every ~30m with a strict prompt contract + (HEARTBEAT.md, "reply HEARTBEAT_OK" when idle), proactive scheduled + cognition as a first-class lifecycle feature. +- **Persists:** workspace markdown (persona + memory), transcripts, SQLite + state, credentials. Everything is on the operator's disk, "All state may + contain secrets." + +## What makes it "an agent" here (our inference) + +Our inference: OpenClaw's agent is a **resident persona**, a named +directory of markdown (soul, identity, memory, tools) plus credentials and +a session store, animated by one always-on gateway daemon and reachable +wherever the operator already chats. Identity is file-shaped and +git-versionable, sessions are routing-scoped conversation lanes rather than +task runs, and delegation is deliberately shallow (depth-1, allowlisted, +with hierarchy frameworks explicitly refused). Together with Hermes it +defines the personal-daemon corner of the spectrum, and its sharpest +contribution to a service design is the trust-boundary statement: one +gateway equals one trusted operator; adversarial isolation belongs at the +process/host boundary, not inside the agent model. + +## Open questions + +- Multi-agent-per-gateway plus `agentToAgent` blurs the single-operator + trust model; the docs punt adversarial isolation to separate gateways. +- Subagent session archival (`archiveAfterMinutes`) semantics vs transcript + retention were not fully traced. +- ACP protocol versioning/compat guarantees for external runtimes (Codex + harness pinned at a specific version) are unclear. +- The Foundation governance model (post-Steinberger-to-OpenAI) and its + effect on roadmap is outside the code but relevant to adoption bets. diff --git a/docs/research/agent-platform/products/opencomputer.md b/docs/research/agent-platform/products/opencomputer.md new file mode 100644 index 000000000..a78af3669 --- /dev/null +++ b/docs/research/agent-platform/products/opencomputer.md @@ -0,0 +1,228 @@ +# OpenComputer: what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from the official docs plus the code in `diggerhq/opencomputer` +(TypeScript SDK, web Zod schemas, design docs) and `diggerhq/oc-runtimes`. + +## The `agent` noun (primary-source quotes) + +- Docs definition: "An **agent** is reusable configuration: `name`, `prompt`, + `model`, `runtime`, and optional **skills**." + (docs.opencomputer.dev/agent-sessions/agents) +- "Every session runs on an agent, so create one first, then start as many + sessions on it as you like." (agent-sessions/agents) +- "The only thing an agent needs is a **model** and a **prompt**, create one + and it runs on a default, managed runtime that just works." + (agent-sessions/agents) +- SDK class comment: `/** Reusable agents, the "what" a session runs. */` + (`sdks/typescript/src/agents/agents.ts:60`) +- The SDK type is a persistent record, not a process + (`sdks/typescript/src/agents/types.ts:32-45`): + +```typescript +export interface Agent { + id: string; + name: string; + promptHash?: string; + model: string; + runtime: Runtime; + revision?: number; + /** The agent's active revision (what new sessions run); `prompt`/`model` are served from it. */ + activeRevision?: { id: string; number: number; digest: string } | null; + credentialId?: CredentialRef | null; + limits?: Limits; + createdAt?: string; + updatedAt?: string; +} +``` + +- Creation is idempotent by name: "Idempotent by name server-side → safe to + auto-retry transient failures." (`agents.ts:85`) +- **Conceptual model: agent-as-config.** The agent is a named, versioned, + persistent configuration record. Execution lives entirely on the session; + the agent object holds no runtime state. +- Caveat: the codebase carries a second, older "agent" concept, the + sandbox-embedded agent in the Python SDK (`sdks/python/opencomputer/agent.py`, + `POST /sandboxes/:id/agent`), which is a Claude Code process running inside + a sandbox VM. The primary current product (v3 Durable Agent Sessions) is the + agent-as-config model above. A separate older repo `diggerhq/oc-agents` uses + "agent" to mean roughly what v3 calls a session. + +## Subagents + +- Not in the platform data model. No `parent_id`, `subagent`, or child-agent + concept exists in the SDK types, Zod schemas, or design docs; built-in + runtimes (`claude`, `codex`, `pi`) expose no subagent-spawning tool. +- Subagents exist only inside the **Flue** framework runtime, in-process and + explicitly second-class: "Subagents (`session.task()`) bundle and execute + in-process, but they are **outside the tested profile**: their model + declarations aren't validated the way the top-level agent's is, and subagent + steps may appear incompletely in the session event log." + (agent-sessions/flue) +- The platform's sanctioned fan-out unit is the session, not the subagent: + "One agent, one conversation per session... **fan out by creating + sessions**." (agent-sessions/flue) +- Sessions do not share memory, so fanned-out sessions are isolated: "**No + cross-session memory.** Each session starts from the agent's prompt + that + session's `input`." (agent-sessions/sessions) +- Events can be attributed to an actor of `type: "human" | "agent" | "system"` + (`types.ts:26-30`), but this is attribution, not hierarchy. +- Nesting limits, inheritance rules, and parent-child communication are + undocumented (the concept barely exists). + +## Configuration surface (what, where, why) + +Agent-level fields (`CreateAgentParams`, `agents.ts:9-21`): + +- `name`, identity; creation is idempotent by name. +- `prompt`, required instructional text. +- `model`, required `provider/model` id; "the provider must be one the + runtime can drive (`anthropic/…` for `claude`, `openai/…` for `codex`, any + catalog provider for `pi`)." +- `runtime`, `claude` | `codex` | `pi` | `flue` | custom; "immutable after + create." +- `key` / `credential`, inline provider key (sealed into a credential) or + `cred_…` id or the `"managed"` sentinel ("run via OpenComputer, no provider + key"); omit to inherit the org default. +- `limits`, `{ tokens?, turnSeconds?, turns? }`. +- `skills`, folder-based instruction sets (`SKILL.md` with YAML frontmatter), + deployed as part of revisions, not a create param. + +Per-session overrides (`CreateSessionParams`): `model`, `revision` (pin a +specific revision), `limits`, `sources` (repos checked out into +`/workspace/sources/`), `metadata` (opaque JSON ≤16 KB, not shown to the +model), `key` ("get-or-create idempotency/routing key, one session per +key"), `input`, webhook `destinations`. + +Where config lives: + +- API objects (`POST /agents`, SDK `oc.agents.create({})`). +- A repo file layout for push-to-deploy: `agent.toml` (`[agent].id`, + `model = "..."`) + `prompt.md` + optional `skills/` directory. +- Credentials in a secrets vault (Infisical), referenced by `cred_…` id. +- At runtime, config reaches the sandbox as `OC_*` env vars + (`OC_AGENT_PROMPT`, `OC_MODEL`, `OC_SKILL_BUNDLE_DIGEST`, ...) + (`oc-runtimes/README.md`). + +Stated rationale for the knobs: + +- Revisions: "every deployment... appends a new one, and **you can roll back + instantly**." (agent-sessions/revisions) +- Skills: give the agent "a playbook it reaches for, **without bloating every + prompt**", progressive disclosure to keep context lean. + (agent-sessions/skills) +- Brain/hands sandbox split: "Commands and file edits stay isolated from the + model loop." (agent-sessions/runtimes) +- Sealed credentials: "The real key never enters the sandbox." / "once + submitted it's never returned by the API." (agent-sessions/credentials) +- Managed credential: run "billed to your credits, with no key to manage." + (agent-sessions/credentials) + +## Binding time + +- **Agent create (immutable):** `runtime`, "to switch engines, create a new + agent." (agent-sessions/runtimes) +- **Agent update / deploy (versioned):** `prompt`, `model`, `skills` are + served from the active revision; every deploy appends an immutable, linear, + numbered revision ("like a Vercel/Fly deployment... never rewritten"). + Rollback = activate an earlier revision. `activate: false` stages a revision + without making it live. +- **Session create (frozen):** "When a session is created it **freezes** the + agent's active revision, `prompt`, `model`, `runtime`, and `skills`, into + the session, and pins the resolved credential too." (agent-sessions/agents) + The pinned copy lives on the session as `agentSnapshot` + (`types.ts:73`). +- **Mid-session (pinned, one exception):** "Editing the named agent afterwards + never changes a running or resumed session, its behavior is pinned for its + whole life." (agent-sessions/agents) The exception is credential rotation, + "the one exception to a session's otherwise pinned config" + (agent-sessions/credentials). Steering (new messages) and webhook + destinations are mutable mid-session; behavior config is not. +- **In-flight vs new:** "In-flight sessions keep the revision they started on; + only new sessions pick up a change." (agent-sessions/revisions) + +## Relationships between nouns + +- **Agent → Session: 1:many.** Session has `agentId`; sessions list filters by + agent. Session is the run; agent is the template. +- **Agent → Revision: 1:many, linear.** Exactly one active revision at a time; + the agent holds an `activeRevision` pointer. +- **Agent → Deployment: 1:many.** A deployment (states: accepted → queued → + ... → ready | failed | superseded) produces a revision. +- **Agent → Schedule: 1:many.** A schedule (`sch_…`, states active | paused | + auto_paused) fires one new session per cron slot. +- **Session → Sandboxes: one brain + one hands.** "The runtime runs the agent + loop in its **own per-session sandbox** (the *brain*)... file and shell + tools run in a **separate sandbox** (the *hands*)." + (agent-sessions/runtimes; `SessionSchema.sandboxes.{brain,hands}` in + `web/src/api/schemas.ts:503-507`). Sandboxes are owned by the end-user org + (design doc `agent-sandbox-ownership.md`). +- **Session → Turn: 1:many.** A turn is one wakeup cycle of the loop; each + steer message starts a new turn. +- **Session → event log: 1:1, append-only.** "A **session** is an agent run + with an **append-only event log**, a pinned agent snapshot, and a lifecycle + status." (agent-sessions/sessions) +- **Session → artifacts, watches, channels, destinations: 1:many.** Watches + wake the session on third-party GitHub PR changes ("A session can only watch + a PR it opened itself"); channels bind a session to an external thread + (Slack/GitHub, Labs preview). +- **Org** is the ownership boundary for agents, sessions, sandboxes, and + credentials. +- **Task** is not a platform noun (only Flue's in-process `session.task()`). + **Workflow** is not an API noun. **Workspace** is just the + `/workspace/sources/` directory convention inside the hands sandbox. + **Tools** are functions exposed to the runtime over MCP during active turns + (`bash`, `read`, `write`, `say`, `ask`, `github_publish_pull_request`, ...). + +## Lifecycle + +- **Agent:** no state machine, it is a definition, not a process. Create + (`POST /agents`, idempotent by name), update (`PATCH`), deploy revisions, + activate/rollback revisions. No delete endpoint found in the SDK. +- **Session:** explicit status enum (`types.ts:4`): `queued`, `running`, + `awaiting_input`, `idle`, `failed`, `archived`; yield reasons `completed | + needs_input | error | budget_exceeded | deadline_exceeded | max_turns | + canceled`. +- **Hibernate/resume:** "With nothing left to do, the session goes **idle** + and the sandbox **hibernates**." A steer message "wakes it; it resumes with + its prior context." (agent-sessions/sessions, messaging) +- **Stop:** `cancel` is a cooperative stop; "**`archive` is not delete**, the + session's log is retained, just read-only." (agent-sessions/sessions) +- **Who owns the loop: the platform.** "A **runtime** executes the agent loop: + provider SDK, model calls, and tool use." (agent-sessions/runtimes) The host + execs the runtime adapter once per turn; no customer process is needed + between turns. Custom runtimes invert this partially, "run your own agent + harness on Durable Agent Sessions" by implementing a `POST /turn` HTTP + handler the adapter calls, but scheduling, durability, and the event log + stay platform-owned. +- **Persistence:** the brain sandbox is resident, "Survives across turns and + hibernate/wake (same pid), so SDK boot is paid once per box, not per turn" + (`oc-runtimes/README.md`); the event log is permanent; runtime state + checkpoints under a state dir ("Work since the last logged event may + repeat" on crash recovery). Across sessions, nothing persists: no + cross-session memory by design. + +## What makes it "an agent" here (our inference) + +Our inference: in OpenComputer, "agent" is a **named, versioned configuration +record**, prompt + model + runtime + credential + limits + skills, that the +platform can instantiate into any number of durable sessions. The agentic +behavior (the loop, tools, sandboxes, persistence) belongs to the +session/runtime pair, not to the agent object; what separates an agent from a +plain LLM call is that the platform runs a managed, tool-using, resumable loop +against that configuration, with the session as the unit of execution and the +agent as the unit of identity, versioning, and reuse. + +## Open questions + +- Agent deletion: no delete endpoint found in docs or the TypeScript SDK; is + the agent lifecycle create/update-only? +- Flue subagent semantics (inheritance, limits, event-log fidelity) are + explicitly "outside the tested profile", undefined rather than documented. +- Whether the sandbox-embedded Python-SDK agent (`/sandboxes/:id/agent`) is + deprecated in favor of v3 Durable Agent Sessions, or a supported parallel + API. +- Cardinality of Slack channel binding is stated as "1 app ⟷ 1 agent ⟷ 1 + workspace" in code comments while sessions support multiple channel + bindings; the exact constraint model is unclear (Labs preview). diff --git a/docs/research/agent-platform/products/vercel.md b/docs/research/agent-platform/products/vercel.md new file mode 100644 index 000000000..d21cba553 --- /dev/null +++ b/docs/research/agent-platform/products/vercel.md @@ -0,0 +1,166 @@ +# Vercel (AI SDK + platform): what "agent" means + +Part of Agent Definition Research. +Produced by running [RESEARCH_PROMPT](../RESEARCH_PROMPT.md). +Evidence from ai-sdk.dev, vercel.com docs/blog, and the `vercel/ai` source. +Vercel has *three* agent stories: the AI SDK code object, the platform +primitives (Workflows/Sandbox/Gateway), and **eve**, a filesystem-first +agent framework ("Like Next.js for web apps, but for agents"). + +## The `agent` noun (primary-source quotes) + +- Canonical AI SDK definition: "Agents are **large language models (LLMs)** + that use **tools** in a **loop** to accomplish tasks." The loop's two jobs: + "Context management: maintaining conversation history and deciding what + the model sees at each step" and "Stopping conditions: determining when + the loop (task) is complete." (ai-sdk.dev/docs/agents/overview) +- In AI SDK 6, "**`Agent` is an interface rather than a class**" + (`version: 'agent-v1'`, optional `id`, `tools`, `generate()`, `stream()`), + anything implementing generate/stream over steps *is* an agent. + `ToolLoopAgent` is the stock implementation: "runs tools in a loop... The + loop continues until: a finish reason other than tool-calls..., a tool + without an execute function..., a tool call needs approval..., or a stop + condition is met (default `isStepCount(20)`)." (`tool-loop-agent.ts`) +- AI SDK 5 was explicit that the class adds nothing: "It doesn't add new + capabilities, everything you can do with `Agent` can be done with + `generateText` or `streamText`. Instead, it allows you to encapsulate your + agent configuration and execution." +- `HarnessAgent` wraps established harnesses ("Claude Code, Codex, or Pi") + behind the same interface: harness-as-agent. +- **eve** re-introduces the file-based definition: "a filesystem-first + framework for durable backend AI agents. You define each agent with files + under an `agent/` directory" (`instructions.md`, `agent.ts`, `tools/*`, + `skills/*`, `subagents/*`, `channels/*`, `connections/*`, `sandbox/*`). +- Separately, **Vercel Agent** (dashboard product) is a Vercel-operated + agent for your projects: agent-as-product, priced per investigation. +- **Conceptual model: agent-as-loop (interface over code)**, with eve + layering agent-as-filesystem-config and Workflows adding durability. + +## Subagents + +- First-class docs page: "A subagent is an agent that a parent agent can + invoke. The parent delegates work via a tool, and the subagent executes + autonomously before returning a result." +- **Isolation:** "Each subagent invocation starts with a fresh context + window"; nothing inherited by default ("you can manually pass conversation + history if needed, though this defeats some isolation advantages"). +- **The distinctive knob is `toModelOutput`:** "Users see full subagent + execution (tool calls, intermediate steps), while the model receives only + a condensed summary. A 100,000-token exploration becomes a focused + summary." The human-view/model-view split is explicit API. +- Constraint: "Subagent tools cannot use approval flows." +- eve's version distinguishes skill vs subagent cleanly: "Unlike a skill, + which adds instructions to the running agent, a subagent runs as a + separate agent with fresh conversation history and state. eve offers two + kinds: the built-in `agent` tool delegates to a copy of the current agent, + and declared subagents live under `agent/subagents/*` with their own + config." + +## Configuration surface (what, where, why) + +- **`ToolLoopAgentSettings`:** `model` (required), `instructions` (the + field is `instructions`, not `system`), `tools` + `toolChoice` + + `activeTools` + `toolOrder`, `stopWhen` (default `isStepCount(20)`), + `output` (structured output), `runtimeContext` and per-tool + `toolsContext`, `toolApproval` / tool-level `needsApproval` + (human-in-the-loop "with a single flag"), `prepareStep`, `prepareCall` + (per-call templates via `callOptionsSchema`), repair/refine hooks, + telemetry, lifecycle callbacks (`onStart`... `onEnd`). +- **Loop-control primitives:** `stopWhen` conditions (`isStepCount`, + `hasToolCall`, `isLoopFinished`, custom predicates over `steps`); the + forced-completion pattern ("Combine `toolChoice: 'required'` with a `done` + tool lacking an execute function"); default 20-step cap as "a safety + measure to prevent runaway loops that could result in excessive API + calls." +- **Context knobs with stated rationale:** "Agents often need server-side + state that should not be placed directly in the prompt, such as tenant + settings, request IDs, feature flags, credentials... Use `runtimeContext` + as the agent's shared runtime state... Use `toolsContext` for per-tool + values such as API keys or scoped permissions; each tool receives only its + own typed context." +- **Where config lives:** pure TypeScript for the SDK; `agent/` directory + files for eve; `wrangler`-less, platform wiring is implicit (AI Gateway + model strings like `'anthropic/claude-sonnet-4.5'` route without SDK + changes). + +## Binding time + +- **Everything binds in code at construction**, with two dynamism layers: + `prepareCall` (once per invocation) and `prepareStep` (before every step, + can swap `model`, `activeTools`, `instructions`, `messages`, contexts, and + even the sandbox; "if you return a `messages` override, those messages + carry forward to later steps"). Per-step rebinding is the SDK's signature + feature: mid-*run* mutation as a designed capability, unique in this + study. +- **No agent versioning** (git is the versioning); `version: 'agent-v1'` is + an interface spec tag. +- **Workflows change the binding story for durability:** "'use workflow' + marks a function as durable... All inputs and outputs are recorded in an + event log. If a deploy or crash happens, the system replays execution + deterministically." Steps ("'use step'") compile into isolated API routes + with retries. **Skew protection pins runs to their deployment:** "Workflows + keep running on the deployment they were created on, so you can deploy new + versions... without affecting existing runs", the code-level equivalent + of OpenComputer's revision pinning. + +## Relationships between nouns + +- **Agent 1:N invocations; invocation 1:N steps** (step = one LLM call + + tool executions); `StopCondition` sees the accumulated `steps`. +- **No session noun in the SDK**: messages are the caller's problem. eve + adds it: "A session is the durable conversation or task started by a + channel or HTTP request. Each user message or external event creates a + turn," with sessions running on Workflows. +- **Platform composition:** Queues ("the lower-level primitive that powers + Vercel Workflows") → Workflow (durable event log) → agent invocation → + Sandbox (Firecracker microVM, snapshot-on-stop, 5-min default timeout, + passed as `experimental_sandbox`) and AI Gateway (model routing/failover). +- **As Managed Agents sandbox provider:** "Anthropic provides the model, + harness, tools, and session state. Self-hosting lets you bring the + execution environment", one Sandbox microVM per session, Workflow as the + durable session record, and OIDC credential brokering ("The environment + key never enters the VM"). +- Marketing stack (vercel.com/agents): AI SDK, AI Gateway, Sandbox, + Workflows, Passport, eve; homepage headline "Agentic Infrastructure: + build agents on infrastructure that thinks like them." + +## Lifecycle + +- **The SDK agent has no lifecycle**: plain object; each + `generate()`/`stream()` call is independent; loop ends on stopWhen / + non-tool finish / unexecutable tool / approval needed. +- **Durability is opt-in by composition:** Workflow runs sleep for free + (`await sleep('7 days')`), pause on hooks (`defineHook` / + `hook.resume(token, data)`), survive deploys; Sandbox snapshots its + filesystem on stop and auto-resumes. +- **Who runs the loop: customer code** on Vercel Functions (Fluid Compute) + or anywhere Node runs. Nothing persists by default; persistence comes from + Workflow event logs, Sandbox snapshots/Drives, or external storage; eve + surfaces "Agent Runs" observability in the dashboard. +- Their production lesson (blog): the sweet spot is "low cognitive load and + high repetition from humans," with human review at critical decision + points. + +## What makes it "an agent" here (our inference) + +Our inference: Vercel defines the agent as **the loop itself**, "an LLM +that uses tools in a loop", and ships it as an interface so that a +config-object (`ToolLoopAgent`), a wrapped harness (`HarnessAgent`), or +anything else can stand behind the same `generate`/`stream` contract. +Identity, sessions, and durability are deliberately *not* the agent's +problem: they come from composing the loop with platform primitives +(Workflows for durable state, Sandbox for execution, Gateway for models), +and eve is Vercel packaging exactly that composition into a filesystem +convention. For a service design, the two standout ideas are per-step +rebinding (`prepareStep`) and the human-view/model-view split +(`toModelOutput`). + +## Open questions + +- eve is early; its session/turn data model and versioning story are thin in + the docs read. +- `experimental_sandbox` threading through prepareStep suggests + sandbox-per-step swapping; semantics undocumented. +- Vercel Agent (the dashboard product) and Passport were only surface-read. +- x402 (agent payments) and BotID surfaced only via search; not yet core to + their agent docs. diff --git a/docs/research/agent-platform/synthesis.md b/docs/research/agent-platform/synthesis.md new file mode 100644 index 000000000..1d109bf63 --- /dev/null +++ b/docs/research/agent-platform/synthesis.md @@ -0,0 +1,259 @@ +# Synthesis: what the industry means by "agent" + +Part of Agent Definition Research. +Seventeen product dossiers, one question: what does the noun "agent" +operationally refer to? Purpose: extract the invariant core our own agent +service must model, and the axes where products deliberately diverge. +This synthesis is frozen as decision-time input: where a conclusion here +differs from an accepted record in the [ADR index](../../adr/index.md), the +ADR is authoritative. + +## Convergence + +**1. The behavioral definition is settled.** Every product that states one +lands on the same sentence: an agent is an LLM using tools in a loop, where +the *model* decides control flow. Vercel says it verbatim ("LLMs that use +tools in a loop"); LangGraph defines agents against workflows ("workflows +have predetermined code paths... agents define their own"); Cloudflare +("autonomously execute tasks by making decisions about tool usage and +process flow"); ADK ("self-contained execution unit designed to act +autonomously"); Claude Code's glossary makes the loop itself the definition +("gather context, take action, verify results, and repeat"). Nobody defines +the agent as the model, and nobody disputes that the loop, tool-use, and +autonomy triad is what separates an agent from an LLM call. + +**2. Every platform decomposes into the same trio.** Wherever "agent" is a +durable thing, three resources appear, always separated: +*definition* (config/identity), *execution* (session/run/thread/task), and +*memory* (attachable, never embedded). +[OpenComputer](./products/opencomputer.md): +agent / session / no-cross-session-memory-by-design. +[Managed +Agents](./products/claude-managed-agents.md): agent / session / memory stores. +[LangGraph](./products/langgraph-platform.md): +assistant / thread+run / store. +[AgentCore](./products/bedrock-agentcore.md): +runtime / session / memory resource. +[Devin](./products/devin.md): org config / +session / knowledge. Even the personal daemons follow it: +[Hermes](./products/hermes-agent.md) and +[OpenClaw](./products/openclaw.md) keep identity +(files), sessions (transcripts), and memory (markdown) as separate +artifacts. **This trio is the invariant core.** + +**3. The definition's content converges.** Wherever the agent is +declarable, the same fields recur: instructions/prompt + model + tools + +limits, with skills, credentials, and delegation roster as the common +extensions. OpenComputer ("name, prompt, model, runtime, skills"), Managed +Agents ("model, system prompt, tools, MCP servers, skills"), Claude Code +frontmatter, OpenAI's constructor, CrewAI's triad + llm + tools, ADK's +LlmAgent, and eve's `agent/` directory: same shape, different serialization. + +**4. Sessions pin; definitions version.** Every managed platform freezes +the definition into the execution at creation time and versions the +definition linearly: OpenComputer ("freezes the agent's active revision... +pinned for its whole life"), Managed Agents (optimistically-locked +`version`, per-session overrides never write back), LangGraph (immutable +assistant versions, full-payload updates), AgentCore (immutable runtime +versions + endpoint pointers, Vertex Agent Engine's revision traffic +splits). Vercel's Workflows reach the same end by pinning runs to +deployments. The sanctioned exceptions are deliberate: credential rotation +flows into live sessions (OpenComputer), and +[Claude +Code](./products/claude-code-agent-sdk.md) binds per *invocation* (live file reload) rather than per session, +so the file-based products trade pinning for git. + +**5. Delegation has one output contract.** However subagents are spawned, +the parent receives only the child's final result, never its intermediate +reasoning. Claude Code ("final message verbatim as the tool result"), +Hermes ("the parent's context only sees the delegation call and the summary +result"), Vercel (`toModelOutput` condenses "a 100,000-token exploration"), +OpenAI's agent-as-tool, AgentTool in ADK, and A2A's opaque-execution +principle ("without needing to share their internal thoughts, plans, or +tool implementations"). Fresh child context is likewise near-universal, and +CrewAI's delegation prompt states the reason plainly: "they know nothing +about the task, so share absolutely everything you know." + +**6. The `description` field is the routing protocol.** LLM-driven +delegation is steered by a natural-language description everywhere it +exists: Claude Code ("Claude uses each subagent's description to decide +when to delegate"), ADK ("primarily used by *other* LLM agents to determine +if they should route a task to this agent"), CrewAI's coworker tool text, +A2A's AgentSkill descriptions. An agent's description is not documentation; +it is its API. + +**7. Tool restriction is the safety primitive.** Constraining which tools +an agent (especially a child) may touch is the first control every product +reaches for: Claude Code `tools`/`disallowedTools`, Hermes leaf-vs- +orchestrator roles, Managed Agents permission policies (MCP defaults +`always_ask`), and OpenClaw's inherited allow/deny lists. Sandboxes come +second; tool scoping comes first. + +## Divergence + +The axes where products make opposite choices (i.e., the decisions our +service must make deliberately): + +**A. What the noun points at.** A spectrum from record to being: +server-side config resource (OpenComputer, Managed Agents, LangGraph's +assistant) → file on disk (Claude Code, eve, OpenClaw persona dirs) → +in-memory code object (OpenAI, CrewAI, ADK, Vercel SDK, Jido's +struct) → running stateful actor (Cloudflare's durable object, Jido's +AgentServer) → network endpoint (A2A) → the product itself (Devin) → an +accumulating learned identity (Hermes). Two instructive extremes: +[Cloudflare](./products/cloudflare-agents.md) +*fuses* identity, state, and process into one object (no definition/ +instance split at all), while +[OpenAI](./products/openai-agents-sdk.md) +went the other way historically: it *had* the versioned server-side agent +resource (Assistants), deprecated it, and decomposed it into config +(Prompts) + state (Conversations) + loop (SDK). + +**B. Who owns the loop.** Three positions: platform-managed loop +(OpenComputer's runtimes, Managed Agents, Devin, and the AgentCore harness +where "Who owns the loop: AWS"), customer loop behind an infrastructure +contract (AgentCore Runtime, "the orchestration loop is yours"; +OpenComputer custom runtimes' `POST /turn`), and library-in-your-process +(OpenAI, CrewAI, ADK, Jido, Vercel SDK, Claude Code SDK). The AgentCore +family spans two of these on one substrate: the Runtime path is a customer +loop, while the harness is an AWS-owned loop over the same microVM, version, +and endpoint machinery. The meeting +point is the **turn contract**: OpenComputer's `POST /turn`, AgentCore's +`POST /invocations` + `GET /ping`, LangGraph's graph-nodes-as-code. The +industry has effectively standardized the *shell* (identity, sessions, +durability, isolation) without standardizing the *brain*. + +**C. Binding time.** Freeze-at-session (all managed platforms), +live-reload per invocation (Claude Code), everything-at-runtime +(Cloudflare), per-*step* rebinding as a designed feature (Vercel's +`prepareStep` can swap model/tools/instructions mid-loop), kickoff-time +string interpolation (CrewAI). Hermes adds a constraint nobody else +surfaces: prompt-cache economics as the reason mid-run mutation must be +rare ("per-conversation prompt caching is sacred"). + +**D. Subagents, the least settled axis.** Declared roster with depth-1 +cap (Managed Agents: 20 agents/25 threads; Hermes default; OpenClaw default, +with hierarchy frameworks explicitly refused in VISION), +deep nesting (Claude Code: depth 5, background by default), handoffs +that *transfer* the conversation rather than spawn (OpenAI, LangGraph's +Command), peer delegation by role (CrewAI), protocol-level peers with no +hierarchy at all (A2A, where the *task* is the delegated unit), dynamic +process spawning with orphan policies (Jido, the most nuanced lifetime +coupling: logical parent refs decoupled from supervision, `on_parent_death` +per child). Overlaid on all of it, Cognition's production verdict +([Devin](./products/devin.md)): parallel agents +work when they "contribute intelligence rather than actions": **fan out +reads, single-thread writes**, and fresh-context verifiers *beat* +shared-context ones for review. + +**E. Session semantics.** Session-as-task-run (OpenComputer, Managed +Agents, Devin, LangGraph runs) vs session-as-conversation-lane (OpenClaw's +routing-scoped lanes, Hermes' session keys, Cloudflare instances that may +*be* a room). Who names it also splits: platform-minted IDs vs +caller-supplied keys (AgentCore's client-named `runtimeSessionId`, +OpenComputer's get-or-create `key`, OpenClaw's deterministic routing keys). + +**F. Identity scope.** Everyone has intra-org identity; only +[A2A](./products/adk-a2a.md) defines cross-org +identity: the AgentCard (name, skills, interfaces, security schemes, JWS +signatures, well-known URI). It is the only serious interoperable +definition, and Vertex + LangGraph + AgentCore + CrewAI all already carry +A2A hooks. + +## Conceptual models in play + +| Model | Exemplars | +| --- | --- | +| agent-as-config (versioned record) | OpenComputer, Managed Agents, LangGraph assistant, AgentCore harness | +| agent-as-file (git-versioned persona) | Claude Code, Vercel eve, OpenClaw workspace | +| agent-as-code-object | OpenAI Agents SDK, CrewAI, ADK, Vercel AI SDK, Jido struct | +| agent-as-process/actor | Jido AgentServer, Cloudflare durable object | +| agent-as-deployed-service | Bedrock AgentCore, Vertex Agent Engine | +| agent-as-network-endpoint | A2A AgentCard | +| agent-as-role/persona | CrewAI (role/goal/backstory), OpenClaw SOUL.md | +| agent-as-teammate/product | Devin | +| agent-as-learning-identity | Hermes (memory + self-authored skills as the definition) | +| agent-as-interface (anything that runs the loop) | Vercel AI SDK 6, Claude Code harness framing | + +These are not mutually exclusive; most products stack two or three. + +## Comparison table + +| Product | The `agent` noun is a... | Subagents | Config binding time | Lifecycle owner | Agent : session | +| --- | --- | --- | --- | --- | --- | +| OpenComputer | versioned config record | Flue-only, untested profile; fan out via sessions | session freezes active revision | platform (custom runtime = BYO turn) | 1:N, session pins revision | +| Claude Managed Agents | versioned identity+config resource | declared roster, depth 1, max 20/25 threads | session pins version; tools/MCP mutable when idle | platform (self-hosted = tool exec only) | 1:N, session = running instance | +| Jido | immutable struct + module (process separate) | dynamic spawn directive, logical refs, orphan policy | compile-time macro; state via cmd/2 | customer BEAM app | no session noun | +| Claude Code / Agent SDK | markdown file → fresh context window | richest: 5 scopes, depth 5, fork, background | live reload per invocation; no versions | customer process (harness/SDK) | subagent ⊂ session | +| OpenAI Agents SDK | code object (dataclass) | handoffs (transfer) + agent-as-tool (return) | construction + per-run overrides; no versions | customer process | sessions independent of agents | +| Bedrock AgentCore | ARN'd deployed container service | none, inside the container; A2A between peers | immutable versions + endpoint pointers | customer loop, AWS shell | 1:N, session = microVM | +| LangGraph Platform | assistant = versioned config over a graph | subgraphs + Command handoffs, declared in code | deploy (graph) / versioned (assistant) / per-run | platform executes customer graph | assistant N:M threads | +| Google ADK + A2A | code object in a tree / endpoint + card | first-class sub_agents tree; A2A = opaque peers | construction / discovery-time (card) | customer (ADK) / managed (Vertex) | runner binds agent↔sessions; task = unit of work | +| Cloudflare Agents | durable object (identity+state+process fused) | child DOs, RPC, kill vs delete distinct | everything at runtime; deploys restart | customer handlers, platform persistence | instance may *be* the session | +| CrewAI | role persona (pydantic) | peer delegation + manager hierarchy | kickoff interpolation; no versions | customer process (in-lib loop) | run = kickoff; no session | +| Devin | the product itself (teammate) | managed Devins, coordinator + children, ACU caps | session creation; config = org onboarding | Cognition entirely | 1 agent : N sessions (VM each) | +| Vercel (SDK + platform) | interface over the loop; eve = file dir | subagent-as-tool, toModelOutput condensing | construction + per-step rebinding; Workflow pins deploys | customer code; platform durability opt-in | no session (SDK); eve adds one | +| Hermes Agent | learning identity (files) + per-session process | delegate_task, leaf/orchestrator, depth 1, cost-capped | session assembly; cache-preservation constrains mid-run | user-run daemon | 1 identity : N session lanes | +| OpenClaw | resident persona (workspace dir) | sessions_spawn, depth 1, agent-to-agent allowlisted | hot reload; per-message routing | user-run gateway daemon | 1:N routing-scoped lanes | +| Netclaw (added post-synthesis) | daemon with file-shaped soul; event-sourced actor sessions | spawn_agent → ephemeral child actors, depth 1 (recursive spawn denied), fail-closed audience inheritance | daemon-start validation; validate-before-restart reload with session drain; identity re-read per session actor | user-run daemon (systemd) | 1:N channel+thread-keyed persistent actors | +| kagent (added post-synthesis) | namespaced K8s custom resource reconciled into an A2A service | agent-as-tool by CRD reference; DAG capped at depth 10; fresh child session, identity-only inheritance | reconcile-time resolution into a config Secret; rebinding = pod roll | platform deploys; in-pod ADK runtime owns the loop | 1:N DB sessions; delegation mints child sessions | +| AgentCore harness (added post-synthesis) | versioned config record over an AWS-owned loop | no subagent noun; agent-as-tool via Gateway; compose above via Step Functions | auto-versioned config; per-call overrides | AWS owns the loop (managed harness on managed Runtime) | 1:N, session = microVM | + +## Working definition + +Derived from the evidence, for our service: + +> **An agent is a named, versioned declaration of persona and capability +> (instructions, model, tools, limits, credentials, and a delegation +> roster) that a runtime instantiates into pinned, resumable executions +> (sessions), with memory and environment attached as separate resources.** +> The agent-ness (loop, autonomy) is a property of the runtime executing +> the declaration, not of the record itself. + +That sentence is the research starting point, not the final ownership map: +the accepted data-ownership decision keeps behavior and dependency +declarations in the revision while assigning limits, credential bindings, +work contracts, resolved session context, and observations to their owning +resources. + +Design decisions the evidence forces, with the industry's answer where one +exists: + +1. **Model the trio as three first-class resources**: AgentDefinition + (versioned), Session (pins a definition version at create), Memory + (attachable N:M). Do not embed memory or environment in the definition; + nobody who scaled did. +2. **Version linearly and immutably; sessions freeze.** Allow per-session + overrides that never write back (Managed Agents), staging/rollback + (OpenComputer, LangGraph), and exactly one live-mutation exception: + credential rotation. +3. **Own the shell, open the brain.** Offer a managed loop *and* a + bring-your-own-loop turn contract (`POST /turn`-shaped): the point + where OpenComputer, AgentCore, and Managed Agents self-hosted all + converged independently. +4. **Subagents: declared roster, depth 1 by default, results-only return, + restricted-tool inheritance.** Depth and fan-out are cost controls + (Hermes) as much as safety ones. Enforce Cognition's rule structurally + if possible: parallel children for reads/analysis; single writer. +5. **Make `description` a first-class, prompt-visible field**: it is the + delegation routing contract, not metadata. +6. **Name sessions with caller-supplied idempotency keys** (get-or-create), + the pattern AgentCore, OpenComputer, and OpenClaw share: it makes the + session a routing lane, not just a run. +7. **Export identity as an A2A AgentCard.** It is the only cross-vendor + agent identity that exists; LangGraph, Vertex, and AgentCore already + emit or embed it. +8. **State the trust boundary explicitly** (OpenClaw's lesson): one + definition namespace per trusted operator/org; adversarial isolation + lives at the process/sandbox boundary, not inside the agent model. +9. **Treat prompt-cache economics as a design input** (Hermes's lesson): + whatever binds mid-run must not invalidate the cached prefix; prefer + session-splitting (compression → new pinned session) over in-place + mutation. + +The one-line reading of the whole study: the industry agrees on the +*sentence* (an LLM using tools in a loop) and on the *trio* +(definition / execution / memory); everything else (where the definition +lives, who runs the loop, how deep delegation goes) is a product decision, +and the most successful designs are the ones that made those decisions +explicit rather than inheriting them.