nolo-cli 0.1.36 → 0.1.38

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/agentAliases.ts CHANGED
@@ -1,52 +1,72 @@
1
1
  export const PLATFORM_DEMO_USER_ID = "b2e06f801f";
2
- export const FRONTEND_IMPLEMENTER_AGENT_ID = "01FRONTENDAG0000000115N4E1";
3
- export const FRONTEND_IMPLEMENTER_AGENT_KEY =
4
- `agent-${PLATFORM_DEMO_USER_ID}-${FRONTEND_IMPLEMENTER_AGENT_ID}`;
5
- export const NOLO_FRONTEND_AGENT_ID = FRONTEND_IMPLEMENTER_AGENT_ID;
6
- export const NOLO_FRONTEND_AGENT_KEY = FRONTEND_IMPLEMENTER_AGENT_KEY;
7
2
  export const NOLO_PROJECT_MANAGER_AGENT_ID = "01NOLOPROJMGR00000000MSVGG";
8
3
  export const NOLO_PROJECT_MANAGER_AGENT_KEY =
9
4
  `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_PROJECT_MANAGER_AGENT_ID}`;
10
- export const NOLO_FULLSTACK_AGENT_ID = "01NOLOFULLSTK00000001J740Q";
11
- export const NOLO_FULLSTACK_AGENT_KEY =
12
- `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_FULLSTACK_AGENT_ID}`;
13
5
  export const NOLO_SENIOR_FULLSTACK_AGENT_ID = "01NOLOFULLSTK00000000T3XR4";
14
6
  export const NOLO_SENIOR_FULLSTACK_AGENT_KEY =
15
7
  `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_SENIOR_FULLSTACK_AGENT_ID}`;
16
- export const NOLO_REVIEW_AGENT_ID = "01CODEXCLIMAIN00000001OK";
17
- export const NOLO_REVIEW_AGENT_KEY =
18
- `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_REVIEW_AGENT_ID}`;
8
+ export const NOLO_FRONTEND_AGENT_ID = "01FRONTENDAG0000000115N4E1";
9
+ export const NOLO_FRONTEND_AGENT_KEY =
10
+ `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_FRONTEND_AGENT_ID}`;
11
+ export const WIN_CODEX_AGENT_ID = "WINCODEX00000000FQ0LK0";
12
+ export const WIN_CODEX_AGENT_KEY =
13
+ `agent-0e95801d90-${WIN_CODEX_AGENT_ID}`;
14
+ export const WIN_QWEN_AGENT_ID = "01CUSTOMCODEA00000001JEAG3";
15
+ export const WIN_QWEN_AGENT_KEY =
16
+ `agent-0e95801d90-${WIN_QWEN_AGENT_ID}`;
17
+ export const MIMO_MONTH_AGENT_ID = "01MIMO25MONTH0000000NEW001";
18
+ export const MIMO_MONTH_AGENT_KEY =
19
+ `agent-0e95801d90-${MIMO_MONTH_AGENT_ID}`;
19
20
 
