nolo-cli 0.1.38 → 0.1.40

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 (65) hide show
  1. package/README.md +4 -4
  2. package/agent-runtime/agentRecordConfig.ts +0 -14
  3. package/agent-runtime/dialogWritePlan.ts +39 -0
  4. package/agent-runtime/hostAdapter.ts +4 -1
  5. package/agent-runtime/hybridRecordStore.ts +1 -1
  6. package/agent-runtime/index.ts +39 -18
  7. package/agent-runtime/localDialogRead.ts +23 -0
  8. package/agent-runtime/localLoop.ts +87 -7
  9. package/agent-runtime/localToolPolicy.ts +1 -17
  10. package/agent-runtime/localWorkspaceTools.ts +51 -110
  11. package/agent-runtime/noloWorkspaceTools.ts +371 -0
  12. package/agent-runtime/runtimeToolPolicy.ts +3 -14
  13. package/agent-runtime/runtimeToolSurface.ts +198 -0
  14. package/agent-runtime/types.ts +2 -1
  15. package/agentAliases.ts +8 -1
  16. package/agentRunCommand.ts +65 -95
  17. package/agentRuntimeLocal.ts +1 -7
  18. package/ai/agent/cliExecutor.ts +249 -34
  19. package/chat/messages/fetchMessages.ts +84 -0
  20. package/chat/messages/types.ts +185 -0
  21. package/cli/agentAliases.ts +8 -1
  22. package/cli/agentRunCommand.ts +65 -95
  23. package/cli/agentRuntimeLocal.ts +1 -7
  24. package/cli/cliEnvHelpers.ts +11 -7
  25. package/cli/client/agentRun.ts +94 -93
  26. package/cli/client/localRuntimeAdapter.ts +250 -69
  27. package/cli/commandRegistry.ts +2 -2
  28. package/cli/connectorRunArtifact.ts +0 -1
  29. package/cli/dialogCommands.ts +572 -0
  30. package/cli/dialogInternalCommandEntries.ts +4 -0
  31. package/cli/machineWsRunDispatch.ts +76 -39
  32. package/cli/offlineMarxistsAgentCommand.ts +6 -4
  33. package/cli/scriptCommandEntries.ts +0 -2
  34. package/cli/tui/readlineWorkspace.ts +69 -0
  35. package/cli/tui/session.ts +176 -1
  36. package/cliEnvHelpers.ts +11 -7
  37. package/client/agentConfigResolver.test.ts +4 -5
  38. package/client/agentRun.test.ts +307 -57
  39. package/client/agentRun.ts +94 -93
  40. package/client/localRuntimeAdapter.test.ts +332 -53
  41. package/client/localRuntimeAdapter.ts +250 -69
  42. package/client/localRuntimeDryRun.test.ts +23 -9
  43. package/client/localToolPolicy.test.ts +4 -4
  44. package/commandRegistry.ts +2 -2
  45. package/connectorRunArtifact.ts +0 -1
  46. package/database/server/db.ts +99 -0
  47. package/database/server/ensureDbOpen.ts +12 -0
  48. package/database/server/levelAuthorityStore.ts +20 -1
  49. package/database/server/memoryAuthorityStore.ts +133 -0
  50. package/database/server/serverStoreFactory.ts +118 -0
  51. package/dialogCommands.ts +572 -0
  52. package/dialogInternalCommandEntries.ts +4 -0
  53. package/machineWsRunDispatch.ts +76 -39
  54. package/offlineMarxistsAgentCommand.ts +6 -4
  55. package/package.json +11 -9
  56. package/scriptCommandEntries.ts +0 -2
  57. package/tui/readlineWorkspace.ts +69 -0
  58. package/tui/session.ts +176 -1
  59. package/agent-runtime/taskWorkspace.ts +0 -193
  60. package/agent-runtime/workspaceSession.ts +0 -76
  61. package/cli/client/taskWorktree.ts +0 -8
  62. package/cli/client/workspaceSession.ts +0 -11
  63. package/client/taskWorktree.ts +0 -8
  64. package/client/workspaceSession.test.ts +0 -57
  65. package/client/workspaceSession.ts +0 -11
