nolo-cli 0.1.32 → 0.1.33

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 (230) hide show
  1. package/README.md +0 -6
  2. package/agent-runtime/agentRecordConfig.ts +2 -0
  3. package/agent-runtime/hostAdapter.ts +1 -0
  4. package/agent-runtime/index.ts +0 -21
  5. package/agent-runtime/localToolPolicy.ts +6 -13
  6. package/agent-runtime/localWorkspaceTools.ts +30 -333
  7. package/agent-runtime/platformChatProvider.ts +1 -4
  8. package/agent-runtime/runtimeToolPolicy.ts +47 -32
  9. package/agentAliases.ts +14 -83
  10. package/agentCommandSupport.ts +3 -6
  11. package/agentInternalCommandEntries.ts +2 -13
  12. package/agentListCommands.ts +19 -28
  13. package/agentMachineCommands.ts +40 -336
  14. package/agentPullCommand.ts +36 -56
  15. package/agentRecordCommands.ts +3 -5
  16. package/agentRecordHelpers.ts +22 -189
  17. package/agentRunCommand.ts +438 -107
  18. package/agentRuntimeLocal.ts +0 -10
  19. package/ai/agent/cliExecutor.ts +19 -323
  20. package/authCommands.ts +2 -36
  21. package/cli/agentAliases.ts +14 -83
  22. package/cli/agentCommandSupport.ts +3 -6
  23. package/cli/agentInternalCommandEntries.ts +2 -13
  24. package/cli/agentListCommands.ts +19 -28
  25. package/cli/agentMachineCommands.ts +40 -336
  26. package/cli/agentPullCommand.ts +36 -56
  27. package/cli/agentRecordCommands.ts +3 -5
  28. package/cli/agentRecordHelpers.ts +22 -189
  29. package/cli/agentRunCommand.ts +438 -107
  30. package/cli/agentRuntimeLocal.ts +0 -10
  31. package/cli/authCommands.ts +2 -36
  32. package/cli/cliEnvHelpers.ts +21 -162
  33. package/cli/client/agentRun.ts +853 -57
  34. package/cli/client/localRuntimeAdapter.ts +22 -159
  35. package/cli/client/streamingOutput.ts +3 -1
  36. package/cli/client/taskRunPrompt.ts +24 -7
  37. package/cli/commandRegistry.ts +2 -3
  38. package/cli/connectorRunArtifact.ts +10 -34
  39. package/cli/connectorWebSocketTarget.ts +6 -10
  40. package/cli/internalCommandEntries.ts +0 -8
  41. package/cli/localRuntimeDb.ts +2 -39
  42. package/cli/machineCommands.ts +189 -190
  43. package/cli/machineDaemonCommands.ts +165 -9
  44. package/cli/machineHeartbeatCommands.ts +2 -1
  45. package/cli/machineStatusCommands.ts +13 -5
  46. package/cli/machineWatchCommands.ts +1 -1
  47. package/cli/machineWsRunDispatch.ts +14 -48
  48. package/cli/machineWsSession.ts +0 -2
  49. package/cli/offlineMarxistsAgentCommand.ts +314 -0
  50. package/cli/runtimeDoctorCommands.ts +69 -18
  51. package/cli/scriptCommandEntries.ts +45 -5
  52. package/cli/tableCommands.ts +133 -33
  53. package/cli/taskRunCommand.ts +211 -16
  54. package/cli/tui/session.ts +372 -43
  55. package/cli/workflowInternalCommandEntries.ts +0 -20
  56. package/cliEnvHelpers.ts +21 -162
  57. package/client/agentConfigResolver.test.ts +2 -0
  58. package/client/agentRun.test.ts +12 -208
  59. package/client/agentRun.ts +853 -57
  60. package/client/localRuntimeAdapter.test.ts +9 -168
  61. package/client/localRuntimeAdapter.ts +22 -159
  62. package/client/localToolPolicy.test.ts +4 -28
  63. package/client/streamingOutput.ts +3 -1
  64. package/client/taskRunPrompt.ts +24 -7
  65. package/commandRegistry.ts +2 -3
  66. package/connectorRunArtifact.ts +10 -34
  67. package/connectorWebSocketTarget.ts +6 -10
  68. package/database/server/db.ts +34 -15
  69. package/internalCommandEntries.ts +0 -8
  70. package/localRuntimeDb.ts +2 -39
  71. package/machineCommands.ts +189 -190
  72. package/machineDaemonCommands.ts +165 -9
  73. package/machineHeartbeatCommands.ts +2 -1
  74. package/machineStatusCommands.ts +13 -5
  75. package/machineWatchCommands.ts +1 -1
  76. package/machineWsRunDispatch.ts +14 -48
  77. package/machineWsSession.ts +0 -2
  78. package/offlineMarxistsAgentCommand.ts +314 -0
  79. package/package.json +49 -193
  80. package/runtimeDoctorCommands.ts +69 -18
  81. package/scriptCommandEntries.ts +45 -5
  82. package/tableCommands.ts +133 -33
  83. package/taskRunCommand.ts +211 -16
  84. package/tui/session.ts +372 -43
  85. package/workflowInternalCommandEntries.ts +0 -20
  86. package/agent-runtime/dialogStatus.ts +0 -139
  87. package/agent-runtime/hostRuntimeConfig.ts +0 -86
  88. package/agentCreateCustomCommands.ts +0 -278
  89. package/agentDialogsCommands.ts +0 -227
  90. package/agentDoctorCommands.ts +0 -203
  91. package/agentRunLocalWorkspace.ts +0 -182
  92. package/agentRunParsing.ts +0 -298
  93. package/agentSuperviseCommands.ts +0 -8
  94. package/agentUnpublishCommands.ts +0 -113
  95. package/ai/agent/agentRunDialogLineage.ts +0 -56
  96. package/ai/policy/types.ts +0 -64
  97. package/ai/skills/skillSummaryMarker.ts +0 -26
  98. package/app/types.ts +0 -358
  99. package/build.ts +0 -16
  100. package/buildPublish.ts +0 -138
  101. package/buildPublishArtifactAssembly.ts +0 -724
  102. package/buildPublishImportAnalysis.ts +0 -242
  103. package/chat/messages/fetchMessages.ts +0 -84
  104. package/chat/messages/types.ts +0 -185
  105. package/cli/agentCreateCustomCommands.ts +0 -278
  106. package/cli/agentDialogsCommands.ts +0 -227
  107. package/cli/agentDoctorCommands.ts +0 -203
  108. package/cli/agentRunLocalWorkspace.ts +0 -182
  109. package/cli/agentRunParsing.ts +0 -298
  110. package/cli/agentSuperviseCommands.ts +0 -8
  111. package/cli/agentUnpublishCommands.ts +0 -113
  112. package/cli/buildPublish.ts +0 -138
  113. package/cli/buildPublishArtifactAssembly.ts +0 -724
  114. package/cli/buildPublishImportAnalysis.ts +0 -242
  115. package/cli/cliIoTypes.ts +0 -3
  116. package/cli/cliRuntimeDoctorReport.ts +0 -193
  117. package/cli/client/agentRunInput.ts +0 -10
  118. package/cli/client/agentRunLocalExecution.ts +0 -124
  119. package/cli/client/agentRunLocalExecutor.ts +0 -179
  120. package/cli/client/agentRunRuntimeSelection.ts +0 -145
  121. package/cli/client/agentRunServerTransport.ts +0 -397
  122. package/cli/client/agentRunTaskRunRecorder.ts +0 -176
  123. package/cli/customCodingAgentFlow.ts +0 -208
  124. package/cli/customCodingAgentRecord.ts +0 -179
  125. package/cli/dialogDataAccess.ts +0 -186
  126. package/cli/dialogInternalCommandEntries.ts +0 -11
  127. package/cli/dialogReadCommands.ts +0 -180
  128. package/cli/dialogReadHelpers.ts +0 -226
  129. package/cli/dialogStatusCommands.ts +0 -123
  130. package/cli/docInternalCommandEntries.ts +0 -20
  131. package/cli/hybridReadHelpers.ts +0 -109
  132. package/cli/machineCommandPrompts.ts +0 -79
  133. package/cli/machineCommandResolution.ts +0 -43
  134. package/cli/noloReadBridgePrompt.ts +0 -16
  135. package/cli/pageCreateCommands.ts +0 -219
  136. package/cli/pageDeleteCommands.ts +0 -167
  137. package/cli/pageReadCommands.ts +0 -151
  138. package/cli/pageUpdateCommands.ts +0 -249
  139. package/cli/pageWriteHelpers.ts +0 -209
  140. package/cli/publishPackage.ts +0 -36
  141. package/cli/runtimeInternalCommandEntries.ts +0 -15
  142. package/cli/runtimePassthroughCommands.ts +0 -22
  143. package/cli/scriptPassthroughHelpers.ts +0 -28
  144. package/cli/secretScanHelpers.ts +0 -31
  145. package/cli/spaceAttachCommands.ts +0 -134
  146. package/cli/spaceCategoryCommands.ts +0 -263
  147. package/cli/spaceCommands.ts +0 -295
  148. package/cli/spaceDeleteCommands.ts +0 -312
  149. package/cli/spaceInternalCommandEntries.ts +0 -21
  150. package/cli/spaceMutationHelpers.ts +0 -135
  151. package/cli/spaceReadCommands.ts +0 -323
  152. package/cli/tableDataOutput.ts +0 -41
  153. package/cli/tableDataSafety.ts +0 -26
  154. package/cli/tableMetaCommands.ts +0 -307
  155. package/cli/tableMutationCommands.ts +0 -266
  156. package/cli/tableQueryParsing.ts +0 -90
  157. package/cli/tableQueryRequest.ts +0 -53
  158. package/cli/taskRunActions.ts +0 -122
  159. package/cli/taskRunParsing.ts +0 -86
  160. package/cli/taskRunPromptShared.ts +0 -55
  161. package/cli/tui/tuiCatalog.ts +0 -22
  162. package/cli/tui/tuiCommandInterpreter.ts +0 -253
  163. package/cli/tui/tuiPresentation.ts +0 -146
  164. package/cliIoTypes.ts +0 -3
  165. package/cliRuntimeDoctorReport.ts +0 -193
  166. package/client/agentRunInput.ts +0 -10
  167. package/client/agentRunLocalExecution.test.ts +0 -123
  168. package/client/agentRunLocalExecution.ts +0 -124
  169. package/client/agentRunLocalExecutor.ts +0 -179
  170. package/client/agentRunRuntimeSelection.test.ts +0 -159
  171. package/client/agentRunRuntimeSelection.ts +0 -145
  172. package/client/agentRunServerTransport.ts +0 -397
  173. package/client/agentRunTaskRunRecorder.ts +0 -176
  174. package/connector-experimental/daemonLauncher.ts +0 -223
  175. package/create/space/spaceKeys.ts +0 -65
  176. package/create/types.ts +0 -21
  177. package/customCodingAgentFlow.ts +0 -208
  178. package/customCodingAgentRecord.ts +0 -179
  179. package/database/keys.ts +0 -765
  180. package/database/server/authorityStoreTypes.ts +0 -31
  181. package/database/server/legacyServerDb.ts +0 -58
  182. package/database/server/levelAuthorityStore.ts +0 -75
  183. package/database/server/memoryAuthorityStore.ts +0 -133
  184. package/database/server/serverStoreFactory.ts +0 -124
  185. package/database/tombstones.ts +0 -52
  186. package/database/utils/ulid.ts +0 -1
  187. package/dialogDataAccess.ts +0 -186
  188. package/dialogInternalCommandEntries.ts +0 -11
  189. package/dialogReadCommands.ts +0 -180
  190. package/dialogReadHelpers.ts +0 -226
  191. package/dialogStatusCommands.ts +0 -123
  192. package/docInternalCommandEntries.ts +0 -20
  193. package/hybridReadHelpers.ts +0 -109
  194. package/machineCommandPrompts.ts +0 -79
  195. package/machineCommandResolution.ts +0 -43
  196. package/noloReadBridgePrompt.ts +0 -16
  197. package/pageCreateCommands.ts +0 -219
  198. package/pageDeleteCommands.ts +0 -167
  199. package/pageReadCommands.ts +0 -151
  200. package/pageUpdateCommands.ts +0 -249
  201. package/pageWriteHelpers.ts +0 -209
  202. package/publishPackage.ts +0 -36
  203. package/runtimeInternalCommandEntries.ts +0 -15
  204. package/runtimePassthroughCommands.ts +0 -22
  205. package/scriptPassthroughHelpers.ts +0 -28
  206. package/secretScanHelpers.ts +0 -31
  207. package/share/helpers.ts +0 -302
  208. package/share/keys.ts +0 -243
  209. package/share/types.ts +0 -100
  210. package/spaceAttachCommands.ts +0 -134
  211. package/spaceCategoryCommands.ts +0 -263
  212. package/spaceCommands.ts +0 -295
  213. package/spaceDeleteCommands.ts +0 -312
  214. package/spaceInternalCommandEntries.ts +0 -21
  215. package/spaceMutationHelpers.ts +0 -135
  216. package/spaceReadCommands.ts +0 -323
  217. package/tableDataOutput.ts +0 -41
  218. package/tableDataSafety.ts +0 -26
  219. package/tableMetaCommands.ts +0 -307
  220. package/tableMutationCommands.ts +0 -266
  221. package/tableQueryParsing.ts +0 -90
  222. package/tableQueryRequest.ts +0 -53
  223. package/taskRunActions.ts +0 -122
  224. package/taskRunParsing.ts +0 -86
  225. package/taskRunPromptShared.ts +0 -55
  226. package/tui/tuiCatalog.ts +0 -22
  227. package/tui/tuiCommandInterpreter.test.ts +0 -169
  228. package/tui/tuiCommandInterpreter.ts +0 -253
  229. package/tui/tuiPresentation.test.ts +0 -154
  230. package/tui/tuiPresentation.ts +0 -146