20
21
  const AGENT_ALIAS_TO_KEY: Record<string, string> = {
21
- "nolo-frontend": FRONTEND_IMPLEMENTER_AGENT_KEY,
22
- "frontend-implementer": FRONTEND_IMPLEMENTER_AGENT_KEY,
23
- "frontend-agent": FRONTEND_IMPLEMENTER_AGENT_KEY,
24
- frontend: FRONTEND_IMPLEMENTER_AGENT_KEY,
25
- "front-end": FRONTEND_IMPLEMENTER_AGENT_KEY,
26
- "前端": FRONTEND_IMPLEMENTER_AGENT_KEY,
27
- "前端实现员": FRONTEND_IMPLEMENTER_AGENT_KEY,
22
+ // Win Codex (高智力终审与架构把关)
23
+ "win-codex": WIN_CODEX_AGENT_KEY,
24
+ "wincodex": WIN_CODEX_AGENT_KEY,
25
+ "win codex": WIN_CODEX_AGENT_KEY,
26
+ "nolo-reviewer": WIN_CODEX_AGENT_KEY,
27
+ reviewer: WIN_CODEX_AGENT_KEY,
28
+ "nolo-code-review": WIN_CODEX_AGENT_KEY,
29
+ "code-review": WIN_CODEX_AGENT_KEY,
30
+ review: WIN_CODEX_AGENT_KEY,
31
+ "代码审查": WIN_CODEX_AGENT_KEY,
32
+ "nolo 代码审查": WIN_CODEX_AGENT_KEY,
33
+
34
+ // 包月 Mimo (全栈业务代码与常规杂活物理实体)
35
+ "包月mimo": MIMO_MONTH_AGENT_KEY,
36
+ "包月mimo2.5": MIMO_MONTH_AGENT_KEY,
37
+ "mimo-month": MIMO_MONTH_AGENT_KEY,
38
+ "nolo-fullstack": MIMO_MONTH_AGENT_KEY,
39
+ fullstack: MIMO_MONTH_AGENT_KEY,
40
+ "full-stack": MIMO_MONTH_AGENT_KEY,
41
+ "全栈": MIMO_MONTH_AGENT_KEY,
42
+ "nolo 全栈工程师": MIMO_MONTH_AGENT_KEY,
43
+
44
+ // 产品前端实现
45
+ "frontend": NOLO_FRONTEND_AGENT_KEY,
46
+ "frontend-agent": NOLO_FRONTEND_AGENT_KEY,
47
+ "frontend-implementer": NOLO_FRONTEND_AGENT_KEY,
48
+ "nolo-frontend": NOLO_FRONTEND_AGENT_KEY,
49
+ "nolo frontend": NOLO_FRONTEND_AGENT_KEY,
50
+ "前端agent": NOLO_FRONTEND_AGENT_KEY,
51
+ "前端 agent": NOLO_FRONTEND_AGENT_KEY,
52
+ "前端": NOLO_FRONTEND_AGENT_KEY,
53
+
54
+ // Win Qwen (本地轻量无Token消耗辅助)
55
+ "win-qwen": WIN_QWEN_AGENT_KEY,
56
+ "win qwen": WIN_QWEN_AGENT_KEY,
57
+ "winqwen": WIN_QWEN_AGENT_KEY,
58
+
59
+ // 其他辅助/特定角色
28
60
  "nolo-project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
29
61
  "project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
30
62
  "nolo-pm": NOLO_PROJECT_MANAGER_AGENT_KEY,
31
63
  pm: NOLO_PROJECT_MANAGER_AGENT_KEY,
32
64
  "项目经理": NOLO_PROJECT_MANAGER_AGENT_KEY,
33
65
  "nolo 项目经理": NOLO_PROJECT_MANAGER_AGENT_KEY,
34
- "nolo-fullstack": NOLO_FULLSTACK_AGENT_KEY,
35
- fullstack: NOLO_FULLSTACK_AGENT_KEY,
36
- "full-stack": NOLO_FULLSTACK_AGENT_KEY,
37
- "全栈": NOLO_FULLSTACK_AGENT_KEY,
38
- "nolo 全栈工程师": NOLO_FULLSTACK_AGENT_KEY,
39
66
  "senior-fullstack": NOLO_SENIOR_FULLSTACK_AGENT_KEY,
40
67
  "nolo-senior-fullstack": NOLO_SENIOR_FULLSTACK_AGENT_KEY,
41
68
  "高级全栈": NOLO_SENIOR_FULLSTACK_AGENT_KEY,
42
69
  "nolo 高级全栈工程师": NOLO_SENIOR_FULLSTACK_AGENT_KEY,
43
- "nolo-reviewer": NOLO_REVIEW_AGENT_KEY,
44
- reviewer: NOLO_REVIEW_AGENT_KEY,
45
- "nolo-code-review": NOLO_REVIEW_AGENT_KEY,
46
- "code-review": NOLO_REVIEW_AGENT_KEY,
47
- review: NOLO_REVIEW_AGENT_KEY,
48
- "代码审查": NOLO_REVIEW_AGENT_KEY,
49
- "nolo 代码审查": NOLO_REVIEW_AGENT_KEY,
50
70
  };
51
71
 
