@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 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
+ });
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.13.0",
6
+ "version": "0.13.2",
7
7
  "description": "An ACP-compatible coding agent powered by the Claude Code SDK (TypeScript)",
8
8
  "main": "dist/lib.js",
9
9
  "bin": {