Skip to content

Commit 8628acb

Browse files
committed
Clean up generate back to strict objects to avoid issues
1 parent a6c3bfa commit 8628acb

3 files changed

Lines changed: 344 additions & 263 deletions

File tree

scripts/generate.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ async function main() {
5252
updateDocs(
5353
zodSrc
5454
.replace(`from "zod"`, `from "zod/v4"`)
55-
.replaceAll(/z\.object\(/g, "z.looseObject(")
5655
// Weird type issue
5756
.replaceAll(
5857
/z\.record\((?!z\.string\(\),\s*)([^)]+)\)/g,
@@ -182,11 +181,12 @@ function injectDocIfMissing(src, exportStr, description) {
182181
const idx = src.indexOf(exportStr);
183182
if (idx === -1) return src;
184183

185-
const before = src.substring(Math.max(0, idx - 50), idx);
184+
const before = src.substring(0, idx);
186185
if (/\*\/\s*$/.test(before)) return src;
187186

188187
const lines = description.split("\n");
189-
const jsdoc = "/**\n" + lines.map((l) => ` * ${l}`).join("\n") + "\n */\n";
188+
const jsdoc =
189+
"/**\n" + lines.map((l) => (l ? ` * ${l}` : " *")).join("\n") + "\n */\n";
190190

191191
return src.slice(0, idx) + jsdoc + src.slice(idx);
192192
}

src/acp.test.ts

Lines changed: 111 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ describe("Connection", () => {
564564
expect(response.authMethods?.[0].id).toBe("oauth");
565565
});
566566

567-
it("preserves unknown properties on known incoming params", async () => {
567+
it("strips unknown properties on known incoming params", async () => {
568568
let receivedInitializeParams: Record<string, unknown> | undefined;
569569
let receivedSessionUpdate: Record<string, unknown> | undefined;
570570

@@ -661,26 +661,20 @@ describe("Connection", () => {
661661
} as any);
662662

663663
await vi.waitFor(() => {
664-
expect(receivedInitializeParams).toMatchObject({
665-
extraTopLevel: "keep me",
666-
clientCapabilities: {
667-
customCapability: {
668-
enabled: true,
669-
},
670-
fs: {
671-
experimentalFs: true,
672-
},
673-
},
674-
});
664+
expect(receivedInitializeParams).not.toHaveProperty("extraTopLevel");
665+
expect(receivedInitializeParams).not.toHaveProperty(
666+
"clientCapabilities.customCapability",
667+
);
668+
expect(receivedInitializeParams).not.toHaveProperty(
669+
"clientCapabilities.fs.experimentalFs",
670+
);
675671

676-
expect(receivedSessionUpdate).toMatchObject({
677-
extraNotificationField: "keep this too",
678-
update: {
679-
extraUpdateField: {
680-
keep: true,
681-
},
682-
},
683-
});
672+
expect(receivedSessionUpdate).not.toHaveProperty(
673+
"extraNotificationField",
674+
);
675+
expect(receivedSessionUpdate).not.toHaveProperty(
676+
"update.extraUpdateField",
677+
);
684678
});
685679
});
686680

@@ -2002,6 +1996,22 @@ describe("Connection", () => {
20021996
expect((receivedRequest as any)?.sessionId).toBe("test-session");
20031997
expect((receivedRequest as any)?.mode).toBe("form");
20041998

1999+
// Test url-mode elicitation request
2000+
receivedRequest = undefined;
2001+
const urlResponse = await clientConnection.unstable_createElicitation({
2002+
sessionId: "test-session",
2003+
mode: "url",
2004+
message: "Please authenticate",
2005+
elicitationId: "elic-url-1",
2006+
url: "https://example.com/auth",
2007+
});
2008+
2009+
expect(urlResponse.action).toBe("accept");
2010+
expect((receivedRequest as any)?.message).toBe("Please authenticate");
2011+
expect((receivedRequest as any)?.mode).toBe("url");
2012+
expect((receivedRequest as any)?.url).toBe("https://example.com/auth");
2013+
expect((receivedRequest as any)?.elicitationId).toBe("elic-url-1");
2014+
20052015
// Test elicitation complete notification
20062016
await clientConnection.unstable_completeElicitation({
20072017
elicitationId: "elic-1",
@@ -2012,6 +2022,58 @@ describe("Connection", () => {
20122022
});
20132023
});
20142024

2025+
it("silently ignores completeElicitation when client does not implement handler", async () => {
2026+
class TestClient implements Client {
2027+
async writeTextFile(
2028+
_: WriteTextFileRequest,
2029+
): Promise<WriteTextFileResponse> {
2030+
return {};
2031+
}
2032+
async readTextFile(
2033+
_: ReadTextFileRequest,
2034+
): Promise<ReadTextFileResponse> {
2035+
return { content: "" };
2036+
}
2037+
async requestPermission(
2038+
_: RequestPermissionRequest,
2039+
): Promise<RequestPermissionResponse> {
2040+
return { outcome: { outcome: "selected", optionId: "allow" } };
2041+
}
2042+
async sessionUpdate(_: SessionNotification): Promise<void> {}
2043+
}
2044+
2045+
class TestAgent implements Agent {
2046+
async initialize(_: InitializeRequest): Promise<InitializeResponse> {
2047+
return {
2048+
protocolVersion: 1,
2049+
agentCapabilities: { loadSession: false },
2050+
authMethods: [],
2051+
};
2052+
}
2053+
async newSession(_: NewSessionRequest): Promise<NewSessionResponse> {
2054+
return { sessionId: "test-session" };
2055+
}
2056+
async authenticate(_: AuthenticateRequest): Promise<void> {}
2057+
async prompt(_: PromptRequest): Promise<PromptResponse> {
2058+
return { stopReason: "end_turn" };
2059+
}
2060+
async cancel(_: CancelNotification): Promise<void> {}
2061+
}
2062+
2063+
new ClientSideConnection(
2064+
() => new TestClient(),
2065+
ndJsonStream(clientToAgent.writable, agentToClient.readable),
2066+
);
2067+
const clientConnection = new AgentSideConnection(
2068+
() => new TestAgent(),
2069+
ndJsonStream(agentToClient.writable, clientToAgent.readable),
2070+
);
2071+
2072+
await clientConnection.unstable_completeElicitation({
2073+
elicitationId: "elic-1",
2074+
});
2075+
});
2076+
20152077
it("rejects elicitation request when client does not implement handler", async () => {
20162078
// Client WITHOUT unstable_createElicitation
20172079
class TestClient implements Client {
@@ -2060,8 +2122,8 @@ describe("Connection", () => {
20602122
ndJsonStream(agentToClient.writable, clientToAgent.readable),
20612123
);
20622124

2063-
try {
2064-
await clientConnection.unstable_createElicitation({
2125+
await expect(
2126+
clientConnection.unstable_createElicitation({
20652127
sessionId: "test-session",
20662128
mode: "form",
20672129
message: "Enter your name",
@@ -2071,11 +2133,8 @@ describe("Connection", () => {
20712133
name: { type: "string" },
20722134
},
20732135
},
2074-
});
2075-
expect.fail("Should have thrown method not found error");
2076-
} catch (error: any) {
2077-
expect(error.code).toBe(-32601); // Method not found
2078-
}
2136+
}),
2137+
).rejects.toMatchObject({ code: -32601 });
20792138
});
20802139
});
20812140

@@ -2125,6 +2184,18 @@ describe("CreateElicitationRequest schema", () => {
21252184
expect(result.success).toBe(true);
21262185
});
21272186

2187+
it("accepts url-mode request with optional toolCallId", () => {
2188+
const result = zCreateElicitationRequest.safeParse({
2189+
sessionId: "sess-1",
2190+
toolCallId: "tc-1",
2191+
mode: "url",
2192+
message: "Please authenticate",
2193+
elicitationId: "elic-1",
2194+
url: "https://example.com/auth",
2195+
});
2196+
expect(result.success).toBe(true);
2197+
});
2198+
21282199
it("accepts url-mode request scoped to a request", () => {
21292200
const result = zCreateElicitationRequest.safeParse({
21302201
requestId: "req-1",
@@ -2163,7 +2234,7 @@ describe("CreateElicitationRequest schema", () => {
21632234
expect(result.success).toBe(false);
21642235
});
21652236

2166-
it("rejects request without scope (no sessionId or requestId)", () => {
2237+
it("rejects form-mode request without scope (no sessionId or requestId)", () => {
21672238
const result = zCreateElicitationRequest.safeParse({
21682239
mode: "form",
21692240
message: "Enter your name",
@@ -2172,14 +2243,24 @@ describe("CreateElicitationRequest schema", () => {
21722243
expect(result.success).toBe(false);
21732244
});
21742245

2175-
it("preserves unknown properties (looseObject)", () => {
2246+
it("rejects url-mode request without scope (no sessionId or requestId)", () => {
2247+
const result = zCreateElicitationRequest.safeParse({
2248+
mode: "url",
2249+
message: "Please authenticate",
2250+
elicitationId: "elic-1",
2251+
url: "https://example.com/auth",
2252+
});
2253+
expect(result.success).toBe(false);
2254+
});
2255+
2256+
it("strips unknown properties", () => {
21762257
const result = zCreateElicitationRequest.safeParse({
21772258
...formSessionRequest,
21782259
customField: "custom-value",
21792260
});
21802261
expect(result.success).toBe(true);
21812262
if (result.success) {
2182-
expect((result.data as any).customField).toBe("custom-value");
2263+
expect((result.data as any).customField).toBeUndefined();
21832264
}
21842265
});
21852266
});

0 commit comments

Comments
 (0)