Skip to content
Closed
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
2 changes: 1 addition & 1 deletion packages/squad-cli/src/cli/shell/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const storage = new FSStorageProvider();
* Approve all permission requests. CLI runs locally with user trust,
* so no interactive confirmation is needed.
*/
const approveAllPermissions: SquadPermissionHandler = () => ({ kind: 'approved' });
const approveAllPermissions: SquadPermissionHandler = () => ({ kind: 'approve-once' });

/** Debug logger — writes to stderr only when SQUAD_DEBUG=1. */
function debugLog(...args: unknown[]): void {
Expand Down
2 changes: 1 addition & 1 deletion packages/squad-sdk/src/adapter/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ export class SquadClient {
if (msg.includes('onPermissionRequest')) {
throw new Error(
'Session creation failed: an onPermissionRequest handler is required. ' +
'Pass { onPermissionRequest: () => ({ kind: "approved" }) } in your session config ' +
'Pass { onPermissionRequest: () => ({ kind: "approve-once" }) } in your session config ' +
'to approve all permissions, or provide a custom handler.'
);
}
Expand Down
22 changes: 11 additions & 11 deletions packages/squad-sdk/src/adapter/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,17 +588,17 @@ export interface SquadPermissionRequest {

/**
* Result of a permission request.
*/
export interface SquadPermissionRequestResult {
/** Outcome of the permission request */
kind:
| "approved"
| "denied-by-rules"
| "denied-no-approval-rule-and-could-not-request-from-user"
| "denied-interactively-by-user";
/** Rules that influenced the decision */
rules?: unknown[];
}
*
* Mirrors Copilot SDK PermissionDecision semantics (v1.0.54+).
*/
export type SquadPermissionRequestResult =
| { kind: "approve-once" }
| { kind: "approve-for-session"; approval?: unknown; domain?: string }
| { kind: "approve-for-location"; approval: unknown; locationKey: string }
| { kind: "approve-permanently"; domain: string }
| { kind: "reject"; feedback?: string }
| { kind: "user-not-available" }
| { kind: "no-result" };

/**
* Handler for permission requests from the agent.
Expand Down
12 changes: 12 additions & 0 deletions test/adapter-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,18 @@ describe('SquadClient — Auto-Reconnection', () => {
await expect(client.createSession()).rejects.toThrow('ECONNREFUSED');
});

it('should provide approve-once guidance for permission handler errors', async () => {
const client = new SquadClient({ autoReconnect: false });
await client.connect();

const MockedCopilotClient = CopilotClient as unknown as ReturnType<typeof vi.fn>;
const instance = MockedCopilotClient.mock.results[0].value;

instance.createSession.mockRejectedValue(new Error('onPermissionRequest is required'));

await expect(client.createSession()).rejects.toThrow('kind: "approve-once"');
});

it('should not auto-reconnect after manual disconnect', async () => {
const client = new SquadClient({ autoReconnect: true, autoStart: false });
await client.connect();
Expand Down