neoctl 0.2.26 → 0.2.28
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 +36 -48
- package/dist/agents/agent-definition.js +1 -1
- package/dist/agents/agent-definition.js.map +1 -1
- package/dist/agents/agent-tool.js +1 -1
- package/dist/agents/agent-tool.js.map +1 -1
- package/dist/agents/smoke-agents.js +2 -2
- package/dist/agents/smoke-agents.js.map +1 -1
- package/dist/context/compaction.js +8 -5
- package/dist/context/compaction.js.map +1 -1
- package/dist/context/context-manager.d.ts +8 -9
- package/dist/context/context-manager.js +33 -41
- package/dist/context/context-manager.js.map +1 -1
- package/dist/context/prompts.js +1 -1
- package/dist/context/prompts.js.map +1 -1
- package/dist/context/smoke-context.js +30 -8
- package/dist/context/smoke-context.js.map +1 -1
- package/dist/core/context-metrics.d.ts +1 -1
- package/dist/core/context-metrics.js +5 -3
- package/dist/core/context-metrics.js.map +1 -1
- package/dist/core/message-pipeline.js +13 -21
- package/dist/core/message-pipeline.js.map +1 -1
- package/dist/core/prompt-cache-key.d.ts +10 -0
- package/dist/core/prompt-cache-key.js +34 -0
- package/dist/core/prompt-cache-key.js.map +1 -0
- package/dist/core/prompt-cache-telemetry.d.ts +2 -1
- package/dist/core/prompt-cache-telemetry.js +23 -1
- package/dist/core/prompt-cache-telemetry.js.map +1 -1
- package/dist/core/query-engine.d.ts +6 -5
- package/dist/core/query-engine.js +18 -18
- package/dist/core/query-engine.js.map +1 -1
- package/dist/core/query.d.ts +0 -1
- package/dist/core/query.js +22 -14
- package/dist/core/query.js.map +1 -1
- package/dist/core/run-agent.d.ts +0 -1
- package/dist/core/run-agent.js +0 -1
- package/dist/core/run-agent.js.map +1 -1
- package/dist/core/smoke-core-loop.js +27 -3
- package/dist/core/smoke-core-loop.js.map +1 -1
- package/dist/core/state.d.ts +0 -2
- package/dist/core/state.js +0 -1
- package/dist/core/state.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/model/config.d.ts +2 -9
- package/dist/model/config.js +3 -31
- package/dist/model/config.js.map +1 -1
- package/dist/model/env.js +2 -10
- package/dist/model/env.js.map +1 -1
- package/dist/model/model-gateway.d.ts +0 -6
- package/dist/model/model-gateway.js.map +1 -1
- package/dist/model/model-metadata.json +0 -40
- package/dist/model/openai-adapter.d.ts +2 -1
- package/dist/model/openai-adapter.js +19 -0
- package/dist/model/openai-adapter.js.map +1 -1
- package/dist/model/openai-chat-mapper.d.ts +0 -1
- package/dist/model/openai-chat-mapper.js +13 -9
- package/dist/model/openai-chat-mapper.js.map +1 -1
- package/dist/model/openai-mappers.d.ts +4 -4
- package/dist/model/openai-mappers.js +23 -17
- package/dist/model/openai-mappers.js.map +1 -1
- package/dist/model/openai-responses-mapper.js +76 -35
- package/dist/model/openai-responses-mapper.js.map +1 -1
- package/dist/model/provider-factory.js +1 -22
- package/dist/model/provider-factory.js.map +1 -1
- package/dist/model/smoke-responses-mapper.js +82 -3
- package/dist/model/smoke-responses-mapper.js.map +1 -1
- package/dist/repl/commands.js +5 -2
- package/dist/repl/commands.js.map +1 -1
- package/dist/repl/index.js +85 -129
- package/dist/repl/index.js.map +1 -1
- package/dist/repl/run-command.d.ts +37 -0
- package/dist/repl/run-command.js +167 -0
- package/dist/repl/run-command.js.map +1 -0
- package/dist/repl/smoke-run-command.js +43 -0
- package/dist/repl/smoke-run-command.js.map +1 -0
- package/dist/secrets/smoke-secrets.js +5 -4
- package/dist/secrets/smoke-secrets.js.map +1 -1
- package/dist/session/session-export.d.ts +0 -1
- package/dist/session/session-export.js +0 -1
- package/dist/session/session-export.js.map +1 -1
- package/dist/session/smoke-session.js +6 -2
- package/dist/session/smoke-session.js.map +1 -1
- package/dist/tools/builtins/exec-process-manager.d.ts +78 -0
- package/dist/tools/builtins/exec-process-manager.js +394 -0
- package/dist/tools/builtins/exec-process-manager.js.map +1 -0
- package/dist/tools/builtins/exec-pty-host.d.ts +1 -0
- package/dist/tools/builtins/exec-pty-host.js +70 -0
- package/dist/tools/builtins/exec-pty-host.js.map +1 -0
- package/dist/tools/builtins/exec-tool.d.ts +16 -27
- package/dist/tools/builtins/exec-tool.js +153 -324
- package/dist/tools/builtins/exec-tool.js.map +1 -1
- package/dist/tools/builtins/image-generation-tool.d.ts +0 -4
- package/dist/tools/builtins/image-generation-tool.js +59 -185
- package/dist/tools/builtins/image-generation-tool.js.map +1 -1
- package/dist/tools/smoke-exec-process.d.ts +1 -0
- package/dist/tools/smoke-exec-process.js +42 -0
- package/dist/tools/smoke-exec-process.js.map +1 -0
- package/dist/tools/smoke-tool-system.js +28 -36
- package/dist/tools/smoke-tool-system.js.map +1 -1
- package/dist/types/events.d.ts +8 -0
- package/dist/types/messages.d.ts +5 -0
- package/dist/types/messages.js +14 -0
- package/dist/types/messages.js.map +1 -1
- package/dist/web/html.js +76 -29
- package/dist/web/html.js.map +1 -1
- package/dist/web/index.d.ts +98 -37
- package/dist/web/index.js +384 -140
- package/dist/web/index.js.map +1 -1
- package/dist/web/plan-payload.d.ts +14 -0
- package/dist/web/plan-payload.js +27 -0
- package/dist/web/plan-payload.js.map +1 -0
- package/dist/web/runtime-context-protocol.d.ts +46 -0
- package/dist/web/runtime-context-protocol.js +72 -0
- package/dist/web/runtime-context-protocol.js.map +1 -0
- package/dist/web/smoke-plan-payload.d.ts +1 -0
- package/dist/web/smoke-plan-payload.js +30 -0
- package/dist/web/smoke-plan-payload.js.map +1 -0
- package/dist/web/smoke-runtime-context-protocol.d.ts +1 -0
- package/dist/web/smoke-runtime-context-protocol.js +31 -0
- package/dist/web/smoke-runtime-context-protocol.js.map +1 -0
- package/dist/web/smoke-web-queue.d.ts +1 -0
- package/dist/web/smoke-web-queue.js +7 -0
- package/dist/web/smoke-web-queue.js.map +1 -0
- package/package.json +10 -5
- package/scripts/verify-ripgrep.cjs +8 -8
- package/dist/model/anthropic-adapter.d.ts +0 -30
- package/dist/model/anthropic-adapter.js +0 -106
- package/dist/model/anthropic-adapter.js.map +0 -1
- package/dist/model/anthropic-mapper.d.ts +0 -13
- package/dist/model/anthropic-mapper.js +0 -508
- package/dist/model/anthropic-mapper.js.map +0 -1
- package/dist/model/smoke-anthropic-mapper.js +0 -89
- package/dist/model/smoke-anthropic-mapper.js.map +0 -1
- /package/dist/{model/smoke-anthropic-mapper.d.ts → repl/smoke-run-command.d.ts} +0 -0
package/dist/repl/index.js
CHANGED
|
@@ -15,7 +15,8 @@ import { CommunicationLogger, LoggingModelGateway } from "../model/communication
|
|
|
15
15
|
import { createModelGatewayFromConfig, createModelGatewayFromProcessEnv } from "../model/provider-factory.js";
|
|
16
16
|
import { ToolRegistry } from "../tools/registry.js";
|
|
17
17
|
import { editTool, writeTool } from "../tools/builtins/edit-tool.js";
|
|
18
|
-
import { createExecTool } from "../tools/builtins/exec-tool.js";
|
|
18
|
+
import { createExecTool, createWriteStdinTool } from "../tools/builtins/exec-tool.js";
|
|
19
|
+
import { ExecProcessManager } from "../tools/builtins/exec-process-manager.js";
|
|
19
20
|
import { listDirectoryTool, readFileTool } from "../tools/builtins/filesystem-tools.js";
|
|
20
21
|
import { grepTool } from "../tools/builtins/grep-tool.js";
|
|
21
22
|
import { searchTool } from "../tools/builtins/search-tool.js";
|
|
@@ -41,40 +42,8 @@ import { getNeoctlHome } from "../paths.js";
|
|
|
41
42
|
import { FileSystemSkillCatalog } from "../skills/skill-filesystem.js";
|
|
42
43
|
import { createSkillAwareCanUseTool, createSkillTool, requireSkillName } from "../skills/skill-tool.js";
|
|
43
44
|
import { createSkillManagementTools } from "../skills/skill-management-tools.js";
|
|
45
|
+
import { executeRunCommand, parseRunCliArgs, readRunPrompt, runCliHelpText } from "./run-command.js";
|
|
44
46
|
const e = React.createElement;
|
|
45
|
-
class ReplForegroundExecDetachRegistry {
|
|
46
|
-
handle;
|
|
47
|
-
subscribers = new Set();
|
|
48
|
-
set(handle) {
|
|
49
|
-
this.handle = handle;
|
|
50
|
-
this.notify();
|
|
51
|
-
return () => {
|
|
52
|
-
if (this.handle === handle) {
|
|
53
|
-
this.handle = undefined;
|
|
54
|
-
this.notify();
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
current() {
|
|
59
|
-
return this.handle;
|
|
60
|
-
}
|
|
61
|
-
subscribe(listener) {
|
|
62
|
-
this.subscribers.add(listener);
|
|
63
|
-
return () => {
|
|
64
|
-
this.subscribers.delete(listener);
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
detachCurrent() {
|
|
68
|
-
const handle = this.handle;
|
|
69
|
-
if (!handle)
|
|
70
|
-
return { ok: false, message: "No foreground exec command is currently running" };
|
|
71
|
-
return handle.detach();
|
|
72
|
-
}
|
|
73
|
-
notify() {
|
|
74
|
-
for (const listener of this.subscribers)
|
|
75
|
-
listener();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
47
|
class SessionUsageTracker {
|
|
79
48
|
totals = emptyUsageTotals();
|
|
80
49
|
lastUsage;
|
|
@@ -132,6 +101,10 @@ function sumUsageTokens(left, right) {
|
|
|
132
101
|
return (left ?? 0) + (right ?? 0);
|
|
133
102
|
}
|
|
134
103
|
async function main(argv = process.argv.slice(2)) {
|
|
104
|
+
if (argv[0] === "run") {
|
|
105
|
+
await runNonInteractive(argv.slice(1));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
135
108
|
const webArgs = parseWebCliArgs(argv);
|
|
136
109
|
if (webArgs) {
|
|
137
110
|
await runWebServer(webArgs);
|
|
@@ -154,6 +127,49 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
154
127
|
await instance.waitUntilExit();
|
|
155
128
|
console.log("bye.");
|
|
156
129
|
}
|
|
130
|
+
async function runNonInteractive(argv) {
|
|
131
|
+
if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h")) {
|
|
132
|
+
console.log(runCliHelpText(binaryName()));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const parsed = parseRunCliArgs(argv);
|
|
136
|
+
if (!parsed.ok) {
|
|
137
|
+
console.error(`${parsed.error}\n\n${runCliHelpText(binaryName())}`);
|
|
138
|
+
process.exitCode = 2;
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
let prompt;
|
|
142
|
+
try {
|
|
143
|
+
prompt = await readRunPrompt(parsed.options.prompt, stdin);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
console.error(`${error instanceof Error ? error.message : String(error)}\n\n${runCliHelpText(binaryName())}`);
|
|
147
|
+
process.exitCode = 2;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const runtime = await createRuntime({ queryOrigin: "cli" });
|
|
151
|
+
if (parsed.options.model || parsed.options.reasoning) {
|
|
152
|
+
const current = runtime.engine.getModelSettings();
|
|
153
|
+
const reasoning = parsed.options.reasoning === undefined
|
|
154
|
+
? current.reasoning
|
|
155
|
+
: parsed.options.reasoning === "off"
|
|
156
|
+
? null
|
|
157
|
+
: parsed.options.reasoning === "default"
|
|
158
|
+
? undefined
|
|
159
|
+
: { effort: parsed.options.reasoning };
|
|
160
|
+
runtime.engine.setModel(parsed.options.model ?? current.model, reasoning, true);
|
|
161
|
+
}
|
|
162
|
+
const abortController = new AbortController();
|
|
163
|
+
const abort = () => abortController.abort();
|
|
164
|
+
process.once("SIGINT", abort);
|
|
165
|
+
try {
|
|
166
|
+
const result = await executeRunCommand(runtime.engine, prompt, parsed.options, { stdout, stderr: process.stderr }, abortController.signal);
|
|
167
|
+
process.exitCode = result.exitCode;
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
process.removeListener("SIGINT", abort);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
157
173
|
function parseWebCliArgs(argv) {
|
|
158
174
|
if (argv.length === 0)
|
|
159
175
|
return undefined;
|
|
@@ -230,17 +246,17 @@ function createTaskNotificationSource(taskStore) {
|
|
|
230
246
|
},
|
|
231
247
|
};
|
|
232
248
|
}
|
|
233
|
-
async function createRuntime() {
|
|
249
|
+
async function createRuntime(options = {}) {
|
|
234
250
|
const envLoad = loadDefaultDotEnvFiles({ override: true });
|
|
235
251
|
const modelConfig = readModelProviderConfig(process.env);
|
|
236
252
|
const communicationLogger = new CommunicationLogger();
|
|
237
253
|
const modelGateway = new LoggingModelGateway(createModelGatewayFromProcessEnv(process.env), communicationLogger);
|
|
238
254
|
const taskStore = new TaskStore();
|
|
239
255
|
const agentActivityStore = new AgentActivityStore();
|
|
240
|
-
const foregroundExecDetach = new ReplForegroundExecDetachRegistry();
|
|
241
256
|
const secretStore = await SecretStore.open();
|
|
242
257
|
const secretRedactions = new InMemorySecretRedactionRegistry();
|
|
243
258
|
const tools = new ToolRegistry();
|
|
259
|
+
const execProcessManager = new ExecProcessManager();
|
|
244
260
|
const skillWorkspaceRoot = path.resolve(process.cwd(), ".neo", "skills");
|
|
245
261
|
const skills = new FileSystemSkillCatalog({
|
|
246
262
|
roots: [
|
|
@@ -251,14 +267,15 @@ async function createRuntime() {
|
|
|
251
267
|
});
|
|
252
268
|
tools.register(editTool);
|
|
253
269
|
tools.register(writeTool);
|
|
254
|
-
tools.register(createExecTool({
|
|
270
|
+
tools.register(createExecTool({ processManager: execProcessManager }));
|
|
271
|
+
tools.register(createWriteStdinTool(execProcessManager));
|
|
255
272
|
tools.register(listDirectoryTool);
|
|
256
273
|
tools.register(readFileTool);
|
|
257
274
|
tools.register(grepTool);
|
|
258
275
|
tools.register(searchTool);
|
|
259
276
|
tools.register(createLoadImageTool());
|
|
260
277
|
if (modelConfig?.provider === "openai")
|
|
261
|
-
tools.register(createOpenAIImageGenerationTool(
|
|
278
|
+
tools.register(createOpenAIImageGenerationTool());
|
|
262
279
|
tools.register(planTool);
|
|
263
280
|
for (const tool of createSecretTools())
|
|
264
281
|
tools.register(tool);
|
|
@@ -283,8 +300,8 @@ async function createRuntime() {
|
|
|
283
300
|
const taskNotificationSource = createTaskNotificationSource(taskStore);
|
|
284
301
|
const engine = new QueryEngine({
|
|
285
302
|
agentId: "main",
|
|
303
|
+
queryOrigin: options.queryOrigin,
|
|
286
304
|
model: modelConfig?.model,
|
|
287
|
-
fallbackModel: modelConfig?.fallbackModel,
|
|
288
305
|
reasoning: modelConfig?.defaultReasoning,
|
|
289
306
|
modelGateway,
|
|
290
307
|
tools,
|
|
@@ -314,7 +331,6 @@ async function createRuntime() {
|
|
|
314
331
|
usage: new SessionUsageTracker(),
|
|
315
332
|
taskStore,
|
|
316
333
|
agentActivityStore,
|
|
317
|
-
foregroundExecDetach,
|
|
318
334
|
tools,
|
|
319
335
|
skills,
|
|
320
336
|
secretStore,
|
|
@@ -328,10 +344,10 @@ async function createRuntime() {
|
|
|
328
344
|
function syncImageGenerationTool(runtime, provider) {
|
|
329
345
|
runtime.tools.unregister("image2");
|
|
330
346
|
if (provider === "openai")
|
|
331
|
-
runtime.tools.register(createOpenAIImageGenerationTool(
|
|
347
|
+
runtime.tools.register(createOpenAIImageGenerationTool());
|
|
332
348
|
}
|
|
333
349
|
function formatCreatedEnvNotice(path) {
|
|
334
|
-
return `Created default config file: ${path}\nSet
|
|
350
|
+
return `Created default config file: ${path}\nSet OPENAI_API_KEY, then restart neo.`;
|
|
335
351
|
}
|
|
336
352
|
function parseResumeFlag(value) {
|
|
337
353
|
if (!value)
|
|
@@ -533,8 +549,6 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
533
549
|
const sessionTitleRef = useRef(sessionTerminalTitle(runtime.engine.snapshot().session));
|
|
534
550
|
const [backgroundTasks, setBackgroundTasks] = useState(() => activeBackgroundTasks(runtime));
|
|
535
551
|
const [agentActivities, setAgentActivities] = useState(() => runtime.agentActivityStore.list());
|
|
536
|
-
const [foregroundExecDetachHandle, setForegroundExecDetachHandle] = useState(() => runtime.foregroundExecDetach.current());
|
|
537
|
-
const [showForegroundExecDetachHint, setShowForegroundExecDetachHint] = useState(false);
|
|
538
552
|
const [backgroundSessionRuns, setBackgroundSessionRuns] = useState([]);
|
|
539
553
|
const backgroundSessionRunsRef = useRef(new Map());
|
|
540
554
|
const suppressReattachedStreamingRef = useRef(new Set());
|
|
@@ -598,25 +612,6 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
598
612
|
debouncedUpdateAgentActivities.cancel();
|
|
599
613
|
};
|
|
600
614
|
}, [runtime]);
|
|
601
|
-
useEffect(() => {
|
|
602
|
-
const updateForegroundExecDetachHandle = () => setForegroundExecDetachHandle(runtime.foregroundExecDetach.current());
|
|
603
|
-
updateForegroundExecDetachHandle();
|
|
604
|
-
return runtime.foregroundExecDetach.subscribe(updateForegroundExecDetachHandle);
|
|
605
|
-
}, [runtime]);
|
|
606
|
-
useEffect(() => {
|
|
607
|
-
if (!foregroundExecDetachHandle) {
|
|
608
|
-
setShowForegroundExecDetachHint(false);
|
|
609
|
-
return undefined;
|
|
610
|
-
}
|
|
611
|
-
const elapsedMs = Date.now() - foregroundExecDetachHandle.startedAt;
|
|
612
|
-
if (elapsedMs >= FOREGROUND_EXEC_DETACH_HINT_DELAY_MS) {
|
|
613
|
-
setShowForegroundExecDetachHint(true);
|
|
614
|
-
return undefined;
|
|
615
|
-
}
|
|
616
|
-
setShowForegroundExecDetachHint(false);
|
|
617
|
-
const timer = setTimeout(() => setShowForegroundExecDetachHint(true), FOREGROUND_EXEC_DETACH_HINT_DELAY_MS - elapsedMs);
|
|
618
|
-
return () => clearTimeout(timer);
|
|
619
|
-
}, [foregroundExecDetachHandle]);
|
|
620
615
|
useEffect(() => {
|
|
621
616
|
if (!terminalTitleWorking) {
|
|
622
617
|
setTerminalTitlePrefix(TERMINAL_TITLE_READY_PREFIX);
|
|
@@ -1347,13 +1342,6 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1347
1342
|
void handleClipboardPaste();
|
|
1348
1343
|
return;
|
|
1349
1344
|
}
|
|
1350
|
-
if (key.ctrl && value.toLowerCase() === "b") {
|
|
1351
|
-
const result = runtime.foregroundExecDetach.detachCurrent();
|
|
1352
|
-
append(result.ok
|
|
1353
|
-
? systemLine(`Detached foreground task to background task ${result.taskId ?? "unknown"}.`)
|
|
1354
|
-
: systemLine(result.message));
|
|
1355
|
-
return;
|
|
1356
|
-
}
|
|
1357
1345
|
if (key.ctrl && value === "c") {
|
|
1358
1346
|
if (inputRef.current.length > 0) {
|
|
1359
1347
|
setPromptState("", 0);
|
|
@@ -1654,7 +1642,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1654
1642
|
return;
|
|
1655
1643
|
}
|
|
1656
1644
|
});
|
|
1657
|
-
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 }),
|
|
1645
|
+
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 }), 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 }));
|
|
1658
1646
|
}
|
|
1659
1647
|
const MessageList = React.memo(function MessageList({ lines, width, lineIndexOffset = 0, onMarkdownRenderComplete }) {
|
|
1660
1648
|
const contentWidth = messageContentWidth(width);
|
|
@@ -1990,13 +1978,6 @@ function backgroundTaskStatusRenderRows(taskCount) {
|
|
|
1990
1978
|
return 0;
|
|
1991
1979
|
return 1 + Math.min(taskCount, 2);
|
|
1992
1980
|
}
|
|
1993
|
-
function ForegroundExecDetachHintLine({ handle, width: terminalWidth }) {
|
|
1994
|
-
const width = statusBarWidth(terminalWidth);
|
|
1995
|
-
const toolName = handle.toolName?.trim() || "exec";
|
|
1996
|
-
const label = handle.description?.trim() || handle.command;
|
|
1997
|
-
const text = `↳ ${toolName} still running · Ctrl+B to detach · ${truncateMiddle(label, Math.max(12, width - toolName.length - 33))}`;
|
|
1998
|
-
return e(Text, { color: "yellow" }, fitToWidth(text, width));
|
|
1999
|
-
}
|
|
2000
1981
|
function SubagentLivePanel({ activities, width: terminalWidth, animationTick }) {
|
|
2001
1982
|
const width = statusBarWidth(terminalWidth);
|
|
2002
1983
|
const sorted = sortAgentActivitiesForPanel(activities);
|
|
@@ -2678,7 +2659,6 @@ async function handleModelCommand(command, runtime) {
|
|
|
2678
2659
|
runtime.engine.setModelProvider({
|
|
2679
2660
|
modelGateway: runtime.modelGateway,
|
|
2680
2661
|
model: config.model,
|
|
2681
|
-
fallbackModel: config.fallbackModel,
|
|
2682
2662
|
reasoning: config.defaultReasoning,
|
|
2683
2663
|
});
|
|
2684
2664
|
syncImageGenerationTool(runtime, config.provider);
|
|
@@ -2733,11 +2713,9 @@ async function persistModelCommandSettings(runtime, command, reasoningUpdate) {
|
|
|
2733
2713
|
return { providerChanged: targetProvider !== currentProvider };
|
|
2734
2714
|
}
|
|
2735
2715
|
function currentModelProvider() {
|
|
2736
|
-
return
|
|
2716
|
+
return "openai";
|
|
2737
2717
|
}
|
|
2738
|
-
function modelEnvKeyForProvider(
|
|
2739
|
-
if (provider === "anthropic")
|
|
2740
|
-
return "ANTHROPIC_MODEL";
|
|
2718
|
+
function modelEnvKeyForProvider(_provider) {
|
|
2741
2719
|
return "OPENAI_MODEL";
|
|
2742
2720
|
}
|
|
2743
2721
|
function envValueForReasoning(reasoning) {
|
|
@@ -2777,8 +2755,6 @@ function formatModelSettings(settings, defaultReasoning) {
|
|
|
2777
2755
|
"Model settings:",
|
|
2778
2756
|
` Model: ${settings.model ?? "<provider default>"}`,
|
|
2779
2757
|
];
|
|
2780
|
-
if (settings.fallbackModel)
|
|
2781
|
-
lines.push(` Fallback: ${settings.fallbackModel}`);
|
|
2782
2758
|
lines.push(` Reasoning effort: ${formatReasoningSetting(settings.reasoning)}`);
|
|
2783
2759
|
if (defaultReasoning?.effort)
|
|
2784
2760
|
lines.push(` Env default reasoning: ${defaultReasoning.effort}`);
|
|
@@ -3077,11 +3053,11 @@ function restoredHistoryLines(runtime) {
|
|
|
3077
3053
|
}
|
|
3078
3054
|
return lines;
|
|
3079
3055
|
}
|
|
3080
|
-
const LOGIN_PROVIDERS = ["openai"
|
|
3056
|
+
const LOGIN_PROVIDERS = ["openai"];
|
|
3081
3057
|
const SHARED_LOGIN_FIELDS = [
|
|
3082
3058
|
{ key: "reasoningEffort", label: "Reasoning effort", envKey: "MODEL_REASONING_EFFORT", scope: "shared", options: ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"] },
|
|
3083
3059
|
{ key: "reasoningSummary", label: "Reasoning summary", envKey: "MODEL_REASONING_SUMMARY", scope: "shared", options: ["", "auto", "concise", "detailed"] },
|
|
3084
|
-
{ key: "maxOutputTokens", label: "Max output tokens", envKey: "MODEL_MAX_OUTPUT_TOKENS", scope: "shared"
|
|
3060
|
+
{ key: "maxOutputTokens", label: "Max output tokens", envKey: "MODEL_MAX_OUTPUT_TOKENS", scope: "shared" },
|
|
3085
3061
|
{ key: "timeoutMs", label: "Timeout ms", envKey: "MODEL_TIMEOUT_MS", scope: "shared", placeholder: "120000" },
|
|
3086
3062
|
{ key: "streamIdleTimeoutMs", label: "Stream idle timeout ms", envKey: "MODEL_STREAM_IDLE_TIMEOUT_MS", scope: "shared", placeholder: "120000" },
|
|
3087
3063
|
{ key: "maxRetries", label: "Max retries", envKey: "MODEL_MAX_RETRIES", scope: "shared", placeholder: "2" },
|
|
@@ -3091,18 +3067,9 @@ const LOGIN_FIELD_DEFINITIONS = {
|
|
|
3091
3067
|
{ key: "apiKey", label: "API key", envKey: "OPENAI_API_KEY", scope: "provider", required: true, secret: true, placeholder: "sk-..." },
|
|
3092
3068
|
{ key: "baseUrl", label: "Base URL", envKey: "OPENAI_BASE_URL", scope: "provider", placeholder: "https://api.openai.com" },
|
|
3093
3069
|
{ key: "model", label: "Model", envKey: "OPENAI_MODEL", scope: "provider", required: true, placeholder: "gpt-5.6" },
|
|
3094
|
-
{ key: "fallbackModel", label: "Fallback model", envKey: "OPENAI_FALLBACK_MODEL", scope: "provider" },
|
|
3095
3070
|
{ key: "endpoint", label: "Endpoint", envKey: "OPENAI_ENDPOINT", scope: "provider", placeholder: "auto", options: ["auto", "responses", "chat"] },
|
|
3096
3071
|
...SHARED_LOGIN_FIELDS,
|
|
3097
3072
|
],
|
|
3098
|
-
anthropic: [
|
|
3099
|
-
{ key: "apiKey", label: "API key", envKey: "ANTHROPIC_API_KEY", scope: "provider", required: true, secret: true, placeholder: "sk-ant-..." },
|
|
3100
|
-
{ key: "baseUrl", label: "Base URL", envKey: "ANTHROPIC_BASE_URL", scope: "provider", placeholder: "https://api.anthropic.com" },
|
|
3101
|
-
{ key: "model", label: "Model", envKey: "ANTHROPIC_MODEL", scope: "provider", required: true, placeholder: "claude-sonnet-4-6" },
|
|
3102
|
-
{ key: "fallbackModel", label: "Fallback model", envKey: "ANTHROPIC_FALLBACK_MODEL", scope: "provider" },
|
|
3103
|
-
{ key: "version", label: "Anthropic version", envKey: "ANTHROPIC_VERSION", scope: "provider", placeholder: "2023-06-01" },
|
|
3104
|
-
...SHARED_LOGIN_FIELDS,
|
|
3105
|
-
],
|
|
3106
3073
|
};
|
|
3107
3074
|
const DEPRECATED_MODEL_ENV_KEYS = [
|
|
3108
3075
|
"MODEL_API_KEY",
|
|
@@ -3117,12 +3084,6 @@ const DEPRECATED_MODEL_ENV_KEYS = [
|
|
|
3117
3084
|
"OPENAI_TIMEOUT_MS",
|
|
3118
3085
|
"OPENAI_STREAM_IDLE_TIMEOUT_MS",
|
|
3119
3086
|
"OPENAI_MAX_RETRIES",
|
|
3120
|
-
"ANTHROPIC_REASONING_EFFORT",
|
|
3121
|
-
"ANTHROPIC_REASONING_SUMMARY",
|
|
3122
|
-
"ANTHROPIC_MAX_OUTPUT_TOKENS",
|
|
3123
|
-
"ANTHROPIC_TIMEOUT_MS",
|
|
3124
|
-
"ANTHROPIC_STREAM_IDLE_TIMEOUT_MS",
|
|
3125
|
-
"ANTHROPIC_MAX_RETRIES",
|
|
3126
3087
|
];
|
|
3127
3088
|
function pagedPageCount(state) {
|
|
3128
3089
|
return Math.max(1, Math.ceil(state.items.length / state.pageSize));
|
|
@@ -3348,7 +3309,6 @@ async function submitLoginForm(state, runtime, append, setLoginFormState, setSta
|
|
|
3348
3309
|
runtime.engine.setModelProvider({
|
|
3349
3310
|
modelGateway: runtime.modelGateway,
|
|
3350
3311
|
model: config.model,
|
|
3351
|
-
fallbackModel: config.fallbackModel,
|
|
3352
3312
|
reasoning: config.defaultReasoning,
|
|
3353
3313
|
});
|
|
3354
3314
|
syncImageGenerationTool(runtime, config.provider);
|
|
@@ -3379,6 +3339,9 @@ function validateLoginForm(state) {
|
|
|
3379
3339
|
if (value && !Number.isFinite(Number(value)))
|
|
3380
3340
|
return `${fieldKey} must be a number.`;
|
|
3381
3341
|
}
|
|
3342
|
+
if (state.provider === "openai" && state.values.endpoint === "chat" && state.values.reasoningSummary?.trim()) {
|
|
3343
|
+
return "Reasoning summary requires the Responses API endpoint.";
|
|
3344
|
+
}
|
|
3382
3345
|
return undefined;
|
|
3383
3346
|
}
|
|
3384
3347
|
function createLoginFormState(envPath = getUserDotEnvPath()) {
|
|
@@ -3413,23 +3376,17 @@ function loginValuesForProvider(provider, env) {
|
|
|
3413
3376
|
return values;
|
|
3414
3377
|
}
|
|
3415
3378
|
function parseLoginProvider(value) {
|
|
3416
|
-
if (value === "openai"
|
|
3379
|
+
if (value === "openai")
|
|
3417
3380
|
return value;
|
|
3418
3381
|
return undefined;
|
|
3419
3382
|
}
|
|
3420
|
-
function guessLoginProvider(
|
|
3421
|
-
if (env.ANTHROPIC_API_KEY ?? process.env.ANTHROPIC_API_KEY)
|
|
3422
|
-
return "anthropic";
|
|
3383
|
+
function guessLoginProvider(_env) {
|
|
3423
3384
|
return "openai";
|
|
3424
3385
|
}
|
|
3425
|
-
function defaultBaseUrlForLoginProvider(
|
|
3426
|
-
if (provider === "anthropic")
|
|
3427
|
-
return "https://api.anthropic.com";
|
|
3386
|
+
function defaultBaseUrlForLoginProvider(_provider) {
|
|
3428
3387
|
return "https://api.openai.com";
|
|
3429
3388
|
}
|
|
3430
|
-
function defaultModelForLoginProvider(
|
|
3431
|
-
if (provider === "anthropic")
|
|
3432
|
-
return "claude-sonnet-4-6";
|
|
3389
|
+
function defaultModelForLoginProvider(_provider) {
|
|
3433
3390
|
return "gpt-5.6";
|
|
3434
3391
|
}
|
|
3435
3392
|
function loginFormViewHeight(state) {
|
|
@@ -3448,7 +3405,7 @@ function LoginFormView({ state, width }) {
|
|
|
3448
3405
|
const visibleValue = formatLoginFieldValue(field, rawValue, selected ? state.cursor : undefined);
|
|
3449
3406
|
const placeholder = rawValue ? "" : (field.placeholder ? ` (${field.placeholder})` : "");
|
|
3450
3407
|
return e(Text, { key: field.key, color: "white" }, e(Text, { color: selected ? "black" : "white", backgroundColor: selected ? "cyan" : undefined }, `${index + 1}.`.padStart(3)), e(Text, { color: field.required ? "yellow" : "gray" }, ` ${field.label.padEnd(maxLabel)} `), e(Text, { color: field.scope === "shared" ? "blue" : "gray" }, field.scope === "shared" ? "shared " : "provider "), e(Text, { color: rawValue ? "white" : "gray" }, fitToWidth(`${visibleValue}${placeholder}`, Math.max(8, contentWidth - maxLabel - 14))));
|
|
3451
|
-
}), e(Text, { color: "gray" }, fitToWidth("↑/↓ field · ←/→ cursor · type edit · Tab cycle choices · Enter save · Esc back/cancel", contentWidth)), e(Text, { color: "gray" }, fitToWidth("Provider fields save as OPENAI_
|
|
3408
|
+
}), e(Text, { color: "gray" }, fitToWidth("↑/↓ field · ←/→ cursor · type edit · Tab cycle choices · Enter save · Esc back/cancel", contentWidth)), e(Text, { color: "gray" }, fitToWidth("Provider fields save as OPENAI_*; shared runtime fields save as MODEL_*.", contentWidth)));
|
|
3452
3409
|
}
|
|
3453
3410
|
function formatLoginFieldValue(field, value, cursor) {
|
|
3454
3411
|
const display = field.secret && value ? "•".repeat(Math.min(value.length, 24)) : value;
|
|
@@ -3500,7 +3457,6 @@ function updateEnvContent(content, updates, removeKeys = []) {
|
|
|
3500
3457
|
const grouped = groupLoginEnvEntries(missing);
|
|
3501
3458
|
appendEnvGroup(updatedLines, "# Neo active provider", grouped.active);
|
|
3502
3459
|
appendEnvGroup(updatedLines, "# OpenAI provider settings", grouped.openai);
|
|
3503
|
-
appendEnvGroup(updatedLines, "# Anthropic provider settings", grouped.anthropic);
|
|
3504
3460
|
appendEnvGroup(updatedLines, "# Shared model runtime settings", grouped.shared);
|
|
3505
3461
|
}
|
|
3506
3462
|
return `${updatedLines.join("\n").replace(/\n*$/u, "")}\n`;
|
|
@@ -3509,7 +3465,6 @@ function groupLoginEnvEntries(entries) {
|
|
|
3509
3465
|
return {
|
|
3510
3466
|
active: entries.filter(([key]) => key === "MODEL_PROVIDER"),
|
|
3511
3467
|
openai: entries.filter(([key]) => key.startsWith("OPENAI_")),
|
|
3512
|
-
anthropic: entries.filter(([key]) => key.startsWith("ANTHROPIC_")),
|
|
3513
3468
|
shared: entries.filter(([key]) => key.startsWith("MODEL_") && key !== "MODEL_PROVIDER"),
|
|
3514
3469
|
};
|
|
3515
3470
|
}
|
|
@@ -3702,7 +3657,7 @@ function formatToolUse(toolUse) {
|
|
|
3702
3657
|
text: formatPlanToolPayload(toolUse.input),
|
|
3703
3658
|
};
|
|
3704
3659
|
}
|
|
3705
|
-
const description = toolUse.name === "
|
|
3660
|
+
const description = toolUse.name === "exec_command" ? execDescriptionFromInput(toolUse.input) : undefined;
|
|
3706
3661
|
return {
|
|
3707
3662
|
kind: "tool",
|
|
3708
3663
|
title: toolTitle(toolUse.name),
|
|
@@ -3735,7 +3690,8 @@ function toolTitle(toolName) {
|
|
|
3735
3690
|
if (toolName === "plan")
|
|
3736
3691
|
return "\u25c6 plan";
|
|
3737
3692
|
const labels = {
|
|
3738
|
-
|
|
3693
|
+
exec_command: "command",
|
|
3694
|
+
write_stdin: "terminal input",
|
|
3739
3695
|
read: "file read",
|
|
3740
3696
|
list: "directory listing",
|
|
3741
3697
|
grep: "search",
|
|
@@ -4101,21 +4057,23 @@ function isExecOutput(value) {
|
|
|
4101
4057
|
return false;
|
|
4102
4058
|
const record = value;
|
|
4103
4059
|
return (typeof record.command === "string" &&
|
|
4104
|
-
(typeof record.
|
|
4105
|
-
typeof record.
|
|
4106
|
-
typeof record.
|
|
4060
|
+
(typeof record.exit_code === "number" || record.exit_code === null) &&
|
|
4061
|
+
typeof record.timed_out === "boolean" &&
|
|
4062
|
+
typeof record.duration_ms === "number" &&
|
|
4107
4063
|
typeof record.stdout === "string" &&
|
|
4108
4064
|
typeof record.stderr === "string");
|
|
4109
4065
|
}
|
|
4110
4066
|
function formatExecToolResult(output, ok) {
|
|
4111
|
-
const status = output.
|
|
4112
|
-
?
|
|
4113
|
-
: output.
|
|
4114
|
-
? "
|
|
4115
|
-
:
|
|
4067
|
+
const status = output.status === "running"
|
|
4068
|
+
? `running as terminal ${output.session_id ?? "unknown"}`
|
|
4069
|
+
: output.timed_out
|
|
4070
|
+
? "timed out"
|
|
4071
|
+
: output.exit_code === 0
|
|
4072
|
+
? "exit 0"
|
|
4073
|
+
: `exit ${output.exit_code ?? output.signal ?? "unknown"}`;
|
|
4116
4074
|
const description = typeof output.description === "string" ? output.description.trim() : "";
|
|
4117
4075
|
const lines = [
|
|
4118
|
-
`${ok && output.
|
|
4076
|
+
`${output.status === "running" ? "command yielded" : ok && output.exit_code === 0 && !output.timed_out ? "command completed" : "command failed"}: ${status} in ${formatDuration(output.duration_ms)}`,
|
|
4119
4077
|
...formatCommandPreview(output.command),
|
|
4120
4078
|
];
|
|
4121
4079
|
if (description)
|
|
@@ -4661,8 +4619,6 @@ const STATUS_SHIMMER_RADIUS = 1;
|
|
|
4661
4619
|
const STATUS_SHIMMER_COLOR = "whiteBright";
|
|
4662
4620
|
const STATUS_SEPARATOR = " · ";
|
|
4663
4621
|
const STATUS_BAR_RENDER_ROWS = 1;
|
|
4664
|
-
const FOREGROUND_EXEC_DETACH_HINT_RENDER_ROWS = 1;
|
|
4665
|
-
const FOREGROUND_EXEC_DETACH_HINT_DELAY_MS = 2000;
|
|
4666
4622
|
const BACKGROUND_TASK_STATUS_RENDER_ROWS = 1;
|
|
4667
4623
|
const QUEUED_INPUT_RENDER_ROWS = 1;
|
|
4668
4624
|
const EMPTY_CTRL_C_EXIT_PLACEHOLDER = "Press Ctrl+C again to exit";
|