mirror of
https://github.com/getpaseo/paseo.git
synced 2026-08-15 04:42:45 +00:00
Add OpenCode auto accept feature
This commit is contained in:
@@ -27,6 +27,7 @@ interface ProviderLaunchConfig {
|
|||||||
model?: string;
|
model?: string;
|
||||||
thinkingOptionId?: string;
|
thinkingOptionId?: string;
|
||||||
modeId?: string;
|
modeId?: string;
|
||||||
|
featureValues?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SEND_TIMEOUT_MS = 240_000;
|
const SEND_TIMEOUT_MS = 240_000;
|
||||||
@@ -52,7 +53,12 @@ function fullAccessConfig(provider: RewindFlowProvider): ProviderLaunchConfig {
|
|||||||
modeId: "full-access",
|
modeId: "full-access",
|
||||||
};
|
};
|
||||||
case "opencode":
|
case "opencode":
|
||||||
return { provider, model: "opencode/big-pickle", modeId: "full-access" };
|
return {
|
||||||
|
provider,
|
||||||
|
model: "opencode/big-pickle",
|
||||||
|
modeId: "build",
|
||||||
|
featureValues: { auto_accept: true },
|
||||||
|
};
|
||||||
case "pi":
|
case "pi":
|
||||||
return {
|
return {
|
||||||
provider,
|
provider,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
||||||
import { useShallow } from "zustand/shallow";
|
import { useShallow } from "zustand/shallow";
|
||||||
import { Brain, ChevronDown, ListTodo, Settings2, Zap } from "lucide-react-native";
|
import { Brain, ChevronDown, ListTodo, Settings2, ShieldCheck, Zap } from "lucide-react-native";
|
||||||
import { getProviderIcon } from "@/components/provider-icons";
|
import { getProviderIcon } from "@/components/provider-icons";
|
||||||
import { CombinedModelSelector } from "@/components/combined-model-selector";
|
import { CombinedModelSelector } from "@/components/combined-model-selector";
|
||||||
import {
|
import {
|
||||||
@@ -139,6 +139,7 @@ function findOptionLabel(
|
|||||||
|
|
||||||
const FEATURE_ICONS: Record<string, typeof Zap> = {
|
const FEATURE_ICONS: Record<string, typeof Zap> = {
|
||||||
"list-todo": ListTodo,
|
"list-todo": ListTodo,
|
||||||
|
"shield-check": ShieldCheck,
|
||||||
zap: Zap,
|
zap: Zap,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -151,6 +152,7 @@ function getFeatureIconColor(
|
|||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
palette: {
|
palette: {
|
||||||
blue: { 400: string };
|
blue: { 400: string };
|
||||||
|
green: { 400: string };
|
||||||
yellow: { 400: string };
|
yellow: { 400: string };
|
||||||
},
|
},
|
||||||
foregroundMuted: string,
|
foregroundMuted: string,
|
||||||
@@ -162,6 +164,8 @@ function getFeatureIconColor(
|
|||||||
switch (getFeatureHighlightColor(featureId)) {
|
switch (getFeatureHighlightColor(featureId)) {
|
||||||
case "blue":
|
case "blue":
|
||||||
return palette.blue[400];
|
return palette.blue[400];
|
||||||
|
case "green":
|
||||||
|
return palette.green[400];
|
||||||
case "yellow":
|
case "yellow":
|
||||||
return palette.yellow[400];
|
return palette.yellow[400];
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { StyleSheet, useUnistyles } from "react-native-unistyles";
|
|||||||
import { useShallow } from "zustand/shallow";
|
import { useShallow } from "zustand/shallow";
|
||||||
import { useStoreWithEqualityFn } from "zustand/traditional";
|
import { useStoreWithEqualityFn } from "zustand/traditional";
|
||||||
import {
|
import {
|
||||||
|
Bot,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ShieldAlert,
|
ShieldAlert,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
@@ -33,6 +34,7 @@ function shouldRenderForPlacement(placement: AgentModeControlPlacement, isCompac
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MODE_ICONS = {
|
const MODE_ICONS = {
|
||||||
|
Bot,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
ShieldAlert,
|
ShieldAlert,
|
||||||
ShieldOff,
|
ShieldOff,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { AgentFeature, AgentModelDefinition } from "@server/server/agent/agent-sdk-types";
|
import type { AgentFeature, AgentModelDefinition } from "@server/server/agent/agent-sdk-types";
|
||||||
|
|
||||||
export type ExplainedAgentControl = "mode" | "model" | "thinking";
|
export type ExplainedAgentControl = "mode" | "model" | "thinking";
|
||||||
export type FeatureHighlightColor = "blue" | "default" | "yellow";
|
export type FeatureHighlightColor = "blue" | "default" | "green" | "yellow";
|
||||||
|
|
||||||
export function getAgentControlHint(selector: ExplainedAgentControl): string {
|
export function getAgentControlHint(selector: ExplainedAgentControl): string {
|
||||||
switch (selector) {
|
switch (selector) {
|
||||||
@@ -32,6 +32,8 @@ export function getFeatureHighlightColor(featureId: string): FeatureHighlightCol
|
|||||||
switch (featureId) {
|
switch (featureId) {
|
||||||
case "fast_mode":
|
case "fast_mode":
|
||||||
return "yellow";
|
return "yellow";
|
||||||
|
case "auto_accept":
|
||||||
|
return "green";
|
||||||
case "plan_mode":
|
case "plan_mode":
|
||||||
return "blue";
|
return "blue";
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1156,11 +1156,12 @@ export class AgentManager {
|
|||||||
async setAgentMode(agentId: string, modeId: string): Promise<void> {
|
async setAgentMode(agentId: string, modeId: string): Promise<void> {
|
||||||
const agent = this.requireSessionAgent(agentId);
|
const agent = this.requireSessionAgent(agentId);
|
||||||
await agent.session.setMode(modeId);
|
await agent.session.setMode(modeId);
|
||||||
agent.config.modeId = modeId;
|
const currentMode = await agent.session.getCurrentMode();
|
||||||
agent.currentModeId = modeId;
|
agent.config.modeId = currentMode ?? undefined;
|
||||||
|
agent.currentModeId = currentMode;
|
||||||
// Update runtimeInfo to reflect the new mode
|
// Update runtimeInfo to reflect the new mode
|
||||||
if (agent.runtimeInfo) {
|
if (agent.runtimeInfo) {
|
||||||
agent.runtimeInfo = { ...agent.runtimeInfo, modeId };
|
agent.runtimeInfo = { ...agent.runtimeInfo, modeId: currentMode };
|
||||||
}
|
}
|
||||||
this.touchUpdatedAt(agent);
|
this.touchUpdatedAt(agent);
|
||||||
this.emitState(agent);
|
this.emitState(agent);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
|||||||
import { resolveAndValidateCreateAgentMode } from "./create-agent-mode.js";
|
import { resolveAndValidateCreateAgentMode } from "./create-agent-mode.js";
|
||||||
|
|
||||||
const CLAUDE_MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
|
const CLAUDE_MODES = ["default", "acceptEdits", "plan", "bypassPermissions"];
|
||||||
const OPENCODE_MODES = ["build", "full-access", "plan"];
|
const OPENCODE_MODES = ["build", "plan"];
|
||||||
const CODEX_MODES = ["auto", "full-access"];
|
const CODEX_MODES = ["auto", "full-access"];
|
||||||
|
|
||||||
describe("resolveAndValidateCreateAgentMode", () => {
|
describe("resolveAndValidateCreateAgentMode", () => {
|
||||||
@@ -25,7 +25,7 @@ describe("resolveAndValidateCreateAgentMode", () => {
|
|||||||
availableModes: OPENCODE_MODES,
|
availableModes: OPENCODE_MODES,
|
||||||
}),
|
}),
|
||||||
).toThrow(
|
).toThrow(
|
||||||
"Invalid mode 'bypassPermissions' for provider 'opencode'. Available modes: build, full-access, plan",
|
"Invalid mode 'bypassPermissions' for provider 'opencode'. Available modes: build, plan",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ describe("resolveAndValidateCreateAgentMode", () => {
|
|||||||
availableModes: OPENCODE_MODES,
|
availableModes: OPENCODE_MODES,
|
||||||
}),
|
}),
|
||||||
).toThrow(
|
).toThrow(
|
||||||
"cannot inherit mode 'bypassPermissions' from caller (provider 'claude') for new agent (provider 'opencode'). Pass an explicit mode. Available modes for 'opencode': build, full-access, plan",
|
"cannot inherit mode 'bypassPermissions' from caller (provider 'claude') for new agent (provider 'opencode'). Pass an explicit mode. Available modes for 'opencode': build, plan",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1447,11 +1447,39 @@ describe("create_agent MCP tool", () => {
|
|||||||
initialPrompt: "Do work",
|
initialPrompt: "Do work",
|
||||||
}),
|
}),
|
||||||
).rejects.toThrow(
|
).rejects.toThrow(
|
||||||
"Invalid mode 'bypassPermissions' for provider 'opencode'. Available modes: build, full-access, plan",
|
"Invalid mode 'bypassPermissions' for provider 'opencode'. Available modes: build, plan",
|
||||||
);
|
);
|
||||||
expect(spies.agentManager.createAgent).not.toHaveBeenCalled();
|
expect(spies.agentManager.createAgent).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("accepts legacy OpenCode full-access as build plus auto accept", async () => {
|
||||||
|
const { agentManager, agentStorage, spies } = createTestDeps();
|
||||||
|
spies.agentManager.createAgent.mockResolvedValue({
|
||||||
|
id: "child-agent",
|
||||||
|
cwd: existingCwd,
|
||||||
|
lifecycle: "idle",
|
||||||
|
currentModeId: "build",
|
||||||
|
availableModes: [],
|
||||||
|
config: { title: "Child", featureValues: { auto_accept: true } },
|
||||||
|
} as ManagedAgent);
|
||||||
|
const server = await createAgentMcpServer({ agentManager, agentStorage, logger });
|
||||||
|
const tool = registeredTool(server, "create_agent");
|
||||||
|
|
||||||
|
await tool.handler({
|
||||||
|
cwd: existingCwd,
|
||||||
|
title: "Legacy mode",
|
||||||
|
provider: "opencode/gpt-5.4",
|
||||||
|
settings: { modeId: "full-access" },
|
||||||
|
initialPrompt: "Do work",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(spies.agentManager.createAgent).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({ modeId: "build", featureValues: { auto_accept: true } }),
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("inherits the caller mode when the new agent uses the same provider", async () => {
|
it("inherits the caller mode when the new agent uses the same provider", async () => {
|
||||||
const { agentManager, agentStorage, spies } = createTestDeps();
|
const { agentManager, agentStorage, spies } = createTestDeps();
|
||||||
spies.agentManager.getAgent.mockReturnValue({
|
spies.agentManager.getAgent.mockReturnValue({
|
||||||
@@ -1513,12 +1541,12 @@ describe("create_agent MCP tool", () => {
|
|||||||
initialPrompt: "Do work",
|
initialPrompt: "Do work",
|
||||||
}),
|
}),
|
||||||
).rejects.toThrow(
|
).rejects.toThrow(
|
||||||
"cannot inherit mode 'default' from caller (provider 'claude') for new agent (provider 'opencode'). Pass an explicit mode. Available modes for 'opencode': build, full-access, plan",
|
"cannot inherit mode 'default' from caller (provider 'claude') for new agent (provider 'opencode'). Pass an explicit mode. Available modes for 'opencode': build, plan",
|
||||||
);
|
);
|
||||||
expect(spies.agentManager.createAgent).not.toHaveBeenCalled();
|
expect(spies.agentManager.createAgent).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("inherits the target provider's unattended mode when caller is unattended cross-provider", async () => {
|
it("maps unattended callers to OpenCode auto accept", async () => {
|
||||||
const { agentManager, agentStorage, spies } = createTestDeps();
|
const { agentManager, agentStorage, spies } = createTestDeps();
|
||||||
spies.agentManager.getAgent.mockReturnValue({
|
spies.agentManager.getAgent.mockReturnValue({
|
||||||
id: "parent-agent",
|
id: "parent-agent",
|
||||||
@@ -1530,9 +1558,9 @@ describe("create_agent MCP tool", () => {
|
|||||||
id: "child-agent",
|
id: "child-agent",
|
||||||
cwd: existingCwd,
|
cwd: existingCwd,
|
||||||
lifecycle: "idle",
|
lifecycle: "idle",
|
||||||
currentModeId: "full-access",
|
currentModeId: "build",
|
||||||
availableModes: [],
|
availableModes: [],
|
||||||
config: { title: "Child" },
|
config: { title: "Child", featureValues: { auto_accept: true } },
|
||||||
} as ManagedAgent);
|
} as ManagedAgent);
|
||||||
|
|
||||||
const server = await createAgentMcpServer({
|
const server = await createAgentMcpServer({
|
||||||
@@ -1549,7 +1577,7 @@ describe("create_agent MCP tool", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(spies.agentManager.createAgent).toHaveBeenCalledWith(
|
expect(spies.agentManager.createAgent).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({ modeId: "full-access" }),
|
expect.objectContaining({ modeId: "build", featureValues: { auto_accept: true } }),
|
||||||
undefined,
|
undefined,
|
||||||
expect.any(Object),
|
expect.any(Object),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type {
|
|||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
import type { AgentProvider } from "./agent-sdk-types.js";
|
import type { AgentProvider } from "./agent-sdk-types.js";
|
||||||
import type { AgentManager, WaitForAgentResult } from "./agent-manager.js";
|
import type { AgentManager, ManagedAgent, WaitForAgentResult } from "./agent-manager.js";
|
||||||
import {
|
import {
|
||||||
AgentFeatureSchema,
|
AgentFeatureSchema,
|
||||||
AgentPermissionRequestPayloadSchema,
|
AgentPermissionRequestPayloadSchema,
|
||||||
@@ -134,6 +134,42 @@ const CODEX_TO_CLAUDE_MODE: Record<string, string> = {
|
|||||||
"full-access": "bypassPermissions",
|
"full-access": "bypassPermissions",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const OPENCODE_PROVIDER_ID = "opencode";
|
||||||
|
const OPENCODE_BUILD_MODE_ID = "build";
|
||||||
|
const OPENCODE_LEGACY_FULL_ACCESS_MODE_ID = "full-access";
|
||||||
|
const OPENCODE_AUTO_ACCEPT_FEATURE_ID = "auto_accept";
|
||||||
|
|
||||||
|
function isOpenCodeLegacyFullAccessMode(
|
||||||
|
provider: AgentProvider,
|
||||||
|
modeId: string | undefined,
|
||||||
|
): boolean {
|
||||||
|
return provider === OPENCODE_PROVIDER_ID && modeId === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function withOpenCodeAutoAcceptFeature(
|
||||||
|
features: Record<string, unknown> | undefined,
|
||||||
|
enabled: boolean,
|
||||||
|
): Record<string, unknown> {
|
||||||
|
return {
|
||||||
|
...features,
|
||||||
|
[OPENCODE_AUTO_ACCEPT_FEATURE_ID]: enabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasOpenCodeAutoAcceptFeature(agent: ManagedAgent): boolean {
|
||||||
|
if (agent.provider !== OPENCODE_PROVIDER_ID) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
agent.features?.some(
|
||||||
|
(feature) =>
|
||||||
|
feature.id === OPENCODE_AUTO_ACCEPT_FEATURE_ID &&
|
||||||
|
feature.type === "toggle" &&
|
||||||
|
feature.value === true,
|
||||||
|
) === true || agent.config.featureValues?.[OPENCODE_AUTO_ACCEPT_FEATURE_ID] === true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function mapModeAcrossProviders(
|
function mapModeAcrossProviders(
|
||||||
sourceMode: string,
|
sourceMode: string,
|
||||||
sourceProvider: AgentProvider,
|
sourceProvider: AgentProvider,
|
||||||
@@ -583,6 +619,9 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
: {}),
|
: {}),
|
||||||
...(callerAgent.config.title ? { title: callerAgent.config.title } : {}),
|
...(callerAgent.config.title ? { title: callerAgent.config.title } : {}),
|
||||||
...(callerAgent.config.extra ? { extra: callerAgent.config.extra } : {}),
|
...(callerAgent.config.extra ? { extra: callerAgent.config.extra } : {}),
|
||||||
|
...(callerAgent.config.featureValues
|
||||||
|
? { featureValues: callerAgent.config.featureValues }
|
||||||
|
: {}),
|
||||||
...(callerAgent.config.systemPrompt ? { systemPrompt: callerAgent.config.systemPrompt } : {}),
|
...(callerAgent.config.systemPrompt ? { systemPrompt: callerAgent.config.systemPrompt } : {}),
|
||||||
...(callerAgent.config.mcpServers ? { mcpServers: callerAgent.config.mcpServers } : {}),
|
...(callerAgent.config.mcpServers ? { mcpServers: callerAgent.config.mcpServers } : {}),
|
||||||
};
|
};
|
||||||
@@ -910,6 +949,48 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
return modes.some((mode) => mode.id === modeId && mode.isUnattended === true);
|
return modes.some((mode) => mode.id === modeId && mode.isUnattended === true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isAgentInUnattendedState = (agent: ManagedAgent): boolean => {
|
||||||
|
return (
|
||||||
|
isParentInUnattendedMode(agent.provider, agent.currentModeId) ||
|
||||||
|
hasOpenCodeAutoAcceptFeature(agent)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveCreateModeAndFeatures = (input: {
|
||||||
|
provider: AgentProvider;
|
||||||
|
requestedMode: string | undefined;
|
||||||
|
parent: { provider: AgentProvider; modeId: string | null; isUnattended: boolean } | null;
|
||||||
|
features: Record<string, unknown> | undefined;
|
||||||
|
}): { mode: string | undefined; features: Record<string, unknown> | undefined } => {
|
||||||
|
const legacyOpenCodeFullAccess = isOpenCodeLegacyFullAccessMode(
|
||||||
|
input.provider,
|
||||||
|
input.requestedMode,
|
||||||
|
);
|
||||||
|
const inheritsOpenCodeUnattended =
|
||||||
|
input.provider === OPENCODE_PROVIDER_ID &&
|
||||||
|
input.requestedMode === undefined &&
|
||||||
|
input.parent?.isUnattended === true;
|
||||||
|
const inheritsOpenCodeAutoAccept =
|
||||||
|
inheritsOpenCodeUnattended && input.features?.[OPENCODE_AUTO_ACCEPT_FEATURE_ID] === undefined;
|
||||||
|
const requestedMode = legacyOpenCodeFullAccess ? OPENCODE_BUILD_MODE_ID : input.requestedMode;
|
||||||
|
const features =
|
||||||
|
legacyOpenCodeFullAccess || inheritsOpenCodeAutoAccept
|
||||||
|
? withOpenCodeAutoAcceptFeature(input.features, true)
|
||||||
|
: input.features;
|
||||||
|
const mode =
|
||||||
|
inheritsOpenCodeUnattended && requestedMode === undefined
|
||||||
|
? OPENCODE_BUILD_MODE_ID
|
||||||
|
: resolveAndValidateCreateAgentMode({
|
||||||
|
requestedMode,
|
||||||
|
targetProvider: input.provider,
|
||||||
|
parent: input.parent,
|
||||||
|
availableModes: getAvailableModeIds(input.provider),
|
||||||
|
targetUnattendedMode: getUnattendedModeId(input.provider),
|
||||||
|
});
|
||||||
|
|
||||||
|
return { mode, features };
|
||||||
|
};
|
||||||
|
|
||||||
const resolveCallerCreateAgentArgs = (
|
const resolveCallerCreateAgentArgs = (
|
||||||
args: unknown,
|
args: unknown,
|
||||||
parentAgentId: string,
|
parentAgentId: string,
|
||||||
@@ -928,16 +1009,15 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
lockedCwd: callerContext?.lockedCwd,
|
lockedCwd: callerContext?.lockedCwd,
|
||||||
allowCustomCwd: callerContext?.allowCustomCwd ?? true,
|
allowCustomCwd: callerContext?.allowCustomCwd ?? true,
|
||||||
});
|
});
|
||||||
const resolvedMode = resolveAndValidateCreateAgentMode({
|
const resolvedRuntime = resolveCreateModeAndFeatures({
|
||||||
|
provider,
|
||||||
requestedMode: settings?.modeId,
|
requestedMode: settings?.modeId,
|
||||||
targetProvider: provider,
|
|
||||||
parent: {
|
parent: {
|
||||||
provider: parentAgent.provider,
|
provider: parentAgent.provider,
|
||||||
modeId: parentAgent.currentModeId,
|
modeId: parentAgent.currentModeId,
|
||||||
isUnattended: isParentInUnattendedMode(parentAgent.provider, parentAgent.currentModeId),
|
isUnattended: isAgentInUnattendedState(parentAgent),
|
||||||
},
|
},
|
||||||
availableModes: getAvailableModeIds(provider),
|
features: settings?.features,
|
||||||
targetUnattendedMode: getUnattendedModeId(provider),
|
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
provider,
|
provider,
|
||||||
@@ -946,11 +1026,11 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
normalizedTitle: callerArgs.title.trim(),
|
normalizedTitle: callerArgs.title.trim(),
|
||||||
model: resolvedProviderModel.model,
|
model: resolvedProviderModel.model,
|
||||||
thinkingOptionId: settings?.thinkingOptionId,
|
thinkingOptionId: settings?.thinkingOptionId,
|
||||||
features: settings?.features,
|
features: resolvedRuntime.features,
|
||||||
labels: callerArgs.labels,
|
labels: callerArgs.labels,
|
||||||
notifyOnFinish: callerArgs.notifyOnFinish ?? false,
|
notifyOnFinish: callerArgs.notifyOnFinish ?? false,
|
||||||
resolvedCwd,
|
resolvedCwd,
|
||||||
resolvedMode,
|
resolvedMode: resolvedRuntime.mode,
|
||||||
setupContinuation: undefined,
|
setupContinuation: undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -962,12 +1042,11 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
const resolvedProviderModel = resolveRequiredProviderModel(topLevelArgs.provider);
|
const resolvedProviderModel = resolveRequiredProviderModel(topLevelArgs.provider);
|
||||||
const { cwd, settings, worktreeName, baseBranch, refName, action, githubPrNumber } =
|
const { cwd, settings, worktreeName, baseBranch, refName, action, githubPrNumber } =
|
||||||
topLevelArgs;
|
topLevelArgs;
|
||||||
const resolvedMode = resolveAndValidateCreateAgentMode({
|
const resolvedRuntime = resolveCreateModeAndFeatures({
|
||||||
|
provider: resolvedProviderModel.provider,
|
||||||
requestedMode: settings?.modeId,
|
requestedMode: settings?.modeId,
|
||||||
targetProvider: resolvedProviderModel.provider,
|
|
||||||
parent: null,
|
parent: null,
|
||||||
availableModes: getAvailableModeIds(resolvedProviderModel.provider),
|
features: settings?.features,
|
||||||
targetUnattendedMode: getUnattendedModeId(resolvedProviderModel.provider),
|
|
||||||
});
|
});
|
||||||
let resolvedCwd = expandUserPath(cwd);
|
let resolvedCwd = expandUserPath(cwd);
|
||||||
let setupContinuation: AgentWorktreeSetupContinuation | undefined;
|
let setupContinuation: AgentWorktreeSetupContinuation | undefined;
|
||||||
@@ -1021,11 +1100,11 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
normalizedTitle: topLevelArgs.title.trim(),
|
normalizedTitle: topLevelArgs.title.trim(),
|
||||||
model: resolvedProviderModel.model,
|
model: resolvedProviderModel.model,
|
||||||
thinkingOptionId: settings?.thinkingOptionId,
|
thinkingOptionId: settings?.thinkingOptionId,
|
||||||
features: settings?.features,
|
features: resolvedRuntime.features,
|
||||||
labels: topLevelArgs.labels,
|
labels: topLevelArgs.labels,
|
||||||
notifyOnFinish: topLevelArgs.notifyOnFinish ?? false,
|
notifyOnFinish: topLevelArgs.notifyOnFinish ?? false,
|
||||||
resolvedCwd,
|
resolvedCwd,
|
||||||
resolvedMode,
|
resolvedMode: resolvedRuntime.mode,
|
||||||
setupContinuation,
|
setupContinuation,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,12 @@ import { z } from "zod";
|
|||||||
import type { AgentMode } from "./agent-sdk-types.js";
|
import type { AgentMode } from "./agent-sdk-types.js";
|
||||||
|
|
||||||
export type AgentModeColorTier = "safe" | "moderate" | "dangerous" | "planning" | `#${string}`;
|
export type AgentModeColorTier = "safe" | "moderate" | "dangerous" | "planning" | `#${string}`;
|
||||||
export type AgentModeIcon = "ShieldCheck" | "ShieldAlert" | "ShieldOff" | "ShieldQuestionMark";
|
export type AgentModeIcon =
|
||||||
|
| "Bot"
|
||||||
|
| "ShieldCheck"
|
||||||
|
| "ShieldAlert"
|
||||||
|
| "ShieldOff"
|
||||||
|
| "ShieldQuestionMark";
|
||||||
|
|
||||||
export interface AgentModeVisuals {
|
export interface AgentModeVisuals {
|
||||||
icon: AgentModeIcon;
|
icon: AgentModeIcon;
|
||||||
@@ -126,22 +131,14 @@ const OPENCODE_MODES: AgentProviderModeDefinition[] = [
|
|||||||
id: "build",
|
id: "build",
|
||||||
label: "Build",
|
label: "Build",
|
||||||
description: "Allows edits and tool execution for implementation work",
|
description: "Allows edits and tool execution for implementation work",
|
||||||
icon: "ShieldCheck",
|
icon: "Bot",
|
||||||
colorTier: "moderate",
|
colorTier: "moderate",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "full-access",
|
|
||||||
label: "Full Access",
|
|
||||||
description: "Automatically approves all tool permission prompts for the session",
|
|
||||||
icon: "ShieldAlert",
|
|
||||||
colorTier: "dangerous",
|
|
||||||
isUnattended: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "plan",
|
id: "plan",
|
||||||
label: "Plan",
|
label: "Plan",
|
||||||
description: "Read-only planning mode that avoids file edits",
|
description: "Read-only planning mode that avoids file edits",
|
||||||
icon: "ShieldCheck",
|
icon: "Bot",
|
||||||
colorTier: "planning",
|
colorTier: "planning",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ function questionEvent(): unknown {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("OpenCode full-access mode", () => {
|
describe("OpenCode auto_accept feature", () => {
|
||||||
test("includes virtual full-access mode with dynamic OpenCode agents", async () => {
|
test("lists OpenCode modes without the legacy virtual full-access mode", async () => {
|
||||||
const { runtime } = mockOpenCodeClient({
|
const { runtime } = mockOpenCodeClient({
|
||||||
agents: [
|
agents: [
|
||||||
{ name: "build", mode: "primary", hidden: false, description: "Build agent" },
|
{ name: "build", mode: "primary", hidden: false, description: "Build agent" },
|
||||||
@@ -73,14 +73,36 @@ describe("OpenCode full-access mode", () => {
|
|||||||
const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime });
|
const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime });
|
||||||
const modes = await client.listModes({ cwd: "/tmp/project", force: false });
|
const modes = await client.listModes({ cwd: "/tmp/project", force: false });
|
||||||
|
|
||||||
expect(modes.map((mode) => mode.id)).toEqual(["build", "plan", "full-access", "paseo-custom"]);
|
expect(modes.map((mode) => mode.id)).toEqual(["build", "plan", "paseo-custom"]);
|
||||||
expect(modes.find((mode) => mode.id === "full-access")).toMatchObject({
|
|
||||||
label: "Full Access",
|
|
||||||
description: "Automatically approves all tool permission prompts for the session",
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("reports full-access but sends prompts through OpenCode build agent", async () => {
|
test("lists auto accept as a provider feature", async () => {
|
||||||
|
const { runtime } = mockOpenCodeClient();
|
||||||
|
|
||||||
|
const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime });
|
||||||
|
const enabledFeatures = await client.listFeatures({
|
||||||
|
provider: "opencode",
|
||||||
|
cwd: "/tmp/project",
|
||||||
|
featureValues: { auto_accept: true },
|
||||||
|
});
|
||||||
|
const legacyFeatures = await client.listFeatures({
|
||||||
|
provider: "opencode",
|
||||||
|
cwd: "/tmp/project",
|
||||||
|
modeId: "full-access",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(enabledFeatures).toEqual([
|
||||||
|
expect.objectContaining({
|
||||||
|
type: "toggle",
|
||||||
|
id: "auto_accept",
|
||||||
|
label: "Auto Accept",
|
||||||
|
value: true,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
expect(legacyFeatures).toEqual([expect.objectContaining({ id: "auto_accept", value: true })]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("keeps legacy full-access as an alias for build plus auto accept", async () => {
|
||||||
const { openCodeClient, runtime } = mockOpenCodeClient();
|
const { openCodeClient, runtime } = mockOpenCodeClient();
|
||||||
|
|
||||||
const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime });
|
const client = new OpenCodeAgentClient(createTestLogger(), undefined, { runtime });
|
||||||
@@ -90,7 +112,8 @@ describe("OpenCode full-access mode", () => {
|
|||||||
modeId: "full-access",
|
modeId: "full-access",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(await session.getCurrentMode()).toBe("full-access");
|
expect(await session.getCurrentMode()).toBe("build");
|
||||||
|
expect(session.features).toEqual([expect.objectContaining({ id: "auto_accept", value: true })]);
|
||||||
|
|
||||||
await session.run("Implement the change");
|
await session.run("Implement the change");
|
||||||
|
|
||||||
@@ -102,7 +125,7 @@ describe("OpenCode full-access mode", () => {
|
|||||||
await session.close();
|
await session.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("auto-approves tool permissions in full-access without surfacing them", async () => {
|
test("auto-approves tool permissions when auto accept is enabled", async () => {
|
||||||
const { openCodeClient, runtime } = mockOpenCodeClient({
|
const { openCodeClient, runtime } = mockOpenCodeClient({
|
||||||
events: [toolPermissionEvent(), idleEvent()],
|
events: [toolPermissionEvent(), idleEvent()],
|
||||||
});
|
});
|
||||||
@@ -112,7 +135,7 @@ describe("OpenCode full-access mode", () => {
|
|||||||
const session = await client.createSession({
|
const session = await client.createSession({
|
||||||
provider: "opencode",
|
provider: "opencode",
|
||||||
cwd: "/tmp/project",
|
cwd: "/tmp/project",
|
||||||
modeId: "full-access",
|
featureValues: { auto_accept: true },
|
||||||
});
|
});
|
||||||
session.subscribe((event) => receivedEvents.push(event));
|
session.subscribe((event) => receivedEvents.push(event));
|
||||||
|
|
||||||
@@ -130,7 +153,7 @@ describe("OpenCode full-access mode", () => {
|
|||||||
await session.close();
|
await session.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("keeps questions separate from full-access tool auto-approval", async () => {
|
test("keeps questions separate from auto accept tool approval", async () => {
|
||||||
const { openCodeClient, runtime } = mockOpenCodeClient({
|
const { openCodeClient, runtime } = mockOpenCodeClient({
|
||||||
events: [questionEvent(), idleEvent()],
|
events: [questionEvent(), idleEvent()],
|
||||||
});
|
});
|
||||||
@@ -140,7 +163,7 @@ describe("OpenCode full-access mode", () => {
|
|||||||
const session = await client.createSession({
|
const session = await client.createSession({
|
||||||
provider: "opencode",
|
provider: "opencode",
|
||||||
cwd: "/tmp/project",
|
cwd: "/tmp/project",
|
||||||
modeId: "full-access",
|
featureValues: { auto_accept: true },
|
||||||
});
|
});
|
||||||
session.subscribe((event) => receivedEvents.push(event));
|
session.subscribe((event) => receivedEvents.push(event));
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ const OPENCODE_CAPABILITIES: AgentCapabilityFlags = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const OPENCODE_BUILD_MODE_ID = "build";
|
const OPENCODE_BUILD_MODE_ID = "build";
|
||||||
const OPENCODE_FULL_ACCESS_MODE_ID = "full-access";
|
const OPENCODE_LEGACY_FULL_ACCESS_MODE_ID = "full-access";
|
||||||
|
const OPENCODE_AUTO_ACCEPT_FEATURE_ID = "auto_accept";
|
||||||
const OPENCODE_PERSISTED_SESSION_LIMIT = 200;
|
const OPENCODE_PERSISTED_SESSION_LIMIT = 200;
|
||||||
const OPENCODE_PENDING_ABORT_START_TIMEOUT_MS = 10_000;
|
const OPENCODE_PENDING_ABORT_START_TIMEOUT_MS = 10_000;
|
||||||
const OPENCODE_PERMISSION_ACTION_ALLOW_ONCE = "allow_once";
|
const OPENCODE_PERMISSION_ACTION_ALLOW_ONCE = "allow_once";
|
||||||
@@ -100,13 +101,24 @@ const DEFAULT_MODES: AgentMode[] = [
|
|||||||
label: "Plan",
|
label: "Plan",
|
||||||
description: "Read-only planning mode that avoids file edits",
|
description: "Read-only planning mode that avoids file edits",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: OPENCODE_FULL_ACCESS_MODE_ID,
|
|
||||||
label: "Full Access",
|
|
||||||
description: "Automatically approves all tool permission prompts for the session",
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function isOpenCodeAutoAcceptEnabled(config: AgentSessionConfig): boolean {
|
||||||
|
return config.featureValues?.[OPENCODE_AUTO_ACCEPT_FEATURE_ID] === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildOpenCodeAutoAcceptFeature(config: AgentSessionConfig): AgentFeature {
|
||||||
|
return {
|
||||||
|
type: "toggle",
|
||||||
|
id: OPENCODE_AUTO_ACCEPT_FEATURE_ID,
|
||||||
|
label: "Auto Accept",
|
||||||
|
description: "Automatically approves OpenCode tool permission prompts.",
|
||||||
|
tooltip: "Auto accept permission prompts",
|
||||||
|
icon: "shield-check",
|
||||||
|
value: isOpenCodeAutoAcceptEnabled(config),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function buildOpenCodePermissionActions(): AgentPermissionAction[] {
|
function buildOpenCodePermissionActions(): AgentPermissionAction[] {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -436,11 +448,26 @@ function normalizeOpenCodeModeId(modeId: string | null | undefined): string {
|
|||||||
|
|
||||||
function resolveOpenCodeRuntimeAgentId(modeId: string | null | undefined): string {
|
function resolveOpenCodeRuntimeAgentId(modeId: string | null | undefined): string {
|
||||||
const normalizedModeId = normalizeOpenCodeModeId(modeId);
|
const normalizedModeId = normalizeOpenCodeModeId(modeId);
|
||||||
return normalizedModeId === OPENCODE_FULL_ACCESS_MODE_ID
|
return normalizedModeId === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID
|
||||||
? OPENCODE_BUILD_MODE_ID
|
? OPENCODE_BUILD_MODE_ID
|
||||||
: normalizedModeId;
|
: normalizedModeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeOpenCodeConfig(config: OpenCodeAgentConfig): OpenCodeAgentConfig {
|
||||||
|
if (normalizeOpenCodeModeId(config.modeId) !== OPENCODE_LEGACY_FULL_ACCESS_MODE_ID) {
|
||||||
|
return { ...config };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
modeId: OPENCODE_BUILD_MODE_ID,
|
||||||
|
featureValues: {
|
||||||
|
...config.featureValues,
|
||||||
|
[OPENCODE_AUTO_ACCEPT_FEATURE_ID]: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function isSelectableOpenCodeAgent(agent: { mode?: string; hidden?: boolean }): boolean {
|
function isSelectableOpenCodeAgent(agent: { mode?: string; hidden?: boolean }): boolean {
|
||||||
return (agent.mode === "primary" || agent.mode === "all") && agent.hidden !== true;
|
return (agent.mode === "primary" || agent.mode === "all") && agent.hidden !== true;
|
||||||
}
|
}
|
||||||
@@ -462,6 +489,7 @@ function mapOpenCodeAgentToMode(agent: {
|
|||||||
return {
|
return {
|
||||||
id: agent.name,
|
id: agent.name,
|
||||||
label: agent.name.charAt(0).toUpperCase() + agent.name.slice(1),
|
label: agent.name.charAt(0).toUpperCase() + agent.name.slice(1),
|
||||||
|
icon: "Bot",
|
||||||
description:
|
description:
|
||||||
typeof agent.description === "string" && agent.description.trim().length > 0
|
typeof agent.description === "string" && agent.description.trim().length > 0
|
||||||
? agent.description.trim()
|
? agent.description.trim()
|
||||||
@@ -473,6 +501,9 @@ function mapOpenCodeAgentToMode(agent: {
|
|||||||
function mergeOpenCodeModes(discoveredModes: AgentMode[]): AgentMode[] {
|
function mergeOpenCodeModes(discoveredModes: AgentMode[]): AgentMode[] {
|
||||||
const modesById = new Map(DEFAULT_MODES.map((mode) => [mode.id, mode]));
|
const modesById = new Map(DEFAULT_MODES.map((mode) => [mode.id, mode]));
|
||||||
for (const mode of discoveredModes) {
|
for (const mode of discoveredModes) {
|
||||||
|
if (mode.id === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
modesById.set(mode.id, mode);
|
modesById.set(mode.id, mode);
|
||||||
}
|
}
|
||||||
return sortOpenCodeModes(Array.from(modesById.values()));
|
return sortOpenCodeModes(Array.from(modesById.values()));
|
||||||
@@ -1329,8 +1360,8 @@ export class OpenCodeAgentClient implements AgentClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async listFeatures(_config: AgentSessionConfig): Promise<AgentFeature[]> {
|
async listFeatures(config: AgentSessionConfig): Promise<AgentFeature[]> {
|
||||||
return [];
|
return [buildOpenCodeAutoAcceptFeature(this.assertConfig(config))];
|
||||||
}
|
}
|
||||||
|
|
||||||
async listPersistedAgents(
|
async listPersistedAgents(
|
||||||
@@ -1437,7 +1468,7 @@ export class OpenCodeAgentClient implements AgentClient {
|
|||||||
if (config.provider !== "opencode") {
|
if (config.provider !== "opencode") {
|
||||||
throw new Error(`OpenCodeAgentClient received config for provider '${config.provider}'`);
|
throw new Error(`OpenCodeAgentClient received config for provider '${config.provider}'`);
|
||||||
}
|
}
|
||||||
return { ...config, provider: "opencode" };
|
return normalizeOpenCodeConfig({ ...config, provider: "opencode" });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async populateModelContextWindowCache(
|
private async populateModelContextWindowCache(
|
||||||
@@ -2541,6 +2572,7 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
private readonly logger: Logger;
|
private readonly logger: Logger;
|
||||||
private readonly modelContextWindowsByModelKey: ReadonlyMap<string, number>;
|
private readonly modelContextWindowsByModelKey: ReadonlyMap<string, number>;
|
||||||
private currentMode: string = "default";
|
private currentMode: string = "default";
|
||||||
|
private autoAcceptEnabled = false;
|
||||||
private pendingPermissions = new Map<string, AgentPermissionRequest>();
|
private pendingPermissions = new Map<string, AgentPermissionRequest>();
|
||||||
private abortController: AbortController | null = null;
|
private abortController: AbortController | null = null;
|
||||||
private pendingAbortPromise: Promise<void> | null = null;
|
private pendingAbortPromise: Promise<void> | null = null;
|
||||||
@@ -2590,6 +2622,7 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
this.logger = logger.child({ agentId: this.agentId });
|
this.logger = logger.child({ agentId: this.agentId });
|
||||||
this.modelContextWindowsByModelKey = modelContextWindowsByModelKey;
|
this.modelContextWindowsByModelKey = modelContextWindowsByModelKey;
|
||||||
this.currentMode = normalizeOpenCodeModeId(config.modeId);
|
this.currentMode = normalizeOpenCodeModeId(config.modeId);
|
||||||
|
this.autoAcceptEnabled = isOpenCodeAutoAcceptEnabled(config);
|
||||||
this.releaseServer = releaseServer ?? null;
|
this.releaseServer = releaseServer ?? null;
|
||||||
this.persistSession = persistSession;
|
this.persistSession = persistSession;
|
||||||
this.selectedModelContextWindowMaxTokens = this.resolveConfiguredModelContextWindowMaxTokens(
|
this.selectedModelContextWindowMaxTokens = this.resolveConfiguredModelContextWindowMaxTokens(
|
||||||
@@ -2602,6 +2635,10 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
return this.sessionId;
|
return this.sessionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get features(): AgentFeature[] {
|
||||||
|
return [buildOpenCodeAutoAcceptFeature(this.config)];
|
||||||
|
}
|
||||||
|
|
||||||
async getRuntimeInfo(): Promise<AgentRuntimeInfo> {
|
async getRuntimeInfo(): Promise<AgentRuntimeInfo> {
|
||||||
return {
|
return {
|
||||||
provider: "opencode",
|
provider: "opencode",
|
||||||
@@ -3238,7 +3275,28 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setMode(modeId: string): Promise<void> {
|
async setMode(modeId: string): Promise<void> {
|
||||||
this.currentMode = normalizeOpenCodeModeId(modeId);
|
const normalizedModeId = normalizeOpenCodeModeId(modeId);
|
||||||
|
if (normalizedModeId === OPENCODE_LEGACY_FULL_ACCESS_MODE_ID) {
|
||||||
|
this.currentMode = OPENCODE_BUILD_MODE_ID;
|
||||||
|
await this.setFeature(OPENCODE_AUTO_ACCEPT_FEATURE_ID, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentMode = normalizedModeId;
|
||||||
|
this.config.modeId = normalizedModeId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setFeature(featureId: string, value: unknown): Promise<void> {
|
||||||
|
if (featureId !== OPENCODE_AUTO_ACCEPT_FEATURE_ID) {
|
||||||
|
throw new Error(`Unsupported OpenCode feature '${featureId}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const enabled = value === true;
|
||||||
|
this.autoAcceptEnabled = enabled;
|
||||||
|
this.config.featureValues = {
|
||||||
|
...this.config.featureValues,
|
||||||
|
[OPENCODE_AUTO_ACCEPT_FEATURE_ID]: enabled,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getPendingPermissions(): AgentPermissionRequest[] {
|
getPendingPermissions(): AgentPermissionRequest[] {
|
||||||
@@ -3521,7 +3579,7 @@ class OpenCodeAgentSession implements AgentSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async tryAutoApproveToolPermission(request: AgentPermissionRequest): Promise<boolean> {
|
private async tryAutoApproveToolPermission(request: AgentPermissionRequest): Promise<boolean> {
|
||||||
if (this.currentMode !== OPENCODE_FULL_ACCESS_MODE_ID || request.kind !== "tool") {
|
if (!this.autoAcceptEnabled || request.kind !== "tool") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user