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
@@ -4,13 +4,7 @@ import type { AgentRuntimeHostAdapter, AgentRuntimeRequestedMode } from "./agent
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  import { extname, resolve } from "node:path";
6
6
  import { parseSkillDocProtocol, type WorkflowReferenceConfig } from "./ai/skills/skillDocProtocol";
7
- import { resolveCliAgentKeyInput } from "./agentAliases";
8
- import {
9
- ensureWorkspacePackageLinks,
10
- formatPreparedTaskWorktree,
11
- prepareTaskWorktree,
12
- type PreparedTaskWorktree,
13
- } from "./client/taskWorktree";
7
+ import { MIMO_MONTH_AGENT_KEY, resolveCliAgentKeyInput } from "./agentAliases";
14
8
  import type { TaskRunPromptContext } from "./client/taskRunPrompt";
15
9
 
16
10
  type EnvLike = Record<string, string | undefined>;
@@ -25,8 +19,6 @@ type AgentRunCommandDeps = {
25
19
  output?: OutputLike;
26
20
  runner?: typeof runAgentTurn;
27
21
  localRuntimeAdapterFactory?: (env: EnvLike, options?: { cwd?: string }) => AgentRuntimeHostAdapter;
28
- prepareTaskWorktree?: typeof prepareTaskWorktree;
29
- ensureWorkspacePackageLinks?: typeof ensureWorkspacePackageLinks;
30
22
  inspectLocalRunWorkspace?: typeof inspectLocalRunWorkspace;
31
23
  resolveWorkflowReference?: typeof resolveWorkflowReference;
32
24
  };
@@ -58,12 +50,11 @@ type ParsedAgentRunArgs = {
58
50
  parentDialogId?: string;
59
51
  background: boolean;
60
52
  noStream: boolean;
61
- noDefaultTestRoot: boolean;
62
53
  cwd?: string;
63
- maxToolRounds?: number;
64
54
  timeoutMs?: number;
65
55
  traceTools: boolean;
66
- useTaskWorktree: boolean;
56
+ eventsMode?: "jsonl";
57
+ injectFeatureWorktreeInstruction: boolean;
67
58
  taskRunContext?: TaskRunPromptContext;
68
59
  workflowRef?: string;
69
60
  };
@@ -74,10 +65,6 @@ type ResolvedWorkflowReference = {
74
65
  config?: Partial<WorkflowReferenceConfig>;
75
66
  };
76
67
 
77
- const DEFAULT_TEST_ROOT_DIALOG_ID = "01KN6V3MVEQA4Q1PNKVM2W7YY8";
78
- const DEFAULT_TEST_ROOT_DIALOG_KEY = `dialog-b2e06f801f-${DEFAULT_TEST_ROOT_DIALOG_ID}`;
79
- const DEFAULT_TEST_DIALOG_CATEGORY_ID = "test-dialogs";
80
-
81
68
  function readFlagValue(args: string[], flag: string) {
82
69
  const index = args.indexOf(flag);
83
70
  if (index < 0) return undefined;
@@ -102,11 +89,19 @@ function runtimeModeFromArgs(args: string[]): AgentRuntimeRequestedMode | undefi
102
89
  return undefined;
103
90
  }
104
91
 
