nolo-cli 0.1.22 → 0.1.26
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/README.md +25 -3
- package/agent-runtime/agentRecordConfig.ts +4 -0
- package/agent-runtime/hostAdapter.ts +2 -0
- package/agent-runtime/hybridRecordStore.ts +30 -9
- package/agent-runtime/index.ts +7 -0
- package/agent-runtime/localLoop.ts +14 -4
- package/agent-runtime/localToolPolicy.ts +22 -10
- package/agent-runtime/localWorkspaceTools.ts +140 -374
- package/agent-runtime/platformChatProvider.ts +3 -0
- package/agent-runtime/runtimeToolPolicy.ts +133 -0
- package/agent-runtime/types.ts +47 -0
- package/agentAliases.ts +1 -1
- package/agentCommandSupport.ts +77 -0
- package/agentInternalCommandEntries.ts +34 -0
- package/agentListCommands.ts +123 -0
- package/agentListHelpers.ts +187 -0
- package/agentMachineCommands.ts +402 -0
- package/agentRecordCommands.ts +141 -0
- package/agentRecordHelpers.ts +395 -0
- package/agentRunCommand.ts +75 -3
- package/agentRuntimeCommands.ts +7 -773
- package/ai/agent/_executeModel.ts +29 -3
- package/ai/agent/buildSystemPrompt.ts +17 -0
- package/ai/agent/cliChatClient.ts +38 -4
- package/ai/agent/cliExecutor.ts +304 -25
- package/ai/agent/createAgentSchema.ts +2 -2
- package/ai/agent/guidedCreation/assistant.ts +82 -0
- package/ai/agent/guidedCreation/capabilities.ts +84 -0
- package/ai/agent/guidedCreation/draft.ts +62 -0
- package/ai/agent/guidedCreation/types.ts +38 -0
- package/ai/agent/machineRunPermissions.ts +21 -3
- package/ai/agent/runtimeGuidance.ts +1 -1
- package/ai/agent/server/fetchPublicAgents.ts +6 -1
- package/ai/agent/streamAgentChatTurn.ts +593 -36
- package/ai/agent/web/useGuidedAgentCreation.ts +217 -0
- package/ai/ai.locale.ts +14 -2
- package/ai/chat/fetchUtils.native.ts +2 -0
- package/ai/chat/fetchUtils.ts +2 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +82 -1
- package/ai/chat/sendOpenAIResponseRequest.ts +65 -0
- package/ai/llm/getPricing.ts +14 -0
- package/ai/llm/kimi.ts +4 -1
- package/ai/llm/modelAvatar.ts +1 -0
- package/ai/llm/providers.ts +4 -1
- package/ai/llm/reasoningModels.ts +1 -0
- package/ai/taskRun/taskRunProtocol.ts +25 -10
- package/ai/token/modelUsageQuery.ts +17 -0
- package/ai/tools/agent/agentTools.ts +38 -0
- package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
- package/ai/tools/agent/startAgentDialogTool.ts +53 -0
- package/ai/tools/checkEnvTool.ts +52 -20
- package/ai/tools/convertMarxistsBookTool.ts +103 -0
- package/ai/tools/{execBashTool.ts → execShellTool.ts} +44 -161
- package/ai/tools/index.ts +24 -14
- package/ai/tools/marxistsOfflineBook.ts +253 -0
- package/ai/tools/modelUsageTools.ts +9 -2
- package/ai/tools/readTool.ts +31 -0
- package/ai/tools/toolApiClient.ts +35 -1
- package/ai/tools/toolNameAliases.ts +13 -0
- package/cliCommandAdapters.ts +28 -0
- package/cliCommandDispatch.ts +66 -0
- package/cliCommandFactories.ts +34 -0
- package/cliCommandTypes.ts +49 -0
- package/cliEnvHelpers.ts +44 -0
- package/client/agentRun.test.ts +330 -24
- package/client/agentRun.ts +117 -35
- package/client/hybridRecordStore.test.ts +42 -0
- package/client/hybridRecordStore.ts +11 -0
- package/client/localRuntimeAdapter.test.ts +191 -21
- package/client/localRuntimeAdapter.ts +99 -10
- package/client/localRuntimeDryRun.test.ts +5 -2
- package/client/taskRunPrompt.ts +1 -0
- package/commandRegistry.ts +11 -61
- package/connector-experimental/capabilities.ts +1 -0
- package/database/actions/common.ts +4 -3
- package/database/config.ts +19 -0
- package/database/server/coreDataProxy.ts +43 -26
- package/index.ts +54 -191
- package/internalCommandEntries.ts +12 -0
- package/machineCommands.ts +688 -64
- package/offlineMarxistsAgentCommand.ts +314 -0
- package/package.json +22 -2
- package/render/canvas/canvasEditContext.ts +127 -0
- package/render/canvas/canvasRuntime.ts +57 -0
- package/render/canvas/canvasSnapshotParser.ts +76 -0
- package/render/canvas/canvasTree.ts +308 -0
- package/render/canvas/types.ts +46 -0
- package/render/layout/deleteBehavior.ts +52 -0
- package/render/layout/mainLayoutSidebar.ts +17 -0
- package/render/layout/mainLayoutViewMode.ts +56 -0
- package/render/layout/topbarUtils.ts +87 -0
- package/render/layout/useDevReloadPending.ts +30 -0
- package/render/page/createPageAction.ts +183 -0
- package/render/page/docSlice.ts +468 -0
- package/render/page/server/createPage.ts +174 -0
- package/render/page/server/handleCreatePage.ts +91 -0
- package/render/page/server/index.ts +4 -0
- package/render/page/types.ts +17 -0
- package/render/page/useKeyboardSave.ts +48 -0
- package/render/styles/zIndex.ts +12 -0
- package/render/surf/WeatherIconStyles.ts +17 -0
- package/render/surf/color.ts +9 -0
- package/render/surf/config.ts +46 -0
- package/render/surf/screens/style.ts +1 -0
- package/render/surf/styles/ToggleButtonStyles.ts +8 -0
- package/render/surf/utils/groupedWeatherData.ts +32 -0
- package/render/surf/weatherUtils.ts +50 -0
- package/render/table/activityColumns.ts +6 -0
- package/render/table/createTableAction.ts +270 -0
- package/render/table/deleteTableAction.ts +129 -0
- package/render/table/fetchAndCacheTableRows.ts +174 -0
- package/render/table/tableSlice.ts +1106 -0
- package/render/table/tableView.ts +289 -0
- package/render/table/toolValueUtils.ts +363 -0
- package/render/table/types.ts +252 -0
- package/render/table/useCreateTable.ts +72 -0
- package/render/table/useTable.ts +61 -0
- package/render/table/utils/tableSerialization.ts +50 -0
- package/render/web/elements/artifactPreviewCode.ts +43 -0
- package/render/web/elements/artifactRuntimePreload.ts +52 -0
- package/render/web/elements/codeBlockAutoPreview.ts +10 -0
- package/render/web/elements/mermaidPreview.ts +21 -0
- package/render/web/ui/useInlineEdit.ts +135 -0
- package/runtimeDoctorCommands.ts +118 -0
- package/scriptCommandEntries.ts +71 -0
- package/systemInternalCommandEntries.ts +71 -0
- package/tableCommands.ts +42 -5
- package/workflowInternalCommandEntries.ts +11 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { resolveCliAgentKeyInput } from "./agentAliases";
|
|
3
|
+
import type { CliKvDb } from "./client/hybridRecordStore";
|
|
4
|
+
import { buildLocalAgentLookupKeys, shouldReadAgentKeyRemotely } from "./client/localAgentRecords";
|
|
5
|
+
import {
|
|
6
|
+
parseUserIdFromAuthToken,
|
|
7
|
+
readOption,
|
|
8
|
+
resolveAuthToken,
|
|
9
|
+
resolveServerCandidates,
|
|
10
|
+
resolveServerUrl,
|
|
11
|
+
type EnvLike,
|
|
12
|
+
} from "./cliEnvHelpers";
|
|
13
|
+
|
|
14
|
+
const PROVIDER_COPY_FIELDS = [
|
|
15
|
+
"apiSource",
|
|
16
|
+
"provider",
|
|
17
|
+
"cliProvider",
|
|
18
|
+
"model",
|
|
19
|
+
"customProviderUrl",
|
|
20
|
+
"apiKey",
|
|
21
|
+
"useServerProxy",
|
|
22
|
+
"inputPrice",
|
|
23
|
+
"outputPrice",
|
|
24
|
+
] as const;
|
|
25
|
+
|
|
26
|
+
function readRepeatedOption(args: string[], flag: string) {
|
|
27
|
+
const values: string[] = [];
|
|
28
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
29
|
+
if (args[index] !== flag || !args[index + 1]) continue;
|
|
30
|
+
values.push(args[index + 1]);
|
|
31
|
+
index += 1;
|
|
32
|
+
}
|
|
33
|
+
return values;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseJsonishValue(raw: string) {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
} catch {
|
|
40
|
+
return raw;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function curlFetch(url: string, init?: RequestInit): Promise<Response> {
|
|
45
|
+
const method = init?.method ?? "GET";
|
|
46
|
+
const headers = new Headers(init?.headers ?? {});
|
|
47
|
+
const command = ["curl", "-sS", "-L", "-X", method];
|
|
48
|
+
|
|
49
|
+
headers.forEach((value, key) => {
|
|
50
|
+
command.push("-H", `${key}: ${value}`);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
if (typeof init?.body === "string" && init.body.length > 0) {
|
|
54
|
+
command.push("--data", init.body);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
command.push("-w", "\n__NOLO_STATUS__:%{http_code}", url);
|
|
58
|
+
const proc = Bun.spawn(command, { stdout: "pipe", stderr: "pipe" });
|
|
59
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
60
|
+
new Response(proc.stdout).text(),
|
|
61
|
+
new Response(proc.stderr).text(),
|
|
62
|
+
proc.exited,
|
|
63
|
+
]);
|
|
64
|
+
if (exitCode !== 0) {
|
|
65
|
+
throw new Error(stderr.trim() || `curl failed for ${url}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const marker = "\n__NOLO_STATUS__:";
|
|
69
|
+
const markerIndex = stdout.lastIndexOf(marker);
|
|
70
|
+
const body = markerIndex >= 0 ? stdout.slice(0, markerIndex) : stdout;
|
|
71
|
+
const statusText = markerIndex >= 0 ? stdout.slice(markerIndex + marker.length).trim() : "0";
|
|
72
|
+
const status = Number(statusText);
|
|
73
|
+
return new Response(body, {
|
|
74
|
+
status: Number.isFinite(status) ? status : 0,
|
|
75
|
+
headers: { "Content-Type": headers.get("Content-Type") ?? "application/json" },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function shouldUseCurlTransportFallback(error: unknown) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
return /Unable to connect|ConnectionRefused|ECONNREFUSED|Failed to connect|Was there a typo|timed out|Timeout|handshake|certificate|ECONNRESET|socket|network/i.test(message);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function fetchWithTransportFallback(
|
|
85
|
+
url: string,
|
|
86
|
+
init: RequestInit,
|
|
87
|
+
options: { fallbackFetchImpl?: typeof fetch; fetchImpl: typeof fetch }
|
|
88
|
+
): Promise<Response> {
|
|
89
|
+
try {
|
|
90
|
+
return await options.fetchImpl(url, init);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (!shouldUseCurlTransportFallback(error)) throw error;
|
|
93
|
+
if (options.fallbackFetchImpl) {
|
|
94
|
+
return options.fallbackFetchImpl(url, init);
|
|
95
|
+
}
|
|
96
|
+
if (options.fetchImpl !== fetch) throw error;
|
|
97
|
+
return curlFetch(url, init);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function readAgentRecord(args: {
|
|
102
|
+
agentKey: string;
|
|
103
|
+
authToken: string;
|
|
104
|
+
fallbackFetchImpl?: typeof fetch;
|
|
105
|
+
fetchImpl: typeof fetch;
|
|
106
|
+
serverUrl: string;
|
|
107
|
+
}) {
|
|
108
|
+
const res = await fetchWithTransportFallback(
|
|
109
|
+
`${args.serverUrl}/api/v1/db/read/${encodeURIComponent(args.agentKey)}`,
|
|
110
|
+
{
|
|
111
|
+
method: "GET",
|
|
112
|
+
headers: { Authorization: `Bearer ${args.authToken}` },
|
|
113
|
+
},
|
|
114
|
+
args
|
|
115
|
+
);
|
|
116
|
+
const data = await res.json().catch(() => ({}));
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
throw new Error(`read failed: HTTP ${res.status} ${JSON.stringify(data)}`);
|
|
119
|
+
}
|
|
120
|
+
return data?.data ?? data;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function readDbRecord(args: {
|
|
124
|
+
dbKey: string;
|
|
125
|
+
authToken: string;
|
|
126
|
+
fallbackFetchImpl?: typeof fetch;
|
|
127
|
+
fetchImpl: typeof fetch;
|
|
128
|
+
serverUrl: string;
|
|
129
|
+
}) {
|
|
130
|
+
const res = await fetchWithTransportFallback(
|
|
131
|
+
`${args.serverUrl}/api/v1/db/read/${encodeURIComponent(args.dbKey)}`,
|
|
132
|
+
{
|
|
133
|
+
method: "GET",
|
|
134
|
+
headers: { Authorization: `Bearer ${args.authToken}` },
|
|
135
|
+
},
|
|
136
|
+
args
|
|
137
|
+
);
|
|
138
|
+
const data = await res.json().catch(() => ({}));
|
|
139
|
+
if (!res.ok) {
|
|
140
|
+
throw new Error(`read failed: HTTP ${res.status} ${JSON.stringify(data)}`);
|
|
141
|
+
}
|
|
142
|
+
return data?.data ?? data;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function queryUserRecords(args: {
|
|
146
|
+
authToken: string;
|
|
147
|
+
fallbackFetchImpl?: typeof fetch;
|
|
148
|
+
fetchImpl: typeof fetch;
|
|
149
|
+
serverUrl: string;
|
|
150
|
+
userId: string;
|
|
151
|
+
type: "agent" | "cybot";
|
|
152
|
+
}) {
|
|
153
|
+
const res = await fetchWithTransportFallback(
|
|
154
|
+
`${args.serverUrl}/api/v1/db/query/${encodeURIComponent(args.userId)}`,
|
|
155
|
+
{
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: {
|
|
158
|
+
Authorization: `Bearer ${args.authToken}`,
|
|
159
|
+
"Content-Type": "application/json",
|
|
160
|
+
},
|
|
161
|
+
body: JSON.stringify({ type: args.type }),
|
|
162
|
+
},
|
|
163
|
+
args
|
|
164
|
+
);
|
|
165
|
+
const data = await res.json().catch(() => ({}));
|
|
166
|
+
if (!res.ok) {
|
|
167
|
+
throw new Error(`query failed: HTTP ${res.status} ${JSON.stringify(data)}`);
|
|
168
|
+
}
|
|
169
|
+
return Array.isArray(data?.data?.data)
|
|
170
|
+
? data.data.data
|
|
171
|
+
: Array.isArray(data?.data)
|
|
172
|
+
? data.data
|
|
173
|
+
: [];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export async function writeAgentRecord(args: {
|
|
177
|
+
agentKey: string;
|
|
178
|
+
authToken: string;
|
|
179
|
+
fallbackFetchImpl?: typeof fetch;
|
|
180
|
+
fetchImpl: typeof fetch;
|
|
181
|
+
serverUrl: string;
|
|
182
|
+
userId: string;
|
|
183
|
+
record: Record<string, any>;
|
|
184
|
+
}) {
|
|
185
|
+
const res = await fetchWithTransportFallback(`${args.serverUrl}/api/v1/db/write/`, {
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers: {
|
|
188
|
+
Authorization: `Bearer ${args.authToken}`,
|
|
189
|
+
"Content-Type": "application/json",
|
|
190
|
+
},
|
|
191
|
+
body: JSON.stringify({
|
|
192
|
+
customKey: args.agentKey,
|
|
193
|
+
userId: args.userId,
|
|
194
|
+
data: {
|
|
195
|
+
...args.record,
|
|
196
|
+
dbKey: args.agentKey,
|
|
197
|
+
},
|
|
198
|
+
}),
|
|
199
|
+
}, args);
|
|
200
|
+
const data = await res.json().catch(() => ({}));
|
|
201
|
+
if (!res.ok) {
|
|
202
|
+
throw new Error(`write failed: HTTP ${res.status} ${JSON.stringify(data)}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function resolveAgentRecordFromHybridStore(args: {
|
|
207
|
+
agentInput: string;
|
|
208
|
+
env: EnvLike;
|
|
209
|
+
db: CliKvDb;
|
|
210
|
+
fetchImpl: typeof fetch;
|
|
211
|
+
fallbackFetchImpl?: typeof fetch;
|
|
212
|
+
}) {
|
|
213
|
+
const agentKey = resolveCliAgentKeyInput(args.agentInput);
|
|
214
|
+
const authToken = resolveAuthToken(args.env);
|
|
215
|
+
const defaultServerUrl = resolveServerUrl(args.env);
|
|
216
|
+
|
|
217
|
+
for (const key of buildLocalAgentLookupKeys({
|
|
218
|
+
agentRef: agentKey,
|
|
219
|
+
userId: parseUserIdFromAuthToken(authToken) || "local",
|
|
220
|
+
})) {
|
|
221
|
+
try {
|
|
222
|
+
const record = await args.db.get(key);
|
|
223
|
+
if (record && typeof record === "object") {
|
|
224
|
+
return {
|
|
225
|
+
agentKey: key,
|
|
226
|
+
record,
|
|
227
|
+
source: record?.serverOrigin ? "remote-cache" : "local-cache",
|
|
228
|
+
} as const;
|
|
229
|
+
}
|
|
230
|
+
} catch {
|
|
231
|
+
// local miss
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!shouldReadAgentKeyRemotely(key)) continue;
|
|
235
|
+
for (const serverUrl of resolveServerCandidates(args.env, defaultServerUrl)) {
|
|
236
|
+
try {
|
|
237
|
+
const remoteRecord = await readAgentRecord({
|
|
238
|
+
agentKey: key,
|
|
239
|
+
authToken,
|
|
240
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
241
|
+
fetchImpl: args.fetchImpl,
|
|
242
|
+
serverUrl,
|
|
243
|
+
});
|
|
244
|
+
const cached = { ...remoteRecord, dbKey: key, serverOrigin: serverUrl };
|
|
245
|
+
await args.db.put(key, cached);
|
|
246
|
+
return {
|
|
247
|
+
agentKey: key,
|
|
248
|
+
record: cached,
|
|
249
|
+
source: "remote-cache",
|
|
250
|
+
} as const;
|
|
251
|
+
} catch {
|
|
252
|
+
// try next
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function normalizeAgentRecordForOutput(agentKey: string, authToken: string, agent: any) {
|
|
261
|
+
return {
|
|
262
|
+
agentKey,
|
|
263
|
+
baseUrl: agent?.serverOrigin ?? null,
|
|
264
|
+
name: agent?.name,
|
|
265
|
+
greeting: agent?.greeting,
|
|
266
|
+
model: agent?.model,
|
|
267
|
+
provider: agent?.provider ?? agent?.apiSource ?? null,
|
|
268
|
+
cliProvider: agent?.cliProvider ?? null,
|
|
269
|
+
customProviderUrl: agent?.customProviderUrl ?? null,
|
|
270
|
+
tools: agent?.tools ?? [],
|
|
271
|
+
isPublic: agent?.isPublic,
|
|
272
|
+
authUserId: parseUserIdFromAuthToken(authToken),
|
|
273
|
+
userId: agent?.userId,
|
|
274
|
+
record: agent,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function parseAgentUpdateArgs(args: string[]) {
|
|
279
|
+
const agentInput = readOption(args, "--agent") ?? readOption(args, "--id") ?? args[0]?.trim();
|
|
280
|
+
if (!agentInput || agentInput === "--help" || agentInput === "-h") return null;
|
|
281
|
+
|
|
282
|
+
const updates: Record<string, unknown> = {};
|
|
283
|
+
const model = readOption(args, "--model");
|
|
284
|
+
const cliProvider = readOption(args, "--cli-provider");
|
|
285
|
+
const apiSource = readOption(args, "--api-source");
|
|
286
|
+
const provider = readOption(args, "--provider");
|
|
287
|
+
const prompt = readOption(args, "--prompt");
|
|
288
|
+
const promptFile = readOption(args, "--prompt-file");
|
|
289
|
+
const promptDoc = readOption(args, "--prompt-doc");
|
|
290
|
+
const tools = readOption(args, "--tools");
|
|
291
|
+
const copyProviderFrom = readOption(args, "--copy-provider-from");
|
|
292
|
+
const name = readOption(args, "--name");
|
|
293
|
+
|
|
294
|
+
const promptSources = [prompt, promptFile, promptDoc].filter(Boolean);
|
|
295
|
+
if (promptSources.length > 1) {
|
|
296
|
+
throw new Error("--prompt / --prompt-file / --prompt-doc are mutually exclusive.");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (model) updates.model = model;
|
|
300
|
+
if (cliProvider) updates.cliProvider = cliProvider;
|
|
301
|
+
if (apiSource) updates.apiSource = apiSource;
|
|
302
|
+
if (provider) updates.provider = provider;
|
|
303
|
+
if (prompt) updates.prompt = prompt;
|
|
304
|
+
if (promptFile) updates.prompt = readFileSync(promptFile, "utf8");
|
|
305
|
+
if (tools) updates.tools = parseJsonishValue(tools);
|
|
306
|
+
if (name) updates.name = name;
|
|
307
|
+
|
|
308
|
+
for (const entry of readRepeatedOption(args, "--field")) {
|
|
309
|
+
const index = entry.indexOf("=");
|
|
310
|
+
if (index <= 0) {
|
|
311
|
+
throw new Error(`--field must use key=value, received ${JSON.stringify(entry)}`);
|
|
312
|
+
}
|
|
313
|
+
updates[entry.slice(0, index)] = parseJsonishValue(entry.slice(index + 1));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (Object.keys(updates).length === 0) {
|
|
317
|
+
if (!promptDoc && !copyProviderFrom) {
|
|
318
|
+
throw new Error("No updates provided. Use --model / --cli-provider / --api-source / --field key=value.");
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return { agentInput, updates, promptDoc, copyProviderFrom };
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export async function buildUpdatedAgentRecord(args: {
|
|
326
|
+
parsed: NonNullable<ReturnType<typeof parseAgentUpdateArgs>>;
|
|
327
|
+
env: EnvLike;
|
|
328
|
+
db: CliKvDb;
|
|
329
|
+
fetchImpl: typeof fetch;
|
|
330
|
+
fallbackFetchImpl?: typeof fetch;
|
|
331
|
+
authToken: string;
|
|
332
|
+
}) {
|
|
333
|
+
const cached = await resolveAgentRecordFromHybridStore({
|
|
334
|
+
agentInput: args.parsed.agentInput,
|
|
335
|
+
env: args.env,
|
|
336
|
+
db: args.db,
|
|
337
|
+
fetchImpl: args.fetchImpl,
|
|
338
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
339
|
+
});
|
|
340
|
+
const agentKey = cached?.agentKey ?? resolveCliAgentKeyInput(args.parsed.agentInput);
|
|
341
|
+
const serverUrl = cached?.record?.serverOrigin ?? resolveServerUrl(args.env);
|
|
342
|
+
const currentRecord = await readAgentRecord({
|
|
343
|
+
agentKey,
|
|
344
|
+
authToken: args.authToken,
|
|
345
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
346
|
+
fetchImpl: args.fetchImpl,
|
|
347
|
+
serverUrl,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
if (args.parsed.copyProviderFrom) {
|
|
351
|
+
const providerSource = await resolveAgentRecordFromHybridStore({
|
|
352
|
+
agentInput: args.parsed.copyProviderFrom,
|
|
353
|
+
env: args.env,
|
|
354
|
+
db: args.db,
|
|
355
|
+
fetchImpl: args.fetchImpl,
|
|
356
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
357
|
+
});
|
|
358
|
+
if (!providerSource) {
|
|
359
|
+
throw new Error(`provider source agent not found: ${args.parsed.copyProviderFrom}`);
|
|
360
|
+
}
|
|
361
|
+
for (const field of PROVIDER_COPY_FIELDS) {
|
|
362
|
+
if (field in providerSource.record) {
|
|
363
|
+
args.parsed.updates[field] = providerSource.record[field];
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (args.parsed.promptDoc) {
|
|
369
|
+
const promptDoc = await readDbRecord({
|
|
370
|
+
dbKey: args.parsed.promptDoc,
|
|
371
|
+
authToken: args.authToken,
|
|
372
|
+
fallbackFetchImpl: args.fallbackFetchImpl,
|
|
373
|
+
fetchImpl: args.fetchImpl,
|
|
374
|
+
serverUrl,
|
|
375
|
+
});
|
|
376
|
+
const promptBody = typeof promptDoc?.content === "string" ? promptDoc.content : "";
|
|
377
|
+
if (!promptBody.trim()) {
|
|
378
|
+
throw new Error(`prompt doc is empty or unreadable: ${args.parsed.promptDoc}`);
|
|
379
|
+
}
|
|
380
|
+
args.parsed.updates.prompt = promptBody;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const nextRecord = {
|
|
384
|
+
...currentRecord,
|
|
385
|
+
...args.parsed.updates,
|
|
386
|
+
updatedAt: Date.now(),
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
return {
|
|
390
|
+
agentKey,
|
|
391
|
+
serverUrl,
|
|
392
|
+
nextRecord,
|
|
393
|
+
updates: args.parsed.updates,
|
|
394
|
+
};
|
|
395
|
+
}
|
package/agentRunCommand.ts
CHANGED
|
@@ -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,11 +65,17 @@ 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";
|
|
69
78
|
const DEFAULT_TEST_ROOT_DIALOG_KEY = `dialog-b2e06f801f-${DEFAULT_TEST_ROOT_DIALOG_ID}`;
|
|
70
|
-
const DEFAULT_AGENT_SPACE_ID = "01KKY77TT0DA9NY7TNW3R7255N";
|
|
71
79
|
const DEFAULT_TEST_DIALOG_CATEGORY_ID = "test-dialogs";
|
|
72
80
|
|
|
73
81
|
function readFlagValue(args: string[], flag: string) {
|
|
@@ -171,6 +179,7 @@ export function parseAgentRunArgs(
|
|
|
171
179
|
...readRepeatedFlagValues(args, "--image-url"),
|
|
172
180
|
];
|
|
173
181
|
const useTaskWorktree = args.includes("--worktree-task");
|
|
182
|
+
const workflowRef = readFlagValue(args, "--workflow");
|
|
174
183
|
const taskRowDbKey = readFlagValue(args, "--task-row-dbkey");
|
|
175
184
|
const artifactIds = readFlagValue(args, "--artifact-ids")
|
|
176
185
|
?.split(",")
|
|
@@ -183,6 +192,7 @@ export function parseAgentRunArgs(
|
|
|
183
192
|
allowShell: args.includes("--dangerously-allow-shell"),
|
|
184
193
|
traceTools: args.includes("--trace-tools"),
|
|
185
194
|
useTaskWorktree,
|
|
195
|
+
...(workflowRef ? { workflowRef } : {}),
|
|
186
196
|
...(taskRowDbKey
|
|
187
197
|
? {
|
|
188
198
|
taskRunContext: {
|
|
@@ -198,7 +208,7 @@ export function parseAgentRunArgs(
|
|
|
198
208
|
noStream: args.includes("--no-stream"),
|
|
199
209
|
noDefaultTestRoot,
|
|
200
210
|
...(continueDialogId ? { continueDialogId } : {}),
|
|
201
|
-
...(!continueDialogId ? { spaceId
|
|
211
|
+
...(!continueDialogId && spaceId ? { spaceId } : {}),
|
|
202
212
|
...(category ? { category } : !continueDialogId && !noDefaultTestRoot ? { category: DEFAULT_TEST_DIALOG_CATEGORY_ID } : {}),
|
|
203
213
|
...(inheritedRef?.dialogKey ? { inheritedFromDialogKey: inheritedRef.dialogKey } : {}),
|
|
204
214
|
...(inheritedRef?.dialogId ? { parentDialogId: inheritedRef.dialogId } : {}),
|
|
@@ -208,6 +218,57 @@ export function parseAgentRunArgs(
|
|
|
208
218
|
};
|
|
209
219
|
}
|
|
210
220
|
|
|
221
|
+
function workflowRefToCandidatePath(cwd: string, ref: string) {
|
|
222
|
+
const normalized = ref.trim();
|
|
223
|
+
if (!normalized) return "";
|
|
224
|
+
if (normalized.endsWith(".md") || normalized.includes("/") || normalized.includes("\\")) {
|
|
225
|
+
const directPath = resolve(cwd, normalized);
|
|
226
|
+
if (existsSync(directPath)) return directPath;
|
|
227
|
+
}
|
|
228
|
+
const fileName = normalized.replace(/[^a-zA-Z0-9\u4e00-\u9fa5]+/g, "-").replace(/^-+|-+$/g, "");
|
|
229
|
+
return resolve(cwd, "docs", "workflows", `${fileName}.md`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export async function resolveWorkflowReference(ref: string, cwd = process.cwd()): Promise<ResolvedWorkflowReference> {
|
|
233
|
+
const path = workflowRefToCandidatePath(cwd, ref);
|
|
234
|
+
if (!path || !existsSync(path)) {
|
|
235
|
+
throw new Error(`Workflow reference not found: ${ref}`);
|
|
236
|
+
}
|
|
237
|
+
const markdown = readFileSync(path, "utf8");
|
|
238
|
+
const parsed = parseSkillDocProtocol(markdown);
|
|
239
|
+
return {
|
|
240
|
+
ref,
|
|
241
|
+
content: parsed.content,
|
|
242
|
+
...(parsed.meta?.workflowConfig ? { config: parsed.meta.workflowConfig } : {}),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function prependWorkflowReferencePrompt(message: string, workflow?: ResolvedWorkflowReference): string {
|
|
247
|
+
if (!workflow) return message;
|
|
248
|
+
const config = workflow.config;
|
|
249
|
+
return [
|
|
250
|
+
"AI-native workflow reference:",
|
|
251
|
+
"- This reference is guidance for the agent, not a central workflow engine.",
|
|
252
|
+
`- ref: ${workflow.ref}`,
|
|
253
|
+
...(config?.id ? [`- id: ${config.id}`] : []),
|
|
254
|
+
...(config?.name ? [`- name: ${config.name}`] : []),
|
|
255
|
+
...(config?.defaultAgent ? [`- suggested defaultAgent: ${config.defaultAgent}`] : []),
|
|
256
|
+
...(config?.inputs?.length ? [`- inputs: ${config.inputs.join(", ")}`] : []),
|
|
257
|
+
...(config?.recommendedTools?.length ? [`- recommendedTools: ${config.recommendedTools.join(", ")}`] : []),
|
|
258
|
+
...(config?.requiredTools?.length ? [`- requiredTools: ${config.requiredTools.join(", ")}`] : []),
|
|
259
|
+
...(config?.requiredOutputs?.length ? [`- requiredOutputs: ${config.requiredOutputs.join(", ")}`] : []),
|
|
260
|
+
...(config?.gates?.length ? [`- gates: ${config.gates.join(", ")}`] : []),
|
|
261
|
+
...(config?.contextStrategy ? [`- contextStrategy: ${config.contextStrategy}`] : []),
|
|
262
|
+
...(config?.failureProtocol ? [`- failureProtocol: ${config.failureProtocol}`] : []),
|
|
263
|
+
"",
|
|
264
|
+
"Reference body:",
|
|
265
|
+
workflow.content,
|
|
266
|
+
"",
|
|
267
|
+
"User task:",
|
|
268
|
+
message,
|
|
269
|
+
].join("\n");
|
|
270
|
+
}
|
|
271
|
+
|
|
211
272
|
function parseDialogReference(rawInput: string) {
|
|
212
273
|
const normalized = rawInput.trim();
|
|
213
274
|
if (normalized.startsWith("dialog-")) {
|
|
@@ -353,6 +414,17 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
|
|
|
353
414
|
}
|
|
354
415
|
|
|
355
416
|
const runner = deps.runner ?? runAgentTurn;
|
|
417
|
+
let workflowReference: ResolvedWorkflowReference | undefined;
|
|
418
|
+
if (parsed.workflowRef) {
|
|
419
|
+
try {
|
|
420
|
+
workflowReference = await (deps.resolveWorkflowReference ?? resolveWorkflowReference)(
|
|
421
|
+
parsed.workflowRef
|
|
422
|
+
);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
output.write(`[nolo] ${error instanceof Error ? error.message : String(error)}\n`);
|
|
425
|
+
return 1;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
356
428
|
let localRuntimeCwd = parsed.cwd;
|
|
357
429
|
if (shouldPrepareTaskWorktree(parsed)) {
|
|
358
430
|
try {
|
|
@@ -399,7 +471,7 @@ export async function runAgentRunCommand(args: string[], deps: AgentRunCommandDe
|
|
|
399
471
|
agentName: parsed.agentKey,
|
|
400
472
|
agentKey: parsed.agentKey,
|
|
401
473
|
serverUrl: resolveServerUrl(env),
|
|
402
|
-
message: parsed.message,
|
|
474
|
+
message: prependWorkflowReferencePrompt(parsed.message, workflowReference),
|
|
403
475
|
imageUrls: parsed.imageUrls.map(normalizeCliImageInput),
|
|
404
476
|
scriptDir: deps.scriptDir,
|
|
405
477
|
env: runEnv,
|