@@ -5,7 +5,6 @@ import type {
5
5
  AgentRuntimeToolCallInput,
6
6
  AgentRuntimeToolResult,
7
7
  } from "./types";
8
- import type { HostRuntimeConfig } from "./hostRuntimeConfig";
9
8
 
10
9
  type LocalWorkspaceToolArgs = {
11
10
  workspaceRoot: string;
@@ -19,13 +18,9 @@ type WorkspaceFileArgs = {
19
18
  oldText?: unknown;
20
19
  newText?: unknown;
21
20
  expectedReplacements?: unknown;
22
- edits?: unknown;
23
- offset?: unknown;
24
- limit?: unknown;
25
21
  query?: unknown;
26
22
  command?: unknown;
27
23
  cmd?: unknown;
28
- timeout?: unknown;
29
24
  patch?: unknown;
30
25
  script?: unknown;
31
26
  args?: unknown;
@@ -79,13 +74,6 @@ const DEFAULT_LOCAL_CODING_TOOL_NAMES = [
79
74
  "searchFiles",
80
75
  ] as const;
81
76
 
82
- const DEFAULT_LOCAL_CODING_TOOL_NAMES_WITH_SHELL = [
83
- "readFile",
84
- "writeFile",
85
- "editFile",
86
- "execShell",
87
- ] as const;
88
-
89
77
  const SHELL_TOOL_NAMES = ["execShell"] as const;
90
78
  const WORKSPACE_TOOL_NAME_SET = new Set<string>(WORKSPACE_TOOL_NAMES);
91
79
  const REMOVED_WORKSPACE_TOOL_NAMES = new Set([
@@ -127,19 +115,11 @@ function buildReadWorkspaceFileTool(): OpenAiCompatibleTool {
127
115
  function: {
128
116
  name: "readWorkspaceFile",
129
117
  description:
130
- "Read a UTF-8 text file inside the workspace. Read before editing when you need the current content or exact text for replaceWorkspaceText; skip unnecessary reads for trivial known writes. Use offset and limit to page large files by line range.",
118
+ "Read a UTF-8 text file inside the workspace. Read before editing when you need the current content or exact text for replaceWorkspaceText; skip unnecessary reads for trivial known writes.",
131
119
  parameters: {
132
120
  type: "object",
133
121
  properties: {
134
122
  path: buildWorkspacePathProperty(),
135
- offset: {
136
- type: "integer",
137
- description: "Optional zero-based starting line offset.",
138
- },
139
- limit: {
140
- type: "integer",
141
- description: "Optional maximum number of lines to return.",
142
- },
143
123
  },
144
124
  required: ["path"],
145
125
  },
@@ -192,26 +172,8 @@ function buildReplaceWorkspaceTextTool(): OpenAiCompatibleTool {
192
172
  type: "integer",
193
173
  description: "Expected replacement count. Defaults to 1.",
194
174
  },
195
- edits: {
196
- type: "array",
197
- description: "Optional batch exact-text replacements applied against the original file snapshot.",
198
- items: {
199
- type: "object",
200
- properties: {
201
- oldText: {
202
- type: "string",
203
- description: "Exact text currently present in the file.",
204
- },
205
- newText: {
206
- type: "string",
207
- description: "Replacement text to write in place of oldText.",
208
- },
209
- },
210
- required: ["oldText", "newText"],
211
- },
212
- },
213
175
  },
214
- required: ["path"],
176
+ required: ["path", "oldText", "newText"],
215
177
  },
216
178
  },
217
179
  };
@@ -354,7 +316,7 @@ function buildExecShellTool(toolName: string): OpenAiCompatibleTool {
354
316
  function: {
355
317
  name: toolName,
356
318
  description:
357
- "Run a shell command in the local workspace. Use execShell for ls, rg/grep, find, and git inspection when shell is available. Commands already execute from the workspace root; do not cd into guessed paths such as /workspace, /repo, /home/user, or /home/user/workspace. Prefer one command that performs a complete verification or related git operation instead of many tiny commands. For branch setup, prefer idempotent commands such as git switch -C <branch> when replacing or recreating a benchmark branch is acceptable. Use portable POSIX commands for macOS/BSD shells; avoid GNU-only flags such as cat -A. Do not use cat -A. Do not use brittle byte-offset commands such as xxd -s -32. For separator lines, use echo or printf '%s\\n'. For text-file content or trailing-newline checks, prefer readFile over shell byte inspection when that tool is available. Do not run repeated git status, git log, git rev-parse, or branch checks after a successful command already returned the needed clean status and commit information; use one final verification command. If a command fails, use the error output to adjust the next command rather than repeating the same shape. When output is truncated, the tool returns a workspace temp path for the full output.",
319
+ "Run a shell command in the local workspace. Commands already execute from the workspace root; do not cd into guessed paths such as /workspace, /repo, /home/user, or /home/user/workspace. Prefer one command that performs a complete verification or related git operation instead of many tiny commands. For branch setup, prefer idempotent commands such as git switch -C <branch> when replacing or recreating a benchmark branch is acceptable. Use portable POSIX commands for macOS/BSD shells; avoid GNU-only flags such as cat -A. Do not use cat -A. Do not use brittle byte-offset commands such as xxd -s -32. For separator lines, use echo or printf '%s\\n'. For text-file content or trailing-newline checks, prefer readFile over shell byte inspection when that tool is available. Do not run repeated git status, git log, git rev-parse, or branch checks after a successful command already returned the needed clean status and commit information; use one final verification command. If a command fails, use the error output to adjust the next command rather than repeating the same shape.",
358
320
  parameters: {
359
321
  type: "object",
360
322
  properties: {
@@ -366,10 +328,6 @@ function buildExecShellTool(toolName: string): OpenAiCompatibleTool {
366
328
  type: "string",
367
329
  description: "Shell command to run.",
368
330
  },
369
- timeout: {
370
- type: "integer",
371
- description: "Optional timeout in milliseconds. The runtime clamps values above its configured maximum.",
372
- },
373
331
  },
374
332
  },
375
333
  },
@@ -494,28 +452,18 @@ function filterDeclaredWorkspaceToolNames(args: {
494
452
  return (args.toolNames ?? []).filter((toolName) =>
495
453
  WORKSPACE_TOOL_NAME_SET.has(toolName) &&
496
454
  !REMOVED_WORKSPACE_TOOL_NAMES.has(toolName) &&
497
- (args.exposeShellTools || !SHELL_TOOL_NAMES.includes(toolName as any))
455
+ (args.exposeShellTools || !SHELL_TOOL_NAMES.includes(toolName as any))
498
456
  );
499
457
  }
500
458
 
501
- export function resolveLocalWorkspaceShellToolExposure(args: {
502
- declaredToolNames?: string[];
503
- hostRuntimeConfig?: Pick<HostRuntimeConfig, "capabilities">;
504
- }) {
505
- return args.hostRuntimeConfig?.capabilities.allowShell === true &&
506
- (args.declaredToolNames ?? []).includes("execShell");
507
- }
508
-
509
459
  export function buildLocalWorkspaceToolset(args: {
510
460
  declaredToolNames?: string[];
511
461
  exposeShellTools?: boolean;
512
462
  }) {
513
463
  const exposeShellTools = args.exposeShellTools === true;
514
- const defaultToolNames = exposeShellTools
515
- ? DEFAULT_LOCAL_CODING_TOOL_NAMES_WITH_SHELL
516
- : DEFAULT_LOCAL_CODING_TOOL_NAMES;
517
464
  const toolNames = removeRedundantWorkspaceToolAliases(new Set([
518
- ...defaultToolNames,
465
+ ...DEFAULT_LOCAL_CODING_TOOL_NAMES,
466
+ ...(exposeShellTools ? SHELL_TOOL_NAMES : []),
519
467
  ...filterDeclaredWorkspaceToolNames({
520
468
  toolNames: args.declaredToolNames,
521
469
  exposeShellTools,
@@ -590,31 +538,6 @@ function requireWorkspaceToolPath(args: WorkspaceFileArgs) {
590
538
  return requestedPath;
591
539
  }
592
540
 
593
- function readOptionalNonNegativeIntegerArg(value: unknown, label: string) {
594
- if (value === undefined) return undefined;
595
- const number = Number(value);
596
- if (!Number.isInteger(number) || number < 0) {
597
- throw new Error(`${label} must be a non-negative integer.`);
598
- }
599
- return number;
600
- }
601
-
602
- function readOptionalPositiveIntegerArg(value: unknown, label: string) {
603
- if (value === undefined) return undefined;
604
- const number = Number(value);
605
- if (!Number.isInteger(number) || number < 1) {
606
- throw new Error(`${label} must be a positive integer.`);
607
- }
608
- return number;
609
- }
610
-
611
- function readWorkspaceReadWindow(args: WorkspaceFileArgs) {
612
- return {
613
- offset: readOptionalNonNegativeIntegerArg(args.offset, "readWorkspaceFile offset") ?? 0,
614
- limit: readOptionalPositiveIntegerArg(args.limit, "readWorkspaceFile limit"),
615
- };
616
- }
617
-
618
541
  function requireWorkspaceFileContent(args: WorkspaceFileArgs) {
619
542
  if (typeof args.content !== "string") {
620
543
  throw new Error("writeWorkspaceFile requires string content.");
@@ -636,37 +559,6 @@ function requireWorkspaceNewText(args: WorkspaceFileArgs) {
636
559
  return args.newText;
637
560
  }
638
561
 
639
- type WorkspaceStructuredEdit = {
640
- oldText: string;
641
- newText: string;
642
- };
643
-
644
- function readWorkspaceStructuredEdits(args: WorkspaceFileArgs) {
645
- if (args.edits === undefined) return null;
646
- if (!Array.isArray(args.edits) || args.edits.length === 0) {
647
- throw new Error("editFile edits must be a non-empty array.");
648
- }
649
- return args.edits.map((entry, index) => {
650
- if (!entry || typeof entry !== "object") {
651
- throw new Error(`editFile edits[${index}] must be an object.`);
652
- }
653
- const oldText = typeof (entry as Record<string, unknown>).oldText === "string"
654
- ? ((entry as Record<string, unknown>).oldText as string)
655
- : "";
656
- if (!oldText) {
657
- throw new Error(`editFile edits[${index}] oldText must be a non-empty string.`);
658
- }
659
- const newTextValue = (entry as Record<string, unknown>).newText;
660
- if (typeof newTextValue !== "string") {
661
- throw new Error(`editFile edits[${index}] newText must be a string.`);
662
- }
663
- return {
664
- oldText,
665
- newText: newTextValue,
666
- };
667
- });
668
- }
669
-
670
562
  function readExpectedReplacementCount(args: WorkspaceFileArgs) {
671
563
  if (args.expectedReplacements === undefined) return 1;
672
564
  const value = Number(args.expectedReplacements);
@@ -687,79 +579,6 @@ function pluralizeReplacement(count: number) {
687
579
  return count === 1 ? "replacement" : "replacements";
688
580
  }
689
581
 
690
- function findUniqueEditRange(args: {
691
- content: string;
692
- oldText: string;
693
- requestedPath: string;
694
- index: number;
695
- }) {
696
- const start = args.content.indexOf(args.oldText);
697
- if (start < 0) {
698
- throw new Error(`editFile edits[${args.index}] oldText was not found in ${args.requestedPath}.`);
699
- }
700
- if (args.content.indexOf(args.oldText, start + 1) >= 0) {
701
- throw new Error(
702
- `editFile edits[${args.index}] oldText must match exactly once in ${args.requestedPath}.`
703
- );
704
- }
705
- return {
706
- start,
707
- end: start + args.oldText.length,
708
- };
709
- }
710
-
711
- function countLineNumberAtOffset(args: {
712
- content: string;
713
- offset: number;
714
- }) {
715
- let lineNumber = 1;
716
- for (let index = 0; index < args.offset; index += 1) {
717
- if (args.content[index] === "\n") {
718
- lineNumber += 1;
719
- }
720
- }
721
- return lineNumber;
722
- }
723
-
724
- function applyWorkspaceStructuredEdits(args: {
725
- content: string;
726
- edits: WorkspaceStructuredEdit[];
727
- requestedPath: string;
728
- }) {
729
- const ranges = args.edits.map((edit, index) => ({
730
- ...edit,
731
- index,
732
- ...findUniqueEditRange({
733
- content: args.content,
734
- oldText: edit.oldText,
735
- requestedPath: args.requestedPath,
736
- index,
737
- }),
738
- })).sort((left, right) => left.start - right.start);
739
- for (let index = 1; index < ranges.length; index += 1) {
740
- if (ranges[index]!.start < ranges[index - 1]!.end) {
741
- throw new Error(`editFile edits overlap in ${args.requestedPath}.`);
742
- }
743
- }
744
- let nextContent = args.content;
745
- for (const range of [...ranges].sort((left, right) => right.start - left.start)) {
746
- nextContent =
747
- nextContent.slice(0, range.start) +
748
- range.newText +
749
- nextContent.slice(range.end);
750
- }
751
- return {
752
- nextContent,
753
- firstChangedLine: ranges.length > 0
754
- ? countLineNumberAtOffset({
755
- content: args.content,
756
- offset: ranges[0]!.start,
757
- })
758
- : 1,
759
- replacements: ranges.length,
760
- };
761
- }
762
-
763
582
  function requireWorkspaceSearchQuery(args: WorkspaceFileArgs) {
764
583
  const query = typeof args.query === "string" ? args.query.trim() : "";
765
584
  if (!query) throw new Error("searchWorkspace requires a non-empty query.");
@@ -788,10 +607,6 @@ function requirePackageScript(args: WorkspaceFileArgs) {
788
607
  return script;
789
608
  }
790
609
 
791
- function readShellTimeoutMs(args: WorkspaceFileArgs) {
792
- return readOptionalPositiveIntegerArg(args.timeout, "execShell timeout");
793
- }
794
-
795
610
  function requireVisualWaitSelector(args: WorkspaceFileArgs) {
796
611
  const selector = typeof args.waitSelector === "string" ? args.waitSelector.trim() : "";
797
612
  if (!selector) throw new Error("captureVisualState requires a non-empty waitSelector.");
@@ -851,38 +666,9 @@ function formatAvailableScripts(scripts: string[]) {
851
666
  return scripts.length ? scripts.join(", ") : "(none)";
852
667
  }
853
668
 
854
- function splitContentIntoLines(content: string) {
855
- if (!content) return [];
856
- const lines: string[] = [];
857
- let start = 0;
858
- for (let index = 0; index < content.length; index += 1) {
859
- if (content[index] !== "\n") continue;
860
- lines.push(content.slice(start, index + 1));
861
- start = index + 1;
862
- }
863
- if (start < content.length) {
864
- lines.push(content.slice(start));
865
- }
866
- return lines;
867
- }
868
-
869
- function appendReadContinuationHint(content: string, hint: string) {
870
- if (!content) return hint;
871
- return content.endsWith("\n") ? `${content}\n${hint}` : `${content}\n\n${hint}`;
872
- }
873
-
874
669
  function truncateToolOutput(value: string, limit = 20_000) {
875
- if (value.length <= limit) {
876
- return { content: value, truncated: false };
877
- }
878
- return {
879
- content: `${value.slice(0, limit)}\n\n[truncated ${value.length - limit} chars]`,
880
- truncated: true,
881
- };
882
- }
883
-
884
- function sanitizeWorkspaceTempName(value: string) {
885
- return value.replace(/[^a-zA-Z0-9._-]+/g, "_");
670
+ if (value.length <= limit) return value;
671
+ return `${value.slice(0, limit)}\n\n[truncated ${value.length - limit} chars]`;
886
672
  }
887
673
 
888
674
  function parseLastJsonObject(text: string): Record<string, unknown> | null {
@@ -982,20 +768,16 @@ async function runWorkspaceCommand(args: {
982
768
  const stderr = timedOut
983
769
  ? `${rawStderr.trim() ? `${rawStderr.trim()}\n` : ""}command timed out after ${timeoutMs}ms\n`
984
770
  : rawStderr;
985
- const fullContent = [
986
- stdout.trim() ? `stdout:\n${stdout.trim()}` : "",
987
- stderr.trim() ? `stderr:\n${stderr.trim()}` : "",
988
- `exitCode: ${exitCode}`,
989
- ].filter(Boolean).join("\n\n");
990
- const summarizedContent = truncateToolOutput(fullContent);
991
771
  return {
992
772
  stdout,
993
773
  stderr,
994
774
  exitCode,
995
775
  timedOut,
996
- fullContent,
997
- outputTruncated: summarizedContent.truncated,
998
- content: summarizedContent.content,
776
+ content: truncateToolOutput([
777
+ stdout.trim() ? `stdout:\n${stdout.trim()}` : "",
778
+ stderr.trim() ? `stderr:\n${stderr.trim()}` : "",
779
+ `exitCode: ${exitCode}`,
780
+ ].filter(Boolean).join("\n\n")),
999
781
  };
1000
782
  }
1001
783
 
@@ -1017,46 +799,19 @@ async function readWorkspaceFileTool(args: {
1017
799
  }): Promise<AgentRuntimeToolResult> {
1018
800
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
1019
801
  const requestedPath = requireWorkspaceToolPath(parsed);
1020
- const readWindow = readWorkspaceReadWindow(parsed);
1021
802
  const absolutePath = resolveLocalWorkspaceToolPath({
1022
803
  workspaceRoot: args.workspaceRoot,
1023
804
  requestedPath,
1024
805
  });
1025
806
  const content = await readFile(absolutePath, "utf8");
1026
- const lines = splitContentIntoLines(content);
1027
- const lineOffset = Math.min(readWindow.offset, lines.length);
1028
- const endLineIndex = readWindow.limit === undefined
1029
- ? lines.length
1030
- : Math.min(lineOffset + readWindow.limit, lines.length);
1031
- const pagedContent = lines.slice(lineOffset, endLineIndex).join("");
1032
- const returnedLineCount = endLineIndex - lineOffset;
1033
- const truncated = readWindow.limit !== undefined && endLineIndex < lines.length;
1034
- const toolContent = truncated && returnedLineCount > 0
1035
- ? appendReadContinuationHint(
1036
- pagedContent,
1037
- `[readFile truncated: returned lines ${lineOffset + 1}-${endLineIndex} of ${lines.length}; ` +
1038
- `call readFile again with offset ${endLineIndex}.]`
1039
- )
1040
- : pagedContent;
1041
- const relativePath = normalizeWorkspaceRelativePath({
1042
- workspaceRoot: resolve(args.workspaceRoot),
1043
- targetPath: absolutePath,
1044
- });
1045
807
  return {
1046
- content: readWindow.offset === 0 && readWindow.limit === undefined ? content : toolContent,
808
+ content,
1047
809
  metadata: {
1048
- path: relativePath,
1049
- bytes: Buffer.byteLength(readWindow.offset === 0 && readWindow.limit === undefined ? content : pagedContent),
1050
- ...(readWindow.offset !== 0 || readWindow.limit !== undefined
1051
- ? {
1052
- offset: readWindow.offset,
1053
- ...(readWindow.limit !== undefined ? { limit: readWindow.limit } : {}),
1054
- returnedLineCount,
1055
- totalLineCount: lines.length,
1056
- truncated,
1057
- ...(truncated ? { nextOffset: endLineIndex } : {}),
1058
- }
1059
- : {}),
810
+ path: normalizeWorkspaceRelativePath({
811
+ workspaceRoot: resolve(args.workspaceRoot),
812
+ targetPath: absolutePath,
813
+ }),
814
+ bytes: Buffer.byteLength(content),
1060
815
  },
1061
816
  };
1062
817
  }
@@ -1093,37 +848,14 @@ async function replaceWorkspaceTextTool(args: {
1093
848
  }): Promise<AgentRuntimeToolResult> {
1094
849
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
1095
850
  const requestedPath = requireWorkspaceToolPath(parsed);
1096
- const structuredEdits = readWorkspaceStructuredEdits(parsed);
851
+ const oldText = requireWorkspaceOldText(parsed);
852
+ const newText = requireWorkspaceNewText(parsed);
853
+ const expectedReplacements = readExpectedReplacementCount(parsed);
1097
854
  const absolutePath = resolveLocalWorkspaceToolPath({
1098
855
  workspaceRoot: args.workspaceRoot,
1099
856
  requestedPath,
1100
857
  });
1101
858
  const content = await readFile(absolutePath, "utf8");
1102
- const relativePath = normalizeWorkspaceRelativePath({
1103
- workspaceRoot: resolve(args.workspaceRoot),
1104
- targetPath: absolutePath,
1105
- });
1106
- if (structuredEdits) {
1107
- const result = applyWorkspaceStructuredEdits({
1108
- content,
1109
- edits: structuredEdits,
1110
- requestedPath,
1111
- });
1112
- await writeFile(absolutePath, result.nextContent, "utf8");
1113
- return {
1114
- content: `applied ${result.replacements} edit${result.replacements === 1 ? "" : "s"} in ${relativePath}`,
1115
- metadata: {
1116
- path: relativePath,
1117
- replacements: result.replacements,
1118
- editCount: structuredEdits.length,
1119
- bytes: Buffer.byteLength(result.nextContent),
1120
- firstChangedLine: result.firstChangedLine,
1121
- },
1122
- };
1123
- }
1124
- const oldText = requireWorkspaceOldText(parsed);
1125
- const newText = requireWorkspaceNewText(parsed);
1126
- const expectedReplacements = readExpectedReplacementCount(parsed);
1127
859
  const replacementCount = countExactTextOccurrences({ content, oldText });
1128
860
  if (replacementCount !== expectedReplacements) {
1129
861
  throw new Error(
@@ -1133,6 +865,10 @@ async function replaceWorkspaceTextTool(args: {
1133
865
  }
1134
866
  const nextContent = content.split(oldText).join(newText);
1135
867
  await writeFile(absolutePath, nextContent, "utf8");
868
+ const relativePath = normalizeWorkspaceRelativePath({
869
+ workspaceRoot: resolve(args.workspaceRoot),
870
+ targetPath: absolutePath,
871
+ });
1136
872
  return {
1137
873
  content: `replaced ${replacementCount} occurrence${replacementCount === 1 ? "" : "s"} in ${relativePath}`,
1138
874
  metadata: {
@@ -1426,20 +1162,7 @@ async function previewLifecycleTool(args: {
1426
1162
  const summary = parseLastJsonObject(String(result.metadata?.stdoutTail ?? result.content)) ?? {};
1427
1163
  return {
1428
1164
  content: [
1429
- typeof summary.preferredPreviewUrl === "string"
1430
- ? `preferredPreviewUrl: ${summary.preferredPreviewUrl}`
1431
- : "",
1432
- typeof summary.assignedPreviewUrl === "string"
1433
- ? `assignedPreviewUrl: ${summary.assignedPreviewUrl}`
1434
- : typeof summary.previewUrl === "string"
1435
- ? `assignedPreviewUrl: ${summary.previewUrl}`
1436
- : "",
1437
- typeof summary.previewUrlVerified === "boolean"
1438
- ? `previewUrlVerified: ${String(summary.previewUrlVerified)}`
1439
- : "",
1440
- typeof summary.previewVerificationReason === "string"
1441
- ? `previewVerificationReason: ${summary.previewVerificationReason}`
1442
- : "",
1165
+ typeof summary.previewUrl === "string" ? `previewUrl: ${summary.previewUrl}` : "",
1443
1166
  typeof summary.localApiOrigin === "string" ? `localApiOrigin: ${summary.localApiOrigin}` : "",
1444
1167
  typeof summary.serverDbPath === "string" ? `serverDbPath: ${summary.serverDbPath}` : "",
1445
1168
  "",
@@ -1461,13 +1184,6 @@ async function execShellTool(args: {
1461
1184
  }): Promise<AgentRuntimeToolResult> {
1462
1185
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
1463
1186
  const command = requireShellCommand(parsed, args.call.name);
1464
- const requestedTimeoutMs = readShellTimeoutMs(parsed);
1465
- const timeoutMs = requestedTimeoutMs === undefined
1466
- ? args.commandTimeoutMs
1467
- : Math.min(
1468
- requestedTimeoutMs,
1469
- args.commandTimeoutMs ?? DEFAULT_WORKSPACE_COMMAND_TIMEOUT_MS,
1470
- );
1471
1187
  const result = await runWorkspaceCommand({
1472
1188
  workspaceRoot: args.workspaceRoot,
1473
1189
  command: buildWorkspaceShellCommand({
@@ -1475,31 +1191,12 @@ async function execShellTool(args: {
1475
1191
  command,
1476
1192
  shell: parsed.shell,
1477
1193
  }),
1478
- timeoutMs,
1194
+ timeoutMs: args.commandTimeoutMs,
1479
1195
  commandPrefix: args.commandPrefix,
1480
1196
  });
1481
- let fullOutputPath: string | undefined;
1482
- if (result.outputTruncated) {
1483
- const relativeOutputPath = `.tmp/agent-runtime/execShell/${sanitizeWorkspaceTempName(args.call.id)}.log`;
1484
- const absoluteOutputPath = resolve(args.workspaceRoot, relativeOutputPath);
1485
- await mkdir(dirname(absoluteOutputPath), { recursive: true });
1486
- await writeFile(absoluteOutputPath, result.fullContent, "utf8");
1487
- fullOutputPath = normalizeWorkspaceRelativePath({
1488
- workspaceRoot: resolve(args.workspaceRoot),
1489
- targetPath: absoluteOutputPath,
1490
- });
1491
- }
1492
1197
  return {
1493
- content: fullOutputPath
1494
- ? `${result.content}\n\n[full output saved to ${fullOutputPath}]`
1495
- : result.content,
1496
- metadata: {
1497
- exitCode: result.exitCode,
1498
- timedOut: result.timedOut,
1499
- timeoutMs: timeoutMs ?? DEFAULT_WORKSPACE_COMMAND_TIMEOUT_MS,
1500
- ...(result.outputTruncated ? { outputTruncated: true } : {}),
1501
- ...(fullOutputPath ? { fullOutputPath } : {}),
1502
- },
1198
+ content: result.content,
1199
+ metadata: { exitCode: result.exitCode, timedOut: result.timedOut },
1503
1200
  };
1504
1201
  }
1505
1202
 
@@ -230,9 +230,7 @@ export function hasDirectOpenAiCompatibleProvider(env: EnvLike) {
230
230
  }
231
231
 
232
232
  function agentRequiresPlatformChatProvider(agentConfig?: AgentRuntimeAgentConfig) {
233
- if (agentConfig?.apiSource === "platform") return true;
234
- if (agentConfig?.customProviderUrl?.trim()) return false;
235
- return agentConfig?.rawRecord?.useServerProxy === true;
233
+ return agentConfig?.apiSource === "platform" || agentConfig?.rawRecord?.useServerProxy === true;
236
234
  }
237
235
 
238
236
  export function shouldUsePlatformChatProvider(
@@ -240,7 +238,6 @@ export function shouldUsePlatformChatProvider(
240
238
  agentConfig?: AgentRuntimeAgentConfig
241
239
  ) {
242
240
  if (agentRequiresPlatformChatProvider(agentConfig)) return true;
243
- if (agentConfig?.customProviderUrl?.trim()) return false;
244
241
  if (env.NOLO_LOCAL_LLM === "platform") return true;
245
242
  if (env.NOLO_LOCAL_LLM === "direct") return false;
246
243
  return !hasDirectOpenAiCompatibleProvider(env) && canUsePlatformChatProvider(env);
@@ -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);
@@ -77,42 +77,57 @@ export function mergeAgentRuntimeToolPolicies(
77
77
  };
78
78
  }
79
79
 
80
- function runtimeBindingRecord(runtimeBinding: unknown): Record<string, unknown> | undefined {
81
- return isRecord(runtimeBinding) ? runtimeBinding : undefined;
80
+ function policyRequestsHostedWorkspace(policy: AgentRuntimeToolPolicy | undefined) {
81
+ if (!policy) return false;
82
+ const workspaceMode = policy.workspace?.mode;
83
+ return (
84
+ policy.shell?.enabled === true ||
85
+ policy.runtimeTools?.some((tool) => tool === "execShell") ||
86
+ workspaceMode === "lease" ||
87
+ workspaceMode === "task-worktree"
88
+ );
82
89
  }
83
90
 
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
- export function resolveRequestedRuntimeToolNames(args: {
97
- agentConfig: Pick<AgentRuntimeAgentConfig, "toolNames" | "runtimeToolPolicy" | "runtimeBinding">;
98
- }) {
99
- const agentPolicy = normalizeAgentRuntimeToolPolicy(args.agentConfig.runtimeToolPolicy);
100
- const bindingPolicy = runtimePolicyFromRuntimeBinding(args.agentConfig.runtimeBinding);
101
- const bindingSnapshotPolicy = runtimePolicySnapshotFromRuntimeBinding(args.agentConfig.runtimeBinding);
102
- return unique([
103
- ...stringArray(args.agentConfig.toolNames),
104
- ...(agentPolicy?.runtimeTools ?? []),
105
- ...(bindingPolicy?.runtimeTools ?? []),
106
- ...(bindingSnapshotPolicy?.runtimeTools ?? []),
107
- ]);
91
+ function applyWebRuntimeSafetyBaseline(
92
+ policy: AgentRuntimeToolPolicy | undefined,
93
+ ): AgentRuntimeToolPolicy | undefined {
94
+ if (!policyRequestsHostedWorkspace(policy)) return policy;
95
+ return mergeAgentRuntimeToolPolicies(
96
+ policy,
97
+ {
98
+ version: 1,
99
+ shell: {
100
+ commandPolicy: "approval",
101
+ networkPolicy: "default-deny",
102
+ },
103
+ isolation: {
104
+ mode: "os-sandbox",
105
+ },
106
+ audit: {
107
+ logToolCalls: true,
108
+ logShellCommands: true,
109
+ writeToDialog: true,
110
+ },
111
+ },
112
+ );
108
113
  }
109
114
 
110
115
  export function resolveEffectiveRuntimeToolPolicy(args: {
111
116
  agentConfig: Pick<AgentRuntimeAgentConfig, "runtimeToolPolicy" | "runtimeBinding">;
117
+ taskRunControl?: { runtimeToolPolicy?: unknown } | null;
118
+ host?: AgentRuntimeHost;
112
119
  }): 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);
120
+ const agentPolicy = normalizeAgentRuntimeToolPolicy(
121
+ args.agentConfig.runtimeToolPolicy ??
122
+ (isRecord(args.agentConfig.runtimeBinding)
123
+ ? args.agentConfig.runtimeBinding.runtimeToolPolicy
124
+ : undefined),
125
+ );
126
+ const taskPolicy = normalizeAgentRuntimeToolPolicy(
127
+ args.taskRunControl?.runtimeToolPolicy,
128
+ );
129
+ const effectivePolicy = mergeAgentRuntimeToolPolicies(agentPolicy, taskPolicy);
130
+ return args.host === "web"
131
+ ? applyWebRuntimeSafetyBaseline(effectivePolicy)
132
+ : effectivePolicy;
118
133
  }