Skip to content

Comments

Add unstable ACP schema support + $/cancel_request#17

Open
ThomasK33 wants to merge 24 commits intomainfrom
unstable-codegen
Open

Add unstable ACP schema support + $/cancel_request#17
ThomasK33 wants to merge 24 commits intomainfrom
unstable-codegen

Conversation

@ThomasK33
Copy link
Member

Add support for ACP unstable schemas alongside stable API generation, and implement full $/cancel_request cancellation semantics.

What changed

  • Schema/version workflow: fetch schema/meta.unstable.json + schema/schema.unstable.json (with safe fallback for older ACP releases).
  • Codegen: optionally load + merge unstable meta/schema without changing stable types/methods; generate unstable-only RPC types under an Unstable* prefix.
  • Runtime: implement $/cancel_request inbound/outbound handling, per-request cancellation contexts, and map cancellations to JSON-RPC error code -32800.
  • Generator robustness: fix union UnmarshalJSON for unions that can be primitives (e.g. RequestId).
  • Tests: add unit tests for merge logic and cancellation behavior.

Verification

  • go test ./...
  • (cd cmd/generate && go test ./...)

📋 Implementation Plan

ACP unstable schema support + Unstable* Go API

Context / Why

Upstream ACP now ships additional schema artifacts:

  • schema/meta.unstable.json
  • schema/schema.unstable.json

Goal:

  • Keep the stable Go API (methods + types generated from meta.json/schema.json) unchanged.
  • Expose unstable-only RPCs/types with an Unstable prefix (user choice: prefix existing schema type names, e.g. ForkSessionRequestUnstableForkSessionRequest).
  • Implement full $\/cancel_request protocol cancellation support (user choice).

Evidence (what we verified)

  • Local schema inputs live in schema/ (schema/meta.json, schema/schema.json, schema/version currently 0.10.7).
  • Upstream stable meta.json vs meta.unstable.json:
    • New unstable agent methods: session/fork, session/list, session/resume, session/set_config_option, session/set_model.
    • New protocol method: $/cancel_request (under protocolMethods).
  • Upstream unstable schema.unstable.json is a superset of schema.json (main branch sample: 29 new $defs, 7 changed $defs).
  • Current generator:
    • Loads only meta.json + schema.json (cmd/generate/main.go, cmd/generate/internal/load/load.go).
    • Derives method names from the *Request type name (so renaming request types is sufficient to rename method names).
    • Has no protocolMethods handling today.
  • Current runtime:
    • Connection does not support $\/cancel_request and does not have per-request cancellation (single shared context for inbound handlers).

Plan

1) Fetch & store unstable schema files in-repo

  • Update Makefile to download (from the pinned release tag) in addition to stable files:
    • schema/meta.unstable.json
    • schema/schema.unstable.json
  • Ensure make version brings these files in (and generator remains compatible if they’re absent for older pins).

2) Loader changes: parse unstable meta + schema (optionally)

  • Extend cmd/generate/internal/load.Meta:
    • add ProtocolMethods map[string]string \json:"protocolMethods,omitempty"``.
  • Add optional readers:
    • ReadMetaUnstable(schemaDir) (*Meta, bool, error)
    • ReadSchemaUnstable(schemaDir) (*Schema, bool, error)
    • return (nil, false, nil) when the *.unstable.json file isn’t present.

3) Core: merge stable + unstable into a single codegen view without changing stable API

Create a merge step (new file in cmd/generate/internal/load/ or cmd/generate/internal/ir/) that outputs:

  • combinedMeta: stable methods + unstable-only methods (for codegen), and protocolMethods.
  • combinedSchema: stable $defs plus additional $defs with Unstable-prefixed names.

3.1 Determine which wire methods are “unstable-only”

Compute unstable-only wire method sets by diffing by wire method string:

  • unstableAgentWires = metaUnstable.AgentMethods − metaStable.AgentMethods
  • unstableClientWires = metaUnstable.ClientMethods − metaStable.ClientMethods
  • unstableProtocolWires = metaUnstable.ProtocolMethods (stable has none)

