Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions packages/acp/tests/adapter-protocol.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,39 @@ describe("Tier EA — the embedded adapters' prompt-response metadata", () => {
});
}

it("HC Claude refuses conflicting confirmation during optional setup", function* () {
let root: string | undefined;
yield* ensure(function* () {
if (root !== undefined) {
yield* rm(root, { recursive: true, force: true });
}
});
root = yield* until(mkdtemp(join(tmpdir(), "xmd startup conflict ")));
const adapter = yield* useAdapter("claude", {
...CLAUDE_ENV,
CLAUDE_CONFIG_DIR: root,
ANTHROPIC_MODEL: "",
FAKE_CLAUDE_CONFIRMATION: "conflicting",
FAKE_CLAUDE_CONFLICT_AT: "context-request",
});
yield* initialize(adapter);
let failure;
try {
yield* adapter.request("session/resume", {
sessionId: "11111111-1111-4111-8111-111111111111",
cwd: root,
mcpServers: [],
});
} catch (error) {
failure = String(error);
}
const log = yield* readTextFile(adapter.log);
expect(log).toContain('"event":"conflict-at-context-request"');
expect(failure).toContain("Claude SessionStart identity confirmation failed");
expect(acceptances(adapter)).toEqual([]);
expect(log).not.toContain('"type":"user"');
});

it("HC Claude cancellation joins the held startup Query with no late response", function* () {
let adapter: Adapter | undefined;
let answered = false;
Expand Down
25 changes: 24 additions & 1 deletion packages/acp/tests/fixtures/fake-claude-cli.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ function confirm() {
uuid: crypto.randomUUID(),
session_id: confirmation === "mismatch" ? "other-conversation" : SESSION,
});
if (confirmation === "conflicting") {
if (confirmation === "conflicting" && process.env.FAKE_CLAUDE_CONFLICT_AT !== "context-request") {
emit({
type: "system",
subtype: "hook_response",
Expand Down Expand Up @@ -142,6 +142,29 @@ process.stdin.on("data", (chunk) => {
// The SDK opens with a control handshake and will not send a turn until it
// is answered.
if (message.type === "control_request") {
if (
confirmation === "conflicting" &&
process.env.FAKE_CLAUDE_CONFLICT_AT === "context-request" &&
message.request?.subtype === "get_context_usage"
) {
log({ event: "conflict-at-context-request" });
emit({
type: "system",
subtype: "hook_response",
hook_id: "internal",
hook_name: "opaque",
hook_event: "SessionStart",
stdout: "",
stderr: "",
output: "",
exit_code: 0,
outcome: "success",
uuid: crypto.randomUUID(),
session_id: "other-conversation",
});
// Only Query failure releases this lookup, so setup cannot outrun the contradiction.
continue;
}
emit({
type: "control_response",
response: {
Expand Down
6 changes: 3 additions & 3 deletions packages/acp/vendor/adapters/MANIFEST.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@
"tarball": "claude/agentclientprotocol-claude-agent-acp-0.70.0.tgz",
"repository": "https://github.com/agentclientprotocol/claude-agent-acp",
"upstreamBase": "8710ce1cbccf562cb04b4bcc30e053e960aee05f",
"patchedCommit": "54df80cbb41f2aa56195fd56b4dc2a88295049af",
"patchedCommit": "bea799c86d16ed2b1bf1cfe8d7fd6e22e6525b56",
"pullRequest": "https://github.com/agentclientprotocol/claude-agent-acp/pull/1047",
"buildCommand": "npm run build && npm pack",
"license": "Apache-2.0",
"contract": "_meta.claudeCode.assistantMessageUuid",
"materializationContract": "session_info_update _meta[executablemd.session-materialization/v1]",
"byteLength": 195701,
"sha256": "a62d36d02a3e3e5cc896d5467ad8026a59e7d5e5a1940068aebae5f03f469488",
"byteLength": 195823,
"sha256": "6490fb5dccf0273720587bf0a2e2d5940d4afb73119b82011e9cedf6ffe9be8f",
"files": [
"package/LICENSE",
"package/README.md",
Expand Down
7 changes: 5 additions & 2 deletions packages/acp/vendor/adapters/PROVENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,16 @@ owned SDK Query supplies a successful SessionStart lifecycle response with that
exact `session_id`. One fixed no-op command hook, using the adapter's own Node
runtime, is composed into settings before startup or resume. Deliberate settings
and hooks survive; disabled, absent, failed, conflicting or mismatched
confirmation refuses. One owned reader buffers early unrelated messages and
confirmation refuses. The reader records identity refusal before closing its SDK
iterator, whose cleanup can release pending setup lookups. Session publication
rechecks reader failure and disposal after optional SDK setup, before registering
or answering success. One owned reader buffers early unrelated messages and
retains the explicit acceptance and exact completed-prompt checkpoint handling.
Fresh confirmation stays provisional until acceptance and the asserting save.
Native Claude allocation remains client-allocated, independently of metadata.

The Claude source revision recorded in MANIFEST.json extends the retained patch
at `beba04dc177ba09bcc9fa10b56e9ded7f219513e`; its local source patch and build
at `54df80cbb41f2aa56195fd56b4dc2a88295049af`; its local source patch and build
receipt are supplied with the implementation evidence. The pin records source
identity, not an unperformed independent review.

Expand Down
Binary file not shown.
Loading
Loading