diff --git a/packages/adapters/grok-local/src/index.ts b/packages/adapters/grok-local/src/index.ts index 9e50feed8f..492464edd9 100644 --- a/packages/adapters/grok-local/src/index.ts +++ b/packages/adapters/grok-local/src/index.ts @@ -26,7 +26,7 @@ Core fields: - instructionsFilePath (string, optional): absolute path to a markdown instructions file. Paperclip stages it into the execution workspace as \`Agents.md\` when safe, otherwise falls back to \`--rules @file\` - promptTemplate (string, optional): run prompt template - model (string, optional): Grok model id. Defaults to grok-build. -- permissionMode (string, optional): Grok permission mode. Defaults to \`dontAsk\` +- permissionMode (string, optional): Grok permission mode passed via \`--permission-mode\`. Unset by default: Grok >= 1.0 enforces \`dontAsk\` as deny-by-default and it overrides \`--always-approve\`, so unattended runs rely on \`--always-approve\` alone unless you explicitly need a mode - reasoningEffort (string, optional): Grok reasoning effort passed via \`--reasoning-effort\` - maxTurns (number, optional): maximum agent turns for the run - command (string, optional): defaults to "grok" diff --git a/packages/adapters/grok-local/src/server/execute.test.ts b/packages/adapters/grok-local/src/server/execute.test.ts index 2b03c7dcbd..46899fec54 100644 --- a/packages/adapters/grok-local/src/server/execute.test.ts +++ b/packages/adapters/grok-local/src/server/execute.test.ts @@ -71,10 +71,11 @@ describe("grok_local execute", () => { "--output-format", "streaming-json", "--always-approve", - "--permission-mode", - "dontAsk", ]), ); + // Grok >= 1.0 enforces `dontAsk` as deny-by-default over --always-approve, + // so no permission mode may be passed unless explicitly configured. + expect(args).not.toContain("--permission-mode"); expect(await fs.readFile(path.join(root, "Agents.md"), "utf8")).toContain("You are Grok."); expect(await pathExists(path.join(root, ".claude", "skills", "paperclip", "SKILL.md"))).toBe(true); await options.onLog?.("stdout", '{"type":"text","data":"done"}\n'); @@ -203,6 +204,42 @@ describe("grok_local execute", () => { } }); + it("passes an explicitly configured permissionMode through to the CLI", async () => { + let seenArgs: string[] = []; + runProcessMock.mockImplementation(async (_runId, _target, _command, args) => { + seenArgs = args; + return { + exitCode: 0, + signal: null, + timedOut: false, + stdout: JSON.stringify({ type: "end", stopReason: "EndTurn", sessionId: "sess-1", requestId: "req-1" }), + stderr: "", + }; + }); + + const ctx: AdapterExecutionContext = { + runId: "run-permission-mode", + agent: { + id: "agent-1", + companyId: "company-1", + name: "Grok Agent", + adapterType: "grok_local", + adapterConfig: {}, + }, + runtime: { sessionId: null, sessionParams: null, sessionDisplayId: null, taskKey: null }, + config: { cwd: await makeTempRoot(), permissionMode: "bypassPermissions" }, + context: {}, + authToken: "run-token", + onLog: async () => {}, + }; + + await execute(ctx); + + const flagIndex = seenArgs.indexOf("--permission-mode"); + expect(flagIndex).toBeGreaterThan(-1); + expect(seenArgs[flagIndex + 1]).toBe("bypassPermissions"); + }); + it("cleans up staged assets when setup fails before the Grok process starts", async () => { const root = await makeTempRoot(); const instructionsPath = path.join(root, "managed", "AGENTS.md"); diff --git a/packages/adapters/grok-local/src/server/execute.ts b/packages/adapters/grok-local/src/server/execute.ts index e82ff1cec6..72ab25d6b0 100644 --- a/packages/adapters/grok-local/src/server/execute.ts +++ b/packages/adapters/grok-local/src/server/execute.ts @@ -203,7 +203,11 @@ export async function execute(ctx: AdapterExecutionContext): Promise= 1.0 enforces `dontAsk` as + // deny-by-default and it overrides --always-approve, so passing it broke + // every unattended run (the first tool call died with "User cancelled the + // execution for tool ..."). --always-approve alone is the unattended policy. + const permissionMode = asString(config.permissionMode, "").trim(); const reasoningEffort = asString(config.reasoningEffort, "").trim(); const maxTurns = asNumber(config.maxTurns, 0); const alwaysApprove = asBoolean(config.alwaysApprove, true);