@@ -20,6 +20,7 @@ type WorkspaceFileArgs = {
20
20
  newText?: unknown;
21
21
  expectedReplacements?: unknown;
22
22
  query?: unknown;
23
+ includeIgnored?: unknown;
23
24
  command?: unknown;
24
25
  cmd?: unknown;
25
26
  branch?: unknown;
@@ -42,18 +43,11 @@ type OpenAiCompatibleTool = Record<string, unknown> & {
42
43
  };
43
44
 
44
45
  const WORKSPACE_TOOL_NAMES = [
45
- "listWorkspaceFiles",
46
- "readWorkspaceFile",
47
- "writeWorkspaceFile",
48
- "replaceWorkspaceText",
49
46
  "listFiles",
50
47
  "readFile",
51
48
  "writeFile",
52
49
  "editFile",
53
50
  "searchFiles",
54
- "searchWorkspace",
55
- "applyEdit",
56
- "applyLineEdits",
57
51
  "startPreview",
58
52
  "getPreviewStatus",
59
53
  "stopPreview",
@@ -92,7 +86,7 @@ function buildListWorkspaceFilesTool(): OpenAiCompatibleTool {
92
86
  return {
93
87
  type: "function",
94
88
  function: {
95
- name: "listWorkspaceFiles",
89
+ name: "listFiles",
96
90
  description: "List files and directories inside a workspace directory.",
97
91
  parameters: {
98
92
  type: "object",
@@ -108,9 +102,9 @@ function buildReadWorkspaceFileTool(): OpenAiCompatibleTool {
108
102
  return {
109
103
  type: "function",
110
104
  function: {
111
- name: "readWorkspaceFile",
105
+ name: "readFile",
112
106
  description:
113
- "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.",
107
+ "Read a UTF-8 text file inside the workspace. Read before editing when you need the current content or exact text for editFile; skip unnecessary reads for trivial known writes.",
114
108
  parameters: {
115
109
  type: "object",
116
110
  properties: {
@@ -126,9 +120,9 @@ function buildWriteWorkspaceFileTool(): OpenAiCompatibleTool {
126
120
  return {
127
121
  type: "function",
128
122
  function: {
129
- name: "writeWorkspaceFile",
123
+ name: "writeFile",
130
124
  description:
131
- "Write full UTF-8 file content inside the workspace. Prefer replaceWorkspaceText for small edits to existing large files; use this for new files or deliberate whole-file rewrites.",
125
+ "Write full UTF-8 file content inside the workspace. For new files or deliberate whole-file rewrites only. For existing files, prefer editFile for targeted edits. Warn that whole-file rewrites can cause line-ending churn.",
132
126
  parameters: {
133
127
  type: "object",
134
128
  properties: {
@@ -148,9 +142,9 @@ function buildReplaceWorkspaceTextTool(): OpenAiCompatibleTool {
148
142
  return {
149
143
  type: "function",
150
144
  function: {
151
- name: "replaceWorkspaceText",
145
+ name: "editFile",
152
146
  description:
153
- "Use for small, exact edits in one workspace file without constructing a patch. Read the file first when you need the exact oldText; set expectedReplacements to avoid accidental broad edits.",
147
+ "Use for small, exact edits in one workspace file without constructing a patch. Read the file first when you need the exact oldText; set expectedReplacements to avoid accidental broad edits. When expected replacement count fails, report a blocker instead of falling back to a full-file rewrite.",
154
148
  parameters: {
155
149
  type: "object",
156
150
  properties: {
@@ -178,7 +172,7 @@ function buildSearchWorkspaceTool(): OpenAiCompatibleTool {
178
172
  return {
179
173
  type: "function",
180
174
  function: {
181
- name: "searchWorkspace",
175
+ name: "searchFiles",
182
176
  description: "Search text in the current workspace using ripgrep when available.",
183
177
  parameters: {
184
178
  type: "object",
@@ -188,6 +182,11 @@ function buildSearchWorkspaceTool(): OpenAiCompatibleTool {
188
182
  description: "Search query or regular expression.",
189
183
  },
190
184
  path: buildWorkspacePathProperty(),
185
+ includeIgnored: {
186
+ type: "boolean",
187
+ description:
188
+ "When true, search files ignored by .gitignore such as .tmp. Defaults to false; .git and node_modules remain excluded.",
189
+ },
191
190
  },
192
191
  required: ["query"],
193
192
  },
@@ -331,45 +330,21 @@ function buildWorkspaceShellCommand(args: {
331
330
  : buildBashCommand(args.command);
332
331
  }
333
332
 
334
- function renameWorkspaceToolDefinition(
335
- tool: OpenAiCompatibleTool,
336
- name: string
337
- ): OpenAiCompatibleTool {
338
- const description = typeof tool.function?.description === "string"
339
- ? tool.function.description
340
- .replaceAll("replaceWorkspaceText", "editFile")
341
- .replaceAll("readWorkspaceFile", "readFile")
342
- : tool.function?.description;
343
- return {
344
- ...tool,
345
- function: {
346
- ...(tool.function ?? {}),
347
- name,
348
- description,
349
- },
350
- };
351
- }
352
-
353
333
  function buildWorkspaceToolDefinition(toolName: string) {
354
- if (toolName === "listWorkspaceFiles" || toolName === "listFiles") {
355
- return renameWorkspaceToolDefinition(buildListWorkspaceFilesTool(), toolName);
334
+ if (toolName === "listFiles") {
335
+ return buildListWorkspaceFilesTool();
356
336
  }
357
- if (toolName === "readWorkspaceFile" || toolName === "readFile") {
358
- return renameWorkspaceToolDefinition(buildReadWorkspaceFileTool(), toolName);
337
+ if (toolName === "readFile") {
338
+ return buildReadWorkspaceFileTool();
359
339
  }
360
- if (toolName === "writeWorkspaceFile" || toolName === "writeFile") {
361
- return renameWorkspaceToolDefinition(buildWriteWorkspaceFileTool(), toolName);
340
+ if (toolName === "writeFile") {
341
+ return buildWriteWorkspaceFileTool();
362
342
  }
363
- if (
364
- toolName === "replaceWorkspaceText" ||
365
- toolName === "editFile" ||
366
- toolName === "applyEdit" ||
367
- toolName === "applyLineEdits"
368
- ) {
369
- return renameWorkspaceToolDefinition(buildReplaceWorkspaceTextTool(), toolName);
343
+ if (toolName === "editFile") {
344
+ return buildReplaceWorkspaceTextTool();
370
345
  }
371
- if (toolName === "searchWorkspace" || toolName === "searchFiles") {
372
- return renameWorkspaceToolDefinition(buildSearchWorkspaceTool(), toolName);
346
+ if (toolName === "searchFiles") {
347
+ return buildSearchWorkspaceTool();
373
348
  }
374
349
  if (toolName === "startPreview" || toolName === "getPreviewStatus" || toolName === "stopPreview" || toolName === "releasePreview") {
375
350
  return buildPreviewLifecycleTool(toolName);
@@ -379,20 +354,6 @@ function buildWorkspaceToolDefinition(toolName: string) {
379
354
  return null;
380
355
  }
381
356
 
382
- function removeRedundantWorkspaceToolAliases(toolNames: Set<string>) {
383
- const next = new Set(toolNames);
384
- if (next.has("listWorkspaceFiles")) next.delete("listFiles");
385
- if (next.has("readWorkspaceFile")) next.delete("readFile");
386
- if (next.has("writeWorkspaceFile")) next.delete("writeFile");
387
- if (next.has("replaceWorkspaceText")) {
388
- next.delete("editFile");
389
- next.delete("applyEdit");
390
- next.delete("applyLineEdits");
391
- }
392
- if (next.has("searchWorkspace")) next.delete("searchFiles");
393
- return next;
394
- }
395
-
396
357
  function filterDeclaredWorkspaceToolNames(args: {
397
358
  toolNames?: string[];
398
359
  exposeShellTools: boolean;
@@ -409,14 +370,14 @@ export function buildLocalWorkspaceToolset(args: {
409
370
  exposeShellTools?: boolean;
410
371
  }) {
411
372
  const exposeShellTools = args.exposeShellTools === true;
412
- const toolNames = removeRedundantWorkspaceToolAliases(new Set([
373
+ const toolNames = new Set([
413
374
  ...DEFAULT_LOCAL_CODING_TOOL_NAMES,
414
375
  ...(exposeShellTools ? SHELL_TOOL_NAMES : []),
415
376
  ...filterDeclaredWorkspaceToolNames({
416
377
  toolNames: args.declaredToolNames,
417
378
  exposeShellTools,
418
379
  }),
419
- ]));
380
+ ]);
420
381
  return {
421
382
  toolNames: [...toolNames],
422
383
  exposeShellTools,
@@ -442,7 +403,7 @@ export function buildLocalWorkspaceOpenAiTools(args: {
442
403
  toolNames?: string[];
443
404
  exposeShellTools?: boolean;
444
405
  }) {
445
- const declaredTools = removeRedundantWorkspaceToolAliases(new Set(args.toolNames ?? []));
406
+ const declaredTools = new Set(args.toolNames ?? []);
446
407
  return WORKSPACE_TOOL_NAMES
447
408
  .filter((toolName) => {
448
409
  if (!declaredTools.has(toolName)) return false;
@@ -488,21 +449,21 @@ function requireWorkspaceToolPath(args: WorkspaceFileArgs) {
488
449
 
489
450
  function requireWorkspaceFileContent(args: WorkspaceFileArgs) {
490
451
  if (typeof args.content !== "string") {
491
- throw new Error("writeWorkspaceFile requires string content.");
452
+ throw new Error("writeFile requires string content.");
492
453
  }
493
454
  return args.content;
494
455
  }
495
456
 
496
457
  function requireWorkspaceOldText(args: WorkspaceFileArgs) {
497
458
  if (typeof args.oldText !== "string" || !args.oldText) {
498
- throw new Error("replaceWorkspaceText requires non-empty oldText.");
459
+ throw new Error("editFile requires non-empty oldText.");
499
460
  }
500
461
  return args.oldText;
501
462
  }
502
463
 
503
464
  function requireWorkspaceNewText(args: WorkspaceFileArgs) {
504
465
  if (typeof args.newText !== "string") {
505
- throw new Error("replaceWorkspaceText requires string newText.");
466
+ throw new Error("editFile requires string newText.");
506
467
  }
507
468
  return args.newText;
508
469
  }
@@ -511,7 +472,7 @@ function readExpectedReplacementCount(args: WorkspaceFileArgs) {
511
472
  if (args.expectedReplacements === undefined) return 1;
512
473
  const value = Number(args.expectedReplacements);
513
474
  if (!Number.isInteger(value) || value < 1) {
514
- throw new Error("replaceWorkspaceText expectedReplacements must be a positive integer.");
475
+ throw new Error("editFile expectedReplacements must be a positive integer.");
515
476
  }
516
477
  return value;
517
478
  }
@@ -529,7 +490,7 @@ function pluralizeReplacement(count: number) {
529
490
 
530
491
  function requireWorkspaceSearchQuery(args: WorkspaceFileArgs) {
531
492
  const query = typeof args.query === "string" ? args.query.trim() : "";
532
- if (!query) throw new Error("searchWorkspace requires a non-empty query.");
493
+ if (!query) throw new Error("searchFiles requires a non-empty query.");
533
494
  return query;
534
495
  }
535
496
 
@@ -777,7 +738,7 @@ export function resolveLocalWorkspaceToolPath(args: {
777
738
  return targetPath;
778
739
  }
779
740
 
780
- async function readWorkspaceFileTool(args: {
741
+ async function readFileTool(args: {
781
742
  call: AgentRuntimeToolCallInput;
782
743
  workspaceRoot: string;
783
744
  }): Promise<AgentRuntimeToolResult> {
@@ -800,10 +761,15 @@ async function readWorkspaceFileTool(args: {
800
761
  };
801
762
  }
802
763
 
803
- async function writeWorkspaceFileTool(args: {
764
+ async function writeFileTool(args: {
804
765
  call: AgentRuntimeToolCallInput;
805
766
  workspaceRoot: string;
806
767
  }): Promise<AgentRuntimeToolResult> {
768
+ // TODO: Explore cheaply including diff stat/EOL warning after writeFile.
769
+ // If implementation is larger than a small safe change, defer it.
770
+ // Exact boundaries: writeFileTool function in localWorkspaceTools.ts.
771
+ // Consider adding a post-write hook that runs `git diff --stat` and checks line endings.
772
+ // Keep it optional and non-blocking.
807
773
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
808
774
  const requestedPath = requireWorkspaceToolPath(parsed);
809
775
  const content = requireWorkspaceFileContent(parsed);
@@ -826,7 +792,7 @@ async function writeWorkspaceFileTool(args: {
826
792
  };
827
793
  }
828
794
 
829
- async function replaceWorkspaceTextTool(args: {
795
+ async function editFileTool(args: {
830
796
  call: AgentRuntimeToolCallInput;
831
797
  workspaceRoot: string;
832
798
  }): Promise<AgentRuntimeToolResult> {
@@ -843,7 +809,7 @@ async function replaceWorkspaceTextTool(args: {
843
809
  const replacementCount = countExactTextOccurrences({ content, oldText });
844
810
  if (replacementCount !== expectedReplacements) {
845
811
  throw new Error(
846
- `replaceWorkspaceText expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} ` +
812
+ `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} ` +
847
813
  `but found ${replacementCount} in ${requestedPath}.`
848
814
  );
849
815
  }
@@ -877,7 +843,7 @@ async function formatWorkspaceDirEntry(args: {
877
843
  return info.isDirectory() ? `${relativePath}/` : relativePath;
878
844
  }
879
845
 
880
- async function listWorkspaceFilesTool(args: {
846
+ async function listFilesTool(args: {
881
847
  call: AgentRuntimeToolCallInput;
882
848
  workspaceRoot: string;
883
849
  }): Promise<AgentRuntimeToolResult> {
@@ -904,7 +870,7 @@ async function listWorkspaceFilesTool(args: {
904
870
  };
905
871
  }
906
872
 
907
- async function searchWorkspaceTool(args: {
873
+ async function searchFilesTool(args: {
908
874
  call: AgentRuntimeToolCallInput;
909
875
  workspaceRoot: string;
910
876
  }): Promise<AgentRuntimeToolResult> {
@@ -913,6 +879,7 @@ async function searchWorkspaceTool(args: {
913
879
  const requestedPath = typeof parsed.path === "string" && parsed.path.trim()
914
880
  ? parsed.path.trim()
915
881
  : ".";
882
+ const includeIgnored = parsed.includeIgnored === true;
916
883
  const searchPath = resolveLocalWorkspaceToolPath({
917
884
  workspaceRoot: args.workspaceRoot,
918
885
  requestedPath,
@@ -930,6 +897,7 @@ async function searchWorkspaceTool(args: {
930
897
  "--line-number",
931
898
  "--no-heading",
932
899
  "--hidden",
900
+ ...(includeIgnored ? ["--no-ignore"] : []),
933
901
  "--glob",
934
902
  "!node_modules",
935
903
  "--glob",
@@ -959,6 +927,7 @@ async function searchWorkspaceTool(args: {
959
927
  metadata: {
960
928
  query,
961
929
  path: requestedPath,
930
+ includeIgnored,
962
931
  exitCode: result.exitCode,
963
932
  },
964
933
  };
@@ -1101,51 +1070,23 @@ async function execShellTool(args: {
1101
1070
 
1102
1071
  export function createLocalWorkspaceToolExecutors(args: LocalWorkspaceToolArgs) {
1103
1072
  return {
1104
- listWorkspaceFiles: (call: AgentRuntimeToolCallInput) => listWorkspaceFilesTool({
1105
- call,
1106
- workspaceRoot: args.workspaceRoot,
1107
- }),
1108
- readWorkspaceFile: (call: AgentRuntimeToolCallInput) => readWorkspaceFileTool({
1109
- call,
1110
- workspaceRoot: args.workspaceRoot,
1111
- }),
1112
- writeWorkspaceFile: (call: AgentRuntimeToolCallInput) => writeWorkspaceFileTool({
1113
- call,
1114
- workspaceRoot: args.workspaceRoot,
1115
- }),
1116
- replaceWorkspaceText: (call: AgentRuntimeToolCallInput) => replaceWorkspaceTextTool({
1117
- call,
1118
- workspaceRoot: args.workspaceRoot,
1119
- }),
1120
- editFile: (call: AgentRuntimeToolCallInput) => replaceWorkspaceTextTool({
1121
- call,
1122
- workspaceRoot: args.workspaceRoot,
1123
- }),
1124
- applyEdit: (call: AgentRuntimeToolCallInput) => replaceWorkspaceTextTool({
1125
- call,
1126
- workspaceRoot: args.workspaceRoot,
1127
- }),
1128
- applyLineEdits: (call: AgentRuntimeToolCallInput) => replaceWorkspaceTextTool({
1129
- call,
1130
- workspaceRoot: args.workspaceRoot,
1131
- }),
1132
- listFiles: (call: AgentRuntimeToolCallInput) => listWorkspaceFilesTool({
1073
+ editFile: (call: AgentRuntimeToolCallInput) => editFileTool({
1133
1074
  call,
1134
1075
  workspaceRoot: args.workspaceRoot,
1135
1076
  }),
1136
- readFile: (call: AgentRuntimeToolCallInput) => readWorkspaceFileTool({
1077
+ listFiles: (call: AgentRuntimeToolCallInput) => listFilesTool({
1137
1078
  call,
1138
1079
  workspaceRoot: args.workspaceRoot,
1139
1080
  }),
1140
- writeFile: (call: AgentRuntimeToolCallInput) => writeWorkspaceFileTool({
1081
+ readFile: (call: AgentRuntimeToolCallInput) => readFileTool({
1141
1082
  call,
1142
1083
  workspaceRoot: args.workspaceRoot,
1143
1084
  }),
1144
- searchWorkspace: (call: AgentRuntimeToolCallInput) => searchWorkspaceTool({
1085
+ writeFile: (call: AgentRuntimeToolCallInput) => writeFileTool({
1145
1086
  call,
1146
1087
  workspaceRoot: args.workspaceRoot,
1147
1088
  }),
1148
- searchFiles: (call: AgentRuntimeToolCallInput) => searchWorkspaceTool({
1089
+ searchFiles: (call: AgentRuntimeToolCallInput) => searchFilesTool({
1149
1090
  call,
1150
1091
  workspaceRoot: args.workspaceRoot,
1151
1092
  }),