nolo-cli 0.1.18 → 0.1.20
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 +14 -0
- package/agent-runtime/hybridRecordStore.ts +147 -0
- package/agent-runtime/index.ts +69 -0
- package/agent-runtime/localLoop.ts +78 -6
- 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 +2 -0
- package/agent-runtime/workspaceSession.ts +76 -0
- package/agentAliases.ts +37 -0
- package/agentPullCommand.ts +1 -1
- package/agentRunCommand.ts +289 -54
- 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 +823 -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 +361 -7
- package/client/agentRun.ts +449 -63
- 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 +813 -20
- package/client/localRuntimeAdapter.ts +279 -232
- 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 +237 -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,19 +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
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
356
|
+
let localRuntimeCwd = parsed.cwd;
|
|
357
|
+
if (shouldPrepareTaskWorktree(parsed)) {
|
|
358
|
+
try {
|
|
359
|
+
const preparedWorktree = await (deps.prepareTaskWorktree ?? prepareTaskWorktree)({
|
|
360
|
+
agentKey: parsed.agentKey,
|
|
361
|
+
env,
|
|
362
|
+
});
|
|
363
|
+
localRuntimeCwd = preparedWorktree.path;
|
|
364
|
+
output.write(formatPreparedTaskWorktree(preparedWorktree));
|
|
365
|
+
} catch (error) {
|
|
366
|
+
output.write(
|
|
367
|
+
"[nolo] Could not prepare a local task worktree.\n" +
|
|
368
|
+
"Run from inside a git repository, or set NOLO_LOCAL_WORKTREE to an existing isolated checkout.\n" +
|
|
369
|
+
`Reason: ${error instanceof Error ? error.message : String(error)}\n`
|
|
370
|
+
);
|
|
371
|
+
return 1;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
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
|
+
}
|
|
188
390
|
}
|
|
189
|
-
const runEnv =
|
|
190
|
-
? {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
391
|
+
const runEnv = shouldBindLocalWorkspace
|
|
392
|
+
? buildTaskWorktreeEnv({
|
|
393
|
+
env,
|
|
394
|
+
worktreePath: localRuntimeCwd,
|
|
395
|
+
allowShell: parsed.allowShell,
|
|
396
|
+
})
|
|
195
397
|
: env;
|
|
196
398
|
const result: RunAgentTurnResult = await runner({
|
|
197
399
|
agentName: parsed.agentKey,
|
|
@@ -202,13 +404,46 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
|
|
|
202
404
|
scriptDir: deps.scriptDir,
|
|
203
405
|
env: runEnv,
|
|
204
406
|
output,
|
|
407
|
+
...(deps.localRuntimeAdapterFactory
|
|
408
|
+
? { localRuntimeAdapterFactory: deps.localRuntimeAdapterFactory }
|
|
409
|
+
: {}),
|
|
205
410
|
...(localRuntimeCwd ? { localRuntimeCwd } : {}),
|
|
206
411
|
...(parsed.runtimeMode ? { runtimeMode: parsed.runtimeMode } : {}),
|
|
207
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 } : {}),
|
|
208
424
|
});
|
|
209
425
|
|
|
210
426
|
if (result.dialogId) {
|
|
211
|
-
|
|
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
|
+
}
|
|
212
447
|
}
|
|
213
448
|
return result.exitCode;
|
|
214
449
|
}
|