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
package/README.md CHANGED
@@ -90,7 +90,7 @@ nolo chat
90
90
  nolo connect
91
91
  nolo connect --watch
92
92
  nolo connect --daemon
93
- nolo daemon --server-url https://api.nolo.chat --api-key sk_machine_xxx
93
+ nolo daemon --server-url https://api.nolo.chat --machine-key sk_machine_xxx
94
94
  nolo machine status
95
95
  nolo run "summarize my latest agent dialogs"
96
96
  nolo doc create --title "Trip Notes" --body "hello" --sync local,us --dry-run
@@ -100,7 +100,6 @@ nolo space read 01KKY77TT0DA9NY7TNW3R7255N --content-key page-user-id --brief
100
100
  nolo agent list --json
101
101
  nolo agent bind-current agent-user-1-agent-1
102
102
  nolo agent runtime-doctor agent-user-1-agent-1
103
- nolo agent smoke-bound agent-user-1-agent-1 --msg "ping"
104
103
  nolo agent smoke-current agent-user-1-agent-1 --msg "ping"
105
104
  nolo chat --agent agent-pub-01APPBUILDER00000001YAII3I --msg "你好"
106
105
  ```
@@ -117,7 +116,7 @@ nolo connect # send one machine heartbeat
117
116
  nolo connect --watch # keep this terminal process online with periodic heartbeats
118
117
  nolo connect --ws # keep a live connector websocket for bound agent runs
119
118
  nolo connect --daemon # start connect --ws silently in the background
120
- nolo daemon --server-url https://api.nolo.chat --api-key sk_machine_xxx
119
+ nolo daemon --server-url https://api.nolo.chat --machine-key sk_machine_xxx
121
120
  nolo machine status # list machines registered to the current profile
122
121
  ```
123
122
 
@@ -127,13 +126,13 @@ They do not expose shell, file-write, or raw local LLM endpoints.
127
126
  Run a Slock-style connector command:
128
127
 
129
128
  ```bash
130
- nolo daemon --server-url https://api.nolo.chat --api-key sk_machine_xxx
129
+ nolo daemon --server-url https://api.nolo.chat --machine-key sk_machine_xxx
131
130
  ```
132
131
 
133
132
  Future published package shape:
134
133
 
135
134
  ```bash
136
- npx @nolo/daemon@latest --server-url https://api.nolo.chat --api-key sk_machine_xxx
135
+ npx @nolo/daemon@latest --server-url https://api.nolo.chat --machine-key sk_machine_xxx
137
136
  ```
138
137
 
139
138
  The daemon registers this computer, reports local CLI capabilities, keeps a live
@@ -153,7 +152,6 @@ Run a one-command connector smoke test:
153
152
 
154
153
  ```bash
155
154
  nolo agent runtime-doctor <agentKey>
156
- nolo agent smoke-bound <agentKey> --msg "ping"
157
155
  nolo agent smoke-current <agentKey> --msg "ping"
158
156
  ```
159
157
 
@@ -161,10 +159,6 @@ nolo agent smoke-current <agentKey> --msg "ping"
161
159
  machine has the required CLI capability. `smoke-current` heartbeats the current
162
160
  machine, binds the agent to it, opens a temporary connector websocket, calls
163
161
  `/api/agent/run`, and prints the returned dialog id/content.
164
- `smoke-bound` does not start a local connector or rebind anything. It validates
165
- the agent through its existing runtime binding by calling the real
166
- `/api/agent/run` path, so it is the safer probe for shared-Space member runs
167
- that should execute through another owner's online connector.
168
162
 
169
163
  ## Product Shape
170
164
 
