mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-22 17:22:47 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b62af7287b | ||
|
|
4ce6704d5e |
@@ -0,0 +1,27 @@
|
||||
# Native Grok question reproduction
|
||||
|
||||
This branch retains an intentionally failing regression for [#3490](https://github.com/getpaseo/paseo/issues/3490). It changes no production behavior.
|
||||
|
||||
Run from the repository root after installing dependencies and building server dependencies:
|
||||
|
||||
```sh
|
||||
npm ci --no-audit --no-fund
|
||||
npm run build:server-deps
|
||||
npm run test:unit --workspace=@getpaseo/server -- src/server/agent/providers/grok-question-repro.test.ts
|
||||
```
|
||||
|
||||
On main `f472c9d1f4339c0f36583e84bf62c80c12867110`, the test fails:
|
||||
|
||||
```text
|
||||
Question permissions: 0 (expected 1)
|
||||
Expected: {"outcome":"accepted","answers":{"Choose a color":["Blue"]}}
|
||||
Received: {"code":-32601,"message":"\"Method not found\": _x.ai/ask_user_question"}
|
||||
```
|
||||
|
||||
The test uses a deterministic subprocess with the real ACP SDK and stdio transport, through the public session API. It emits the native request, chooses Blue if a question permission arrives, and checks the response. It does not use a live model or render the app.
|
||||
|
||||
The same test passes unchanged on [#7015](https://github.com/getpaseo/paseo/pull/7015) at `b8a48e4825cf49d09f352a4b3d7793847657908c`. All 165 targeted local checks passed there: 146 ACP tests including this independent regression, 10 shared form-core tests, two serialization tests, and seven Chromium question-form tests.
|
||||
|
||||
The real Grok attempt ended during session initialization, before any question was emitted. Live question generation, the rendered card, lettered fallback, OpenCode comparison, and the contributor's newer report of Grok merging labels after answering remain unverified. Reproduction confidence is 3/5; diagnosis confidence is 5/5 for the reproduced protocol failure only.
|
||||
|
||||
The missing-handler analysis from [luoph](https://github.com/luoph) holds at the ACP boundary. The proposed fix and subsequent answer-identity work are by luoph and [RoundRedDot](https://github.com/RRD-dev); this branch retains independent verification rather than a replacement fix. Updates stay on [the issue](https://github.com/getpaseo/paseo/issues/3490).
|
||||
@@ -0,0 +1,94 @@
|
||||
import { mkdtemp, writeFile, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { createRequire } from "node:module";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { expect, test } from "vitest";
|
||||
import { ACPAgentSession } from "./acp-agent.js";
|
||||
import { createTestLogger } from "../../../test-utils/test-logger.js";
|
||||
|
||||
// A deterministic ACP agent emits the native request captured in issue #3490.
|
||||
// This exercises the public session API and SDK transport, without a live model.
|
||||
const agentSource = `
|
||||
import { Readable, Writable } from 'node:stream';
|
||||
const { AgentSideConnection, PROTOCOL_VERSION, ndJsonStream } = await import(process.env.ACP_SDK_URL);
|
||||
new AgentSideConnection(connection => ({
|
||||
async initialize() { return {protocolVersion:PROTOCOL_VERSION}; },
|
||||
async newSession() { return {sessionId:'question-repro'}; },
|
||||
async authenticate() {}, async cancel() {},
|
||||
async prompt() {
|
||||
let result;
|
||||
try {
|
||||
result = await connection.extMethod('_x.ai/ask_user_question', {
|
||||
sessionId:'question-repro', toolCallId:'question-1', mode:'default',
|
||||
questions:[{question:'Choose a color',options:[{label:'Red'},{label:'Blue'}],multiSelect:false}]
|
||||
});
|
||||
} catch(error) { result = {code:error.code,message:error.message}; }
|
||||
await connection.sessionUpdate({sessionId:'question-repro',update:{
|
||||
sessionUpdate:'agent_message_chunk', content:{type:'text',text:JSON.stringify(result)}
|
||||
}});
|
||||
return {stopReason:'end_turn'};
|
||||
}
|
||||
}), ndJsonStream(Writable.toWeb(process.stdout),Readable.toWeb(process.stdin)));
|
||||
`;
|
||||
|
||||
test("native Grok question reaches the permission interface and accepts the selected answer", async () => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), "paseo-grok-question-"));
|
||||
const script = path.join(directory, "agent.mjs");
|
||||
await writeFile(script, agentSource);
|
||||
const session = new ACPAgentSession(
|
||||
{ provider: "grok", cwd: directory },
|
||||
{
|
||||
provider: "grok",
|
||||
logger: createTestLogger(),
|
||||
defaultCommand: [process.execPath, script],
|
||||
defaultModes: [],
|
||||
capabilities: {
|
||||
supportsStreaming: true,
|
||||
supportsSessionPersistence: true,
|
||||
supportsDynamicModes: true,
|
||||
supportsMcpServers: true,
|
||||
supportsReasoningStream: true,
|
||||
supportsToolInvocations: true,
|
||||
},
|
||||
launchEnv: {
|
||||
ACP_SDK_URL: pathToFileURL(
|
||||
createRequire(import.meta.url).resolve("@agentclientprotocol/sdk"),
|
||||
).href,
|
||||
},
|
||||
},
|
||||
);
|
||||
const questions: unknown[] = [];
|
||||
const replies: string[] = [];
|
||||
let finish!: () => void;
|
||||
const done = new Promise<void>((resolve) => {
|
||||
finish = resolve;
|
||||
});
|
||||
session.subscribe((event) => {
|
||||
if (event.type === "permission_requested") {
|
||||
questions.push(event.request);
|
||||
void session.respondToPermission(event.request.id, {
|
||||
behavior: "allow",
|
||||
updatedInput: {
|
||||
questionAnswers: { "Question 1": { selectedOptionIds: ["1"] } },
|
||||
},
|
||||
});
|
||||
}
|
||||
if (event.type === "timeline" && event.item.type === "assistant_message")
|
||||
replies.push(event.item.text);
|
||||
if (event.type === "turn_completed" || event.type === "turn_failed") finish();
|
||||
});
|
||||
try {
|
||||
await session.initializeNewSession();
|
||||
await session.startTurn("Ask me to choose a color.");
|
||||
await done;
|
||||
expect.soft(questions).toHaveLength(1);
|
||||
expect(replies.join("")).toBe(
|
||||
JSON.stringify({ outcome: "accepted", answers: { "Choose a color": ["Blue"] } }),
|
||||
);
|
||||
expect(session.getPendingPermissions()).toEqual([]);
|
||||
} finally {
|
||||
await session.close();
|
||||
await rm(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user