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
@@ -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
 
@@ -2,11 +2,13 @@ import type {
2
2
  AgentRuntimeChatMessage,
3
3
  AgentRuntimeResult,
4
4
  } from "./types";
5
+ import { buildProviderAuthHeaders } from "./providerResolution";
5
6
 
6
7
  export type OpenAiCompatibleProviderConfig = {
7
8
  model: string;
8
9
  endpoint: string;
9
10
  apiKey: string;
11
+ apiKeyHeader?: string;
10
12
  provider: string;
11
13
  requestOptions: Record<string, number | string>;
12
14
  };
@@ -41,9 +43,11 @@ export function buildOpenAiCompatibleChatCompletionRequest(args: {
41
43
  method: "POST",
42
44
  headers: {
43
45
  "Content-Type": "application/json",
44
- ...(args.providerConfig.apiKey
45
- ? { Authorization: `Bearer ${args.providerConfig.apiKey}` }
46
- : {}),
46
+ ...buildProviderAuthHeaders({
47
+ endpoint: args.providerConfig.endpoint,
48
+ apiKey: args.providerConfig.apiKey,
49
+ apiKeyHeader: args.providerConfig.apiKeyHeader,
50
+ }),
47
51
  },
48
52
  body: JSON.stringify(body),
49
53
  },
@@ -1,38 +1,27 @@
1
1
  import type { AgentRuntimeAgentConfig } from "./hostAdapter";
2
2
  import type { OpenAiCompatibleProviderConfig } from "./openAiCompatibleProvider";
3
- import { pickAgentRuntimeInferenceOptions } from "./agentConfigOptions";
3
+ import { buildProviderExecutionPlan } from "./providerResolution";
4
4
 
5
5
  type EnvLike = Record<string, string | undefined>;
6
6
 
7
- function trimTrailingSlash(value: string) {
8
- return value.replace(/\/+$/, "");
9
- }
10
-
11
- function resolveChatCompletionsEndpoint(value: string) {
12
- const trimmed = trimTrailingSlash(value.trim());
13
- return trimmed.endsWith("/chat/completions")
14
- ? trimmed
15
- : `${trimmed}/chat/completions`;
16
- }
17
-
18
- function resolveOpenAiCompatibleBaseUrl(env: EnvLike) {
19
- return env.NOLO_LOCAL_OPENAI_BASE_URL || env.OPENAI_BASE_URL || "https://api.openai.com/v1";
20
- }
21
-
22
- function resolveOpenAiCompatibleApiKey(env: EnvLike) {
23
- return env.OPENAI_API_KEY || env.NOLO_LOCAL_OPENAI_API_KEY || "";
24
- }
25
-
26
7
  export function resolveOpenAiCompatibleProviderConfig(args: {
27
8
  agentConfig: AgentRuntimeAgentConfig;
28
9
  env: EnvLike;
29
10
  }): OpenAiCompatibleProviderConfig {
30
- const customProviderUrl = args.agentConfig.customProviderUrl?.trim();
11
+ const plan = buildProviderExecutionPlan({
12
+ agentConfig: args.agentConfig,
13
+ env: args.env,
14
+ runtimeKind: "local",
15
+ });
16
+ if (plan.mode === "cli" || plan.transport !== "direct") {
17
+ throw new Error("OpenAI-compatible provider config requires a direct provider execution plan.");
18
+ }
31
19
  return {
32
- model: args.agentConfig.model || "gpt-4.1-mini",
33
- endpoint: resolveChatCompletionsEndpoint(customProviderUrl || resolveOpenAiCompatibleBaseUrl(args.env)),
34
- apiKey: resolveOpenAiCompatibleApiKey(args.env),
35
- provider: args.agentConfig.provider || "openai-compatible",
36
- requestOptions: pickAgentRuntimeInferenceOptions(args.agentConfig),
20
+ model: plan.model,
21
+ endpoint: plan.endpoint,
22
+ apiKey: plan.apiKey,
23
+ ...(plan.apiKeyHeader ? { apiKeyHeader: plan.apiKeyHeader } : {}),
24
+ provider: plan.provider,
25
+ requestOptions: plan.requestOptions,
37
26
  };
38
27
  }