Merge these into combinedMeta (keep stable maps unchanged; only add new entries).

3.2 Build the set of $defs to duplicate as Unstable*

Roots: in schema.unstable.json, find all $defs where:

  • x-method ∈ unstable-only wire set, and
  • x-side matches (agent, client, protocol), and
  • name ends with Request, Response, or Notification.

Reachability: traverse $ref edges from roots within schema.unstable.json to compute reachable.

Fixpoint selection (minimal duplication): compute needed where we must create an unstable-prefixed copy:

  • Seed needed with all roots.
  • Add any reachable def that is new (missing in stable schema) or changed vs stable (e.g. reflect.DeepEqual(stableDef, unstableDef) == false).
  • Iterate until fixed:
    • if a reachable def references any def already in needed, then it must also be in needed (so we can rewrite refs and keep the unstable graph self-consistent).

This matches the user requirement: generate unstable variants for stable-RPC types only when needed transitively.

3.3 Emit Unstable* defs (deep-copy + rewrite refs)

For each def name T in needed:

  • Deep-copy schemaUnstable.$defs[T].
  • Rewrite all nested refs #/$defs/X#/$defs/UnstableX only when X is also in needed.
  • Insert into combinedSchema.$defs under the new key Unstable<T>.

Avoid method-group collisions: if the copied def has x-method pointing to a stable wire method (present in meta.json), clear x-method/x-side on the copy so it won’t be treated as the canonical request/response type for stable dispatch.

Naming rule (chosen): ForkSessionRequestUnstableForkSessionRequest (simple prefix; no re-ordering).

4) Wire up generator entrypoint to use merged inputs

Update cmd/generate/main.go:

  • Load stable meta/schema as today.
  • Attempt to load unstable meta/schema.
  • If unstable inputs exist:
    • run merge to produce combinedMeta + combinedSchema.
    • pass combined inputs into all emitters:
      • emit.WriteConstantsJen
      • emit.WriteTypesJen
      • emit.WriteDispatchJen
      • emit.WriteHelpersJen

Expected result:

  • Stable interfaces (Agent, Client, etc.) remain unchanged.
  • New unstable-only methods are generated as Unstable* and land in AgentExperimental / ClientExperimental via the existing “unstable” doc marker detection.

5) Full $\/cancel_request support (protocol cancellation)

Implement cancellation primarily in runtime, not by exposing protocol hooks on Agent/Client.

5.1 Inbound cancellation (server-side)

Update connection.go:

  • Add an inflight map keyed by JSON-RPC id string (raw JSON form, consistent with existing pending map keys).
  • For inbound requests (messages with both id and method):
    • create a per-request context reqCtx, cancel := context.WithCancelCause(c.ctx)
    • store cancel in inflight[idKey]
    • pass reqCtx to the handler
    • ensure cleanup (delete map entry) on completion
  • Intercept $\/cancel_request notifications in receive() before invoking c.handler:
    • parse params as an object, extract requestId as raw JSON
    • if a matching cancel exists, call it (cause can be context.Canceled)
    • do not log MethodNotFound

5.2 Outbound cancellation (client-side)

When SendRequest waits for a response and ctx.Done() fires first:

  • Best-effort send a $\/cancel_request notification to the peer with params { "requestId": <raw id> }.
  • Then behave as today (cleanup pending + return an error).

5.3 Error mapping for canceled requests

  • Add a helper like NewRequestCancelled(...) returning JSON-RPC error code -32800.
  • Update toReqErr to map context.Canceled (and/or cancel causes) to -32800 instead of treating it as internal error.

6) Fix union unmarshal for non-object variants (needed for RequestId)

RequestId is an anyOf union of null | number | string. Today the generated UnmarshalJSON always tries map[string]json.RawMessage, which fails for primitives.

Update emitUnion codegen so:

  • it attempts map[string]json.RawMessage unmarshal
  • if that fails (non-object JSON), it skips discriminator/required-key logic and falls back to sequential variant decoding

This makes RequestId (and thus CancelRequestNotification) usable.

