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.
Files changed (234) hide show
  1. package/README.md +4 -10
  2. package/agent-runtime/agentRecordConfig.ts +10 -0
  3. package/agent-runtime/hostAdapter.ts +10 -1
  4. package/agent-runtime/index.ts +9 -20
  5. package/agent-runtime/localLoop.ts +49 -2
  6. package/agent-runtime/localToolPolicy.ts +76 -34
  7. package/agent-runtime/localWorkspaceTools.ts +30 -333
  8. package/agent-runtime/openAiCompatibleProvider.ts +7 -3
  9. package/agent-runtime/openAiCompatibleProviderConfig.ts +15 -26
  10. package/agent-runtime/platformChatProvider.ts +47 -73
  11. package/agent-runtime/providerResolution.ts +244 -0
  12. package/agent-runtime/runtimeToolPolicy.ts +77 -29
  13. package/agent-runtime/shellCommandPolicy.ts +120 -0
  14. package/agentAliases.ts +14 -83
  15. package/agentCommandSupport.ts +3 -6
  16. package/agentInternalCommandEntries.ts +4 -13
  17. package/agentListCommands.ts +18 -27
  18. package/agentMachineCommands.ts +40 -336
  19. package/agentPullCommand.ts +30 -55
  20. package/agentRecordCommands.ts +84 -1
  21. package/agentRecordHelpers.ts +102 -186
  22. package/agentRunCommand.ts +438 -107
  23. package/agentRuntimeLocal.ts +2 -9
  24. package/ai/agent/cliExecutor.ts +19 -323
  25. package/ai/tools/toolNameAliases.ts +70 -0
  26. package/authCommands.ts +2 -36
  27. package/cli/agentAliases.ts +14 -83
  28. package/cli/agentCommandSupport.ts +3 -6
  29. package/cli/agentInternalCommandEntries.ts +4 -13
  30. package/cli/agentListCommands.ts +18 -27
  31. package/cli/agentMachineCommands.ts +40 -336
  32. package/cli/agentPullCommand.ts +30 -55
  33. package/cli/agentRecordCommands.ts +84 -1
  34. package/cli/agentRecordHelpers.ts +102 -186
  35. package/cli/agentRunCommand.ts +438 -107
  36. package/cli/agentRuntimeLocal.ts +2 -9
  37. package/cli/authCommands.ts +2 -36
  38. package/cli/cliCommandDispatch.ts +2 -2
  39. package/cli/cliEnvHelpers.ts +34 -107
  40. package/cli/client/agentRun.ts +854 -57
  41. package/cli/client/localRuntimeAdapter.ts +59 -159
  42. package/cli/client/streamingOutput.ts +3 -1
  43. package/cli/client/taskRunPrompt.ts +24 -7
  44. package/cli/commandRegistry.ts +4 -5
  45. package/cli/connectorRunArtifact.ts +10 -34
  46. package/cli/connectorWebSocketTarget.ts +6 -10
  47. package/cli/internalCommandEntries.ts +0 -8
  48. package/cli/localRuntimeDb.ts +2 -39
  49. package/cli/machineCommands.ts +189 -190
  50. package/cli/machineDaemonCommands.ts +165 -9
  51. package/cli/machineHeartbeatCommands.ts +2 -1
  52. package/cli/machineStatusCommands.ts +13 -5
  53. package/cli/machineWatchCommands.ts +1 -1
  54. package/cli/machineWsRunDispatch.ts +30 -44
  55. package/cli/machineWsSession.ts +0 -2
  56. package/cli/offlineMarxistsAgentCommand.ts +314 -0
  57. package/cli/runtimeDoctorCommands.ts +72 -15
  58. package/cli/scriptCommandEntries.ts +45 -5
  59. package/cli/tableCommands.ts +133 -33
  60. package/cli/taskRunCommand.ts +211 -16
  61. package/cli/tui/session.ts +372 -43
  62. package/cli/workflowInternalCommandEntries.ts +0 -20
  63. package/cliCommandDispatch.ts +2 -2
  64. package/cliEnvHelpers.ts +34 -107
  65. package/client/agentConfigResolver.test.ts +2 -0
  66. package/client/agentRun.test.ts +12 -208
  67. package/client/agentRun.ts +854 -57
  68. package/client/localProviderResolver.test.ts +26 -2
  69. package/client/localRuntimeAdapter.test.ts +296 -171
  70. package/client/localRuntimeAdapter.ts +59 -159
  71. package/client/localToolPolicy.test.ts +4 -28
  72. package/client/streamingOutput.ts +3 -1
  73. package/client/taskRunPrompt.ts +24 -7
  74. package/commandRegistry.ts +4 -5
  75. package/connectorRunArtifact.ts +10 -34
  76. package/connectorWebSocketTarget.ts +6 -10
  77. package/database/server/serverStoreFactory.ts +2 -8
  78. package/internalCommandEntries.ts +0 -8
  79. package/localRuntimeDb.ts +2 -39
  80. package/machineCommands.ts +189 -190
  81. package/machineDaemonCommands.ts +165 -9
  82. package/machineHeartbeatCommands.ts +2 -1
  83. package/machineStatusCommands.ts +13 -5
  84. package/machineWatchCommands.ts +1 -1
  85. package/machineWsRunDispatch.ts +30 -44
  86. package/machineWsSession.ts +0 -2
  87. package/offlineMarxistsAgentCommand.ts +314 -0
  88. package/package.json +52 -188
  89. package/runtimeDoctorCommands.ts +72 -15
  90. package/scriptCommandEntries.ts +45 -5
  91. package/tableCommands.ts +133 -33
  92. package/taskRunCommand.ts +211 -16
  93. package/tui/session.ts +372 -43
  94. package/workflowInternalCommandEntries.ts +0 -20
  95. package/agent-runtime/dialogStatus.ts +0 -139
  96. package/agent-runtime/hostRuntimeConfig.ts +0 -86
  97. package/agentCreateCustomCommands.ts +0 -278
  98. package/agentDialogsCommands.ts +0 -227
  99. package/agentDoctorCommands.ts +0 -203
  100. package/agentRunLocalWorkspace.ts +0 -182
  101. package/agentRunParsing.ts +0 -298
  102. package/agentSuperviseCommands.ts +0 -8
  103. package/agentUnpublishCommands.ts +0 -113
  104. package/ai/agent/agentRunDialogLineage.ts +0 -56
  105. package/ai/policy/types.ts +0 -64
  106. package/ai/skills/skillSummaryMarker.ts +0 -26
  107. package/app/types.ts +0 -358
  108. package/build.ts +0 -16
  109. package/buildPublish.ts +0 -138
  110. package/buildPublishArtifactAssembly.ts +0 -724
  111. package/buildPublishImportAnalysis.ts +0 -242
  112. package/chat/messages/fetchMessages.ts +0 -84
  113. package/chat/messages/types.ts +0 -185
  114. package/cli/agentCreateCustomCommands.ts +0 -278
  115. package/cli/agentDialogsCommands.ts +0 -227
  116. package/cli/agentDoctorCommands.ts +0 -203
  117. package/cli/agentRunLocalWorkspace.ts +0 -182
  118. package/cli/agentRunParsing.ts +0 -298
  119. package/cli/agentSuperviseCommands.ts +0 -8
  120. package/cli/agentUnpublishCommands.ts +0 -113
  121. package/cli/buildPublish.ts +0 -138
  122. package/cli/buildPublishArtifactAssembly.ts +0 -724
  123. package/cli/buildPublishImportAnalysis.ts +0 -242
  124. package/cli/cliIoTypes.ts +0 -3
  125. package/cli/cliRuntimeDoctorReport.ts +0 -193
  126. package/cli/client/agentRunInput.ts +0 -10
  127. package/cli/client/agentRunLocalExecution.ts +0 -124
  128. package/cli/client/agentRunLocalExecutor.ts +0 -179
  129. package/cli/client/agentRunRuntimeSelection.ts +0 -145
  130. package/cli/client/agentRunServerTransport.ts +0 -397
  131. package/cli/client/agentRunTaskRunRecorder.ts +0 -176
  132. package/cli/customCodingAgentFlow.ts +0 -208
  133. package/cli/customCodingAgentRecord.ts +0 -179
  134. package/cli/dialogDataAccess.ts +0 -186
  135. package/cli/dialogInternalCommandEntries.ts +0 -11
  136. package/cli/dialogReadCommands.ts +0 -180
  137. package/cli/dialogReadHelpers.ts +0 -226
  138. package/cli/dialogStatusCommands.ts +0 -123
  139. package/cli/docInternalCommandEntries.ts +0 -20
  140. package/cli/hybridReadHelpers.ts +0 -109
  141. package/cli/machineCommandPrompts.ts +0 -79
  142. package/cli/machineCommandResolution.ts +0 -43
  143. package/cli/noloReadBridgePrompt.ts +0 -16
  144. package/cli/pageCreateCommands.ts +0 -219
  145. package/cli/pageDeleteCommands.ts +0 -167
  146. package/cli/pageReadCommands.ts +0 -151
  147. package/cli/pageUpdateCommands.ts +0 -249
  148. package/cli/pageWriteHelpers.ts +0 -209
  149. package/cli/publishPackage.ts +0 -36
  150. package/cli/runtimeInternalCommandEntries.ts +0 -15
  151. package/cli/runtimePassthroughCommands.ts +0 -22
  152. package/cli/scriptPassthroughHelpers.ts +0 -28
  153. package/cli/secretScanHelpers.ts +0 -31
  154. package/cli/spaceAttachCommands.ts +0 -134
  155. package/cli/spaceCategoryCommands.ts +0 -263
  156. package/cli/spaceCommands.ts +0 -295
  157. package/cli/spaceDeleteCommands.ts +0 -312
  158. package/cli/spaceInternalCommandEntries.ts +0 -21
  159. package/cli/spaceMutationHelpers.ts +0 -135
  160. package/cli/spaceReadCommands.ts +0 -323
  161. package/cli/tableDataOutput.ts +0 -41
  162. package/cli/tableDataSafety.ts +0 -26
  163. package/cli/tableMetaCommands.ts +0 -307
  164. package/cli/tableMutationCommands.ts +0 -266
  165. package/cli/tableQueryParsing.ts +0 -90
  166. package/cli/tableQueryRequest.ts +0 -53
  167. package/cli/taskRunActions.ts +0 -122
  168. package/cli/taskRunParsing.ts +0 -86
  169. package/cli/taskRunPromptShared.ts +0 -55
  170. package/cli/tui/tuiCatalog.ts +0 -22
  171. package/cli/tui/tuiCommandInterpreter.ts +0 -253
  172. package/cli/tui/tuiPresentation.ts +0 -146
  173. package/cliIoTypes.ts +0 -3
  174. package/cliRuntimeDoctorReport.ts +0 -193
  175. package/client/agentRunInput.ts +0 -10
  176. package/client/agentRunLocalExecution.test.ts +0 -123
  177. package/client/agentRunLocalExecution.ts +0 -124
  178. package/client/agentRunLocalExecutor.ts +0 -179
  179. package/client/agentRunRuntimeSelection.test.ts +0 -159
  180. package/client/agentRunRuntimeSelection.ts +0 -145
  181. package/client/agentRunServerTransport.ts +0 -397
  182. package/client/agentRunTaskRunRecorder.ts +0 -176
  183. package/connector-experimental/daemonLauncher.ts +0 -223
  184. package/create/space/spaceKeys.ts +0 -65
  185. package/create/types.ts +0 -21
  186. package/customCodingAgentFlow.ts +0 -208
  187. package/customCodingAgentRecord.ts +0 -179
  188. package/database/keys.ts +0 -765
  189. package/database/tombstones.ts +0 -52
  190. package/database/utils/ulid.ts +0 -1
  191. package/dialogDataAccess.ts +0 -186
  192. package/dialogInternalCommandEntries.ts +0 -11
  193. package/dialogReadCommands.ts +0 -180
  194. package/dialogReadHelpers.ts +0 -226
  195. package/dialogStatusCommands.ts +0 -123
  196. package/docInternalCommandEntries.ts +0 -20
  197. package/hybridReadHelpers.ts +0 -109
  198. package/machineCommandPrompts.ts +0 -79
  199. package/machineCommandResolution.ts +0 -43
  200. package/noloReadBridgePrompt.ts +0 -16
  201. package/pageCreateCommands.ts +0 -219
  202. package/pageDeleteCommands.ts +0 -167
  203. package/pageReadCommands.ts +0 -151
  204. package/pageUpdateCommands.ts +0 -249
  205. package/pageWriteHelpers.ts +0 -209
  206. package/publishPackage.ts +0 -36
  207. package/runtimeInternalCommandEntries.ts +0 -15
  208. package/runtimePassthroughCommands.ts +0 -22
  209. package/scriptPassthroughHelpers.ts +0 -28
  210. package/secretScanHelpers.ts +0 -31
  211. package/share/helpers.ts +0 -302
  212. package/share/keys.ts +0 -243
  213. package/share/types.ts +0 -100
  214. package/spaceAttachCommands.ts +0 -134
  215. package/spaceCategoryCommands.ts +0 -263
  216. package/spaceCommands.ts +0 -295
  217. package/spaceDeleteCommands.ts +0 -312
  218. package/spaceInternalCommandEntries.ts +0 -21
  219. package/spaceMutationHelpers.ts +0 -135
  220. package/spaceReadCommands.ts +0 -323
  221. package/tableDataOutput.ts +0 -41
  222. package/tableDataSafety.ts +0 -26
  223. package/tableMetaCommands.ts +0 -307
  224. package/tableMutationCommands.ts +0 -266
  225. package/tableQueryParsing.ts +0 -90
  226. package/tableQueryRequest.ts +0 -53
  227. package/taskRunActions.ts +0 -122
  228. package/taskRunParsing.ts +0 -86
  229. package/taskRunPromptShared.ts +0 -55
  230. package/tui/tuiCatalog.ts +0 -22
  231. package/tui/tuiCommandInterpreter.test.ts +0 -169
  232. package/tui/tuiCommandInterpreter.ts +0 -253
  233. package/tui/tuiPresentation.test.ts +0 -154
  234. package/tui/tuiPresentation.ts +0 -146
