diff --git a/docs/vrs/01-ding/requirements.md b/docs/vrs/01-ding/requirements.md index 130f72fa..3fe9a55d 100644 --- a/docs/vrs/01-ding/requirements.md +++ b/docs/vrs/01-ding/requirements.md @@ -20,10 +20,10 @@ is in [`spec.md`](./spec.md). ## Assumptions -- **DING-A01 Rendered screens only:** The only available evidence about a - composer's state is a rendered terminal screen. No maintained harness exposes - an evented idle signal, so every precondition below is a measured heuristic - over text. `DQ2` in [`../spec.md`](../spec.md) tracks closing that gap. +`DING-A01 Rendered screens only` is retired. Rendered-screen evidence is a +limit of the legacy `ding` transport. It is not an assumption for a maintained +harness that declares a native `deliver` transport. + - **DING-A02 Cooperative human:** The human sharing a pane is not adversarial. A screen that deliberately imitates another harness's composer is a correctness concern, not a security boundary, consistent with `A02`. @@ -41,21 +41,43 @@ is in [`spec.md`](./spec.md). ## Requirements -### Must preserve initial transport and gate every retry - -- **DING-R01 Combined initial transport:** A fresh notice uses one bounded PTY - transaction containing the bracketed paste, the accepted 0.5 second delay, - and Return. Ownership is recorded before that command starts. Composer - heuristics do not split or suppress this initial transport. -- **DING-R02 Two adjacent retained-safe retry observations:** A later bare - Return is permitted only for a transport-owned payload whose exact notice is - still the complete composer and is classified `RetainedSafe` in two - immediately adjacent inspections. The final observation is adjacent to the - Return itself. Any change, block, or uncertainty prevents retry submission. -- **DING-R03 Fail-closed receipt and retry:** After the initial transport, a - changed composer, a human draft, an active turn, a modal, an unreadable - screen, an unrecognized harness, and a bounded observation timeout never - become `Delivered` and receive no retry input. Anything not positively +### Must select one explicit transport + +- **DING-R11 Declared transport:** An agent selects at most one delivery + transport. `ding` selects the legacy screen transport. `deliver "mcp"` + selects the Claude native transport. `deliver "app-server"` selects the + Codex native transport. A declaration with neither node has no DING delivery. + A declaration with both nodes, multiple `deliver` nodes, or an unsupported + `deliver` value is invalid. +- **DING-R12 No transport inference:** st2 does not infer a native transport + from an agent command or a screen. Native delivery uses the declared adapter. + A binary that supports `deliver` validates its value and its mutual exclusion + with `ding`. +- **DING-R13 Durable native delivery:** The inbox file remains the source of + truth for native delivery. Only the adapter's declared success condition can + complete a delivery attempt. A closed, unavailable, stale, or unknown native + transport leaves the message unread and retryable. Archive precedence and + restart recovery remain unchanged. +- **DING-R14 Missing transport report:** Doctor reports an active agent that + declares neither `ding` nor `deliver`. The omission remains a valid opt-out + and does not block the agent. The report makes a no-delivery state visible. + +### Must preserve legacy transport and gate every legacy retry + +- **DING-R01 Combined initial transport:** A fresh legacy notice uses one + bounded PTY transaction containing the bracketed paste, the accepted 0.5 + second delay, and Return. Ownership is recorded before that command starts. + Composer heuristics do not split or suppress this initial transport. +- **DING-R02 Two adjacent retained-safe retry observations:** A later legacy + bare Return is permitted only for a transport-owned payload whose exact + notice is still the complete composer and is classified `RetainedSafe` in + two immediately adjacent inspections. The final observation is adjacent to + the Return itself. Any change, block, or uncertainty prevents retry + submission. +- **DING-R03 Fail-closed receipt and retry:** After the initial legacy + transport, a changed composer, a human draft, an active turn, a modal, an + unreadable screen, an unrecognized harness, and a bounded observation timeout + never become `Delivered` and receive no retry input. Anything not positively understood retains staged ownership. On an inspect-only staged retry, a maintained adapter may positively prove that the exact owned payload is no longer retained; that proof relinquishes ownership only when an archive diff --git a/docs/vrs/01-ding/spec.md b/docs/vrs/01-ding/spec.md index 3d432022..65b114d1 100644 --- a/docs/vrs/01-ding/spec.md +++ b/docs/vrs/01-ding/spec.md @@ -10,6 +10,42 @@ specified in [`01-claude/spec.md`](./01-claude/spec.md) and Active. A map to the implementation and its evidence, not a replacement for the tests. +## Delivery selection + +Delivery is opt-in. An agent declaration selects one transport: + +| Declaration | Transport | +| --- | --- | +| `ding` | Legacy screen transport | +| `deliver "mcp"` | Claude native MCP transport | +| `deliver "app-server"` | Codex native app-server transport | +| Neither node | No delivery | + +`ding` and `deliver` are mutually exclusive. More than one `deliver` node is +invalid. Any other `deliver` value is invalid. st2 does not infer a transport +from the agent command because command arguments are opaque. + +The native selector is a new `deliver` node. A binary released before this +contract ignores that unknown agent child. It lowers a valid `deliver`-only +agent with no delivery sidecar. The agent receives no DING. It does not silently +use the legacy screen transport. This is a visible delivery outage. + +A binary that supports `deliver` validates its value and its mutual exclusion +with `ding`. Native delivery must not be encoded as an argument to `ding`, +because a pre-change parser would accept that form as legacy `ding` and use the +wrong transport. Doctor reports an active agent that declares no transport. The +report does not make the valid no-delivery opt-out an error. + +The durable inbox is the source of truth for every transport. An archive with +the same message name wins. A native adapter completes delivery only after its +declared provider-specific success condition. If the adapter is closed, +unavailable, stale, or in an unknown state, it sends no unsafe input and leaves +the inbox message unread for retry. Native adapters do not use the +rendered-screen classifier. + +The rest of this document defines the unchanged legacy screen transport. The +native wire contracts are in each maintained harness specification. + ## Composer states One inspection of a rendered screen, evaluated against one exact expected @@ -45,11 +81,11 @@ staged retry ─► receipt ─┬─ Accepted ─────────── └─ RetainedBlocked / Unproven ─────────────► Staged ``` -Fresh delivery preserves the production transport: one bounded PTY transaction -contains a bracketed paste, a 0.5 second delay, and Return (`DING-R01`). -Ownership is recorded immediately before that transaction. The production path -does not inspect the composer first and does not use the separate staging -helper. +Fresh legacy delivery preserves the production transport: one bounded PTY +transaction contains a bracketed paste, a 0.5 second delay, and Return +(`DING-R01`). Ownership is recorded immediately before that transaction. The +production path does not inspect the composer first and does not use the +separate staging helper. Every failure of that terminal command or of the following receipt observation resolves to `Staged` (`DING-R07`): the paste and Return may already have reached @@ -148,10 +184,11 @@ Declared `busy` never suppresses delivery; only fresh `dnd` defers it ## Known limits -- Idle proof depends on footer chrome that a harness may render differently - across permission or approval modes. A harness whose footer is not recognized - in a given mode defers indefinitely rather than delivering. This is an - explicit limit per `T01`, and each harness spec states which modes it proves. -- The classifier is a measured heuristic over rendered text, not an evented - signal, so a renderer change can defer delivery until the grammar is updated. - Tracked as `DQ2` in [`../spec.md`](../spec.md). +- Legacy idle proof depends on footer chrome that a harness may render + differently across permission or approval modes. A harness whose footer is + not recognized in a given mode defers indefinitely rather than delivering. + This is an explicit limit per `T01`, and each harness spec states which modes + it proves. +- The legacy classifier is a measured heuristic over rendered text, not an + evented signal, so a renderer change can defer legacy delivery until the + grammar is updated. Maintained native transports do not use this classifier. diff --git a/docs/vrs/02-agent-spec/spec.md b/docs/vrs/02-agent-spec/spec.md index 5ef2aedb..e7b524d3 100644 --- a/docs/vrs/02-agent-spec/spec.md +++ b/docs/vrs/02-agent-spec/spec.md @@ -358,10 +358,13 @@ Authoring: [pinned complete declaration][evals-fields]. st2 source:

F14 Compact agent fields

-Compact `command`, `argv`, `env`, `lifecycle`, and `ding` convert to the -generated agent PTY and derived sidecar. The tasks use F09, F11, and F12; -`ding` carries the dependency on the generated agent task described there. -Compact syntax adds no other behavior. +Compact `command`, `argv`, `env`, and `lifecycle` define the generated agent +PTY. Bare `ding` selects the derived legacy screen sidecar. `deliver "mcp"` +selects native Claude delivery. `deliver "app-server"` selects native Codex +delivery. The two selector nodes are mutually exclusive. More than one +`deliver` node and any other value are invalid. Neither node means no delivery. +Each generated transport depends on the generated agent task. The tasks use +F09, F11, and F12. Compact syntax adds no other behavior. Authoring: [pinned compact tasks][evals-tasks]. That document and st2 `9887b28` predate compact `argv` and `lifecycle`. Current st2 source: @@ -370,9 +373,10 @@ predate compact `argv` and `lifecycle`. Current st2 source:

F15 Provider and ignored fields

-Core st2 ignores `harness`, `model`, `persona`, `permissions`, `transport`, -`strategy`, `meta`, and provider extensions. They do not change core equality, -wake behavior, or actions. Providers may convert them into F05 through F14; +Core st2 ignores `harness`, `model`, `persona`, `permissions`, the legacy +render-only `transport` field, `strategy`, `meta`, and provider extensions. +They do not change core equality, wake behavior, or actions. Providers may +convert them into F05 through F14; core acts only on that concrete output. Authoring: [pinned complete declaration][evals-fields]. st2 source: diff --git a/docs/vrs/ontology.md b/docs/vrs/ontology.md index 38606fb0..e57b46cb 100644 --- a/docs/vrs/ontology.md +++ b/docs/vrs/ontology.md @@ -154,9 +154,12 @@ Authority: [`message::Message`](../../src/message.rs#L26-L46) ### DING -The terminal notification that makes an agent aware of unread messages. +The delivery signal that makes an agent aware of unread messages. A declaration +selects either the legacy terminal transport or one provider-native control +transport. -Authority: [DING module contract](../../src/ding/mod.rs#L1-L14) +Authority: [DING specification](./01-ding/spec.md) and +[DING module contract](../../src/ding/mod.rs#L1-L14) ## Collision rules @@ -172,7 +175,7 @@ Authority: [DING module contract](../../src/ding/mod.rs#L1-L14) value and [bus ID](../../crates/agent-spec/src/spec.rs#L203-L211) for the host-qualified address. - Use [message](../../src/message.rs#L26-L46) for the durable record and - [DING](../../src/ding/mod.rs#L1-L14) for its terminal notification. + [DING](./01-ding/spec.md) for its delivery signal. ## Avoid diff --git a/docs/vrs/requirements.md b/docs/vrs/requirements.md index dc519e72..18bd6ab9 100644 --- a/docs/vrs/requirements.md +++ b/docs/vrs/requirements.md @@ -84,7 +84,10 @@ accepted. - **R05 DING/archive semantics:** Inbox delivery, archive precedence, retries, suppression, and restart recovery are deterministic and tested. DING may interrupt agent work, but it must not alter or submit a human's active draft; - an unknown interaction state defers delivery. + an unknown interaction state defers delivery. A declaration selects either + the legacy screen transport or one supported native transport. st2 does not + infer a native transport from an opaque command. A failed or unavailable + transport leaves the inbox message unread and retryable. - **R06 Restartable launch definitions:** A restarted PTY or exec receives the complete effective launch definition, including environment and supported launch fields. diff --git a/docs/vrs/spec.md b/docs/vrs/spec.md index 561aa7ce..a894f073 100644 --- a/docs/vrs/spec.md +++ b/docs/vrs/spec.md @@ -664,6 +664,34 @@ atomic inbox file → DING attempt → agent reads → archive receipt composer cannot create a short-lived PTY probe on every inbox poll. Inbox reads do not wake the sidecar; only mutations bypass its bounded poll cadence. +### Declared native DING delivery (DQ2 resolution) + +An agent selects at most one delivery transport. `ding` keeps the legacy +screen transport unchanged. `deliver "mcp"` selects native Claude delivery. +`deliver "app-server"` selects native Codex delivery. An agent with neither +node has no delivery. The two node forms are mutually exclusive, multiple +`deliver` nodes are invalid, and an unknown `deliver` value is invalid. + +The transport is explicit because an agent command is opaque. A binary released +before this contract ignores the unknown `deliver` child. It lowers a valid +`deliver`-only declaration with no delivery sidecar. The agent receives no DING +instead of receiving a DING through the legacy screen transport. This is a +visible delivery outage. A binary that supports `deliver` validates its value +and its mutual exclusion with `ding`. + +Native delivery is not an argument to `ding`, which a pre-change parser would +accept as legacy delivery and route through the wrong transport. Doctor reports +an active agent that declares neither transport. The no-delivery form remains a +valid opt-out and does not block the agent. + +Native delivery replaces rendered-screen inference for maintained Claude and +Codex agents that select it. Each native adapter uses its provider's evented +control channel and its provider-specific success condition. A closed, +unavailable, stale, or unknown channel sends no unsafe input. The durable inbox +message remains unread and retryable. Archive precedence, suppression, and +restart recovery remain the same for every transport. The legacy classifier +remains available only for agents that select `ding`. + ## State and scope - **R08:** Presence and activity status are separate signals. The catalog must @@ -785,15 +813,6 @@ the resident supervisor continues to reconcile the complete local catalog. boundary, and execution receipts are not yet specified. A successful executable eval and Nathan's approval should resolve this before adding scheduler requirements. -- **DQ2 Safe DING delivery:** Bounded observation now replaces the fixed - paste-to-Return delay: maintained Codex and Claude composers must be - positively empty before paste and show the exact staged notice twice before - a separate Return. Human, modal, active, changed, timed-out, and unknown - states fail closed, with staged-payload ownership preventing duplicate paste. - This measured screen heuristic is still not an evented proof and renderer - changes may defer delivery. Resolve the remaining gap with a stronger evented - signal or other measured classifier; a small on-device model is an optional - experiment, not a required architecture. - **DQ3 Catalog agent state:** Define the catalog paths, schemas, freshness rules, and atomic update semantics for presence, activity status, current plan, and current plan step. Prove that stale state is distinguishable and