52
72
  function parseAgentKeyFromInput(raw: string): string {
@@ -400,7 +400,7 @@ function buildTaskWorktreeEnv(args: {
400
400
  function writeUsage(output: OutputLike) {
401
401
  output.write(
402
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> [--image <url-or-path>] [--space <spaceId>] [--category <name>] [--inherit-from-dialog <dialog>] [--task-row-dbkey <key>] [--bg] [--timeout-ms <n>] [--no-stream]\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"
404
404
  );
405
405
  }
406
406
 
@@ -16,6 +16,9 @@ export {
16
16
  parsePlatformChatCompletionData,
17
17
  parsePlatformChatCompletionResponse,
18
18
  resolveAgentRuntimeConfigFromRecord,
19
+ resolveCurrentRunRuntimeToolPolicy,
20
+ resolveLocalRuntimeEnvFromPolicy,
21
+ resolveLocalWorkspaceExecutorOptionsFromPolicy,
19
22
  resolvePlatformChatProviderConfig,
20
23
  resolveLocalToolPolicy,
21
24
  resolveOpenAiCompatibleProviderConfig,
@@ -1,52 +1,72 @@
1
1
  export const PLATFORM_DEMO_USER_ID = "b2e06f801f";
2
- export const FRONTEND_IMPLEMENTER_AGENT_ID = "01FRONTENDAG0000000115N4E1";
3
- export const FRONTEND_IMPLEMENTER_AGENT_KEY =
4
- `agent-${PLATFORM_DEMO_USER_ID}-${FRONTEND_IMPLEMENTER_AGENT_ID}`;
5
- export const NOLO_FRONTEND_AGENT_ID = FRONTEND_IMPLEMENTER_AGENT_ID;
6
- export const NOLO_FRONTEND_AGENT_KEY = FRONTEND_IMPLEMENTER_AGENT_KEY;
7
2
  export const NOLO_PROJECT_MANAGER_AGENT_ID = "01NOLOPROJMGR00000000MSVGG";
8
3
  export const NOLO_PROJECT_MANAGER_AGENT_KEY =
9
4
  `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_PROJECT_MANAGER_AGENT_ID}`;
10
- export const NOLO_FULLSTACK_AGENT_ID = "01NOLOFULLSTK00000001J740Q";
11
- export const NOLO_FULLSTACK_AGENT_KEY =
12
- `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_FULLSTACK_AGENT_ID}`;
13
5
  export const NOLO_SENIOR_FULLSTACK_AGENT_ID = "01NOLOFULLSTK00000000T3XR4";
14
6
  export const NOLO_SENIOR_FULLSTACK_AGENT_KEY =
15
7
  `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_SENIOR_FULLSTACK_AGENT_ID}`;
16
- export const NOLO_REVIEW_AGENT_ID = "01CODEXCLIMAIN00000001OK";
17
- export const NOLO_REVIEW_AGENT_KEY =
18
- `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_REVIEW_AGENT_ID}`;
8
+ export const NOLO_FRONTEND_AGENT_ID = "01FRONTENDAG0000000115N4E1";
9
+ export const NOLO_FRONTEND_AGENT_KEY =
10
+ `agent-${PLATFORM_DEMO_USER_ID}-${NOLO_FRONTEND_AGENT_ID}`;
11
+ export const WIN_CODEX_AGENT_ID = "WINCODEX00000000FQ0LK0";
12
+ export const WIN_CODEX_AGENT_KEY =
13
+ `agent-0e95801d90-${WIN_CODEX_AGENT_ID}`;
14
+ export const WIN_QWEN_AGENT_ID = "01CUSTOMCODEA00000001JEAG3";
15
+ export const WIN_QWEN_AGENT_KEY =
16
+ `agent-0e95801d90-${WIN_QWEN_AGENT_ID}`;
17
+ export const MIMO_MONTH_AGENT_ID = "01MIMO25MONTH0000000NEW001";
18
+ export const MIMO_MONTH_AGENT_KEY =
19
+ `agent-0e95801d90-${MIMO_MONTH_AGENT_ID}`;
19
20
 
20
21
  const AGENT_ALIAS_TO_KEY: Record<string, string> = {
21
- "nolo-frontend": FRONTEND_IMPLEMENTER_AGENT_KEY,
22
- "frontend-implementer": FRONTEND_IMPLEMENTER_AGENT_KEY,
23
- "frontend-agent": FRONTEND_IMPLEMENTER_AGENT_KEY,
24
- frontend: FRONTEND_IMPLEMENTER_AGENT_KEY,
25
- "front-end": FRONTEND_IMPLEMENTER_AGENT_KEY,
26
- "前端": FRONTEND_IMPLEMENTER_AGENT_KEY,
27
- "前端实现员": FRONTEND_IMPLEMENTER_AGENT_KEY,
22
+ // Win Codex (高智力终审与架构把关)
23
+ "win-codex": WIN_CODEX_AGENT_KEY,
24
+ "wincodex": WIN_CODEX_AGENT_KEY,
25
+ "win codex": WIN_CODEX_AGENT_KEY,
26
+ "nolo-reviewer": WIN_CODEX_AGENT_KEY,
27
+ reviewer: WIN_CODEX_AGENT_KEY,
28
+ "nolo-code-review": WIN_CODEX_AGENT_KEY,
29
+ "code-review": WIN_CODEX_AGENT_KEY,
30
+ review: WIN_CODEX_AGENT_KEY,
31
+ "代码审查": WIN_CODEX_AGENT_KEY,
32
+ "nolo 代码审查": WIN_CODEX_AGENT_KEY,
33
+
34
+ // 包月 Mimo (全栈业务代码与常规杂活物理实体)
35
+ "包月mimo": MIMO_MONTH_AGENT_KEY,
36
+ "包月mimo2.5": MIMO_MONTH_AGENT_KEY,
37
+ "mimo-month": MIMO_MONTH_AGENT_KEY,
38
+ "nolo-fullstack": MIMO_MONTH_AGENT_KEY,
39
+ fullstack: MIMO_MONTH_AGENT_KEY,
40
+ "full-stack": MIMO_MONTH_AGENT_KEY,
41
+ "全栈": MIMO_MONTH_AGENT_KEY,
42
+ "nolo 全栈工程师": MIMO_MONTH_AGENT_KEY,
43
+
44
+ // 产品前端实现
45
+ "frontend": NOLO_FRONTEND_AGENT_KEY,
46
+ "frontend-agent": NOLO_FRONTEND_AGENT_KEY,
47
+ "frontend-implementer": NOLO_FRONTEND_AGENT_KEY,
48
+ "nolo-frontend": NOLO_FRONTEND_AGENT_KEY,
49
+ "nolo frontend": NOLO_FRONTEND_AGENT_KEY,
50
+ "前端agent": NOLO_FRONTEND_AGENT_KEY,
51
+ "前端 agent": NOLO_FRONTEND_AGENT_KEY,
52
+ "前端": NOLO_FRONTEND_AGENT_KEY,
53
+
54
+ // Win Qwen (本地轻量无Token消耗辅助)
55
+ "win-qwen": WIN_QWEN_AGENT_KEY,
56
+ "win qwen": WIN_QWEN_AGENT_KEY,
57
+ "winqwen": WIN_QWEN_AGENT_KEY,
58
+
59
+ // 其他辅助/特定角色
28
60
  "nolo-project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
29
61
  "project-manager": NOLO_PROJECT_MANAGER_AGENT_KEY,
30
62
  "nolo-pm": NOLO_PROJECT_MANAGER_AGENT_KEY,
31
63
  pm: NOLO_PROJECT_MANAGER_AGENT_KEY,
32
64
  "项目经理": NOLO_PROJECT_MANAGER_AGENT_KEY,
33
65
  "nolo 项目经理": NOLO_PROJECT_MANAGER_AGENT_KEY,
34
- "nolo-fullstack": NOLO_FULLSTACK_AGENT_KEY,
35
- fullstack: NOLO_FULLSTACK_AGENT_KEY,
36
- "full-stack": NOLO_FULLSTACK_AGENT_KEY,
37
- "全栈": NOLO_FULLSTACK_AGENT_KEY,
38
- "nolo 全栈工程师": NOLO_FULLSTACK_AGENT_KEY,
39
66
  "senior-fullstack": NOLO_SENIOR_FULLSTACK_AGENT_KEY,
40
67
  "nolo-senior-fullstack": NOLO_SENIOR_FULLSTACK_AGENT_KEY,
41
68
  "高级全栈": NOLO_SENIOR_FULLSTACK_AGENT_KEY,
42
69
  "nolo 高级全栈工程师": NOLO_SENIOR_FULLSTACK_AGENT_KEY,
43
- "nolo-reviewer": NOLO_REVIEW_AGENT_KEY,
44
- reviewer: NOLO_REVIEW_AGENT_KEY,
45
- "nolo-code-review": NOLO_REVIEW_AGENT_KEY,
46
- "code-review": NOLO_REVIEW_AGENT_KEY,
47
- review: NOLO_REVIEW_AGENT_KEY,
48
- "代码审查": NOLO_REVIEW_AGENT_KEY,
49
- "nolo 代码审查": NOLO_REVIEW_AGENT_KEY,
50
70
  };
51
71
 
52
72
  function parseAgentKeyFromInput(raw: string): string {
@@ -400,7 +400,7 @@ function buildTaskWorktreeEnv(args: {
400
400
  function writeUsage(output: OutputLike) {
401
401
  output.write(
402
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> [--image <url-or-path>] [--space <spaceId>] [--category <name>] [--inherit-from-dialog <dialog>] [--task-row-dbkey <key>] [--bg] [--timeout-ms <n>] [--no-stream]\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"
404
404
  );
405
405
  }
406
406
 
@@ -16,6 +16,9 @@ export {
16
16
  parsePlatformChatCompletionData,
17
17
  parsePlatformChatCompletionResponse,
18
18
  resolveAgentRuntimeConfigFromRecord,
19
+ resolveCurrentRunRuntimeToolPolicy,
20
+ resolveLocalRuntimeEnvFromPolicy,
21
+ resolveLocalWorkspaceExecutorOptionsFromPolicy,
19
22
  resolvePlatformChatProviderConfig,
20
23
  resolveLocalToolPolicy,
21
24
  resolveOpenAiCompatibleProviderConfig,
@@ -1,22 +1,20 @@
1
- import { existsSync } from "node:fs";
2
1
  import { join } from "node:path";
3
2
  import { runLocalAgentTurn } from "../agentRuntimeLocal";
4
3
  import { LOCAL_AGENT_CONFIG_MISSING_CODE } from "../../agent-runtime/localLoop";
5
4
  import {
6
- FRONTEND_IMPLEMENTER_AGENT_KEY,
7
- NOLO_FULLSTACK_AGENT_KEY,
5
+ MIMO_MONTH_AGENT_KEY,
8
6
  NOLO_PROJECT_MANAGER_AGENT_KEY,
9
- NOLO_REVIEW_AGENT_KEY,
10
7
  NOLO_SENIOR_FULLSTACK_AGENT_KEY,
8
+ WIN_CODEX_AGENT_KEY,
11
9
  } from "../agentAliases";
12
10
  import type { LocalAgentToolEvent } from "../../agent-runtime/localLoop";
13
11
  import type { AgentRuntimeHostAdapter, AgentRuntimeRequestedMode } from "../agentRuntimeLocal";
14
12
  import { createCliLocalRuntimeAdapter } from "./localRuntimeAdapter";
15
13
  import { createStreamingTextWriter } from "./streamingOutput";
16
14
  import { prependTaskRunPrompt, type TaskRunPromptContext } from "./taskRunPrompt";
17
-
18
- type EnvLike = Record<string, string | undefined>;
19
-
15
+
16
+ type EnvLike = Record<string, string | undefined>;
17
+
20
18
  type OutputLike = {
21
19
  write(chunk: string): unknown;
22
20
  };
@@ -48,7 +46,7 @@ type RunAgentTurnOptions = {
48
46
  noStream?: boolean;
49
47
  noDefaultTestRoot?: boolean;
50
48
  scriptDir: string;
51
- env: EnvLike;
49
+ env: EnvLike;
52
50
  output: OutputLike;
53
51
  runtimeMode?: AgentRuntimeRequestedMode;
54
52
  localRuntimeAdapter?: AgentRuntimeHostAdapter;
@@ -66,20 +64,55 @@ type RunAgentTurnOptions = {
66
64
  errorCode?: string;
67
65
  toolEvidence?: TaskRunToolEvidence;
68
66
  }) => Promise<void>;
69
- scriptPathExists?: (path: string) => boolean;
70
67
  fetchImpl?: typeof fetch;
71
68
  };
72
-
69
+
73
70
  export type RunAgentTurnResult = {
74
71
  exitCode: number;
75
72
  dialogId?: string;
76
73
  streamInterrupted?: boolean;
77
74
  };
78
-
79
- type ScriptBridgeDecision = {
80
- hasAuthToken: boolean;
81
- scriptPathExists: boolean;
82
- };
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
+
83
116
 
84
117
  const SERVER_PLATFORM_TOOL_NAMES = new Set([
85
118
  "addTableRow",
@@ -94,10 +127,9 @@ const SERVER_PLATFORM_TOOL_NAMES = new Set([
94
127
  ]);
95
128
 
96
129
  const KNOWN_SERVER_PLATFORM_AGENT_KEYS = new Set([
97
- FRONTEND_IMPLEMENTER_AGENT_KEY,
98
- NOLO_FULLSTACK_AGENT_KEY,
130
+ MIMO_MONTH_AGENT_KEY,
99
131
  NOLO_PROJECT_MANAGER_AGENT_KEY,
100
- NOLO_REVIEW_AGENT_KEY,
132
+ WIN_CODEX_AGENT_KEY,
101
133
  NOLO_SENIOR_FULLSTACK_AGENT_KEY,
102
134
  ]);
103
135
 
@@ -125,10 +157,6 @@ const KNOWN_SERVER_PLATFORM_AGENT_ALIASES = new Set([
125
157
  "reviewer",
126
158
  ]);
127
159
 
128
- export function shouldUseScriptBridge(decision: ScriptBridgeDecision) {
129
- return !decision.hasAuthToken && decision.scriptPathExists;
130
- }
131
-
132
160
  export function findServerPlatformTools(toolNames?: string[]) {
133
161
  if (!Array.isArray(toolNames)) return [];
134
162
  return toolNames.filter((toolName) => SERVER_PLATFORM_TOOL_NAMES.has(toolName));
@@ -514,62 +542,9 @@ function buildTransportErrorHint(serverUrl: string, error: unknown) {
514
542
  return detail;
515
543
  }
516
544
 
517
- async function runScriptBridge(options: RunAgentTurnOptions, scriptPath: string) {
518
- options.output.write(`\n${options.agentName} -> working via local script...\n\n`);
519
- const proc = Bun.spawn({
520
- cmd: [
521
- process.execPath,
522
- scriptPath,
523
- "--agent",
524
- options.agentKey,
525
- "--server",
526
- options.serverUrl,
527
- "--msg",
528
- options.message,
529
- ...(options.taskRunContext?.rowDbKey
530
- ? ["--task-row-dbkey", options.taskRunContext.rowDbKey]
531
- : []),
532
- ...(options.taskRunContext?.taskRunId
533
- ? ["--task-run-id", options.taskRunContext.taskRunId]
534
- : []),
535
- ...(options.taskRunContext?.workItemId
536
- ? ["--work-item-id", options.taskRunContext.workItemId]
537
- : []),
538
- ...(options.taskRunContext?.artifactIds?.length
539
- ? ["--artifact-ids", options.taskRunContext.artifactIds.join(",")]
540
- : []),
541
- ...(options.noDefaultTestRoot ? ["--no-default-test-root"] : []),
542
- ...(options.continueDialogId
543
- ? ["--continue", options.continueDialogId]
544
- : []),
545
- ...(options.spaceId ? ["--space", options.spaceId] : []),
546
- ...(options.category ? ["--category", options.category] : []),
547
- ...(options.inheritedFromDialogKey
548
- ? ["--inherit-from-dialog", options.inheritedFromDialogKey]
549
- : []),
550
- ...(options.background ? ["--bg"] : []),
551
- ...(typeof options.timeoutMs === "number" ? ["--timeout-ms", String(options.timeoutMs)] : []),
552
- ...(options.noStream ? ["--no-stream"] : []),
553
- ],
554
- stdin: "inherit",
555
- stdout: "inherit",
556
- stderr: "inherit",
557
- env: {
558
- ...process.env,
559
- ...options.env,
560
- ASSISTANT_LABEL: options.agentName,
561
- },
562
- });
563
-
564
- const exitCode = await proc.exited;
565
- if (exitCode !== 0) {
566
- options.output.write(`\n[nolo] Agent run exited with code ${exitCode}.\n`);
567
- }
568
- return { exitCode };
569
- }
570
-
571
545
  async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string) {
572
- options.output.write(`\n${options.agentName} -> working...\n`);
546
+ const spinner = new Spinner(options.output, `${options.agentName} -> working`);
547
+ spinner.start();
573
548
 
574
549
  const fetchImpl = options.fetchImpl ?? fetch;
575
550
  let res: Response;
@@ -623,14 +598,16 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
623
598
  ...(typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {}),
624
599
  stream: !options.noStream && !options.background,
625
600
  }),
626
- });
627
- } catch (error) {
628
- options.output.write(buildTransportErrorHint(options.serverUrl, error));
629
- return { exitCode: 1 };
630
- }
631
-
601
+ });
602
+ } catch (error) {
603
+ spinner.stop();
604
+ options.output.write(buildTransportErrorHint(options.serverUrl, error));
605
+ return { exitCode: 1 };
606
+ }
607
+
632
608
  const contentType = res.headers.get("content-type") || "";
633
609
  if (contentType.includes("text/event-stream") && res.body) {
610
+ spinner.stop();
634
611
  const result = await readStreamingAgentRun(options, res);
635
612
  await recordTaskRunOutcome({
636
613
  options,
@@ -644,14 +621,15 @@ async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string)
644
621
  });
645
622
  return result;
646
623
  }
647
-
648
- let data: any = {};
649
- try {
650
- data = await res.json();
651
- } catch {
652
- data = {};
653
- }
654
-
624
+
625
+ spinner.stop();
626
+ let data: any = {};
627
+ try {
628
+ data = await res.json();
629
+ } catch {
630
+ data = {};
631
+ }
632
+
655
633
  if (!res.ok) {
656
634
  options.output.write(`[nolo] Agent request failed: HTTP ${res.status}\n`);
657
635
  if (data?.error || data?.message) {
@@ -758,7 +736,8 @@ async function runLocalAgentTurnForCli(
758
736
  return { exitCode: 1 };
759
737
  }
760
738
 
761
- options.output.write(`\n${options.agentName} -> working locally...\n`);
739
+ const spinner = new Spinner(options.output, `${options.agentName} -> working locally`);
740
+ spinner.start();
762
741
  const toolEvents: LocalAgentToolEvent[] = [];
763
742
  try {
764
743
  const traceLocalTools = shouldTraceLocalTools(options);
@@ -783,11 +762,15 @@ async function runLocalAgentTurnForCli(
783
762
  ? {
784
763
  onToolEvent: (event) => {
785
764
  if (collectToolEvidence) toolEvents.push(event);
786
- if (traceLocalTools) options.output.write(formatToolTraceEvent(event));
765
+ if (traceLocalTools) {
766
+ spinner.stop();
767
+ options.output.write(formatToolTraceEvent(event));
768
+ }
787
769
  },
788
770
  }
789
771
  : {}),
790
772
  });
773
+ spinner.stop();
791
774
  const content = result.content.trim();
792
775
  if (content) {
793
776
  options.output.write(`\n${options.agentName} > ${content}\n`);
@@ -803,6 +786,7 @@ async function runLocalAgentTurnForCli(
803
786
  });
804
787
  return { exitCode: 0, dialogId: result.dialogId };
805
788
  } catch (error) {
789
+ spinner.stop();
806
790
  if (settings.reportFailure) {
807
791
  options.output.write(
808
792
  `[nolo] Local agent run failed: ${
@@ -927,8 +911,6 @@ async function readStreamingAgentRun(
927
911
 
928
912
  export async function runAgentTurn(options: RunAgentTurnOptions) {
929
913
  const authToken = resolveAuthToken(options.env);
930
- const scriptPath = join(options.scriptDir, "chatWithAgent.ts");
931
- const scriptPathExists = (options.scriptPathExists ?? existsSync)(scriptPath);
932
914
  const runtimeMode = resolveRequestedRuntimeMode(options);
933
915
 
934
916
  if (runtimeMode === "local") {
@@ -969,17 +951,13 @@ export async function runAgentTurn(options: RunAgentTurnOptions) {
969
951
  }
970
952
  }
971
953
 
972
- if (shouldUseScriptBridge({ hasAuthToken: Boolean(authToken), scriptPathExists })) {
973
- return runScriptBridge(options, scriptPath);
954
+ if (!authToken) {
955
+ options.output.write(
956
+ "[nolo] This install needs an auth token before it can talk to agents.\n" +
957
+ "Run `nolo login`, or set AUTH_TOKEN / NOLO_SERVER for non-interactive runs.\n"
958
+ );
959
+ return { exitCode: 1 };
974
960
  }
975
961
 
976
- if (!authToken) {
977
- options.output.write(
978
- "[nolo] This install needs an auth token before it can talk to agents.\n" +
979
- "Set AUTH_TOKEN, or use the repo-local CLI where the dev script bridge is available.\n"
980
- );
981
- return { exitCode: 1 };
982
- }
983
-
984
962
  return runHttpAgentTurn(options, authToken);
985
963
  }
@@ -16,6 +16,8 @@ import {
16
16
  parsePlatformChatCompletionData,
17
17
  parsePlatformChatCompletionResponse,
18
18
  resolvePlatformChatProviderConfig,
19
+ resolveCurrentRunRuntimeToolPolicy,
20
+ resolveLocalWorkspaceExecutorOptionsFromPolicy,
19
21
  resolveRequestedRuntimeToolNames,
20
22
  shouldUsePlatformChatProvider,
21
23
  } from "../agentRuntimeLocal";
@@ -337,9 +339,15 @@ function buildLocalToolExecutors(args: {
337
339
  env: EnvLike;
338
340
  fetchImpl: typeof fetch;
339
341
  localToolExecutors?: CliLocalRuntimeAdapterDeps["localToolExecutors"];
342
+ commandTimeoutMs?: number;
343
+ commandOutputLimit?: number;
340
344
  }) {
341
345
  return {
342
- ...createLocalWorkspaceToolExecutors({ workspaceRoot: args.workspaceRoot }),
346
+ ...createLocalWorkspaceToolExecutors({
347
+ workspaceRoot: args.workspaceRoot,
348
+ commandTimeoutMs: args.commandTimeoutMs,
349
+ commandOutputLimit: args.commandOutputLimit,
350
+ }),
343
351
  ...buildServerPlatformToolExecutors({
344
352
  env: args.env,
345
353
  fetchImpl: args.fetchImpl,
@@ -434,11 +442,13 @@ export function createCliLocalRuntimeAdapter(
434
442
  ? { defaultCwd: deps.cwd }
435
443
  : { cwd: deps.cwd }
436
444
  );
445
+ let runtimeToolExecutionLimits: ReturnType<typeof resolveLocalWorkspaceExecutorOptionsFromPolicy> = {};
437
446
  let localToolExecutors = buildLocalToolExecutors({
438
447
  workspaceRoot: workspaceSession.workspaceRoot,
439
448
  env: deps.env,
440
449
  fetchImpl,
441
450
  localToolExecutors: deps.localToolExecutors,
451
+ ...runtimeToolExecutionLimits,
442
452
  });
443
453
 
444
454
  async function activateAgentWorkspaceSession(args: {
@@ -458,6 +468,7 @@ export function createCliLocalRuntimeAdapter(
458
468
  env: deps.env,
459
469
  fetchImpl,
460
470
  localToolExecutors: deps.localToolExecutors,
471
+ ...runtimeToolExecutionLimits,
461
472
  });
462
473
  deps.output?.write(formatWorkspaceSessionActivation(workspaceSession));
463
474
  }
@@ -478,7 +489,17 @@ export function createCliLocalRuntimeAdapter(
478
489
  toolNames: requestedToolNames,
479
490
  env: deps.env,
480
491
  });
492
+ runtimeToolExecutionLimits = resolveLocalWorkspaceExecutorOptionsFromPolicy(
493
+ resolveCurrentRunRuntimeToolPolicy(agentConfig),
494
+ );
481
495
  await activateAgentWorkspaceSession({ agentRef, agentConfig });
496
+ localToolExecutors = buildLocalToolExecutors({
497
+ workspaceRoot: workspaceSession.workspaceRoot,
498
+ env: deps.env,
499
+ fetchImpl,
500
+ localToolExecutors: deps.localToolExecutors,
501
+ ...runtimeToolExecutionLimits,
502
+ });
482
503
  return agentConfig;
483
504
  },
484
505
  loadDialogHistory: async (dialogId) => readDialogMessages({
@@ -1,4 +1,5 @@
1
1
  import { runLocalAgentTurn, type LocalAgentToolEvent } from "../agent-runtime/localLoop";
2
+ import { resolveLocalRuntimeEnvFromPolicy } from "../agent-runtime/runtimeToolPolicy";
2
3
  import {
3
4
  assertMachineRunAllowed as defaultAssertMachineRunAllowed,
4
5
  resolveMachineRunPermissionPolicy as defaultResolveMachineRunPermissionPolicy,
@@ -225,11 +226,8 @@ function mergeToolNames(...values: unknown[]) {
225
226
  return names;
226
227
  }
227
228
 
228
- function localRuntimeEnvFromPolicy(runtimeEnv: EnvLike, policy?: ConnectorRuntimePolicy): EnvLike {
229
- return {
230
- ...runtimeEnv,
231
- ...(policy?.workspace?.cwd ? { NOLO_LOCAL_WORKTREE: policy.workspace.cwd } : {}),
232
- };
229
+ export function localRuntimeEnvFromPolicy(runtimeEnv: EnvLike, policy?: ConnectorRuntimePolicy): EnvLike {
230
+ return resolveLocalRuntimeEnvFromPolicy(runtimeEnv, policy);
233
231
  }
234
232
 
235
233
  function forwardedUserAuthToken(parsed: any): string {