nolo-cli 0.1.37 → 0.1.39
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/agent-runtime/agentRecordConfig.ts +0 -14
- package/agent-runtime/hostAdapter.ts +0 -1
- package/agent-runtime/index.ts +24 -18
- package/agent-runtime/localDialogRead.ts +23 -0
- package/agent-runtime/localToolPolicy.ts +2 -7
- package/agent-runtime/localWorkspaceTools.ts +9 -0
- package/agent-runtime/noloWorkspaceTools.ts +371 -0
- package/agent-runtime/runtimeToolPolicy.ts +1 -3
- package/agent-runtime/types.ts +1 -1
- package/agentAliases.ts +6 -0
- package/agentRunCommand.ts +57 -65
- package/agentRuntimeLocal.ts +0 -7
- package/chat/messages/fetchMessages.ts +84 -0
- package/chat/messages/types.ts +185 -0
- package/cli/agentAliases.ts +6 -0
- package/cli/agentRunCommand.ts +57 -65
- package/cli/agentRuntimeLocal.ts +0 -7
- package/cli/cliEnvHelpers.ts +11 -7
- package/cli/client/agentRun.ts +123 -24
- package/cli/client/localRuntimeAdapter.ts +87 -59
- package/cli/connectorRunArtifact.ts +0 -1
- package/cli/dialogCommands.ts +572 -0
- package/cli/dialogInternalCommandEntries.ts +4 -0
- package/cli/machineWsRunDispatch.ts +29 -37
- package/cli/scriptCommandEntries.ts +0 -2
- package/cli/tui/readlineWorkspace.ts +69 -0
- package/cli/tui/session.ts +176 -1
- package/cliEnvHelpers.ts +11 -7
- package/client/agentConfigResolver.test.ts +2 -3
- package/client/agentRun.test.ts +185 -1
- package/client/agentRun.ts +123 -24
- package/client/localRuntimeAdapter.test.ts +90 -25
- package/client/localRuntimeAdapter.ts +87 -59
- package/connectorRunArtifact.ts +0 -1
- package/database/server/db.ts +99 -0
- package/database/server/ensureDbOpen.ts +12 -0
- package/database/server/memoryAuthorityStore.ts +133 -0
- package/database/server/serverStoreFactory.ts +118 -0
- package/dialogCommands.ts +572 -0
- package/dialogInternalCommandEntries.ts +4 -0
- package/machineWsRunDispatch.ts +29 -37
- package/package.json +10 -9
- package/scriptCommandEntries.ts +0 -2
- package/tui/readlineWorkspace.ts +69 -0
- package/tui/session.ts +176 -1
- package/agent-runtime/taskWorkspace.ts +0 -193
- package/agent-runtime/workspaceSession.ts +0 -76
- package/cli/client/taskWorktree.ts +0 -8
- package/cli/client/workspaceSession.ts +0 -11
- package/client/taskWorktree.ts +0 -8
- package/client/workspaceSession.test.ts +0 -57
- package/client/workspaceSession.ts +0 -11
package/cli/agentRunCommand.ts
CHANGED
|
@@ -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
|
};
|
|
@@ -63,7 +55,7 @@ type ParsedAgentRunArgs = {
|
|
|
63
55
|
maxToolRounds?: number;
|
|
64
56
|
timeoutMs?: number;
|
|
65
57
|
traceTools: boolean;
|
|
66
|
-
|
|
58
|
+
injectFeatureWorktreeInstruction: boolean;
|
|
67
59
|
taskRunContext?: TaskRunPromptContext;
|
|
68
60
|
workflowRef?: string;
|
|
69
61
|
};
|
|
@@ -102,6 +94,21 @@ function runtimeModeFromArgs(args: string[]): AgentRuntimeRequestedMode | undefi
|
|
|
102
94
|
return undefined;
|
|
103
95
|
}
|
|
104
96
|
|
|
97
|
+
function isMonthlyMimoAgentRef(raw: string | undefined, resolved: string) {
|
|
98
|
+
const normalized = raw?.trim().toLowerCase();
|
|
99
|
+
return (
|
|
100
|
+
resolved === MIMO_MONTH_AGENT_KEY ||
|
|
101
|
+
normalized === "fullstack" ||
|
|
102
|
+
normalized === "full-stack" ||
|
|
103
|
+
normalized === "nolo-fullstack" ||
|
|
104
|
+
normalized === "包月mimo" ||
|
|
105
|
+
normalized === "包月mimo2.5" ||
|
|
106
|
+
normalized === "mimo-month" ||
|
|
107
|
+
normalized === "全栈" ||
|
|
108
|
+
normalized === "nolo 全栈工程师"
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
105
112
|
function parseNonNegativeInteger(value: string | undefined) {
|
|
106
113
|
if (!value) return undefined;
|
|
107
114
|
const parsed = Number(value);
|
|
@@ -123,7 +130,6 @@ function positionalArgs(args: string[]) {
|
|
|
123
130
|
"--server",
|
|
124
131
|
"--auto",
|
|
125
132
|
"--dangerously-allow-shell",
|
|
126
|
-
"--worktree-task",
|
|
127
133
|
"--trace-tools",
|
|
128
134
|
"--bg",
|
|
129
135
|
"--no-stream",
|
|
@@ -146,7 +152,8 @@ export function parseAgentRunArgs(
|
|
|
146
152
|
args: string[],
|
|
147
153
|
options: ParseAgentRunArgsOptions = {}
|
|
148
154
|
): ParsedAgentRunArgs | null {
|
|
149
|
-
const
|
|
155
|
+
const rawAgentKey = readFlagValue(args, "--agent") ?? positionalArgs(args)[0];
|
|
156
|
+
const agentKey = rawAgentKey ? resolveCliAgentKeyInput(rawAgentKey) : undefined;
|
|
150
157
|
const explicitMsg = readFlagValue(args, "--msg");
|
|
151
158
|
const msgFile = readFlagValue(args, "--msg-file");
|
|
152
159
|
const fileMessage = msgFile
|
|
@@ -178,7 +185,8 @@ export function parseAgentRunArgs(
|
|
|
178
185
|
...readRepeatedFlagValues(args, "--image"),
|
|
179
186
|
...readRepeatedFlagValues(args, "--image-url"),
|
|
180
187
|
];
|
|
181
|
-
const
|
|
188
|
+
const mimoLocalWorkspaceDefault =
|
|
189
|
+
isMonthlyMimoAgentRef(rawAgentKey, agentKey) && runtimeMode !== "server";
|
|
182
190
|
const workflowRef = readFlagValue(args, "--workflow");
|
|
183
191
|
const taskRowDbKey = readFlagValue(args, "--task-row-dbkey");
|
|
184
192
|
const artifactIds = readFlagValue(args, "--artifact-ids")
|
|
@@ -186,12 +194,12 @@ export function parseAgentRunArgs(
|
|
|
186
194
|
.map((item) => item.trim())
|
|
187
195
|
.filter(Boolean);
|
|
188
196
|
return {
|
|
189
|
-
agentKey
|
|
197
|
+
agentKey,
|
|
190
198
|
message,
|
|
191
199
|
imageUrls,
|
|
192
|
-
allowShell: args.includes("--dangerously-allow-shell"),
|
|
200
|
+
allowShell: args.includes("--dangerously-allow-shell") || mimoLocalWorkspaceDefault,
|
|
193
201
|
traceTools: args.includes("--trace-tools"),
|
|
194
|
-
|
|
202
|
+
injectFeatureWorktreeInstruction: mimoLocalWorkspaceDefault,
|
|
195
203
|
...(workflowRef ? { workflowRef } : {}),
|
|
196
204
|
...(taskRowDbKey
|
|
197
205
|
? {
|
|
@@ -203,7 +211,11 @@ export function parseAgentRunArgs(
|
|
|
203
211
|
},
|
|
204
212
|
}
|
|
205
213
|
: {}),
|
|
206
|
-
...(runtimeMode
|
|
214
|
+
...(runtimeMode
|
|
215
|
+
? { runtimeMode }
|
|
216
|
+
: mimoLocalWorkspaceDefault || args.includes("--dangerously-allow-shell")
|
|
217
|
+
? { runtimeMode: "local" as const }
|
|
218
|
+
: {}),
|
|
207
219
|
background: args.includes("--bg"),
|
|
208
220
|
noStream: args.includes("--no-stream"),
|
|
209
221
|
noDefaultTestRoot,
|
|
@@ -269,6 +281,20 @@ export function prependWorkflowReferencePrompt(message: string, workflow?: Resol
|
|
|
269
281
|
].join("\n");
|
|
270
282
|
}
|
|
271
283
|
|
|
284
|
+
function prependFeatureWorktreeInstruction(message: string, enabled: boolean) {
|
|
285
|
+
if (!enabled) return message;
|
|
286
|
+
return [
|
|
287
|
+
"Local execution rule:",
|
|
288
|
+
"- You are running in the current git checkout with shell access.",
|
|
289
|
+
"- For read-only checks, smoke tests, or answering questions, stay in the current directory.",
|
|
290
|
+
"- 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.",
|
|
291
|
+
"- Commit and push only when the user explicitly asks or the task requires it.",
|
|
292
|
+
"",
|
|
293
|
+
"User task:",
|
|
294
|
+
message,
|
|
295
|
+
].join("\n");
|
|
296
|
+
}
|
|
297
|
+
|
|
272
298
|
function parseDialogReference(rawInput: string) {
|
|
273
299
|
const normalized = rawInput.trim();
|
|
274
300
|
if (normalized.startsWith("dialog-")) {
|
|
@@ -381,19 +407,13 @@ function resolveServerUrl(env: EnvLike) {
|
|
|
381
407
|
return (env.NOLO_SERVER || env.BASE_URL || DEFAULT_NOLO_SERVER_URL).replace(/\/+$/, "");
|
|
382
408
|
}
|
|
383
409
|
|
|
384
|
-
function
|
|
385
|
-
return !parsed.cwd && (parsed.useTaskWorktree || parsed.allowShell);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function buildTaskWorktreeEnv(args: {
|
|
410
|
+
function buildLocalRunEnv(args: {
|
|
389
411
|
env: EnvLike;
|
|
390
|
-
worktreePath?: string;
|
|
391
412
|
allowShell: boolean;
|
|
392
413
|
}) {
|
|
393
414
|
return {
|
|
394
415
|
...args.env,
|
|
395
416
|
...(args.allowShell ? { NOLO_LOCAL_SHELL_MODE: "worktree" } : {}),
|
|
396
|
-
...(args.worktreePath ? { NOLO_LOCAL_WORKTREE: args.worktreePath } : {}),
|
|
397
417
|
};
|
|
398
418
|
}
|
|
399
419
|
|
|
@@ -426,52 +446,24 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
|
|
|
426
446
|
}
|
|
427
447
|
}
|
|
428
448
|
let localRuntimeCwd = parsed.cwd;
|
|
429
|
-
if (
|
|
430
|
-
|
|
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
|
-
}
|
|
449
|
+
if (!localRuntimeCwd && parsed.runtimeMode === "local") {
|
|
450
|
+
localRuntimeCwd = process.cwd();
|
|
445
451
|
}
|
|
446
|
-
const
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
}
|
|
462
|
-
}
|
|
463
|
-
const runEnv = shouldBindLocalWorkspace
|
|
464
|
-
? buildTaskWorktreeEnv({
|
|
465
|
-
env,
|
|
466
|
-
worktreePath: localRuntimeCwd,
|
|
467
|
-
allowShell: parsed.allowShell,
|
|
468
|
-
})
|
|
469
|
-
: env;
|
|
452
|
+
const runEnv = buildLocalRunEnv({
|
|
453
|
+
env,
|
|
454
|
+
allowShell: parsed.allowShell,
|
|
455
|
+
});
|
|
470
456
|
const result: RunAgentTurnResult = await runner({
|
|
471
457
|
agentName: parsed.agentKey,
|
|
472
458
|
agentKey: parsed.agentKey,
|
|
473
459
|
serverUrl: resolveServerUrl(env),
|
|
474
|
-
message: prependWorkflowReferencePrompt(
|
|
460
|
+
message: prependWorkflowReferencePrompt(
|
|
461
|
+
prependFeatureWorktreeInstruction(
|
|
462
|
+
parsed.message,
|
|
463
|
+
parsed.injectFeatureWorktreeInstruction
|
|
464
|
+
),
|
|
465
|
+
workflowReference
|
|
466
|
+
),
|
|
475
467
|
imageUrls: parsed.imageUrls.map(normalizeCliImageInput),
|
|
476
468
|
scriptDir: deps.scriptDir,
|
|
477
469
|
env: runEnv,
|
package/cli/agentRuntimeLocal.ts
CHANGED
|
@@ -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,
|
|
@@ -44,8 +41,4 @@ export type {
|
|
|
44
41
|
HybridRecordKvDb,
|
|
45
42
|
HybridRecordStore,
|
|
46
43
|
LocalToolPolicyDecision,
|
|
47
|
-
PreparedTaskWorkspace,
|
|
48
|
-
PrepareTaskWorkspace,
|
|
49
|
-
WorkspaceSession,
|
|
50
|
-
WorkspaceSessionMode,
|
|
51
44
|
} from "../agent-runtime";
|
package/cli/cliEnvHelpers.ts
CHANGED
|
@@ -96,14 +96,18 @@ export function resolveAuthToken(
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export function parseUserIdFromAuthToken(token: string) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
const parts = token.trim().split(".").filter(Boolean);
|
|
100
|
+
const payloadCandidates = parts.length >= 3 ? [parts[1], parts[0]] : [parts[0]];
|
|
101
|
+
for (const payload of payloadCandidates) {
|
|
102
|
+
if (!payload) continue;
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(Buffer.from(payload, "base64").toString("utf8"));
|
|
105
|
+
if (typeof parsed?.userId === "string") return parsed.userId;
|
|
106
|
+
} catch {
|
|
107
|
+
// Try the next token shape.
|
|
108
|
+
}
|
|
106
109
|
}
|
|
110
|
+
return "";
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
export function readOption(args: string[], flag: string) {
|
package/cli/client/agentRun.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "../agentAliases";
|
|
10
10
|
import type { LocalAgentToolEvent } from "../../agent-runtime/localLoop";
|
|
11
11
|
import type { AgentRuntimeHostAdapter, AgentRuntimeRequestedMode } from "../agentRuntimeLocal";
|
|
12
|
-
import { createCliLocalRuntimeAdapter } from "./localRuntimeAdapter";
|
|
12
|
+
import { createCliLocalRuntimeAdapter, isBuiltinNoloAgentRef } from "./localRuntimeAdapter";
|
|
13
13
|
import { createStreamingTextWriter } from "./streamingOutput";
|
|
14
14
|
import { prependTaskRunPrompt, type TaskRunPromptContext } from "./taskRunPrompt";
|
|
15
15
|
|
|
@@ -73,6 +73,47 @@ export type RunAgentTurnResult = {
|
|
|
73
73
|
streamInterrupted?: boolean;
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
+
class Spinner {
|
|
77
|
+
private timer: any = null;
|
|
78
|
+
private frameIndex = 0;
|
|
79
|
+
private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
80
|
+
private isTTY: boolean;
|
|
81
|
+
|
|
82
|
+
constructor(
|
|
83
|
+
private output: OutputLike,
|
|
84
|
+
private text: string
|
|
85
|
+
) {
|
|
86
|
+
this.isTTY = Boolean((output as any).isTTY ?? process.stdout.isTTY);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
start() {
|
|
90
|
+
if (!this.isTTY) {
|
|
91
|
+
this.output.write(`\n${this.text}...\n`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.frameIndex = 0;
|
|
96
|
+
this.output.write(`\x1b[36m${this.frames[0]}\x1b[39m ${this.text}...`);
|
|
97
|
+
|
|
98
|
+
this.timer = setInterval(() => {
|
|
99
|
+
this.frameIndex = (this.frameIndex + 1) % this.frames.length;
|
|
100
|
+
this.output.write(`\r\x1b[36m${this.frames[this.frameIndex]}\x1b[39m ${this.text}...`);
|
|
101
|
+
}, 80);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
stop() {
|
|
105
|
+
if (this.timer) {
|
|
106
|
+
clearInterval(this.timer);
|
|
107
|
+
this.timer = null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (this.isTTY) {
|
|
111
|
+
this.output.write("\r\x1b[K");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
76
117
|
const SERVER_PLATFORM_TOOL_NAMES = new Set([
|
|
77
118
|
"addTableRow",
|
|
78
119
|
"addTableRows",
|
|
@@ -139,10 +180,27 @@ function isKnownServerPlatformAgent(options: RunAgentTurnOptions) {
|
|
|
139
180
|
const normalizedKey = normalizeAgentRef(options.agentKey);
|
|
140
181
|
return Boolean(normalizedKey && KNOWN_SERVER_PLATFORM_AGENT_ALIASES.has(normalizedKey));
|
|
141
182
|
}
|
|
142
|
-
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
|
|
183
|
+
|
|
184
|
+
function isMachineBoundLocalhostCustomProvider(agentConfig: any) {
|
|
185
|
+
const machineId =
|
|
186
|
+
agentConfig?.runtimeBinding && typeof agentConfig.runtimeBinding === "object"
|
|
187
|
+
? String(agentConfig.runtimeBinding.machineId ?? "").trim()
|
|
188
|
+
: "";
|
|
189
|
+
const providerUrl = typeof agentConfig?.customProviderUrl === "string"
|
|
190
|
+
? agentConfig.customProviderUrl.trim()
|
|
191
|
+
: "";
|
|
192
|
+
if (!machineId || !providerUrl) return false;
|
|
193
|
+
try {
|
|
194
|
+
const hostname = new URL(providerUrl).hostname.toLowerCase();
|
|
195
|
+
return hostname === "127.0.0.1" || hostname === "localhost" || hostname === "::1";
|
|
196
|
+
} catch {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function resolveAuthToken(env: EnvLike) {
|
|
202
|
+
return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
|
|
203
|
+
}
|
|
146
204
|
|
|
147
205
|
function shouldShowUsage(env: EnvLike) {
|
|
148
206
|
return env.NOLO_DEBUG === "1" || env.NOLO_SHOW_USAGE === "1";
|
|
@@ -173,6 +231,10 @@ function resolveLocalRuntimeAdapter(options: RunAgentTurnOptions) {
|
|
|
173
231
|
}
|
|
174
232
|
|
|
175
233
|
async function shouldSkipAutoLocalForServerPlatformTools(options: RunAgentTurnOptions) {
|
|
234
|
+
if (isBuiltinNoloAgentRef(options.agentKey)) return false;
|
|
235
|
+
if (options.localRuntimeCwd && options.env.NOLO_LOCAL_SHELL_MODE === "worktree") {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
176
238
|
if (isKnownServerPlatformAgent(options)) {
|
|
177
239
|
options.output.write(
|
|
178
240
|
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a known platform agent. ` +
|
|
@@ -188,6 +250,13 @@ async function shouldSkipAutoLocalForServerPlatformTools(options: RunAgentTurnOp
|
|
|
188
250
|
} catch {
|
|
189
251
|
return false;
|
|
190
252
|
}
|
|
253
|
+
if (isMachineBoundLocalhostCustomProvider(agentConfig)) {
|
|
254
|
+
options.output.write(
|
|
255
|
+
`[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a machine-bound localhost custom provider. ` +
|
|
256
|
+
"Use --local explicitly to force the current machine.\n"
|
|
257
|
+
);
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
191
260
|
const serverTools = resolveServerPlatformToolNames(agentConfig);
|
|
192
261
|
if (serverTools.length === 0) return false;
|
|
193
262
|
options.output.write(
|
|
@@ -288,6 +357,13 @@ function formatToolTraceEvent(event: LocalAgentToolEvent) {
|
|
|
288
357
|
|
|
289
358
|
function shouldAttemptAutoLocal(options: RunAgentTurnOptions) {
|
|
290
359
|
if (options.localRuntimeAdapter || options.localRuntimeAdapterFactory) return true;
|
|
360
|
+
if (
|
|
361
|
+
options.env.NOLO_DISABLE_CLI_WORKSPACE_TOOLS !== "1" &&
|
|
362
|
+
isBuiltinNoloAgentRef(options.agentKey) &&
|
|
363
|
+
resolveAuthToken(options.env)
|
|
364
|
+
) {
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
291
367
|
return Boolean(
|
|
292
368
|
options.env.NOLO_LOCAL_OPENAI_API_KEY ||
|
|
293
369
|
options.env.OPENAI_API_KEY ||
|
|
@@ -502,7 +578,8 @@ function buildTransportErrorHint(serverUrl: string, error: unknown) {
|
|
|
502
578
|
}
|
|
503
579
|
|
|
504
580
|
async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string) {
|
|
505
|
-
options.output
|
|
581
|
+
const spinner = new Spinner(options.output, `${options.agentName} -> working`);
|
|
582
|
+
spinner.start();
|
|
506
583
|
|
|
507
584
|
const fetchImpl = options.fetchImpl ?? fetch;
|
|
508
585
|
let res: Response;
|
|
@@ -556,14 +633,16 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
|
|
|
556
633
|
...(typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {}),
|
|
557
634
|
stream: !options.noStream && !options.background,
|
|
558
635
|
}),
|
|
559
|
-
});
|
|
560
|
-
} catch (error) {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
636
|
+
});
|
|
637
|
+
} catch (error) {
|
|
638
|
+
spinner.stop();
|
|
639
|
+
options.output.write(buildTransportErrorHint(options.serverUrl, error));
|
|
640
|
+
return { exitCode: 1 };
|
|
641
|
+
}
|
|
642
|
+
|
|
565
643
|
const contentType = res.headers.get("content-type") || "";
|
|
566
644
|
if (contentType.includes("text/event-stream") && res.body) {
|
|
645
|
+
spinner.stop();
|
|
567
646
|
const result = await readStreamingAgentRun(options, res);
|
|
568
647
|
await recordTaskRunOutcome({
|
|
569
648
|
options,
|
|
@@ -577,18 +656,32 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
|
|
|
577
656
|
});
|
|
578
657
|
return result;
|
|
579
658
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
659
|
+
|
|
660
|
+
spinner.stop();
|
|
661
|
+
let data: any = {};
|
|
662
|
+
try {
|
|
663
|
+
data = await res.json();
|
|
664
|
+
} catch {
|
|
665
|
+
data = {};
|
|
666
|
+
}
|
|
667
|
+
|
|
588
668
|
if (!res.ok) {
|
|
589
669
|
options.output.write(`[nolo] Agent request failed: HTTP ${res.status}\n`);
|
|
590
|
-
|
|
591
|
-
|
|
670
|
+
const errorText = typeof data?.error === "string" ? data.error.trim() : "";
|
|
671
|
+
const messageText = typeof data?.message === "string" ? data.message.trim() : "";
|
|
672
|
+
const reasonText = typeof data?.reason === "string" ? data.reason.trim() : "";
|
|
673
|
+
const codeText = typeof data?.code === "string" ? data.code.trim() : "";
|
|
674
|
+
if (errorText || messageText) {
|
|
675
|
+
options.output.write(`${errorText || messageText}\n`);
|
|
676
|
+
if (messageText && messageText !== errorText) {
|
|
677
|
+
options.output.write(`${messageText}\n`);
|
|
678
|
+
}
|
|
679
|
+
if (codeText && codeText !== errorText && codeText !== messageText) {
|
|
680
|
+
options.output.write(`code=${codeText}\n`);
|
|
681
|
+
}
|
|
682
|
+
if (reasonText && reasonText !== errorText && reasonText !== messageText) {
|
|
683
|
+
options.output.write(`reason=${reasonText}\n`);
|
|
684
|
+
}
|
|
592
685
|
}
|
|
593
686
|
await recordTaskRunOutcome({
|
|
594
687
|
options,
|
|
@@ -691,7 +784,8 @@ async function runLocalAgentTurnForCli(
|
|
|
691
784
|
return { exitCode: 1 };
|
|
692
785
|
}
|
|
693
786
|
|
|
694
|
-
options.output
|
|
787
|
+
const spinner = new Spinner(options.output, `${options.agentName} -> working locally`);
|
|
788
|
+
spinner.start();
|
|
695
789
|
const toolEvents: LocalAgentToolEvent[] = [];
|
|
696
790
|
try {
|
|
697
791
|
const traceLocalTools = shouldTraceLocalTools(options);
|
|
@@ -716,11 +810,15 @@ async function runLocalAgentTurnForCli(
|
|
|
716
810
|
? {
|
|
717
811
|
onToolEvent: (event) => {
|
|
718
812
|
if (collectToolEvidence) toolEvents.push(event);
|
|
719
|
-
if (traceLocalTools)
|
|
813
|
+
if (traceLocalTools) {
|
|
814
|
+
spinner.stop();
|
|
815
|
+
options.output.write(formatToolTraceEvent(event));
|
|
816
|
+
}
|
|
720
817
|
},
|
|
721
818
|
}
|
|
722
819
|
: {}),
|
|
723
820
|
});
|
|
821
|
+
spinner.stop();
|
|
724
822
|
const content = result.content.trim();
|
|
725
823
|
if (content) {
|
|
726
824
|
options.output.write(`\n${options.agentName} > ${content}\n`);
|
|
@@ -736,6 +834,7 @@ async function runLocalAgentTurnForCli(
|
|
|
736
834
|
});
|
|
737
835
|
return { exitCode: 0, dialogId: result.dialogId };
|
|
738
836
|
} catch (error) {
|
|
837
|
+
spinner.stop();
|
|
739
838
|
if (settings.reportFailure) {
|
|
740
839
|
options.output.write(
|
|
741
840
|
`[nolo] Local agent run failed: ${
|