nolo-cli 0.1.35 → 0.1.37

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.
@@ -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";
@@ -221,6 +223,29 @@ function buildRequestTimeoutSignal(timeoutMs?: number) {
221
223
  return controller.signal;
222
224
  }
223
225
 
226
+ function summarizeEndpoint(value: unknown) {
227
+ if (typeof value !== "string" || !value.trim()) return undefined;
228
+ try {
229
+ const url = new URL(value);
230
+ return `${url.protocol}//${url.host}${url.pathname}`;
231
+ } catch {
232
+ return "invalid-url";
233
+ }
234
+ }
235
+
236
+ function parseJsonObject(raw: string) {
237
+ try {
238
+ const parsed = JSON.parse(raw);
239
+ return parsed && typeof parsed === "object" ? parsed : {};
240
+ } catch {
241
+ return {};
242
+ }
243
+ }
244
+
245
+ function logLocalRuntimeDiagnostic(event: string, fields: Record<string, unknown>) {
246
+ console.error(`[nolo-local-runtime] ${event} ${JSON.stringify(fields)}`);
247
+ }
248
+
224
249
  function buildOpenAiTools(args: { toolNames?: string[]; env: EnvLike }) {
225
250
  const toolset = buildLocalWorkspaceToolsetForEnv(args);
226
251
  return [
@@ -314,9 +339,15 @@ function buildLocalToolExecutors(args: {
314
339
  env: EnvLike;
315
340
  fetchImpl: typeof fetch;
316
341
  localToolExecutors?: CliLocalRuntimeAdapterDeps["localToolExecutors"];
342
+ commandTimeoutMs?: number;
343
+ commandOutputLimit?: number;
317
344
  }) {
318
345
  return {
319
- ...createLocalWorkspaceToolExecutors({ workspaceRoot: args.workspaceRoot }),
346
+ ...createLocalWorkspaceToolExecutors({
347
+ workspaceRoot: args.workspaceRoot,
348
+ commandTimeoutMs: args.commandTimeoutMs,
349
+ commandOutputLimit: args.commandOutputLimit,
350
+ }),
320
351
  ...buildServerPlatformToolExecutors({
321
352
  env: args.env,
322
353
  fetchImpl: args.fetchImpl,
@@ -411,11 +442,13 @@ export function createCliLocalRuntimeAdapter(
411
442
  ? { defaultCwd: deps.cwd }
412
443
  : { cwd: deps.cwd }
413
444
  );
445
+ let runtimeToolExecutionLimits: ReturnType<typeof resolveLocalWorkspaceExecutorOptionsFromPolicy> = {};
414
446
  let localToolExecutors = buildLocalToolExecutors({
415
447
  workspaceRoot: workspaceSession.workspaceRoot,
416
448
  env: deps.env,
417
449
  fetchImpl,
418
450
  localToolExecutors: deps.localToolExecutors,
451
+ ...runtimeToolExecutionLimits,
419
452
  });
420
453
 
421
454
  async function activateAgentWorkspaceSession(args: {
@@ -435,6 +468,7 @@ export function createCliLocalRuntimeAdapter(
435
468
  env: deps.env,
436
469
  fetchImpl,
437
470
  localToolExecutors: deps.localToolExecutors,
471
+ ...runtimeToolExecutionLimits,
438
472
  });
439
473
  deps.output?.write(formatWorkspaceSessionActivation(workspaceSession));
440
474
  }
@@ -455,7 +489,17 @@ export function createCliLocalRuntimeAdapter(
455
489
  toolNames: requestedToolNames,
456
490
  env: deps.env,
457
491
  });
492
+ runtimeToolExecutionLimits = resolveLocalWorkspaceExecutorOptionsFromPolicy(
493
+ resolveCurrentRunRuntimeToolPolicy(agentConfig),
494
+ );
458
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
+ });
459
503
  return agentConfig;
460
504
  },
461
505
  loadDialogHistory: async (dialogId) => readDialogMessages({
@@ -476,6 +520,20 @@ export function createCliLocalRuntimeAdapter(
476
520
  agentConfig,
477
521
  env: deps.env,
478
522
  });
523
+ logLocalRuntimeDiagnostic("provider.selected", {
524
+ agentKey: agentConfig.key,
525
+ transport: "platform-proxy",
526
+ apiSource: agentConfig.apiSource ?? null,
527
+ provider: providerConfig.provider,
528
+ model: providerConfig.model,
529
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
530
+ proxyServer: summarizeEndpoint(providerConfig.serverUrl) ?? null,
531
+ hasAuthToken: Boolean(providerConfig.authToken),
532
+ hasApiKey: Boolean(providerConfig.apiKey),
533
+ apiKeyHeader: providerConfig.apiKeyHeader ?? null,
534
+ useServerProxy: agentConfig.useServerProxy ?? null,
535
+ customProviderEndpoint: summarizeEndpoint(agentConfig.customProviderUrl) ?? null,
536
+ });
479
537
  return {
480
538
  model: providerConfig.model,
481
539
  complete: async (messages, options) => {
@@ -492,6 +550,16 @@ export function createCliLocalRuntimeAdapter(
492
550
  tools,
493
551
  });
494
552
  const signal = buildRequestTimeoutSignal(options?.timeoutMs);
553
+ logLocalRuntimeDiagnostic("provider.request.start", {
554
+ agentKey: agentConfig.key,
555
+ transport: "platform-proxy",
556
+ requestUrl: summarizeEndpoint(request.url) ?? null,
557
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
558
+ model: providerConfig.model,
559
+ messageCount: messages.length,
560
+ toolCount: tools.length,
561
+ timeoutMs: options?.timeoutMs ?? null,
562
+ });
495
563
  const res = await fetchWithTransientRetry(fetchImpl, request.url, {
496
564
  ...request.init,
497
565
  ...(signal ? { signal } : {}),
@@ -499,7 +567,15 @@ export function createCliLocalRuntimeAdapter(
499
567
  sleep: deps.sleep,
500
568
  loopbackRequest,
501
569
  });
502
- const data = parsePlatformChatCompletionData(await res.text().catch(() => ""));
570
+ const raw = await res.text().catch(() => "");
571
+ logLocalRuntimeDiagnostic("provider.request.result", {
572
+ agentKey: agentConfig.key,
573
+ transport: "platform-proxy",
574
+ status: res.status,
575
+ ok: res.ok,
576
+ responseBytes: raw.length,
577
+ });
578
+ const data = parsePlatformChatCompletionData(raw);
503
579
  if (!res.ok) {
504
580
  throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
505
581
  }
@@ -516,6 +592,18 @@ export function createCliLocalRuntimeAdapter(
516
592
  agentConfig,
517
593
  env: deps.env,
518
594
  });
595
+ logLocalRuntimeDiagnostic("provider.selected", {
596
+ agentKey: agentConfig.key,
597
+ transport: "direct-openai-compatible",
598
+ apiSource: agentConfig.apiSource ?? null,
599
+ provider: providerConfig.provider,
600
+ model: providerConfig.model,
601
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
602
+ hasApiKey: Boolean(providerConfig.apiKey),
603
+ apiKeyHeader: providerConfig.apiKeyHeader ?? null,
604
+ useServerProxy: agentConfig.useServerProxy ?? null,
605
+ customProviderEndpoint: summarizeEndpoint(agentConfig.customProviderUrl) ?? null,
606
+ });
519
607
  return {
520
608
  model: providerConfig.model,
521
609
  complete: async (messages, options) => {
@@ -532,6 +620,15 @@ export function createCliLocalRuntimeAdapter(
532
620
  tools,
533
621
  });
534
622
  const signal = buildRequestTimeoutSignal(options?.timeoutMs);
623
+ logLocalRuntimeDiagnostic("provider.request.start", {
624
+ agentKey: agentConfig.key,
625
+ transport: "direct-openai-compatible",
626
+ requestUrl: summarizeEndpoint(request.url) ?? null,
627
+ model: providerConfig.model,
628
+ messageCount: messages.length,
629
+ toolCount: tools.length,
630
+ timeoutMs: options?.timeoutMs ?? null,
631
+ });
535
632
  const res = await fetchWithTransientRetry(fetchImpl, request.url, {
536
633
  ...request.init,
537
634
  ...(signal ? { signal } : {}),
@@ -539,7 +636,15 @@ export function createCliLocalRuntimeAdapter(
539
636
  sleep: deps.sleep,
540
637
  loopbackRequest,
541
638
  });
542
- const data = await res.json().catch(() => ({}));
639
+ const raw = await res.text().catch(() => "");
640
+ logLocalRuntimeDiagnostic("provider.request.result", {
641
+ agentKey: agentConfig.key,
642
+ transport: "direct-openai-compatible",
643
+ status: res.status,
644
+ ok: res.ok,
645
+ responseBytes: raw.length,
646
+ });
647
+ const data = parseJsonObject(raw);
543
648
  if (!res.ok) {
544
649
  throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
545
650
  }
@@ -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,
@@ -96,6 +97,37 @@ function logConnectorRun(message: string, fields: Record<string, unknown> = {})
96
97
  console.error(`[nolo-connector] ${message} ${JSON.stringify(fields)}`);
97
98
  }
98
99
 
100
+ function summarizeEndpoint(value: unknown) {
101
+ if (typeof value !== "string" || !value.trim()) return undefined;
102
+ try {
103
+ const url = new URL(value);
104
+ return `${url.protocol}//${url.host}${url.pathname}`;
105
+ } catch {
106
+ return "invalid-url";
107
+ }
108
+ }
109
+
110
+ function summarizeAgentConfigForLogs(agentConfig: any) {
111
+ const runtimeBinding = isRecord(agentConfig?.runtimeBinding) ? agentConfig.runtimeBinding : {};
112
+ return {
113
+ apiSource: agentConfig?.apiSource ?? null,
114
+ provider: agentConfig?.provider ?? null,
115
+ cliProvider: agentConfig?.cliProvider ?? null,
116
+ model: agentConfig?.model ?? null,
117
+ customProviderEndpoint: summarizeEndpoint(agentConfig?.customProviderUrl) ?? null,
118
+ useServerProxy: agentConfig?.useServerProxy ?? null,
119
+ skipLocalRuntimeEnsure: agentConfig?.skipLocalRuntimeEnsure ?? null,
120
+ hasApiKey: typeof agentConfig?.apiKey === "string" && agentConfig.apiKey.length > 0,
121
+ apiKeyHeader: agentConfig?.apiKeyHeader ?? null,
122
+ hasApiKeyFromAgentKey:
123
+ typeof agentConfig?.apiKeyFromAgentKey === "string" &&
124
+ agentConfig.apiKeyFromAgentKey.length > 0,
125
+ runtimeBindingKind: runtimeBinding.kind ?? null,
126
+ runtimeMachineId: runtimeBinding.machineId ?? null,
127
+ connectorSurface: runtimeBinding.connectorSurface ?? runtimeBinding.surface ?? null,
128
+ };
129
+ }
130
+
99
131
  function isRecord(value: unknown): value is Record<string, any> {
100
132
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
101
133
  }
@@ -194,11 +226,8 @@ function mergeToolNames(...values: unknown[]) {
194
226
  return names;
195
227
  }
196
228
 
197
- function localRuntimeEnvFromPolicy(runtimeEnv: EnvLike, policy?: ConnectorRuntimePolicy): EnvLike {
198
- return {
199
- ...runtimeEnv,
200
- ...(policy?.workspace?.cwd ? { NOLO_LOCAL_WORKTREE: policy.workspace.cwd } : {}),
201
- };
229
+ export function localRuntimeEnvFromPolicy(runtimeEnv: EnvLike, policy?: ConnectorRuntimePolicy): EnvLike {
230
+ return resolveLocalRuntimeEnvFromPolicy(runtimeEnv, policy);
202
231
  }
203
232
 
204
233
  function forwardedUserAuthToken(parsed: any): string {
@@ -569,6 +598,15 @@ export async function handleConnectorRunMessage(
569
598
  if (!requestsLocalWorkspaceRuntime(runtimePolicy) && !isMachineBoundLocalCustomProvider(agentConfig)) {
570
599
  throw new Error("Connector can only execute non-CLI agents when runtimeToolPolicySnapshot requests a local workspace runtime.");
571
600
  }
601
+ logConnectorRun("agent.run.local-runtime.start", {
602
+ requestId: parsed.requestId,
603
+ agentKey: parsed.payload?.agentKey,
604
+ cwd,
605
+ timeout,
606
+ machineBoundLocalCustomProvider: isMachineBoundLocalCustomProvider(agentConfig),
607
+ runtimePolicy: Boolean(runtimePolicy),
608
+ ...summarizeAgentConfigForLogs(agentConfig),
609
+ });
572
610
  baseSha = await readConnectorGitHead(cwd);
573
611
  const result = await runConnectorLocalRuntimeAgent({
574
612
  parsed,
@@ -607,6 +645,8 @@ export async function handleConnectorRunMessage(
607
645
  logConnectorRun("agent.run.error", {
608
646
  requestId: parsed.requestId,
609
647
  agentKey: parsed.payload?.agentKey,
648
+ ...summarizeAgentConfigForLogs(agentConfig),
649
+ errorName: error instanceof Error ? error.name : null,
610
650
  error: error instanceof Error ? error.message : String(error),
611
651
  });
612
652
  send(JSON.stringify({
@@ -5,7 +5,6 @@ import {
5
5
  classifyTaskRunReviewStatus,
6
6
  findServerPlatformTools,
7
7
  runAgentTurn,
8
- shouldUseScriptBridge,
9
8
  } from "./agentRun";
10
9
  import { NOLO_PROJECT_MANAGER_AGENT_KEY } from "../agentAliases";
11
10
 
@@ -34,12 +33,6 @@ describe("cli agent run client", () => {
34
33
  expect(classifyTaskRunReviewStatus("I looked at the diff and have notes")).toBeUndefined();
35
34
  });
36
35
 
37
- test("uses the repo script bridge only when no auth token is available", () => {
38
- expect(shouldUseScriptBridge({ hasAuthToken: false, scriptPathExists: true })).toBe(true);
39
- expect(shouldUseScriptBridge({ hasAuthToken: true, scriptPathExists: true })).toBe(false);
40
- expect(shouldUseScriptBridge({ hasAuthToken: false, scriptPathExists: false })).toBe(false);
41
- });
42
-
43
36
  test("identifies server platform tools that local runtime cannot provide", () => {
44
37
  expect(findServerPlatformTools(["readWorkspaceFile", "queryTableRows", "updateTableRow"])).toEqual([
45
38
  "queryTableRows",
@@ -61,7 +54,6 @@ describe("cli agent run client", () => {
61
54
  scriptDir: "C:/missing/scripts",
62
55
  env: { AUTH_TOKEN: "token-123" },
63
56
  output,
64
- scriptPathExists: () => false,
65
57
  fetchImpl: async (url, init) => {
66
58
  requests.push({
67
59
  url: String(url),
@@ -120,7 +112,6 @@ describe("cli agent run client", () => {
120
112
  workItemId: "frontend-filter",
121
113
  artifactIds: ["artifact-1"],
122
114
  },
123
- scriptPathExists: () => false,
124
115
  fetchImpl: async (_url, init) => {
125
116
  requests.push({ body: JSON.parse(String(init?.body)) });
126
117
  return Response.json({ content: "ok", dialogId: "dialog-1" });
@@ -160,7 +151,6 @@ describe("cli agent run client", () => {
160
151
  taskRunRecorder: async (args) => {
161
152
  writebacks.push(args);
162
153
  },
163
- scriptPathExists: () => false,
164
154
  fetchImpl: async () => {
165
155
  return Response.json({ content: "patched", dialogId: "dialog-1" });
166
156
  },
@@ -193,7 +183,6 @@ describe("cli agent run client", () => {
193
183
  env: { AUTH_TOKEN: "token-123" },
194
184
  output,
195
185
  runtimeMode: "server",
196
- scriptPathExists: () => false,
197
186
  fetchImpl: async (_url, init) => {
198
187
  requests.push({ body: JSON.parse(String(init?.body)) });
199
188
  return Response.json({ content: "ok" });
@@ -206,7 +195,7 @@ describe("cli agent run client", () => {
206
195
  ]);
207
196
  });
208
197
 
209
- test("sends script-compatible dialog metadata over HTTP", async () => {
198
+ test("sends CLI dialog metadata over HTTP", async () => {
210
199
  const output = new CaptureOutput();
211
200
  const requests: Array<{ body: any }> = [];
212
201
 
@@ -227,7 +216,6 @@ describe("cli agent run client", () => {
227
216
  env: { AUTH_TOKEN: "token-123" },
228
217
  output,
229
218
  runtimeMode: "server",
230
- scriptPathExists: () => false,
231
219
  fetchImpl: async (_url, init) => {
232
220
  requests.push({ body: JSON.parse(String(init?.body)) });
233
221
  return Response.json({ dialogId: "dialog-1", status: "queued" });
@@ -271,7 +259,6 @@ describe("cli agent run client", () => {
271
259
  writebacks.push(args);
272
260
  },
273
261
  output,
274
- scriptPathExists: () => false,
275
262
  fetchImpl: async () => {
276
263
  return Response.json({ dialogId: "dialog-bg", status: "pending" }, { status: 202 });
277
264
  },
@@ -319,7 +306,6 @@ describe("cli agent run client", () => {
319
306
  throw new Error("no tools expected");
320
307
  },
321
308
  },
322
- scriptPathExists: () => false,
323
309
  fetchImpl: async () => {
324
310
  throw new Error("HTTP should not be called for forced local runs");
325
311
  },
@@ -371,7 +357,6 @@ describe("cli agent run client", () => {
371
357
  }),
372
358
  executeTool: async () => ({ content: "ok" }),
373
359
  },
374
- scriptPathExists: () => false,
375
360
  fetchImpl: async () => {
376
361
  throw new Error("HTTP should not be called for forced local runs");
377
362
  },
@@ -432,7 +417,6 @@ describe("cli agent run client", () => {
432
417
  return { content: "ok" };
433
418
  },
434
419
  },
435
- scriptPathExists: () => false,
436
420
  });
437
421
 
438
422
  expect(result).toEqual({ exitCode: 0, dialogId: "dialog-local-retry" });
@@ -487,7 +471,6 @@ describe("cli agent run client", () => {
487
471
  }),
488
472
  executeTool: async () => ({ content: "clean" }),
489
473
  },
490
- scriptPathExists: () => false,
491
474
  });
492
475
 
493
476
  expect(result).toMatchObject({ exitCode: 0, dialogId: "dialog-local" });
@@ -548,7 +531,6 @@ describe("cli agent run client", () => {
548
531
  }),
549
532
  executeTool: async () => ({ content: "clean" }),
550
533
  },
551
- scriptPathExists: () => false,
552
534
  });
553
535
 
554
536
  expect(result).toMatchObject({ exitCode: 0, dialogId: "dialog-local" });
@@ -604,7 +586,6 @@ describe("cli agent run client", () => {
604
586
  throw new Error("no tools expected");
605
587
  },
606
588
  },
607
- scriptPathExists: () => false,
608
589
  });
609
590
 
610
591
  expect(result).toEqual({ exitCode: 0, dialogId: "dialog-local-bg" });
@@ -648,7 +629,6 @@ describe("cli agent run client", () => {
648
629
  throw new Error("no tools expected");
649
630
  },
650
631
  },
651
- scriptPathExists: () => false,
652
632
  fetchImpl: async (url) => {
653
633
  httpCalls.push(String(url));
654
634
  return Response.json({ content: "server" });
@@ -701,7 +681,6 @@ describe("cli agent run client", () => {
701
681
  throw new Error("no tools expected");
702
682
  },
703
683
  },
704
- scriptPathExists: () => false,
705
684
  fetchImpl: async (url) => {
706
685
  httpCalls.push(String(url));
707
686
  return Response.json({ content: "server" });
@@ -748,7 +727,6 @@ describe("cli agent run client", () => {
748
727
  throw new Error("no tools expected");
749
728
  },
750
729
  },
751
- scriptPathExists: () => false,
752
730
  fetchImpl: async (url, init) => {
753
731
  httpCalls.push({ url: String(url), body: JSON.parse(String(init?.body)) });
754
732
  return Response.json({ content: "server fallback", dialogId: "dialog-server-fallback" });
@@ -798,7 +776,6 @@ describe("cli agent run client", () => {
798
776
  throw new Error("local tools should be skipped");
799
777
  },
800
778
  },
801
- scriptPathExists: () => false,
802
779
  fetchImpl: async (url, init) => {
803
780
  httpCalls.push({ url: String(url), body: JSON.parse(String(init?.body)) });
804
781
  return Response.json({ content: "server ok", dialogId: "dialog-server" });
@@ -852,7 +829,6 @@ describe("cli agent run client", () => {
852
829
  throw new Error("local tools should be skipped");
853
830
  },
854
831
  },
855
- scriptPathExists: () => false,
856
832
  fetchImpl: async (url, init) => {
857
833
  httpCalls.push({ url: String(url), body: JSON.parse(String(init?.body)) });
858
834
  return Response.json({ content: "server ok", dialogId: "dialog-server-policy" });
@@ -898,7 +874,6 @@ describe("cli agent run client", () => {
898
874
  throw new Error("local tools should be skipped");
899
875
  },
900
876
  },
901
- scriptPathExists: () => false,
902
877
  fetchImpl: async (url, init) => {
903
878
  httpCalls.push({ url: String(url), body: JSON.parse(String(init?.body)) });
904
879
  return Response.json({ content: "server ok", dialogId: "dialog-server" });
@@ -942,7 +917,6 @@ describe("cli agent run client", () => {
942
917
  },
943
918
  };
944
919
  },
945
- scriptPathExists: () => false,
946
920
  fetchImpl: async () => {
947
921
  throw new Error("HTTP should not be called for env local runs");
948
922
  },
@@ -964,7 +938,6 @@ describe("cli agent run client", () => {
964
938
  scriptDir: "C:/missing/scripts",
965
939
  env: { AUTH_TOKEN: "token-123" },
966
940
  output,
967
- scriptPathExists: () => false,
968
941
  fetchImpl: async () =>
969
942
  new Response(
970
943
  [
@@ -998,7 +971,6 @@ describe("cli agent run client", () => {
998
971
  scriptDir: "C:/missing/scripts",
999
972
  env: { AUTH_TOKEN: "token-123" },
1000
973
  output,
1001
- scriptPathExists: () => false,
1002
974
  fetchImpl: async () => {
1003
975
  let sent = false;
1004
976
  return new Response(
@@ -1054,14 +1026,13 @@ describe("cli agent run client", () => {
1054
1026
  scriptDir: "C:/missing/scripts",
1055
1027
  env: {},
1056
1028
  output,
1057
- scriptPathExists: () => false,
1058
1029
  fetchImpl: async () => {
1059
1030
  throw new Error("fetch should not be called");
1060
1031
  },
1061
1032
  });
1062
1033
 
1063
1034
  expect(result.exitCode).toBe(1);
1064
- expect(output.text()).toContain("Set AUTH_TOKEN");
1035
+ expect(output.text()).toContain("Run `nolo login`");
1065
1036
  });
1066
1037
 
1067
1038
  test("prints a friendly connection hint instead of crashing on transport errors", async () => {
@@ -1075,7 +1046,6 @@ describe("cli agent run client", () => {
1075
1046
  scriptDir: "C:/missing/scripts",
1076
1047
  env: { AUTH_TOKEN: "token-123" },
1077
1048
  output,
1078
- scriptPathExists: () => false,
1079
1049
  fetchImpl: async () => {
1080
1050
  throw new Error("ConnectionRefused");
1081
1051
  },
@@ -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,14 @@ 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
- };
83
75
 
84
76
  const SERVER_PLATFORM_TOOL_NAMES = new Set([
85
77
  "addTableRow",
@@ -94,10 +86,9 @@ const SERVER_PLATFORM_TOOL_NAMES = new Set([
94
86
  ]);
95
87
 
96
88
  const KNOWN_SERVER_PLATFORM_AGENT_KEYS = new Set([
97
- FRONTEND_IMPLEMENTER_AGENT_KEY,
98
- NOLO_FULLSTACK_AGENT_KEY,
89
+ MIMO_MONTH_AGENT_KEY,
99
90
  NOLO_PROJECT_MANAGER_AGENT_KEY,
100
- NOLO_REVIEW_AGENT_KEY,
91
+ WIN_CODEX_AGENT_KEY,
101
92
  NOLO_SENIOR_FULLSTACK_AGENT_KEY,
102
93
  ]);
103
94
 
@@ -125,10 +116,6 @@ const KNOWN_SERVER_PLATFORM_AGENT_ALIASES = new Set([
125
116
  "reviewer",
126
117
  ]);
127
118
 
128
- export function shouldUseScriptBridge(decision: ScriptBridgeDecision) {
129
- return !decision.hasAuthToken && decision.scriptPathExists;
130
- }
131
-
132
119
  export function findServerPlatformTools(toolNames?: string[]) {
133
120
  if (!Array.isArray(toolNames)) return [];
134
121
  return toolNames.filter((toolName) => SERVER_PLATFORM_TOOL_NAMES.has(toolName));
@@ -514,60 +501,6 @@ function buildTransportErrorHint(serverUrl: string, error: unknown) {
514
501
  return detail;
515
502
  }
516
503
 
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
504
  async function runHttpAgentTurn(options: RunAgentTurnOptions, authToken: string) {
572
505
  options.output.write(`\n${options.agentName} -> working...\n`);
573
506
 
@@ -927,8 +860,6 @@ async function readStreamingAgentRun(
927
860
 
928
861
  export async function runAgentTurn(options: RunAgentTurnOptions) {
929
862
  const authToken = resolveAuthToken(options.env);
930
- const scriptPath = join(options.scriptDir, "chatWithAgent.ts");
931
- const scriptPathExists = (options.scriptPathExists ?? existsSync)(scriptPath);
932
863
  const runtimeMode = resolveRequestedRuntimeMode(options);
933
864
 
934
865
  if (runtimeMode === "local") {
@@ -969,17 +900,13 @@ export async function runAgentTurn(options: RunAgentTurnOptions) {
969
900
  }
970
901
  }
971
902
 
972
- if (shouldUseScriptBridge({ hasAuthToken: Boolean(authToken), scriptPathExists })) {
973
- return runScriptBridge(options, scriptPath);
903
+ if (!authToken) {
904
+ options.output.write(
905
+ "[nolo] This install needs an auth token before it can talk to agents.\n" +
906
+ "Run `nolo login`, or set AUTH_TOKEN / NOLO_SERVER for non-interactive runs.\n"
907
+ );
908
+ return { exitCode: 1 };
974
909
  }
975
910
 
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
911
  return runHttpAgentTurn(options, authToken);
985
912
  }