neoctl 0.2.5 → 0.2.7
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 +16 -26
- package/dist/agents/agent-activity.d.ts +70 -0
- package/dist/agents/agent-activity.js +261 -0
- package/dist/agents/agent-activity.js.map +1 -0
- package/dist/agents/agent-definition.d.ts +7 -0
- package/dist/agents/agent-definition.js +44 -1
- package/dist/agents/agent-definition.js.map +1 -1
- package/dist/agents/agent-report-tool.d.ts +11 -0
- package/dist/agents/agent-report-tool.js +50 -0
- package/dist/agents/agent-report-tool.js.map +1 -0
- package/dist/agents/agent-tool.d.ts +3 -1
- package/dist/agents/agent-tool.js +56 -11
- package/dist/agents/agent-tool.js.map +1 -1
- package/dist/agents/local-agent-task.d.ts +3 -0
- package/dist/agents/local-agent-task.js +2 -0
- package/dist/agents/local-agent-task.js.map +1 -1
- package/dist/agents/smoke-agents.js +131 -7
- package/dist/agents/smoke-agents.js.map +1 -1
- package/dist/context/prompts.js +4 -0
- package/dist/context/prompts.js.map +1 -1
- package/dist/core/query-engine.d.ts +3 -1
- package/dist/core/query-engine.js +2 -0
- package/dist/core/query-engine.js.map +1 -1
- package/dist/core/query.d.ts +4 -0
- package/dist/core/query.js +10 -1
- package/dist/core/query.js.map +1 -1
- package/dist/core/run-agent.d.ts +2 -0
- package/dist/core/run-agent.js +156 -21
- package/dist/core/run-agent.js.map +1 -1
- package/dist/core/smoke-core-loop.js +1 -1
- package/dist/core/smoke-core-loop.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/model/anthropic-mapper.js +27 -2
- package/dist/model/anthropic-mapper.js.map +1 -1
- package/dist/model/config.d.ts +2 -8
- package/dist/model/config.js +1 -41
- package/dist/model/config.js.map +1 -1
- package/dist/model/env.js +6 -11
- package/dist/model/env.js.map +1 -1
- package/dist/model/model-metadata.json +1 -115
- package/dist/model/openai-chat-mapper.js +4 -19
- package/dist/model/openai-chat-mapper.js.map +1 -1
- package/dist/model/provider-factory.js +0 -32
- package/dist/model/provider-factory.js.map +1 -1
- package/dist/model/smoke-anthropic-mapper.js +6 -2
- package/dist/model/smoke-anthropic-mapper.js.map +1 -1
- package/dist/repl/commands.d.ts +8 -0
- package/dist/repl/commands.js +35 -1
- package/dist/repl/commands.js.map +1 -1
- package/dist/repl/index.js +731 -127
- package/dist/repl/index.js.map +1 -1
- package/dist/secrets/secret-crypto.d.ts +22 -0
- package/dist/secrets/secret-crypto.js +58 -0
- package/dist/secrets/secret-crypto.js.map +1 -0
- package/dist/secrets/secret-redaction.d.ts +8 -0
- package/dist/secrets/secret-redaction.js +40 -0
- package/dist/secrets/secret-redaction.js.map +1 -0
- package/dist/secrets/secret-store.d.ts +28 -0
- package/dist/secrets/secret-store.js +158 -0
- package/dist/secrets/secret-store.js.map +1 -0
- package/dist/secrets/secret-types.d.ts +31 -0
- package/dist/secrets/secret-types.js +17 -0
- package/dist/secrets/secret-types.js.map +1 -0
- package/dist/secrets/smoke-secrets.js +68 -0
- package/dist/secrets/smoke-secrets.js.map +1 -0
- package/dist/tools/builtins/exec-tool.d.ts +20 -1
- package/dist/tools/builtins/exec-tool.js +164 -29
- package/dist/tools/builtins/exec-tool.js.map +1 -1
- package/dist/tools/builtins/plan-tool.d.ts +1 -0
- package/dist/tools/builtins/plan-tool.js +80 -27
- package/dist/tools/builtins/plan-tool.js.map +1 -1
- package/dist/tools/builtins/secret-tools.d.ts +10 -0
- package/dist/tools/builtins/secret-tools.js +75 -0
- package/dist/tools/builtins/secret-tools.js.map +1 -0
- package/dist/tools/run-tool-use.js +4 -2
- package/dist/tools/run-tool-use.js.map +1 -1
- package/dist/tools/smoke-tool-system.js +42 -10
- package/dist/tools/smoke-tool-system.js.map +1 -1
- package/dist/tools/tool.d.ts +3 -0
- package/dist/tools/tool.js.map +1 -1
- package/dist/web/html.js +1 -1
- package/dist/web/index.js +11 -50
- package/dist/web/index.js.map +1 -1
- package/package.json +5 -3
- package/scripts/install-ripgrep.cjs +62 -18
- package/vendor/ripgrep/darwin-arm64/COPYING +3 -0
- package/vendor/ripgrep/darwin-arm64/LICENSE-MIT +21 -0
- package/vendor/ripgrep/darwin-arm64/UNLICENSE +24 -0
- package/vendor/ripgrep/darwin-arm64/manifest.json +7 -0
- package/vendor/ripgrep/darwin-arm64/rg +0 -0
- package/vendor/ripgrep/darwin-x64/COPYING +3 -0
- package/vendor/ripgrep/darwin-x64/LICENSE-MIT +21 -0
- package/vendor/ripgrep/darwin-x64/UNLICENSE +24 -0
- package/vendor/ripgrep/darwin-x64/manifest.json +7 -0
- package/vendor/ripgrep/darwin-x64/rg +0 -0
- package/vendor/ripgrep/linux-arm64/COPYING +3 -0
- package/vendor/ripgrep/linux-arm64/LICENSE-MIT +21 -0
- package/vendor/ripgrep/linux-arm64/UNLICENSE +24 -0
- package/vendor/ripgrep/linux-arm64/manifest.json +7 -0
- package/vendor/ripgrep/linux-arm64/rg +0 -0
- package/vendor/ripgrep/linux-x64/COPYING +3 -0
- package/vendor/ripgrep/linux-x64/LICENSE-MIT +21 -0
- package/vendor/ripgrep/linux-x64/UNLICENSE +24 -0
- package/vendor/ripgrep/linux-x64/manifest.json +7 -0
- package/vendor/ripgrep/linux-x64/rg +0 -0
- package/vendor/ripgrep/win32-arm64/COPYING +3 -0
- package/vendor/ripgrep/win32-arm64/LICENSE-MIT +21 -0
- package/vendor/ripgrep/win32-arm64/UNLICENSE +24 -0
- package/vendor/ripgrep/win32-arm64/manifest.json +7 -0
- package/vendor/ripgrep/win32-arm64/rg.exe +0 -0
- package/vendor/ripgrep/win32-x64/COPYING +3 -0
- package/vendor/ripgrep/win32-x64/LICENSE-MIT +21 -0
- package/vendor/ripgrep/win32-x64/UNLICENSE +24 -0
- package/vendor/ripgrep/win32-x64/manifest.json +7 -0
- package/vendor/ripgrep/win32-x64/rg.exe +0 -0
- package/dist/model/deepseek-adapter.d.ts +0 -29
- package/dist/model/deepseek-adapter.js +0 -108
- package/dist/model/deepseek-adapter.js.map +0 -1
- package/dist/model/kimi-adapter.d.ts +0 -29
- package/dist/model/kimi-adapter.js +0 -108
- package/dist/model/kimi-adapter.js.map +0 -1
- package/dist/model/smoke-deepseek-mapper.js +0 -65
- package/dist/model/smoke-deepseek-mapper.js.map +0 -1
- /package/dist/{model/smoke-deepseek-mapper.d.ts → secrets/smoke-secrets.d.ts} +0 -0
package/dist/repl/index.js
CHANGED
|
@@ -22,7 +22,11 @@ import { searchTool } from "../tools/builtins/search-tool.js";
|
|
|
22
22
|
import { planTool } from "../tools/builtins/plan-tool.js";
|
|
23
23
|
import { createOpenAIImageGenerationTool } from "../tools/builtins/image-generation-tool.js";
|
|
24
24
|
import { createLoadImageTool } from "../tools/builtins/image-loader-tool.js";
|
|
25
|
+
import { createSecretTools } from "../tools/builtins/secret-tools.js";
|
|
26
|
+
import { SecretStore } from "../secrets/secret-store.js";
|
|
27
|
+
import { InMemorySecretRedactionRegistry } from "../secrets/secret-redaction.js";
|
|
25
28
|
import { createAgentTool, resumeAgentTask } from "../agents/agent-tool.js";
|
|
29
|
+
import { AgentActivityStore } from "../agents/agent-activity.js";
|
|
26
30
|
import { createTaskTools } from "../tasks/task-tools.js";
|
|
27
31
|
import { TaskStore } from "../tasks/task-store.js";
|
|
28
32
|
import { cliHelpText, isModelReasoningArgument, isValidReplCommandLine, parseCliReplCommandArgs, parseReplCommand, helpText, replCommandDefinitions } from "./commands.js";
|
|
@@ -39,6 +43,39 @@ import { FileSystemSkillCatalog } from "../skills/skill-filesystem.js";
|
|
|
39
43
|
import { createSkillAwareCanUseTool, createSkillTool, requireSkillName } from "../skills/skill-tool.js";
|
|
40
44
|
import { createSkillManagementTools } from "../skills/skill-management-tools.js";
|
|
41
45
|
const e = React.createElement;
|
|
46
|
+
class ReplForegroundExecDetachRegistry {
|
|
47
|
+
handle;
|
|
48
|
+
subscribers = new Set();
|
|
49
|
+
set(handle) {
|
|
50
|
+
this.handle = handle;
|
|
51
|
+
this.notify();
|
|
52
|
+
return () => {
|
|
53
|
+
if (this.handle === handle) {
|
|
54
|
+
this.handle = undefined;
|
|
55
|
+
this.notify();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
current() {
|
|
60
|
+
return this.handle;
|
|
61
|
+
}
|
|
62
|
+
subscribe(listener) {
|
|
63
|
+
this.subscribers.add(listener);
|
|
64
|
+
return () => {
|
|
65
|
+
this.subscribers.delete(listener);
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
detachCurrent() {
|
|
69
|
+
const handle = this.handle;
|
|
70
|
+
if (!handle)
|
|
71
|
+
return { ok: false, message: "No foreground exec command is currently running" };
|
|
72
|
+
return handle.detach();
|
|
73
|
+
}
|
|
74
|
+
notify() {
|
|
75
|
+
for (const listener of this.subscribers)
|
|
76
|
+
listener();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
42
79
|
class SessionUsageTracker {
|
|
43
80
|
totals = emptyUsageTotals();
|
|
44
81
|
lastUsage;
|
|
@@ -200,6 +237,10 @@ async function createRuntime() {
|
|
|
200
237
|
const communicationLogger = new CommunicationLogger();
|
|
201
238
|
const modelGateway = new LoggingModelGateway(createModelGatewayFromProcessEnv(process.env), communicationLogger);
|
|
202
239
|
const taskStore = new TaskStore();
|
|
240
|
+
const agentActivityStore = new AgentActivityStore();
|
|
241
|
+
const foregroundExecDetach = new ReplForegroundExecDetachRegistry();
|
|
242
|
+
const secretStore = await SecretStore.open();
|
|
243
|
+
const secretRedactions = new InMemorySecretRedactionRegistry();
|
|
203
244
|
const tools = new ToolRegistry();
|
|
204
245
|
const skillWorkspaceRoot = path.resolve(process.cwd(), ".neo", "skills");
|
|
205
246
|
const skills = new FileSystemSkillCatalog({
|
|
@@ -211,7 +252,7 @@ async function createRuntime() {
|
|
|
211
252
|
});
|
|
212
253
|
tools.register(editTool);
|
|
213
254
|
tools.register(writeTool);
|
|
214
|
-
tools.register(createExecTool({ taskStore }));
|
|
255
|
+
tools.register(createExecTool({ taskStore, foregroundDetachRegistry: foregroundExecDetach }));
|
|
215
256
|
tools.register(listDirectoryTool);
|
|
216
257
|
tools.register(readFileTool);
|
|
217
258
|
tools.register(grepTool);
|
|
@@ -220,16 +261,20 @@ async function createRuntime() {
|
|
|
220
261
|
if (modelConfig?.provider === "openai")
|
|
221
262
|
tools.register(createOpenAIImageGenerationTool());
|
|
222
263
|
tools.register(planTool);
|
|
264
|
+
for (const tool of createSecretTools())
|
|
265
|
+
tools.register(tool);
|
|
223
266
|
tools.register(createSkillTool(skills));
|
|
224
267
|
for (const tool of createSkillManagementTools(skills, { requireApproval: true, allowDelete: false }))
|
|
225
268
|
tools.register(tool);
|
|
226
|
-
const agentRuntime = { modelGateway, tools, taskStore };
|
|
269
|
+
const agentRuntime = { modelGateway, tools, taskStore, agentActivityStore };
|
|
227
270
|
tools.register(createAgentTool(agentRuntime));
|
|
228
271
|
const resumeHandler = async (taskId, directive) => {
|
|
229
272
|
const dummyContext = {
|
|
230
273
|
agentId: "main",
|
|
231
274
|
tools,
|
|
232
275
|
appState: new (await import("../app/app-state.js")).InMemoryAppState("main"),
|
|
276
|
+
secrets: secretStore,
|
|
277
|
+
secretRedactions,
|
|
233
278
|
emit: () => undefined,
|
|
234
279
|
};
|
|
235
280
|
return resumeAgentTask(taskId, directive, agentRuntime, taskStore, dummyContext);
|
|
@@ -246,6 +291,8 @@ async function createRuntime() {
|
|
|
246
291
|
tools,
|
|
247
292
|
contextManager: new SkillCatalogContextManager(skills),
|
|
248
293
|
canUseTool: createSkillAwareCanUseTool(skills),
|
|
294
|
+
secrets: secretStore,
|
|
295
|
+
secretRedactions,
|
|
249
296
|
taskNotificationSource,
|
|
250
297
|
commands: replCommandDefinitions.map((command) => command.usage),
|
|
251
298
|
session: {
|
|
@@ -267,8 +314,11 @@ async function createRuntime() {
|
|
|
267
314
|
agentRuntime,
|
|
268
315
|
usage: new SessionUsageTracker(),
|
|
269
316
|
taskStore,
|
|
317
|
+
agentActivityStore,
|
|
318
|
+
foregroundExecDetach,
|
|
270
319
|
tools,
|
|
271
320
|
skills,
|
|
321
|
+
secretStore,
|
|
272
322
|
skillWorkspaceRoot,
|
|
273
323
|
initialMetrics,
|
|
274
324
|
defaultReasoning: modelConfig?.defaultReasoning,
|
|
@@ -282,7 +332,7 @@ function syncImageGenerationTool(runtime, provider) {
|
|
|
282
332
|
runtime.tools.register(createOpenAIImageGenerationTool());
|
|
283
333
|
}
|
|
284
334
|
function formatCreatedEnvNotice(path) {
|
|
285
|
-
return `Created default config file: ${path}\nSet MODEL_PROVIDER and the matching provider section (
|
|
335
|
+
return `Created default config file: ${path}\nSet MODEL_PROVIDER and the matching provider section (OPENAI_API_KEY or ANTHROPIC_API_KEY), then restart neo.`;
|
|
286
336
|
}
|
|
287
337
|
function parseResumeFlag(value) {
|
|
288
338
|
if (!value)
|
|
@@ -292,6 +342,34 @@ function parseResumeFlag(value) {
|
|
|
292
342
|
function activeBackgroundTasks(runtime) {
|
|
293
343
|
return runtime.taskStore.list().filter((task) => !runtime.taskStore.isTerminal(task));
|
|
294
344
|
}
|
|
345
|
+
function debounceVoid(callback, delayMs) {
|
|
346
|
+
let timer;
|
|
347
|
+
return {
|
|
348
|
+
run: () => {
|
|
349
|
+
if (timer)
|
|
350
|
+
clearTimeout(timer);
|
|
351
|
+
timer = setTimeout(() => {
|
|
352
|
+
timer = undefined;
|
|
353
|
+
callback();
|
|
354
|
+
}, delayMs);
|
|
355
|
+
},
|
|
356
|
+
cancel: () => {
|
|
357
|
+
if (!timer)
|
|
358
|
+
return;
|
|
359
|
+
clearTimeout(timer);
|
|
360
|
+
timer = undefined;
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function activeAgentActivities(runtime) {
|
|
365
|
+
const now = Date.now();
|
|
366
|
+
return runtime.agentActivityStore.list().filter((activity) => {
|
|
367
|
+
if (activity.status === "running" || activity.status === "pending")
|
|
368
|
+
return true;
|
|
369
|
+
const completedAt = activity.completedAt ? new Date(activity.completedAt).getTime() : new Date(activity.updatedAt).getTime();
|
|
370
|
+
return Number.isFinite(completedAt) && now - completedAt < SUBAGENT_COMPLETED_LINGER_MS;
|
|
371
|
+
});
|
|
372
|
+
}
|
|
295
373
|
function runningSessionIds(runs) {
|
|
296
374
|
return [...runs.keys()];
|
|
297
375
|
}
|
|
@@ -458,6 +536,9 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
458
536
|
const [status, setStatus] = useState(() => initialStatus(runtime));
|
|
459
537
|
const sessionTitleRef = useRef(sessionTerminalTitle(runtime.engine.snapshot().session));
|
|
460
538
|
const [backgroundTasks, setBackgroundTasks] = useState(() => activeBackgroundTasks(runtime));
|
|
539
|
+
const [agentActivities, setAgentActivities] = useState(() => runtime.agentActivityStore.list());
|
|
540
|
+
const [foregroundExecDetachHandle, setForegroundExecDetachHandle] = useState(() => runtime.foregroundExecDetach.current());
|
|
541
|
+
const [showForegroundExecDetachHint, setShowForegroundExecDetachHint] = useState(false);
|
|
461
542
|
const [backgroundSessionRuns, setBackgroundSessionRuns] = useState([]);
|
|
462
543
|
const backgroundSessionRunsRef = useRef(new Map());
|
|
463
544
|
const suppressReattachedStreamingRef = useRef(new Set());
|
|
@@ -468,6 +549,8 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
468
549
|
const backgroundTaskCount = backgroundTasks.length;
|
|
469
550
|
const terminalTitleWorking = isActivePhase(status.phase) || backgroundTaskCount > 0 || backgroundSessionRuns.length > 0;
|
|
470
551
|
const [sessionsBrowser, setSessionsBrowser] = useState(undefined);
|
|
552
|
+
const [skillsBrowser, setSkillsBrowser] = useState(undefined);
|
|
553
|
+
const [secretsBrowser, setSecretsBrowser] = useState(undefined);
|
|
471
554
|
const inputRef = useRef(input);
|
|
472
555
|
const queuedInputRef = useRef(undefined);
|
|
473
556
|
const cursorRef = useRef(cursor);
|
|
@@ -484,6 +567,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
484
567
|
const pasteStatusTimerRef = useRef(undefined);
|
|
485
568
|
const [slashCompletionIndex, setSlashCompletionIndex] = useState(0);
|
|
486
569
|
const [skillCompletions, setSkillCompletions] = useState([]);
|
|
570
|
+
const [secretCompletions, setSecretCompletions] = useState([]);
|
|
487
571
|
const [loginForm, setLoginForm] = useState(undefined);
|
|
488
572
|
const loginFormRef = useRef(undefined);
|
|
489
573
|
useEffect(() => {
|
|
@@ -495,16 +579,48 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
495
579
|
};
|
|
496
580
|
}, []);
|
|
497
581
|
useEffect(() => {
|
|
498
|
-
if (!busy && backgroundTaskCount === 0 && backgroundSessionRuns.length === 0)
|
|
582
|
+
if (!busy && backgroundTaskCount === 0 && backgroundSessionRuns.length === 0 && agentActivities.length === 0)
|
|
499
583
|
return undefined;
|
|
500
|
-
const interval = setInterval(() =>
|
|
584
|
+
const interval = setInterval(() => {
|
|
585
|
+
setAnimationTick((current) => current + 1);
|
|
586
|
+
setAgentActivities(activeAgentActivities(runtime));
|
|
587
|
+
}, REPL_ANIMATION_INTERVAL_MS);
|
|
501
588
|
return () => clearInterval(interval);
|
|
502
|
-
}, [busy, backgroundTaskCount, backgroundSessionRuns.length]);
|
|
589
|
+
}, [busy, backgroundTaskCount, backgroundSessionRuns.length, agentActivities.length, runtime]);
|
|
503
590
|
useEffect(() => {
|
|
504
591
|
const updateBackgroundTasks = () => setBackgroundTasks(activeBackgroundTasks(runtime));
|
|
505
592
|
updateBackgroundTasks();
|
|
506
593
|
return runtime.taskStore.subscribe(updateBackgroundTasks);
|
|
507
594
|
}, [runtime]);
|
|
595
|
+
useEffect(() => {
|
|
596
|
+
const updateAgentActivities = () => setAgentActivities(activeAgentActivities(runtime));
|
|
597
|
+
const debouncedUpdateAgentActivities = debounceVoid(updateAgentActivities, SUBAGENT_ACTIVITY_UPDATE_DEBOUNCE_MS);
|
|
598
|
+
updateAgentActivities();
|
|
599
|
+
const unsubscribe = runtime.agentActivityStore.subscribe(debouncedUpdateAgentActivities.run);
|
|
600
|
+
return () => {
|
|
601
|
+
unsubscribe();
|
|
602
|
+
debouncedUpdateAgentActivities.cancel();
|
|
603
|
+
};
|
|
604
|
+
}, [runtime]);
|
|
605
|
+
useEffect(() => {
|
|
606
|
+
const updateForegroundExecDetachHandle = () => setForegroundExecDetachHandle(runtime.foregroundExecDetach.current());
|
|
607
|
+
updateForegroundExecDetachHandle();
|
|
608
|
+
return runtime.foregroundExecDetach.subscribe(updateForegroundExecDetachHandle);
|
|
609
|
+
}, [runtime]);
|
|
610
|
+
useEffect(() => {
|
|
611
|
+
if (!foregroundExecDetachHandle) {
|
|
612
|
+
setShowForegroundExecDetachHint(false);
|
|
613
|
+
return undefined;
|
|
614
|
+
}
|
|
615
|
+
const elapsedMs = Date.now() - foregroundExecDetachHandle.startedAt;
|
|
616
|
+
if (elapsedMs >= FOREGROUND_EXEC_DETACH_HINT_DELAY_MS) {
|
|
617
|
+
setShowForegroundExecDetachHint(true);
|
|
618
|
+
return undefined;
|
|
619
|
+
}
|
|
620
|
+
setShowForegroundExecDetachHint(false);
|
|
621
|
+
const timer = setTimeout(() => setShowForegroundExecDetachHint(true), FOREGROUND_EXEC_DETACH_HINT_DELAY_MS - elapsedMs);
|
|
622
|
+
return () => clearTimeout(timer);
|
|
623
|
+
}, [foregroundExecDetachHandle]);
|
|
508
624
|
useEffect(() => {
|
|
509
625
|
if (!terminalTitleWorking) {
|
|
510
626
|
setTerminalTitlePrefix(TERMINAL_TITLE_READY_PREFIX);
|
|
@@ -563,9 +679,21 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
563
679
|
setSkillCompletions([]);
|
|
564
680
|
}
|
|
565
681
|
}, [runtime]);
|
|
682
|
+
const refreshSecretCompletions = useCallback(async () => {
|
|
683
|
+
try {
|
|
684
|
+
const secrets = await runtime.secretStore.list();
|
|
685
|
+
setSecretCompletions(secrets.map((secret) => ({ key: secret.key, status: secret.status, length: secret.length, requestReason: secret.requestReason })));
|
|
686
|
+
}
|
|
687
|
+
catch {
|
|
688
|
+
setSecretCompletions([]);
|
|
689
|
+
}
|
|
690
|
+
}, [runtime]);
|
|
566
691
|
useEffect(() => {
|
|
567
692
|
void refreshSkillCompletions();
|
|
568
693
|
}, [refreshSkillCompletions]);
|
|
694
|
+
useEffect(() => {
|
|
695
|
+
void refreshSecretCompletions();
|
|
696
|
+
}, [refreshSecretCompletions]);
|
|
569
697
|
const syncAttachmentsForText = (text) => {
|
|
570
698
|
const next = attachmentsRef.current.filter((attachment) => text.includes(attachment.label));
|
|
571
699
|
if (next.length === attachmentsRef.current.length)
|
|
@@ -1069,10 +1197,35 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1069
1197
|
}
|
|
1070
1198
|
if (command.type === "sessions") {
|
|
1071
1199
|
detachRunningForeground("session browser");
|
|
1200
|
+
setSkillsBrowser(undefined);
|
|
1201
|
+
setSecretsBrowser(undefined);
|
|
1072
1202
|
await handleSessionsCommand(runtime, runningSessionIds(backgroundSessionRunsRef.current), setSessionsBrowser, (line) => append(line));
|
|
1073
1203
|
return;
|
|
1074
1204
|
}
|
|
1205
|
+
if (command.type === "secret") {
|
|
1206
|
+
try {
|
|
1207
|
+
if (command.action === "list") {
|
|
1208
|
+
setSessionsBrowser(undefined);
|
|
1209
|
+
setSkillsBrowser(undefined);
|
|
1210
|
+
await handleSecretsCommand(runtime, setSecretsBrowser, (line) => append(line));
|
|
1211
|
+
}
|
|
1212
|
+
else {
|
|
1213
|
+
append(await handleSecretCommand(command, runtime));
|
|
1214
|
+
void refreshSecretCompletions();
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
catch (error) {
|
|
1218
|
+
append({ kind: "error", text: error instanceof Error ? error.message : String(error) });
|
|
1219
|
+
}
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1075
1222
|
if (command.type === "skill") {
|
|
1223
|
+
if (command.action === "list") {
|
|
1224
|
+
setSessionsBrowser(undefined);
|
|
1225
|
+
setSecretsBrowser(undefined);
|
|
1226
|
+
await handleSkillsCommand(runtime, setSkillsBrowser, (line) => append(line));
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1076
1229
|
if (command.action !== "invoke" || !command.name || !command.args) {
|
|
1077
1230
|
append(await handleSkillCommand(command, runtime));
|
|
1078
1231
|
if (command.action === "import" || command.action === "delete")
|
|
@@ -1084,6 +1237,8 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1084
1237
|
}
|
|
1085
1238
|
if (command.type === "login") {
|
|
1086
1239
|
setSessionsBrowser(undefined);
|
|
1240
|
+
setSkillsBrowser(undefined);
|
|
1241
|
+
setSecretsBrowser(undefined);
|
|
1087
1242
|
setLoginFormState(createLoginFormState(runtime.envPath));
|
|
1088
1243
|
append(systemLine("Opening provider login. Use ↑/↓ to choose, Enter to continue/save, Esc to cancel."));
|
|
1089
1244
|
return;
|
|
@@ -1201,6 +1356,8 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1201
1356
|
clearPendingToolResultTimers();
|
|
1202
1357
|
setStatus(initialStatus(runtime));
|
|
1203
1358
|
setSessionsBrowser(undefined);
|
|
1359
|
+
setSkillsBrowser(undefined);
|
|
1360
|
+
setSecretsBrowser(undefined);
|
|
1204
1361
|
setLoginFormState(undefined);
|
|
1205
1362
|
setQueuedPromptState(undefined);
|
|
1206
1363
|
setPromptState("", 0);
|
|
@@ -1220,7 +1377,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1220
1377
|
const promptDisplayCursor = cursor;
|
|
1221
1378
|
const promptLayoutText = activePlaceholder ? ` ${activePlaceholder}` : promptDisplayText;
|
|
1222
1379
|
const promptLayoutCursor = activePlaceholder ? 0 : promptDisplayCursor;
|
|
1223
|
-
const slashCompletions = inputLockedByQueue || (input.length === 0 && promptPlaceholder !== undefined) || loginForm ? [] : slashCommandCompletions(input, cursor, skillCompletions);
|
|
1380
|
+
const slashCompletions = inputLockedByQueue || (input.length === 0 && promptPlaceholder !== undefined) || loginForm ? [] : slashCommandCompletions(input, cursor, skillCompletions, secretCompletions);
|
|
1224
1381
|
const visibleSlashCompletionCount = slashCompletions.length;
|
|
1225
1382
|
const selectedSlashCompletionIndex = visibleSlashCompletionCount === 0
|
|
1226
1383
|
? 0
|
|
@@ -1236,10 +1393,18 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1236
1393
|
const blockIndex = staticLines.length + i;
|
|
1237
1394
|
return sum + (blockIndex > 0 ? MESSAGE_BLOCK_SPACING_LINES : 0);
|
|
1238
1395
|
}, 0);
|
|
1239
|
-
const
|
|
1240
|
-
const
|
|
1396
|
+
const subagentRows = subagentLivePanelRenderRows(agentActivities, terminalSize.rows);
|
|
1397
|
+
const nonAgentBackgroundTasks = backgroundTasks.filter((task) => task.type !== "agent");
|
|
1398
|
+
const statusRenderRows = STATUS_BAR_RENDER_ROWS + (showForegroundExecDetachHint && foregroundExecDetachHandle ? FOREGROUND_EXEC_DETACH_HINT_RENDER_ROWS : 0) + subagentRows + backgroundTaskStatusRenderRows(subagentRows > 0 ? nonAgentBackgroundTasks.length : backgroundTasks.length);
|
|
1399
|
+
const managementBrowserHeight = sessionsBrowser
|
|
1400
|
+
? sessionsBrowserViewHeight(sessionsBrowser)
|
|
1401
|
+
: skillsBrowser
|
|
1402
|
+
? skillsBrowserViewHeight(skillsBrowser)
|
|
1403
|
+
: secretsBrowser
|
|
1404
|
+
? secretsBrowserViewHeight(secretsBrowser)
|
|
1405
|
+
: 0;
|
|
1241
1406
|
const loginFormHeight = loginForm ? loginFormViewHeight(loginForm) : 0;
|
|
1242
|
-
const liveViewportLines = Math.max(MIN_LIVE_VIEWPORT_LINES, terminalSize.rows - promptHeight - statusRenderRows -
|
|
1407
|
+
const liveViewportLines = Math.max(MIN_LIVE_VIEWPORT_LINES, terminalSize.rows - promptHeight - statusRenderRows - managementBrowserHeight - loginFormHeight - dynamicMarginOverhead - 1);
|
|
1243
1408
|
useInput((value, key) => {
|
|
1244
1409
|
if (isTerminalFocusInSequence(value)) {
|
|
1245
1410
|
terminalFocusedRef.current = true;
|
|
@@ -1260,6 +1425,13 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1260
1425
|
void handleClipboardPaste();
|
|
1261
1426
|
return;
|
|
1262
1427
|
}
|
|
1428
|
+
if (key.ctrl && value.toLowerCase() === "b") {
|
|
1429
|
+
const result = runtime.foregroundExecDetach.detachCurrent();
|
|
1430
|
+
append(result.ok
|
|
1431
|
+
? systemLine(`Detached foreground exec to background task ${result.taskId ?? "unknown"}.`)
|
|
1432
|
+
: systemLine(result.message));
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1263
1435
|
if (key.ctrl && value === "c") {
|
|
1264
1436
|
if (inputRef.current.length > 0) {
|
|
1265
1437
|
setPromptState("", 0);
|
|
@@ -1334,20 +1506,124 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1334
1506
|
}
|
|
1335
1507
|
return;
|
|
1336
1508
|
}
|
|
1509
|
+
if (skillsBrowser) {
|
|
1510
|
+
if (key.escape) {
|
|
1511
|
+
setSkillsBrowser(undefined);
|
|
1512
|
+
return;
|
|
1513
|
+
}
|
|
1514
|
+
if (key.upArrow) {
|
|
1515
|
+
setSkillsBrowser((current) => current ? movePagedSelection(current, -1) : current);
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
if (key.downArrow) {
|
|
1519
|
+
setSkillsBrowser((current) => current ? movePagedSelection(current, 1) : current);
|
|
1520
|
+
return;
|
|
1521
|
+
}
|
|
1522
|
+
if (key.leftArrow || key.pageUp) {
|
|
1523
|
+
setSkillsBrowser((current) => current ? movePagedPage(current, -1) : current);
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
if (key.rightArrow || key.pageDown) {
|
|
1527
|
+
setSkillsBrowser((current) => current ? movePagedPage(current, 1) : current);
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
const selected = skillsBrowser.skills[pagedAbsoluteIndex(skillsBrowser)];
|
|
1531
|
+
if (key.return && selected) {
|
|
1532
|
+
setSkillsBrowser(undefined);
|
|
1533
|
+
append(systemLine(formatSkillDetails(selected), EXPANDED_SUMMARY_MAX_LINES));
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
if (value.toLowerCase() === "i" && selected) {
|
|
1537
|
+
setSkillsBrowser(undefined);
|
|
1538
|
+
setPromptState(`/skill ${selected.name} `, selected.name.length + 8);
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
if ((key.delete || key.backspace || value.toLowerCase() === "d") && selected) {
|
|
1542
|
+
void handleSkillDeleteByName(selected.name, runtime).then((line) => {
|
|
1543
|
+
append(line);
|
|
1544
|
+
void refreshSkillCompletions();
|
|
1545
|
+
void handleSkillsCommand(runtime, setSkillsBrowser, (nextLine) => append(nextLine));
|
|
1546
|
+
});
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1549
|
+
if (value.toLowerCase() === "a") {
|
|
1550
|
+
setSkillsBrowser(undefined);
|
|
1551
|
+
setPromptState("/skill import ", 14);
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
return;
|
|
1555
|
+
}
|
|
1556
|
+
if (secretsBrowser) {
|
|
1557
|
+
if (key.escape) {
|
|
1558
|
+
setSecretsBrowser(undefined);
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
if (key.upArrow) {
|
|
1562
|
+
setSecretsBrowser((current) => current ? movePagedSelection(current, -1) : current);
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
if (key.downArrow) {
|
|
1566
|
+
setSecretsBrowser((current) => current ? movePagedSelection(current, 1) : current);
|
|
1567
|
+
return;
|
|
1568
|
+
}
|
|
1569
|
+
if (key.leftArrow || key.pageUp) {
|
|
1570
|
+
setSecretsBrowser((current) => current ? movePagedPage(current, -1) : current);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
if (key.rightArrow || key.pageDown) {
|
|
1574
|
+
setSecretsBrowser((current) => current ? movePagedPage(current, 1) : current);
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
const selected = secretsBrowser.secrets[pagedAbsoluteIndex(secretsBrowser)];
|
|
1578
|
+
if (key.return && selected) {
|
|
1579
|
+
setSecretsBrowser(undefined);
|
|
1580
|
+
void handleSecretCommand({ type: "secret", action: "info", key: selected.key }, runtime).then((line) => append(line));
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
if (value.toLowerCase() === "s" && selected) {
|
|
1584
|
+
setSecretsBrowser(undefined);
|
|
1585
|
+
setPromptState(`/secret set ${selected.key} `, selected.key.length + 13);
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
if (value.toLowerCase() === "r" && selected) {
|
|
1589
|
+
setSecretsBrowser(undefined);
|
|
1590
|
+
setPromptState(`/secret rename ${selected.key} `, selected.key.length + 16);
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
if ((key.delete || key.backspace || value.toLowerCase() === "d") && selected) {
|
|
1594
|
+
void handleSecretCommand({ type: "secret", action: "delete", key: selected.key }, runtime).then((line) => {
|
|
1595
|
+
append(line);
|
|
1596
|
+
void refreshSecretCompletions();
|
|
1597
|
+
void handleSecretsCommand(runtime, setSecretsBrowser, (nextLine) => append(nextLine));
|
|
1598
|
+
}).catch((error) => append({ kind: "error", text: error instanceof Error ? error.message : String(error) }));
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
if (value.toLowerCase() === "a") {
|
|
1602
|
+
setSecretsBrowser(undefined);
|
|
1603
|
+
setPromptState("/secret set ", 12);
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
if (value.toLowerCase() === "e") {
|
|
1607
|
+
setSecretsBrowser(undefined);
|
|
1608
|
+
setPromptState("/secret request ", 16);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
return;
|
|
1612
|
+
}
|
|
1337
1613
|
if (key.return) {
|
|
1338
1614
|
const currentText = inputRef.current;
|
|
1339
1615
|
const currentCursor = cursorRef.current;
|
|
1340
|
-
const completion = selectedSlashCommandCompletion(currentText, currentCursor, slashCompletionIndexRef.current, skillCompletions);
|
|
1616
|
+
const completion = selectedSlashCommandCompletion(currentText, currentCursor, slashCompletionIndexRef.current, skillCompletions, secretCompletions);
|
|
1341
1617
|
if (completion !== undefined && completion.kind === "command" && completion.arguments !== "none") {
|
|
1342
1618
|
const nextText = `${completion.insertText} ${currentText.slice(currentCursor)}`;
|
|
1343
1619
|
setPromptState(nextText, completion.insertText.length + 1);
|
|
1344
1620
|
return;
|
|
1345
1621
|
}
|
|
1346
|
-
if (currentText.trimEnd() === "/skill") {
|
|
1622
|
+
if (currentText.trimEnd() === "/skill" || currentText.trimEnd() === "/secret") {
|
|
1347
1623
|
void submitLine(currentText);
|
|
1348
1624
|
return;
|
|
1349
1625
|
}
|
|
1350
|
-
if (completion !== undefined && completion.kind === "skill-action") {
|
|
1626
|
+
if (completion !== undefined && (completion.kind === "skill-action" || completion.kind === "secret-action")) {
|
|
1351
1627
|
const nextText = `${completion.insertText}${currentText.slice(currentCursor)}`;
|
|
1352
1628
|
setPromptState(nextText, completion.insertText.length);
|
|
1353
1629
|
return;
|
|
@@ -1372,7 +1648,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1372
1648
|
return;
|
|
1373
1649
|
}
|
|
1374
1650
|
if (key.leftArrow) {
|
|
1375
|
-
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current, skillCompletions);
|
|
1651
|
+
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current, skillCompletions, secretCompletions);
|
|
1376
1652
|
if (completionCount > SLASH_COMPLETION_PAGE_SIZE) {
|
|
1377
1653
|
setSlashCompletionSelection((slashCompletionIndexRef.current + completionCount - SLASH_COMPLETION_PAGE_SIZE) % completionCount);
|
|
1378
1654
|
return;
|
|
@@ -1385,7 +1661,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1385
1661
|
return;
|
|
1386
1662
|
}
|
|
1387
1663
|
if (key.rightArrow) {
|
|
1388
|
-
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current, skillCompletions);
|
|
1664
|
+
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current, skillCompletions, secretCompletions);
|
|
1389
1665
|
if (completionCount > SLASH_COMPLETION_PAGE_SIZE) {
|
|
1390
1666
|
setSlashCompletionSelection((slashCompletionIndexRef.current + SLASH_COMPLETION_PAGE_SIZE) % completionCount);
|
|
1391
1667
|
return;
|
|
@@ -1416,7 +1692,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1416
1692
|
setTipIndex((current) => current - 1);
|
|
1417
1693
|
return;
|
|
1418
1694
|
}
|
|
1419
|
-
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current, skillCompletions);
|
|
1695
|
+
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current, skillCompletions, secretCompletions);
|
|
1420
1696
|
if (completionCount > 0) {
|
|
1421
1697
|
setSlashCompletionSelection((slashCompletionIndexRef.current + completionCount - 1) % completionCount);
|
|
1422
1698
|
return;
|
|
@@ -1433,7 +1709,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1433
1709
|
setTipIndex((current) => current + 1);
|
|
1434
1710
|
return;
|
|
1435
1711
|
}
|
|
1436
|
-
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current, skillCompletions);
|
|
1712
|
+
const completionCount = slashCompletionSelectableCount(inputRef.current, cursorRef.current, skillCompletions, secretCompletions);
|
|
1437
1713
|
if (completionCount > 0) {
|
|
1438
1714
|
setSlashCompletionSelection((slashCompletionIndexRef.current + 1) % completionCount);
|
|
1439
1715
|
return;
|
|
@@ -1459,7 +1735,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1459
1735
|
return;
|
|
1460
1736
|
}
|
|
1461
1737
|
const currentCursor = cursorRef.current;
|
|
1462
|
-
const completions = slashCommandCompletions(currentText, currentCursor, skillCompletions);
|
|
1738
|
+
const completions = slashCommandCompletions(currentText, currentCursor, skillCompletions, secretCompletions);
|
|
1463
1739
|
const completion = completions[Math.min(slashCompletionIndexRef.current, completions.length - 1)];
|
|
1464
1740
|
if (completion !== undefined) {
|
|
1465
1741
|
const nextText = `${completion.insertText}${currentText.slice(currentCursor)}`;
|
|
@@ -1472,7 +1748,7 @@ function InkRepl({ runtime, initialCommandLine }) {
|
|
|
1472
1748
|
return;
|
|
1473
1749
|
}
|
|
1474
1750
|
});
|
|
1475
|
-
return e(Box, { flexDirection: "column" }, e((Static), { items: staticLines, children: (line, index) => e(MessageBlock, { key: line.id, line, width, blockIndex: index }) }), e(MessageList, { lines: dynamicLines, width, liveMaxLines: liveViewportLines, lineIndexOffset: staticLines.length, onMarkdownRenderComplete: markLineRendered }), sessionsBrowser ? e(SessionsBrowser, { state: sessionsBrowser, width }) : null, loginForm ? e(LoginFormView, { state: loginForm, width }) : null, e(StatusBar, { status, animationTick, width }), backgroundTasks.length > 0 ? e(BackgroundTaskStatusLine, { tasks: backgroundTasks, 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 }));
|
|
1751
|
+
return e(Box, { flexDirection: "column" }, e((Static), { items: staticLines, children: (line, index) => e(MessageBlock, { key: line.id, line, width, blockIndex: index }) }), e(MessageList, { lines: dynamicLines, width, liveMaxLines: liveViewportLines, 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, terminalRows: terminalSize.rows, 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 }));
|
|
1476
1752
|
}
|
|
1477
1753
|
const MessageList = React.memo(function MessageList({ lines, width, liveMaxLines, lineIndexOffset = 0, onMarkdownRenderComplete }) {
|
|
1478
1754
|
const contentWidth = messageContentWidth(width);
|
|
@@ -1829,6 +2105,139 @@ function backgroundTaskStatusRenderRows(taskCount) {
|
|
|
1829
2105
|
return 0;
|
|
1830
2106
|
return 1 + Math.min(taskCount, 2);
|
|
1831
2107
|
}
|
|
2108
|
+
function ForegroundExecDetachHintLine({ handle, width: terminalWidth }) {
|
|
2109
|
+
const width = statusBarWidth(terminalWidth);
|
|
2110
|
+
const label = handle.description?.trim() || handle.command;
|
|
2111
|
+
const text = `↳ exec still running · Ctrl+B to detach · ${truncateMiddle(label, Math.max(12, width - 38))}`;
|
|
2112
|
+
return e(Text, { color: "yellow" }, fitToWidth(text, width));
|
|
2113
|
+
}
|
|
2114
|
+
function SubagentLivePanel({ activities, width: terminalWidth, terminalRows, animationTick }) {
|
|
2115
|
+
const width = statusBarWidth(terminalWidth);
|
|
2116
|
+
const rows = subagentLivePanelRenderRows(activities, terminalRows);
|
|
2117
|
+
if (rows <= 0)
|
|
2118
|
+
return null;
|
|
2119
|
+
const sorted = sortAgentActivitiesForPanel(activities);
|
|
2120
|
+
const selected = sorted[0];
|
|
2121
|
+
if (!selected)
|
|
2122
|
+
return null;
|
|
2123
|
+
const activeCount = activities.filter((activity) => activity.status === "running" || activity.status === "pending").length;
|
|
2124
|
+
const header = `◇ subagents: ${activeCount} active${activities.length > activeCount ? ` · ${activities.length - activeCount} recent` : ""} | auto: latest activity`;
|
|
2125
|
+
if (rows <= 1) {
|
|
2126
|
+
return e(Text, { color: "yellow" }, fitToWidth(`${header} | ${compactAgentSummary(selected, width - header.length - 3)}`, width));
|
|
2127
|
+
}
|
|
2128
|
+
const detailLines = buildSubagentDetailLines(selected, sorted, animationTick);
|
|
2129
|
+
return e(Box, { flexDirection: "column", width, overflow: "hidden" }, e(Text, { color: "yellow" }, fitToWidth(header, width)), ...detailLines.map((line, index) => e(Text, {
|
|
2130
|
+
key: `agent-detail-${selected.agentId}-${index}`,
|
|
2131
|
+
color: line.color,
|
|
2132
|
+
}, fitToWidth(line.text, width))));
|
|
2133
|
+
}
|
|
2134
|
+
const SUBAGENT_DETAIL_ROWS = 3;
|
|
2135
|
+
function subagentLivePanelRenderRows(activities, terminalRows) {
|
|
2136
|
+
if (activities.length === 0)
|
|
2137
|
+
return 0;
|
|
2138
|
+
if (terminalRows < 18)
|
|
2139
|
+
return 1;
|
|
2140
|
+
return 1 + SUBAGENT_DETAIL_ROWS;
|
|
2141
|
+
}
|
|
2142
|
+
function sortAgentActivitiesForPanel(activities) {
|
|
2143
|
+
const rank = (status) => {
|
|
2144
|
+
if (status === "running")
|
|
2145
|
+
return 0;
|
|
2146
|
+
if (status === "pending")
|
|
2147
|
+
return 1;
|
|
2148
|
+
if (status === "failed" || status === "killed")
|
|
2149
|
+
return 2;
|
|
2150
|
+
return 3;
|
|
2151
|
+
};
|
|
2152
|
+
return [...activities].sort((left, right) => rank(left.status) - rank(right.status) || right.updatedAt.localeCompare(left.updatedAt));
|
|
2153
|
+
}
|
|
2154
|
+
function buildSubagentDetailLines(selected, sorted, animationTick) {
|
|
2155
|
+
const spinner = selected.status === "running" ? spinnerFrame(animationTick) : statusGlyph(selected.status);
|
|
2156
|
+
const elapsed = formatElapsed(Date.now() - new Date(selected.startedAt).getTime());
|
|
2157
|
+
const headerLine = `${spinner} ${selected.description || selected.agentId} · ${agentModeLabel(selected)} · ${selected.agentType} · ${selected.status} · ${elapsed}`;
|
|
2158
|
+
const currentLine = selected.currentTool
|
|
2159
|
+
? `→ ${selected.currentTool.name}${selected.currentTool.inputPreview ? ` · ${selected.currentTool.inputPreview}` : ""}`
|
|
2160
|
+
: selected.error
|
|
2161
|
+
? `✖ ${selected.error}`
|
|
2162
|
+
: selected.resultPreview
|
|
2163
|
+
? `✓ ${selected.resultPreview}`
|
|
2164
|
+
: selected.lastText
|
|
2165
|
+
? `• ${selected.lastText}`
|
|
2166
|
+
: `• ${selected.prompt}`;
|
|
2167
|
+
const recent = selected.timeline.slice(-2).map((entry) => `${timelinePrefix(entry)} ${formatTimelineEntry(entry, 240)}`);
|
|
2168
|
+
const otherRunning = sorted
|
|
2169
|
+
.filter((activity) => activity.agentId !== selected.agentId && (activity.status === "running" || activity.status === "pending"))
|
|
2170
|
+
.slice(0, 2)
|
|
2171
|
+
.map((activity) => compactAgentSummary(activity, 180));
|
|
2172
|
+
const tail = [...recent, ...otherRunning.map((summary) => `· ${summary}`)].find((line) => line.trim()) ?? `tools:${selected.totalToolUseCount}`;
|
|
2173
|
+
return [
|
|
2174
|
+
{ text: headerLine, color: statusColor(selected.status) },
|
|
2175
|
+
{ text: currentLine, color: selected.error ? "red" : selected.currentTool ? "#d4b04c" : "yellow" },
|
|
2176
|
+
{ text: tail, color: "gray" },
|
|
2177
|
+
];
|
|
2178
|
+
}
|
|
2179
|
+
function compactAgentSummary(activity, maxLength) {
|
|
2180
|
+
const current = activity.currentTool
|
|
2181
|
+
? `${activity.currentTool.name}${activity.currentTool.inputPreview ? ` ${activity.currentTool.inputPreview}` : ""}`
|
|
2182
|
+
: activity.lastText ?? activity.resultPreview ?? activity.error ?? activity.prompt;
|
|
2183
|
+
return truncateMiddle(`${activity.description || activity.agentId} · ${agentModeLabel(activity)} · ${activity.status} · tools:${activity.totalToolUseCount} · ${current.replace(/\s+/g, " ")}`, Math.max(8, maxLength));
|
|
2184
|
+
}
|
|
2185
|
+
function agentModeLabel(activity) {
|
|
2186
|
+
if (activity.mode === "explore")
|
|
2187
|
+
return "explore";
|
|
2188
|
+
return activity.mode;
|
|
2189
|
+
}
|
|
2190
|
+
function formatTimelineEntry(entry, maxLength) {
|
|
2191
|
+
const detail = entry.detail ? ` · ${entry.detail.replace(/\s+/g, " ")}` : "";
|
|
2192
|
+
return truncateMiddle(`${entry.title}${detail}`, Math.max(8, maxLength));
|
|
2193
|
+
}
|
|
2194
|
+
function timelinePrefix(entry) {
|
|
2195
|
+
if (entry.kind === "tool_start")
|
|
2196
|
+
return "→";
|
|
2197
|
+
if (entry.kind === "tool_result")
|
|
2198
|
+
return entry.status === "failed" ? "✖" : "←";
|
|
2199
|
+
if (entry.kind === "thinking")
|
|
2200
|
+
return "◆";
|
|
2201
|
+
if (entry.kind === "error")
|
|
2202
|
+
return "✖";
|
|
2203
|
+
if (entry.kind === "status")
|
|
2204
|
+
return "•";
|
|
2205
|
+
return "assistant:";
|
|
2206
|
+
}
|
|
2207
|
+
function timelineColor(entry) {
|
|
2208
|
+
if (entry.status === "failed" || entry.kind === "error")
|
|
2209
|
+
return "red";
|
|
2210
|
+
if (entry.kind === "tool_start" || entry.kind === "tool_result")
|
|
2211
|
+
return "#d4b04c";
|
|
2212
|
+
if (entry.kind === "thinking")
|
|
2213
|
+
return THINKING_COLOR;
|
|
2214
|
+
if (entry.kind === "status")
|
|
2215
|
+
return "gray";
|
|
2216
|
+
return "green";
|
|
2217
|
+
}
|
|
2218
|
+
function statusGlyph(status) {
|
|
2219
|
+
if (status === "completed")
|
|
2220
|
+
return "✓";
|
|
2221
|
+
if (status === "failed")
|
|
2222
|
+
return "✖";
|
|
2223
|
+
if (status === "killed")
|
|
2224
|
+
return "■";
|
|
2225
|
+
if (status === "pending")
|
|
2226
|
+
return "…";
|
|
2227
|
+
return "●";
|
|
2228
|
+
}
|
|
2229
|
+
function statusColor(status) {
|
|
2230
|
+
if (status === "completed")
|
|
2231
|
+
return "green";
|
|
2232
|
+
if (status === "failed" || status === "killed")
|
|
2233
|
+
return "red";
|
|
2234
|
+
if (status === "pending")
|
|
2235
|
+
return "gray";
|
|
2236
|
+
return "yellow";
|
|
2237
|
+
}
|
|
2238
|
+
function spinnerFrame(tick) {
|
|
2239
|
+
return ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"][tick % 10] ?? "●";
|
|
2240
|
+
}
|
|
1832
2241
|
function BackgroundTaskStatusLine({ tasks, width: terminalWidth }) {
|
|
1833
2242
|
const width = statusBarWidth(terminalWidth);
|
|
1834
2243
|
const summary = `◇ background tools: ${tasks.length} task${tasks.length === 1 ? "" : "s"}`;
|
|
@@ -1905,10 +2314,20 @@ const SLASH_COMPLETION_PAGE_SIZE = 10;
|
|
|
1905
2314
|
const MODEL_REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
1906
2315
|
const MODEL_REASONING_CONTROL_CHOICES = ["default", "off"];
|
|
1907
2316
|
const SKILL_COMMAND_ACTIONS = [
|
|
2317
|
+
{ name: "list", description: "Open the skill management browser", aliases: ["ls"] },
|
|
1908
2318
|
{ name: "import", description: "Import by linking a skill directory" },
|
|
1909
2319
|
{ name: "delete", description: "Delete a workspace skill link/directory", aliases: ["remove", "rm"] },
|
|
1910
2320
|
];
|
|
1911
|
-
|
|
2321
|
+
const SECRET_COMMAND_ACTIONS = [
|
|
2322
|
+
{ name: "list", description: "List secret keys/status/length; add --show to print values" },
|
|
2323
|
+
{ name: "get", description: "Print one secret value in the REPL" },
|
|
2324
|
+
{ name: "set", description: "Set a plaintext secret value" },
|
|
2325
|
+
{ name: "request", description: "Create an empty placeholder secret", aliases: ["empty"] },
|
|
2326
|
+
{ name: "info", description: "Show one secret's metadata" },
|
|
2327
|
+
{ name: "rename", description: "Rename a secret key", aliases: ["mv"] },
|
|
2328
|
+
{ name: "delete", description: "Delete a secret", aliases: ["remove", "rm"] },
|
|
2329
|
+
];
|
|
2330
|
+
function slashCommandCompletions(text, cursor, skills = [], secrets = []) {
|
|
1912
2331
|
const safeCursor = Math.max(0, Math.min(cursor, text.length));
|
|
1913
2332
|
const prefix = text.slice(0, safeCursor);
|
|
1914
2333
|
if (!prefix.startsWith("/") || /\r|\n/.test(prefix))
|
|
@@ -1924,6 +2343,9 @@ function slashCommandCompletions(text, cursor, skills = []) {
|
|
|
1924
2343
|
if (prefix.startsWith("/skill") && (prefix.length === "/skill".length || prefix["/skill".length] === " ")) {
|
|
1925
2344
|
return skillCommandCompletions(prefix, skills);
|
|
1926
2345
|
}
|
|
2346
|
+
if (prefix.startsWith("/secret") && (prefix.length === "/secret".length || prefix["/secret".length] === " ")) {
|
|
2347
|
+
return secretCommandCompletions(prefix, secrets);
|
|
2348
|
+
}
|
|
1927
2349
|
if (prefix.length > 1 && !/^\/[\w-]*$/.test(prefix))
|
|
1928
2350
|
return [];
|
|
1929
2351
|
const normalizedPrefix = prefix.toLowerCase();
|
|
@@ -1937,22 +2359,19 @@ function skillCommandCompletions(prefix, skills) {
|
|
|
1937
2359
|
const argumentTokens = tokens.slice(1);
|
|
1938
2360
|
if (!hasTrailingSpace && argumentTokens.length === 0 && !"/skill".startsWith(prefix.toLowerCase()))
|
|
1939
2361
|
return [];
|
|
1940
|
-
if (argumentTokens.length === 0)
|
|
1941
|
-
return
|
|
1942
|
-
}
|
|
2362
|
+
if (argumentTokens.length === 0)
|
|
2363
|
+
return skillActionCompletions("");
|
|
1943
2364
|
const [first = "", second = ""] = argumentTokens;
|
|
1944
|
-
if (first === "import")
|
|
2365
|
+
if (first === "list" || first === "ls" || first === "import")
|
|
1945
2366
|
return [];
|
|
1946
2367
|
if (first === "delete" || first === "remove" || first === "rm") {
|
|
1947
2368
|
if (argumentTokens.length > 1 && hasTrailingSpace)
|
|
1948
2369
|
return [];
|
|
1949
2370
|
return skillNameCompletions(skills, hasTrailingSpace ? "" : second, "delete");
|
|
1950
2371
|
}
|
|
1951
|
-
if (argumentTokens.length > 1)
|
|
1952
|
-
return [];
|
|
1953
|
-
if (hasTrailingSpace)
|
|
2372
|
+
if (argumentTokens.length > 1 || hasTrailingSpace)
|
|
1954
2373
|
return [];
|
|
1955
|
-
return
|
|
2374
|
+
return skillActionCompletions(first);
|
|
1956
2375
|
}
|
|
1957
2376
|
function skillActionCompletions(current) {
|
|
1958
2377
|
return SKILL_COMMAND_ACTIONS
|
|
@@ -1960,7 +2379,7 @@ function skillActionCompletions(current) {
|
|
|
1960
2379
|
.filter((action) => action.name.startsWith(current.toLowerCase()))
|
|
1961
2380
|
.map((action) => ({
|
|
1962
2381
|
value: action.name,
|
|
1963
|
-
insertText: `/skill ${action.name} `,
|
|
2382
|
+
insertText: action.name === "list" || action.name === "ls" ? `/skill ${action.name}` : `/skill ${action.name} `,
|
|
1964
2383
|
description: action.description,
|
|
1965
2384
|
arguments: "optional",
|
|
1966
2385
|
kind: "skill-action",
|
|
@@ -1981,6 +2400,90 @@ function formatSkillCompletionDescription(skill) {
|
|
|
1981
2400
|
const tags = skill.tags?.length ? ` · ${skill.tags.join(",")}` : "";
|
|
1982
2401
|
return `${skill.description}${skill.execution ? ` · ${skill.execution}` : ""}${tags}`;
|
|
1983
2402
|
}
|
|
2403
|
+
function secretCommandCompletions(prefix, secrets) {
|
|
2404
|
+
const hasTrailingSpace = /\s$/.test(prefix);
|
|
2405
|
+
const tokens = prefix.trim().split(/\s+/).filter(Boolean);
|
|
2406
|
+
const argumentTokens = tokens.slice(1);
|
|
2407
|
+
if (!hasTrailingSpace && argumentTokens.length === 0 && !"/secret".startsWith(prefix.toLowerCase()))
|
|
2408
|
+
return [];
|
|
2409
|
+
if (argumentTokens.length === 0)
|
|
2410
|
+
return secretActionCompletions("");
|
|
2411
|
+
const [action = "", key = "", newKey = ""] = argumentTokens;
|
|
2412
|
+
const normalizedAction = secretCanonicalAction(action);
|
|
2413
|
+
if (!normalizedAction) {
|
|
2414
|
+
if (argumentTokens.length > 1 || hasTrailingSpace)
|
|
2415
|
+
return [];
|
|
2416
|
+
return secretActionCompletions(action);
|
|
2417
|
+
}
|
|
2418
|
+
if (normalizedAction === "list") {
|
|
2419
|
+
if (argumentTokens.length === 1 && hasTrailingSpace)
|
|
2420
|
+
return [{ value: "--show", insertText: "/secret list --show", description: "Print plaintext values in the REPL", arguments: "optional", kind: "secret-action" }];
|
|
2421
|
+
if (argumentTokens.length === 2 && !hasTrailingSpace)
|
|
2422
|
+
return "--show".startsWith(key) ? [{ value: "--show", insertText: "/secret list --show", description: "Print plaintext values in the REPL", arguments: "optional", kind: "secret-action" }] : [];
|
|
2423
|
+
return [];
|
|
2424
|
+
}
|
|
2425
|
+
if (normalizedAction === "set" || normalizedAction === "request") {
|
|
2426
|
+
if (argumentTokens.length <= 1 && hasTrailingSpace)
|
|
2427
|
+
return [];
|
|
2428
|
+
return [];
|
|
2429
|
+
}
|
|
2430
|
+
if (normalizedAction === "rename") {
|
|
2431
|
+
if (argumentTokens.length <= 1)
|
|
2432
|
+
return hasTrailingSpace ? secretKeyCompletions(secrets, "", normalizedAction) : [];
|
|
2433
|
+
if (argumentTokens.length === 2 && !hasTrailingSpace)
|
|
2434
|
+
return secretKeyCompletions(secrets, key, normalizedAction);
|
|
2435
|
+
if (argumentTokens.length === 2 && hasTrailingSpace)
|
|
2436
|
+
return [];
|
|
2437
|
+
if (argumentTokens.length === 3 && !hasTrailingSpace && newKey)
|
|
2438
|
+
return [];
|
|
2439
|
+
return [];
|
|
2440
|
+
}
|
|
2441
|
+
if (normalizedAction === "get" || normalizedAction === "info" || normalizedAction === "delete") {
|
|
2442
|
+
if (argumentTokens.length <= 1)
|
|
2443
|
+
return hasTrailingSpace ? secretKeyCompletions(secrets, "", normalizedAction) : [];
|
|
2444
|
+
if (argumentTokens.length === 2 && !hasTrailingSpace)
|
|
2445
|
+
return secretKeyCompletions(secrets, key, normalizedAction);
|
|
2446
|
+
return [];
|
|
2447
|
+
}
|
|
2448
|
+
return [];
|
|
2449
|
+
}
|
|
2450
|
+
function secretCanonicalAction(action) {
|
|
2451
|
+
const lower = action.toLowerCase();
|
|
2452
|
+
if (lower === "ls")
|
|
2453
|
+
return "list";
|
|
2454
|
+
if (lower === "show")
|
|
2455
|
+
return "get";
|
|
2456
|
+
if (lower === "empty")
|
|
2457
|
+
return "request";
|
|
2458
|
+
if (lower === "mv")
|
|
2459
|
+
return "rename";
|
|
2460
|
+
if (lower === "remove" || lower === "rm")
|
|
2461
|
+
return "delete";
|
|
2462
|
+
return ["list", "get", "set", "request", "info", "rename", "delete"].includes(lower) ? lower : undefined;
|
|
2463
|
+
}
|
|
2464
|
+
function secretActionCompletions(current) {
|
|
2465
|
+
return SECRET_COMMAND_ACTIONS
|
|
2466
|
+
.flatMap((action) => [action.name, ...("aliases" in action ? action.aliases ?? [] : [])].map((name) => ({ name, description: action.description })))
|
|
2467
|
+
.filter((action) => action.name.startsWith(current.toLowerCase()))
|
|
2468
|
+
.map((action) => ({
|
|
2469
|
+
value: action.name,
|
|
2470
|
+
insertText: `/secret ${action.name} `,
|
|
2471
|
+
description: action.description,
|
|
2472
|
+
arguments: "optional",
|
|
2473
|
+
kind: "secret-action",
|
|
2474
|
+
}));
|
|
2475
|
+
}
|
|
2476
|
+
function secretKeyCompletions(secrets, current, action) {
|
|
2477
|
+
return secrets
|
|
2478
|
+
.filter((secret) => secret.key.toLowerCase().includes(current.toLowerCase()))
|
|
2479
|
+
.map((secret) => ({
|
|
2480
|
+
value: secret.key,
|
|
2481
|
+
insertText: `/secret ${action} ${secret.key}${action === "rename" ? " " : ""}`,
|
|
2482
|
+
description: `${secret.status} · length=${secret.length}${secret.requestReason ? ` · ${secret.requestReason}` : ""}`,
|
|
2483
|
+
arguments: "optional",
|
|
2484
|
+
kind: "secret-key",
|
|
2485
|
+
}));
|
|
2486
|
+
}
|
|
1984
2487
|
function modelCommandCompletions(prefix) {
|
|
1985
2488
|
const hasTrailingSpace = /\s$/.test(prefix);
|
|
1986
2489
|
const tokens = prefix.trim().split(/\s+/).filter(Boolean);
|
|
@@ -2066,11 +2569,11 @@ function slashCompletionViewHeight(completions) {
|
|
|
2066
2569
|
return 0;
|
|
2067
2570
|
return Math.min(completions.length, SLASH_COMPLETION_PAGE_SIZE) + 2;
|
|
2068
2571
|
}
|
|
2069
|
-
function slashCompletionSelectableCount(text, cursor, skills = []) {
|
|
2070
|
-
return slashCommandCompletions(text, cursor, skills).length;
|
|
2572
|
+
function slashCompletionSelectableCount(text, cursor, skills = [], secrets = []) {
|
|
2573
|
+
return slashCommandCompletions(text, cursor, skills, secrets).length;
|
|
2071
2574
|
}
|
|
2072
|
-
function selectedSlashCommandCompletion(text, cursor, selectedIndex, skills = []) {
|
|
2073
|
-
const completions = slashCommandCompletions(text, cursor, skills);
|
|
2575
|
+
function selectedSlashCommandCompletion(text, cursor, selectedIndex, skills = [], secrets = []) {
|
|
2576
|
+
const completions = slashCommandCompletions(text, cursor, skills, secrets);
|
|
2074
2577
|
if (completions.length === 0)
|
|
2075
2578
|
return undefined;
|
|
2076
2579
|
return completions[Math.max(0, Math.min(selectedIndex, completions.length - 1))];
|
|
@@ -2143,6 +2646,65 @@ function SlashCompletionLines({ completions, width, prompt, selectedIndex }) {
|
|
|
2143
2646
|
e(Text, { key: "slash-completion-footer", color: "gray" }, fitToWidth(footer, contentWidth)),
|
|
2144
2647
|
].map((line, index) => e(Box, { key: `slash-completion-line-${index}`, height: 1, overflow: "hidden" }, e(Text, { color: "gray" }, " ".repeat(prompt.length)), line));
|
|
2145
2648
|
}
|
|
2649
|
+
async function handleSecretCommand(command, runtime) {
|
|
2650
|
+
const usage = "Usage: /secret <list|get|set|request|delete|rename|info> ...";
|
|
2651
|
+
const action = command.action ?? "list";
|
|
2652
|
+
const requireKey = () => {
|
|
2653
|
+
if (!command.key)
|
|
2654
|
+
throw new Error(usage);
|
|
2655
|
+
return command.key;
|
|
2656
|
+
};
|
|
2657
|
+
if (action === "list") {
|
|
2658
|
+
const entries = await runtime.secretStore.list();
|
|
2659
|
+
if (entries.length === 0)
|
|
2660
|
+
return systemLine("No secrets stored.");
|
|
2661
|
+
const lines = await Promise.all(entries.map(async (entry) => {
|
|
2662
|
+
if (command.show) {
|
|
2663
|
+
const value = entry.status === "set" ? await runtime.secretStore.getPlaintext(entry.key) : "";
|
|
2664
|
+
return `${entry.key} = ${value}`;
|
|
2665
|
+
}
|
|
2666
|
+
const reason = entry.requestReason ? ` reason=${JSON.stringify(entry.requestReason)}` : "";
|
|
2667
|
+
return `${entry.key}\t${entry.status}\tlength=${entry.length}${reason}`;
|
|
2668
|
+
}));
|
|
2669
|
+
return systemLine(lines.join("\n"), EXPANDED_SUMMARY_MAX_LINES);
|
|
2670
|
+
}
|
|
2671
|
+
if (action === "get") {
|
|
2672
|
+
const key = requireKey();
|
|
2673
|
+
const info = await runtime.secretStore.info(key);
|
|
2674
|
+
if (!info)
|
|
2675
|
+
return systemLine(`Secret "${key}" does not exist.`);
|
|
2676
|
+
const value = await runtime.secretStore.getPlaintext(key);
|
|
2677
|
+
return systemLine(info.status === "empty" ? `Secret "${key}" is empty.` : value, EXPANDED_SUMMARY_MAX_LINES);
|
|
2678
|
+
}
|
|
2679
|
+
if (action === "set") {
|
|
2680
|
+
const key = requireKey();
|
|
2681
|
+
const meta = await runtime.secretStore.setPlaintext(key, command.value ?? "");
|
|
2682
|
+
return systemLine(`Secret "${meta.key}" saved, status=${meta.status}, length=${meta.length}.`);
|
|
2683
|
+
}
|
|
2684
|
+
if (action === "request" || action === "empty") {
|
|
2685
|
+
const key = requireKey();
|
|
2686
|
+
const meta = await runtime.secretStore.requestEmpty(key, { reason: command.reason, requestedBy: "user" });
|
|
2687
|
+
return systemLine(`Secret "${meta.key}" is ${meta.status}. Fill it with: /secret set ${meta.key} <value>`);
|
|
2688
|
+
}
|
|
2689
|
+
if (action === "delete") {
|
|
2690
|
+
const key = requireKey();
|
|
2691
|
+
const deleted = await runtime.secretStore.delete(key);
|
|
2692
|
+
return systemLine(deleted ? `Secret "${key}" deleted.` : `Secret "${key}" did not exist.`);
|
|
2693
|
+
}
|
|
2694
|
+
if (action === "rename") {
|
|
2695
|
+
const key = requireKey();
|
|
2696
|
+
if (!command.newKey)
|
|
2697
|
+
throw new Error("Usage: /secret rename <oldKey> <newKey>");
|
|
2698
|
+
const meta = await runtime.secretStore.rename(key, command.newKey);
|
|
2699
|
+
return systemLine(`Secret renamed to "${meta.key}".`);
|
|
2700
|
+
}
|
|
2701
|
+
if (action === "info") {
|
|
2702
|
+
const key = requireKey();
|
|
2703
|
+
const info = await runtime.secretStore.info(key);
|
|
2704
|
+
return systemLine(info ? formatReplData(info, 4000) : `Secret "${key}" does not exist.`, EXPANDED_SUMMARY_MAX_LINES);
|
|
2705
|
+
}
|
|
2706
|
+
return systemLine(usage);
|
|
2707
|
+
}
|
|
2146
2708
|
async function handleSkillCommand(command, runtime) {
|
|
2147
2709
|
if (command.action === "import")
|
|
2148
2710
|
return handleSkillImportCommand(command, runtime);
|
|
@@ -2189,7 +2751,10 @@ async function handleSkillImportCommand(command, runtime) {
|
|
|
2189
2751
|
async function handleSkillDeleteCommand(command, runtime) {
|
|
2190
2752
|
if (!command.name)
|
|
2191
2753
|
return { kind: "error", text: "Usage: /skill delete <name>" };
|
|
2192
|
-
|
|
2754
|
+
return handleSkillDeleteByName(command.name, runtime);
|
|
2755
|
+
}
|
|
2756
|
+
async function handleSkillDeleteByName(nameInput, runtime) {
|
|
2757
|
+
const name = requireSkillName(nameInput);
|
|
2193
2758
|
const skillPath = path.join(runtime.skillWorkspaceRoot, name);
|
|
2194
2759
|
const existing = await safeLstat(skillPath);
|
|
2195
2760
|
if (!existing)
|
|
@@ -2344,10 +2909,6 @@ function currentModelProvider() {
|
|
|
2344
2909
|
function modelEnvKeyForProvider(provider) {
|
|
2345
2910
|
if (provider === "anthropic")
|
|
2346
2911
|
return "ANTHROPIC_MODEL";
|
|
2347
|
-
if (provider === "deepseek")
|
|
2348
|
-
return "DEEPSEEK_MODEL";
|
|
2349
|
-
if (provider === "kimi")
|
|
2350
|
-
return "KIMI_MODEL";
|
|
2351
2912
|
return "OPENAI_MODEL";
|
|
2352
2913
|
}
|
|
2353
2914
|
function envValueForReasoning(reasoning) {
|
|
@@ -2455,6 +3016,8 @@ function renderMessage(message, append, activeAssistantId, options = {}) {
|
|
|
2455
3016
|
rendered = true;
|
|
2456
3017
|
}
|
|
2457
3018
|
if (block.type === "thinking") {
|
|
3019
|
+
if (options.includeThinkingBlocks === false)
|
|
3020
|
+
continue;
|
|
2458
3021
|
append(thinkingLine(block.text));
|
|
2459
3022
|
rendered = true;
|
|
2460
3023
|
}
|
|
@@ -2598,7 +3161,25 @@ async function handleSessionsCommand(runtime, runningSessionIds, setBrowser, app
|
|
|
2598
3161
|
append(systemLine("No saved sessions found."));
|
|
2599
3162
|
return;
|
|
2600
3163
|
}
|
|
2601
|
-
setBrowser({ sessions, runningSessionIds, pageSize: SESSIONS_DEFAULT_PAGE_SIZE, pageIndex: 0, selectedIndex: 0 });
|
|
3164
|
+
setBrowser({ items: sessions, sessions, runningSessionIds, pageSize: SESSIONS_DEFAULT_PAGE_SIZE, pageIndex: 0, selectedIndex: 0 });
|
|
3165
|
+
}
|
|
3166
|
+
async function handleSkillsCommand(runtime, setBrowser, append) {
|
|
3167
|
+
const skills = await runtime.skills.list();
|
|
3168
|
+
if (skills.length === 0) {
|
|
3169
|
+
setBrowser(undefined);
|
|
3170
|
+
append(systemLine(formatSkillList(skills), EXPANDED_SUMMARY_MAX_LINES));
|
|
3171
|
+
return;
|
|
3172
|
+
}
|
|
3173
|
+
setBrowser({ items: skills, skills, pageSize: SESSIONS_DEFAULT_PAGE_SIZE, pageIndex: 0, selectedIndex: 0 });
|
|
3174
|
+
}
|
|
3175
|
+
async function handleSecretsCommand(runtime, setBrowser, append) {
|
|
3176
|
+
const secrets = await runtime.secretStore.list();
|
|
3177
|
+
if (secrets.length === 0) {
|
|
3178
|
+
setBrowser(undefined);
|
|
3179
|
+
append(systemLine("No secrets stored. Press /secret set <key> <value> to add one, or /secret request <key> to create an empty placeholder."));
|
|
3180
|
+
return;
|
|
3181
|
+
}
|
|
3182
|
+
setBrowser({ items: secrets, secrets, pageSize: SESSIONS_DEFAULT_PAGE_SIZE, pageIndex: 0, selectedIndex: 0 });
|
|
2602
3183
|
}
|
|
2603
3184
|
async function handleExportCommand(command, runtime) {
|
|
2604
3185
|
const snapshot = runtime.engine.snapshot();
|
|
@@ -2646,6 +3227,7 @@ async function handleDeleteSessionCommand(sessionId, current, runtime, setBrowse
|
|
|
2646
3227
|
const pageLength = nextSessions.slice(pageIndex * current.pageSize, pageIndex * current.pageSize + current.pageSize).length;
|
|
2647
3228
|
setBrowser({
|
|
2648
3229
|
...current,
|
|
3230
|
+
items: nextSessions,
|
|
2649
3231
|
sessions: nextSessions,
|
|
2650
3232
|
runningSessionIds: current.runningSessionIds.filter((id) => id !== sessionId),
|
|
2651
3233
|
pageIndex,
|
|
@@ -2683,11 +3265,11 @@ function restoredHistoryLines(runtime) {
|
|
|
2683
3265
|
return lines.length;
|
|
2684
3266
|
};
|
|
2685
3267
|
for (const message of runtime.engine.getHistoryMessages()) {
|
|
2686
|
-
renderMessage(message, append, undefined, { includeToolUseBlocks: true });
|
|
3268
|
+
renderMessage(message, append, undefined, { includeToolUseBlocks: true, includeThinkingBlocks: false });
|
|
2687
3269
|
}
|
|
2688
3270
|
return lines;
|
|
2689
3271
|
}
|
|
2690
|
-
const LOGIN_PROVIDERS = ["openai", "anthropic"
|
|
3272
|
+
const LOGIN_PROVIDERS = ["openai", "anthropic"];
|
|
2691
3273
|
const SHARED_LOGIN_FIELDS = [
|
|
2692
3274
|
{ key: "reasoningEffort", label: "Reasoning effort", envKey: "MODEL_REASONING_EFFORT", scope: "shared", options: ["", "off", "none", "minimal", "low", "medium", "high", "xhigh", "max"] },
|
|
2693
3275
|
{ key: "reasoningSummary", label: "Reasoning summary", envKey: "MODEL_REASONING_SUMMARY", scope: "shared", options: ["", "auto", "concise", "detailed"] },
|
|
@@ -2713,20 +3295,6 @@ const LOGIN_FIELD_DEFINITIONS = {
|
|
|
2713
3295
|
{ key: "version", label: "Anthropic version", envKey: "ANTHROPIC_VERSION", scope: "provider", placeholder: "2023-06-01" },
|
|
2714
3296
|
...SHARED_LOGIN_FIELDS,
|
|
2715
3297
|
],
|
|
2716
|
-
deepseek: [
|
|
2717
|
-
{ key: "apiKey", label: "API key", envKey: "DEEPSEEK_API_KEY", scope: "provider", required: true, secret: true, placeholder: "sk-..." },
|
|
2718
|
-
{ key: "baseUrl", label: "Base URL", envKey: "DEEPSEEK_BASE_URL", scope: "provider", placeholder: "https://api.deepseek.com" },
|
|
2719
|
-
{ key: "model", label: "Model", envKey: "DEEPSEEK_MODEL", scope: "provider", required: true, placeholder: "deepseek-chat" },
|
|
2720
|
-
{ key: "fallbackModel", label: "Fallback model", envKey: "DEEPSEEK_FALLBACK_MODEL", scope: "provider" },
|
|
2721
|
-
...SHARED_LOGIN_FIELDS,
|
|
2722
|
-
],
|
|
2723
|
-
kimi: [
|
|
2724
|
-
{ key: "apiKey", label: "API key", envKey: "KIMI_API_KEY", scope: "provider", required: true, secret: true, placeholder: "sk-..." },
|
|
2725
|
-
{ key: "baseUrl", label: "Base URL", envKey: "KIMI_BASE_URL", scope: "provider", placeholder: "https://api.moonshot.cn/v1" },
|
|
2726
|
-
{ key: "model", label: "Model", envKey: "KIMI_MODEL", scope: "provider", required: true, placeholder: "kimi-k2.6" },
|
|
2727
|
-
{ key: "fallbackModel", label: "Fallback model", envKey: "KIMI_FALLBACK_MODEL", scope: "provider" },
|
|
2728
|
-
...SHARED_LOGIN_FIELDS,
|
|
2729
|
-
],
|
|
2730
3298
|
};
|
|
2731
3299
|
const DEPRECATED_MODEL_ENV_KEYS = [
|
|
2732
3300
|
"MODEL_API_KEY",
|
|
@@ -2747,55 +3315,55 @@ const DEPRECATED_MODEL_ENV_KEYS = [
|
|
|
2747
3315
|
"ANTHROPIC_TIMEOUT_MS",
|
|
2748
3316
|
"ANTHROPIC_STREAM_IDLE_TIMEOUT_MS",
|
|
2749
3317
|
"ANTHROPIC_MAX_RETRIES",
|
|
2750
|
-
"DEEPSEEK_REASONING_EFFORT",
|
|
2751
|
-
"DEEPSEEK_REASONING_SUMMARY",
|
|
2752
|
-
"DEEPSEEK_MAX_OUTPUT_TOKENS",
|
|
2753
|
-
"DEEPSEEK_TIMEOUT_MS",
|
|
2754
|
-
"DEEPSEEK_STREAM_IDLE_TIMEOUT_MS",
|
|
2755
|
-
"DEEPSEEK_MAX_RETRIES",
|
|
2756
|
-
"KIMI_REASONING_EFFORT",
|
|
2757
|
-
"KIMI_REASONING_SUMMARY",
|
|
2758
|
-
"KIMI_MAX_OUTPUT_TOKENS",
|
|
2759
|
-
"KIMI_TIMEOUT_MS",
|
|
2760
|
-
"KIMI_STREAM_IDLE_TIMEOUT_MS",
|
|
2761
|
-
"KIMI_MAX_RETRIES",
|
|
2762
|
-
"MOONSHOT_REASONING_EFFORT",
|
|
2763
|
-
"MOONSHOT_REASONING_SUMMARY",
|
|
2764
|
-
"MOONSHOT_MAX_OUTPUT_TOKENS",
|
|
2765
|
-
"MOONSHOT_TIMEOUT_MS",
|
|
2766
|
-
"MOONSHOT_STREAM_IDLE_TIMEOUT_MS",
|
|
2767
|
-
"MOONSHOT_MAX_RETRIES",
|
|
2768
3318
|
];
|
|
2769
|
-
function
|
|
2770
|
-
return Math.max(1, Math.ceil(state.
|
|
3319
|
+
function pagedPageCount(state) {
|
|
3320
|
+
return Math.max(1, Math.ceil(state.items.length / state.pageSize));
|
|
2771
3321
|
}
|
|
2772
|
-
function
|
|
3322
|
+
function pagedPageItems(state) {
|
|
2773
3323
|
const start = state.pageIndex * state.pageSize;
|
|
2774
|
-
return state.
|
|
3324
|
+
return state.items.slice(start, start + state.pageSize);
|
|
2775
3325
|
}
|
|
2776
|
-
function
|
|
3326
|
+
function pagedAbsoluteIndex(state) {
|
|
2777
3327
|
return state.pageIndex * state.pageSize + state.selectedIndex;
|
|
2778
3328
|
}
|
|
2779
|
-
function
|
|
2780
|
-
const pageLength =
|
|
3329
|
+
function movePagedSelection(state, delta) {
|
|
3330
|
+
const pageLength = pagedPageItems(state).length;
|
|
2781
3331
|
if (pageLength <= 0)
|
|
2782
3332
|
return state;
|
|
2783
3333
|
const selectedIndex = (state.selectedIndex + delta + pageLength) % pageLength;
|
|
2784
3334
|
return { ...state, selectedIndex };
|
|
2785
3335
|
}
|
|
2786
|
-
function
|
|
2787
|
-
const pageCount =
|
|
3336
|
+
function movePagedPage(state, delta) {
|
|
3337
|
+
const pageCount = pagedPageCount(state);
|
|
2788
3338
|
if (pageCount <= 1)
|
|
2789
3339
|
return state;
|
|
2790
3340
|
const pageIndex = (state.pageIndex + delta + pageCount) % pageCount;
|
|
2791
|
-
const pageLength = state.
|
|
3341
|
+
const pageLength = state.items.slice(pageIndex * state.pageSize, pageIndex * state.pageSize + state.pageSize).length;
|
|
2792
3342
|
return { ...state, pageIndex, selectedIndex: Math.min(state.selectedIndex, Math.max(0, pageLength - 1)) };
|
|
2793
3343
|
}
|
|
3344
|
+
function sessionsPageItems(state) {
|
|
3345
|
+
return pagedPageItems(state);
|
|
3346
|
+
}
|
|
3347
|
+
function sessionAbsoluteIndex(state) {
|
|
3348
|
+
return pagedAbsoluteIndex(state);
|
|
3349
|
+
}
|
|
3350
|
+
function moveSessionsSelection(state, delta) {
|
|
3351
|
+
return movePagedSelection(state, delta);
|
|
3352
|
+
}
|
|
3353
|
+
function moveSessionsPage(state, delta) {
|
|
3354
|
+
return movePagedPage(state, delta);
|
|
3355
|
+
}
|
|
2794
3356
|
function sessionsBrowserViewHeight(state) {
|
|
2795
3357
|
return sessionsPageItems(state).length + 3;
|
|
2796
3358
|
}
|
|
3359
|
+
function skillsBrowserViewHeight(state) {
|
|
3360
|
+
return pagedPageItems(state).length + 3;
|
|
3361
|
+
}
|
|
3362
|
+
function secretsBrowserViewHeight(state) {
|
|
3363
|
+
return pagedPageItems(state).length + 3;
|
|
3364
|
+
}
|
|
2797
3365
|
function SessionsBrowser({ state, width }) {
|
|
2798
|
-
const pageCount =
|
|
3366
|
+
const pageCount = pagedPageCount(state);
|
|
2799
3367
|
const pageItems = sessionsPageItems(state);
|
|
2800
3368
|
const showPagination = pageCount > 1;
|
|
2801
3369
|
const contentWidth = Math.max(20, width);
|
|
@@ -2815,6 +3383,51 @@ function SessionsBrowser({ state, width }) {
|
|
|
2815
3383
|
}, row.numberPrefix), row.rest);
|
|
2816
3384
|
}), e(Text, { color: "gray" }, fitToWidth(footer, contentWidth)));
|
|
2817
3385
|
}
|
|
3386
|
+
function SkillsBrowser({ state, width }) {
|
|
3387
|
+
const pageCount = pagedPageCount(state);
|
|
3388
|
+
const pageItems = pagedPageItems(state);
|
|
3389
|
+
const showPagination = pageCount > 1;
|
|
3390
|
+
const contentWidth = Math.max(20, width);
|
|
3391
|
+
const header = showPagination
|
|
3392
|
+
? `Skills (${state.skills.length}) · page ${state.pageIndex + 1}/${pageCount}`
|
|
3393
|
+
: `Skills (${state.skills.length})`;
|
|
3394
|
+
const footer = showPagination
|
|
3395
|
+
? "↑/↓ select · ←/→ page · Enter details · i invoke · a import · d/Delete remove · Esc close"
|
|
3396
|
+
: "↑/↓ select · Enter details · i invoke · a import · d/Delete remove · Esc close";
|
|
3397
|
+
const nameWidth = Math.min(28, Math.max(...pageItems.map((skill) => skill.name.length)));
|
|
3398
|
+
return e(Box, { flexDirection: "column", marginTop: 1 }, e(Text, { color: "cyan", bold: true }, fitToWidth(header, contentWidth)), ...pageItems.map((skill, index) => {
|
|
3399
|
+
const selected = index === state.selectedIndex;
|
|
3400
|
+
const absoluteIndex = state.pageIndex * state.pageSize + index;
|
|
3401
|
+
const prefix = `${absoluteIndex + 1}.`.padStart(String(state.skills.length).length + 1);
|
|
3402
|
+
const tags = skill.tags?.length ? ` [${skill.tags.join(",")}]` : "";
|
|
3403
|
+
const execution = skill.execution ? ` (${skill.execution})` : "";
|
|
3404
|
+
const restWidth = Math.max(0, contentWidth - prefix.length - nameWidth - 4);
|
|
3405
|
+
const rest = fitToWidth(`${skill.description}${execution}${tags}`, restWidth);
|
|
3406
|
+
return e(Text, { key: skill.name, color: "white" }, e(Text, { color: selected ? "black" : "white", backgroundColor: selected ? "cyan" : undefined }, prefix), e(Text, { color: "gray" }, " "), e(Text, { color: "cyan" }, fitToWidth(skill.name, nameWidth).padEnd(nameWidth)), e(Text, { color: "gray" }, " "), e(Text, { color: selected ? "white" : "gray" }, rest));
|
|
3407
|
+
}), e(Text, { color: "gray" }, fitToWidth(footer, contentWidth)));
|
|
3408
|
+
}
|
|
3409
|
+
function SecretsBrowser({ state, width }) {
|
|
3410
|
+
const pageCount = pagedPageCount(state);
|
|
3411
|
+
const pageItems = pagedPageItems(state);
|
|
3412
|
+
const showPagination = pageCount > 1;
|
|
3413
|
+
const contentWidth = Math.max(20, width);
|
|
3414
|
+
const header = showPagination
|
|
3415
|
+
? `Secrets (${state.secrets.length}) · page ${state.pageIndex + 1}/${pageCount}`
|
|
3416
|
+
: `Secrets (${state.secrets.length})`;
|
|
3417
|
+
const footer = showPagination
|
|
3418
|
+
? "↑/↓ select · ←/→ page · Enter info · s set · r rename · a add · e empty · d/Delete remove · Esc close"
|
|
3419
|
+
: "↑/↓ select · Enter info · s set · r rename · a add · e empty · d/Delete remove · Esc close";
|
|
3420
|
+
const keyWidth = Math.min(32, Math.max(...pageItems.map((secret) => secret.key.length)));
|
|
3421
|
+
return e(Box, { flexDirection: "column", marginTop: 1 }, e(Text, { color: "cyan", bold: true }, fitToWidth(header, contentWidth)), ...pageItems.map((secret, index) => {
|
|
3422
|
+
const selected = index === state.selectedIndex;
|
|
3423
|
+
const absoluteIndex = state.pageIndex * state.pageSize + index;
|
|
3424
|
+
const prefix = `${absoluteIndex + 1}.`.padStart(String(state.secrets.length).length + 1);
|
|
3425
|
+
const reason = secret.requestReason ? ` reason=${JSON.stringify(secret.requestReason)}` : "";
|
|
3426
|
+
const restWidth = Math.max(0, contentWidth - prefix.length - keyWidth - 4);
|
|
3427
|
+
const rest = fitToWidth(`${secret.status} · length=${secret.length}${reason}`, restWidth);
|
|
3428
|
+
return e(Text, { key: secret.key, color: "white" }, e(Text, { color: selected ? "black" : "white", backgroundColor: selected ? "cyan" : undefined }, prefix), e(Text, { color: "gray" }, " "), e(Text, { color: secret.status === "set" ? "green" : "yellow" }, fitToWidth(secret.key, keyWidth).padEnd(keyWidth)), e(Text, { color: "gray" }, " "), e(Text, { color: selected ? "white" : "gray" }, rest));
|
|
3429
|
+
}), e(Text, { color: "gray" }, fitToWidth(footer, contentWidth)));
|
|
3430
|
+
}
|
|
2818
3431
|
function handleLoginFormInput(value, key, state, setLoginFormState, runtime, append, setStatus) {
|
|
2819
3432
|
if (key.escape) {
|
|
2820
3433
|
if (state.step === "fields")
|
|
@@ -2983,12 +3596,6 @@ function loginValuesForProvider(provider, env) {
|
|
|
2983
3596
|
for (const field of LOGIN_FIELD_DEFINITIONS[provider]) {
|
|
2984
3597
|
values[field.key] = env[field.envKey] ?? "";
|
|
2985
3598
|
}
|
|
2986
|
-
if (provider === "kimi") {
|
|
2987
|
-
values.apiKey ||= env.MOONSHOT_API_KEY ?? process.env.MOONSHOT_API_KEY ?? "";
|
|
2988
|
-
values.baseUrl ||= env.MOONSHOT_BASE_URL ?? process.env.MOONSHOT_BASE_URL ?? "";
|
|
2989
|
-
values.model ||= env.MOONSHOT_MODEL ?? process.env.MOONSHOT_MODEL ?? "";
|
|
2990
|
-
values.fallbackModel ||= env.MOONSHOT_FALLBACK_MODEL ?? process.env.MOONSHOT_FALLBACK_MODEL ?? "";
|
|
2991
|
-
}
|
|
2992
3599
|
if (!values.baseUrl)
|
|
2993
3600
|
values.baseUrl = defaultBaseUrlForLoginProvider(provider);
|
|
2994
3601
|
if (!values.model)
|
|
@@ -2998,15 +3605,11 @@ function loginValuesForProvider(provider, env) {
|
|
|
2998
3605
|
return values;
|
|
2999
3606
|
}
|
|
3000
3607
|
function parseLoginProvider(value) {
|
|
3001
|
-
if (value === "openai" || value === "anthropic"
|
|
3608
|
+
if (value === "openai" || value === "anthropic")
|
|
3002
3609
|
return value;
|
|
3003
3610
|
return undefined;
|
|
3004
3611
|
}
|
|
3005
3612
|
function guessLoginProvider(env) {
|
|
3006
|
-
if (env.KIMI_API_KEY ?? env.MOONSHOT_API_KEY ?? process.env.KIMI_API_KEY ?? process.env.MOONSHOT_API_KEY)
|
|
3007
|
-
return "kimi";
|
|
3008
|
-
if (env.DEEPSEEK_API_KEY ?? process.env.DEEPSEEK_API_KEY)
|
|
3009
|
-
return "deepseek";
|
|
3010
3613
|
if (env.ANTHROPIC_API_KEY ?? process.env.ANTHROPIC_API_KEY)
|
|
3011
3614
|
return "anthropic";
|
|
3012
3615
|
return "openai";
|
|
@@ -3014,19 +3617,11 @@ function guessLoginProvider(env) {
|
|
|
3014
3617
|
function defaultBaseUrlForLoginProvider(provider) {
|
|
3015
3618
|
if (provider === "anthropic")
|
|
3016
3619
|
return "https://api.anthropic.com";
|
|
3017
|
-
if (provider === "deepseek")
|
|
3018
|
-
return "https://api.deepseek.com";
|
|
3019
|
-
if (provider === "kimi")
|
|
3020
|
-
return "https://api.moonshot.cn/v1";
|
|
3021
3620
|
return "https://api.openai.com";
|
|
3022
3621
|
}
|
|
3023
3622
|
function defaultModelForLoginProvider(provider) {
|
|
3024
3623
|
if (provider === "anthropic")
|
|
3025
3624
|
return "claude-sonnet-4-6";
|
|
3026
|
-
if (provider === "deepseek")
|
|
3027
|
-
return "deepseek-chat";
|
|
3028
|
-
if (provider === "kimi")
|
|
3029
|
-
return "kimi-k2.6";
|
|
3030
3625
|
return "gpt-5.5";
|
|
3031
3626
|
}
|
|
3032
3627
|
function loginFormViewHeight(state) {
|
|
@@ -3045,7 +3640,7 @@ function LoginFormView({ state, width }) {
|
|
|
3045
3640
|
const visibleValue = formatLoginFieldValue(field, rawValue, selected ? state.cursor : undefined);
|
|
3046
3641
|
const placeholder = rawValue ? "" : (field.placeholder ? ` (${field.placeholder})` : "");
|
|
3047
3642
|
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))));
|
|
3048
|
-
}), 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_* / ANTHROPIC_
|
|
3643
|
+
}), 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_* / ANTHROPIC_*; shared runtime fields save as MODEL_*.", contentWidth)));
|
|
3049
3644
|
}
|
|
3050
3645
|
function formatLoginFieldValue(field, value, cursor) {
|
|
3051
3646
|
const display = field.secret && value ? "•".repeat(Math.min(value.length, 24)) : value;
|
|
@@ -3071,12 +3666,6 @@ function envEntriesForLoginForm(state) {
|
|
|
3071
3666
|
const value = (state.values[field.key] ?? "").trim();
|
|
3072
3667
|
entries[field.envKey] = value || undefined;
|
|
3073
3668
|
}
|
|
3074
|
-
if (state.provider === "kimi") {
|
|
3075
|
-
entries.MOONSHOT_API_KEY = undefined;
|
|
3076
|
-
entries.MOONSHOT_BASE_URL = undefined;
|
|
3077
|
-
entries.MOONSHOT_MODEL = undefined;
|
|
3078
|
-
entries.MOONSHOT_FALLBACK_MODEL = undefined;
|
|
3079
|
-
}
|
|
3080
3669
|
return entries;
|
|
3081
3670
|
}
|
|
3082
3671
|
function updateEnvContent(content, updates, removeKeys = []) {
|
|
@@ -3104,8 +3693,6 @@ function updateEnvContent(content, updates, removeKeys = []) {
|
|
|
3104
3693
|
appendEnvGroup(updatedLines, "# Neo active provider", grouped.active);
|
|
3105
3694
|
appendEnvGroup(updatedLines, "# OpenAI provider settings", grouped.openai);
|
|
3106
3695
|
appendEnvGroup(updatedLines, "# Anthropic provider settings", grouped.anthropic);
|
|
3107
|
-
appendEnvGroup(updatedLines, "# DeepSeek provider settings", grouped.deepseek);
|
|
3108
|
-
appendEnvGroup(updatedLines, "# Kimi provider settings", grouped.kimi);
|
|
3109
3696
|
appendEnvGroup(updatedLines, "# Shared model runtime settings", grouped.shared);
|
|
3110
3697
|
}
|
|
3111
3698
|
return `${updatedLines.join("\n").replace(/\n*$/u, "")}\n`;
|
|
@@ -3115,8 +3702,6 @@ function groupLoginEnvEntries(entries) {
|
|
|
3115
3702
|
active: entries.filter(([key]) => key === "MODEL_PROVIDER"),
|
|
3116
3703
|
openai: entries.filter(([key]) => key.startsWith("OPENAI_")),
|
|
3117
3704
|
anthropic: entries.filter(([key]) => key.startsWith("ANTHROPIC_")),
|
|
3118
|
-
deepseek: entries.filter(([key]) => key.startsWith("DEEPSEEK_")),
|
|
3119
|
-
kimi: entries.filter(([key]) => key.startsWith("KIMI_") || key.startsWith("MOONSHOT_")),
|
|
3120
3705
|
shared: entries.filter(([key]) => key.startsWith("MODEL_") && key !== "MODEL_PROVIDER"),
|
|
3121
3706
|
};
|
|
3122
3707
|
}
|
|
@@ -3366,12 +3951,18 @@ function execDescriptionFromInput(input) {
|
|
|
3366
3951
|
function isPlanToolPayload(value) {
|
|
3367
3952
|
if (!isRecord(value) || !Array.isArray(value.items))
|
|
3368
3953
|
return false;
|
|
3369
|
-
return value.items.every(
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3954
|
+
return value.items.every(isPlanItemLike);
|
|
3955
|
+
}
|
|
3956
|
+
function isPlanItemLike(item) {
|
|
3957
|
+
if (!isRecord(item))
|
|
3958
|
+
return false;
|
|
3959
|
+
if (typeof item.description !== "string")
|
|
3960
|
+
return false;
|
|
3961
|
+
if (item.status !== "pending" && item.status !== "in_progress" && item.status !== "completed")
|
|
3962
|
+
return false;
|
|
3963
|
+
if (item.subitems === undefined)
|
|
3964
|
+
return true;
|
|
3965
|
+
return Array.isArray(item.subitems) && item.subitems.every(isPlanItemLike);
|
|
3375
3966
|
}
|
|
3376
3967
|
function planToolBodyTitle(payload) {
|
|
3377
3968
|
const title = payload.title?.trim();
|
|
@@ -3383,16 +3974,25 @@ function formatPlanToolPayload(payload) {
|
|
|
3383
3974
|
sections.push(payload.summary.trim());
|
|
3384
3975
|
if (payload.note?.trim())
|
|
3385
3976
|
sections.push(payload.note.trim());
|
|
3386
|
-
sections.push(payload.items.
|
|
3977
|
+
sections.push(payload.items.flatMap((item) => formatPlanItem(item)).join("\n"));
|
|
3387
3978
|
return sections.filter(Boolean).join("\n");
|
|
3388
3979
|
}
|
|
3389
|
-
function formatPlanItem(item) {
|
|
3980
|
+
function formatPlanItem(item, depth = 0) {
|
|
3981
|
+
const indent = " ".repeat(Math.max(0, depth));
|
|
3390
3982
|
const text = escapePlanMarkdown(item.description.trim());
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3983
|
+
const marker = planItemMarker(item.status);
|
|
3984
|
+
const line = item.status === "completed"
|
|
3985
|
+
? `${indent}- ${marker} ~~${text}~~`
|
|
3986
|
+
: `${indent}- ${marker} ${text}`;
|
|
3987
|
+
const subitems = item.subitems?.flatMap((subitem) => formatPlanItem(subitem, depth + 1)) ?? [];
|
|
3988
|
+
return [line, ...subitems];
|
|
3989
|
+
}
|
|
3990
|
+
function planItemMarker(status) {
|
|
3991
|
+
if (status === "completed")
|
|
3992
|
+
return "✓";
|
|
3993
|
+
if (status === "in_progress")
|
|
3994
|
+
return "▶";
|
|
3995
|
+
return "○";
|
|
3396
3996
|
}
|
|
3397
3997
|
function escapePlanMarkdown(text) {
|
|
3398
3998
|
return text.replace(/([\\`*_{}[\]()#+.!|>~-])/g, "\\$1");
|
|
@@ -4070,6 +4670,8 @@ const TERMINAL_TITLE_WORKING_PREFIX = "● ";
|
|
|
4070
4670
|
const TERMINAL_TITLE_READY_PREFIX = "✓ ";
|
|
4071
4671
|
const REPL_ANIMATION_INTERVAL_MS = 420;
|
|
4072
4672
|
const TOOL_RESULT_REPLACEMENT_DELAY_MS = 2000;
|
|
4673
|
+
const SUBAGENT_ACTIVITY_UPDATE_DEBOUNCE_MS = 180;
|
|
4674
|
+
const SUBAGENT_COMPLETED_LINGER_MS = 8000;
|
|
4073
4675
|
const TOKEN_PULSE_MS = 900;
|
|
4074
4676
|
const ANIMATED_NUMBER_INTERVAL_MS = 50;
|
|
4075
4677
|
const ANIMATED_NUMBER_MIN_DURATION_MS = 180;
|
|
@@ -4082,6 +4684,8 @@ const STATUS_SHIMMER_RADIUS = 1;
|
|
|
4082
4684
|
const STATUS_SHIMMER_COLOR = "whiteBright";
|
|
4083
4685
|
const STATUS_SEPARATOR = " · ";
|
|
4084
4686
|
const STATUS_BAR_RENDER_ROWS = 2;
|
|
4687
|
+
const FOREGROUND_EXEC_DETACH_HINT_RENDER_ROWS = 1;
|
|
4688
|
+
const FOREGROUND_EXEC_DETACH_HINT_DELAY_MS = 2000;
|
|
4085
4689
|
const BACKGROUND_TASK_STATUS_RENDER_ROWS = 1;
|
|
4086
4690
|
const QUEUED_INPUT_RENDER_ROWS = 1;
|
|
4087
4691
|
const EMPTY_CTRL_C_EXIT_PLACEHOLDER = "Press Ctrl+C again to exit";
|