@antmanler/claude-code-acp 0.13.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp-agent.js +27 -1
- package/dist/tests/acp-agent.test.js +155 -1
- package/package.json +1 -1
package/dist/acp-agent.js
CHANGED
|
@@ -117,6 +117,18 @@ export class ClaudeAcpAgent {
|
|
|
117
117
|
case "system":
|
|
118
118
|
switch (message.subtype) {
|
|
119
119
|
case "init":
|
|
120
|
+
// Update permission mode from Claude Code's actual configuration
|
|
121
|
+
if (this.sessions[params.sessionId]) {
|
|
122
|
+
this.sessions[params.sessionId].permissionMode = message.permissionMode;
|
|
123
|
+
// Notify client of the actual permission mode
|
|
124
|
+
await this.client.sessionUpdate({
|
|
125
|
+
sessionId: params.sessionId,
|
|
126
|
+
update: {
|
|
127
|
+
sessionUpdate: "current_mode_update",
|
|
128
|
+
currentModeId: message.permissionMode,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
}
|
|
120
132
|
break;
|
|
121
133
|
case "compact_boundary":
|
|
122
134
|
case "hook_response":
|
|
@@ -245,6 +257,14 @@ export class ClaudeAcpAgent {
|
|
|
245
257
|
this.sessions[params.sessionId].permissionMode = params.modeId;
|
|
246
258
|
try {
|
|
247
259
|
await this.sessions[params.sessionId].query.setPermissionMode(params.modeId);
|
|
260
|
+
// Notify client of the mode change
|
|
261
|
+
await this.client.sessionUpdate({
|
|
262
|
+
sessionId: params.sessionId,
|
|
263
|
+
update: {
|
|
264
|
+
sessionUpdate: "current_mode_update",
|
|
265
|
+
currentModeId: params.modeId,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
248
268
|
}
|
|
249
269
|
catch (error) {
|
|
250
270
|
const errorMessage = error instanceof Error && error.message ? error.message : "Invalid Mode";
|
|
@@ -255,6 +275,12 @@ export class ClaudeAcpAgent {
|
|
|
255
275
|
throw new Error("Invalid Mode");
|
|
256
276
|
}
|
|
257
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Get the current permission mode for a session
|
|
280
|
+
*/
|
|
281
|
+
getCurrentPermissionMode(sessionId) {
|
|
282
|
+
return this.sessions[sessionId]?.permissionMode;
|
|
283
|
+
}
|
|
258
284
|
async readTextFile(params) {
|
|
259
285
|
const response = await this.client.readTextFile(params);
|
|
260
286
|
return response;
|
|
@@ -470,7 +496,7 @@ export class ClaudeAcpAgent {
|
|
|
470
496
|
// If we want bypassPermissions to be an option, we have to allow it here.
|
|
471
497
|
// But it doesn't work in root mode, so we only activate it if it will work.
|
|
472
498
|
allowDangerouslySkipPermissions: !IS_ROOT,
|
|
473
|
-
permissionMode,
|
|
499
|
+
// permissionMode,
|
|
474
500
|
canUseTool: this.canUseTool(sessionId),
|
|
475
501
|
// note: although not documented by the types, passing an absolute path
|
|
476
502
|
// here works to find zed's managed node version.
|
|
@@ -3,7 +3,7 @@ import { spawn, spawnSync } from "child_process";
|
|
|
3
3
|
import { ClientSideConnection, ndJsonStream, } from "@agentclientprotocol/sdk";
|
|
4
4
|
import { nodeToWebWritable, nodeToWebReadable } from "../utils.js";
|
|
5
5
|
import { markdownEscape, toolInfoFromToolUse, toolUpdateFromToolResult } from "../tools.js";
|
|
6
|
-
import { toAcpNotifications, promptToClaude } from "../acp-agent.js";
|
|
6
|
+
import { toAcpNotifications, promptToClaude, ClaudeAcpAgent } from "../acp-agent.js";
|
|
7
7
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
8
8
|
import { randomUUID } from "crypto";
|
|
9
9
|
describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("ACP subprocess integration", () => {
|
|
@@ -751,3 +751,157 @@ describe("permission requests", () => {
|
|
|
751
751
|
}
|
|
752
752
|
});
|
|
753
753
|
});
|
|
754
|
+
describe("permission mode management", () => {
|
|
755
|
+
it("should get current permission mode for existing session", () => {
|
|
756
|
+
const agent = new ClaudeAcpAgent({});
|
|
757
|
+
const sessionId = randomUUID();
|
|
758
|
+
// Manually add a session to test
|
|
759
|
+
agent.sessions[sessionId] = {
|
|
760
|
+
permissionMode: "acceptEdits",
|
|
761
|
+
query: {},
|
|
762
|
+
input: {},
|
|
763
|
+
cancelled: false,
|
|
764
|
+
settingsManager: {},
|
|
765
|
+
};
|
|
766
|
+
const mode = agent.getCurrentPermissionMode(sessionId);
|
|
767
|
+
expect(mode).toBe("acceptEdits");
|
|
768
|
+
});
|
|
769
|
+
it("should return undefined for non-existent session", () => {
|
|
770
|
+
const agent = new ClaudeAcpAgent({});
|
|
771
|
+
const nonExistentSessionId = randomUUID();
|
|
772
|
+
const mode = agent.getCurrentPermissionMode(nonExistentSessionId);
|
|
773
|
+
expect(mode).toBeUndefined();
|
|
774
|
+
});
|
|
775
|
+
it("should track all permission modes correctly", () => {
|
|
776
|
+
const agent = new ClaudeAcpAgent({});
|
|
777
|
+
const modes = ["default", "acceptEdits", "bypassPermissions", "dontAsk", "plan"];
|
|
778
|
+
modes.forEach((mode) => {
|
|
779
|
+
const sessionId = randomUUID();
|
|
780
|
+
agent.sessions[sessionId] = {
|
|
781
|
+
permissionMode: mode,
|
|
782
|
+
query: {},
|
|
783
|
+
input: {},
|
|
784
|
+
cancelled: false,
|
|
785
|
+
settingsManager: {},
|
|
786
|
+
};
|
|
787
|
+
expect(agent.getCurrentPermissionMode(sessionId)).toBe(mode);
|
|
788
|
+
});
|
|
789
|
+
});
|
|
790
|
+
it("should sync permission mode from SDK init message", async () => {
|
|
791
|
+
// Track sessionUpdate calls
|
|
792
|
+
const sessionUpdates = [];
|
|
793
|
+
const mockClient = {
|
|
794
|
+
sessionUpdate: async (params) => {
|
|
795
|
+
sessionUpdates.push(params);
|
|
796
|
+
},
|
|
797
|
+
};
|
|
798
|
+
const agent = new ClaudeAcpAgent(mockClient);
|
|
799
|
+
const sessionId = randomUUID();
|
|
800
|
+
// Manually create a session with default mode
|
|
801
|
+
agent.sessions[sessionId] = {
|
|
802
|
+
permissionMode: "default",
|
|
803
|
+
query: {},
|
|
804
|
+
input: {},
|
|
805
|
+
cancelled: false,
|
|
806
|
+
settingsManager: {},
|
|
807
|
+
};
|
|
808
|
+
// Simulate an init message with a different permission mode
|
|
809
|
+
const initMessage = {
|
|
810
|
+
type: "system",
|
|
811
|
+
subtype: "init",
|
|
812
|
+
permissionMode: "bypassPermissions",
|
|
813
|
+
};
|
|
814
|
+
// Manually trigger the logic from the prompt method (case "init")
|
|
815
|
+
if (agent.sessions[sessionId]) {
|
|
816
|
+
agent.sessions[sessionId].permissionMode = initMessage.permissionMode;
|
|
817
|
+
await agent.client.sessionUpdate({
|
|
818
|
+
sessionId: sessionId,
|
|
819
|
+
update: {
|
|
820
|
+
sessionUpdate: "current_mode_update",
|
|
821
|
+
currentModeId: initMessage.permissionMode,
|
|
822
|
+
},
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
// Verify the permission mode was updated
|
|
826
|
+
expect(agent.getCurrentPermissionMode(sessionId)).toBe("bypassPermissions");
|
|
827
|
+
// Verify sessionUpdate was called with correct parameters
|
|
828
|
+
expect(sessionUpdates).toHaveLength(1);
|
|
829
|
+
expect(sessionUpdates[0]).toEqual({
|
|
830
|
+
sessionId: sessionId,
|
|
831
|
+
update: {
|
|
832
|
+
sessionUpdate: "current_mode_update",
|
|
833
|
+
currentModeId: "bypassPermissions",
|
|
834
|
+
},
|
|
835
|
+
});
|
|
836
|
+
});
|
|
837
|
+
it("should notify client when permission mode changes", async () => {
|
|
838
|
+
// Track sessionUpdate calls
|
|
839
|
+
const sessionUpdates = [];
|
|
840
|
+
const mockClient = {
|
|
841
|
+
sessionUpdate: async (params) => {
|
|
842
|
+
sessionUpdates.push(params);
|
|
843
|
+
},
|
|
844
|
+
};
|
|
845
|
+
const agent = new ClaudeAcpAgent(mockClient);
|
|
846
|
+
const sessionId = randomUUID();
|
|
847
|
+
// Manually create a session
|
|
848
|
+
agent.sessions[sessionId] = {
|
|
849
|
+
permissionMode: "default",
|
|
850
|
+
query: {
|
|
851
|
+
setPermissionMode: async (_mode) => {
|
|
852
|
+
// Mock implementation
|
|
853
|
+
},
|
|
854
|
+
},
|
|
855
|
+
input: {},
|
|
856
|
+
cancelled: false,
|
|
857
|
+
settingsManager: {},
|
|
858
|
+
};
|
|
859
|
+
// Call setSessionMode
|
|
860
|
+
await agent.setSessionMode({
|
|
861
|
+
sessionId: sessionId,
|
|
862
|
+
modeId: "acceptEdits",
|
|
863
|
+
});
|
|
864
|
+
// Verify the permission mode was updated
|
|
865
|
+
expect(agent.getCurrentPermissionMode(sessionId)).toBe("acceptEdits");
|
|
866
|
+
// Verify sessionUpdate was called
|
|
867
|
+
expect(sessionUpdates).toHaveLength(1);
|
|
868
|
+
expect(sessionUpdates[0]).toEqual({
|
|
869
|
+
sessionId: sessionId,
|
|
870
|
+
update: {
|
|
871
|
+
sessionUpdate: "current_mode_update",
|
|
872
|
+
currentModeId: "acceptEdits",
|
|
873
|
+
},
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
it("should handle setSessionMode errors without sending update", async () => {
|
|
877
|
+
const sessionUpdates = [];
|
|
878
|
+
const mockClient = {
|
|
879
|
+
sessionUpdate: async (params) => {
|
|
880
|
+
sessionUpdates.push(params);
|
|
881
|
+
},
|
|
882
|
+
};
|
|
883
|
+
const agent = new ClaudeAcpAgent(mockClient);
|
|
884
|
+
const sessionId = randomUUID();
|
|
885
|
+
// Create session with query that throws error
|
|
886
|
+
agent.sessions[sessionId] = {
|
|
887
|
+
permissionMode: "default",
|
|
888
|
+
query: {
|
|
889
|
+
setPermissionMode: async (_mode) => {
|
|
890
|
+
throw new Error("Test error");
|
|
891
|
+
},
|
|
892
|
+
},
|
|
893
|
+
input: {},
|
|
894
|
+
cancelled: false,
|
|
895
|
+
settingsManager: {},
|
|
896
|
+
};
|
|
897
|
+
// Try to change mode - should throw
|
|
898
|
+
await expect(agent.setSessionMode({
|
|
899
|
+
sessionId: sessionId,
|
|
900
|
+
modeId: "acceptEdits",
|
|
901
|
+
})).rejects.toThrow("Test error");
|
|
902
|
+
// Verify no sessionUpdate was sent
|
|
903
|
+
expect(sessionUpdates).toHaveLength(0);
|
|
904
|
+
// Permission mode should still be updated locally (happens before SDK call)
|
|
905
|
+
expect(agent.getCurrentPermissionMode(sessionId)).toBe("acceptEdits");
|
|
906
|
+
});
|
|
907
|
+
});
|