neoctl 0.2.13 → 0.2.15
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/agents/agent-activity.js +11 -1
- package/dist/agents/agent-activity.js.map +1 -1
- package/dist/agents/local-agent-task.d.ts +1 -1
- package/dist/core/image-registry.js +4 -1
- package/dist/core/image-registry.js.map +1 -1
- package/dist/core/message-pipeline.js +16 -2
- package/dist/core/message-pipeline.js.map +1 -1
- package/dist/core/query-engine.d.ts +2 -0
- package/dist/core/query-engine.js +8 -4
- package/dist/core/query-engine.js.map +1 -1
- package/dist/core/query.js +6 -2
- package/dist/core/query.js.map +1 -1
- package/dist/repl/index.js +70 -91
- package/dist/repl/index.js.map +1 -1
- package/dist/tools/builtins/exec-tool.d.ts +1 -0
- package/dist/tools/builtins/exec-tool.js.map +1 -1
- package/dist/tools/builtins/image-generation-tool.d.ts +4 -0
- package/dist/tools/builtins/image-generation-tool.js +179 -52
- package/dist/tools/builtins/image-generation-tool.js.map +1 -1
- package/dist/web/index.d.ts +24 -1
- package/dist/web/index.js +206 -23
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/release-local.mjs +42 -0
package/dist/repl/index.js
CHANGED
|
@@ -231,6 +231,40 @@ function createTaskNotificationSource(taskStore) {
|
|
|
231
231
|
},
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
|
+
function resolveSkillCatalogRoots(cwd) {
|
|
235
|
+
const userRoot = path.resolve(process.env.NEO_SKILL_CREATE_ROOT || path.join(getNeoctlHome(), "skills"));
|
|
236
|
+
const configuredRoots = splitPathList(process.env.NEO_SKILL_ROOTS);
|
|
237
|
+
const workspaceRoot = path.resolve(cwd, ".neo", "skills");
|
|
238
|
+
const roots = uniquePaths([
|
|
239
|
+
userRoot,
|
|
240
|
+
...configuredRoots,
|
|
241
|
+
workspaceRoot,
|
|
242
|
+
]).map((root) => ({
|
|
243
|
+
root,
|
|
244
|
+
kind: path.resolve(root) === userRoot ? "user" : "workspace",
|
|
245
|
+
}));
|
|
246
|
+
return { roots, createRoot: userRoot };
|
|
247
|
+
}
|
|
248
|
+
function splitPathList(value) {
|
|
249
|
+
return String(value || "")
|
|
250
|
+
.split(path.delimiter)
|
|
251
|
+
.map((item) => item.trim())
|
|
252
|
+
.filter(Boolean)
|
|
253
|
+
.map((item) => path.resolve(item));
|
|
254
|
+
}
|
|
255
|
+
function uniquePaths(values) {
|
|
256
|
+
const seen = new Set();
|
|
257
|
+
const result = [];
|
|
258
|
+
for (const value of values) {
|
|
259
|
+
const resolved = path.resolve(value);
|
|
260
|
+
const key = process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
261
|
+
if (seen.has(key))
|
|
262
|
+
continue;
|
|
263
|
+
seen.add(key);
|
|
264
|
+
result.push(resolved);
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
234
268
|
async function createRuntime() {
|
|
235
269
|
const envLoad = loadDefaultDotEnvFiles({ override: true });
|
|
236
270
|
const modelConfig = readModelProviderConfig(process.env);
|
|
@@ -242,12 +276,9 @@ async function createRuntime() {
|
|
|
242
276
|
const secretStore = await SecretStore.open();
|
|
243
277
|
const secretRedactions = new InMemorySecretRedactionRegistry();
|
|
244
278
|
const tools = new ToolRegistry();
|
|
245
|
-
const skillWorkspaceRoot =
|
|
279
|
+
const { roots: skillRoots, createRoot: skillWorkspaceRoot } = resolveSkillCatalogRoots(process.cwd());
|
|
246
280
|
const skills = new FileSystemSkillCatalog({
|
|
247
|
-
roots:
|
|
248
|
-
{ root: skillWorkspaceRoot, kind: "workspace" },
|
|
249
|
-
{ root: path.resolve(getNeoctlHome(), "skills"), kind: "user" },
|
|
250
|
-
],
|
|
281
|
+
roots: skillRoots,
|
|
251
282
|
createRoot: skillWorkspaceRoot,
|
|
252
283
|
});
|
|
253
284
|
tools.register(editTool);
|
|
@@ -260,7 +291,7 @@ async function createRuntime() {
|
|
|
260
291
|
tools.register(createLoadImageTool());
|
|
261
292
|
tools.register(createImageNoteTool());
|
|
262
293
|
if (modelConfig?.provider === "openai")
|
|
263
|
-
tools.register(createOpenAIImageGenerationTool());
|
|
294
|
+
tools.register(createOpenAIImageGenerationTool({ taskStore, foregroundDetachRegistry: foregroundExecDetach }));
|
|
264
295
|
tools.register(planTool);
|
|
265
296
|
for (const tool of createSecretTools())
|
|
266
297
|
tools.register(tool);
|
|
@@ -330,7 +361,7 @@ async function createRuntime() {
|
|
|
330
361
|
function syncImageGenerationTool(runtime, provider) {
|
|
331
362
|
runtime.tools.unregister("image2");
|
|
332
363
|
if (provider === "openai")
|
|
333
|
-
runtime.tools.register(createOpenAIImageGenerationTool());
|
|
364
|
+
runtime.tools.register(createOpenAIImageGenerationTool({ taskStore: runtime.taskStore, foregroundDetachRegistry: runtime.foregroundExecDetach }));
|
|
334
365
|
}
|
|
335
366
|
function formatCreatedEnvNotice(path) {
|
|
336
367
|
return `Created default config file: ${path}\nSet MODEL_PROVIDER and the matching provider section (OPENAI_API_KEY or ANTHROPIC_API_KEY), then restart neo.`;
|
|
@@ -1352,7 +1383,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1352
1383
|
if (key.ctrl && value.toLowerCase() === "b") {
|
|
1353
1384
|
const result = runtime.foregroundExecDetach.detachCurrent();
|
|
1354
1385
|
append(result.ok
|
|
1355
|
-
? systemLine(`Detached foreground
|
|
1386
|
+
? systemLine(`Detached foreground task to background task ${result.taskId ?? "unknown"}.`)
|
|
1356
1387
|
: systemLine(result.message));
|
|
1357
1388
|
return;
|
|
1358
1389
|
}
|
|
@@ -1656,7 +1687,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1656
1687
|
return;
|
|
1657
1688
|
}
|
|
1658
1689
|
});
|
|
1659
|
-
return e(Box, { flexDirection: "column" }, e((Static), { items: staticLines, children: (line, index) => e(MessageBlock, { key: line.id, line, width, blockIndex: index }) }), e(MessageList, { lines: visibleDynamicLines, width, lineIndexOffset: staticLines.length, onMarkdownRenderComplete: markLineRendered }), sessionsBrowser ? e(SessionsBrowser, { state: sessionsBrowser, width }) : null, skillsBrowser ? e(SkillsBrowser, { state: skillsBrowser, width }) : null, secretsBrowser ? e(SecretsBrowser, { state: secretsBrowser, width }) : null, loginForm ? e(LoginFormView, { state: loginForm, width }) : null, e(StatusBar, { status, animationTick, width }), showForegroundExecDetachHint && foregroundExecDetachHandle ? e(ForegroundExecDetachHintLine, { handle: foregroundExecDetachHandle, width }) : null, agentActivities.length > 0 ? e(SubagentLivePanel, { activities: agentActivities, width,
|
|
1690
|
+
return e(Box, { flexDirection: "column" }, e((Static), { items: staticLines, children: (line, index) => e(MessageBlock, { key: line.id, line, width, blockIndex: index }) }), e(MessageList, { lines: visibleDynamicLines, width, lineIndexOffset: staticLines.length, onMarkdownRenderComplete: markLineRendered }), sessionsBrowser ? e(SessionsBrowser, { state: sessionsBrowser, width }) : null, skillsBrowser ? e(SkillsBrowser, { state: skillsBrowser, width }) : null, secretsBrowser ? e(SecretsBrowser, { state: secretsBrowser, width }) : null, loginForm ? e(LoginFormView, { state: loginForm, width }) : null, e(StatusBar, { status, animationTick, width }), showForegroundExecDetachHint && foregroundExecDetachHandle ? e(ForegroundExecDetachHintLine, { handle: foregroundExecDetachHandle, width }) : null, agentActivities.length > 0 ? e(SubagentLivePanel, { activities: agentActivities, width, animationTick }) : null, agentActivities.length === 0 && backgroundTasks.length > 0 ? e(BackgroundTaskStatusLine, { tasks: backgroundTasks, width }) : null, agentActivities.length > 0 && nonAgentBackgroundTasks.length > 0 ? e(BackgroundTaskStatusLine, { tasks: nonAgentBackgroundTasks, width }) : null, pasteStatus ? e(PasteStatusLine, { text: pasteStatus, width }) : null, queuedInput !== undefined ? e(QueuedInputLine, { text: queuedInput, width }) : null, e(PromptLine, { text: promptDisplayText, cursor: promptDisplayCursor, busy, locked: inputLockedByQueue, placeholder: input.length === 0 && promptPlaceholder !== undefined, ghostText: activePlaceholder, width, prompt, slashCompletions, selectedSlashCompletionIndex, attachments }));
|
|
1660
1691
|
}
|
|
1661
1692
|
const MessageList = React.memo(function MessageList({ lines, width, lineIndexOffset = 0, onMarkdownRenderComplete }) {
|
|
1662
1693
|
const contentWidth = messageContentWidth(width);
|
|
@@ -1994,37 +2025,26 @@ function backgroundTaskStatusRenderRows(taskCount) {
|
|
|
1994
2025
|
}
|
|
1995
2026
|
function ForegroundExecDetachHintLine({ handle, width: terminalWidth }) {
|
|
1996
2027
|
const width = statusBarWidth(terminalWidth);
|
|
2028
|
+
const toolName = handle.toolName?.trim() || "exec";
|
|
1997
2029
|
const label = handle.description?.trim() || handle.command;
|
|
1998
|
-
const text = `↳
|
|
2030
|
+
const text = `↳ ${toolName} still running · Ctrl+B to detach · ${truncateMiddle(label, Math.max(12, width - toolName.length - 33))}`;
|
|
1999
2031
|
return e(Text, { color: "yellow" }, fitToWidth(text, width));
|
|
2000
2032
|
}
|
|
2001
|
-
function SubagentLivePanel({ activities, width: terminalWidth,
|
|
2033
|
+
function SubagentLivePanel({ activities, width: terminalWidth, animationTick }) {
|
|
2002
2034
|
const width = statusBarWidth(terminalWidth);
|
|
2003
|
-
const rows = subagentLivePanelRenderRows(activities, terminalRows, compact);
|
|
2004
|
-
if (rows <= 0)
|
|
2005
|
-
return null;
|
|
2006
2035
|
const sorted = sortAgentActivitiesForPanel(activities);
|
|
2007
2036
|
const selected = sorted[0];
|
|
2008
2037
|
if (!selected)
|
|
2009
2038
|
return null;
|
|
2010
2039
|
const activeCount = activities.filter((activity) => activity.status === "running" || activity.status === "pending").length;
|
|
2011
|
-
const
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
}, fitToWidth(line.text, width))));
|
|
2020
|
-
}
|
|
2021
|
-
const SUBAGENT_DETAIL_ROWS = 3;
|
|
2022
|
-
function subagentLivePanelRenderRows(activities, terminalRows, compact = false) {
|
|
2023
|
-
if (activities.length === 0)
|
|
2024
|
-
return 0;
|
|
2025
|
-
if (compact || terminalRows < 22 || activities.length > 1)
|
|
2026
|
-
return 1;
|
|
2027
|
-
return 1 + SUBAGENT_DETAIL_ROWS;
|
|
2040
|
+
const recentCount = Math.max(0, activities.length - activeCount);
|
|
2041
|
+
const countText = activeCount > 1
|
|
2042
|
+
? `${activeCount} active${recentCount ? ` · ${recentCount} recent` : ""}`
|
|
2043
|
+
: recentCount && activeCount === 0
|
|
2044
|
+
? `${recentCount} recent`
|
|
2045
|
+
: "";
|
|
2046
|
+
const text = `↳ subagent ${subagentStatusText(selected.status, animationTick)}${countText ? ` · ${countText}` : ""} · ${compactAgentSummary(selected, width)}`;
|
|
2047
|
+
return e(Text, { color: statusColor(selected.status) }, fitToWidth(text, width));
|
|
2028
2048
|
}
|
|
2029
2049
|
function sortAgentActivitiesForPanel(activities) {
|
|
2030
2050
|
const rank = (status) => {
|
|
@@ -2038,76 +2058,35 @@ function sortAgentActivitiesForPanel(activities) {
|
|
|
2038
2058
|
};
|
|
2039
2059
|
return [...activities].sort((left, right) => rank(left.status) - rank(right.status) || right.updatedAt.localeCompare(left.updatedAt));
|
|
2040
2060
|
}
|
|
2041
|
-
function buildSubagentDetailLines(selected, sorted, animationTick) {
|
|
2042
|
-
const spinner = selected.status === "running" ? spinnerFrame(animationTick) : statusGlyph(selected.status);
|
|
2043
|
-
const elapsed = formatElapsed(Date.now() - new Date(selected.startedAt).getTime());
|
|
2044
|
-
const headerLine = `${spinner} ${selected.description || selected.agentId} · ${elapsed}`;
|
|
2045
|
-
const currentLine = selected.currentTool
|
|
2046
|
-
? `→ ${selected.currentTool.name}${selected.currentTool.inputPreview ? ` · ${selected.currentTool.inputPreview}` : ""}`
|
|
2047
|
-
: selected.error
|
|
2048
|
-
? `✖ ${selected.error}`
|
|
2049
|
-
: selected.resultPreview
|
|
2050
|
-
? `✓ ${selected.resultPreview}`
|
|
2051
|
-
: selected.lastText
|
|
2052
|
-
? `• ${selected.lastText}`
|
|
2053
|
-
: `• ${selected.prompt}`;
|
|
2054
|
-
const recent = selected.timeline.slice(-2).map((entry) => `${timelinePrefix(entry)} ${formatTimelineEntry(entry, 240)}`);
|
|
2055
|
-
const otherRunning = sorted
|
|
2056
|
-
.filter((activity) => activity.agentId !== selected.agentId && (activity.status === "running" || activity.status === "pending"))
|
|
2057
|
-
.slice(0, 2)
|
|
2058
|
-
.map((activity) => compactAgentSummary(activity, 180));
|
|
2059
|
-
const tail = [...recent, ...otherRunning.map((summary) => `· ${summary}`)].find((line) => line.trim()) ?? `tools:${selected.totalToolUseCount}`;
|
|
2060
|
-
return [
|
|
2061
|
-
{ text: headerLine, color: statusColor(selected.status) },
|
|
2062
|
-
{ text: currentLine, color: selected.error ? "red" : selected.currentTool ? "#d4b04c" : "yellow" },
|
|
2063
|
-
{ text: tail, color: "gray" },
|
|
2064
|
-
];
|
|
2065
|
-
}
|
|
2066
2061
|
function compactAgentSummary(activity, maxLength) {
|
|
2067
2062
|
const current = activity.currentTool
|
|
2068
2063
|
? `${activity.currentTool.name}${activity.currentTool.inputPreview ? ` ${activity.currentTool.inputPreview}` : ""}`
|
|
2069
|
-
: activity.
|
|
2064
|
+
: firstSafeSubagentPreview(activity.resultPreview, activity.error, activity.lastText, activity.prompt);
|
|
2070
2065
|
const elapsed = formatElapsed(Date.now() - new Date(activity.startedAt).getTime());
|
|
2071
2066
|
return truncateMiddle(`${activity.description || activity.agentId} · ${elapsed} · tools:${activity.totalToolUseCount} · ${current.replace(/\s+/g, " ")}`, Math.max(8, maxLength));
|
|
2072
2067
|
}
|
|
2073
|
-
function
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
if (entry.kind === "thinking")
|
|
2083
|
-
return "◆";
|
|
2084
|
-
if (entry.kind === "error")
|
|
2085
|
-
return "✖";
|
|
2086
|
-
if (entry.kind === "status")
|
|
2087
|
-
return "•";
|
|
2088
|
-
return "assistant:";
|
|
2089
|
-
}
|
|
2090
|
-
function timelineColor(entry) {
|
|
2091
|
-
if (entry.status === "failed" || entry.kind === "error")
|
|
2092
|
-
return "red";
|
|
2093
|
-
if (entry.kind === "tool_start" || entry.kind === "tool_result")
|
|
2094
|
-
return "#d4b04c";
|
|
2095
|
-
if (entry.kind === "thinking")
|
|
2096
|
-
return THINKING_COLOR;
|
|
2097
|
-
if (entry.kind === "status")
|
|
2098
|
-
return "gray";
|
|
2099
|
-
return "green";
|
|
2068
|
+
function firstSafeSubagentPreview(...values) {
|
|
2069
|
+
return values.find((value) => value && !isInternalContinuationPreview(value)) ?? "working";
|
|
2070
|
+
}
|
|
2071
|
+
function isInternalContinuationPreview(value) {
|
|
2072
|
+
const normalized = value.toLowerCase();
|
|
2073
|
+
return normalized.includes("<compact_state>")
|
|
2074
|
+
|| normalized.includes("internal continuation state")
|
|
2075
|
+
|| normalized.includes("auto compact")
|
|
2076
|
+
|| normalized.includes("conversation summary generated by compact");
|
|
2100
2077
|
}
|
|
2101
|
-
function
|
|
2078
|
+
function subagentStatusText(status, animationTick) {
|
|
2079
|
+
if (status === "running")
|
|
2080
|
+
return `still running ${spinnerFrame(animationTick)}`;
|
|
2081
|
+
if (status === "pending")
|
|
2082
|
+
return "pending";
|
|
2102
2083
|
if (status === "completed")
|
|
2103
|
-
return "
|
|
2084
|
+
return "completed";
|
|
2104
2085
|
if (status === "failed")
|
|
2105
|
-
return "
|
|
2086
|
+
return "failed";
|
|
2106
2087
|
if (status === "killed")
|
|
2107
|
-
return "
|
|
2108
|
-
|
|
2109
|
-
return "…";
|
|
2110
|
-
return "●";
|
|
2088
|
+
return "killed";
|
|
2089
|
+
return status;
|
|
2111
2090
|
}
|
|
2112
2091
|
function statusColor(status) {
|
|
2113
2092
|
if (status === "completed")
|