mixdog 0.9.151 → 0.9.152
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 +110 -68
- package/package.json +3 -2
- package/scripts/tool-stress.mjs +0 -2
- package/src/defaults/skills/setup/SKILL.md +18 -27
- package/src/headless-exec.mjs +3 -10
- package/src/headless-exec.test.mjs +2 -7
- package/src/runtime/agent/orchestrator/config.mjs +6 -66
- package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +153 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +49 -45
- package/src/runtime/agent/orchestrator/mcp/client.mjs +242 -15
- package/src/runtime/agent/orchestrator/mcp/features.test.mjs +66 -0
- package/src/runtime/agent/orchestrator/mcp/security.test.mjs +36 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +54 -33
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +3 -8
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +10 -2
- package/src/runtime/agent/orchestrator/providers/lib/provider-replay.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/lib/sse-framing.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +25 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +35 -4
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +0 -2
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +13 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +4 -7
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +235 -0
- package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +719 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +399 -39
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +26 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +24 -1
- package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +161 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +14 -4
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +31 -18
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/message-sanitize.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +556 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +457 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +105 -186
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +115 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -10
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +30 -14
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +15 -4
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +73 -15
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +76 -2
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +24 -683
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +64 -72
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +2 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.mjs +75 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +24 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +24 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +12 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +35 -21
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +28 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +54 -0
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/browser-bridge/client.mjs +103 -0
- package/src/runtime/browser-bridge/tool-defs.mjs +42 -0
- package/src/runtime/computer-bridge/client.mjs +101 -0
- package/src/runtime/computer-bridge/tool-defs.mjs +53 -0
- package/src/runtime/media/store.mjs +18 -8
- package/src/runtime/media/store.test.mjs +14 -0
- package/src/runtime/memory/index.mjs +1 -1
- package/src/runtime/memory/lib/transcript-ingest.mjs +8 -6
- package/src/runtime/memory/lib/transcript-ingest.test.mjs +52 -0
- package/src/runtime/shared/child-spawn-gate.mjs +17 -0
- package/src/runtime/shared/child-spawn-remote.mjs +69 -7
- package/src/runtime/shared/child-spawn-remote.test.mjs +124 -0
- package/src/runtime/shared/pristine-execution-contract.json +2 -1
- package/src/runtime/shared/provider-api-key.mjs +1 -0
- package/src/runtime/shared/session-runtime-health.mjs +91 -0
- package/src/runtime/shared/session-runtime-health.test.mjs +53 -0
- package/src/runtime/shared/skill-document.mjs +86 -0
- package/src/runtime/shared/skill-document.test.mjs +81 -0
- package/src/runtime/shared/tool-surface.mjs +18 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +1 -0
- package/src/runtime/shared/turn-snapshot.mjs +32 -9
- package/src/runtime/shared/user-cwd.mjs +33 -14
- package/src/runtime/shared/user-cwd.test.mjs +43 -0
- package/src/session-runtime/cwd-plugins.mjs +26 -26
- package/src/session-runtime/cwd-plugins.test.mjs +75 -0
- package/src/session-runtime/cwd-tool-routing.test.mjs +5 -0
- package/src/session-runtime/global-extensions.mjs +26 -0
- package/src/session-runtime/global-extensions.test.mjs +28 -0
- package/src/session-runtime/goal-runtime.mjs +721 -0
- package/src/session-runtime/goal-runtime.test.mjs +108 -0
- package/src/session-runtime/lifecycle-api.mjs +18 -4
- package/src/session-runtime/lifecycle-api.test.mjs +21 -0
- package/src/session-runtime/mcp-glue.mjs +49 -47
- package/src/session-runtime/plugin-mcp-standard.test.mjs +177 -0
- package/src/session-runtime/plugin-mcp.mjs +96 -22
- package/src/session-runtime/prewarm.mjs +1 -10
- package/src/session-runtime/resource-api-global.test.mjs +69 -0
- package/src/session-runtime/resource-api.mjs +173 -58
- package/src/session-runtime/runtime-core.mjs +148 -20
- package/src/session-runtime/runtime-tunables.mjs +6 -8
- package/src/session-runtime/session-turn-api.mjs +22 -11
- package/src/session-runtime/skills-api.mjs +79 -25
- package/src/session-runtime/skills-api.test.mjs +99 -0
- package/src/session-runtime/tool-catalog-schema.mjs +0 -5
- package/src/session-runtime/tool-catalog.mjs +4 -13
- package/src/session-runtime/tool-policy-surface.test.mjs +33 -11
- package/src/session-runtime/tool-surface.mjs +12 -0
- package/src/standalone/agent-dispatch-broker.mjs +5 -9
- package/src/standalone/agent-tool/notify.mjs +6 -8
- package/src/standalone/agent-tool/notify.test.mjs +37 -0
- package/src/standalone/daemon.mjs +31 -15
- package/src/standalone/hook-bus/config.mjs +1 -0
- package/src/standalone/plugin-admin.mjs +17 -0
- package/src/standalone/plugin-admin.test.mjs +7 -0
- package/src/standalone/provider-admin.mjs +1 -0
- package/src/standalone/session-protocol.mjs +5 -0
- package/src/standalone/session-runtime-agent-control-client.mjs +133 -0
- package/src/standalone/session-runtime-agent-control-client.test.mjs +71 -0
- package/src/standalone/session-runtime-dispatch-cancel.test.mjs +139 -0
- package/src/standalone/session-runtime-host-factory.mjs +17 -0
- package/src/standalone/session-runtime-host-factory.test.mjs +31 -0
- package/src/standalone/session-runtime-host-health.test.mjs +5 -0
- package/src/standalone/session-runtime-host.mjs +696 -58
- package/src/standalone/session-runtime-inline-host.mjs +351 -0
- package/src/standalone/session-runtime-inline-host.test.mjs +83 -0
- package/src/standalone/session-runtime-provider-cooldown.mjs +78 -0
- package/src/standalone/session-runtime-provider-cooldown.test.mjs +80 -0
- package/src/standalone/session-runtime-shard-host.test.mjs +569 -0
- package/src/standalone/session-runtime-shard-router.mjs +149 -0
- package/src/standalone/session-runtime-shard-router.test.mjs +101 -0
- package/src/standalone/session-runtime-worker.mjs +476 -18
- package/src/standalone/session-service.mjs +81 -6
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/core-memory-picker.mjs +2 -0
- package/src/tui/app/extension-pickers.mjs +1 -1
- package/src/tui/app/maintenance-pickers.mjs +3 -3
- package/src/tui/app/model-options.mjs +2 -0
- package/src/tui/app/model-picker.mjs +14 -4
- package/src/tui/app/panel-handoff.test.mjs +311 -0
- package/src/tui/app/project-picker.mjs +1 -0
- package/src/tui/app/route-pickers.mjs +21 -6
- package/src/tui/app/settings-picker.mjs +26 -3
- package/src/tui/app/slash-commands.mjs +1 -0
- package/src/tui/app/slash-dispatch.mjs +73 -22
- package/src/tui/app/theme-effort-pickers.mjs +7 -2
- package/src/tui/components/Picker.jsx +10 -1
- package/src/tui/dist/index.mjs +163 -44
- package/src/tui/session/context-state.mjs +1 -0
- package/src/tui/session/goal-continuation.mjs +110 -0
- package/src/tui/session/goal-continuation.test.mjs +84 -0
- package/src/tui/session/queue-helpers.mjs +5 -2
- package/src/tui/session/session-action-surface.test.mjs +108 -0
- package/src/tui/session/session-api-ext.mjs +39 -8
- package/src/tui/session/session-api.mjs +58 -0
- package/src/tui/session/session-flow.mjs +6 -0
- package/src/tui/session/turn.mjs +1 -0
- package/src/tui/session-local.mjs +15 -1
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +0 -301
|
@@ -188,6 +188,9 @@ export function createRoutePickers({
|
|
|
188
188
|
|
|
189
189
|
const openWorkflowPicker = async (options = {}) => {
|
|
190
190
|
const returnTo = typeof options.returnTo === 'function' ? options.returnTo : null;
|
|
191
|
+
const handoffPanel = options.handoffPanel && typeof options.handoffPanel === 'object'
|
|
192
|
+
? options.handoffPanel
|
|
193
|
+
: null;
|
|
191
194
|
const own = surface.claim();
|
|
192
195
|
let workflows = [];
|
|
193
196
|
try {
|
|
@@ -225,21 +228,26 @@ export function createRoutePickers({
|
|
|
225
228
|
// Clear-and-continue: the flow keeps the surface it just emptied, so
|
|
226
229
|
// the post-ack hop below is bound to THIS keypress. An Esc before the
|
|
227
230
|
// switch acks cancels the hop instead of re-opening Settings.
|
|
228
|
-
own.paint(
|
|
231
|
+
own.paint(handoffPanel);
|
|
229
232
|
const returnAfterSwitch = own.defer(() => { if (returnTo) returnTo(); });
|
|
230
233
|
void store.setWorkflow?.(workflow.id)
|
|
231
234
|
.then((result) => {
|
|
232
235
|
if (!result) {
|
|
233
236
|
store.pushNotice('Workflow switch is already running.', 'warn');
|
|
237
|
+
if (handoffPanel) returnAfterSwitch();
|
|
234
238
|
return;
|
|
235
239
|
}
|
|
236
240
|
store.pushNotice(workflowSwitchNotice(result), 'info');
|
|
237
241
|
returnAfterSwitch();
|
|
238
242
|
})
|
|
239
|
-
.catch((e) =>
|
|
243
|
+
.catch((e) => {
|
|
244
|
+
store.pushNotice(`Couldn’t switch workflow: ${e?.message || e}`, 'error');
|
|
245
|
+
if (handoffPanel) returnAfterSwitch();
|
|
246
|
+
});
|
|
240
247
|
},
|
|
241
248
|
onCancel: () => {
|
|
242
|
-
own.
|
|
249
|
+
if (handoffPanel) own.paint(handoffPanel);
|
|
250
|
+
else own.close();
|
|
243
251
|
if (returnTo) returnTo();
|
|
244
252
|
},
|
|
245
253
|
});
|
|
@@ -247,6 +255,9 @@ export function createRoutePickers({
|
|
|
247
255
|
|
|
248
256
|
const openOutputStylePicker = async (options = {}) => {
|
|
249
257
|
const returnTo = typeof options.returnTo === 'function' ? options.returnTo : null;
|
|
258
|
+
const handoffPanel = options.handoffPanel && typeof options.handoffPanel === 'object'
|
|
259
|
+
? options.handoffPanel
|
|
260
|
+
: null;
|
|
250
261
|
// Onboarding mode: Enter (row select) and ConfirmBar Next must both persist
|
|
251
262
|
// the chosen style, then advance. `onboarding.onAdvance/onBack` drive the
|
|
252
263
|
// wizard; the confirm bar is built here so both paths share `saveStyle`.
|
|
@@ -284,7 +295,7 @@ export function createRoutePickers({
|
|
|
284
295
|
// Onboarding advance: keep the current picker visible during the async
|
|
285
296
|
// style switch so the screen never flashes empty between steps; the next
|
|
286
297
|
// step (or finishOnboarding) replaces/clears the picker itself.
|
|
287
|
-
if (!(advance && onboarding)) own.paint(
|
|
298
|
+
if (!(advance && onboarding)) own.paint(handoffPanel);
|
|
288
299
|
// Post-ack delegation (next onboarding step, or back to the caller):
|
|
289
300
|
// bound after this keypress's own navigation so an Esc while the switch
|
|
290
301
|
// is in flight cannot paint a panel over the new surface.
|
|
@@ -301,7 +312,10 @@ export function createRoutePickers({
|
|
|
301
312
|
}
|
|
302
313
|
advanceAfterSave();
|
|
303
314
|
})
|
|
304
|
-
.catch((e) =>
|
|
315
|
+
.catch((e) => {
|
|
316
|
+
store.pushNotice(`Couldn’t switch output style: ${e?.message || e}`, 'error');
|
|
317
|
+
if (handoffPanel) advanceAfterSave();
|
|
318
|
+
});
|
|
305
319
|
};
|
|
306
320
|
own.paint({
|
|
307
321
|
title: 'Output Style',
|
|
@@ -334,7 +348,8 @@ export function createRoutePickers({
|
|
|
334
348
|
saveStyle(style.id, { advance: Boolean(onboarding) });
|
|
335
349
|
},
|
|
336
350
|
onCancel: () => {
|
|
337
|
-
own.
|
|
351
|
+
if (handoffPanel) own.paint(handoffPanel);
|
|
352
|
+
else own.close();
|
|
338
353
|
if (onboarding) onboarding.onCancel?.();
|
|
339
354
|
else if (returnTo) returnTo();
|
|
340
355
|
},
|
|
@@ -41,6 +41,15 @@ export function createSettingsPicker({
|
|
|
41
41
|
openMemoryCorePicker,
|
|
42
42
|
openUpdatePicker,
|
|
43
43
|
}) {
|
|
44
|
+
const settingsHandoffPanel = (description = 'Loading settings...') => ({
|
|
45
|
+
title: 'Settings',
|
|
46
|
+
description,
|
|
47
|
+
help: 'Esc Close',
|
|
48
|
+
indexMode: 'never',
|
|
49
|
+
pickerKey: 'settings-handoff',
|
|
50
|
+
loading: true,
|
|
51
|
+
items: [],
|
|
52
|
+
});
|
|
44
53
|
// Build generation guard. Every open/refresh takes a ticket and Esc bumps it,
|
|
45
54
|
// so a slow snapshot from a superseded (or already closed) build can never
|
|
46
55
|
// re-open Settings. The ref is owned by App so it survives this per-render
|
|
@@ -60,6 +69,10 @@ export function createSettingsPicker({
|
|
|
60
69
|
// caught here instead.
|
|
61
70
|
const own = surface.claim();
|
|
62
71
|
const light = opts.light === true;
|
|
72
|
+
// A full Settings open usually follows another option panel. Paint the
|
|
73
|
+
// destination synchronously before the daemon snapshot so the empty-chat
|
|
74
|
+
// welcome logo cannot appear between the old panel and Settings.
|
|
75
|
+
if (!light && !own.paint(settingsHandoffPanel())) return;
|
|
63
76
|
const overrides = opts.overrides || null;
|
|
64
77
|
const heavyCache = light ? settingsHeavyCacheRef.current : null;
|
|
65
78
|
// ONE round-trip for the whole panel: on a daemon-backed store each getter
|
|
@@ -448,14 +461,24 @@ export function createSettingsPicker({
|
|
|
448
461
|
else if (item._action === 'memory-enabled') toggleMemory();
|
|
449
462
|
else if (item._action === 'memory-cycles') toggleMemoryCycles();
|
|
450
463
|
else if (item._action === 'voice') applyVoice();
|
|
451
|
-
else if (item._action === 'output-style') openOutputStylePicker({
|
|
452
|
-
|
|
453
|
-
|
|
464
|
+
else if (item._action === 'output-style') openOutputStylePicker({
|
|
465
|
+
returnTo: openSettingsPicker,
|
|
466
|
+
handoffPanel: settingsHandoffPanel('Applying output style...'),
|
|
467
|
+
});
|
|
468
|
+
else if (item._action === 'theme') openThemePicker({
|
|
469
|
+
returnTo: openSettingsPicker,
|
|
470
|
+
handoffPanel: settingsHandoffPanel('Applying theme...'),
|
|
471
|
+
});
|
|
472
|
+
else if (item._action === 'workflow') openWorkflowPicker({
|
|
473
|
+
returnTo: openSettingsPicker,
|
|
474
|
+
handoffPanel: settingsHandoffPanel('Switching workflow...'),
|
|
475
|
+
});
|
|
454
476
|
else if (item._action === 'model') openModelPicker({
|
|
455
477
|
returnTo: openSettingsPicker,
|
|
456
478
|
returnLabel: 'Settings',
|
|
457
479
|
returnOnNestedCancel: true,
|
|
458
480
|
onAfterSelect: openSettingsPicker,
|
|
481
|
+
handoffPanel: settingsHandoffPanel('Switching model...'),
|
|
459
482
|
});
|
|
460
483
|
else if (item._action === 'websearch') openWebSearchPicker({
|
|
461
484
|
returnTo: openSettingsPicker,
|
|
@@ -7,6 +7,7 @@ export const SLASH_COMMANDS = [
|
|
|
7
7
|
{ name: 'clear', usage: '/clear', aliases: ['new'], aliasUsage: ['new'], description: 'Start a fresh chat' },
|
|
8
8
|
{ name: 'project', usage: '/project', aliases: ['projects'], aliasUsage: ['projects'], showAliasUsage: false, params: '[path]', description: 'Switch working directory (project)' },
|
|
9
9
|
{ name: 'compact', usage: '/compact', description: 'Compact older conversation context' },
|
|
10
|
+
{ name: 'goal', usage: '/goal', params: '[objective --time 1h|status|pause|resume|edit|complete|clear]', description: 'Run and manage a durable session Goal' },
|
|
10
11
|
{ name: 'autoclear', usage: '/autoclear', params: '[on|off|duration]', description: 'Reduce cache-miss cost after long idle gaps' },
|
|
11
12
|
{ name: 'resume', usage: '/resume', params: '[id]', description: 'Resume a saved chat' },
|
|
12
13
|
{ name: 'context', usage: '/context', description: 'Show current context surface' },
|
|
@@ -55,6 +55,44 @@ export function createSlashDispatch({
|
|
|
55
55
|
runDoctor,
|
|
56
56
|
requestExit,
|
|
57
57
|
}) {
|
|
58
|
+
const openSlashPanel = (command, title, open) => {
|
|
59
|
+
const own = surface.claim();
|
|
60
|
+
if (!own.paint({
|
|
61
|
+
_kind: `slash-loading:${command}`,
|
|
62
|
+
title,
|
|
63
|
+
description: `Loading ${title.toLowerCase()}...`,
|
|
64
|
+
help: 'Esc Close',
|
|
65
|
+
indexMode: 'never',
|
|
66
|
+
pickerKey: `slash-loading:${command}`,
|
|
67
|
+
loading: true,
|
|
68
|
+
items: [],
|
|
69
|
+
onSelect: () => {},
|
|
70
|
+
onCancel: () => own.close(),
|
|
71
|
+
})) return;
|
|
72
|
+
const finishUnclaimedLoading = () => {
|
|
73
|
+
// A real picker/context/usage surface supersedes this loading identity.
|
|
74
|
+
// If the opener completed without painting (empty result or failure),
|
|
75
|
+
// remove only the still-owned placeholder and restore the normal prompt.
|
|
76
|
+
if (own.owns()) own.close();
|
|
77
|
+
};
|
|
78
|
+
try {
|
|
79
|
+
const opening = open();
|
|
80
|
+
if (opening && typeof opening.then === 'function') {
|
|
81
|
+
void Promise.resolve(opening).then(
|
|
82
|
+
finishUnclaimedLoading,
|
|
83
|
+
(error) => {
|
|
84
|
+
finishUnclaimedLoading();
|
|
85
|
+
store.pushNotice(`${title} panel failed: ${error?.message || error}`, 'error');
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
} else {
|
|
89
|
+
finishUnclaimedLoading();
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
finishUnclaimedLoading();
|
|
93
|
+
store.pushNotice(`${title} panel failed: ${error?.message || error}`, 'error');
|
|
94
|
+
}
|
|
95
|
+
};
|
|
58
96
|
const runSlashCommand = (cmd, arg = '') => {
|
|
59
97
|
const rawName = String(cmd || '').toLowerCase();
|
|
60
98
|
cmd = normalizeSlashCommandName(cmd);
|
|
@@ -86,12 +124,12 @@ export function createSlashDispatch({
|
|
|
86
124
|
return true;
|
|
87
125
|
case 'model':
|
|
88
126
|
if (!arg) {
|
|
89
|
-
openModelPicker();
|
|
127
|
+
openSlashPanel('model', 'Model', () => openModelPicker());
|
|
90
128
|
return true;
|
|
91
129
|
}
|
|
92
130
|
if (arg.trim().toLowerCase() === 'refresh') {
|
|
93
131
|
// Explicit catalog reload: force a fresh remote provider list.
|
|
94
|
-
openModelPicker({ refreshModels: true });
|
|
132
|
+
openSlashPanel('model', 'Model', () => openModelPicker({ refreshModels: true }));
|
|
95
133
|
return true;
|
|
96
134
|
}
|
|
97
135
|
void store.setModel(arg)
|
|
@@ -104,14 +142,16 @@ export function createSlashDispatch({
|
|
|
104
142
|
// in-flight turn, and the same picker is already reachable mid-turn via
|
|
105
143
|
// /settings, so blocking it here was inconsistent.
|
|
106
144
|
if (arg) store.pushNotice('/websearch sets the web-search provider/model; the web_search tool uses that model when called.', 'warn');
|
|
107
|
-
openWebSearchPicker();
|
|
145
|
+
openSlashPanel('websearch', 'Web Search Model', () => openWebSearchPicker());
|
|
108
146
|
return true;
|
|
109
147
|
case 'agents':
|
|
110
|
-
|
|
148
|
+
openSlashPanel('agents', 'Agents', () => openAgentsPicker(
|
|
149
|
+
arg.trim().toLowerCase() === 'refresh' ? { refreshModels: true } : {},
|
|
150
|
+
));
|
|
111
151
|
return true;
|
|
112
152
|
case 'workflow':
|
|
113
153
|
if (!arg) {
|
|
114
|
-
openWorkflowPicker();
|
|
154
|
+
openSlashPanel('workflow', 'Workflow', () => openWorkflowPicker());
|
|
115
155
|
return true;
|
|
116
156
|
}
|
|
117
157
|
void store.setWorkflow?.(arg.trim())
|
|
@@ -132,7 +172,7 @@ export function createSlashDispatch({
|
|
|
132
172
|
const value = arg.trim();
|
|
133
173
|
const lower = value.toLowerCase();
|
|
134
174
|
if (!value) {
|
|
135
|
-
openOutputStylePicker();
|
|
175
|
+
openSlashPanel('outputstyle', 'Output Style', () => openOutputStylePicker());
|
|
136
176
|
return true;
|
|
137
177
|
}
|
|
138
178
|
if (lower === 'status' || lower === 'current' || lower === 'show') {
|
|
@@ -159,7 +199,7 @@ export function createSlashDispatch({
|
|
|
159
199
|
const value = arg.trim();
|
|
160
200
|
const lower = value.toLowerCase();
|
|
161
201
|
if (!value) {
|
|
162
|
-
openThemePicker();
|
|
202
|
+
openSlashPanel('theme', 'Theme', () => openThemePicker());
|
|
163
203
|
return true;
|
|
164
204
|
}
|
|
165
205
|
let themes = [];
|
|
@@ -194,7 +234,7 @@ export function createSlashDispatch({
|
|
|
194
234
|
// turn. Same for /fast below.
|
|
195
235
|
const pendingTurn = state.busy ? ' (applies from the next turn)' : '';
|
|
196
236
|
if (!arg) {
|
|
197
|
-
openEffortPicker();
|
|
237
|
+
openSlashPanel('effort', 'Effort', () => openEffortPicker());
|
|
198
238
|
return true;
|
|
199
239
|
}
|
|
200
240
|
void store.setEffort(arg)
|
|
@@ -237,23 +277,23 @@ export function createSlashDispatch({
|
|
|
237
277
|
enterProject(target);
|
|
238
278
|
return true;
|
|
239
279
|
}
|
|
240
|
-
openProjectPicker();
|
|
280
|
+
openSlashPanel('project', 'Projects', () => openProjectPicker());
|
|
241
281
|
return true;
|
|
242
282
|
}
|
|
243
283
|
case 'mcp':
|
|
244
|
-
openMcpPicker();
|
|
284
|
+
openSlashPanel('mcp', 'MCP Servers', () => openMcpPicker());
|
|
245
285
|
return true;
|
|
246
286
|
case 'skills':
|
|
247
|
-
openSkillsPicker();
|
|
287
|
+
openSlashPanel('skills', 'Skills', () => openSkillsPicker());
|
|
248
288
|
return true;
|
|
249
289
|
case 'plugins':
|
|
250
|
-
openPluginsPicker();
|
|
290
|
+
openSlashPanel('plugins', 'Plugins', () => openPluginsPicker());
|
|
251
291
|
return true;
|
|
252
292
|
case 'hooks':
|
|
253
|
-
openHooksPicker();
|
|
293
|
+
openSlashPanel('hooks', 'Hooks', () => openHooksPicker());
|
|
254
294
|
return true;
|
|
255
295
|
case 'providers':
|
|
256
|
-
|
|
296
|
+
openSlashPanel('providers', 'Providers', () => openProviderSetupPicker());
|
|
257
297
|
return true;
|
|
258
298
|
case 'schedules':
|
|
259
299
|
case 'webhooks':
|
|
@@ -263,7 +303,7 @@ export function createSlashDispatch({
|
|
|
263
303
|
return true;
|
|
264
304
|
case 'memory': {
|
|
265
305
|
if (!arg.trim()) {
|
|
266
|
-
openMemoryCorePicker({ returnTo: null });
|
|
306
|
+
openSlashPanel('memory', 'Memory', () => openMemoryCorePicker({ returnTo: null }));
|
|
267
307
|
return true;
|
|
268
308
|
}
|
|
269
309
|
void store.memoryControl?.(parseMemoryCommand(arg))
|
|
@@ -273,7 +313,7 @@ export function createSlashDispatch({
|
|
|
273
313
|
case 'autoclear': {
|
|
274
314
|
const value = arg.trim().toLowerCase();
|
|
275
315
|
if (!value) {
|
|
276
|
-
openAutoClearPicker();
|
|
316
|
+
openSlashPanel('autoclear', 'Auto-clear', () => openAutoClearPicker());
|
|
277
317
|
return true;
|
|
278
318
|
}
|
|
279
319
|
// Promise-shaped on a daemon-backed store, so the verdict is reported
|
|
@@ -324,6 +364,17 @@ export function createSlashDispatch({
|
|
|
324
364
|
})
|
|
325
365
|
.catch((error) => store.pushNotice(compactFailureNotice(error), 'error'));
|
|
326
366
|
return true;
|
|
367
|
+
case 'goal':
|
|
368
|
+
void Promise.resolve(store.goalControl?.({ command: arg }))
|
|
369
|
+
.then((result) => {
|
|
370
|
+
if (!result) {
|
|
371
|
+
store.pushNotice('Goal is unavailable.', 'warn');
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
store.pushNotice(result.message || 'Goal updated.', 'info');
|
|
375
|
+
})
|
|
376
|
+
.catch((error) => store.pushNotice(`Goal failed: ${error?.message || error}`, 'error'));
|
|
377
|
+
return true;
|
|
327
378
|
case 'resume':
|
|
328
379
|
if (state.busy) {
|
|
329
380
|
store.pushNotice('wait for the current turn to finish before /resume', 'warn');
|
|
@@ -334,14 +385,14 @@ export function createSlashDispatch({
|
|
|
334
385
|
.then(ok => store.pushNotice(ok ? `Resumed ${arg}` : 'Couldn’t resume chat.', ok ? 'info' : 'warn'))
|
|
335
386
|
.catch((e) => store.pushNotice(`Couldn’t resume chat: ${e?.message || e}`, 'error'));
|
|
336
387
|
} else {
|
|
337
|
-
openResumePicker();
|
|
388
|
+
openSlashPanel('resume', 'Resume', () => openResumePicker());
|
|
338
389
|
}
|
|
339
390
|
return true;
|
|
340
391
|
case 'usage':
|
|
341
|
-
openUsagePanel(arg);
|
|
392
|
+
openSlashPanel('usage', 'Provider Quotas', () => openUsagePanel(arg));
|
|
342
393
|
return true;
|
|
343
394
|
case 'context':
|
|
344
|
-
openContextPicker();
|
|
395
|
+
openSlashPanel('context', 'Context Usage', () => openContextPicker());
|
|
345
396
|
return true;
|
|
346
397
|
case 'inherit':
|
|
347
398
|
if (state.busy) {
|
|
@@ -363,13 +414,13 @@ export function createSlashDispatch({
|
|
|
363
414
|
return true;
|
|
364
415
|
case 'settings':
|
|
365
416
|
case 'config':
|
|
366
|
-
openSettingsPicker();
|
|
417
|
+
openSlashPanel('settings', 'Settings', () => openSettingsPicker());
|
|
367
418
|
return true;
|
|
368
419
|
case 'profile':
|
|
369
|
-
openProfilePicker();
|
|
420
|
+
openSlashPanel('profile', 'Profile', () => openProfilePicker());
|
|
370
421
|
return true;
|
|
371
422
|
case 'update':
|
|
372
|
-
openUpdatePicker();
|
|
423
|
+
openSlashPanel('update', 'Update', () => openUpdatePicker());
|
|
373
424
|
return true;
|
|
374
425
|
case 'doctor':
|
|
375
426
|
if (state.commandBusy) {
|
|
@@ -24,6 +24,9 @@ export function createThemeEffortPickers({
|
|
|
24
24
|
const openThemePicker = (options = {}) => {
|
|
25
25
|
const returnTo = typeof options.returnTo === 'function' ? options.returnTo : null;
|
|
26
26
|
const onboarding = options.onboarding || null;
|
|
27
|
+
const handoffPanel = options.handoffPanel && typeof options.handoffPanel === 'object'
|
|
28
|
+
? options.handoffPanel
|
|
29
|
+
: null;
|
|
27
30
|
const own = surface.claim();
|
|
28
31
|
let themes = [];
|
|
29
32
|
try {
|
|
@@ -60,7 +63,8 @@ export function createThemeEffortPickers({
|
|
|
60
63
|
// Onboarding: Enter (row) and ConfirmBar Next both persist the highlighted
|
|
61
64
|
// theme, then advance; Back restores the original palette then steps back.
|
|
62
65
|
const saveTheme = (id, { advance = false } = {}) => {
|
|
63
|
-
own.
|
|
66
|
+
if (handoffPanel) own.paint(handoffPanel);
|
|
67
|
+
else own.close();
|
|
64
68
|
const applied = applyTheme(id, { persist: true });
|
|
65
69
|
store.pushNotice(themeNotice(applied || { id }), 'info');
|
|
66
70
|
if (advance && onboarding) onboarding.onAdvance?.();
|
|
@@ -102,7 +106,8 @@ export function createThemeEffortPickers({
|
|
|
102
106
|
saveTheme(entry.id, { advance: Boolean(onboarding) });
|
|
103
107
|
},
|
|
104
108
|
onCancel: () => {
|
|
105
|
-
own.
|
|
109
|
+
if (handoffPanel) own.paint(handoffPanel);
|
|
110
|
+
else own.close();
|
|
106
111
|
// Restore the theme that was active before the picker opened.
|
|
107
112
|
if (currentId) applyTheme(currentId, { persist: false });
|
|
108
113
|
if (onboarding) onboarding.onCancel?.();
|
|
@@ -94,6 +94,10 @@ export function Picker({
|
|
|
94
94
|
indexMode = 'auto',
|
|
95
95
|
fillHeight = false,
|
|
96
96
|
visibleCount = MAX_VISIBLE,
|
|
97
|
+
// Loading panels reserve the same content-row geometry as the real list.
|
|
98
|
+
// This prevents an `(empty)` row from being replaced by a multi-row menu in
|
|
99
|
+
// one commit, which reads as the option items jumping into place.
|
|
100
|
+
loading = false,
|
|
97
101
|
// Onboarding confirm bar: { buttons:[{value,label}], onConfirm(button,index) }.
|
|
98
102
|
// When present, ←/→ and Tab drive button focus (mutually exclusive with
|
|
99
103
|
// onLeft/onRight), and Enter fires onConfirm while a button is focused.
|
|
@@ -284,6 +288,11 @@ export function Picker({
|
|
|
284
288
|
// Clamp selected index when items change length.
|
|
285
289
|
if (items.length === 0) {
|
|
286
290
|
const emptyLine = truncateText(String(description || '').replace(/\s+/g, ' ').trim(), Math.max(0, columns - 4));
|
|
291
|
+
const loadingRows = loading
|
|
292
|
+
? Array.from({ length: visibleLimit }, (_, index) => (
|
|
293
|
+
<Text key={`loading-${index}`} color={theme.inactive}> </Text>
|
|
294
|
+
))
|
|
295
|
+
: null;
|
|
287
296
|
return (
|
|
288
297
|
<Box flexDirection="column" flexShrink={0} height={fillHeight ? '100%' : undefined}>
|
|
289
298
|
<Box
|
|
@@ -303,7 +312,7 @@ export function Picker({
|
|
|
303
312
|
<Text> </Text>
|
|
304
313
|
<Text color={theme.text}>{emptyLine || ' '}</Text>
|
|
305
314
|
<Text> </Text>
|
|
306
|
-
<Text color={theme.inactive}>(empty)</Text>
|
|
315
|
+
{loading ? loadingRows : <Text color={theme.inactive}>(empty)</Text>}
|
|
307
316
|
{hasConfirm ? (
|
|
308
317
|
<>
|
|
309
318
|
<Box flexGrow={1} />
|