diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f0db136..f43035d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,6 +18,16 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + - name: Check required RFC draft shell: bash run: | @@ -26,6 +36,13 @@ jobs: test -s drafts/agent-surface.md grep -q '^# Agent Surface Protocol Specification' drafts/agent-surface.md + - name: Check RFC review dashboard + shell: bash + run: | + set -euo pipefail + python -m pip install --requirement review/requirements.txt + make review-check + - name: Check Markdown hygiene shell: bash run: | diff --git a/.gitignore b/.gitignore index 31bfd70..e8bdab5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target/ /generated/ .DS_Store +review/__pycache__/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8be7b9a --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +PYTHON ?= python3 + +.PHONY: review-build review-check + +review-build: + $(PYTHON) review/build_review.py + +review-check: + $(PYTHON) review/build_review.py --check + node review/check_review.mjs diff --git a/README.md b/README.md index 228874d..2436aa4 100644 --- a/README.md +++ b/README.md @@ -61,13 +61,33 @@ Agent Surface + Agent Grant bind those pieces into safe app-specific delegation. agent-surface/ .github/ PULL_REQUEST_TEMPLATE.md - workflows/docs.yml Markdown and RFC checks. + workflows/docs.yml Markdown, RFC, and dashboard checks. drafts/ Source RFCs written in Markdown. + review/ Source data, template, and generated RFC review dashboard. LICENSE MIT license for repository source code. LICENSE-CC-BY-4.0 CC BY 4.0 summary for specifications and documents. CONTRIBUTING.md Contribution guidelines. ``` +## Interactive RFC Review Dashboard + +The standalone [RFC review dashboard](review/agent-surface-rfc-review.html) is +generated from the RFC, card data, and UI template. Do not edit the generated +HTML directly. + +```sh +python3 -m pip install -r review/requirements.txt +make review-build +make review-check +``` + +When the RFC changes, update the relevant cards in +[`review/review-data.json`](review/review-data.json), rebuild the dashboard, +and commit the RFC, review data, and generated HTML together. `review-check` +validates card fields and priorities, verifies every linked heading still +exists in the RFC, checks that the generated artifact is current, and parses +the dashboard's inline JavaScript. + ## Status The specification is experimental and subject to change. The current draft is diff --git a/review/agent-surface-rfc-review.html b/review/agent-surface-rfc-review.html index 0f83831..fd0aeaf 100644 --- a/review/agent-surface-rfc-review.html +++ b/review/agent-surface-rfc-review.html @@ -210,6 +210,7 @@
Unless otherwise stated, the following sections are normative:
The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" in this -document are to be interpreted in the RFC 2119 and RFC 8174 sense when, and only -when, they appear in all capitals.
+The key words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", and +"MAY" in this document are to be interpreted in the RFC 2119 and RFC 8174 sense +when, and only when, they appear in all capitals.
This is not an IETF document. The keywords are used to make interoperability and security expectations explicit for future implementers.
If the manifest contains sensitive tenant-specific affordances, it MUST require ordinary authenticated app access.
-The manifest SHOULD be served with:
+The manifest MUST be served over HTTPS and SHOULD be served with:
Content-Type: application/json
Cache-Control: max-age=300
@@ -932,6 +935,7 @@ output_schemaside_effectexecutioncapability_hintidempotency for side-effecting actionsreceipt for side-effecting actionsA proposal-only action is a typed action whose output is a draft, suggestion, patch, review body, or other non-committed artifact.
+A proposal-only action declares side_effect: false because it does not
+commit domain-visible changes. However, when the application persists
+proposals as drafts — as the proposal flow in this draft assumes — repeated
+proposal requests can still accumulate duplicate drafts under retries and
+agent loops. Applications that persist proposals SHOULD accept idempotency
+keys for proposal actions and deduplicate stored drafts accordingly.
Example:
{
"id": "pull_request.review.propose",
@@ -993,6 +1003,11 @@ Events
"schema": "https://example.com/schemas/ci-failed.event.schema.json"
}
+Grant constraints filter events the same way they filter actions: a grant +constrained to one repository SHOULD NOT receive events about other +repositories, even when the event scope matches.
+Event delivery semantics — transport, ordering, acknowledgement, and replay — +are not defined in this draft; see Open Questions.
Every action SHOULD have a standard risk label. Runtimes can map risk labels to local policy defaults.
@@ -1006,6 +1021,15 @@financial_side_effect | Charges, refunds, purchases, invoices, payroll. | Always require explicit approval. |
| destructive | Deletes, closes, revokes, disables, or irreversibly changes state. | Deny by default or require step-up approval. |
| privileged | Changes permissions, secrets, tokens, admin settings, or access policy. | Deny by default. |
+Risk labels are ordered by increasing severity from read to privileged.
+The labels are not mutually exclusive properties of an action: a single action
+can plausibly be described by several of them. When more than one label
+applies, the action MUST carry the most severe applicable label. For example,
+invoice.refund.request is both a mutation and a financial operation; it MUST
+be labeled financial_side_effect, not write.
The risk label and the side_effect flag MUST be consistent: an action
+labeled write or a more severe label MUST declare side_effect: true, and
+an action labeled read MUST declare side_effect: false.
Applications MAY define extension risk labels, but they SHOULD map them to the standard labels for runtime interoperability.
user_or_app | Either a runtime approval or app-side approval MAY satisfy the requirement, depending on grant caveats. |
| runtime_and_app | Both runtime-side and app-side approval are required. |
Approval records SHOULD be linked into receipts.
+The runtime and user_or_app modes allow a runtime-side approval to satisfy
+the requirement. In those modes the application is accepting the runtime's
+assertion that a local user approval occurred. To keep this compatible with
+the rule that an application MUST NOT accept a runtime's self-assertion of
+authority, that acceptance MUST be an explicit grant caveat presented to the
+user at consent time, not a silent default. Action requests that rely on a
+runtime-side approval SHOULD carry an approval reference (for example an
+approval_ref identifier, or in future profiles a signed approval object) so
+the approval can be linked into receipts and audited. Applications that do not
+want to accept runtime approval assertions MUST declare app or
+runtime_and_app for the affected actions.
Every side-effecting action MUST support idempotency.
Action requests SHOULD include:
@@ -1030,6 +1065,13 @@If the same key is reused with different normalized input, the application SHOULD return an idempotency conflict error.
+Idempotency keys are scoped to the grant and action: the application MUST
+treat a request as a duplicate only when the same key is presented under the
+same grant_id and action_id. On a duplicate request, the application
+SHOULD return the original result and receipt reference rather than an error,
+so a retrying runtime can converge on the outcome of the first attempt.
+Applications SHOULD retain idempotency state at least for the remaining
+lifetime of the grant and SHOULD document their retention window.
Applications SHOULD define the input normalization procedure per action, or use the declared input schema with a canonical JSON profile. A future draft is expected to define canonicalization requirements for signed receipts and signed @@ -1052,7 +1094,7 @@
Numeric caveats need defined accounting. In this draft, max_actions counts
+side-effecting action requests accepted by the application under the grant.
+Reads and denied requests do not consume the budget, and neither do idempotent
+replays: a retry deduplicated under a previously accepted idempotency key
+MUST NOT consume the budget again, or lost responses and transport retries
+could exhaust a grant without producing new side effects. max_cost_usd is
+advisory in the MVP profile: the runtime SHOULD meter agent-side cost against
+it, and applications MAY additionally meter app-side cost where actions carry
+a price. When a budget caveat is exhausted, further matching requests MUST be
+rejected with limit_exceeded.
discover surface
-> verify manifest
@@ -1097,6 +1149,11 @@ M
This is the RECOMMENDED MVP model because it fits existing OAuth/resource-server deployments.
+Because this draft does not require browser-to-localhost communication, the +consent flow SHOULD support a completion mode that does not depend on a +loopback redirect — for example an OAuth device-authorization-style exchange +or an app-mediated pairing code that the runtime polls or receives over its +outbound channel.
Pros:
The action request MUST be authorized by the HTTP authorization layer or an
equivalent proof. The grant_id inside the body is a correlation identifier, not
a credential.
The application MUST also verify that the supplied session_id belongs to the
+presented grant. Otherwise a valid grant credential could be replayed against
+sessions created under other grants, corrupting session accounting and receipt
+linkage.
If both the Idempotency-Key header and the body idempotency_key field are
+present, they MUST match, and the application MUST reject a mismatch as
+schema_invalid. Accepting a mismatched request and picking either value
+would let app-side deduplication and runtime receipts refer to different
+idempotency identifiers.
Example:
POST /agent-actions HTTP/1.1
Host: example.com
@@ -1366,7 +1432,7 @@ App Receipt
"session_id": "sess_456",
"action_id": "comment.create",
"app_id": "code.example.com",
- "surface_version": "code-review-agent-surface/0.1",
+ "surface_version": "2026-06-25",
"runtime": {
"runtime_id": "application_runtime_456"
},
@@ -1397,8 +1463,16 @@ App Receipt
Receipts MAY be signed by the app, runtime, or both. A future draft is expected to define canonicalization and signature profiles.
+How the application obtains the runtime_receipt_hash value for the links
+field — for example, in the action request itself or through a later
+submission to the receipt endpoint — is not defined in this draft; see Open
+Questions.
The protocol MUST define what happens when authority changes.
+Revocation MUST be possible from both sides. Applications SHOULD give users an +in-app view of active agent grants — comparable to OAuth application +management pages — where a grant can be inspected and revoked without going +through the runtime.
If a grant is revoked:
grant.revoked as a final event before the
+app closes the subscription; delivery of this one event MUST NOT itself
+require an active grant, or the runtime could never learn about the
+revocation through the event channelIf the runtime disconnects:
@@ -1451,7 +1529,14 @@passport_invalid | Agent Passport is missing, expired, revoked, or invalid. |
| runtime_untrusted | Runtime binding or attestation is not accepted. |
| surface_incompatible | Runtime does not support the surface version. |
-| proposal_required | The app only supports proposal mode for this action or grant. |
+| proposal_required | The app only supports proposal mode for this action or grant. |
+| session_invalid | Session is unknown, ended, or not associated with the presented grant. |
+| action_unknown | Action id is not part of the surface version the grant was issued against. |
+| limit_exceeded | A grant budget caveat such as max_actions or max_cost_usd is exhausted. |
+| rate_limited | The request was throttled independently of grant caveats. |
+Errors SHOULD be returned in a structured envelope containing at least the +error code, a human-readable description, and a retryability indication. +Mapping error codes to HTTP status codes is left to a future draft.
Errors SHOULD be safe to show to users and precise enough for runtime policy debugging.
The Terminology section allows a runtime to be embedded in an application. +That deployment collapses the two trust domains this protocol otherwise +separates: the component that is supposed to protect the user is operated by +the party the user is being protected from. An app-embedded runtime can +satisfy the wire protocol while voiding the "runtime protects the user" +guarantee — its policy checks, approvals, and runtime receipts are all +app-controlled.
+When the runtime is app-operated, the user's protection reduces to app-side +consent and app receipts. Runtimes SHOULD disclose their operator during +consent, and enterprise policy MAY require user-controlled or third-party +runtimes for high-risk scopes.
Agents can hallucinate, loop, ignore instructions, leak data, or attempt unauthorized actions.
@@ -1566,6 +1667,11 @@Runtime and app policies SHOULD treat model output as untrusted until validated.
+Session task descriptions, resource payloads, and event payloads are +app-authored input to the agent and can carry injected instructions. The +runtime SHOULD present the session task to the user at session start or +consent time, and MUST NOT allow app-delivered content to widen grant scope, +weaken approval requirements, or alter local policy.
Idempotency keys, timestamps, nonce binding, and grant expiration reduce replay risk. Side-effecting actions MUST be idempotent.
@@ -1709,6 +1815,15 @@max_cost_usd metered when runtime-side inference cost and app-side
+action cost diverge, and which side is authoritative?approval_ref identifier — signed approval objects, step-up verification,
+or app-rendered approval UI?