7) Tests + validation

  • Add focused unit tests:
    1. Merge logic: stable defs unchanged; unstable roots become Unstable*; refs rewritten; stable method grouping unaffected.
    2. Cancellation:
      • inbound $\/cancel_request cancels a long-running handler and produces -32800
      • outbound ctx cancellation sends $\/cancel_request on the wire (capture writes)
  • Run verification commands:
    • make version
    • make fmt
    • make test
Notes / non-goals (keep scope tight)
  • We will not change stable method signatures to return unstable-extended response types.
  • Unstable-only methods remain optional (behind AgentExperimental / ClientExperimental) to avoid breaking implementers.

Generated with mux • Model: openai:gpt-5.2 • Thinking: xhigh

Change-Id: Id1d0dff677c5a0ec7506a423f278c8a2ded27966
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: I91e09574e4a46d5d6645f0844e3e4f2b3040cf2b
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Add optional loading for meta/schema.unstable.json and merge unstable-only methods into the IR without changing stable defs.

Also adjust union UnmarshalJSON generation to tolerate primitive JSON by only peeking at object maps when that unmarshal succeeds.

Change-Id: I81dbcd9f72e9106c95c6aafb07f895b7758e7f5e
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Regenerate types_gen.go so union UnmarshalJSON methods no longer error on primitive JSON variants.

Change-Id: Ie20a7e301e4808e8a66a339456029d1f08ecad7d
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: Iee25c62931dea5b76748fe8ece4700d73e32211c
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: If59c040a3faa7a21ca59dce5f86d4bd26252c020
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ac0b690036

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@agentcooper
Copy link
Contributor

Hey @ThomasK33, can this be merged? Also https://agentclientprotocol.com/protocol/session-config-options is now stable.

agentcooper added a commit to agentcooper/acp-go-sdk that referenced this pull request Feb 5, 2026
Change-Id: I1110d20acf558e42a8b2b95d30e285f446e3a7e7
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Change-Id: I9c10393f4d6ce1ccac4df0eb83e11795c4ad2867
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bb34ca2793

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I30839ab01619439e1a3178f319abe3312d63a7b3
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: acb634959a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I24665f26981bcaf92e98cd2e616071fc84d04c93
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7ec4e89e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I98d174fac0f30681f36c6735a0c71a71d1fc55fe
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Var().Id("v").Id(vi.typeName),
If(Qual("encoding/json", "Unmarshal").Call(Id("b"), Op("&").Id("v")).Op("==").Nil()).Block(
Id("u").Dot(vi.fieldName).Op("=").Op("&").Id("v"),
Return(Nil()),

P1 Badge Disambiguate sequential union decoding for array variants

The fallback branch accepts the first variant whose json.Unmarshal succeeds, which is unsafe for unions where multiple array variants can decode the same payload. With the new unstable schema this causes valid grouped select-options payloads (objects with group/options) to be parsed as the ungrouped variant first because unknown fields are ignored and missing required fields become zero values, so grouping information is silently lost in UnstableSessionConfigSelectOptions.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I381d40fbf2040fe4c2bcc511e0c3bf3b91816796
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c7c2c7c34b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I0a5cc811d92a23987bf47a57ad16c77f2b41bf4e
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c67a44c49a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I37676ffb056b5867b5bbb2e2cff121145c9b5c65
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 853d58e391

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I690ec1462659be0d1f068dbf786f0a96a10ed611
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 476669d162

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I0bab8ffb5e201d1c82d5fd9aba087fa45f995787
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aba87cd94b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I163e43ba12da91083562f08aec8646eab81d96e6
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 14f70fb539

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: Ib8b24f1cd85b13ca47fc59fa2b3e3f4af1a7ad3e
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2d3ab6aa6d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I3b3a79ee49f2c069abd5dd24db0e8a22e8627ae3
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b54e8a331d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Change-Id: I1e6195bae6969b5e07b532c5f042244530eb9a9e
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Swish!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ThomasK33
Copy link
Member Author

@codex review

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Another round soon, please!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants