nolo-cli 0.1.21 → 0.1.23

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.
Files changed (72) hide show
  1. package/agent-runtime/agentRecordConfig.ts +4 -0
  2. package/agent-runtime/hostAdapter.ts +2 -0
  3. package/agent-runtime/index.ts +7 -0
  4. package/agent-runtime/localLoop.ts +2 -0
  5. package/agent-runtime/platformChatProvider.ts +3 -0
  6. package/agent-runtime/runtimeToolPolicy.ts +92 -0
  7. package/agent-runtime/types.ts +42 -0
  8. package/agentRunCommand.ts +74 -1
  9. package/agentRuntimeCommands.ts +17 -89
  10. package/ai/agent/streamAgentChatTurn.ts +104 -20
  11. package/ai/chat/fetchUtils.native.ts +2 -0
  12. package/ai/chat/fetchUtils.ts +2 -0
  13. package/ai/chat/sendOpenAICompletionsRequest.ts +56 -0
  14. package/ai/chat/sendOpenAIResponseRequest.ts +64 -0
  15. package/ai/llm/kimi.ts +1 -1
  16. package/ai/llm/providers.ts +3 -0
  17. package/ai/llm/reasoningModels.ts +1 -0
  18. package/ai/skills/skillDocProtocol.ts +95 -3
  19. package/ai/taskRun/taskRunProtocol.ts +1 -0
  20. package/ai/tools/agent/agentTools.ts +17 -0
  21. package/ai/tools/agent/startAgentDialogTool.ts +53 -0
  22. package/ai/tools/modelUsageTools.ts +5 -0
  23. package/client/agentRun.test.ts +257 -7
  24. package/client/agentRun.ts +133 -34
  25. package/client/localRuntimeAdapter.test.ts +2 -0
  26. package/client/localRuntimeAdapter.ts +15 -2
  27. package/database/actions/common.ts +4 -3
  28. package/database/config.ts +19 -0
  29. package/machineCommands.ts +400 -45
  30. package/package.json +4 -2
  31. package/render/canvas/canvasEditContext.ts +127 -0
  32. package/render/canvas/canvasRuntime.ts +57 -0
  33. package/render/canvas/canvasSnapshotParser.ts +76 -0
  34. package/render/canvas/canvasTree.ts +308 -0
  35. package/render/canvas/types.ts +46 -0
  36. package/render/layout/deleteBehavior.ts +52 -0
  37. package/render/layout/mainLayoutSidebar.ts +17 -0
  38. package/render/layout/mainLayoutViewMode.ts +56 -0
  39. package/render/layout/topbarUtils.ts +87 -0
  40. package/render/layout/useDevReloadPending.ts +30 -0
  41. package/render/page/createPageAction.ts +183 -0
  42. package/render/page/docSlice.ts +468 -0
  43. package/render/page/server/createPage.ts +174 -0
  44. package/render/page/server/handleCreatePage.ts +91 -0
  45. package/render/page/server/index.ts +4 -0
  46. package/render/page/types.ts +17 -0
  47. package/render/page/useKeyboardSave.ts +48 -0
  48. package/render/styles/zIndex.ts +12 -0
  49. package/render/surf/WeatherIconStyles.ts +17 -0
  50. package/render/surf/color.ts +9 -0
  51. package/render/surf/config.ts +46 -0
  52. package/render/surf/screens/style.ts +1 -0
  53. package/render/surf/styles/ToggleButtonStyles.ts +8 -0
  54. package/render/surf/utils/groupedWeatherData.ts +32 -0
  55. package/render/surf/weatherUtils.ts +50 -0
  56. package/render/table/activityColumns.ts +6 -0
  57. package/render/table/createTableAction.ts +270 -0
  58. package/render/table/deleteTableAction.ts +129 -0
  59. package/render/table/fetchAndCacheTableRows.ts +174 -0
  60. package/render/table/tableSlice.ts +1106 -0
  61. package/render/table/tableView.ts +289 -0
  62. package/render/table/toolValueUtils.ts +363 -0
  63. package/render/table/types.ts +252 -0
  64. package/render/table/useCreateTable.ts +72 -0
  65. package/render/table/useTable.ts +61 -0
  66. package/render/table/utils/tableSerialization.ts +50 -0
  67. package/render/web/elements/artifactPreviewCode.ts +43 -0
  68. package/render/web/elements/artifactRuntimePreload.ts +52 -0
  69. package/render/web/elements/codeBlockAutoPreview.ts +10 -0
  70. package/render/web/elements/mermaidPreview.ts +21 -0
  71. package/render/web/ui/useInlineEdit.ts +135 -0
  72. package/tableCommands.ts +42 -5
@@ -73,6 +73,9 @@ export function resolveAgentRuntimeConfigFromRecord(
73
73
  const maxTokens = numberField(record, "max_tokens");
74
74
  const reasoningEffort = stringField(record, "reasoning_effort");
75
75
  const runtimeBinding = objectField(record, "runtimeBinding");
76
+ const runtimeToolPolicy =
77
+ objectField(record, "runtimeToolPolicy") ??
78
+ objectField(runtimeBinding ?? {}, "runtimeToolPolicy");
76
79
  const localWorkspaceMode = localWorkspaceModeField(record);
77
80
  const delegation = objectField(record, "delegation");
78
81
  return {
@@ -92,6 +95,7 @@ export function resolveAgentRuntimeConfigFromRecord(
92
95
  ...(maxTokens !== undefined ? { max_tokens: maxTokens } : {}),
93
96
  ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),
94
97
  ...(runtimeBinding ? { runtimeBinding } : {}),
98
+ ...(runtimeToolPolicy ? { runtimeToolPolicy } : {}),
95
99
  ...(localWorkspaceMode ? { localWorkspaceMode } : {}),
96
100
  ...(delegation ? { delegation } : {}),
97
101
  rawRecord: record,
@@ -2,6 +2,7 @@ import type {
2
2
  AgentRuntimeChatMessage,
3
3
  AgentRuntimeHost,
4
4
  AgentRuntimeResult,
5
+ AgentRuntimeToolPolicy,
5
6
  } from "./types";
6
7
 
7
8
  export type AgentRuntimeAgentConfig = {
@@ -21,6 +22,7 @@ export type AgentRuntimeAgentConfig = {
21
22
  max_tokens?: number;
22
23
  reasoning_effort?: string;
23
24
  runtimeBinding?: Record<string, unknown>;
25
+ runtimeToolPolicy?: AgentRuntimeToolPolicy;
24
26
  localWorkspaceMode?: "current" | "task-worktree";
25
27
  delegation?: Record<string, unknown>;
26
28
  rawRecord?: Record<string, unknown>;
@@ -45,6 +45,11 @@ export {
45
45
  executeLocalToolWithPolicy,
46
46
  resolveLocalToolPolicy,
47
47
  } from "./localToolPolicy";
48
+ export {
49
+ mergeAgentRuntimeToolPolicies,
50
+ normalizeAgentRuntimeToolPolicy,
51
+ resolveEffectiveRuntimeToolPolicy,
52
+ } from "./runtimeToolPolicy";
48
53
  export {
49
54
  buildLocalWorkspaceOpenAiTools,
50
55
  buildLocalWorkspacePolicyToolNames,
@@ -94,5 +99,7 @@ export type {
94
99
  AgentRuntimeMode,
95
100
  AgentRuntimeRequestedMode,
96
101
  AgentRuntimeResult,
102
+ AgentRuntimeToolPolicy,
97
103
  AgentRuntimeToolCall,
104
+ AgentRuntimeWorkspaceMode,
98
105
  } from "./types";
@@ -26,6 +26,7 @@ export type LocalAgentToolEvent = {
26
26
  toolCallId: string;
27
27
  toolName: string;
28
28
  message?: string;
29
+ metadata?: Record<string, unknown>;
29
30
  };
30
31
 
31
32
  const DEFAULT_LOCAL_AGENT_MAX_TOOL_ROUNDS = 16;
@@ -119,6 +120,7 @@ export async function runLocalAgentTurn(
119
120
  round,
120
121
  toolCallId: toolCall.id,
121
122
  toolName,
123
+ metadata: toolResult.metadata,
122
124
  });
123
125
  } catch (error) {
124
126
  if (!shouldReturnToolExecutionErrors(input.adapter)) throw error;
@@ -23,6 +23,7 @@ const PROVIDER_ENDPOINTS: Record<string, string> = {
23
23
  export type PlatformChatProviderConfig = {
24
24
  serverUrl: string;
25
25
  authToken: string;
26
+ agentKey: string;
26
27
  model: string;
27
28
  provider: string;
28
29
  endpoint: string;
@@ -93,6 +94,7 @@ export function resolvePlatformChatProviderConfig(args: {
93
94
  return {
94
95
  serverUrl: resolvePlatformServerUrl(args.env),
95
96
  authToken: resolvePlatformAuthToken(args.env),
97
+ agentKey: args.agentConfig.key,
96
98
  model: args.agentConfig.model || "gpt-4.1-mini",
97
99
  provider,
98
100
  endpoint: resolveProviderEndpoint(args.agentConfig),
@@ -118,6 +120,7 @@ export function buildPlatformChatCompletionRequest(args: {
118
120
  ...(shouldDisableThinking(args.providerConfig) ? { thinking: { type: "disabled" } } : {}),
119
121
  url: args.providerConfig.endpoint,
120
122
  provider: args.providerConfig.provider,
123
+ agentKey: args.providerConfig.agentKey,
121
124
  ...(args.providerConfig.apiSource ? { apiSource: args.providerConfig.apiSource } : {}),
122
125
  ...(args.providerConfig.apiKey ? { KEY: args.providerConfig.apiKey } : {}),
123
126
  };
@@ -0,0 +1,92 @@
1
+ import type { AgentRuntimeAgentConfig } from "./hostAdapter";
2
+ import type { AgentRuntimeToolPolicy } from "./types";
3
+
4
+ function isRecord(value: unknown): value is Record<string, unknown> {
5
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
6
+ }
7
+
8
+ function stringArray(value: unknown): string[] {
9
+ if (!Array.isArray(value)) return [];
10
+ return value.filter((item): item is string => typeof item === "string" && !!item.trim());
11
+ }
12
+
13
+ function unique(values: string[]) {
14
+ return [...new Set(values)];
15
+ }
16
+
17
+ function mergeRecords<T extends Record<string, unknown>>(
18
+ base: T | undefined,
19
+ override: T | undefined,
20
+ ): T | undefined {
21
+ if (!base && !override) return undefined;
22
+ return {
23
+ ...(base ?? {}),
24
+ ...(override ?? {}),
25
+ } as T;
26
+ }
27
+
28
+ export function normalizeAgentRuntimeToolPolicy(
29
+ value: unknown,
30
+ ): AgentRuntimeToolPolicy | undefined {
31
+ if (!isRecord(value)) return undefined;
32
+ return {
33
+ version: 1,
34
+ ...(stringArray(value.agentTools).length
35
+ ? { agentTools: unique(stringArray(value.agentTools)) }
36
+ : {}),
37
+ ...(stringArray(value.runtimeTools).length
38
+ ? { runtimeTools: unique(stringArray(value.runtimeTools)) }
39
+ : {}),
40
+ ...(isRecord(value.workspace) ? { workspace: { ...value.workspace } } : {}),
41
+ ...(isRecord(value.shell) ? { shell: { ...value.shell } } : {}),
42
+ ...(isRecord(value.git) ? { git: { ...value.git } } : {}),
43
+ ...(isRecord(value.budget) ? { budget: { ...value.budget } } : {}),
44
+ ...(isRecord(value.audit) ? { audit: { ...value.audit } } : {}),
45
+ };
46
+ }
47
+
48
+ export function mergeAgentRuntimeToolPolicies(
49
+ base?: AgentRuntimeToolPolicy,
50
+ override?: AgentRuntimeToolPolicy,
51
+ ): AgentRuntimeToolPolicy | undefined {
52
+ if (!base && !override) return undefined;
53
+ return {
54
+ version: 1,
55
+ agentTools: unique([
56
+ ...(base?.agentTools ?? []),
57
+ ...(override?.agentTools ?? []),
58
+ ]),
59
+ runtimeTools: unique([
60
+ ...(base?.runtimeTools ?? []),
61
+ ...(override?.runtimeTools ?? []),
62
+ ]),
63
+ workspace: {
64
+ ...(base?.workspace ?? {}),
65
+ ...(override?.workspace ?? {}),
66
+ writableRoots: unique([
67
+ ...(base?.workspace?.writableRoots ?? []),
68
+ ...(override?.workspace?.writableRoots ?? []),
69
+ ]),
70
+ },
71
+ shell: mergeRecords(base?.shell, override?.shell),
72
+ git: mergeRecords(base?.git, override?.git),
73
+ budget: mergeRecords(base?.budget, override?.budget),
74
+ audit: mergeRecords(base?.audit, override?.audit),
75
+ };
76
+ }
77
+
78
+ export function resolveEffectiveRuntimeToolPolicy(args: {
79
+ agentConfig: Pick<AgentRuntimeAgentConfig, "runtimeToolPolicy" | "runtimeBinding">;
80
+ taskRunControl?: { runtimeToolPolicy?: unknown } | null;
81
+ }): AgentRuntimeToolPolicy | undefined {
82
+ const agentPolicy = normalizeAgentRuntimeToolPolicy(
83
+ args.agentConfig.runtimeToolPolicy ??
84
+ (isRecord(args.agentConfig.runtimeBinding)
85
+ ? args.agentConfig.runtimeBinding.runtimeToolPolicy
86
+ : undefined),
87
+ );
88
+ const taskPolicy = normalizeAgentRuntimeToolPolicy(
89
+ args.taskRunControl?.runtimeToolPolicy,
90
+ );
91
+ return mergeAgentRuntimeToolPolicies(agentPolicy, taskPolicy);
92
+ }
@@ -81,6 +81,48 @@ export type AgentRuntimeDecisionInput = {
81
81
  serverFallbackAvailable: boolean;
82
82
  };
83
83
 
84
+ export type AgentRuntimeWorkspaceMode = "none" | "current" | "task-worktree" | "lease";
85
+
86
+ export type AgentRuntimeShellPolicy = {
87
+ enabled?: boolean;
88
+ mode?: "off" | "worktree";
89
+ commandPolicy?: "denylist" | "allowlist" | "approval";
90
+ networkPolicy?: "default-deny" | "allowed" | "approval";
91
+ maxSeconds?: number;
92
+ maxOutputBytes?: number;
93
+ };
94
+
95
+ export type AgentRuntimeGitPolicy = {
96
+ canCommit?: boolean;
97
+ canPushAlpha?: boolean;
98
+ canMergeMain?: boolean;
99
+ };
100
+
101
+ export type AgentRuntimeAuditPolicy = {
102
+ logToolCalls?: boolean;
103
+ logShellCommands?: boolean;
104
+ writeToDialog?: boolean;
105
+ writeToTask?: boolean;
106
+ };
107
+
108
+ export type AgentRuntimeToolPolicy = {
109
+ version?: 1;
110
+ agentTools?: string[];
111
+ runtimeTools?: string[];
112
+ workspace?: {
113
+ mode?: AgentRuntimeWorkspaceMode;
114
+ writableRoots?: string[];
115
+ cwd?: string;
116
+ };
117
+ shell?: AgentRuntimeShellPolicy;
118
+ git?: AgentRuntimeGitPolicy;
119
+ budget?: {
120
+ dailyUsdLimit?: number;
121
+ maxRunSeconds?: number;
122
+ };
123
+ audit?: AgentRuntimeAuditPolicy;
124
+ };
125
+
84
126
  export type AgentRuntimeDecision = {
85
127
  mode: AgentRuntimeMode;
86
128
  runnable: boolean;
@@ -3,6 +3,7 @@ import { runAgentTurn, type RunAgentTurnResult } from "./client/agentRun";
3
3
  import type { AgentRuntimeHostAdapter, AgentRuntimeRequestedMode } from "./agentRuntimeLocal";
4
4
  import { existsSync, readFileSync } from "node:fs";
5
5
  import { extname, resolve } from "node:path";
6
+ import { parseSkillDocProtocol, type WorkflowReferenceConfig } from "./ai/skills/skillDocProtocol";
6
7
  import { resolveCliAgentKeyInput } from "./agentAliases";
7
8
  import {
8
9
  ensureWorkspacePackageLinks,
@@ -27,6 +28,7 @@ type AgentRunCommandDeps = {
27
28
  prepareTaskWorktree?: typeof prepareTaskWorktree;
28
29
  ensureWorkspacePackageLinks?: typeof ensureWorkspacePackageLinks;
29
30
  inspectLocalRunWorkspace?: typeof inspectLocalRunWorkspace;
31
+ resolveWorkflowReference?: typeof resolveWorkflowReference;
30
32
  };
31
33
 
32
34
  type LocalRunWorkspaceInspection = {
@@ -63,6 +65,13 @@ type ParsedAgentRunArgs = {
63
65
  traceTools: boolean;
64
66
  useTaskWorktree: boolean;
65
67
  taskRunContext?: TaskRunPromptContext;
68
+ workflowRef?: string;
69
+ };
70
+
71
+ type ResolvedWorkflowReference = {
72
+ ref: string;
73
+ content: string;
74
+ config?: Partial<WorkflowReferenceConfig>;
66
75
  };
67
76
 
68
77
  const DEFAULT_TEST_ROOT_DIALOG_ID = "01KN6V3MVEQA4Q1PNKVM2W7YY8";
@@ -171,6 +180,7 @@ export function parseAgentRunArgs(
171
180
  ...readRepeatedFlagValues(args, "--image-url"),
172
181
  ];
173
182
  const useTaskWorktree = args.includes("--worktree-task");
183
+ const workflowRef = readFlagValue(args, "--workflow");
174
184
  const taskRowDbKey = readFlagValue(args, "--task-row-dbkey");
175
185
  const artifactIds = readFlagValue(args, "--artifact-ids")
176
186
  ?.split(",")
@@ -183,6 +193,7 @@ export function parseAgentRunArgs(
183
193
  allowShell: args.includes("--dangerously-allow-shell"),
184
194
  traceTools: args.includes("--trace-tools"),
185
195
  useTaskWorktree,
196
+ ...(workflowRef ? { workflowRef } : {}),
186
197
  ...(taskRowDbKey
187
198
  ? {
188
199
  taskRunContext: {
@@ -208,6 +219,57 @@ export function parseAgentRunArgs(
208
219
  };
209
220
  }
210
221
 
222
+ function workflowRefToCandidatePath(cwd: string, ref: string) {
223
+ const normalized = ref.trim();
224
+ if (!normalized) return "";
225
+ if (normalized.endsWith(".md") || normalized.includes("/") || normalized.includes("\\")) {
226
+ const directPath = resolve(cwd, normalized);
227
+ if (existsSync(directPath)) return directPath;
228
+ }
229
+ const fileName = normalized.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]+/g, "-").replace(/^-+|-+$/g, "");
230
+ return resolve(cwd, "docs", "workflows", `${fileName}.md`);
231
+ }
232
+
233
+ export async function resolveWorkflowReference(ref: string, cwd = process.cwd()): Promise<ResolvedWorkflowReference> {
234
+ const path = workflowRefToCandidatePath(cwd, ref);
235
+ if (!path || !existsSync(path)) {
236
+ throw new Error(`Workflow reference not found: ${ref}`);
237
+ }
238
+ const markdown = readFileSync(path, "utf8");
239
+ const parsed = parseSkillDocProtocol(markdown);
240
+ return {
241
+ ref,
242
+ content: parsed.content,
243
+ ...(parsed.meta?.workflowConfig ? { config: parsed.meta.workflowConfig } : {}),
244
+ };
245
+ }
246
+
247
+ export function prependWorkflowReferencePrompt(message: string, workflow?: ResolvedWorkflowReference): string {
248
+ if (!workflow) return message;
249
+ const config = workflow.config;
250
+ return [
251
+ "AI-native workflow reference:",
252
+ "- This reference is guidance for the agent, not a central workflow engine.",
253
+ `- ref: ${workflow.ref}`,
254
+ ...(config?.id ? [`- id: ${config.id}`] : []),
255
+ ...(config?.name ? [`- name: ${config.name}`] : []),
256
+ ...(config?.defaultAgent ? [`- suggested defaultAgent: ${config.defaultAgent}`] : []),
257
+ ...(config?.inputs?.length ? [`- inputs: ${config.inputs.join(", ")}`] : []),
258
+ ...(config?.recommendedTools?.length ? [`- recommendedTools: ${config.recommendedTools.join(", ")}`] : []),
259
+ ...(config?.requiredTools?.length ? [`- requiredTools: ${config.requiredTools.join(", ")}`] : []),
260
+ ...(config?.requiredOutputs?.length ? [`- requiredOutputs: ${config.requiredOutputs.join(", ")}`] : []),
261
+ ...(config?.gates?.length ? [`- gates: ${config.gates.join(", ")}`] : []),
262
+ ...(config?.contextStrategy ? [`- contextStrategy: ${config.contextStrategy}`] : []),
263
+ ...(config?.failureProtocol ? [`- failureProtocol: ${config.failureProtocol}`] : []),
264
+ "",
265
+ "Reference body:",
266
+ workflow.content,
267
+ "",
268
+ "User task:",
269
+ message,
270
+ ].join("\n");
271
+ }
272
+
211
273
  function parseDialogReference(rawInput: string) {
212
274
  const normalized = rawInput.trim();
213
275
  if (normalized.startsWith("dialog-")) {
@@ -353,6 +415,17 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
353
415
  }
354
416
 
355
417
  const runner = deps.runner ?? runAgentTurn;
418
+ let workflowReference: ResolvedWorkflowReference | undefined;
419
+ if (parsed.workflowRef) {
420
+ try {
421
+ workflowReference = await (deps.resolveWorkflowReference ?? resolveWorkflowReference)(
422
+ parsed.workflowRef
423
+ );
424
+ } catch (error) {
425
+ output.write(`[nolo] ${error instanceof Error ? error.message : String(error)}\n`);
426
+ return 1;
427
+ }
428
+ }
356
429
  let localRuntimeCwd = parsed.cwd;
357
430
  if (shouldPrepareTaskWorktree(parsed)) {
358
431
  try {
@@ -399,7 +472,7 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
399
472
  agentName: parsed.agentKey,
400
473
  agentKey: parsed.agentKey,
401
474
  serverUrl: resolveServerUrl(env),
402
- message: parsed.message,
475
+ message: prependWorkflowReferencePrompt(parsed.message, workflowReference),
403
476
  imageUrls: parsed.imageUrls.map(normalizeCliImageInput),
404
477
  scriptDir: deps.scriptDir,
405
478
  env: runEnv,
@@ -2,19 +2,11 @@ import type { MachineHeartbeat } from "./connector-experimental/protocol";
2
2
  import { detectMachineInfo } from "./connector-experimental/machineInfo";
3
3
  import { resolveAgentRuntimeDecision } from "./agent-runtime/runtimeDecision";
4
4
  import { resolveCliLocalRuntimeDbPath } from "./localRuntimeDb";
5
- import {
6
- assertMachineRunAllowed,
7
- buildMachinePermissionPromptBlock,
8
- resolveMachineRunPermissionPolicy,
9
- } from "./ai/agent/machineRunPermissions";
5
+ import { resolveMachineRunPermissionPolicy } from "./ai/agent/machineRunPermissions";
10
6
  import { resolveConnectorWebSocketTarget } from "./connectorWebSocketTarget";
11
7
  import { DEFAULT_NOLO_SERVER_URL } from "./defaultServer";
12
8
  import { resolveCliAgentKeyInput } from "./agentAliases";
13
- import {
14
- collectConnectorRunArtifact,
15
- readConnectorGitHead,
16
- resolveConnectorRunCwd,
17
- } from "./connectorRunArtifact";
9
+ import { handleConnectorRunMessage } from "./machineCommands";
18
10
 
19
11
  type EnvLike = Record<string, string | undefined>;
20
12
  type OutputLike = { write(chunk: string): unknown };
@@ -138,22 +130,6 @@ function detectLaunchableMachineInfo() {
138
130
  return detectMachineInfo({ probeLaunchable: true });
139
131
  }
140
132
 
141
- function buildConnectorCliPrompt(agentConfig: any, userInput: string) {
142
- const policy = resolveMachineRunPermissionPolicy(agentConfig);
143
- return [
144
- typeof agentConfig?.prompt === "string" ? agentConfig.prompt.trim() : "",
145
- buildMachinePermissionPromptBlock(policy),
146
- `--- User task ---\n${userInput}`,
147
- ].filter(Boolean).join("\n\n");
148
- }
149
-
150
- function normalizeConnectorRunTimeoutMs(value: unknown): number | undefined {
151
- if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
152
- return undefined;
153
- }
154
- return Math.floor(value);
155
- }
156
-
157
133
  function requiredCapabilityForAgent(agent: any) {
158
134
  if (agent?.apiSource !== "cli") return "";
159
135
  const cliProvider = String(agent?.cliProvider || "").trim();
@@ -255,64 +231,6 @@ async function defaultConnectWebSocket(url: string, options: SmokeWebSocketOptio
255
231
  });
256
232
  }
257
233
 
258
- async function handleSmokeConnectorMessage(
259
- message: string,
260
- send: (message: string) => void,
261
- executeCli: LocalCliExecutor
262
- ) {
263
- let parsed: any;
264
- try {
265
- parsed = JSON.parse(message);
266
- } catch {
267
- return;
268
- }
269
- if (parsed?.type !== "agent.run" || typeof parsed.requestId !== "string") return;
270
- const agentConfig = parsed.payload?.agentConfig ?? {};
271
- try {
272
- if (agentConfig.apiSource !== "cli") {
273
- throw new Error("Connector can only execute CLI agents. Set the agent apiSource to cli and choose a cliProvider.");
274
- }
275
- const provider = String(agentConfig.cliProvider || "copilot");
276
- const policy = resolveMachineRunPermissionPolicy(agentConfig);
277
- const userInput = String(parsed.payload?.userInput ?? "");
278
- const timeout = normalizeConnectorRunTimeoutMs(parsed.payload?.timeoutMs);
279
- assertMachineRunAllowed(userInput, policy);
280
- const cwd = resolveConnectorRunCwd({ env: process.env, policy });
281
- const baseSha = await readConnectorGitHead(cwd);
282
- const result = await executeCli(
283
- provider,
284
- buildConnectorCliPrompt(agentConfig, userInput),
285
- {
286
- model: agentConfig.model || undefined,
287
- timeout,
288
- cwd,
289
- yolo: true,
290
- }
291
- );
292
- const artifacts = await collectConnectorRunArtifact({
293
- cwd,
294
- baseSha,
295
- exitStatus: "completed",
296
- });
297
- send(JSON.stringify({
298
- type: "agent.run.result",
299
- requestId: parsed.requestId,
300
- result: {
301
- content: result.text,
302
- model: agentConfig.model ?? provider,
303
- trace: [{ role: "assistant", content: result.text }],
304
- artifacts,
305
- },
306
- }));
307
- } catch (error) {
308
- send(JSON.stringify({
309
- type: "agent.run.result",
310
- requestId: parsed.requestId,
311
- error: error instanceof Error ? error.message : String(error),
312
- }));
313
- }
314
- }
315
-
316
234
  async function readAgentRecord(args: {
317
235
  agentKey: string;
318
236
  authToken: string;
@@ -625,11 +543,21 @@ export async function runAgentSmokeCurrentCommand(
625
543
  {
626
544
  headers: { Authorization: `Bearer ${authToken}` },
627
545
  sentMessages,
628
- onMessage: (message) => handleSmokeConnectorMessage(
629
- message,
630
- (response) => sentMessages.push(response),
631
- deps.executeCli ?? defaultExecuteCli
632
- ),
546
+ onMessage: (message) => handleConnectorRunMessage(
547
+ machine,
548
+ message,
549
+ (response) => sentMessages.push(response),
550
+ deps.executeCli ?? defaultExecuteCli,
551
+ {
552
+ ...env,
553
+ NOLO_SERVER: serverUrl,
554
+ NOLO_SERVER_URL: serverUrl,
555
+ BASE_URL: serverUrl,
556
+ AUTH_TOKEN: authToken,
557
+ NOLO_MACHINE_API_KEY: authToken,
558
+ },
559
+ fetchImpl
560
+ ),
633
561
  onOpen: async () => {
634
562
  const res = await fetchImpl(`${serverUrl}/api/agent/run`, {
635
563
  method: "POST",