nolo-cli 0.1.32 → 0.1.34
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/README.md +4 -10
- package/agent-runtime/agentRecordConfig.ts +10 -0
- package/agent-runtime/hostAdapter.ts +10 -1
- package/agent-runtime/index.ts +9 -20
- package/agent-runtime/localLoop.ts +49 -2
- package/agent-runtime/localToolPolicy.ts +76 -34
- package/agent-runtime/localWorkspaceTools.ts +30 -333
- package/agent-runtime/openAiCompatibleProvider.ts +7 -3
- package/agent-runtime/openAiCompatibleProviderConfig.ts +15 -26
- package/agent-runtime/platformChatProvider.ts +47 -73
- package/agent-runtime/providerResolution.ts +244 -0
- package/agent-runtime/runtimeToolPolicy.ts +77 -29
- package/agent-runtime/shellCommandPolicy.ts +120 -0
- package/agentAliases.ts +14 -83
- package/agentCommandSupport.ts +3 -6
- package/agentInternalCommandEntries.ts +4 -13
- package/agentListCommands.ts +18 -27
- package/agentMachineCommands.ts +40 -336
- package/agentPullCommand.ts +30 -55
- package/agentRecordCommands.ts +84 -1
- package/agentRecordHelpers.ts +102 -186
- package/agentRunCommand.ts +438 -107
- package/agentRuntimeLocal.ts +2 -9
- package/ai/agent/cliExecutor.ts +19 -323
- package/ai/tools/toolNameAliases.ts +70 -0
- package/authCommands.ts +2 -36
- package/cli/agentAliases.ts +14 -83
- package/cli/agentCommandSupport.ts +3 -6
- package/cli/agentInternalCommandEntries.ts +4 -13
- package/cli/agentListCommands.ts +18 -27
- package/cli/agentMachineCommands.ts +40 -336
- package/cli/agentPullCommand.ts +30 -55
- package/cli/agentRecordCommands.ts +84 -1
- package/cli/agentRecordHelpers.ts +102 -186
- package/cli/agentRunCommand.ts +438 -107
- package/cli/agentRuntimeLocal.ts +2 -9
- package/cli/authCommands.ts +2 -36
- package/cli/cliCommandDispatch.ts +2 -2
- package/cli/cliEnvHelpers.ts +34 -107
- package/cli/client/agentRun.ts +854 -57
- package/cli/client/localRuntimeAdapter.ts +59 -159
- package/cli/client/streamingOutput.ts +3 -1
- package/cli/client/taskRunPrompt.ts +24 -7
- package/cli/commandRegistry.ts +4 -5
- package/cli/connectorRunArtifact.ts +10 -34
- package/cli/connectorWebSocketTarget.ts +6 -10
- package/cli/internalCommandEntries.ts +0 -8
- package/cli/localRuntimeDb.ts +2 -39
- package/cli/machineCommands.ts +189 -190
- package/cli/machineDaemonCommands.ts +165 -9
- package/cli/machineHeartbeatCommands.ts +2 -1
- package/cli/machineStatusCommands.ts +13 -5
- package/cli/machineWatchCommands.ts +1 -1
- package/cli/machineWsRunDispatch.ts +30 -44
- package/cli/machineWsSession.ts +0 -2
- package/cli/offlineMarxistsAgentCommand.ts +314 -0
- package/cli/runtimeDoctorCommands.ts +72 -15
- package/cli/scriptCommandEntries.ts +45 -5
- package/cli/tableCommands.ts +133 -33
- package/cli/taskRunCommand.ts +211 -16
- package/cli/tui/session.ts +372 -43
- package/cli/workflowInternalCommandEntries.ts +0 -20
- package/cliCommandDispatch.ts +2 -2
- package/cliEnvHelpers.ts +34 -107
- package/client/agentConfigResolver.test.ts +2 -0
- package/client/agentRun.test.ts +12 -208
- package/client/agentRun.ts +854 -57
- package/client/localProviderResolver.test.ts +26 -2
- package/client/localRuntimeAdapter.test.ts +296 -171
- package/client/localRuntimeAdapter.ts +59 -159
- package/client/localToolPolicy.test.ts +4 -28
- package/client/streamingOutput.ts +3 -1
- package/client/taskRunPrompt.ts +24 -7
- package/commandRegistry.ts +4 -5
- package/connectorRunArtifact.ts +10 -34
- package/connectorWebSocketTarget.ts +6 -10
- package/database/server/serverStoreFactory.ts +2 -8
- package/internalCommandEntries.ts +0 -8
- package/localRuntimeDb.ts +2 -39
- package/machineCommands.ts +189 -190
- package/machineDaemonCommands.ts +165 -9
- package/machineHeartbeatCommands.ts +2 -1
- package/machineStatusCommands.ts +13 -5
- package/machineWatchCommands.ts +1 -1
- package/machineWsRunDispatch.ts +30 -44
- package/machineWsSession.ts +0 -2
- package/offlineMarxistsAgentCommand.ts +314 -0
- package/package.json +52 -188
- package/runtimeDoctorCommands.ts +72 -15
- package/scriptCommandEntries.ts +45 -5
- package/tableCommands.ts +133 -33
- package/taskRunCommand.ts +211 -16
- package/tui/session.ts +372 -43
- package/workflowInternalCommandEntries.ts +0 -20
- package/agent-runtime/dialogStatus.ts +0 -139
- package/agent-runtime/hostRuntimeConfig.ts +0 -86
- package/agentCreateCustomCommands.ts +0 -278
- package/agentDialogsCommands.ts +0 -227
- package/agentDoctorCommands.ts +0 -203
- package/agentRunLocalWorkspace.ts +0 -182
- package/agentRunParsing.ts +0 -298
- package/agentSuperviseCommands.ts +0 -8
- package/agentUnpublishCommands.ts +0 -113
- package/ai/agent/agentRunDialogLineage.ts +0 -56
- package/ai/policy/types.ts +0 -64
- package/ai/skills/skillSummaryMarker.ts +0 -26
- package/app/types.ts +0 -358
- package/build.ts +0 -16
- package/buildPublish.ts +0 -138
- package/buildPublishArtifactAssembly.ts +0 -724
- package/buildPublishImportAnalysis.ts +0 -242
- package/chat/messages/fetchMessages.ts +0 -84
- package/chat/messages/types.ts +0 -185
- package/cli/agentCreateCustomCommands.ts +0 -278
- package/cli/agentDialogsCommands.ts +0 -227
- package/cli/agentDoctorCommands.ts +0 -203
- package/cli/agentRunLocalWorkspace.ts +0 -182
- package/cli/agentRunParsing.ts +0 -298
- package/cli/agentSuperviseCommands.ts +0 -8
- package/cli/agentUnpublishCommands.ts +0 -113
- package/cli/buildPublish.ts +0 -138
- package/cli/buildPublishArtifactAssembly.ts +0 -724
- package/cli/buildPublishImportAnalysis.ts +0 -242
- package/cli/cliIoTypes.ts +0 -3
- package/cli/cliRuntimeDoctorReport.ts +0 -193
- package/cli/client/agentRunInput.ts +0 -10
- package/cli/client/agentRunLocalExecution.ts +0 -124
- package/cli/client/agentRunLocalExecutor.ts +0 -179
- package/cli/client/agentRunRuntimeSelection.ts +0 -145
- package/cli/client/agentRunServerTransport.ts +0 -397
- package/cli/client/agentRunTaskRunRecorder.ts +0 -176
- package/cli/customCodingAgentFlow.ts +0 -208
- package/cli/customCodingAgentRecord.ts +0 -179
- package/cli/dialogDataAccess.ts +0 -186
- package/cli/dialogInternalCommandEntries.ts +0 -11
- package/cli/dialogReadCommands.ts +0 -180
- package/cli/dialogReadHelpers.ts +0 -226
- package/cli/dialogStatusCommands.ts +0 -123
- package/cli/docInternalCommandEntries.ts +0 -20
- package/cli/hybridReadHelpers.ts +0 -109
- package/cli/machineCommandPrompts.ts +0 -79
- package/cli/machineCommandResolution.ts +0 -43
- package/cli/noloReadBridgePrompt.ts +0 -16
- package/cli/pageCreateCommands.ts +0 -219
- package/cli/pageDeleteCommands.ts +0 -167
- package/cli/pageReadCommands.ts +0 -151
- package/cli/pageUpdateCommands.ts +0 -249
- package/cli/pageWriteHelpers.ts +0 -209
- package/cli/publishPackage.ts +0 -36
- package/cli/runtimeInternalCommandEntries.ts +0 -15
- package/cli/runtimePassthroughCommands.ts +0 -22
- package/cli/scriptPassthroughHelpers.ts +0 -28
- package/cli/secretScanHelpers.ts +0 -31
- package/cli/spaceAttachCommands.ts +0 -134
- package/cli/spaceCategoryCommands.ts +0 -263
- package/cli/spaceCommands.ts +0 -295
- package/cli/spaceDeleteCommands.ts +0 -312
- package/cli/spaceInternalCommandEntries.ts +0 -21
- package/cli/spaceMutationHelpers.ts +0 -135
- package/cli/spaceReadCommands.ts +0 -323
- package/cli/tableDataOutput.ts +0 -41
- package/cli/tableDataSafety.ts +0 -26
- package/cli/tableMetaCommands.ts +0 -307
- package/cli/tableMutationCommands.ts +0 -266
- package/cli/tableQueryParsing.ts +0 -90
- package/cli/tableQueryRequest.ts +0 -53
- package/cli/taskRunActions.ts +0 -122
- package/cli/taskRunParsing.ts +0 -86
- package/cli/taskRunPromptShared.ts +0 -55
- package/cli/tui/tuiCatalog.ts +0 -22
- package/cli/tui/tuiCommandInterpreter.ts +0 -253
- package/cli/tui/tuiPresentation.ts +0 -146
- package/cliIoTypes.ts +0 -3
- package/cliRuntimeDoctorReport.ts +0 -193
- package/client/agentRunInput.ts +0 -10
- package/client/agentRunLocalExecution.test.ts +0 -123
- package/client/agentRunLocalExecution.ts +0 -124
- package/client/agentRunLocalExecutor.ts +0 -179
- package/client/agentRunRuntimeSelection.test.ts +0 -159
- package/client/agentRunRuntimeSelection.ts +0 -145
- package/client/agentRunServerTransport.ts +0 -397
- package/client/agentRunTaskRunRecorder.ts +0 -176
- package/connector-experimental/daemonLauncher.ts +0 -223
- package/create/space/spaceKeys.ts +0 -65
- package/create/types.ts +0 -21
- package/customCodingAgentFlow.ts +0 -208
- package/customCodingAgentRecord.ts +0 -179
- package/database/keys.ts +0 -765
- package/database/tombstones.ts +0 -52
- package/database/utils/ulid.ts +0 -1
- package/dialogDataAccess.ts +0 -186
- package/dialogInternalCommandEntries.ts +0 -11
- package/dialogReadCommands.ts +0 -180
- package/dialogReadHelpers.ts +0 -226
- package/dialogStatusCommands.ts +0 -123
- package/docInternalCommandEntries.ts +0 -20
- package/hybridReadHelpers.ts +0 -109
- package/machineCommandPrompts.ts +0 -79
- package/machineCommandResolution.ts +0 -43
- package/noloReadBridgePrompt.ts +0 -16
- package/pageCreateCommands.ts +0 -219
- package/pageDeleteCommands.ts +0 -167
- package/pageReadCommands.ts +0 -151
- package/pageUpdateCommands.ts +0 -249
- package/pageWriteHelpers.ts +0 -209
- package/publishPackage.ts +0 -36
- package/runtimeInternalCommandEntries.ts +0 -15
- package/runtimePassthroughCommands.ts +0 -22
- package/scriptPassthroughHelpers.ts +0 -28
- package/secretScanHelpers.ts +0 -31
- package/share/helpers.ts +0 -302
- package/share/keys.ts +0 -243
- package/share/types.ts +0 -100
- package/spaceAttachCommands.ts +0 -134
- package/spaceCategoryCommands.ts +0 -263
- package/spaceCommands.ts +0 -295
- package/spaceDeleteCommands.ts +0 -312
- package/spaceInternalCommandEntries.ts +0 -21
- package/spaceMutationHelpers.ts +0 -135
- package/spaceReadCommands.ts +0 -323
- package/tableDataOutput.ts +0 -41
- package/tableDataSafety.ts +0 -26
- package/tableMetaCommands.ts +0 -307
- package/tableMutationCommands.ts +0 -266
- package/tableQueryParsing.ts +0 -90
- package/tableQueryRequest.ts +0 -53
- package/taskRunActions.ts +0 -122
- package/taskRunParsing.ts +0 -86
- package/taskRunPromptShared.ts +0 -55
- package/tui/tuiCatalog.ts +0 -22
- package/tui/tuiCommandInterpreter.test.ts +0 -169
- package/tui/tuiCommandInterpreter.ts +0 -253
- package/tui/tuiPresentation.test.ts +0 -154
- package/tui/tuiPresentation.ts +0 -146
|
@@ -3,23 +3,19 @@ import type {
|
|
|
3
3
|
AgentRuntimeChatMessage,
|
|
4
4
|
AgentRuntimeResult,
|
|
5
5
|
} from "./types";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
buildProviderExecutionPlan,
|
|
8
|
+
canUsePlatformChatProvider as canUsePlatformChatProviderFromEnv,
|
|
9
|
+
hasDirectOpenAiCompatibleProvider as hasDirectOpenAiCompatibleProviderFromEnv,
|
|
10
|
+
resolvePlatformAuthToken,
|
|
11
|
+
resolvePlatformServerUrl,
|
|
12
|
+
shouldUsePlatformProxyTransport,
|
|
13
|
+
} from "./providerResolution";
|
|
7
14
|
|
|
8
15
|
type EnvLike = Record<string, string | undefined>;
|
|
9
16
|
|
|
10
17
|
const CHAT_PROXY_PATH = "/api/v1/chat";
|
|
11
18
|
|
|
12
|
-
const PROVIDER_ENDPOINTS: Record<string, string> = {
|
|
13
|
-
deepinfra: "https://api.deepinfra.com/v1/openai/chat/completions",
|
|
14
|
-
deepseek: "https://api.deepseek.com/chat/completions",
|
|
15
|
-
fireworks: "https://api.fireworks.ai/inference/v1/chat/completions",
|
|
16
|
-
google: "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
|
|
17
|
-
mistral: "https://api.mistral.ai/v1/chat/completions",
|
|
18
|
-
mimo: "https://token-plan-cn.xiaomimimo.com/v1/chat/completions",
|
|
19
|
-
openai: "https://api.openai.com/v1/chat/completions",
|
|
20
|
-
openrouter: "https://openrouter.ai/api/v1/chat/completions",
|
|
21
|
-
};
|
|
22
|
-
|
|
23
19
|
export type PlatformChatProviderConfig = {
|
|
24
20
|
serverUrl: string;
|
|
25
21
|
authToken: string;
|
|
@@ -29,46 +25,12 @@ export type PlatformChatProviderConfig = {
|
|
|
29
25
|
endpoint: string;
|
|
30
26
|
requestOptions: Record<string, number | string>;
|
|
31
27
|
apiKey?: string;
|
|
28
|
+
apiKeyHeader?: string;
|
|
32
29
|
apiSource?: string;
|
|
33
30
|
};
|
|
34
31
|
|
|
35
32
|
type PlatformChatTool = Record<string, unknown>;
|
|
36
33
|
|
|
37
|
-
function trimTrailingSlash(value: string) {
|
|
38
|
-
return value.trim().replace(/\/+$/, "");
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function resolveChatCompletionsEndpoint(value: string) {
|
|
42
|
-
const trimmed = trimTrailingSlash(value);
|
|
43
|
-
return trimmed.endsWith("/chat/completions")
|
|
44
|
-
? trimmed
|
|
45
|
-
: `${trimmed}/chat/completions`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function resolvePlatformServerUrl(env: EnvLike) {
|
|
49
|
-
return trimTrailingSlash(env.NOLO_SERVER || env.BASE_URL || "https://nolo.chat");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function resolvePlatformAuthToken(env: EnvLike) {
|
|
53
|
-
return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function resolveProviderEndpoint(agentConfig: AgentRuntimeAgentConfig) {
|
|
57
|
-
const customProviderUrl = agentConfig.customProviderUrl?.trim();
|
|
58
|
-
if (customProviderUrl) return resolveChatCompletionsEndpoint(customProviderUrl);
|
|
59
|
-
|
|
60
|
-
const provider = agentConfig.provider?.trim().toLowerCase();
|
|
61
|
-
if (!provider) {
|
|
62
|
-
throw new Error("Platform chat provider requires agentConfig.provider.");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const endpoint = PROVIDER_ENDPOINTS[provider];
|
|
66
|
-
if (!endpoint) {
|
|
67
|
-
throw new Error(`Platform chat provider does not support provider "${provider}".`);
|
|
68
|
-
}
|
|
69
|
-
return endpoint;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
34
|
function shouldDisableThinking(providerConfig: PlatformChatProviderConfig) {
|
|
73
35
|
return (
|
|
74
36
|
providerConfig.provider.toLowerCase() === "mimo" ||
|
|
@@ -90,19 +52,39 @@ export function resolvePlatformChatProviderConfig(args: {
|
|
|
90
52
|
agentConfig: AgentRuntimeAgentConfig;
|
|
91
53
|
env: EnvLike;
|
|
92
54
|
}): PlatformChatProviderConfig {
|
|
93
|
-
const
|
|
55
|
+
const plan = buildProviderExecutionPlan({
|
|
56
|
+
agentConfig: args.agentConfig,
|
|
57
|
+
env: args.env,
|
|
58
|
+
runtimeKind: "local",
|
|
59
|
+
});
|
|
60
|
+
if (plan.mode === "cli") {
|
|
61
|
+
throw new Error("Platform chat provider does not support cli agents.");
|
|
62
|
+
}
|
|
63
|
+
if (plan.transport === "direct") {
|
|
64
|
+
return {
|
|
65
|
+
serverUrl: resolvePlatformServerUrl(args.env),
|
|
66
|
+
authToken: resolvePlatformAuthToken(args.env),
|
|
67
|
+
agentKey: args.agentConfig.key,
|
|
68
|
+
model: plan.model,
|
|
69
|
+
provider: plan.provider,
|
|
70
|
+
endpoint: plan.endpoint,
|
|
71
|
+
requestOptions: plan.requestOptions,
|
|
72
|
+
...(plan.apiKey ? { apiKey: plan.apiKey } : {}),
|
|
73
|
+
...(plan.apiKeyHeader ? { apiKeyHeader: plan.apiKeyHeader } : {}),
|
|
74
|
+
...(args.agentConfig.apiSource ? { apiSource: args.agentConfig.apiSource } : {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
94
77
|
return {
|
|
95
|
-
serverUrl:
|
|
96
|
-
authToken:
|
|
97
|
-
agentKey:
|
|
98
|
-
model:
|
|
99
|
-
provider,
|
|
100
|
-
endpoint:
|
|
101
|
-
requestOptions:
|
|
102
|
-
...(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
...(args.agentConfig.apiSource ? { apiSource: args.agentConfig.apiSource } : {}),
|
|
78
|
+
serverUrl: plan.serverUrl,
|
|
79
|
+
authToken: plan.authToken,
|
|
80
|
+
agentKey: plan.agentKey,
|
|
81
|
+
model: plan.model,
|
|
82
|
+
provider: plan.provider,
|
|
83
|
+
endpoint: plan.endpoint,
|
|
84
|
+
requestOptions: plan.requestOptions,
|
|
85
|
+
...(plan.apiKey ? { apiKey: plan.apiKey } : {}),
|
|
86
|
+
...(plan.apiKeyHeader ? { apiKeyHeader: plan.apiKeyHeader } : {}),
|
|
87
|
+
...(plan.apiSource ? { apiSource: plan.apiSource } : {}),
|
|
106
88
|
};
|
|
107
89
|
}
|
|
108
90
|
|
|
@@ -123,6 +105,7 @@ export function buildPlatformChatCompletionRequest(args: {
|
|
|
123
105
|
agentKey: args.providerConfig.agentKey,
|
|
124
106
|
...(args.providerConfig.apiSource ? { apiSource: args.providerConfig.apiSource } : {}),
|
|
125
107
|
...(args.providerConfig.apiKey ? { KEY: args.providerConfig.apiKey } : {}),
|
|
108
|
+
...(args.providerConfig.apiKeyHeader ? { apiKeyHeader: args.providerConfig.apiKeyHeader } : {}),
|
|
126
109
|
};
|
|
127
110
|
|
|
128
111
|
return {
|
|
@@ -216,23 +199,17 @@ export function parsePlatformChatCompletionResponse(args: {
|
|
|
216
199
|
}
|
|
217
200
|
|
|
218
201
|
export function canUsePlatformChatProvider(env: EnvLike) {
|
|
219
|
-
return
|
|
202
|
+
return canUsePlatformChatProviderFromEnv(env);
|
|
220
203
|
}
|
|
221
204
|
|
|
222
205
|
export function hasDirectOpenAiCompatibleProvider(env: EnvLike) {
|
|
223
|
-
return
|
|
224
|
-
env.NOLO_LOCAL_OPENAI_API_KEY ||
|
|
225
|
-
env.OPENAI_API_KEY ||
|
|
226
|
-
env.NOLO_LOCAL_OPENAI_BASE_URL ||
|
|
227
|
-
env.OPENAI_BASE_URL ||
|
|
228
|
-
env.OLLAMA_BASE_URL
|
|
229
|
-
);
|
|
206
|
+
return hasDirectOpenAiCompatibleProviderFromEnv(env);
|
|
230
207
|
}
|
|
231
208
|
|
|
232
209
|
function agentRequiresPlatformChatProvider(agentConfig?: AgentRuntimeAgentConfig) {
|
|
233
210
|
if (agentConfig?.apiSource === "platform") return true;
|
|
234
|
-
if (agentConfig?.customProviderUrl?.trim()) return false;
|
|
235
|
-
return agentConfig?.
|
|
211
|
+
if (agentConfig?.apiSource === "custom" || agentConfig?.customProviderUrl?.trim()) return false;
|
|
212
|
+
return agentConfig?.useServerProxy === true;
|
|
236
213
|
}
|
|
237
214
|
|
|
238
215
|
export function shouldUsePlatformChatProvider(
|
|
@@ -240,8 +217,5 @@ export function shouldUsePlatformChatProvider(
|
|
|
240
217
|
agentConfig?: AgentRuntimeAgentConfig
|
|
241
218
|
) {
|
|
242
219
|
if (agentRequiresPlatformChatProvider(agentConfig)) return true;
|
|
243
|
-
|
|
244
|
-
if (env.NOLO_LOCAL_LLM === "platform") return true;
|
|
245
|
-
if (env.NOLO_LOCAL_LLM === "direct") return false;
|
|
246
|
-
return !hasDirectOpenAiCompatibleProvider(env) && canUsePlatformChatProvider(env);
|
|
220
|
+
return shouldUsePlatformProxyTransport(env, agentConfig);
|
|
247
221
|
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import type { AgentRuntimeAgentConfig } from "./hostAdapter";
|
|
2
|
+
import { pickAgentRuntimeInferenceOptions } from "./agentConfigOptions";
|
|
3
|
+
|
|
4
|
+
type EnvLike = Record<string, string | undefined>;
|
|
5
|
+
|
|
6
|
+
const PROVIDER_ENDPOINTS: Record<string, string> = {
|
|
7
|
+
deepinfra: "https://api.deepinfra.com/v1/openai/chat/completions",
|
|
8
|
+
deepseek: "https://api.deepseek.com/chat/completions",
|
|
9
|
+
fireworks: "https://api.fireworks.ai/inference/v1/chat/completions",
|
|
10
|
+
google: "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
|
|
11
|
+
mistral: "https://api.mistral.ai/v1/chat/completions",
|
|
12
|
+
mimo: "https://token-plan-cn.xiaomimimo.com/v1/chat/completions",
|
|
13
|
+
openai: "https://api.openai.com/v1/chat/completions",
|
|
14
|
+
openrouter: "https://openrouter.ai/api/v1/chat/completions",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type AgentProviderMode = "cli" | "platform" | "custom";
|
|
18
|
+
export type ProviderExecutionTransport = "direct" | "proxy";
|
|
19
|
+
|
|
20
|
+
type ProviderExecutionPlanBase = {
|
|
21
|
+
mode: AgentProviderMode;
|
|
22
|
+
transport: ProviderExecutionTransport;
|
|
23
|
+
model: string;
|
|
24
|
+
provider: string;
|
|
25
|
+
requestOptions: Record<string, number | string>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type CliProviderExecutionPlan = ProviderExecutionPlanBase & {
|
|
29
|
+
mode: "cli";
|
|
30
|
+
transport: "direct";
|
|
31
|
+
cliProvider: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type DirectProviderExecutionPlan = ProviderExecutionPlanBase & {
|
|
35
|
+
mode: "platform" | "custom";
|
|
36
|
+
transport: "direct";
|
|
37
|
+
endpoint: string;
|
|
38
|
+
apiKey: string;
|
|
39
|
+
apiKeyHeader?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type ProxyProviderExecutionPlan = ProviderExecutionPlanBase & {
|
|
43
|
+
mode: "platform" | "custom";
|
|
44
|
+
transport: "proxy";
|
|
45
|
+
endpoint: string;
|
|
46
|
+
serverUrl: string;
|
|
47
|
+
authToken: string;
|
|
48
|
+
agentKey: string;
|
|
49
|
+
apiSource?: string;
|
|
50
|
+
apiKey?: string;
|
|
51
|
+
apiKeyHeader?: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type ProviderExecutionPlan =
|
|
55
|
+
| CliProviderExecutionPlan
|
|
56
|
+
| DirectProviderExecutionPlan
|
|
57
|
+
| ProxyProviderExecutionPlan;
|
|
58
|
+
|
|
59
|
+
export function trimTrailingSlash(value: string) {
|
|
60
|
+
return value.trim().replace(/\/+$/, "");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function resolveChatCompletionsEndpoint(value: string) {
|
|
64
|
+
const trimmed = trimTrailingSlash(value);
|
|
65
|
+
return trimmed.endsWith("/chat/completions")
|
|
66
|
+
? trimmed
|
|
67
|
+
: `${trimmed}/chat/completions`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function resolveOpenAiCompatibleBaseUrl(env: EnvLike) {
|
|
71
|
+
return env.NOLO_LOCAL_OPENAI_BASE_URL || env.OPENAI_BASE_URL || "https://api.openai.com/v1";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function resolveOpenAiCompatibleApiKey(env: EnvLike) {
|
|
75
|
+
return env.OPENAI_API_KEY || env.NOLO_LOCAL_OPENAI_API_KEY || "";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function resolvePlatformServerUrl(env: EnvLike) {
|
|
79
|
+
return trimTrailingSlash(env.NOLO_SERVER || env.BASE_URL || "https://nolo.chat");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function resolvePlatformAuthToken(env: EnvLike) {
|
|
83
|
+
return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function canUsePlatformChatProvider(env: EnvLike) {
|
|
87
|
+
return Boolean(resolvePlatformAuthToken(env));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function hasDirectOpenAiCompatibleProvider(env: EnvLike) {
|
|
91
|
+
return Boolean(
|
|
92
|
+
env.NOLO_LOCAL_OPENAI_API_KEY ||
|
|
93
|
+
env.OPENAI_API_KEY ||
|
|
94
|
+
env.NOLO_LOCAL_OPENAI_BASE_URL ||
|
|
95
|
+
env.OPENAI_BASE_URL ||
|
|
96
|
+
env.OLLAMA_BASE_URL
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function resolvePlatformProviderEndpoint(agentConfig: AgentRuntimeAgentConfig) {
|
|
101
|
+
const customProviderUrl = agentConfig.customProviderUrl?.trim();
|
|
102
|
+
if (customProviderUrl) return resolveChatCompletionsEndpoint(customProviderUrl);
|
|
103
|
+
|
|
104
|
+
const provider = agentConfig.provider?.trim().toLowerCase();
|
|
105
|
+
if (!provider) {
|
|
106
|
+
throw new Error("Platform chat provider requires agentConfig.provider.");
|
|
107
|
+
}
|
|
108
|
+
const endpoint = PROVIDER_ENDPOINTS[provider];
|
|
109
|
+
if (!endpoint) {
|
|
110
|
+
throw new Error(`Platform chat provider does not support provider "${provider}".`);
|
|
111
|
+
}
|
|
112
|
+
return endpoint;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function resolveAgentProviderMode(agentConfig: AgentRuntimeAgentConfig): AgentProviderMode {
|
|
116
|
+
if (agentConfig.apiSource === "cli" || agentConfig.provider === "cli" || agentConfig.cliProvider) {
|
|
117
|
+
return "cli";
|
|
118
|
+
}
|
|
119
|
+
if (agentConfig.apiSource === "custom" || agentConfig.customProviderUrl) {
|
|
120
|
+
return "custom";
|
|
121
|
+
}
|
|
122
|
+
return "platform";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function shouldUsePlatformProxyTransport(
|
|
126
|
+
env: EnvLike,
|
|
127
|
+
agentConfig?: AgentRuntimeAgentConfig
|
|
128
|
+
) {
|
|
129
|
+
if (agentConfig?.apiSource === "platform") return true;
|
|
130
|
+
if (agentConfig?.apiSource === "custom" || agentConfig?.customProviderUrl?.trim()) return false;
|
|
131
|
+
if (agentConfig?.useServerProxy === true) return true;
|
|
132
|
+
if (env.NOLO_LOCAL_LLM === "platform") return true;
|
|
133
|
+
if (env.NOLO_LOCAL_LLM === "direct") return false;
|
|
134
|
+
return !hasDirectOpenAiCompatibleProvider(env) && canUsePlatformChatProvider(env);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function resolveProviderAuthHeaderName(args: {
|
|
138
|
+
endpoint: string;
|
|
139
|
+
apiKeyHeader?: string;
|
|
140
|
+
}) {
|
|
141
|
+
const explicitHeader = args.apiKeyHeader?.trim();
|
|
142
|
+
if (explicitHeader) return explicitHeader;
|
|
143
|
+
if (/xiaomimimo\.com/i.test(args.endpoint)) return "api-key";
|
|
144
|
+
return "Authorization";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function buildProviderAuthHeaders(args: {
|
|
148
|
+
endpoint: string;
|
|
149
|
+
apiKey: string;
|
|
150
|
+
apiKeyHeader?: string;
|
|
151
|
+
}) {
|
|
152
|
+
if (!args.apiKey) return {};
|
|
153
|
+
const headerName = resolveProviderAuthHeaderName(args);
|
|
154
|
+
return headerName.toLowerCase() === "authorization"
|
|
155
|
+
? { Authorization: `Bearer ${args.apiKey}` }
|
|
156
|
+
: { [headerName]: args.apiKey };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function buildProviderExecutionPlan(args: {
|
|
160
|
+
agentConfig: AgentRuntimeAgentConfig;
|
|
161
|
+
env: EnvLike;
|
|
162
|
+
runtimeKind: "local" | "desktop" | "server";
|
|
163
|
+
}): ProviderExecutionPlan {
|
|
164
|
+
const { agentConfig, env } = args;
|
|
165
|
+
const mode = resolveAgentProviderMode(agentConfig);
|
|
166
|
+
const model = agentConfig.model || "gpt-4.1-mini";
|
|
167
|
+
const requestOptions = pickAgentRuntimeInferenceOptions(agentConfig);
|
|
168
|
+
|
|
169
|
+
if (mode === "cli") {
|
|
170
|
+
return {
|
|
171
|
+
mode,
|
|
172
|
+
transport: "direct",
|
|
173
|
+
cliProvider: agentConfig.cliProvider || agentConfig.provider || "codex",
|
|
174
|
+
model,
|
|
175
|
+
provider: agentConfig.provider || "cli",
|
|
176
|
+
requestOptions,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (mode === "custom") {
|
|
181
|
+
const endpoint = resolveChatCompletionsEndpoint(agentConfig.customProviderUrl || resolveOpenAiCompatibleBaseUrl(env));
|
|
182
|
+
const apiKey = agentConfig.apiKey?.trim() || agentConfig.apiKeyFromAgentKey?.trim() || "";
|
|
183
|
+
const apiKeyHeader = resolveProviderAuthHeaderName({
|
|
184
|
+
endpoint,
|
|
185
|
+
apiKeyHeader: agentConfig.apiKeyHeader,
|
|
186
|
+
});
|
|
187
|
+
if (agentConfig.useServerProxy) {
|
|
188
|
+
return {
|
|
189
|
+
mode,
|
|
190
|
+
transport: "proxy",
|
|
191
|
+
model,
|
|
192
|
+
provider: agentConfig.provider || "custom",
|
|
193
|
+
endpoint,
|
|
194
|
+
requestOptions,
|
|
195
|
+
serverUrl: resolvePlatformServerUrl(env),
|
|
196
|
+
authToken: resolvePlatformAuthToken(env),
|
|
197
|
+
agentKey: agentConfig.key,
|
|
198
|
+
...(agentConfig.apiSource ? { apiSource: agentConfig.apiSource } : {}),
|
|
199
|
+
...(apiKey ? { apiKey } : {}),
|
|
200
|
+
...(apiKeyHeader ? { apiKeyHeader } : {}),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
mode,
|
|
205
|
+
transport: "direct",
|
|
206
|
+
model,
|
|
207
|
+
provider: agentConfig.provider || "custom",
|
|
208
|
+
endpoint,
|
|
209
|
+
requestOptions,
|
|
210
|
+
apiKey,
|
|
211
|
+
...(apiKeyHeader ? { apiKeyHeader } : {}),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const provider = agentConfig.provider || agentConfig.apiSource || "openai";
|
|
216
|
+
const endpoint = shouldUsePlatformProxyTransport(env, agentConfig)
|
|
217
|
+
? resolvePlatformProviderEndpoint(agentConfig)
|
|
218
|
+
: resolveChatCompletionsEndpoint(resolveOpenAiCompatibleBaseUrl(env));
|
|
219
|
+
if (shouldUsePlatformProxyTransport(env, agentConfig)) {
|
|
220
|
+
return {
|
|
221
|
+
mode,
|
|
222
|
+
transport: "proxy",
|
|
223
|
+
model,
|
|
224
|
+
provider,
|
|
225
|
+
endpoint,
|
|
226
|
+
requestOptions,
|
|
227
|
+
serverUrl: resolvePlatformServerUrl(env),
|
|
228
|
+
authToken: resolvePlatformAuthToken(env),
|
|
229
|
+
agentKey: agentConfig.key,
|
|
230
|
+
...(agentConfig.apiSource ? { apiSource: agentConfig.apiSource } : {}),
|
|
231
|
+
...(agentConfig.apiKey?.trim() ? { apiKey: agentConfig.apiKey.trim() } : {}),
|
|
232
|
+
...(agentConfig.apiKeyHeader?.trim() ? { apiKeyHeader: agentConfig.apiKeyHeader.trim() } : {}),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
mode,
|
|
237
|
+
transport: "direct",
|
|
238
|
+
model,
|
|
239
|
+
provider: agentConfig.provider || "openai-compatible",
|
|
240
|
+
endpoint,
|
|
241
|
+
requestOptions,
|
|
242
|
+
apiKey: resolveOpenAiCompatibleApiKey(env),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentRuntimeAgentConfig } from "./hostAdapter";
|
|
2
|
-
import type { AgentRuntimeToolPolicy } from "./types";
|
|
2
|
+
import type { AgentRuntimeHost, AgentRuntimeToolPolicy } from "./types";
|
|
3
3
|
|
|
4
4
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
5
5
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -46,6 +46,16 @@ export function normalizeAgentRuntimeToolPolicy(
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function runtimeBindingRecord(
|
|
50
|
+
runtimeBinding: unknown,
|
|
51
|
+
): Record<string, unknown> | undefined {
|
|
52
|
+
return isRecord(runtimeBinding) ? runtimeBinding : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function runtimeToolNamesFromPolicy(policy: AgentRuntimeToolPolicy | undefined) {
|
|
56
|
+
return stringArray(policy?.runtimeTools);
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
export function mergeAgentRuntimeToolPolicies(
|
|
50
60
|
base?: AgentRuntimeToolPolicy,
|
|
51
61
|
override?: AgentRuntimeToolPolicy,
|
|
@@ -77,42 +87,80 @@ export function mergeAgentRuntimeToolPolicies(
|
|
|
77
87
|
};
|
|
78
88
|
}
|
|
79
89
|
|
|
80
|
-
function runtimeBindingRecord(runtimeBinding: unknown): Record<string, unknown> | undefined {
|
|
81
|
-
return isRecord(runtimeBinding) ? runtimeBinding : undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function runtimePolicyFromRuntimeBinding(runtimeBinding: unknown): AgentRuntimeToolPolicy | undefined {
|
|
85
|
-
const binding = runtimeBindingRecord(runtimeBinding);
|
|
86
|
-
if (!binding) return undefined;
|
|
87
|
-
return normalizeAgentRuntimeToolPolicy(binding.runtimeToolPolicy);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function runtimePolicySnapshotFromRuntimeBinding(runtimeBinding: unknown): AgentRuntimeToolPolicy | undefined {
|
|
91
|
-
const binding = runtimeBindingRecord(runtimeBinding);
|
|
92
|
-
if (!binding) return undefined;
|
|
93
|
-
return normalizeAgentRuntimeToolPolicy(binding.runtimeToolPolicySnapshot);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
90
|
export function resolveRequestedRuntimeToolNames(args: {
|
|
97
|
-
agentConfig: Pick<
|
|
91
|
+
agentConfig: Pick<
|
|
92
|
+
AgentRuntimeAgentConfig,
|
|
93
|
+
"toolNames" | "runtimeToolPolicy" | "runtimeBinding"
|
|
94
|
+
>;
|
|
98
95
|
}) {
|
|
99
|
-
const
|
|
100
|
-
const bindingPolicy =
|
|
101
|
-
|
|
96
|
+
const runtimeBinding = runtimeBindingRecord(args.agentConfig.runtimeBinding);
|
|
97
|
+
const bindingPolicy = normalizeAgentRuntimeToolPolicy(
|
|
98
|
+
runtimeBinding?.runtimeToolPolicy,
|
|
99
|
+
);
|
|
100
|
+
const bindingSnapshotPolicy = normalizeAgentRuntimeToolPolicy(
|
|
101
|
+
runtimeBinding?.runtimeToolPolicySnapshot,
|
|
102
|
+
);
|
|
102
103
|
return unique([
|
|
103
104
|
...stringArray(args.agentConfig.toolNames),
|
|
104
|
-
...(
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
...runtimeToolNamesFromPolicy(
|
|
106
|
+
normalizeAgentRuntimeToolPolicy(args.agentConfig.runtimeToolPolicy),
|
|
107
|
+
),
|
|
108
|
+
...runtimeToolNamesFromPolicy(bindingPolicy),
|
|
109
|
+
...runtimeToolNamesFromPolicy(bindingSnapshotPolicy),
|
|
107
110
|
]);
|
|
108
111
|
}
|
|
109
112
|
|
|
113
|
+
function policyRequestsHostedWorkspace(policy: AgentRuntimeToolPolicy | undefined) {
|
|
114
|
+
if (!policy) return false;
|
|
115
|
+
const workspaceMode = policy.workspace?.mode;
|
|
116
|
+
return (
|
|
117
|
+
policy.shell?.enabled === true ||
|
|
118
|
+
policy.runtimeTools?.some((tool) => tool === "execShell") ||
|
|
119
|
+
workspaceMode === "lease" ||
|
|
120
|
+
workspaceMode === "task-worktree"
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function applyWebRuntimeSafetyBaseline(
|
|
125
|
+
policy: AgentRuntimeToolPolicy | undefined,
|
|
126
|
+
): AgentRuntimeToolPolicy | undefined {
|
|
127
|
+
if (!policyRequestsHostedWorkspace(policy)) return policy;
|
|
128
|
+
return mergeAgentRuntimeToolPolicies(
|
|
129
|
+
policy,
|
|
130
|
+
{
|
|
131
|
+
version: 1,
|
|
132
|
+
shell: {
|
|
133
|
+
commandPolicy: "approval",
|
|
134
|
+
networkPolicy: "default-deny",
|
|
135
|
+
},
|
|
136
|
+
isolation: {
|
|
137
|
+
mode: "os-sandbox",
|
|
138
|
+
},
|
|
139
|
+
audit: {
|
|
140
|
+
logToolCalls: true,
|
|
141
|
+
logShellCommands: true,
|
|
142
|
+
writeToDialog: true,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
110
148
|
export function resolveEffectiveRuntimeToolPolicy(args: {
|
|
111
149
|
agentConfig: Pick<AgentRuntimeAgentConfig, "runtimeToolPolicy" | "runtimeBinding">;
|
|
150
|
+
taskRunControl?: { runtimeToolPolicy?: unknown } | null;
|
|
151
|
+
host?: AgentRuntimeHost;
|
|
112
152
|
}): AgentRuntimeToolPolicy | undefined {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
153
|
+
const agentPolicy = normalizeAgentRuntimeToolPolicy(
|
|
154
|
+
args.agentConfig.runtimeToolPolicy ??
|
|
155
|
+
(isRecord(args.agentConfig.runtimeBinding)
|
|
156
|
+
? args.agentConfig.runtimeBinding.runtimeToolPolicy
|
|
157
|
+
: undefined),
|
|
158
|
+
);
|
|
159
|
+
const taskPolicy = normalizeAgentRuntimeToolPolicy(
|
|
160
|
+
args.taskRunControl?.runtimeToolPolicy,
|
|
161
|
+
);
|
|
162
|
+
const effectivePolicy = mergeAgentRuntimeToolPolicies(agentPolicy, taskPolicy);
|
|
163
|
+
return args.host === "web"
|
|
164
|
+
? applyWebRuntimeSafetyBaseline(effectivePolicy)
|
|
165
|
+
: effectivePolicy;
|
|
118
166
|
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export type ShellCommandPolicyVerdict = "allowed" | "forbidden";
|
|
2
|
+
|
|
3
|
+
export type ShellCommandPolicyResult =
|
|
4
|
+
| {
|
|
5
|
+
verdict: "allowed";
|
|
6
|
+
}
|
|
7
|
+
| {
|
|
8
|
+
verdict: "forbidden";
|
|
9
|
+
code: "destructive_action_requires_confirmation";
|
|
10
|
+
reason: string;
|
|
11
|
+
policy: {
|
|
12
|
+
capability: "destructive_action";
|
|
13
|
+
target: "shell_command";
|
|
14
|
+
detail: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DESTRUCTIVE_NEGATION_PATTERNS = [
|
|
19
|
+
"别删",
|
|
20
|
+
"别删除",
|
|
21
|
+
"不要删",
|
|
22
|
+
"不要删除",
|
|
23
|
+
"别乱删",
|
|
24
|
+
"别动",
|
|
25
|
+
"不要动",
|
|
26
|
+
"不要清理",
|
|
27
|
+
"不要清空",
|
|
28
|
+
"不要重置",
|
|
29
|
+
"don't delete",
|
|
30
|
+
"do not delete",
|
|
31
|
+
"don't remove",
|
|
32
|
+
"do not remove",
|
|
33
|
+
"without deleting",
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
const EXPLICIT_DESTRUCTIVE_REQUEST_PATTERNS = [
|
|
37
|
+
"删除",
|
|
38
|
+
"删掉",
|
|
39
|
+
"移除",
|
|
40
|
+
"清理",
|
|
41
|
+
"清空",
|
|
42
|
+
"重置",
|
|
43
|
+
"drop",
|
|
44
|
+
"delete",
|
|
45
|
+
"remove",
|
|
46
|
+
"erase",
|
|
47
|
+
"clean up",
|
|
48
|
+
"reset",
|
|
49
|
+
] as const;
|
|
50
|
+
|
|
51
|
+
const SHELL_DESTRUCTIVE_PATTERNS = [
|
|
52
|
+
/\brm\s+(-[^\n\r]*\s+)?/i,
|
|
53
|
+
/\bremove-item\b/i,
|
|
54
|
+
/\bdel\s+\/?[a-z]*\s+/i,
|
|
55
|
+
/\berase\s+/i,
|
|
56
|
+
/\brmdir\s+/i,
|
|
57
|
+
/\brd\s+\/s\b/i,
|
|
58
|
+
/\bgit\s+reset\s+(--hard\b|--merge\b|--keep\b)/i,
|
|
59
|
+
/\bgit\s+clean\s+-[^\n\r]*f/i,
|
|
60
|
+
/\bgit\s+checkout\s+--\b/i,
|
|
61
|
+
/\bgit\s+restore\s+--source=/i,
|
|
62
|
+
] as const;
|
|
63
|
+
|
|
64
|
+
const normalizeUserInput = (value: unknown): string =>
|
|
65
|
+
typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
66
|
+
|
|
67
|
+
const containsAny = (text: string, patterns: readonly string[]): boolean =>
|
|
68
|
+
patterns.some((pattern) => text.includes(pattern));
|
|
69
|
+
|
|
70
|
+
const hasExplicitDestructivePermission = (userInput: unknown): boolean => {
|
|
71
|
+
const normalized = normalizeUserInput(userInput);
|
|
72
|
+
if (!normalized) return false;
|
|
73
|
+
if (containsAny(normalized, DESTRUCTIVE_NEGATION_PATTERNS)) return false;
|
|
74
|
+
return containsAny(normalized, EXPLICIT_DESTRUCTIVE_REQUEST_PATTERNS);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const buildCombinedShellInput = (args: {
|
|
78
|
+
command?: unknown;
|
|
79
|
+
input?: unknown;
|
|
80
|
+
}): string =>
|
|
81
|
+
[
|
|
82
|
+
typeof args.command === "string" ? args.command : "",
|
|
83
|
+
typeof args.input === "string" ? args.input : "",
|
|
84
|
+
]
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.join("\n");
|
|
87
|
+
|
|
88
|
+
export function isDestructiveShellCommand(args: {
|
|
89
|
+
command?: unknown;
|
|
90
|
+
input?: unknown;
|
|
91
|
+
}): boolean {
|
|
92
|
+
const combined = buildCombinedShellInput(args);
|
|
93
|
+
if (!combined.trim()) return false;
|
|
94
|
+
return SHELL_DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(combined));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function evaluateShellCommandPolicy(args: {
|
|
98
|
+
command?: unknown;
|
|
99
|
+
input?: unknown;
|
|
100
|
+
userInput?: unknown;
|
|
101
|
+
}): ShellCommandPolicyResult {
|
|
102
|
+
if (
|
|
103
|
+
isDestructiveShellCommand(args) &&
|
|
104
|
+
!hasExplicitDestructivePermission(args.userInput)
|
|
105
|
+
) {
|
|
106
|
+
return {
|
|
107
|
+
verdict: "forbidden",
|
|
108
|
+
code: "destructive_action_requires_confirmation",
|
|
109
|
+
reason:
|
|
110
|
+
"当前运行默认禁止自动执行可能删除用户内容的 shell 命令。只有当用户在当前请求里明确要求删除/清理时,才能继续;否则请停止并先说明限制。",
|
|
111
|
+
policy: {
|
|
112
|
+
capability: "destructive_action",
|
|
113
|
+
target: "shell_command",
|
|
114
|
+
detail: "execShell destructive command",
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { verdict: "allowed" };
|
|
120
|
+
}
|