105
- function parseNonNegativeInteger(value: string | undefined) {
106
- if (!value) return undefined;
107
- const parsed = Number(value);
108
- if (!Number.isFinite(parsed) || parsed < 0) return undefined;
109
- return Math.floor(parsed);
92
+ function isMonthlyMimoAgentRef(raw: string | undefined, resolved: string) {
93
+ const normalized = raw?.trim().toLowerCase();
94
+ return (
95
+ resolved === MIMO_MONTH_AGENT_KEY ||
96
+ normalized === "fullstack" ||
97
+ normalized === "full-stack" ||
98
+ normalized === "nolo-fullstack" ||
99
+ normalized === "包月mimo" ||
100
+ normalized === "包月mimo2.5" ||
101
+ normalized === "mimo-month" ||
102
+ normalized === "全栈" ||
103
+ normalized === "nolo 全栈工程师"
104
+ );
110
105
  }
111
106
 
112
107
  function parsePositiveInteger(value: string | undefined) {
@@ -123,12 +118,10 @@ function positionalArgs(args: string[]) {
123
118
  "--server",
124
119
  "--auto",
125
120
  "--dangerously-allow-shell",
126
- "--worktree-task",
127
121
  "--trace-tools",
128
122
  "--bg",
129
123
  "--no-stream",
130
124
  "--debug",
131
- "--no-default-test-root",
132
125
  ]);
133
126
  for (let index = 0; index < args.length; index += 1) {
134
127
  const arg = args[index];
@@ -146,7 +139,8 @@ export function parseAgentRunArgs(
146
139
  args: string[],
147
140
  options: ParseAgentRunArgsOptions = {}
148
141
  ): ParsedAgentRunArgs | null {
149
- const agentKey = readFlagValue(args, "--agent") ?? positionalArgs(args)[0];
142
+ const rawAgentKey = readFlagValue(args, "--agent") ?? positionalArgs(args)[0];
143
+ const agentKey = rawAgentKey ? resolveCliAgentKeyInput(rawAgentKey) : undefined;
150
144
  const explicitMsg = readFlagValue(args, "--msg");
151
145
  const msgFile = readFlagValue(args, "--msg-file");
152
146
  const fileMessage = msgFile
@@ -160,25 +154,19 @@ export function parseAgentRunArgs(
160
154
  const spaceId = readFlagValue(args, "--space");
161
155
  const category = readFlagValue(args, "--category");
162
156
  const inheritedFromDialog = readFlagValue(args, "--inherit-from-dialog");
163
- const noDefaultTestRoot = args.includes("--no-default-test-root");
164
157
  const cwd = readFlagValue(args, "--cwd");
165
- const maxToolRounds = parseNonNegativeInteger(
166
- readFlagValue(args, "--max-tool-rounds") ?? readFlagValue(args, "--rounds")
167
- );
168
158
  const timeoutMs = parsePositiveInteger(readFlagValue(args, "--timeout-ms"));
159
+ const rawEventsMode = readFlagValue(args, "--events");
160
+ const eventsMode = rawEventsMode === "jsonl" ? "jsonl" : undefined;
169
161
  const inheritedRef = inheritedFromDialog
170
162
  ? parseDialogReference(inheritedFromDialog)
171
- : !continueDialogId && !noDefaultTestRoot
172
- ? {
173
- dialogKey: DEFAULT_TEST_ROOT_DIALOG_KEY,
174
- dialogId: DEFAULT_TEST_ROOT_DIALOG_ID,
175
- }
176
- : undefined;
163
+ : undefined;
177
164
  const imageUrls = [
178
165
  ...readRepeatedFlagValues(args, "--image"),
179
166
  ...readRepeatedFlagValues(args, "--image-url"),
180
167
  ];
181
- const useTaskWorktree = args.includes("--worktree-task");
168
+ const mimoLocalWorkspaceDefault =
169
+ isMonthlyMimoAgentRef(rawAgentKey, agentKey) && runtimeMode !== "server";
182
170
  const workflowRef = readFlagValue(args, "--workflow");
183
171
  const taskRowDbKey = readFlagValue(args, "--task-row-dbkey");
184
172
  const artifactIds = readFlagValue(args, "--artifact-ids")
@@ -186,12 +174,13 @@ export function parseAgentRunArgs(
186
174
  .map((item) => item.trim())
187
175
  .filter(Boolean);
188
176
  return {
189
- agentKey: resolveCliAgentKeyInput(agentKey),
177
+ agentKey,
190
178
  message,
191
179
  imageUrls,
192
- allowShell: args.includes("--dangerously-allow-shell"),
180
+ allowShell: args.includes("--dangerously-allow-shell") || mimoLocalWorkspaceDefault,
193
181
  traceTools: args.includes("--trace-tools"),
194
- useTaskWorktree,
182
+ ...(eventsMode ? { eventsMode } : {}),
183
+ injectFeatureWorktreeInstruction: mimoLocalWorkspaceDefault,
195
184
  ...(workflowRef ? { workflowRef } : {}),
196
185
  ...(taskRowDbKey
197
186
  ? {
@@ -203,17 +192,19 @@ export function parseAgentRunArgs(
203
192
  },
204
193
  }
205
194
  : {}),
206
- ...(runtimeMode ? { runtimeMode } : useTaskWorktree ? { runtimeMode: "local" as const } : {}),
195
+ ...(runtimeMode
196
+ ? { runtimeMode }
197
+ : mimoLocalWorkspaceDefault || args.includes("--dangerously-allow-shell")
198
+ ? { runtimeMode: "local" as const }
199
+ : {}),
207
200
  background: args.includes("--bg"),
208
201
  noStream: args.includes("--no-stream"),
209
- noDefaultTestRoot,
210
202
  ...(continueDialogId ? { continueDialogId } : {}),
211
203
  ...(!continueDialogId && spaceId ? { spaceId } : {}),
212
- ...(category ? { category } : !continueDialogId && !noDefaultTestRoot ? { category: DEFAULT_TEST_DIALOG_CATEGORY_ID } : {}),
204
+ ...(category ? { category } : {}),
213
205
  ...(inheritedRef?.dialogKey ? { inheritedFromDialogKey: inheritedRef.dialogKey } : {}),
214
206
  ...(inheritedRef?.dialogId ? { parentDialogId: inheritedRef.dialogId } : {}),
215
207
  ...(cwd ? { cwd } : {}),
216
- ...(typeof maxToolRounds === "number" ? { maxToolRounds } : {}),
217
208
  ...(typeof timeoutMs === "number" ? { timeoutMs } : {}),
218
209
  };
219
210
  }
@@ -269,6 +260,20 @@ export function prependWorkflowReferencePrompt(message: string, workflow?: Resol
269
260
  ].join("\n");
270
261
  }
271
262
 
263
+ function prependFeatureWorktreeInstruction(message: string, enabled: boolean) {
264
+ if (!enabled) return message;
265
+ return [
266
+ "Local execution rule:",
267
+ "- You are running in the current git checkout with shell access.",
268
+ "- For read-only checks, smoke tests, or answering questions, stay in the current directory.",
269
+ "- Before developing a new feature or making non-trivial code changes, create a separate git worktree yourself with git worktree and do the edits there.",
270
+ "- Commit and push only when the user explicitly asks or the task requires it.",
271
+ "",
272
+ "User task:",
273
+ message,
274
+ ].join("\n");
275
+ }
276
+
272
277
  function parseDialogReference(rawInput: string) {
273
278
  const normalized = rawInput.trim();
274
279
  if (normalized.startsWith("dialog-")) {
@@ -381,26 +386,20 @@ function resolveServerUrl(env: EnvLike) {
381
386
  return (env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL).replace(/\/+$/, "");
382
387
  }
383
388
 
384
- function shouldPrepareTaskWorktree(parsed: ParsedAgentRunArgs) {
385
- return !parsed.cwd && (parsed.useTaskWorktree || parsed.allowShell);
386
- }
387
-
388
- function buildTaskWorktreeEnv(args: {
389
+ function buildLocalRunEnv(args: {
389
390
  env: EnvLike;
390
- worktreePath?: string;
391
391
  allowShell: boolean;
392
392
  }) {
393
+ void args.allowShell;
393
394
  return {
394
395
  ...args.env,
395
- ...(args.allowShell ? { NOLO_LOCAL_SHELL_MODE: "worktree" } : {}),
396
- ...(args.worktreePath ? { NOLO_LOCAL_WORKTREE: args.worktreePath } : {}),
397
396
  };
398
397
  }
399
398
 
400
399
  function writeUsage(output: OutputLike) {
401
400
  output.write(
402
- "Usage: nolo agent run <agent> <message> [--local|--server|--auto] [--continue <dialogId>] [--cwd <path>] [--max-tool-rounds <n>]\n" +
403
- " nolo agent run --agent <agent> (--msg <message>|--msg-file <path>) [--image <url-or-path>] [--space <spaceId>] [--category <name>] [--inherit-from-dialog <dialog>] [--task-row-dbkey <key>] [--bg] [--timeout-ms <n>] [--no-stream]\n"
401
+ "Usage: nolo agent run <agent> <message> [--local|--server|--auto] [--continue <dialogId>] [--cwd <path>]\n" +
402
+ " nolo agent run --agent <agent> (--msg <message>|--msg-file <path>) [--image <url-or-path>] [--space <spaceId>] [--category <name>] [--inherit-from-dialog <dialog>] [--task-row-dbkey <key>] [--bg] [--timeout-ms <n>] [--events jsonl] [--no-stream]\n"
404
403
  );
405
404
  }
406
405
 
@@ -426,52 +425,24 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
426
425
  }
427
426
  }
428
427
  let localRuntimeCwd = parsed.cwd;
429
- if (shouldPrepareTaskWorktree(parsed)) {
430
- try {
431
- const preparedWorktree = await (deps.prepareTaskWorktree ?? prepareTaskWorktree)({
432
- agentKey: parsed.agentKey,
433
- env,
434
- });
435
- localRuntimeCwd = preparedWorktree.path;
436
- output.write(formatPreparedTaskWorktree(preparedWorktree));
437
- } catch (error) {
438
- output.write(
439
- "[nolo] Could not prepare a local task worktree.\n" +
440
- "Run from inside a git repository, or set NOLO_LOCAL_WORKTREE to an existing isolated checkout.\n" +
441
- `Reason: ${error instanceof Error ? error.message : String(error)}\n`
442
- );
443
- return 1;
444
- }
445
- }
446
- const shouldBindLocalWorkspace =
447
- !!localRuntimeCwd &&
448
- (parsed.runtimeMode === "local" || parsed.allowShell || parsed.useTaskWorktree);
449
- if (shouldBindLocalWorkspace) {
450
- try {
451
- await (deps.ensureWorkspacePackageLinks ?? ensureWorkspacePackageLinks)(
452
- localRuntimeCwd!
453
- );
454
- } catch (error) {
455
- output.write(
456
- "[nolo] Dependency preflight failed for local task worktree.\n" +
457
- "The worktree may have node_modules entries that point outside the task checkout, or paths that cannot be repaired safely.\n" +
458
- `Reason: ${error instanceof Error ? error.message : String(error)}\n`
459
- );
460
- return 1;
461
- }
428
+ if (!localRuntimeCwd && parsed.runtimeMode === "local") {
429
+ localRuntimeCwd = process.cwd();
462
430
  }
463
- const runEnv = shouldBindLocalWorkspace
464
- ? buildTaskWorktreeEnv({
465
- env,
466
- worktreePath: localRuntimeCwd,
467
- allowShell: parsed.allowShell,
468
- })
469
- : env;
431
+ const runEnv = buildLocalRunEnv({
432
+ env,
433
+ allowShell: parsed.allowShell,
434
+ });
470
435
  const result: RunAgentTurnResult = await runner({
471
436
  agentName: parsed.agentKey,
472
437
  agentKey: parsed.agentKey,
473
438
  serverUrl: resolveServerUrl(env),
474
- message: prependWorkflowReferencePrompt(parsed.message, workflowReference),
439
+ message: prependWorkflowReferencePrompt(
440
+ prependFeatureWorktreeInstruction(
441
+ parsed.message,
442
+ parsed.injectFeatureWorktreeInstruction
443
+ ),
444
+ workflowReference
445
+ ),
475
446
  imageUrls: parsed.imageUrls.map(normalizeCliImageInput),
476
447
  scriptDir: deps.scriptDir,
477
448
  env: runEnv,
@@ -488,10 +459,9 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
488
459
  ...(parsed.parentDialogId ? { parentDialogId: parsed.parentDialogId } : {}),
489
460
  background: parsed.background,
490
461
  noStream: parsed.noStream,
491
- noDefaultTestRoot: parsed.noDefaultTestRoot,
492
- ...(typeof parsed.maxToolRounds === "number" ? { maxToolRounds: parsed.maxToolRounds } : {}),
493
462
  ...(typeof parsed.timeoutMs === "number" ? { timeoutMs: parsed.timeoutMs } : {}),
494
463
  traceTools: parsed.traceTools,
464
+ ...(parsed.eventsMode ? { eventsMode: parsed.eventsMode } : {}),
495
465
  ...(parsed.taskRunContext ? { taskRunContext: parsed.taskRunContext } : {}),
496
466
  });
497
467
 
@@ -5,12 +5,9 @@ export {
5
5
  hasDirectOpenAiCompatibleProvider,
6
6
  createHybridRecordStore,
7
7
  executeLocalToolWithPolicy,
8
- activateWorkspaceSession,
9
- createWorkspaceSession,
10
8
  buildAgentRuntimeAgentLookupKeys,
11
9
  buildAgentRuntimeDialogWritePlan,
12
10
  dialogMessageRecordToAgentRuntimeMessage,
13
- formatWorkspaceSessionActivation,
14
11
  pickAgentRuntimeInferenceOptions,
15
12
  parseOpenAiCompatibleChatCompletionResponse,
16
13
  parsePlatformChatCompletionData,
@@ -23,6 +20,7 @@ export {
23
20
  resolveLocalToolPolicy,
24
21
  resolveOpenAiCompatibleProviderConfig,
25
22
  resolveRequestedRuntimeToolNames,
23
+ resolveRuntimeToolSurfaceForAgent,
26
24
  evaluateShellCommandPolicy,
27
25
  isDestructiveShellCommand,
28
26
  shouldUsePlatformChatProvider,
@@ -44,8 +42,4 @@ export type {
44
42
  HybridRecordKvDb,
45
43
  HybridRecordStore,
46
44
  LocalToolPolicyDecision,
47
- PreparedTaskWorkspace,
48
- PrepareTaskWorkspace,
49
- WorkspaceSession,
50
- WorkspaceSessionMode,
51
45
  } from "./agent-runtime";