Appearance
Implementation brief for the coding agent
Status: Delivered — these were the build instructions for v0.1, and v0.1 is implemented against them (all acceptance criteria green). Kept as the record of what was specified; the decision records carry the as-built addenda. If this brief and another doc disagree, the execution pipeline and reference win, and the disagreement is a bug to report. Begin coding from this document without reinterpreting the project.
Project objective
Build oRPC Agent v0.1: a TypeScript monorepo of five packages that turns existing oRPC procedures into governed capabilities invocable by AI agents and other machine clients through a single runtime — with explicit per-surface exposure, deterministic policies, input-bound human approvals, normalized model-safe errors, structured audit events, and OpenTelemetry-compatible tracing — plus a customer-support reference application and a deterministic testing package, exactly as specified by the documentation in this repository.
First release scope
Core (@orpc-agent/core): AgentMeta validation; agentProcedure; dot-path registry with filter/inspect; runtime with the 15-stage pipeline; policies (4 decisions, 3 phases, all-evaluate, deny-precedence, fail-closed); approvals (in-memory coordinator, inline handler, hash-bound single-use resume); CapabilityError with the 21-code table; audit events (11 types, strict mode); neutral tracing interface; timeout/cancellation via composite AbortSignal; eligibility-gated retries; schema subpath with Zod v4 JSON-Schema conversion + converter registry.
AI SDK adapter: per-request tool sets from describe, raw-arg forwarding, envelope results, approval signaling, abort propagation, name mapping.
MCP adapter: per-session identity via createContext, tools/list + tools/call, same envelope + isError, cancellation.
OpenTelemetry adapter: TracingAdapter implementation, 4 span names, safe attributes.
Testing package: test runtime wrapping the real one, fakeActor/testClock/approvalProbe/capturedAudit, overrides, adapter conformance factory.
Reference example: customer-support app with the 9 capabilities, 3 policies, approval dashboard flow, chat + MCP endpoints, full governance test suite.
Package order
M1 core-model → M2 core-runtime → M3 core-policies → M4 core-approvals → M5 testing → {M6 ai-sdk ∥ M7 opentelemetry ∥ M8 mcp} → M9 example+docs-sync. Details, per-increment tests, and acceptance: milestones.md.
Required public APIs
Implement exactly these exports (contracts in reference/core, reference/runtime, reference/errors, reference/events, reference/configuration):
- core:
agentProcedure,createCapabilityRegistry,createAgentRuntime,definePolicy,composePolicies,allow,deny,hide,requireApproval,unwrap,createInMemoryApprovalCoordinator,CapabilityError; typesAgentMeta,AgentCapability,CapabilityRegistry,AgentRuntime,Actor,AgentInvocationInfo,ExecutionRequest,ExecutionResult,ExecutionOptions,CapabilityDescriptor,AgentPolicy,PolicyDecision,PolicyPhase,PolicyRequest,ApprovalRequest,ApprovalRecord,ApprovalDecision,ApprovalCoordinator,AgentAuditEvent,AuditSink,TracingAdapter,SpanHandle,ExposureSurface,SideEffect,RiskLevel,FailureStage,ErrorCode - core/schema subpath:
toJsonSchema,registerSchemaConverter - ai-sdk:
toAISDKTools,AISDKToolsOptions,AISDKToolResult - mcp:
createMCPServer,MCPServerOptions - opentelemetry:
createOpenTelemetryTracing - testing:
createAgentTestRuntime,fakeActor,testClock,approvalProbe,capturedAudit
Additions require doc PRs; renames require ADRs. No other public surface.
Runtime lifecycle (canonical)
Implement execution-pipeline.md verbatim:
- Adapter authenticates, builds actor+context, calls
invokewith its hardcoded surface. - Assign
executionId; open spanagent.capability_call; emitcapability.requested. - Resolve id — miss ⇒
CAPABILITY_NOT_FOUND. - Exposure check — fail ⇒ concealed
CAPABILITY_NOT_FOUND, audited truthfully. - Validate input (Standard Schema) — fail ⇒
INPUT_INVALIDwith issue summary. - Actor presence/shape — fail ⇒
UNAUTHENTICATED. - Invocation-phase policies: runtime-level then capability-level, ALL evaluated, precedence deny > hide > require-approval > allow, throw/timeout ⇒
POLICY_FAILEDdeny. - Approval gate: create hashed request, emit, return
approval-required(or await inline handler). Resume re-enters here: approved ∧ unexpired ∧ unconsumed ∧ hash-match ∧ approver≠requester, then atomicmarkConsumed, new executionId, original actor. - Execution-phase policies (opted-in only).
- Compose AbortSignal (caller + timeout); emit
capability.started(awaited iffstrict). - Invoke procedure via oRPC server-side call with
{...context, agent}and signal; middleware runs here; retry loop wraps this stage only. - Validate output — fail ⇒
OUTPUT_INVALID, withhold output. - Apply
redact.output. - Emit
capability.completed; close span. - Return envelope. All failures: normalize → emit terminal event → close span error → return envelope. Never throw for governed outcomes.
Discovery: exposure filter → discovery-phase policies → minimal descriptors → capabilities.discovered.
Security invariants
Never violate; each ships with named tests (SI-* tags) that must never be weakened:
SI-1 deny-by-default per surface · SI-2 enforcement at execution time (filtering is UX) · SI-3 model never the actor · SI-4 no self-approval; decisions never from model output · SI-5 approval binds (capability, actor, input-hash), single-use atomic consumption · SI-6 validated input is immutable through execution · SI-7 policy errors fail closed · SI-8 unknown/unexposed/hidden externally indistinguishable · SI-9 internal details never reach models · SI-10 no payloads in audit/traces by default · SI-11 no auto-retry of write/destructive/external absent explicit idempotent+retry · SI-12 signal propagation, exactly one envelope per execution. Full text: security-model.md.
Required test matrix
Minimum behaviors with passing tests before v0.1 (shapes in guides/testing-capabilities and per-milestone lists):
- [ ] Metadata validation: every required-field omission; retry-without-idempotent on writes; toolName collisions; aggregate startup error
- [ ] Registry: id derivation, exclusion + inspect, filter composition
- [ ] Pipeline order: audit event sequence for success / each failure class
- [ ] Every
ErrorCodereachable via a test; envelope-never-throws property - [ ] Concealment: unknown vs unexposed vs hidden — identical external shape, distinct audit reasons
- [ ] Validation: raw-in/validated-through (SI-6); issue summary exposure; output-invalid withholds +
executedBeforeFailure - [ ] Policies: precedence table; all-evaluate audit completeness; merge of approvals; fail-closed throw/timeout; phases incl. discovery annotation; hide-at-invocation concealment
- [ ] Approvals: full lifecycle; all seven failure codes; hash key-order stability; concurrent-resume atomicity; execution-phase re-check; original-actor + approver in ctx; inline mode; self-approval both settings
- [ ] Bounds: timeout vs cancel distinction; signal reaches handler; late completion not consumed
- [ ] Retries: eligibility matrix; same-executionId +
capability.retried; model re-invocation distinct - [ ] Redaction: output redaction before adapter/trace; approvalInput display; trace attributes payload-free
- [ ] Audit: strict-mode abort (
AUDIT_UNAVAILABLE); sink isolation; onSinkError - [ ] ctx.agent: injection, absence on non-agent calls, idempotencyKey stability across retries
- [ ] Adapters (each): conformance factory — listing correctness, raw forwarding, 4-status translation, concealed serialization, naming bijection, cancellation
- [ ] Example suite: exposure snapshots; refund 400/649/9000 table; E2E approval flow audit sequence; MCP concealment; timeout-not-retried write
Deferred features (do not build)
Durable execution engine or any scheduler; persistent approval/audit store packages (interfaces + in-memory only); workflow/A2A/CLI adapters; MCP list_changed, elicitation approvals, resources/prompts; streaming (event-iterator) capabilities — registry must reject them with a clear error; rate limiting/quotas; policy input rewriting; session/multi-use approvals; exactly-once semantics; replay; run-explorer UI; long-term memory; multi-agent orchestration; metrics emission. See ROADMAP and open-questions.
Acceptance criteria
v0.1 is complete when, observably:
pnpm install && pnpm build && pnpm testpasses on a clean checkout; CI enforces package-boundary rules (core imports no adapter/provider/protocol SDK; adapters import no adapter).- The full test matrix above is green, with SI-tagged tests present for all twelve invariants.
- The reference example runs its scripted (mock-model) chat flow: read → eligibility → refund(649) → approval-required → decide-as-manager → resume → completed → draft → send-with-inline-confirmation, producing exactly the audit sequence in the example doc.
- The same example refuses: refund 9000 (
POLICY_DENIED), refund over MCP (CAPABILITY_NOT_FOUND), self-approval (APPROVAL_SELF_APPROVAL), double resume (APPROVAL_CONSUMED), post-expiry resume (APPROVAL_EXPIRED). describe("mcp")anddescribe("aiSdk")listings in the example match the exposure table in the example doc, verified by snapshot.- OpenTelemetry in-memory exporter shows the documented span tree with no payload attributes.
- Public exports of every package equal the Required-public-APIs list (api-report diff clean).
- Docs updated where implementation forced divergence, each with an ADR addendum; no silent drift (a final grep-based consistency check of symbol names, error codes, and event names across docs passes).