nolo-cli 0.1.19 → 0.1.21
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.
- package/README.md +9 -1
- package/agent-runtime/agentConfigOptions.ts +12 -0
- package/agent-runtime/agentRecordConfig.ts +99 -0
- package/agent-runtime/agentRecordKeys.ts +14 -0
- package/agent-runtime/dialogMessageRecord.ts +16 -0
- package/agent-runtime/dialogWritePlan.ts +130 -0
- package/agent-runtime/hostAdapter.ts +13 -0
- package/agent-runtime/hybridRecordStore.ts +147 -0
- package/agent-runtime/index.ts +69 -0
- package/agent-runtime/localLoop.ts +69 -5
- package/agent-runtime/localToolPolicy.ts +130 -0
- package/agent-runtime/localWorkspaceTools.ts +1532 -0
- package/agent-runtime/openAiCompatibleProvider.ts +70 -0
- package/agent-runtime/openAiCompatibleProviderConfig.ts +38 -0
- package/agent-runtime/platformChatProvider.ts +241 -0
- package/agent-runtime/taskWorkspace.ts +193 -0
- package/agent-runtime/types.ts +1 -0
- package/agent-runtime/workspaceSession.ts +76 -0
- package/agentAliases.ts +37 -0
- package/agentPullCommand.ts +1 -1
- package/agentRunCommand.ts +278 -52
- package/agentRuntimeCommands.ts +354 -164
- package/agentRuntimeLocal.ts +38 -0
- package/ai/agent/agentSlice.ts +10 -0
- package/ai/agent/buildEditingContext.ts +5 -0
- package/ai/agent/buildSystemPrompt.ts +41 -18
- package/ai/agent/canvasEditingContext.ts +49 -0
- package/ai/agent/cliExecutor.ts +15 -4
- package/ai/agent/createAgentSchema.ts +2 -0
- package/ai/agent/executeToolCall.ts +3 -2
- package/ai/agent/hooks/usePublicAgents.ts +6 -0
- package/ai/agent/pageBuilderHandoffRules.ts +75 -0
- package/ai/agent/runAgentClientLoop.ts +4 -1
- package/ai/agent/runtimeGuidance.ts +19 -0
- package/ai/agent/server/fetchPublicAgents.ts +51 -1
- package/ai/agent/streamAgentChatTurn.ts +20 -2
- package/ai/agent/streamAgentChatTurnUtils.ts +60 -16
- package/ai/chat/accumulateToolCallChunks.ts +40 -9
- package/ai/chat/parseApiError.ts +3 -0
- package/ai/chat/sendOpenAICompletionsRequest.native.ts +23 -10
- package/ai/chat/sendOpenAICompletionsRequest.ts +13 -1
- package/ai/chat/updateTotalUsage.ts +26 -9
- package/ai/llm/deepinfra.ts +51 -0
- package/ai/llm/getPricing.ts +6 -0
- package/ai/llm/kimi.ts +2 -0
- package/ai/llm/openrouterModels.ts +0 -135
- package/ai/llm/providers.ts +1 -0
- package/ai/llm/types.ts +8 -0
- package/ai/taskRun/taskRunProtocol.ts +882 -0
- package/ai/token/calculatePrice.ts +30 -0
- package/ai/token/externalToolCost.ts +49 -29
- package/ai/token/prepareTokenUsageData.ts +6 -1
- package/ai/token/serverTokenWriter.ts +4 -2
- package/ai/tools/agent/agentTools.ts +21 -0
- package/ai/tools/agent/presets/appBuilderPreset.ts +7 -0
- package/ai/tools/agent/streamParallelAgentsTool.ts +2 -1
- package/ai/tools/agent/taskRunTool.ts +112 -0
- package/ai/tools/applyEditTool.ts +6 -3
- package/ai/tools/applyLineEditsTool.ts +6 -3
- package/ai/tools/checkEnvTool.ts +14 -9
- package/ai/tools/codeSearchTool.ts +17 -5
- package/ai/tools/execBashTool.ts +33 -29
- package/ai/tools/fetchWebpageSupport.ts +24 -0
- package/ai/tools/fetchWebpageTool.ts +18 -5
- package/ai/tools/index.ts +158 -0
- package/ai/tools/jdProductScraperTool.ts +821 -0
- package/ai/tools/listFilesTool.ts +6 -3
- package/ai/tools/localFilesTool.ts +200 -0
- package/ai/tools/readFileTool.ts +6 -3
- package/ai/tools/searchRepoTool.ts +6 -3
- package/ai/tools/table/rowTools.ts +6 -1
- package/ai/tools/taobaoTmallProductScraperTool.ts +49 -0
- package/ai/tools/toolApiClient.ts +20 -6
- package/ai/tools/wereadGatewayTool.ts +152 -0
- package/ai/tools/writeFileTool.ts +6 -3
- package/client/agentConfigResolver.test.ts +70 -0
- package/client/agentConfigResolver.ts +1 -0
- package/client/agentRun.test.ts +430 -7
- package/client/agentRun.ts +504 -64
- package/client/hybridRecordStore.test.ts +115 -0
- package/client/hybridRecordStore.ts +41 -0
- package/client/localAgentRecords.test.ts +27 -0
- package/client/localAgentRecords.ts +7 -0
- package/client/localDialogRecords.test.ts +124 -0
- package/client/localDialogRecords.ts +30 -0
- package/client/localProviderResolver.test.ts +78 -0
- package/client/localProviderResolver.ts +1 -0
- package/client/localRuntimeAdapter.test.ts +621 -9
- package/client/localRuntimeAdapter.ts +275 -250
- package/client/localRuntimeDryRun.test.ts +116 -0
- package/client/localToolPolicy.ts +8 -81
- package/client/taskRunPrompt.ts +26 -0
- package/client/taskWorktree.ts +8 -0
- package/client/workspaceSession.test.ts +57 -0
- package/client/workspaceSession.ts +11 -0
- package/commandRegistry.ts +23 -6
- package/connectorRunArtifact.ts +121 -0
- package/database/actions/write.ts +16 -2
- package/database/hooks/useUserData.ts +9 -3
- package/database/server/dataHandlers.ts +18 -20
- package/database/server/emailRepository.ts +3 -3
- package/database/server/patch.ts +18 -10
- package/database/server/query.ts +43 -4
- package/database/server/read.ts +24 -38
- package/database/server/recordIdentity.ts +100 -0
- package/database/server/write.ts +21 -25
- package/index.ts +70 -33
- package/machineCommands.ts +318 -144
- package/package.json +4 -1
- package/tableCommands.ts +181 -0
- package/taskRunCommand.ts +265 -0
package/agentRunCommand.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
|
|
2
2
|
import { runAgentTurn, type RunAgentTurnResult } from "./client/agentRun";
|
|
3
|
-
import type { AgentRuntimeRequestedMode } from "./agentRuntimeLocal";
|
|
3
|
+
import type { AgentRuntimeHostAdapter, AgentRuntimeRequestedMode } from "./agentRuntimeLocal";
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
-
import { extname,
|
|
5
|
+
import { extname, resolve } from "node:path";
|
|
6
6
|
import { resolveCliAgentKeyInput } from "./agentAliases";
|
|
7
|
+
import {
|
|
8
|
+
ensureWorkspacePackageLinks,
|
|
9
|
+
formatPreparedTaskWorktree,
|
|
10
|
+
prepareTaskWorktree,
|
|
11
|
+
type PreparedTaskWorktree,
|
|
12
|
+
} from "./client/taskWorktree";
|
|
13
|
+
import type { TaskRunPromptContext } from "./client/taskRunPrompt";
|
|
7
14
|
|
|
8
15
|
type EnvLike = Record<string, string | undefined>;
|
|
9
16
|
|
|
@@ -16,7 +23,24 @@ type AgentRunCommandDeps = {
|
|
|
16
23
|
scriptDir: string;
|
|
17
24
|
output?: OutputLike;
|
|
18
25
|
runner?: typeof runAgentTurn;
|
|
19
|
-
|
|
26
|
+
localRuntimeAdapterFactory?: (env: EnvLike, options?: { cwd?: string }) => AgentRuntimeHostAdapter;
|
|
27
|
+
prepareTaskWorktree?: typeof prepareTaskWorktree;
|
|
28
|
+
ensureWorkspacePackageLinks?: typeof ensureWorkspacePackageLinks;
|
|
29
|
+
inspectLocalRunWorkspace?: typeof inspectLocalRunWorkspace;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type LocalRunWorkspaceInspection = {
|
|
33
|
+
cwd: string;
|
|
34
|
+
clean: boolean;
|
|
35
|
+
status: string;
|
|
36
|
+
commit?: {
|
|
37
|
+
hash: string;
|
|
38
|
+
subject: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
type ParseAgentRunArgsOptions = {
|
|
43
|
+
readTextFile?: (path: string) => string;
|
|
20
44
|
};
|
|
21
45
|
|
|
22
46
|
type ParsedAgentRunArgs = {
|
|
@@ -26,8 +50,26 @@ type ParsedAgentRunArgs = {
|
|
|
26
50
|
allowShell: boolean;
|
|
27
51
|
runtimeMode?: AgentRuntimeRequestedMode;
|
|
28
52
|
continueDialogId?: string;
|
|
53
|
+
spaceId?: string;
|
|
54
|
+
category?: string;
|
|
55
|
+
inheritedFromDialogKey?: string;
|
|
56
|
+
parentDialogId?: string;
|
|
57
|
+
background: boolean;
|
|
58
|
+
noStream: boolean;
|
|
59
|
+
noDefaultTestRoot: boolean;
|
|
60
|
+
cwd?: string;
|
|
61
|
+
maxToolRounds?: number;
|
|
62
|
+
timeoutMs?: number;
|
|
63
|
+
traceTools: boolean;
|
|
64
|
+
useTaskWorktree: boolean;
|
|
65
|
+
taskRunContext?: TaskRunPromptContext;
|
|
29
66
|
};
|
|
30
67
|
|
|
68
|
+
const DEFAULT_TEST_ROOT_DIALOG_ID = "01KN6V3MVEQA4Q1PNKVM2W7YY8";
|
|
69
|
+
const DEFAULT_TEST_ROOT_DIALOG_KEY = `dialog-b2e06f801f-${DEFAULT_TEST_ROOT_DIALOG_ID}`;
|
|
70
|
+
const DEFAULT_AGENT_SPACE_ID = "01KKY77TT0DA9NY7TNW3R7255N";
|
|
71
|
+
const DEFAULT_TEST_DIALOG_CATEGORY_ID = "test-dialogs";
|
|
72
|
+
|
|
31
73
|
function readFlagValue(args: string[], flag: string) {
|
|
32
74
|
const index = args.indexOf(flag);
|
|
33
75
|
if (index < 0) return undefined;
|
|
@@ -52,6 +94,20 @@ function runtimeModeFromArgs(args: string[]): AgentRuntimeRequestedMode | undefi
|
|
|
52
94
|
return undefined;
|
|
53
95
|
}
|
|
54
96
|
|
|
97
|
+
function parseNonNegativeInteger(value: string | undefined) {
|
|
98
|
+
if (!value) return undefined;
|
|
99
|
+
const parsed = Number(value);
|
|
100
|
+
if (!Number.isFinite(parsed) || parsed < 0) return undefined;
|
|
101
|
+
return Math.floor(parsed);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function parsePositiveInteger(value: string | undefined) {
|
|
105
|
+
if (!value) return undefined;
|
|
106
|
+
const parsed = Number(value);
|
|
107
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return undefined;
|
|
108
|
+
return Math.floor(parsed);
|
|
109
|
+
}
|
|
110
|
+
|
|
55
111
|
function positionalArgs(args: string[]) {
|
|
56
112
|
const values: string[] = [];
|
|
57
113
|
const valuelessFlags = new Set([
|
|
@@ -59,6 +115,12 @@ function positionalArgs(args: string[]) {
|
|
|
59
115
|
"--server",
|
|
60
116
|
"--auto",
|
|
61
117
|
"--dangerously-allow-shell",
|
|
118
|
+
"--worktree-task",
|
|
119
|
+
"--trace-tools",
|
|
120
|
+
"--bg",
|
|
121
|
+
"--no-stream",
|
|
122
|
+
"--debug",
|
|
123
|
+
"--no-default-test-root",
|
|
62
124
|
]);
|
|
63
125
|
for (let index = 0; index < args.length; index += 1) {
|
|
64
126
|
const arg = args[index];
|
|
@@ -72,24 +134,98 @@ function positionalArgs(args: string[]) {
|
|
|
72
134
|
return values;
|
|
73
135
|
}
|
|
74
136
|
|
|
75
|
-
export function parseAgentRunArgs(
|
|
137
|
+
export function parseAgentRunArgs(
|
|
138
|
+
args: string[],
|
|
139
|
+
options: ParseAgentRunArgsOptions = {}
|
|
140
|
+
): ParsedAgentRunArgs | null {
|
|
76
141
|
const agentKey = readFlagValue(args, "--agent") ?? positionalArgs(args)[0];
|
|
77
142
|
const explicitMsg = readFlagValue(args, "--msg");
|
|
78
|
-
const
|
|
79
|
-
|
|
143
|
+
const msgFile = readFlagValue(args, "--msg-file");
|
|
144
|
+
const fileMessage = msgFile
|
|
145
|
+
? (options.readTextFile ?? ((path: string) => readFileSync(path, "utf8")))(msgFile)
|
|
146
|
+
: undefined;
|
|
147
|
+
const rawMessage = explicitMsg ?? fileMessage ?? positionalArgs(args).slice(1).join(" ");
|
|
148
|
+
if (!agentKey || !rawMessage.trim()) return null;
|
|
149
|
+
const message = rawMessage.trim();
|
|
80
150
|
const runtimeMode = runtimeModeFromArgs(args);
|
|
81
151
|
const continueDialogId = readFlagValue(args, "--continue") ?? readFlagValue(args, "--dialog");
|
|
152
|
+
const spaceId = readFlagValue(args, "--space");
|
|
153
|
+
const category = readFlagValue(args, "--category");
|
|
154
|
+
const inheritedFromDialog = readFlagValue(args, "--inherit-from-dialog");
|
|
155
|
+
const noDefaultTestRoot = args.includes("--no-default-test-root");
|
|
156
|
+
const cwd = readFlagValue(args, "--cwd");
|
|
157
|
+
const maxToolRounds = parseNonNegativeInteger(
|
|
158
|
+
readFlagValue(args, "--max-tool-rounds") ?? readFlagValue(args, "--rounds")
|
|
159
|
+
);
|
|
160
|
+
const timeoutMs = parsePositiveInteger(readFlagValue(args, "--timeout-ms"));
|
|
161
|
+
const inheritedRef = inheritedFromDialog
|
|
162
|
+
? parseDialogReference(inheritedFromDialog)
|
|
163
|
+
: !continueDialogId && !noDefaultTestRoot
|
|
164
|
+
? {
|
|
165
|
+
dialogKey: DEFAULT_TEST_ROOT_DIALOG_KEY,
|
|
166
|
+
dialogId: DEFAULT_TEST_ROOT_DIALOG_ID,
|
|
167
|
+
}
|
|
168
|
+
: undefined;
|
|
82
169
|
const imageUrls = [
|
|
83
170
|
...readRepeatedFlagValues(args, "--image"),
|
|
84
171
|
...readRepeatedFlagValues(args, "--image-url"),
|
|
85
172
|
];
|
|
173
|
+
const useTaskWorktree = args.includes("--worktree-task");
|
|
174
|
+
const taskRowDbKey = readFlagValue(args, "--task-row-dbkey");
|
|
175
|
+
const artifactIds = readFlagValue(args, "--artifact-ids")
|
|
176
|
+
?.split(",")
|
|
177
|
+
.map((item) => item.trim())
|
|
178
|
+
.filter(Boolean);
|
|
86
179
|
return {
|
|
87
180
|
agentKey: resolveCliAgentKeyInput(agentKey),
|
|
88
|
-
message
|
|
181
|
+
message,
|
|
89
182
|
imageUrls,
|
|
90
183
|
allowShell: args.includes("--dangerously-allow-shell"),
|
|
91
|
-
|
|
184
|
+
traceTools: args.includes("--trace-tools"),
|
|
185
|
+
useTaskWorktree,
|
|
186
|
+
...(taskRowDbKey
|
|
187
|
+
? {
|
|
188
|
+
taskRunContext: {
|
|
189
|
+
rowDbKey: taskRowDbKey,
|
|
190
|
+
...(readFlagValue(args, "--task-run-id") ? { taskRunId: readFlagValue(args, "--task-run-id") } : {}),
|
|
191
|
+
...(readFlagValue(args, "--work-item-id") ? { workItemId: readFlagValue(args, "--work-item-id") } : {}),
|
|
192
|
+
...(artifactIds?.length ? { artifactIds } : {}),
|
|
193
|
+
},
|
|
194
|
+
}
|
|
195
|
+
: {}),
|
|
196
|
+
...(runtimeMode ? { runtimeMode } : useTaskWorktree ? { runtimeMode: "local" as const } : {}),
|
|
197
|
+
background: args.includes("--bg"),
|
|
198
|
+
noStream: args.includes("--no-stream"),
|
|
199
|
+
noDefaultTestRoot,
|
|
92
200
|
...(continueDialogId ? { continueDialogId } : {}),
|
|
201
|
+
...(!continueDialogId ? { spaceId: spaceId ?? DEFAULT_AGENT_SPACE_ID } : {}),
|
|
202
|
+
...(category ? { category } : !continueDialogId && !noDefaultTestRoot ? { category: DEFAULT_TEST_DIALOG_CATEGORY_ID } : {}),
|
|
203
|
+
...(inheritedRef?.dialogKey ? { inheritedFromDialogKey: inheritedRef.dialogKey } : {}),
|
|
204
|
+
...(inheritedRef?.dialogId ? { parentDialogId: inheritedRef.dialogId } : {}),
|
|
205
|
+
...(cwd ? { cwd } : {}),
|
|
206
|
+
...(typeof maxToolRounds === "number" ? { maxToolRounds } : {}),
|
|
207
|
+
...(typeof timeoutMs === "number" ? { timeoutMs } : {}),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function parseDialogReference(rawInput: string) {
|
|
212
|
+
const normalized = rawInput.trim();
|
|
213
|
+
if (normalized.startsWith("dialog-")) {
|
|
214
|
+
const parts = normalized.split("-");
|
|
215
|
+
return {
|
|
216
|
+
dialogKey: normalized,
|
|
217
|
+
dialogId: parts.at(-1) ?? normalized,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const dialogMatch = normalized.match(/dialog-([a-zA-Z0-9]+)-([a-zA-Z0-9]+)/);
|
|
221
|
+
if (dialogMatch) {
|
|
222
|
+
return {
|
|
223
|
+
dialogKey: `dialog-${dialogMatch[1]}-${dialogMatch[2]}`,
|
|
224
|
+
dialogId: dialogMatch[2],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
dialogId: normalized,
|
|
93
229
|
};
|
|
94
230
|
}
|
|
95
231
|
|
|
@@ -115,24 +251,14 @@ export function normalizeCliImageInput(input: string) {
|
|
|
115
251
|
return `data:${mimeTypeForPath(absolutePath)};base64,${base64}`;
|
|
116
252
|
}
|
|
117
253
|
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function createTaskId() {
|
|
127
|
-
return Date.now().toString(36).toUpperCase();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function runGit(args: string[], cwd: string) {
|
|
131
|
-
const proc = Bun.spawn(["git", ...args], {
|
|
132
|
-
cwd,
|
|
254
|
+
async function runGitForLocalSummary(args: {
|
|
255
|
+
cwd: string;
|
|
256
|
+
gitArgs: string[];
|
|
257
|
+
}) {
|
|
258
|
+
const proc = Bun.spawn(["git", ...args.gitArgs], {
|
|
259
|
+
cwd: args.cwd,
|
|
133
260
|
stdout: "pipe",
|
|
134
261
|
stderr: "pipe",
|
|
135
|
-
stdin: "ignore",
|
|
136
262
|
});
|
|
137
263
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
138
264
|
new Response(proc.stdout).text(),
|
|
@@ -140,32 +266,80 @@ async function runGit(args: string[], cwd: string) {
|
|
|
140
266
|
proc.exited,
|
|
141
267
|
]);
|
|
142
268
|
if (exitCode !== 0) {
|
|
143
|
-
throw new Error(stderr
|
|
269
|
+
throw new Error((stderr || stdout || `git ${args.gitArgs.join(" ")} failed`).trim());
|
|
144
270
|
}
|
|
145
271
|
return stdout.trim();
|
|
146
272
|
}
|
|
147
273
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
274
|
+
async function inspectLocalRunWorkspace(cwd: string): Promise<LocalRunWorkspaceInspection> {
|
|
275
|
+
const status = await runGitForLocalSummary({
|
|
276
|
+
cwd,
|
|
277
|
+
gitArgs: ["status", "--short"],
|
|
278
|
+
});
|
|
279
|
+
const rawCommit = await runGitForLocalSummary({
|
|
280
|
+
cwd,
|
|
281
|
+
gitArgs: ["log", "-1", "--format=%h%x00%s"],
|
|
282
|
+
}).catch(() => "");
|
|
283
|
+
const [hash, subject] = rawCommit.split("\0");
|
|
284
|
+
return {
|
|
285
|
+
cwd,
|
|
286
|
+
clean: status.trim() === "",
|
|
287
|
+
status,
|
|
288
|
+
...(hash ? { commit: { hash, subject: subject ?? "" } } : {}),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function shouldPrintLocalRunSummary(args: {
|
|
293
|
+
parsed: ParsedAgentRunArgs;
|
|
294
|
+
localRuntimeCwd?: string;
|
|
295
|
+
}) {
|
|
296
|
+
return Boolean(args.localRuntimeCwd && args.parsed.runtimeMode === "local");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function formatLocalRunSummary(args: {
|
|
300
|
+
dialogId?: string;
|
|
301
|
+
inspection: LocalRunWorkspaceInspection;
|
|
302
|
+
}) {
|
|
303
|
+
return [
|
|
304
|
+
"",
|
|
305
|
+
"[nolo] local run summary",
|
|
306
|
+
`workspace: ${args.inspection.cwd}`,
|
|
307
|
+
...(args.dialogId ? [`dialog: ${args.dialogId}`] : []),
|
|
308
|
+
...(args.inspection.commit
|
|
309
|
+
? [`commit: ${args.inspection.commit.hash} ${args.inspection.commit.subject}`]
|
|
310
|
+
: []),
|
|
311
|
+
`dirty: ${args.inspection.clean ? "clean" : "dirty"}`,
|
|
312
|
+
...(!args.inspection.clean && args.inspection.status.trim()
|
|
313
|
+
? [`status:\n${args.inspection.status.trim()}`]
|
|
314
|
+
: []),
|
|
315
|
+
"",
|
|
316
|
+
].join("\n");
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function resolveServerUrl(env: EnvLike) {
|
|
320
|
+
return (env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL).replace(/\/+$/, "");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function shouldPrepareTaskWorktree(parsed: ParsedAgentRunArgs) {
|
|
324
|
+
return !parsed.cwd && (parsed.useTaskWorktree || parsed.allowShell);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function buildTaskWorktreeEnv(args: {
|
|
328
|
+
env: EnvLike;
|
|
329
|
+
worktreePath?: string;
|
|
330
|
+
allowShell: boolean;
|
|
152
331
|
}) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const taskId = createTaskId();
|
|
159
|
-
const worktreePath = join(parent, `nolo-agent-${safeAgent}-${taskId}`);
|
|
160
|
-
const branchName = `nolo-agent-${safeAgent}-${taskId}`;
|
|
161
|
-
await runGit(["worktree", "add", worktreePath, "-b", branchName, "HEAD"], root);
|
|
162
|
-
return worktreePath;
|
|
332
|
+
return {
|
|
333
|
+
...args.env,
|
|
334
|
+
...(args.allowShell ? { NOLO_LOCAL_SHELL_MODE: "worktree" } : {}),
|
|
335
|
+
...(args.worktreePath ? { NOLO_LOCAL_WORKTREE: args.worktreePath } : {}),
|
|
336
|
+
};
|
|
163
337
|
}
|
|
164
338
|
|
|
165
339
|
function writeUsage(output: OutputLike) {
|
|
166
340
|
output.write(
|
|
167
|
-
"Usage: nolo agent run <agent> <message> [--local|--server|--auto] [--continue <dialogId>]\n" +
|
|
168
|
-
" nolo agent run --agent <agent> --msg <message> [--image <url-or-path>] [--
|
|
341
|
+
"Usage: nolo agent run <agent> <message> [--local|--server|--auto] [--continue <dialogId>] [--cwd <path>] [--max-tool-rounds <n>]\n" +
|
|
342
|
+
" nolo agent run --agent <agent> --msg <message> [--image <url-or-path>] [--space <spaceId>] [--category <name>] [--inherit-from-dialog <dialog>] [--task-row-dbkey <key>] [--bg] [--timeout-ms <n>] [--no-stream]\n"
|
|
169
343
|
);
|
|
170
344
|
}
|
|
171
345
|
|
|
@@ -179,28 +353,47 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
|
|
|
179
353
|
}
|
|
180
354
|
|
|
181
355
|
const runner = deps.runner ?? runAgentTurn;
|
|
182
|
-
let localRuntimeCwd
|
|
183
|
-
if (parsed
|
|
356
|
+
let localRuntimeCwd = parsed.cwd;
|
|
357
|
+
if (shouldPrepareTaskWorktree(parsed)) {
|
|
184
358
|
try {
|
|
185
|
-
|
|
359
|
+
const preparedWorktree = await (deps.prepareTaskWorktree ?? prepareTaskWorktree)({
|
|
186
360
|
agentKey: parsed.agentKey,
|
|
187
361
|
env,
|
|
188
362
|
});
|
|
363
|
+
localRuntimeCwd = preparedWorktree.path;
|
|
364
|
+
output.write(formatPreparedTaskWorktree(preparedWorktree));
|
|
189
365
|
} catch (error) {
|
|
190
366
|
output.write(
|
|
191
|
-
"[nolo] Could not prepare a local
|
|
367
|
+
"[nolo] Could not prepare a local task worktree.\n" +
|
|
192
368
|
"Run from inside a git repository, or set NOLO_LOCAL_WORKTREE to an existing isolated checkout.\n" +
|
|
193
369
|
`Reason: ${error instanceof Error ? error.message : String(error)}\n`
|
|
194
370
|
);
|
|
195
371
|
return 1;
|
|
196
372
|
}
|
|
197
373
|
}
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
374
|
+
const shouldBindLocalWorkspace =
|
|
375
|
+
!!localRuntimeCwd &&
|
|
376
|
+
(parsed.runtimeMode === "local" || parsed.allowShell || parsed.useTaskWorktree);
|
|
377
|
+
if (shouldBindLocalWorkspace) {
|
|
378
|
+
try {
|
|
379
|
+
await (deps.ensureWorkspacePackageLinks ?? ensureWorkspacePackageLinks)(
|
|
380
|
+
localRuntimeCwd!
|
|
381
|
+
);
|
|
382
|
+
} catch (error) {
|
|
383
|
+
output.write(
|
|
384
|
+
"[nolo] Dependency preflight failed for local task worktree.\n" +
|
|
385
|
+
"The worktree may have node_modules entries that point outside the task checkout, or paths that cannot be repaired safely.\n" +
|
|
386
|
+
`Reason: ${error instanceof Error ? error.message : String(error)}\n`
|
|
387
|
+
);
|
|
388
|
+
return 1;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const runEnv = shouldBindLocalWorkspace
|
|
392
|
+
? buildTaskWorktreeEnv({
|
|
393
|
+
env,
|
|
394
|
+
worktreePath: localRuntimeCwd,
|
|
395
|
+
allowShell: parsed.allowShell,
|
|
396
|
+
})
|
|
204
397
|
: env;
|
|
205
398
|
const result: RunAgentTurnResult = await runner({
|
|
206
399
|
agentName: parsed.agentKey,
|
|
@@ -211,13 +404,46 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
|
|
|
211
404
|
scriptDir: deps.scriptDir,
|
|
212
405
|
env: runEnv,
|
|
213
406
|
output,
|
|
407
|
+
...(deps.localRuntimeAdapterFactory
|
|
408
|
+
? { localRuntimeAdapterFactory: deps.localRuntimeAdapterFactory }
|
|
409
|
+
: {}),
|
|
214
410
|
...(localRuntimeCwd ? { localRuntimeCwd } : {}),
|
|
215
411
|
...(parsed.runtimeMode ? { runtimeMode: parsed.runtimeMode } : {}),
|
|
216
412
|
...(parsed.continueDialogId ? { continueDialogId: parsed.continueDialogId } : {}),
|
|
413
|
+
...(parsed.spaceId ? { spaceId: parsed.spaceId } : {}),
|
|
414
|
+
...(parsed.category ? { category: parsed.category } : {}),
|
|
415
|
+
...(parsed.inheritedFromDialogKey ? { inheritedFromDialogKey: parsed.inheritedFromDialogKey } : {}),
|
|
416
|
+
...(parsed.parentDialogId ? { parentDialogId: parsed.parentDialogId } : {}),
|
|
417
|
+
background: parsed.background,
|
|
418
|
+
noStream: parsed.noStream,
|
|
419
|
+
noDefaultTestRoot: parsed.noDefaultTestRoot,
|
|
420
|
+
...(typeof parsed.maxToolRounds === "number" ? { maxToolRounds: parsed.maxToolRounds } : {}),
|
|
421
|
+
...(typeof parsed.timeoutMs === "number" ? { timeoutMs: parsed.timeoutMs } : {}),
|
|
422
|
+
traceTools: parsed.traceTools,
|
|
423
|
+
...(parsed.taskRunContext ? { taskRunContext: parsed.taskRunContext } : {}),
|
|
217
424
|
});
|
|
218
425
|
|
|
219
426
|
if (result.dialogId) {
|
|
220
|
-
|
|
427
|
+
if (parsed.background) {
|
|
428
|
+
output.write(`\n[nolo] background dialog ${result.dialogId}\n`);
|
|
429
|
+
output.write(`[nolo] read: nolo dialog read ${result.dialogId}\n`);
|
|
430
|
+
} else {
|
|
431
|
+
output.write(`\n[nolo] dialog ${result.dialogId}\n`);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (shouldPrintLocalRunSummary({ parsed, localRuntimeCwd })) {
|
|
435
|
+
try {
|
|
436
|
+
const inspect = deps.inspectLocalRunWorkspace ?? inspectLocalRunWorkspace;
|
|
437
|
+
const inspection = await inspect(localRuntimeCwd!);
|
|
438
|
+
output.write(formatLocalRunSummary({
|
|
439
|
+
dialogId: result.dialogId,
|
|
440
|
+
inspection,
|
|
441
|
+
}));
|
|
442
|
+
} catch (error) {
|
|
443
|
+
output.write(
|
|
444
|
+
`\n[nolo] local run summary unavailable: ${error instanceof Error ? error.message : String(error)}\n`
|
|
445
|
+
);
|
|
446
|
+
}
|
|
221
447
|
}
|
|
222
448
|
return result.exitCode;
|
|
223
449
|
}
|