@@ -3,23 +3,19 @@ import type {
3
3
  AgentRuntimeChatMessage,
4
4
  AgentRuntimeResult,
5
5
  } from "./types";
6
- import { pickAgentRuntimeInferenceOptions } from "./agentConfigOptions";
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 provider = args.agentConfig.provider || args.agentConfig.apiSource || "openai";
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: resolvePlatformServerUrl(args.env),
96
- authToken: resolvePlatformAuthToken(args.env),
97
- agentKey: args.agentConfig.key,
98
- model: args.agentConfig.model || "gpt-4.1-mini",
99
- provider,
100
- endpoint: resolveProviderEndpoint(args.agentConfig),
101
- requestOptions: pickAgentRuntimeInferenceOptions(args.agentConfig),
102
- ...(typeof args.agentConfig.rawRecord?.apiKey === "string"
103
- ? { apiKey: args.agentConfig.rawRecord.apiKey.trim() }
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 Boolean(resolvePlatformAuthToken(env));
202
+ return canUsePlatformChatProviderFromEnv(env);
220
203
  }
221
204
 
222
205
  export function hasDirectOpenAiCompatibleProvider(env: EnvLike) {
223
- return Boolean(
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?.rawRecord?.useServerProxy === true;
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
- if (agentConfig?.customProviderUrl?.trim()) return false;
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<AgentRuntimeAgentConfig, "toolNames" | "runtimeToolPolicy" | "runtimeBinding">;
91
+ agentConfig: Pick<
92
+ AgentRuntimeAgentConfig,
93
+ "toolNames" | "runtimeToolPolicy" | "runtimeBinding"
94
+ >;
98
95
  }) {
99
- const agentPolicy = normalizeAgentRuntimeToolPolicy(args.agentConfig.runtimeToolPolicy);
100
- const bindingPolicy = runtimePolicyFromRuntimeBinding(args.agentConfig.runtimeBinding);
101
- const bindingSnapshotPolicy = runtimePolicySnapshotFromRuntimeBinding(args.agentConfig.runtimeBinding);
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
- ...(agentPolicy?.runtimeTools ?? []),
105
- ...(bindingPolicy?.runtimeTools ?? []),
106
- ...(bindingSnapshotPolicy?.runtimeTools ?? []),
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 bindingSnapshot = runtimePolicySnapshotFromRuntimeBinding(args.agentConfig.runtimeBinding);
114
- if (bindingSnapshot) return bindingSnapshot;
115
- const topLevelPolicy = normalizeAgentRuntimeToolPolicy(args.agentConfig.runtimeToolPolicy);
116
- if (topLevelPolicy) return topLevelPolicy;
117
- return runtimePolicyFromRuntimeBinding(args.agentConfig.runtimeBinding);
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
+ }