@@ -64,6 +64,10 @@ export function resolveAgentRuntimeConfigFromRecord(
64
64
  const apiSource = stringField(record, "apiSource");
65
65
  const cliProvider = stringField(record, "cliProvider");
66
66
  const customProviderUrl = stringField(record, "customProviderUrl");
67
+ const apiKey = stringField(record, "apiKey");
68
+ const apiKeyHeader = stringField(record, "apiKeyHeader");
69
+ const apiKeyFromAgentKey = stringField(record, "apiKeyFromAgentKey");
70
+ const useServerProxy = record.useServerProxy === true;
67
71
  const provider = stringField(record, "provider") ?? stringField(record, "apiSource");
68
72
  const toolNames = resolveAgentRuntimeToolNames(record);
69
73
  const temperature = numberField(record, "temperature");
@@ -77,6 +81,7 @@ export function resolveAgentRuntimeConfigFromRecord(
77
81
  objectField(record, "runtimeToolPolicy") ??
78
82
  objectField(runtimeBinding ?? {}, "runtimeToolPolicy");
79
83
  const localWorkspaceMode = localWorkspaceModeField(record);
84
+ const delegation = objectField(record, "delegation");
80
85
  return {
81
86
  key,
82
87
  ...(name ? { name } : {}),
@@ -86,6 +91,10 @@ export function resolveAgentRuntimeConfigFromRecord(
86
91
  ...(apiSource ? { apiSource } : {}),
87
92
  ...(cliProvider ? { cliProvider } : {}),
88
93
  ...(customProviderUrl ? { customProviderUrl } : {}),
94
+ ...(apiKey ? { apiKey } : {}),
95
+ ...(apiKeyHeader ? { apiKeyHeader } : {}),
96
+ ...(apiKeyFromAgentKey ? { apiKeyFromAgentKey } : {}),
97
+ ...(useServerProxy ? { useServerProxy } : {}),
89
98
  ...(toolNames ? { toolNames } : {}),
90
99
  ...(temperature !== undefined ? { temperature } : {}),
91
100
  ...(topP !== undefined ? { top_p: topP } : {}),
@@ -96,6 +105,7 @@ export function resolveAgentRuntimeConfigFromRecord(
96
105
  ...(runtimeBinding ? { runtimeBinding } : {}),
97
106
  ...(runtimeToolPolicy ? { runtimeToolPolicy } : {}),
98
107
  ...(localWorkspaceMode ? { localWorkspaceMode } : {}),
108
+ ...(delegation ? { delegation } : {}),
99
109
  rawRecord: record,
100
110
  };
101
111
  }
@@ -14,6 +14,10 @@ export type AgentRuntimeAgentConfig = {
14
14
  apiSource?: string;
15
15
  cliProvider?: string;
16
16
  customProviderUrl?: string;
17
+ apiKey?: string;
18
+ apiKeyHeader?: string;
19
+ apiKeyFromAgentKey?: string;
20
+ useServerProxy?: boolean;
17
21
  toolNames?: string[];
18
22
  temperature?: number;
19
23
  top_p?: number;
@@ -24,18 +28,23 @@ export type AgentRuntimeAgentConfig = {
24
28
  runtimeBinding?: Record<string, unknown>;
25
29
  runtimeToolPolicy?: AgentRuntimeToolPolicy;
26
30
  localWorkspaceMode?: "current" | "task-worktree";
31
+ delegation?: Record<string, unknown>;
27
32
  rawRecord?: Record<string, unknown>;
28
33
  };
29
34
 
30
35
  export type AgentRuntimeProvider = {
31
36
  model: string;
32
- complete(messages: AgentRuntimeChatMessage[]): Promise<AgentRuntimeResult>;
37
+ complete(
38
+ messages: AgentRuntimeChatMessage[],
39
+ options?: { timeoutMs?: number }
40
+ ): Promise<AgentRuntimeResult>;
33
41
  };
34
42
 
35
43
  export type AgentRuntimeToolCallInput = {
36
44
  id: string;
37
45
  name: string;
38
46
  arguments: string;
47
+ userInput?: string;
39
48
  };
40
49
 
41
50
  export type AgentRuntimeToolResult = {
@@ -10,6 +10,11 @@ export {
10
10
  parseOpenAiCompatibleChatCompletionResponse,
11
11
  } from "./openAiCompatibleProvider";
12
12
  export { resolveOpenAiCompatibleProviderConfig } from "./openAiCompatibleProviderConfig";
13
+ export {
14
+ buildProviderAuthHeaders,
15
+ buildProviderExecutionPlan,
16
+ resolveAgentProviderMode,
17
+ } from "./providerResolution";
13
18
  export {
14
19
  buildPlatformChatCompletionRequest,
15
20
  canUsePlatformChatProvider,
@@ -27,11 +32,6 @@ export {
27
32
  } from "./agentRecordKeys";
28
33
  export { resolveAgentRuntimeConfigFromRecord } from "./agentRecordConfig";
29
34
  export { dialogMessageRecordToAgentRuntimeMessage } from "./dialogMessageRecord";
30
- export {
31
- renderDialogStatus,
32
- resolveDialogActiveHealth,
33
- resolveDialogTerminalState,
34
- } from "./dialogStatus";
35
35
  export { buildAgentRuntimeDialogWritePlan } from "./dialogWritePlan";
36
36
  export {
37
37
  createHybridRecordStore,
@@ -46,16 +46,14 @@ export {
46
46
  formatPreparedGitTaskWorkspace,
47
47
  prepareGitTaskWorkspace,
48
48
  } from "./taskWorkspace";
49
- export {
50
- createHostRuntimeConfig,
51
- createHostRuntimeCapabilities,
52
- resolveHostRuntimeConfigFromEnv,
53
- resolveHostRuntimeCapabilitiesFromEnv,
54
- } from "./hostRuntimeConfig";
55
49
  export {
56
50
  executeLocalToolWithPolicy,
57
51
  resolveLocalToolPolicy,
58
52
  } from "./localToolPolicy";
53
+ export {
54
+ evaluateShellCommandPolicy,
55
+ isDestructiveShellCommand,
56
+ } from "./shellCommandPolicy";
59
57
  export {
60
58
  mergeAgentRuntimeToolPolicies,
61
59
  normalizeAgentRuntimeToolPolicy,
@@ -93,12 +91,6 @@ export type {
93
91
  export type {
94
92
  PreparedGitTaskWorkspace,
95
93
  } from "./taskWorkspace";
96
- export type {
97
- HostRuntimeConfig,
98
- HostRuntimeCapabilities,
99
- HostRuntimeSurface,
100
- HostRuntimeWorkspaceBinding,
101
- } from "./hostRuntimeConfig";
102
94
  export type {
103
95
  LocalToolPolicyDecision,
104
96
  } from "./localToolPolicy";
@@ -108,9 +100,6 @@ export type {
108
100
  export type {
109
101
  PlatformChatProviderConfig,
110
102
  } from "./platformChatProvider";
111
- export type {
112
- DialogStatusSnapshot,
113
- } from "./dialogStatus";
114
103
  export type {
115
104
  AgentRuntimeChatMessage,
116
105
  AgentRuntimeDecision,
@@ -13,6 +13,7 @@ export type LocalAgentTurnInput = {
13
13
  input: AgentRuntimeMessageContent;
14
14
  continueDialogId?: string;
15
15
  maxToolRounds?: number;
16
+ timeoutMs?: number;
16
17
  onToolEvent?: (event: LocalAgentToolEvent) => void;
17
18
  };
18
19
 
@@ -40,6 +41,29 @@ function formatToolExecutionError(args: {
40
41
  return `${args.toolName} failed: ${message}`;
41
42
  }
42
43
 
44
+ function formatStructuredToolExecutionError(args: {
45
+ toolName: string;
46
+ error: unknown;
47
+ }) {
48
+ if (!args.error || typeof args.error !== "object") return null;
49
+ const error = args.error as {
50
+ code?: unknown;
51
+ message?: unknown;
52
+ policy?: unknown;
53
+ };
54
+ if (typeof error.code !== "string") return null;
55
+ return JSON.stringify({
56
+ error: error.code,
57
+ message:
58
+ typeof error.message === "string"
59
+ ? error.message
60
+ : formatToolExecutionError(args),
61
+ ...(error.policy && typeof error.policy === "object"
62
+ ? { policy: error.policy }
63
+ : {}),
64
+ });
65
+ }
66
+
43
67
  function shouldReturnToolExecutionErrors(adapter: AgentRuntimeHostAdapter) {
44
68
  return adapter.capabilities.includes("local-tools");
45
69
  }
@@ -66,6 +90,16 @@ function buildMessages(args: {
66
90
  ];
67
91
  }
68
92
 
93
+ function extractUserInputText(content: AgentRuntimeMessageContent): string {
94
+ if (typeof content === "string") return content;
95
+ if (!Array.isArray(content)) return "";
96
+ return content
97
+ .map((part) => (part?.type === "text" ? part.text : ""))
98
+ .filter(Boolean)
99
+ .join("\n")
100
+ .trim();
101
+ }
102
+
69
103
  export async function runLocalAgentTurn(
70
104
  input: LocalAgentTurnInput
71
105
  ): Promise<LocalAgentTurnResult> {
@@ -92,10 +126,13 @@ export async function runLocalAgentTurn(
92
126
  });
93
127
  const provider = await input.adapter.resolveProvider(agentConfig);
94
128
  const maxToolRounds = input.maxToolRounds ?? DEFAULT_LOCAL_AGENT_MAX_TOOL_ROUNDS;
129
+ const userInputText = extractUserInputText(input.input);
95
130
  let toolCallCount = 0;
96
131
  let result: AgentRuntimeResult;
97
132
  for (let round = 0; round <= maxToolRounds; round += 1) {
98
- result = await provider.complete(messages);
133
+ result = await provider.complete(messages, {
134
+ ...(typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {}),
135
+ });
99
136
  const toolCalls = result.tool_calls ?? [];
100
137
  if (toolCalls.length === 0) break;
101
138
  if (round === maxToolRounds) {
@@ -122,6 +159,7 @@ export async function runLocalAgentTurn(
122
159
  id: toolCall.id,
123
160
  name: toolName,
124
161
  arguments: toolCall.function.arguments,
162
+ ...(userInputText ? { userInput: userInputText } : {}),
125
163
  });
126
164
  emitToolEvent(input, {
127
165
  type: "tool-result",
@@ -140,11 +178,20 @@ export async function runLocalAgentTurn(
140
178
  message: error instanceof Error ? error.message : String(error),
141
179
  });
142
180
  toolResult = {
143
- content: formatToolExecutionError({ toolName, error }),
181
+ content:
182
+ formatStructuredToolExecutionError({ toolName, error }) ??
183
+ formatToolExecutionError({ toolName, error }),
144
184
  metadata: {
145
185
  error: true,
146
186
  toolName,
147
187
  message: error instanceof Error ? error.message : String(error),
188
+ ...(
189
+ error &&
190
+ typeof error === "object" &&
191
+ typeof (error as { code?: unknown }).code === "string"
192
+ ? { code: (error as { code: string }).code }
193
+ : {}
194
+ ),
148
195
  },
149
196
  };
150
197
  }
@@ -2,7 +2,8 @@ import type {
2
2
  AgentRuntimeToolCallInput,
3
3
  AgentRuntimeToolResult,
4
4
  } from "./types";
5
- import type { HostRuntimeConfig } from "./hostRuntimeConfig";
5
+ import { canonicalizeToolName } from "../ai/tools/toolNameAliases";
6
+ import { evaluateShellCommandPolicy } from "./shellCommandPolicy";
6
7
 
7
8
  type EnvLike = Record<string, string | undefined>;
8
9
 
@@ -49,6 +50,26 @@ function parseToolAllowlist(value: string | undefined) {
49
50
  .filter(Boolean);
50
51
  }
51
52
 
53
+ function normalizeLocalToolName(toolName: string) {
54
+ return canonicalizeToolName(
55
+ String(toolName ?? "").replace(/^functions\./, "").trim(),
56
+ );
57
+ }
58
+
59
+ function parseShellCommandPayload(rawArguments: string) {
60
+ try {
61
+ const parsed = JSON.parse(rawArguments || "{}");
62
+ if (!parsed || typeof parsed !== "object") return {};
63
+ return parsed as { command?: unknown; cmd?: unknown; input?: unknown };
64
+ } catch {
65
+ return {};
66
+ }
67
+ }
68
+
69
+ function isLocalShellModeEnabled(env: EnvLike) {
70
+ return ["worktree", "dangerous", "1", "true"].includes(env.NOLO_LOCAL_SHELL_MODE || "");
71
+ }
72
+
52
73
  function isRestrictedLocalToolMode(env: EnvLike) {
53
74
  return env.NOLO_LOCAL_TOOL_MODE === "restricted";
54
75
  }
@@ -66,84 +87,105 @@ function isToolDeclaredOrExplicitlyFree(args: {
66
87
 
67
88
  export function resolveLocalToolPolicy(args: {
68
89
  env: EnvLike;
69
- hostRuntimeConfig?: HostRuntimeConfig;
70
90
  agentToolNames?: string[];
71
91
  toolName: string;
72
92
  }): LocalToolPolicyDecision {
93
+ const toolName = normalizeLocalToolName(args.toolName);
73
94
  const restrictedMode = isRestrictedLocalToolMode(args.env);
74
- if (args.toolName === "execShell") {
75
- if (!(args.agentToolNames ?? []).includes("execShell")) {
76
- return {
77
- allowed: false,
78
- toolName: args.toolName,
79
- reason: "execShell is not requested by this agent runtime.",
80
- };
81
- }
82
- if (args.hostRuntimeConfig?.capabilities.allowShell === true) {
83
- return { allowed: true, toolName: args.toolName };
95
+ if (!toolName) {
96
+ return {
97
+ allowed: false,
98
+ toolName,
99
+ reason: "Tool name is required.",
100
+ };
101
+ }
102
+
103
+ if (toolName === "execShell") {
104
+ if (isLocalShellModeEnabled(args.env)) {
105
+ return { allowed: true, toolName };
84
106
  }
85
107
  return {
86
108
  allowed: false,
87
- toolName: args.toolName,
88
- reason: "execShell requires host shell capability.",
109
+ toolName,
110
+ reason: "execShell requires NOLO_LOCAL_SHELL_MODE=worktree.",
89
111
  };
90
112
  }
91
113
 
92
- if (NEVER_LOCAL_TOOLS.has(args.toolName)) {
114
+ if (NEVER_LOCAL_TOOLS.has(toolName)) {
93
115
  return {
94
116
  allowed: false,
95
- toolName: args.toolName,
96
- reason: `${args.toolName} is blocked by the local runtime safety policy.`,
117
+ toolName,
118
+ reason: `${toolName} is blocked by the local runtime safety policy.`,
97
119
  };
98
120
  }
99
121
 
100
- if (REMOVED_LOCAL_TOOLS.has(args.toolName)) {
122
+ if (REMOVED_LOCAL_TOOLS.has(toolName)) {
101
123
  return {
102
124
  allowed: false,
103
- toolName: args.toolName,
104
- reason: `${args.toolName} has been removed from the local runtime tool surface. Use execShell in shell-enabled workspace runs when shell is needed.`,
125
+ toolName,
126
+ reason: `${toolName} has been removed from the local runtime tool surface. Use execShell in shell-enabled workspace runs when shell is needed.`,
105
127
  };
106
128
  }
107
129
 
108
130
  const allowedByEnv = new Set(parseToolAllowlist(args.env.NOLO_LOCAL_ALLOWED_TOOLS));
109
- const agentTools = new Set(args.agentToolNames ?? []);
110
- if (!restrictedMode && agentTools.has(args.toolName)) {
111
- return { allowed: true, toolName: args.toolName };
131
+ const agentTools = new Set(
132
+ (args.agentToolNames ?? []).map(normalizeLocalToolName).filter(Boolean),
133
+ );
134
+ if (!restrictedMode && agentTools.has(toolName)) {
135
+ return { allowed: true, toolName };
112
136
  }
113
- if (DEFAULT_LOCAL_TOOLS.has(args.toolName) && agentTools.has(args.toolName)) {
114
- return { allowed: true, toolName: args.toolName };
137
+ if (DEFAULT_LOCAL_TOOLS.has(toolName) && agentTools.has(toolName)) {
138
+ return { allowed: true, toolName };
115
139
  }
116
- if (allowedByEnv.has(args.toolName) && agentTools.has(args.toolName)) {
117
- return { allowed: true, toolName: args.toolName };
140
+ if (allowedByEnv.has(toolName) && agentTools.has(toolName)) {
141
+ return { allowed: true, toolName };
118
142
  }
119
143
 
120
144
  return {
121
145
  allowed: false,
122
- toolName: args.toolName,
146
+ toolName,
123
147
  reason:
124
- `${args.toolName} is not enabled for local runtime runs. ` +
148
+ `${toolName} is not enabled for local runtime runs. ` +
125
149
  "Set NOLO_LOCAL_ALLOWED_TOOLS and make sure the agent declares the tool before using it locally.",
126
150
  };
127
151
  }
128
152
 
129
153
  export async function executeLocalToolWithPolicy(args: {
130
154
  env: EnvLike;
131
- hostRuntimeConfig?: HostRuntimeConfig;
132
155
  agentToolNames?: string[];
133
156
  call: AgentRuntimeToolCallInput;
134
157
  executors?: Record<string, (call: AgentRuntimeToolCallInput) => Promise<AgentRuntimeToolResult>>;
135
158
  }): Promise<AgentRuntimeToolResult> {
136
159
  const decision = resolveLocalToolPolicy({
137
160
  env: args.env,
138
- hostRuntimeConfig: args.hostRuntimeConfig,
139
161
  agentToolNames: args.agentToolNames,
140
162
  toolName: args.call.name,
141
163
  });
142
164
  if (!decision.allowed) throw new Error(decision.reason);
143
165
 
144
- const executor = args.executors?.[args.call.name];
166
+ const executor = args.executors?.[decision.toolName];
145
167
  if (!executor) {
146
- throw new Error(`${args.call.name} is allowed by policy but no local executor is registered.`);
168
+ throw new Error(`${decision.toolName} is allowed by policy but no local executor is registered.`);
147
169
  }
148
- return executor(args.call);
170
+ if (decision.toolName === "execShell") {
171
+ const parsed = parseShellCommandPayload(args.call.arguments);
172
+ const shellPolicy = evaluateShellCommandPolicy({
173
+ command: parsed.command ?? parsed.cmd,
174
+ input: parsed.input,
175
+ userInput: args.call.userInput,
176
+ });
177
+ if (shellPolicy.verdict === "forbidden") {
178
+ const error = new Error(shellPolicy.reason) as Error & {
179
+ code?: string;
180
+ policy?: Record<string, unknown>;
181
+ };
182
+ error.code = shellPolicy.code;
183
+ error.policy = shellPolicy.policy;
184
+ throw error;
185
+ }
186
+ }
187
+ return executor({
188
+ ...args.call,
189
+ name: decision.toolName,
190
+ });
149
191
  }