Skip to content

Commit dbb5044

Browse files
authored
Add unstable ACP schema support + $/cancel_request (#17)
* Make version fetch unstable schema artifacts Change-Id: Id1d0dff677c5a0ec7506a423f278c8a2ded27966 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Add $/cancel_request support to Connection Change-Id: I91e09574e4a46d5d6645f0844e3e4f2b3040cf2b Signed-off-by: Thomas Kosiewski <tk@coder.com> * cmd/generate: support unstable schema merge 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> * regen: update union UnmarshalJSON output 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> * Add tests for unstable schema merge Change-Id: Iee25c62931dea5b76748fe8ece4700d73e32211c Signed-off-by: Thomas Kosiewski <tk@coder.com> * Update agents for unstable AgentExperimental methods Change-Id: If59c040a3faa7a21ca59dce5f86d4bd26252c020 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Fix cancel_request race with inflight registration Change-Id: I1110d20acf558e42a8b2b95d30e285f446e3a7e7 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Temporarily force GitHub-hosted CI runner Change-Id: I9c10393f4d6ce1ccac4df0eb83e11795c4ad2867 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Address Codex review findings for cancel + unions + fetch Change-Id: I30839ab01619439e1a3178f319abe3312d63a7b3 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Fix latest Codex review issues Change-Id: I24665f26981bcaf92e98cd2e616071fc84d04c93 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Handle non-object unstable union variants correctly Change-Id: I98d174fac0f30681f36c6735a0c71a71d1fc55fe Signed-off-by: Thomas Kosiewski <tk@coder.com> * Bound cancel notification sender goroutine usage Change-Id: I381d40fbf2040fe4c2bcc511e0c3bf3b91816796 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Address latest Codex feedback on cancel and merge Change-Id: I0a5cc811d92a23987bf47a57ad16c77f2b41bf4e Signed-off-by: Thomas Kosiewski <tk@coder.com> * Disambiguate unstable grouped select options decoding Change-Id: I37676ffb056b5867b5bbb2e2cff121145c9b5c65 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Canonicalize JSON-RPC IDs for cancellation lookup Change-Id: I764e173988645075bc56b04c2dc4532e5678ec46 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Fix JSON-RPC ID canonicalization precision Change-Id: I77b298bd622531069b6e15d5cec9d9f61d2b9d11 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Map only explicit cancellations to -32800 Change-Id: I0669b997c35ef24cee1c03d0910b3c26ab7d7331 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Fix unstable meta fallback version handling Change-Id: I290bd2e5bdfe69795621a966ae8e0e1a81414f3d Signed-off-by: Thomas Kosiewski <tk@coder.com> * Prefer disconnect error over derived request cancel Change-Id: I690ec1462659be0d1f068dbf786f0a96a10ed611 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Assert unstable dispatch capabilities per method Change-Id: I0bab8ffb5e201d1c82d5fd9aba087fa45f995787 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Canonicalize numeric JSON-RPC IDs by value Change-Id: I163e43ba12da91083562f08aec8646eab81d96e6 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Keep timeout errors distinct from cancellations Change-Id: Ib8b24f1cd85b13ca47fc59fa2b3e3f4af1a7ad3e Signed-off-by: Thomas Kosiewski <tk@coder.com> * Bound JSON-RPC numeric ID canonicalization work Change-Id: I3b3a79ee49f2c069abd5dd24db0e8a22e8627ae3 Signed-off-by: Thomas Kosiewski <tk@coder.com> * Bound pending cancel notification queue Change-Id: I1e6195bae6969b5e07b532c5f042244530eb9a9e Signed-off-by: Thomas Kosiewski <tk@coder.com> * Format connection constructor indentation Change-Id: Ic0c7b1edb993c1f5d30b267d6c70cf711a3c16ef Signed-off-by: Thomas Kosiewski <tk@coder.com> --------- Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 38d6de7 commit dbb5044

25 files changed

+7897
-829
lines changed

.github/workflows/ci.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ concurrency:
1919
jobs:
2020
check:
2121
name: check
22-
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-latest' || 'ubuntu-latest' }}
22+
# Temporarily disabled Depot runner selection due connectivity issues.
23+
# Re-enable once Depot runners can reliably connect to the repository.
24+
# runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-latest' || 'ubuntu-latest' }}
25+
runs-on: ubuntu-latest
2326
permissions:
2427
contents: read
2528
actions: write

Makefile

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ ACP_VERSION := $(file < ./schema/version)
44
GOCACHE ?= $(CURDIR)/.gocache
55
MDSH ?= mdsh
66

7-
version: README.md schema/meta.json schema/schema.json
7+
version: README.md schema/meta.json schema/schema.json schema/meta.unstable.json schema/schema.unstable.json
88
cd cmd/generate && env -u GOPATH -u GOMODCACHE go run .
99
env -u GOPATH -u GOMODCACHE go run mvdan.cc/gofumpt@latest -w .
1010
touch $@
@@ -16,6 +16,38 @@ schema/meta.json: schema/version
1616
schema/schema.json: schema/version
1717
curl -o $@ --fail -L https://github.com/agentclientprotocol/agent-client-protocol/releases/download/v$(ACP_VERSION)/schema.json
1818

19+
schema/meta.unstable.json: schema/version
20+
@set -e; \
21+
url=https://github.com/agentclientprotocol/agent-client-protocol/releases/download/v$(ACP_VERSION)/meta.unstable.json; \
22+
tmp=$@.tmp; \
23+
status=$$(curl -sS -L -o "$$tmp" -w '%{http_code}' "$$url") || { rm -f "$$tmp"; exit 1; }; \
24+
if [ "$$status" = "200" ]; then \
25+
mv "$$tmp" $@; \
26+
elif [ "$$status" = "404" ]; then \
27+
rm -f "$$tmp"; \
28+
printf '%s\n' '{"agentMethods":{},"clientMethods":{},"protocolMethods":{}}' > $@; \
29+
else \
30+
rm -f "$$tmp"; \
31+
echo "failed to download $$url (http $$status)" 1>&2; \
32+
exit 1; \
33+
fi
34+
35+
schema/schema.unstable.json: schema/version
36+
@set -e; \
37+
url=https://github.com/agentclientprotocol/agent-client-protocol/releases/download/v$(ACP_VERSION)/schema.unstable.json; \
38+
tmp=$@.tmp; \
39+
status=$$(curl -sS -L -o "$$tmp" -w '%{http_code}' "$$url") || { rm -f "$$tmp"; exit 1; }; \
40+
if [ "$$status" = "200" ]; then \
41+
mv "$$tmp" $@; \
42+
elif [ "$$status" = "404" ]; then \
43+
rm -f "$$tmp"; \
44+
printf '%s\n' '{"$$defs":{}}' > $@; \
45+
else \
46+
rm -f "$$tmp"; \
47+
echo "failed to download $$url (http $$status)" 1>&2; \
48+
exit 1; \
49+
fi
50+
1951
README.md: schema/version
2052
@command -v $(MDSH) >/dev/null || { echo "mdsh not found; run 'nix develop' or install it." 1>&2; exit 1; }
2153
$(MDSH) --input README.md
@@ -40,7 +72,7 @@ test: $(GO_FILES)
4072

4173
.PHONY: clean
4274
clean:
43-
rm schema/meta.json schema/schema.json version
75+
rm -f schema/meta.json schema/schema.json schema/meta.unstable.json schema/schema.unstable.json version
4476
mdsh --clean --input README.md
4577
touch schema/version # Touching the schema version file ensures that the README.md is regenerated on next make.
4678

acp_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ type agentFuncs struct {
117117
PromptFunc func(context.Context, PromptRequest) (PromptResponse, error)
118118
CancelFunc func(context.Context, CancelNotification) error
119119
SetSessionModeFunc func(ctx context.Context, params SetSessionModeRequest) (SetSessionModeResponse, error)
120+
// Unstable (schema/meta.unstable.json)
121+
UnstableForkSessionFunc func(context.Context, UnstableForkSessionRequest) (UnstableForkSessionResponse, error)
122+
UnstableListSessionsFunc func(context.Context, UnstableListSessionsRequest) (UnstableListSessionsResponse, error)
123+
UnstableResumeSessionFunc func(context.Context, UnstableResumeSessionRequest) (UnstableResumeSessionResponse, error)
124+
UnstableSetSessionConfigOptionFunc func(context.Context, UnstableSetSessionConfigOptionRequest) (UnstableSetSessionConfigOptionResponse, error)
125+
UnstableSetSessionModelFunc func(context.Context, UnstableSetSessionModelRequest) (UnstableSetSessionModelResponse, error)
120126

121127
HandleExtensionMethodFunc func(context.Context, string, json.RawMessage) (any, error)
122128
}
@@ -178,13 +184,115 @@ func (a agentFuncs) SetSessionMode(ctx context.Context, params SetSessionModeReq
178184
return SetSessionModeResponse{}, nil
179185
}
180186

187+
// UnstableForkSession implements AgentExperimental.
188+
func (a agentFuncs) UnstableForkSession(ctx context.Context, params UnstableForkSessionRequest) (UnstableForkSessionResponse, error) {
189+
if a.UnstableForkSessionFunc != nil {
190+
return a.UnstableForkSessionFunc(ctx, params)
191+
}
192+
return UnstableForkSessionResponse{}, nil
193+
}
194+
195+
// UnstableListSessions implements AgentExperimental.
196+
func (a agentFuncs) UnstableListSessions(ctx context.Context, params UnstableListSessionsRequest) (UnstableListSessionsResponse, error) {
197+
if a.UnstableListSessionsFunc != nil {
198+
return a.UnstableListSessionsFunc(ctx, params)
199+
}
200+
return UnstableListSessionsResponse{}, nil
201+
}
202+
203+
// UnstableResumeSession implements AgentExperimental.
204+
func (a agentFuncs) UnstableResumeSession(ctx context.Context, params UnstableResumeSessionRequest) (UnstableResumeSessionResponse, error) {
205+
if a.UnstableResumeSessionFunc != nil {
206+
return a.UnstableResumeSessionFunc(ctx, params)
207+
}
208+
return UnstableResumeSessionResponse{}, nil
209+
}
210+
211+
// UnstableSetSessionConfigOption implements AgentExperimental.
212+
func (a agentFuncs) UnstableSetSessionConfigOption(ctx context.Context, params UnstableSetSessionConfigOptionRequest) (UnstableSetSessionConfigOptionResponse, error) {
213+
if a.UnstableSetSessionConfigOptionFunc != nil {
214+
return a.UnstableSetSessionConfigOptionFunc(ctx, params)
215+
}
216+
return UnstableSetSessionConfigOptionResponse{}, nil
217+
}
218+
219+
// UnstableSetSessionModel implements AgentExperimental.
220+
func (a agentFuncs) UnstableSetSessionModel(ctx context.Context, params UnstableSetSessionModelRequest) (UnstableSetSessionModelResponse, error) {
221+
if a.UnstableSetSessionModelFunc != nil {
222+
return a.UnstableSetSessionModelFunc(ctx, params)
223+
}
224+
return UnstableSetSessionModelResponse{}, nil
225+
}
226+
181227
func (a agentFuncs) HandleExtensionMethod(ctx context.Context, method string, params json.RawMessage) (any, error) {
182228
if a.HandleExtensionMethodFunc != nil {
183229
return a.HandleExtensionMethodFunc(ctx, method, params)
184230
}
185231
return nil, NewMethodNotFound(method)
186232
}
187233

234+
type forkOnlyUnstableAgent struct {
235+
called bool
236+
}
237+
238+
func (a *forkOnlyUnstableAgent) Authenticate(context.Context, AuthenticateRequest) (AuthenticateResponse, error) {
239+
return AuthenticateResponse{}, nil
240+
}
241+
242+
func (a *forkOnlyUnstableAgent) Initialize(context.Context, InitializeRequest) (InitializeResponse, error) {
243+
return InitializeResponse{}, nil
244+
}
245+
246+
func (a *forkOnlyUnstableAgent) Cancel(context.Context, CancelNotification) error {
247+
return nil
248+
}
249+
250+
func (a *forkOnlyUnstableAgent) NewSession(context.Context, NewSessionRequest) (NewSessionResponse, error) {
251+
return NewSessionResponse{}, nil
252+
}
253+
254+
func (a *forkOnlyUnstableAgent) Prompt(context.Context, PromptRequest) (PromptResponse, error) {
255+
return PromptResponse{}, nil
256+
}
257+
258+
func (a *forkOnlyUnstableAgent) SetSessionMode(context.Context, SetSessionModeRequest) (SetSessionModeResponse, error) {
259+
return SetSessionModeResponse{}, nil
260+
}
261+
262+
func (a *forkOnlyUnstableAgent) UnstableForkSession(context.Context, UnstableForkSessionRequest) (UnstableForkSessionResponse, error) {
263+
a.called = true
264+
return UnstableForkSessionResponse{SessionId: "forked-session"}, nil
265+
}
266+
267+
func TestAgentDispatch_AllowsPartialUnstableMethodImplementation(t *testing.T) {
268+
agent := &forkOnlyUnstableAgent{}
269+
conn := &AgentSideConnection{
270+
agent: agent,
271+
sessionCancels: make(map[string]context.CancelFunc),
272+
}
273+
274+
params, err := json.Marshal(UnstableForkSessionRequest{Cwd: "/tmp", SessionId: "source-session"})
275+
if err != nil {
276+
t.Fatalf("marshal request params: %v", err)
277+
}
278+
279+
result, reqErr := conn.handle(context.Background(), AgentMethodSessionFork, params)
280+
if reqErr != nil {
281+
t.Fatalf("unexpected request error: %+v", reqErr)
282+
}
283+
if !agent.called {
284+
t.Fatal("expected UnstableForkSession method to be invoked")
285+
}
286+
287+
resp, ok := result.(UnstableForkSessionResponse)
288+
if !ok {
289+
t.Fatalf("expected UnstableForkSessionResponse, got %T", result)
290+
}
291+
if resp.SessionId != "forked-session" {
292+
t.Fatalf("unexpected response session id: %q", resp.SessionId)
293+
}
294+
}
295+
188296
// Test bidirectional error handling similar to typescript/acp.test.ts
189297
func TestConnectionHandlesErrorsBidirectional(t *testing.T) {
190298
ctx := context.Background()

agent_gen.go

Lines changed: 95 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client_gen.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)