neoctl 0.2.14 → 0.2.16
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/dist/repl/index.js +36 -5
- package/dist/repl/index.js.map +1 -1
- package/dist/web/index.d.ts +8 -1
- package/dist/web/index.js +219 -13
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
package/dist/web/index.d.ts
CHANGED
|
@@ -52,17 +52,19 @@ interface UiLine {
|
|
|
52
52
|
title?: string;
|
|
53
53
|
bodyTitle?: string;
|
|
54
54
|
titleStatus?: "success" | "failure";
|
|
55
|
+
toolKind?: string;
|
|
55
56
|
format?: "markdown" | "ansi" | "plain" | "diff";
|
|
56
57
|
previewStyle?: "summary";
|
|
57
58
|
summaryMaxLines?: number;
|
|
58
59
|
live?: boolean;
|
|
59
60
|
collapsible?: boolean;
|
|
60
61
|
image?: UiLineImage;
|
|
62
|
+
artifact?: unknown;
|
|
61
63
|
}
|
|
62
64
|
interface UiActiveTool {
|
|
63
65
|
id: string;
|
|
64
66
|
name: string;
|
|
65
|
-
|
|
67
|
+
kind: string;
|
|
66
68
|
startedAt: number;
|
|
67
69
|
}
|
|
68
70
|
interface UiStatus {
|
|
@@ -96,6 +98,10 @@ export interface CreateWebRuntimeOptions {
|
|
|
96
98
|
cwd?: string;
|
|
97
99
|
/** Additional tools to register in the web runtime. */
|
|
98
100
|
externalTools?: readonly Tool[];
|
|
101
|
+
/** Additional shared skill roots. Defaults can also be provided with NEO_SKILL_ROOTS. */
|
|
102
|
+
skillRoots?: readonly string[];
|
|
103
|
+
/** Writable root for skill_create/skill_update. Defaults to NEO_SKILL_CREATE_ROOT or ~/.neoctl/skills. */
|
|
104
|
+
skillCreateRoot?: string;
|
|
99
105
|
}
|
|
100
106
|
export interface WebRuntimeScope {
|
|
101
107
|
/** Browser-tab or client-instance identifier. Omit for the legacy singleton runtime. */
|
|
@@ -271,6 +277,7 @@ export declare class WebRepl {
|
|
|
271
277
|
private handleCommandOrPrompt;
|
|
272
278
|
private runCompaction;
|
|
273
279
|
private send;
|
|
280
|
+
private sendSerialized;
|
|
274
281
|
private broadcastSync;
|
|
275
282
|
}
|
|
276
283
|
export {};
|
package/dist/web/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import http from "node:http";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { createReadStream, existsSync, readFileSync } from "node:fs";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
8
|
import { QueryEngine } from "../core/query-engine.js";
|
|
@@ -28,9 +28,14 @@ import { createTaskTools } from "../tasks/task-tools.js";
|
|
|
28
28
|
import { TaskStore } from "../tasks/task-store.js";
|
|
29
29
|
import { parseReplCommand, helpText, replCommandDefinitions } from "../repl/commands.js";
|
|
30
30
|
import { writeSessionMarkdownExport } from "../session/session-export.js";
|
|
31
|
+
import { DefaultContextManager } from "../context/context-manager.js";
|
|
32
|
+
import { buildEffectiveSystemPrompt } from "../context/prompts.js";
|
|
31
33
|
import { WEB_HTML } from "./html.js";
|
|
32
34
|
import { openDirectory } from "../open-directory.js";
|
|
33
|
-
import {
|
|
35
|
+
import { getNeoctlHome } from "../paths.js";
|
|
36
|
+
import { FileSystemSkillCatalog } from "../skills/skill-filesystem.js";
|
|
37
|
+
import { createSkillTool } from "../skills/skill-tool.js";
|
|
38
|
+
import { createSkillManagementTools } from "../skills/skill-management-tools.js";
|
|
34
39
|
const require = createRequire(import.meta.url);
|
|
35
40
|
const markedPackageDir = path.dirname(require.resolve("marked/package.json"));
|
|
36
41
|
const highlightPackageDir = path.dirname(require.resolve("@highlightjs/cdn-assets/package.json"));
|
|
@@ -109,6 +114,85 @@ function parseWebArgs(argv) {
|
|
|
109
114
|
port = 3000;
|
|
110
115
|
return { host, port: Math.round(port) };
|
|
111
116
|
}
|
|
117
|
+
class SkillCatalogContextManager {
|
|
118
|
+
catalog;
|
|
119
|
+
base;
|
|
120
|
+
constructor(catalog, base = new DefaultContextManager()) {
|
|
121
|
+
this.catalog = catalog;
|
|
122
|
+
this.base = base;
|
|
123
|
+
}
|
|
124
|
+
async build(input) {
|
|
125
|
+
const runtimeContext = await this.base.build(input);
|
|
126
|
+
const skillSection = await buildSkillCatalogPromptSection(this.catalog);
|
|
127
|
+
if (!skillSection)
|
|
128
|
+
return runtimeContext;
|
|
129
|
+
const promptSections = [...runtimeContext.promptSections, skillSection];
|
|
130
|
+
return {
|
|
131
|
+
...runtimeContext,
|
|
132
|
+
promptSections,
|
|
133
|
+
systemPrompt: buildEffectiveSystemPrompt(promptSections, input),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function buildSkillCatalogPromptSection(catalog) {
|
|
138
|
+
const skills = await catalog.list();
|
|
139
|
+
if (skills.length === 0)
|
|
140
|
+
return undefined;
|
|
141
|
+
const visible = skills.slice(0, 80);
|
|
142
|
+
const lines = visible.map((skill) => {
|
|
143
|
+
const tags = skill.tags?.length ? `; tags=${skill.tags.join(",")}` : "";
|
|
144
|
+
const tools = skill.allowedTools?.length ? `; allowedTools=${skill.allowedTools.join(",")}` : "";
|
|
145
|
+
return `- ${skill.name}: ${skill.description} (execution=${skill.execution}${tags}${tools})`;
|
|
146
|
+
});
|
|
147
|
+
if (skills.length > visible.length)
|
|
148
|
+
lines.push(`- ... ${skills.length - visible.length} more skills available; use skill_list for the full catalog.`);
|
|
149
|
+
return {
|
|
150
|
+
name: "Available Skills",
|
|
151
|
+
cacheStable: false,
|
|
152
|
+
content: [
|
|
153
|
+
"Reusable skills are available through the `skill` tool and the /skill UI command.",
|
|
154
|
+
"When the user's task matches a skill name, description, tags, or domain capability, proactively call the `skill` tool before doing the work directly.",
|
|
155
|
+
"Do not wait for the user to explicitly say 'use skill'. Use skill_list/skill_read if you need to inspect details.",
|
|
156
|
+
"Available skill catalog:",
|
|
157
|
+
...lines,
|
|
158
|
+
].join("\n"),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function resolveSkillCatalogRoots(cwd, extraRoots = [], createRoot) {
|
|
162
|
+
const userRoot = path.resolve(process.env.NEO_SKILL_CREATE_ROOT || createRoot || path.join(getNeoctlHome(), "skills"));
|
|
163
|
+
const configuredRoots = splitPathList(process.env.NEO_SKILL_ROOTS);
|
|
164
|
+
const workspaceRoot = path.resolve(cwd, ".neo", "skills");
|
|
165
|
+
const roots = uniquePaths([
|
|
166
|
+
userRoot,
|
|
167
|
+
...extraRoots,
|
|
168
|
+
...configuredRoots,
|
|
169
|
+
workspaceRoot,
|
|
170
|
+
]).map((root) => ({
|
|
171
|
+
root,
|
|
172
|
+
kind: path.resolve(root) === userRoot ? "user" : "workspace",
|
|
173
|
+
}));
|
|
174
|
+
return { roots, createRoot: userRoot };
|
|
175
|
+
}
|
|
176
|
+
function splitPathList(value) {
|
|
177
|
+
return String(value || "")
|
|
178
|
+
.split(path.delimiter)
|
|
179
|
+
.map((item) => item.trim())
|
|
180
|
+
.filter(Boolean)
|
|
181
|
+
.map((item) => path.resolve(item));
|
|
182
|
+
}
|
|
183
|
+
function uniquePaths(values) {
|
|
184
|
+
const seen = new Set();
|
|
185
|
+
const result = [];
|
|
186
|
+
for (const value of values) {
|
|
187
|
+
const resolved = path.resolve(value);
|
|
188
|
+
const key = process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
189
|
+
if (seen.has(key))
|
|
190
|
+
continue;
|
|
191
|
+
seen.add(key);
|
|
192
|
+
result.push(resolved);
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
112
196
|
export async function createWebRuntime(options = {}) {
|
|
113
197
|
const envLoad = loadDefaultDotEnvFiles({ override: true });
|
|
114
198
|
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
|
|
@@ -117,6 +201,11 @@ export async function createWebRuntime(options = {}) {
|
|
|
117
201
|
const modelGateway = new LoggingModelGateway(createModelGatewayFromProcessEnv(process.env), communicationLogger);
|
|
118
202
|
const taskStore = new TaskStore();
|
|
119
203
|
const tools = new ToolRegistry();
|
|
204
|
+
const { roots: skillRoots, createRoot: skillCreateRoot } = resolveSkillCatalogRoots(cwd, options.skillRoots, options.skillCreateRoot);
|
|
205
|
+
const skills = new FileSystemSkillCatalog({
|
|
206
|
+
roots: skillRoots,
|
|
207
|
+
createRoot: skillCreateRoot,
|
|
208
|
+
});
|
|
120
209
|
tools.register(editTool);
|
|
121
210
|
tools.register(writeTool);
|
|
122
211
|
tools.register(createExecTool({ taskStore }));
|
|
@@ -129,6 +218,9 @@ export async function createWebRuntime(options = {}) {
|
|
|
129
218
|
if (modelConfig?.provider === "openai")
|
|
130
219
|
tools.register(createOpenAIImageGenerationTool());
|
|
131
220
|
tools.register(planTool);
|
|
221
|
+
tools.register(createSkillTool(skills));
|
|
222
|
+
for (const tool of createSkillManagementTools(skills, { requireApproval: true, allowDelete: false }))
|
|
223
|
+
tools.register(tool);
|
|
132
224
|
for (const tool of options.externalTools ?? [])
|
|
133
225
|
tools.register(tool);
|
|
134
226
|
const agentRuntime = { modelGateway, tools, taskStore };
|
|
@@ -155,6 +247,8 @@ export async function createWebRuntime(options = {}) {
|
|
|
155
247
|
modelGateway,
|
|
156
248
|
tools,
|
|
157
249
|
appPromptStore,
|
|
250
|
+
contextManager: new SkillCatalogContextManager(skills),
|
|
251
|
+
skills: (await skills.list()).map((skill) => skill.name),
|
|
158
252
|
taskNotificationSource: createTaskNotificationSource(taskStore),
|
|
159
253
|
commands: replCommandDefinitions.map((command) => command.usage),
|
|
160
254
|
session: {
|
|
@@ -266,6 +360,7 @@ export class WebRepl {
|
|
|
266
360
|
queuedAttachments;
|
|
267
361
|
foregroundRun;
|
|
268
362
|
foregroundRunToken = 0;
|
|
363
|
+
pendingUserImageEchoLabels;
|
|
269
364
|
backgroundSessionRuns = new Map();
|
|
270
365
|
suppressReattachedStreaming = new Set();
|
|
271
366
|
backgroundTaskCount;
|
|
@@ -508,7 +603,7 @@ export class WebRepl {
|
|
|
508
603
|
this.broadcastSync();
|
|
509
604
|
}
|
|
510
605
|
setStatus(next) {
|
|
511
|
-
this.status = next;
|
|
606
|
+
this.status = next.phase === "running_tools" ? next : { ...next, currentTool: undefined };
|
|
512
607
|
this.broadcastSync();
|
|
513
608
|
}
|
|
514
609
|
finalizeForegroundView() {
|
|
@@ -611,6 +706,11 @@ export class WebRepl {
|
|
|
611
706
|
}
|
|
612
707
|
handleEvent(event) {
|
|
613
708
|
this.reduce(event);
|
|
709
|
+
if (event.type === "message" && this.matchesPendingUserImageEcho(event.message)) {
|
|
710
|
+
this.pendingUserImageEchoLabels = undefined;
|
|
711
|
+
this.broadcastSync();
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
614
714
|
if (event.type === "state" || event.type === "context.metrics" || event.type === "usage" || event.type === "tool_call.delta" || event.type === "retrying") {
|
|
615
715
|
this.broadcastSync();
|
|
616
716
|
return;
|
|
@@ -695,6 +795,15 @@ export class WebRepl {
|
|
|
695
795
|
if (event.type === "error")
|
|
696
796
|
this.append({ kind: "error", text: event.error.message });
|
|
697
797
|
}
|
|
798
|
+
matchesPendingUserImageEcho(message) {
|
|
799
|
+
const pending = this.pendingUserImageEchoLabels;
|
|
800
|
+
if (!pending?.length || message.role !== "user")
|
|
801
|
+
return false;
|
|
802
|
+
const labels = message.blocks
|
|
803
|
+
.filter((block) => block.type === "image" && typeof block.label === "string")
|
|
804
|
+
.map((block) => block.label);
|
|
805
|
+
return labels.length === pending.length && labels.every((label, index) => label === pending[index]);
|
|
806
|
+
}
|
|
698
807
|
async handleCommandOrPrompt(text, attachments = []) {
|
|
699
808
|
const command = parseReplCommand(text);
|
|
700
809
|
if (command.type === "exit") {
|
|
@@ -794,6 +903,10 @@ export class WebRepl {
|
|
|
794
903
|
this.append({ kind: "user", text: promptPayload.displayText });
|
|
795
904
|
for (const line of imageLinesForBlocks("user", promptPayload.blocks))
|
|
796
905
|
this.append(line);
|
|
906
|
+
const optimisticImageLabels = (promptPayload.blocks ?? [])
|
|
907
|
+
.filter((block) => block.type === "image" && typeof block.label === "string")
|
|
908
|
+
.map((block) => block.label);
|
|
909
|
+
this.pendingUserImageEchoLabels = optimisticImageLabels.length ? optimisticImageLabels : undefined;
|
|
797
910
|
const runToken = ++this.foregroundRunToken;
|
|
798
911
|
const abortController = new AbortController();
|
|
799
912
|
this.activeAbortController = abortController;
|
|
@@ -833,6 +946,7 @@ export class WebRepl {
|
|
|
833
946
|
this.activeAbortController = undefined;
|
|
834
947
|
this.interruptArmed = false;
|
|
835
948
|
this.finalizeForegroundView();
|
|
949
|
+
this.pendingUserImageEchoLabels = undefined;
|
|
836
950
|
const queuedText = this.queuedInput;
|
|
837
951
|
const queuedAttach = this.queuedAttachments;
|
|
838
952
|
this.queuedInput = undefined;
|
|
@@ -885,10 +999,14 @@ export class WebRepl {
|
|
|
885
999
|
res.write(`event: ${event}\n`);
|
|
886
1000
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
887
1001
|
}
|
|
1002
|
+
sendSerialized(res, event, data) {
|
|
1003
|
+
res.write(`event: ${event}\n`);
|
|
1004
|
+
res.write(`data: ${data}\n\n`);
|
|
1005
|
+
}
|
|
888
1006
|
broadcastSync() {
|
|
889
|
-
const payload = this.snapshot(false);
|
|
1007
|
+
const payload = JSON.stringify(this.snapshot(false));
|
|
890
1008
|
for (const res of this.subscribers)
|
|
891
|
-
this.
|
|
1009
|
+
this.sendSerialized(res, "sync", payload);
|
|
892
1010
|
}
|
|
893
1011
|
}
|
|
894
1012
|
function reqKeepAlive(res) {
|
|
@@ -907,6 +1025,8 @@ async function route(req, res, router) {
|
|
|
907
1025
|
return sendFile(res, highlightAssetPath, "text/javascript; charset=utf-8");
|
|
908
1026
|
if (req.method === "GET" && url.pathname === "/vendor/highlight-theme.css")
|
|
909
1027
|
return sendFile(res, highlightThemeAssetPath, "text/css; charset=utf-8");
|
|
1028
|
+
if (req.method === "GET" && url.pathname === "/api/images")
|
|
1029
|
+
return sendImage(res, url.searchParams.get("path"), url.searchParams.get("mime"));
|
|
910
1030
|
const scope = webRuntimeScopeFromUrl(url);
|
|
911
1031
|
const repl = await router.get(scope);
|
|
912
1032
|
if (req.method === "GET" && url.pathname === "/events")
|
|
@@ -970,6 +1090,23 @@ async function sendFile(res, filepath, contentType) {
|
|
|
970
1090
|
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "public, max-age=3600" });
|
|
971
1091
|
res.end(body);
|
|
972
1092
|
}
|
|
1093
|
+
async function sendImage(res, encodedPath, mimeType) {
|
|
1094
|
+
const filepath = decodeImagePath(encodedPath);
|
|
1095
|
+
if (!filepath)
|
|
1096
|
+
return sendJson(res, { error: "missing image path" }, 400);
|
|
1097
|
+
const contentType = safeImageContentType(mimeType);
|
|
1098
|
+
const binaryPath = filepath.endsWith(".base64.txt") ? filepath.slice(0, -".base64.txt".length) : filepath;
|
|
1099
|
+
if (existsSync(binaryPath)) {
|
|
1100
|
+
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "no-store" });
|
|
1101
|
+
createReadStream(binaryPath).pipe(res);
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
if (!existsSync(filepath))
|
|
1105
|
+
return sendJson(res, { error: "image not found" }, 404);
|
|
1106
|
+
const base64 = (await fs.readFile(filepath, "utf8")).trim();
|
|
1107
|
+
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "no-store" });
|
|
1108
|
+
res.end(Buffer.from(stripDataUrlPrefix(base64), "base64"));
|
|
1109
|
+
}
|
|
973
1110
|
function sendJson(res, value, status = 200) {
|
|
974
1111
|
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-store" });
|
|
975
1112
|
res.end(JSON.stringify(value));
|
|
@@ -1074,7 +1211,7 @@ function reduceStatus(status, event) {
|
|
|
1074
1211
|
if (event.type === "state")
|
|
1075
1212
|
return { ...status, phase: event.phase, detail: event.detail, currentTool: event.phase === "preparing" || event.phase === "calling_model" || event.phase === "ready" ? undefined : status.currentTool, usage: event.phase === "preparing" ? undefined : status.usage, streamedOutputTokens: event.phase === "preparing" ? 0 : status.streamedOutputTokens, inputTokenUpdatedAt: event.phase === "preparing" ? undefined : status.inputTokenUpdatedAt, outputTokenUpdatedAt: event.phase === "preparing" ? undefined : status.outputTokenUpdatedAt, retryCooldownUntil: event.phase === "preparing" ? undefined : status.retryCooldownUntil, activityTick: status.activityTick + 1 };
|
|
1076
1213
|
if (event.type === "tool.started")
|
|
1077
|
-
return { ...status, phase: "running_tools", detail: event.toolUse.name, currentTool: { id: event.toolUse.id, name: event.toolUse.name,
|
|
1214
|
+
return { ...status, phase: "running_tools", detail: event.toolUse.name, currentTool: { id: event.toolUse.id, name: event.toolUse.name, kind: toolKindForToolUse(event.toolUse.name, event.toolUse.input), startedAt: Date.now() }, activityTick: status.activityTick + 1 };
|
|
1078
1215
|
if (event.type === "tool.finished")
|
|
1079
1216
|
return { ...status, currentTool: status.currentTool?.id === event.toolUse.id ? undefined : status.currentTool, activityTick: status.activityTick + 1 };
|
|
1080
1217
|
if (event.type === "context.metrics")
|
|
@@ -1095,6 +1232,21 @@ function reduceStatus(status, event) {
|
|
|
1095
1232
|
return { ...status, activityTick: status.activityTick + 1 };
|
|
1096
1233
|
return status;
|
|
1097
1234
|
}
|
|
1235
|
+
function toolKindForToolUse(toolName, input) {
|
|
1236
|
+
if (toolName === "image2")
|
|
1237
|
+
return isRecord(input) && input.mode === "edit" ? "修图" : "作图";
|
|
1238
|
+
if (toolName === "edit" || toolName === "write" || toolName.includes("artifact_editor") || toolName.includes("apply_patch"))
|
|
1239
|
+
return "编辑";
|
|
1240
|
+
if (toolName === "exec" || toolName.includes("shell") || toolName.includes("command"))
|
|
1241
|
+
return "执行";
|
|
1242
|
+
if (toolName.includes("download"))
|
|
1243
|
+
return "下载";
|
|
1244
|
+
if (toolName === "read" || toolName === "list" || toolName === "grep" || toolName === "search" || toolName.includes("query") || toolName.includes("load"))
|
|
1245
|
+
return "查询";
|
|
1246
|
+
if (toolName === "plan")
|
|
1247
|
+
return "计划";
|
|
1248
|
+
return "工具";
|
|
1249
|
+
}
|
|
1098
1250
|
async function handleExportCommand(outputPath, runtime) {
|
|
1099
1251
|
const snapshot = runtime.engine.snapshot();
|
|
1100
1252
|
if (!snapshot.session)
|
|
@@ -1495,20 +1647,43 @@ function imageLineForBlock(role, block) {
|
|
|
1495
1647
|
kind,
|
|
1496
1648
|
text: block.label ?? `[image ${block.mimeType}]`,
|
|
1497
1649
|
image: {
|
|
1498
|
-
src:
|
|
1650
|
+
src: imageBlockToSrc(block),
|
|
1499
1651
|
label: block.label,
|
|
1500
1652
|
mimeType: block.mimeType,
|
|
1501
1653
|
},
|
|
1502
1654
|
};
|
|
1503
1655
|
}
|
|
1504
|
-
function
|
|
1505
|
-
|
|
1656
|
+
function imageBlockToSrc(block) {
|
|
1657
|
+
if (block.storage?.path) {
|
|
1658
|
+
return `/api/images?path=${encodeImagePath(block.storage.path)}&mime=${encodeURIComponent(block.mimeType)}`;
|
|
1659
|
+
}
|
|
1660
|
+
const data = block.data.trim();
|
|
1506
1661
|
if (!data)
|
|
1507
1662
|
return "";
|
|
1508
1663
|
if (data.startsWith("data:"))
|
|
1509
1664
|
return data;
|
|
1510
1665
|
return `data:${block.mimeType};base64,${data}`;
|
|
1511
1666
|
}
|
|
1667
|
+
function encodeImagePath(filepath) {
|
|
1668
|
+
return Buffer.from(filepath, "utf8").toString("base64url");
|
|
1669
|
+
}
|
|
1670
|
+
function decodeImagePath(value) {
|
|
1671
|
+
if (!value)
|
|
1672
|
+
return undefined;
|
|
1673
|
+
try {
|
|
1674
|
+
return Buffer.from(value, "base64url").toString("utf8");
|
|
1675
|
+
}
|
|
1676
|
+
catch {
|
|
1677
|
+
return undefined;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
function safeImageContentType(value) {
|
|
1681
|
+
return value && /^image\/[a-z0-9.+-]+$/i.test(value) ? value : "application/octet-stream";
|
|
1682
|
+
}
|
|
1683
|
+
function stripDataUrlPrefix(value) {
|
|
1684
|
+
const match = /^data:[^;]+;base64,(.*)$/is.exec(value);
|
|
1685
|
+
return match ? match[1] : value;
|
|
1686
|
+
}
|
|
1512
1687
|
function assistantText(message) {
|
|
1513
1688
|
const text = message.blocks.filter((block) => block.type === "text").map((block) => block.text).join("");
|
|
1514
1689
|
return text.length > 0 ? text : undefined;
|
|
@@ -1561,14 +1736,21 @@ function thinkingLine(text, live = false) {
|
|
|
1561
1736
|
return { kind: "thinking", title: titleForKind("thinking"), text, previewStyle: "summary", summaryMaxLines: THINKING_SUMMARY_MAX_LINES, live };
|
|
1562
1737
|
}
|
|
1563
1738
|
function formatToolUse(toolUse) {
|
|
1739
|
+
const toolKind = toolKindForToolUse(toolUse.name, toolUse.input);
|
|
1564
1740
|
if (toolUse.name === "plan" && isPlanToolPayload(toolUse.input))
|
|
1565
|
-
return { kind: "tool", title: toolTitle(toolUse.name, "running"), bodyTitle: planToolBodyTitle(toolUse.input), text: formatPlanToolPayload(toolUse.input), collapsible: true };
|
|
1741
|
+
return { kind: "tool", title: toolTitle(toolUse.name, "running"), bodyTitle: planToolBodyTitle(toolUse.input), toolKind, text: formatPlanToolPayload(toolUse.input), collapsible: true };
|
|
1566
1742
|
const description = toolUse.name === "exec" ? execDescriptionFromInput(toolUse.input) : undefined;
|
|
1567
|
-
return { kind: "tool", title: toolTitle(toolUse.name, "running"), bodyTitle: description, text: formatReplData(toolUse.input, 1200), previewStyle: "summary", collapsible: true };
|
|
1743
|
+
return { kind: "tool", title: toolTitle(toolUse.name, "running"), bodyTitle: description, toolKind, text: formatReplData(toolUse.input, 1200), previewStyle: "summary", collapsible: true };
|
|
1568
1744
|
}
|
|
1569
1745
|
function formatToolResultLine(toolName, output, ok) {
|
|
1570
1746
|
const formatted = formatToolResult(toolName, output, ok);
|
|
1571
|
-
return { kind: ok ? "tool" : "error", title: toolTitle(toolName, "finished"), bodyTitle: formatted.bodyTitle, titleStatus: ok ? "success" : "failure", text: formatted.text, format: formatted.format, live: false, previewStyle: formatted.full ? undefined : "summary", summaryMaxLines: formatted.summaryMaxLines, collapsible: true };
|
|
1747
|
+
return { kind: ok ? "tool" : "error", title: toolTitle(toolName, "finished"), bodyTitle: formatted.bodyTitle, titleStatus: ok ? "success" : "failure", toolKind: toolKindForToolUse(toolName, undefined), text: formatted.text, format: formatted.format, live: false, previewStyle: formatted.full ? undefined : "summary", summaryMaxLines: formatted.summaryMaxLines, collapsible: true, artifact: xhsArtifactFromToolOutput(toolName, output) };
|
|
1748
|
+
}
|
|
1749
|
+
function xhsArtifactFromToolOutput(toolName, output) {
|
|
1750
|
+
if (toolName !== "open_xhs_artifact_editor" || !isRecord(output))
|
|
1751
|
+
return undefined;
|
|
1752
|
+
const artifact = output.artifact;
|
|
1753
|
+
return isRecord(artifact) && typeof artifact.id === "string" ? artifact : undefined;
|
|
1572
1754
|
}
|
|
1573
1755
|
function toolTitle(toolName, _phase) {
|
|
1574
1756
|
return toolName;
|
|
@@ -1695,6 +1877,17 @@ function formatImageGenerationToolResult(output, ok) {
|
|
|
1695
1877
|
const quality = typeof output.quality === "string" ? output.quality : undefined;
|
|
1696
1878
|
const format = typeof output.outputFormat === "string" ? output.outputFormat : undefined;
|
|
1697
1879
|
const sourceImages = typeof output.sourceImages === "number" ? output.sourceImages : undefined;
|
|
1880
|
+
const prompt = typeof output.prompt === "string" ? output.prompt.trim() : "";
|
|
1881
|
+
const semanticName = typeof output.semanticName === "string" ? output.semanticName.trim() : "";
|
|
1882
|
+
const background = typeof output.background === "string" ? output.background : undefined;
|
|
1883
|
+
const startedAtIso = typeof output.startedAtIso === "string" ? output.startedAtIso : undefined;
|
|
1884
|
+
const finishedAtIso = typeof output.finishedAtIso === "string" ? output.finishedAtIso : undefined;
|
|
1885
|
+
const revisedPrompt = Array.isArray(output.images)
|
|
1886
|
+
? output.images
|
|
1887
|
+
.filter(isRecord)
|
|
1888
|
+
.map((image) => typeof image.revisedPrompt === "string" ? image.revisedPrompt.trim() : "")
|
|
1889
|
+
.find((value) => Boolean(value))
|
|
1890
|
+
: undefined;
|
|
1698
1891
|
const lines = [`${mode === "edit" ? "edited" : "generated"} ${returnedImages ?? 0} image${returnedImages === 1 ? "" : "s"}`];
|
|
1699
1892
|
const details = [provider, model, size, quality && quality !== "auto" ? quality : undefined, format].filter((value) => Boolean(value));
|
|
1700
1893
|
if (details.length > 0)
|
|
@@ -1704,6 +1897,19 @@ function formatImageGenerationToolResult(output, ok) {
|
|
|
1704
1897
|
const duration = imageGenerationDuration(output);
|
|
1705
1898
|
if (duration !== undefined)
|
|
1706
1899
|
lines.push(`duration: ${duration}ms`);
|
|
1900
|
+
if (prompt)
|
|
1901
|
+
lines.push(`prompt: ${prompt}`);
|
|
1902
|
+
if (revisedPrompt && revisedPrompt !== prompt)
|
|
1903
|
+
lines.push(`revisedPrompt: ${revisedPrompt}`);
|
|
1904
|
+
lines.push(`mode: ${mode}`);
|
|
1905
|
+
if (semanticName)
|
|
1906
|
+
lines.push(`semanticName: ${semanticName}`);
|
|
1907
|
+
if (background)
|
|
1908
|
+
lines.push(`background: ${background}`);
|
|
1909
|
+
if (startedAtIso)
|
|
1910
|
+
lines.push(`startedAt: ${startedAtIso}`);
|
|
1911
|
+
if (finishedAtIso)
|
|
1912
|
+
lines.push(`finishedAt: ${finishedAtIso}`);
|
|
1707
1913
|
return lines.join("\n");
|
|
1708
1914
|
}
|
|
1709
1915
|
function imageGenerationDuration(output) {
|
|
@@ -1916,4 +2122,4 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me
|
|
|
1916
2122
|
process.exitCode = 1;
|
|
1917
2123
|
});
|
|
1918
2124
|
}
|
|
1919
|
-
//# sourceMappingURL=index.js.map
|
|
2125
|
+
//# sourceMappingURL=index.js.map
|