nolo-cli 0.1.35 → 0.1.36

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.
@@ -14,6 +14,8 @@ export {
14
14
  buildProviderAuthHeaders,
15
15
  buildProviderExecutionPlan,
16
16
  resolveAgentProviderMode,
17
+ resolveAgentRuntimeLocation,
18
+ resolveProviderTransportDecision,
17
19
  } from "./providerResolution";
18
20
  export {
19
21
  buildPlatformChatCompletionRequest,
@@ -7,9 +7,10 @@ import {
7
7
  buildProviderExecutionPlan,
8
8
  canUsePlatformChatProvider as canUsePlatformChatProviderFromEnv,
9
9
  hasDirectOpenAiCompatibleProvider as hasDirectOpenAiCompatibleProviderFromEnv,
10
+ resolveAgentRuntimeLocation,
10
11
  resolvePlatformAuthToken,
11
12
  resolvePlatformServerUrl,
12
- shouldUsePlatformProxyTransport,
13
+ resolveProviderTransportDecision,
13
14
  } from "./providerResolution";
14
15
 
15
16
  type EnvLike = Record<string, string | undefined>;
@@ -206,16 +207,23 @@ export function hasDirectOpenAiCompatibleProvider(env: EnvLike) {
206
207
  return hasDirectOpenAiCompatibleProviderFromEnv(env);
207
208
  }
208
209
 
209
- function agentRequiresPlatformChatProvider(agentConfig?: AgentRuntimeAgentConfig) {
210
- if (agentConfig?.apiSource === "platform") return true;
211
- if (agentConfig?.apiSource === "custom" || agentConfig?.customProviderUrl?.trim()) return false;
212
- return agentConfig?.useServerProxy === true;
213
- }
214
-
215
210
  export function shouldUsePlatformChatProvider(
216
211
  env: EnvLike,
217
212
  agentConfig?: AgentRuntimeAgentConfig
218
213
  ) {
219
- if (agentRequiresPlatformChatProvider(agentConfig)) return true;
220
- return shouldUsePlatformProxyTransport(env, agentConfig);
214
+ if (!agentConfig) {
215
+ return resolveProviderTransportDecision({
216
+ agentConfig: { key: "default-platform" },
217
+ env,
218
+ runtimeLocation: "local-host",
219
+ }).transport === "proxy";
220
+ }
221
+ return resolveProviderTransportDecision({
222
+ agentConfig,
223
+ env,
224
+ runtimeLocation: resolveAgentRuntimeLocation({
225
+ agentConfig,
226
+ runtimeKind: "local",
227
+ }),
228
+ }).transport === "proxy";
221
229
  }
@@ -16,6 +16,21 @@ const PROVIDER_ENDPOINTS: Record<string, string> = {
16
16
 
17
17
  export type AgentProviderMode = "cli" | "platform" | "custom";
18
18
  export type ProviderExecutionTransport = "direct" | "proxy";
19
+ export type AgentRuntimeLocation = "server" | "bound-machine" | "local-host";
20
+
21
+ export type ProviderTransportDecision = {
22
+ mode: AgentProviderMode;
23
+ transport: ProviderExecutionTransport;
24
+ reason:
25
+ | "cli-provider"
26
+ | "custom-provider"
27
+ | "custom-server-proxy"
28
+ | "platform-agent"
29
+ | "forced-platform-env"
30
+ | "forced-direct-env"
31
+ | "direct-provider-env"
32
+ | "platform-proxy-fallback";
33
+ };
19
34
 
20
35
  type ProviderExecutionPlanBase = {
21
36
  mode: AgentProviderMode;
@@ -122,14 +137,50 @@ export function resolveAgentProviderMode(agentConfig: AgentRuntimeAgentConfig):
122
137
  return "platform";
123
138
  }
124
139
 
125
- export function shouldUsePlatformProxyTransport(
126
- env: EnvLike,
127
- agentConfig?: AgentRuntimeAgentConfig
128
- ) {
129
- if (agentConfig?.apiSource === "platform" || agentConfig?.useServerProxy === true) return true;
130
- if (env.NOLO_LOCAL_LLM === "platform") return true;
131
- if (env.NOLO_LOCAL_LLM === "direct") return false;
132
- return !hasDirectOpenAiCompatibleProvider(env) && canUsePlatformChatProvider(env);
140
+ export function resolveAgentRuntimeLocation(args: {
141
+ agentConfig: AgentRuntimeAgentConfig;
142
+ runtimeKind: "local" | "desktop" | "server";
143
+ }): AgentRuntimeLocation {
144
+ const runtimeBinding = args.agentConfig.runtimeBinding;
145
+ const machineId = typeof runtimeBinding?.machineId === "string"
146
+ ? runtimeBinding.machineId.trim()
147
+ : "";
148
+ if (machineId) return "bound-machine";
149
+ if (args.runtimeKind === "server") return "server";
150
+ return "local-host";
151
+ }
152
+
153
+ export function resolveProviderTransportDecision(args: {
154
+ agentConfig: AgentRuntimeAgentConfig;
155
+ env: EnvLike;
156
+ runtimeLocation: AgentRuntimeLocation;
157
+ }): ProviderTransportDecision {
158
+ const mode = resolveAgentProviderMode(args.agentConfig);
159
+ if (mode === "cli") {
160
+ return { mode, transport: "direct", reason: "cli-provider" };
161
+ }
162
+ if (mode === "custom") {
163
+ if (args.runtimeLocation === "server" && args.agentConfig.useServerProxy === true) {
164
+ return { mode, transport: "proxy", reason: "custom-server-proxy" };
165
+ }
166
+ return { mode, transport: "direct", reason: "custom-provider" };
167
+ }
168
+ if (args.agentConfig.apiSource === "platform" || args.agentConfig.useServerProxy === true) {
169
+ return { mode, transport: "proxy", reason: "platform-agent" };
170
+ }
171
+ if (args.env.NOLO_LOCAL_LLM === "platform") {
172
+ return { mode, transport: "proxy", reason: "forced-platform-env" };
173
+ }
174
+ if (args.env.NOLO_LOCAL_LLM === "direct") {
175
+ return { mode, transport: "direct", reason: "forced-direct-env" };
176
+ }
177
+ if (hasDirectOpenAiCompatibleProvider(args.env)) {
178
+ return { mode, transport: "direct", reason: "direct-provider-env" };
179
+ }
180
+ if (canUsePlatformChatProvider(args.env)) {
181
+ return { mode, transport: "proxy", reason: "platform-proxy-fallback" };
182
+ }
183
+ return { mode, transport: "direct", reason: "direct-provider-env" };
133
184
  }
134
185
 
135
186
  export function resolveProviderAuthHeaderName(args: {
@@ -161,6 +212,15 @@ export function buildProviderExecutionPlan(args: {
161
212
  }): ProviderExecutionPlan {
162
213
  const { agentConfig, env } = args;
163
214
  const mode = resolveAgentProviderMode(agentConfig);
215
+ const runtimeLocation = resolveAgentRuntimeLocation({
216
+ agentConfig,
217
+ runtimeKind: args.runtimeKind,
218
+ });
219
+ const transportDecision = resolveProviderTransportDecision({
220
+ agentConfig,
221
+ env,
222
+ runtimeLocation,
223
+ });
164
224
  const model = agentConfig.model || "gpt-4.1-mini";
165
225
  const requestOptions = pickAgentRuntimeInferenceOptions(agentConfig);
166
226
 
@@ -182,7 +242,7 @@ export function buildProviderExecutionPlan(args: {
182
242
  endpoint,
183
243
  apiKeyHeader: agentConfig.apiKeyHeader,
184
244
  });
185
- if (agentConfig.useServerProxy) {
245
+ if (transportDecision.transport === "proxy") {
186
246
  return {
187
247
  mode,
188
248
  transport: "proxy",
@@ -211,10 +271,10 @@ export function buildProviderExecutionPlan(args: {
211
271
  }
212
272
 
213
273
  const provider = agentConfig.provider || agentConfig.apiSource || "openai";
214
- const endpoint = shouldUsePlatformProxyTransport(env, agentConfig)
274
+ const endpoint = transportDecision.transport === "proxy"
215
275
  ? resolvePlatformProviderEndpoint(agentConfig)
216
276
  : resolveChatCompletionsEndpoint(resolveOpenAiCompatibleBaseUrl(env));
217
- if (shouldUsePlatformProxyTransport(env, agentConfig)) {
277
+ if (transportDecision.transport === "proxy") {
218
278
  return {
219
279
  mode,
220
280
  transport: "proxy",
@@ -221,6 +221,29 @@ function buildRequestTimeoutSignal(timeoutMs?: number) {
221
221
  return controller.signal;
222
222
  }
223
223
 
224
+ function summarizeEndpoint(value: unknown) {
225
+ if (typeof value !== "string" || !value.trim()) return undefined;
226
+ try {
227
+ const url = new URL(value);
228
+ return `${url.protocol}//${url.host}${url.pathname}`;
229
+ } catch {
230
+ return "invalid-url";
231
+ }
232
+ }
233
+
234
+ function parseJsonObject(raw: string) {
235
+ try {
236
+ const parsed = JSON.parse(raw);
237
+ return parsed && typeof parsed === "object" ? parsed : {};
238
+ } catch {
239
+ return {};
240
+ }
241
+ }
242
+
243
+ function logLocalRuntimeDiagnostic(event: string, fields: Record<string, unknown>) {
244
+ console.error(`[nolo-local-runtime] ${event} ${JSON.stringify(fields)}`);
245
+ }
246
+
224
247
  function buildOpenAiTools(args: { toolNames?: string[]; env: EnvLike }) {
225
248
  const toolset = buildLocalWorkspaceToolsetForEnv(args);
226
249
  return [
@@ -476,6 +499,20 @@ export function createCliLocalRuntimeAdapter(
476
499
  agentConfig,
477
500
  env: deps.env,
478
501
  });
502
+ logLocalRuntimeDiagnostic("provider.selected", {
503
+ agentKey: agentConfig.key,
504
+ transport: "platform-proxy",
505
+ apiSource: agentConfig.apiSource ?? null,
506
+ provider: providerConfig.provider,
507
+ model: providerConfig.model,
508
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
509
+ proxyServer: summarizeEndpoint(providerConfig.serverUrl) ?? null,
510
+ hasAuthToken: Boolean(providerConfig.authToken),
511
+ hasApiKey: Boolean(providerConfig.apiKey),
512
+ apiKeyHeader: providerConfig.apiKeyHeader ?? null,
513
+ useServerProxy: agentConfig.useServerProxy ?? null,
514
+ customProviderEndpoint: summarizeEndpoint(agentConfig.customProviderUrl) ?? null,
515
+ });
479
516
  return {
480
517
  model: providerConfig.model,
481
518
  complete: async (messages, options) => {
@@ -492,6 +529,16 @@ export function createCliLocalRuntimeAdapter(
492
529
  tools,
493
530
  });
494
531
  const signal = buildRequestTimeoutSignal(options?.timeoutMs);
532
+ logLocalRuntimeDiagnostic("provider.request.start", {
533
+ agentKey: agentConfig.key,
534
+ transport: "platform-proxy",
535
+ requestUrl: summarizeEndpoint(request.url) ?? null,
536
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
537
+ model: providerConfig.model,
538
+ messageCount: messages.length,
539
+ toolCount: tools.length,
540
+ timeoutMs: options?.timeoutMs ?? null,
541
+ });
495
542
  const res = await fetchWithTransientRetry(fetchImpl, request.url, {
496
543
  ...request.init,
497
544
  ...(signal ? { signal } : {}),
@@ -499,7 +546,15 @@ export function createCliLocalRuntimeAdapter(
499
546
  sleep: deps.sleep,
500
547
  loopbackRequest,
501
548
  });
502
- const data = parsePlatformChatCompletionData(await res.text().catch(() => ""));
549
+ const raw = await res.text().catch(() => "");
550
+ logLocalRuntimeDiagnostic("provider.request.result", {
551
+ agentKey: agentConfig.key,
552
+ transport: "platform-proxy",
553
+ status: res.status,
554
+ ok: res.ok,
555
+ responseBytes: raw.length,
556
+ });
557
+ const data = parsePlatformChatCompletionData(raw);
503
558
  if (!res.ok) {
504
559
  throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
505
560
  }
@@ -516,6 +571,18 @@ export function createCliLocalRuntimeAdapter(
516
571
  agentConfig,
517
572
  env: deps.env,
518
573
  });
574
+ logLocalRuntimeDiagnostic("provider.selected", {
575
+ agentKey: agentConfig.key,
576
+ transport: "direct-openai-compatible",
577
+ apiSource: agentConfig.apiSource ?? null,
578
+ provider: providerConfig.provider,
579
+ model: providerConfig.model,
580
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
581
+ hasApiKey: Boolean(providerConfig.apiKey),
582
+ apiKeyHeader: providerConfig.apiKeyHeader ?? null,
583
+ useServerProxy: agentConfig.useServerProxy ?? null,
584
+ customProviderEndpoint: summarizeEndpoint(agentConfig.customProviderUrl) ?? null,
585
+ });
519
586
  return {
520
587
  model: providerConfig.model,
521
588
  complete: async (messages, options) => {
@@ -532,6 +599,15 @@ export function createCliLocalRuntimeAdapter(
532
599
  tools,
533
600
  });
534
601
  const signal = buildRequestTimeoutSignal(options?.timeoutMs);
602
+ logLocalRuntimeDiagnostic("provider.request.start", {
603
+ agentKey: agentConfig.key,
604
+ transport: "direct-openai-compatible",
605
+ requestUrl: summarizeEndpoint(request.url) ?? null,
606
+ model: providerConfig.model,
607
+ messageCount: messages.length,
608
+ toolCount: tools.length,
609
+ timeoutMs: options?.timeoutMs ?? null,
610
+ });
535
611
  const res = await fetchWithTransientRetry(fetchImpl, request.url, {
536
612
  ...request.init,
537
613
  ...(signal ? { signal } : {}),
@@ -539,7 +615,15 @@ export function createCliLocalRuntimeAdapter(
539
615
  sleep: deps.sleep,
540
616
  loopbackRequest,
541
617
  });
542
- const data = await res.json().catch(() => ({}));
618
+ const raw = await res.text().catch(() => "");
619
+ logLocalRuntimeDiagnostic("provider.request.result", {
620
+ agentKey: agentConfig.key,
621
+ transport: "direct-openai-compatible",
622
+ status: res.status,
623
+ ok: res.ok,
624
+ responseBytes: raw.length,
625
+ });
626
+ const data = parseJsonObject(raw);
543
627
  if (!res.ok) {
544
628
  throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
545
629
  }
@@ -96,6 +96,37 @@ function logConnectorRun(message: string, fields: Record<string, unknown> = {})
96
96
  console.error(`[nolo-connector] ${message} ${JSON.stringify(fields)}`);
97
97
  }
98
98
 
99
+ function summarizeEndpoint(value: unknown) {
100
+ if (typeof value !== "string" || !value.trim()) return undefined;
101
+ try {
102
+ const url = new URL(value);
103
+ return `${url.protocol}//${url.host}${url.pathname}`;
104
+ } catch {
105
+ return "invalid-url";
106
+ }
107
+ }
108
+
109
+ function summarizeAgentConfigForLogs(agentConfig: any) {
110
+ const runtimeBinding = isRecord(agentConfig?.runtimeBinding) ? agentConfig.runtimeBinding : {};
111
+ return {
112
+ apiSource: agentConfig?.apiSource ?? null,
113
+ provider: agentConfig?.provider ?? null,
114
+ cliProvider: agentConfig?.cliProvider ?? null,
115
+ model: agentConfig?.model ?? null,
116
+ customProviderEndpoint: summarizeEndpoint(agentConfig?.customProviderUrl) ?? null,
117
+ useServerProxy: agentConfig?.useServerProxy ?? null,
118
+ skipLocalRuntimeEnsure: agentConfig?.skipLocalRuntimeEnsure ?? null,
119
+ hasApiKey: typeof agentConfig?.apiKey === "string" && agentConfig.apiKey.length > 0,
120
+ apiKeyHeader: agentConfig?.apiKeyHeader ?? null,
121
+ hasApiKeyFromAgentKey:
122
+ typeof agentConfig?.apiKeyFromAgentKey === "string" &&
123
+ agentConfig.apiKeyFromAgentKey.length > 0,
124
+ runtimeBindingKind: runtimeBinding.kind ?? null,
125
+ runtimeMachineId: runtimeBinding.machineId ?? null,
126
+ connectorSurface: runtimeBinding.connectorSurface ?? runtimeBinding.surface ?? null,
127
+ };
128
+ }
129
+
99
130
  function isRecord(value: unknown): value is Record<string, any> {
100
131
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
101
132
  }
@@ -569,6 +600,15 @@ export async function handleConnectorRunMessage(
569
600
  if (!requestsLocalWorkspaceRuntime(runtimePolicy) && !isMachineBoundLocalCustomProvider(agentConfig)) {
570
601
  throw new Error("Connector can only execute non-CLI agents when runtimeToolPolicySnapshot requests a local workspace runtime.");
571
602
  }
603
+ logConnectorRun("agent.run.local-runtime.start", {
604
+ requestId: parsed.requestId,
605
+ agentKey: parsed.payload?.agentKey,
606
+ cwd,
607
+ timeout,
608
+ machineBoundLocalCustomProvider: isMachineBoundLocalCustomProvider(agentConfig),
609
+ runtimePolicy: Boolean(runtimePolicy),
610
+ ...summarizeAgentConfigForLogs(agentConfig),
611
+ });
572
612
  baseSha = await readConnectorGitHead(cwd);
573
613
  const result = await runConnectorLocalRuntimeAgent({
574
614
  parsed,
@@ -607,6 +647,8 @@ export async function handleConnectorRunMessage(
607
647
  logConnectorRun("agent.run.error", {
608
648
  requestId: parsed.requestId,
609
649
  agentKey: parsed.payload?.agentKey,
650
+ ...summarizeAgentConfigForLogs(agentConfig),
651
+ errorName: error instanceof Error ? error.name : null,
610
652
  error: error instanceof Error ? error.message : String(error),
611
653
  });
612
654
  send(JSON.stringify({
@@ -221,6 +221,29 @@ function buildRequestTimeoutSignal(timeoutMs?: number) {
221
221
  return controller.signal;
222
222
  }
223
223
 
224
+ function summarizeEndpoint(value: unknown) {
225
+ if (typeof value !== "string" || !value.trim()) return undefined;
226
+ try {
227
+ const url = new URL(value);
228
+ return `${url.protocol}//${url.host}${url.pathname}`;
229
+ } catch {
230
+ return "invalid-url";
231
+ }
232
+ }
233
+
234
+ function parseJsonObject(raw: string) {
235
+ try {
236
+ const parsed = JSON.parse(raw);
237
+ return parsed && typeof parsed === "object" ? parsed : {};
238
+ } catch {
239
+ return {};
240
+ }
241
+ }
242
+
243
+ function logLocalRuntimeDiagnostic(event: string, fields: Record<string, unknown>) {
244
+ console.error(`[nolo-local-runtime] ${event} ${JSON.stringify(fields)}`);
245
+ }
246
+
224
247
  function buildOpenAiTools(args: { toolNames?: string[]; env: EnvLike }) {
225
248
  const toolset = buildLocalWorkspaceToolsetForEnv(args);
226
249
  return [
@@ -476,6 +499,20 @@ export function createCliLocalRuntimeAdapter(
476
499
  agentConfig,
477
500
  env: deps.env,
478
501
  });
502
+ logLocalRuntimeDiagnostic("provider.selected", {
503
+ agentKey: agentConfig.key,
504
+ transport: "platform-proxy",
505
+ apiSource: agentConfig.apiSource ?? null,
506
+ provider: providerConfig.provider,
507
+ model: providerConfig.model,
508
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
509
+ proxyServer: summarizeEndpoint(providerConfig.serverUrl) ?? null,
510
+ hasAuthToken: Boolean(providerConfig.authToken),
511
+ hasApiKey: Boolean(providerConfig.apiKey),
512
+ apiKeyHeader: providerConfig.apiKeyHeader ?? null,
513
+ useServerProxy: agentConfig.useServerProxy ?? null,
514
+ customProviderEndpoint: summarizeEndpoint(agentConfig.customProviderUrl) ?? null,
515
+ });
479
516
  return {
480
517
  model: providerConfig.model,
481
518
  complete: async (messages, options) => {
@@ -492,6 +529,16 @@ export function createCliLocalRuntimeAdapter(
492
529
  tools,
493
530
  });
494
531
  const signal = buildRequestTimeoutSignal(options?.timeoutMs);
532
+ logLocalRuntimeDiagnostic("provider.request.start", {
533
+ agentKey: agentConfig.key,
534
+ transport: "platform-proxy",
535
+ requestUrl: summarizeEndpoint(request.url) ?? null,
536
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
537
+ model: providerConfig.model,
538
+ messageCount: messages.length,
539
+ toolCount: tools.length,
540
+ timeoutMs: options?.timeoutMs ?? null,
541
+ });
495
542
  const res = await fetchWithTransientRetry(fetchImpl, request.url, {
496
543
  ...request.init,
497
544
  ...(signal ? { signal } : {}),
@@ -499,7 +546,15 @@ export function createCliLocalRuntimeAdapter(
499
546
  sleep: deps.sleep,
500
547
  loopbackRequest,
501
548
  });
502
- const data = parsePlatformChatCompletionData(await res.text().catch(() => ""));
549
+ const raw = await res.text().catch(() => "");
550
+ logLocalRuntimeDiagnostic("provider.request.result", {
551
+ agentKey: agentConfig.key,
552
+ transport: "platform-proxy",
553
+ status: res.status,
554
+ ok: res.ok,
555
+ responseBytes: raw.length,
556
+ });
557
+ const data = parsePlatformChatCompletionData(raw);
503
558
  if (!res.ok) {
504
559
  throw new Error(`platform provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
505
560
  }
@@ -516,6 +571,18 @@ export function createCliLocalRuntimeAdapter(
516
571
  agentConfig,
517
572
  env: deps.env,
518
573
  });
574
+ logLocalRuntimeDiagnostic("provider.selected", {
575
+ agentKey: agentConfig.key,
576
+ transport: "direct-openai-compatible",
577
+ apiSource: agentConfig.apiSource ?? null,
578
+ provider: providerConfig.provider,
579
+ model: providerConfig.model,
580
+ endpoint: summarizeEndpoint(providerConfig.endpoint) ?? null,
581
+ hasApiKey: Boolean(providerConfig.apiKey),
582
+ apiKeyHeader: providerConfig.apiKeyHeader ?? null,
583
+ useServerProxy: agentConfig.useServerProxy ?? null,
584
+ customProviderEndpoint: summarizeEndpoint(agentConfig.customProviderUrl) ?? null,
585
+ });
519
586
  return {
520
587
  model: providerConfig.model,
521
588
  complete: async (messages, options) => {
@@ -532,6 +599,15 @@ export function createCliLocalRuntimeAdapter(
532
599
  tools,
533
600
  });
534
601
  const signal = buildRequestTimeoutSignal(options?.timeoutMs);
602
+ logLocalRuntimeDiagnostic("provider.request.start", {
603
+ agentKey: agentConfig.key,
604
+ transport: "direct-openai-compatible",
605
+ requestUrl: summarizeEndpoint(request.url) ?? null,
606
+ model: providerConfig.model,
607
+ messageCount: messages.length,
608
+ toolCount: tools.length,
609
+ timeoutMs: options?.timeoutMs ?? null,
610
+ });
535
611
  const res = await fetchWithTransientRetry(fetchImpl, request.url, {
536
612
  ...request.init,
537
613
  ...(signal ? { signal } : {}),
@@ -539,7 +615,15 @@ export function createCliLocalRuntimeAdapter(
539
615
  sleep: deps.sleep,
540
616
  loopbackRequest,
541
617
  });
542
- const data = await res.json().catch(() => ({}));
618
+ const raw = await res.text().catch(() => "");
619
+ logLocalRuntimeDiagnostic("provider.request.result", {
620
+ agentKey: agentConfig.key,
621
+ transport: "direct-openai-compatible",
622
+ status: res.status,
623
+ ok: res.ok,
624
+ responseBytes: raw.length,
625
+ });
626
+ const data = parseJsonObject(raw);
543
627
  if (!res.ok) {
544
628
  throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
545
629
  }
@@ -96,6 +96,37 @@ function logConnectorRun(message: string, fields: Record<string, unknown> = {})
96
96
  console.error(`[nolo-connector] ${message} ${JSON.stringify(fields)}`);
97
97
  }
98
98
 
99
+ function summarizeEndpoint(value: unknown) {
100
+ if (typeof value !== "string" || !value.trim()) return undefined;
101
+ try {
102
+ const url = new URL(value);
103
+ return `${url.protocol}//${url.host}${url.pathname}`;
104
+ } catch {
105
+ return "invalid-url";
106
+ }
107
+ }
108
+
109
+ function summarizeAgentConfigForLogs(agentConfig: any) {
110
+ const runtimeBinding = isRecord(agentConfig?.runtimeBinding) ? agentConfig.runtimeBinding : {};
111
+ return {
112
+ apiSource: agentConfig?.apiSource ?? null,
113
+ provider: agentConfig?.provider ?? null,
114
+ cliProvider: agentConfig?.cliProvider ?? null,
115
+ model: agentConfig?.model ?? null,
116
+ customProviderEndpoint: summarizeEndpoint(agentConfig?.customProviderUrl) ?? null,
117
+ useServerProxy: agentConfig?.useServerProxy ?? null,
118
+ skipLocalRuntimeEnsure: agentConfig?.skipLocalRuntimeEnsure ?? null,
119
+ hasApiKey: typeof agentConfig?.apiKey === "string" && agentConfig.apiKey.length > 0,
120
+ apiKeyHeader: agentConfig?.apiKeyHeader ?? null,
121
+ hasApiKeyFromAgentKey:
122
+ typeof agentConfig?.apiKeyFromAgentKey === "string" &&
123
+ agentConfig.apiKeyFromAgentKey.length > 0,
124
+ runtimeBindingKind: runtimeBinding.kind ?? null,
125
+ runtimeMachineId: runtimeBinding.machineId ?? null,
126
+ connectorSurface: runtimeBinding.connectorSurface ?? runtimeBinding.surface ?? null,
127
+ };
128
+ }
129
+
99
130
  function isRecord(value: unknown): value is Record<string, any> {
100
131
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
101
132
  }
@@ -569,6 +600,15 @@ export async function handleConnectorRunMessage(
569
600
  if (!requestsLocalWorkspaceRuntime(runtimePolicy) && !isMachineBoundLocalCustomProvider(agentConfig)) {
570
601
  throw new Error("Connector can only execute non-CLI agents when runtimeToolPolicySnapshot requests a local workspace runtime.");
571
602
  }
603
+ logConnectorRun("agent.run.local-runtime.start", {
604
+ requestId: parsed.requestId,
605
+ agentKey: parsed.payload?.agentKey,
606
+ cwd,
607
+ timeout,
608
+ machineBoundLocalCustomProvider: isMachineBoundLocalCustomProvider(agentConfig),
609
+ runtimePolicy: Boolean(runtimePolicy),
610
+ ...summarizeAgentConfigForLogs(agentConfig),
611
+ });
572
612
  baseSha = await readConnectorGitHead(cwd);
573
613
  const result = await runConnectorLocalRuntimeAgent({
574
614
  parsed,
@@ -607,6 +647,8 @@ export async function handleConnectorRunMessage(
607
647
  logConnectorRun("agent.run.error", {
608
648
  requestId: parsed.requestId,
609
649
  agentKey: parsed.payload?.agentKey,
650
+ ...summarizeAgentConfigForLogs(agentConfig),
651
+ errorName: error instanceof Error ? error.name : null,
610
652
  error: error instanceof Error ? error.message : String(error),
611
653
  });
612
654
  send(JSON.stringify({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nolo-cli",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
4
4
  "type": "module",
5
5
  "description": "Agent-first terminal workspace for Nolo",
6
6
  "bin": {