mixdog 1.2.1 → 1.2.2
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/package.json +1 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +1 -0
- package/src/repl/slash-commands.mjs +16 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +36 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +49 -10
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +40 -3
- package/src/runtime/agent/orchestrator/mcp/client.mjs +230 -96
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +188 -29
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +34 -7
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +77 -17
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
- package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +56 -23
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +4 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +31 -20
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
- package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -97
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +30 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +41 -21
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +5 -10
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/memory/lib/pg/process.mjs +34 -24
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/err-text.mjs +25 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +123 -134
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -40
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +11 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/model-display.mjs +13 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/turn-changes.mjs +447 -219
- package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
- package/src/runtime/shared/turn-snapshot.mjs +170 -48
- package/src/runtime/shared/turn-worktree-snapshot.mjs +135 -46
- package/src/runtime/shared/worktrees/contract.mjs +0 -24
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/session-runtime/boot/apis.mjs +6 -0
- package/src/session-runtime/boot/infrastructure.mjs +6 -0
- package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/connect.mjs +29 -9
- package/src/session-runtime/mcp-glue/server-catalog.mjs +20 -3
- package/src/session-runtime/mcp-glue.mjs +3 -0
- package/src/session-runtime/mcp-oauth-login.mjs +174 -0
- package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
- package/src/session-runtime/model-route/set-route.mjs +83 -15
- package/src/session-runtime/model-route/web-search-route.mjs +1 -1
- package/src/session-runtime/native-web-search/routes.mjs +22 -3
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
- package/src/session-runtime/quick-web-search-models.mjs +13 -38
- package/src/session-runtime/resource-extension-sync.mjs +4 -0
- package/src/session-runtime/resource-mcp-api.mjs +98 -2
- package/src/session-runtime/runtime-review-api.mjs +13 -0
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
- package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/settings-compaction-api.mjs +1 -0
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-telemetry.mjs +55 -0
- package/src/standalone/daemon.mjs +3 -7
- package/src/standalone/session-protocol.mjs +7 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
- package/src/standalone/session-service/agent-tree.mjs +12 -0
- package/src/standalone/session-service/quota-resume.mjs +8 -0
- package/src/standalone/session-service/retention/busy.mjs +15 -1
- package/src/standalone/session-service-api.mjs +5 -0
- package/src/standalone/session-service.mjs +1 -0
- package/src/tui/app/app-view.jsx +1 -0
- package/src/tui/app/create-app-pickers.mjs +4 -0
- package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
- package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
- package/src/tui/app/extension-pickers.mjs +2 -1
- package/src/tui/app/model-options.mjs +11 -16
- package/src/tui/app/slash-commands.mjs +7 -2
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- package/src/tui/components/StatusLine.jsx +2 -0
- package/src/tui/components/status-line/local-line.mjs +5 -2
- package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
- package/src/tui/dist/index.mjs +816 -43
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +33 -24
- package/src/tui/session/session-api/extensions.mjs +22 -0
- package/src/tui/session/session-api/integrations.mjs +7 -0
- package/src/tui/session/session-api/lifecycle.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +5 -14
- package/src/tui/session/session-flow/drain.mjs +7 -1
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/turn.mjs +38 -15
- package/src/tui/statusline-ansi-bridge.mjs +1 -1
- package/src/ui/statusline-agents.mjs +15 -4
- package/src/ui/statusline.mjs +7 -3
- package/src/ui/streaming-markdown-heal.mjs +34 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
// agent-tree/agent-cancel.mjs
|
|
2
2
|
// Cancelling an agent subtree: descendants first, then a durable canonical
|
|
3
3
|
// close of the session itself. Concurrent cancels of one id share a run.
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
cancelBackgroundTask,
|
|
6
|
+
cancelBackgroundTasks,
|
|
7
|
+
listBackgroundTasks,
|
|
8
|
+
} from '../../../runtime/shared/background-tasks.mjs';
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
// Cancel the running Agent task(s) bound to this session itself (owned by its
|
|
11
|
+
// parent) BEFORE the session closes, so the owner hears `cancelled` with the
|
|
12
|
+
// turn's partial output instead of the closed turn failing the task.
|
|
13
|
+
function cancelOwnAgentTasks(sessionId, reason) {
|
|
14
|
+
for (const row of listBackgroundTasks({ surface: 'agent' })) {
|
|
15
|
+
if (row.status === 'running' && row.sessionId === sessionId) cancelBackgroundTask(row.task_id, reason);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createAgentCancellation({
|
|
20
|
+
registry,
|
|
21
|
+
rehydrateAgentSessions,
|
|
22
|
+
sessionOwner,
|
|
23
|
+
entryForSession,
|
|
24
|
+
log,
|
|
25
|
+
rememberCancelTranscript,
|
|
26
|
+
}) {
|
|
7
27
|
const agentCancelRuns = new Map();
|
|
8
28
|
|
|
9
29
|
/** The runtime entry that can close this descriptor: the live owner, else a
|
|
@@ -39,6 +59,7 @@ export function createAgentCancellation({ registry, rehydrateAgentSessions, sess
|
|
|
39
59
|
});
|
|
40
60
|
const children = registry.childrenOf(id);
|
|
41
61
|
await Promise.all(children.map((childId) => cancelAgentTree(childId, reason)));
|
|
62
|
+
cancelOwnAgentTasks(id, reason);
|
|
42
63
|
const descriptor = registry.get(id);
|
|
43
64
|
if (!descriptor) return children.length > 0;
|
|
44
65
|
if (descriptor.closed) return true;
|
|
@@ -47,6 +68,16 @@ export function createAgentCancellation({ registry, rehydrateAgentSessions, sess
|
|
|
47
68
|
if (typeof closeCanonical !== 'function') {
|
|
48
69
|
throw new TypeError('session runtime must implement closeCanonicalSession');
|
|
49
70
|
}
|
|
71
|
+
// The close clears the transcript: capture a running turn's transcript
|
|
72
|
+
// first so the cancelled turn still hands off its partial output.
|
|
73
|
+
if (descriptor.status === 'running') {
|
|
74
|
+
try {
|
|
75
|
+
const { messages } = await entry.runtime.readModelMessages(0);
|
|
76
|
+
rememberCancelTranscript(id, messages);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
log(`agent cancel transcript read failed session=${id}: ${error?.message || error}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
50
81
|
const closed = await closeCanonical.call(entry.runtime, reason);
|
|
51
82
|
if (closed !== true) throw new Error(`agent session ${id} could not be durably closed`);
|
|
52
83
|
descriptor.closed = true;
|
|
@@ -12,16 +12,23 @@ export function createAgentTurns({
|
|
|
12
12
|
retainUnwatched,
|
|
13
13
|
createSession,
|
|
14
14
|
takeWatchdogStop,
|
|
15
|
+
takeCancelTranscript,
|
|
15
16
|
}) {
|
|
16
17
|
const createAgentChild = createAgentChildFactory({ registry, rehydrateAgentSessions, createSession });
|
|
17
18
|
|
|
18
|
-
// The progress watchdog
|
|
19
|
-
// the assistant text the turn produced, so the owner still gets a
|
|
20
|
-
// result.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
// The turn was stopped (progress watchdog or cancellation): rethrow its error
|
|
20
|
+
// carrying the assistant text the turn produced, so the owner still gets a
|
|
21
|
+
// partial result. A cancel that closed the session captured the transcript
|
|
22
|
+
// before the close cleared it.
|
|
23
|
+
async function withPartialHandoff(runtime, sessionId, error, messageStart) {
|
|
24
|
+
try {
|
|
25
|
+
const captured = takeCancelTranscript(sessionId);
|
|
26
|
+
const messages = captured
|
|
27
|
+
? captured.slice(messageStart)
|
|
28
|
+
: (await runtime.readModelMessages(messageStart)).messages;
|
|
29
|
+
const partial = partialHandoffTextFromSession({ messages });
|
|
30
|
+
if (partial) error.partialHandoff = partial;
|
|
31
|
+
} catch {}
|
|
25
32
|
return error;
|
|
26
33
|
}
|
|
27
34
|
|
|
@@ -44,6 +51,7 @@ export function createAgentTurns({
|
|
|
44
51
|
}
|
|
45
52
|
// A stop left over from an earlier turn must not relabel this one.
|
|
46
53
|
takeWatchdogStop(sessionId);
|
|
54
|
+
takeCancelTranscript(sessionId);
|
|
47
55
|
// Transcript length before this turn: where a watchdog stop's partial
|
|
48
56
|
// output starts.
|
|
49
57
|
const { messageCount: messageStart } = await entry.runtime.readModelMessages(Number.MAX_SAFE_INTEGER);
|
|
@@ -64,9 +72,16 @@ export function createAgentTurns({
|
|
|
64
72
|
throw new Error(String(detail.error || 'agent session turn failed'));
|
|
65
73
|
}
|
|
66
74
|
if (detail?.status === 'cancelled') {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
const stop = takeWatchdogStop(sessionId) || new Error('agent session turn cancelled');
|
|
76
|
+
throw await withPartialHandoff(entry.runtime, sessionId, stop, messageStart);
|
|
77
|
+
}
|
|
78
|
+
// Only a completed turn has a result; a rejected submission (session
|
|
79
|
+
// closing/terminal, steering persist failure) or any unknown status is a
|
|
80
|
+
// failure, never an empty completion.
|
|
81
|
+
if (detail?.status !== 'done' && detail?.status !== 'completed') {
|
|
82
|
+
const status = String(detail?.status || 'unknown');
|
|
83
|
+
const reason = String(detail?.reason || detail?.error || '').trim();
|
|
84
|
+
throw new Error(`agent turn ${status}${reason ? `: ${reason}` : ''}`);
|
|
70
85
|
}
|
|
71
86
|
const result = detail?.result || { content: '' };
|
|
72
87
|
descriptor.status = 'idle';
|
|
@@ -81,6 +96,7 @@ export function createAgentTurns({
|
|
|
81
96
|
descriptor.stage = descriptor.status;
|
|
82
97
|
throw error;
|
|
83
98
|
} finally {
|
|
99
|
+
takeCancelTranscript(sessionId);
|
|
84
100
|
descriptor.updatedAt = Date.now();
|
|
85
101
|
retainUnwatched(entry, 'agent child idle');
|
|
86
102
|
}
|
|
@@ -37,6 +37,16 @@ export function createAgentTree({
|
|
|
37
37
|
watchdogStops.delete(sessionId);
|
|
38
38
|
return error;
|
|
39
39
|
};
|
|
40
|
+
// Transcripts of running turns captured by a cancel just before it closes
|
|
41
|
+
// (and clears) the session, so the cancelled turn still reports the output
|
|
42
|
+
// it produced.
|
|
43
|
+
const cancelTranscripts = new Map();
|
|
44
|
+
const rememberCancelTranscript = (sessionId, messages) => cancelTranscripts.set(sessionId, messages);
|
|
45
|
+
const takeCancelTranscript = (sessionId) => {
|
|
46
|
+
const messages = cancelTranscripts.get(sessionId) || null;
|
|
47
|
+
cancelTranscripts.delete(sessionId);
|
|
48
|
+
return messages;
|
|
49
|
+
};
|
|
40
50
|
function getSessionProgressSnapshot(sessionId) {
|
|
41
51
|
const runtime = sessionOwner(sessionId)?.runtime;
|
|
42
52
|
if (typeof runtime?.getTurnLiveness !== 'function') return null;
|
|
@@ -58,6 +68,7 @@ export function createAgentTree({
|
|
|
58
68
|
retainUnwatched,
|
|
59
69
|
createSession,
|
|
60
70
|
takeWatchdogStop,
|
|
71
|
+
takeCancelTranscript,
|
|
61
72
|
});
|
|
62
73
|
const { cancelAgentTree, cancelAgentDescendants } = createAgentCancellation({
|
|
63
74
|
registry,
|
|
@@ -65,6 +76,7 @@ export function createAgentTree({
|
|
|
65
76
|
sessionOwner,
|
|
66
77
|
entryForSession,
|
|
67
78
|
log,
|
|
79
|
+
rememberCancelTranscript,
|
|
68
80
|
});
|
|
69
81
|
|
|
70
82
|
/** The descriptor as callers see it: status from the live runtime when one
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
// again, and the session's own lane runs the retry under the entry's
|
|
9
9
|
// generation. Overdue entries run as soon as the scheduler starts.
|
|
10
10
|
|
|
11
|
+
import { isAgentOnlySession } from '../../runtime/shared/quota-resume-store.mjs';
|
|
12
|
+
|
|
11
13
|
/** A still-exhausted reading is re-taken once, this much later. */
|
|
12
14
|
export const QUOTA_RESUME_RETRY_MS = 5 * 60_000;
|
|
13
15
|
/** A busy session is re-checked this much later, without spending an attempt. */
|
|
@@ -30,6 +32,12 @@ async function resumeDueEntry(entry, { store, materializeSession, readQuotaUsabl
|
|
|
30
32
|
return 'unavailable';
|
|
31
33
|
}
|
|
32
34
|
if (!isCurrent(store, entry)) return 'cancelled';
|
|
35
|
+
// A delegated agent child never owns a resume (the Lead decides retries), so
|
|
36
|
+
// an entry persisted for one never fires, whatever wrote it.
|
|
37
|
+
if (isAgentOnlySession({ id: sessionId, ...runtime?.session })) {
|
|
38
|
+
store.cancelQuotaResumeEntry(sessionId, { generation });
|
|
39
|
+
return 'agent-session';
|
|
40
|
+
}
|
|
33
41
|
let usable = false;
|
|
34
42
|
try {
|
|
35
43
|
usable = await readQuotaUsable(entry.provider, entry.model);
|
|
@@ -28,6 +28,12 @@ export function createBusyTracker({ sessions, currentSessionId }) {
|
|
|
28
28
|
// reaches a terminal state and its completion can be delivered back into
|
|
29
29
|
// this session.
|
|
30
30
|
if (sessionId && hasActiveBackgroundTasks({ callerSessionId: sessionId })) return true;
|
|
31
|
+
return sessionTurnBusy(entry);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// A turn is running (provider stream / tool call / queued input). Background
|
|
35
|
+
// tasks are deliberately not counted: they run outside the heap-churning turn.
|
|
36
|
+
function sessionTurnBusy(entry) {
|
|
31
37
|
if (typeof entry?.busy === 'boolean') return entry.busy;
|
|
32
38
|
try {
|
|
33
39
|
return updateEntryBusy(entry, entry.runtime.getState?.() || {});
|
|
@@ -46,5 +52,13 @@ export function createBusyTracker({ sessions, currentSessionId }) {
|
|
|
46
52
|
return count;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
function liveTurnBusyCount() {
|
|
56
|
+
let count = 0;
|
|
57
|
+
for (const entry of sessions) {
|
|
58
|
+
if (sessionTurnBusy(entry)) count += 1;
|
|
59
|
+
}
|
|
60
|
+
return count;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { stateBusy, updateEntryBusy, sessionBusy, liveBusyCount, liveTurnBusyCount };
|
|
50
64
|
}
|
|
@@ -29,6 +29,7 @@ export function createSessionServiceApi({
|
|
|
29
29
|
methods,
|
|
30
30
|
getSize,
|
|
31
31
|
getBusyCount,
|
|
32
|
+
getTurnBusyCount,
|
|
32
33
|
getStatus,
|
|
33
34
|
getExternalClientCount,
|
|
34
35
|
}) {
|
|
@@ -46,6 +47,10 @@ export function createSessionServiceApi({
|
|
|
46
47
|
get busyCount() {
|
|
47
48
|
return getBusyCount();
|
|
48
49
|
},
|
|
50
|
+
/** Sessions with a running turn; background-task-only sessions excluded. */
|
|
51
|
+
get turnBusyCount() {
|
|
52
|
+
return getTurnBusyCount();
|
|
53
|
+
},
|
|
49
54
|
get status() {
|
|
50
55
|
return getStatus();
|
|
51
56
|
},
|
|
@@ -296,6 +296,7 @@ export function createSessionService({
|
|
|
296
296
|
},
|
|
297
297
|
getSize: () => sessions.size,
|
|
298
298
|
getBusyCount: retention.liveBusyCount,
|
|
299
|
+
getTurnBusyCount: retention.liveTurnBusyCount,
|
|
299
300
|
getStatus,
|
|
300
301
|
getExternalClientCount: () => desktopServices.externalClientCount,
|
|
301
302
|
});
|
package/src/tui/app/app-view.jsx
CHANGED
|
@@ -235,6 +235,7 @@ export function renderAppView(ctx) {
|
|
|
235
235
|
model={state.model}
|
|
236
236
|
effort={state.effort}
|
|
237
237
|
fast={state.fast}
|
|
238
|
+
ultra={state.fast === true && state.modelParameters?.serviceTier === 'ultrafast'}
|
|
238
239
|
cwd={state.cwd}
|
|
239
240
|
stats={statuslineStats}
|
|
240
241
|
contextWindow={state.contextWindow}
|
|
@@ -88,6 +88,8 @@ export function createAppPickers({
|
|
|
88
88
|
const {
|
|
89
89
|
openMcpServersPicker,
|
|
90
90
|
openMcpPicker,
|
|
91
|
+
loginMcpServer,
|
|
92
|
+
logoutMcpServer,
|
|
91
93
|
openProjectSkillsPicker,
|
|
92
94
|
openSkillsPicker,
|
|
93
95
|
openSkillDetailPicker,
|
|
@@ -219,6 +221,8 @@ export function createAppPickers({
|
|
|
219
221
|
enterProject: (...a) => enterProject(...a),
|
|
220
222
|
openProjectPicker: (...a) => openProjectPicker(...a),
|
|
221
223
|
openMcpPicker: (...a) => openMcpPicker(...a),
|
|
224
|
+
loginMcpServer,
|
|
225
|
+
logoutMcpServer,
|
|
222
226
|
openSkillsPicker: (...a) => openSkillsPicker(...a),
|
|
223
227
|
openPluginsPicker: (...a) => openPluginsPicker(...a),
|
|
224
228
|
openProviderSetupPicker: (...a) => openProviderSetupPicker(...a),
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// extension-pickers/mcp-login.mjs
|
|
2
|
+
// `/mcp login <name>` and `/mcp logout <name>`. The sign-in URL is always
|
|
3
|
+
// printed (and kept in the live prompt); the runtime may open the browser. The
|
|
4
|
+
// flow is polled while a paste prompt (the shared 'oauth-code' provider prompt)
|
|
5
|
+
// accepts the final redirect address. Pasted text is never echoed.
|
|
6
|
+
const POLL_MS = 1500;
|
|
7
|
+
|
|
8
|
+
export function createMcpLogin({ store, setProviderPrompt, pollMs = POLL_MS }) {
|
|
9
|
+
let active = null;
|
|
10
|
+
|
|
11
|
+
const stop = (flow) => {
|
|
12
|
+
flow.finished = true;
|
|
13
|
+
clearTimeout(flow.timer);
|
|
14
|
+
if (active === flow) active = null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const closePrompt = (flow) => setProviderPrompt((prompt) => (prompt === flow.prompt ? null : prompt));
|
|
18
|
+
|
|
19
|
+
const showPrompt = (flow) => {
|
|
20
|
+
if (flow.finished) return;
|
|
21
|
+
const prompt = {
|
|
22
|
+
kind: 'oauth-code',
|
|
23
|
+
providerName: `MCP ${flow.name}`,
|
|
24
|
+
// The shared prompt pushes the one completion notice; no provider picker follows.
|
|
25
|
+
successReturn: () => {},
|
|
26
|
+
label: `MCP sign-in · ${flow.name}`,
|
|
27
|
+
hint: 'If the browser page fails to load, paste its address here.',
|
|
28
|
+
detail: flow.url,
|
|
29
|
+
login: {
|
|
30
|
+
completeCode: async (text) => {
|
|
31
|
+
const result = await store.completeMcpOAuthLogin(flow.id, text);
|
|
32
|
+
if (result?.state !== 'complete') throw new Error(result?.error || 'sign-in did not complete');
|
|
33
|
+
stop(flow);
|
|
34
|
+
},
|
|
35
|
+
cancel: () => {
|
|
36
|
+
if (flow.finished) return;
|
|
37
|
+
stop(flow);
|
|
38
|
+
void Promise.resolve(store.cancelMcpOAuthLogin(flow.id)).catch(() => {});
|
|
39
|
+
store.pushNotice(`MCP sign-in cancelled for ${flow.name}`, 'info');
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
failureReturn: () => showPrompt(flow),
|
|
43
|
+
};
|
|
44
|
+
flow.prompt = prompt;
|
|
45
|
+
setProviderPrompt(prompt);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const poll = (flow) => {
|
|
49
|
+
flow.timer = setTimeout(async () => {
|
|
50
|
+
if (flow.finished) return;
|
|
51
|
+
let status;
|
|
52
|
+
try {
|
|
53
|
+
status = await store.getMcpOAuthLoginStatus(flow.id);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
if (flow.finished) return;
|
|
56
|
+
stop(flow);
|
|
57
|
+
closePrompt(flow);
|
|
58
|
+
store.pushNotice(`MCP sign-in failed for ${flow.name}: ${e?.message || e}`, 'error');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (flow.finished) return;
|
|
62
|
+
if (status?.state === 'pending') {
|
|
63
|
+
poll(flow);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
stop(flow);
|
|
67
|
+
closePrompt(flow);
|
|
68
|
+
if (status?.state === 'complete') store.pushNotice(`MCP sign-in complete for ${flow.name}`, 'info');
|
|
69
|
+
else if (status?.state === 'cancelled') store.pushNotice(`MCP sign-in cancelled for ${flow.name}`, 'info');
|
|
70
|
+
else store.pushNotice(`MCP sign-in failed for ${flow.name}${status?.error ? `: ${status.error}` : ''}`, 'error');
|
|
71
|
+
}, pollMs);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const loginMcpServer = async (name) => {
|
|
75
|
+
if (active) {
|
|
76
|
+
const previous = active;
|
|
77
|
+
stop(previous);
|
|
78
|
+
closePrompt(previous);
|
|
79
|
+
void Promise.resolve(store.cancelMcpOAuthLogin(previous.id)).catch(() => {});
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const started = await store.beginMcpOAuthLogin(name, { openBrowser: true });
|
|
83
|
+
if (started?.state === 'complete') {
|
|
84
|
+
store.pushNotice(`MCP sign-in complete for ${name}`, 'info');
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const flow = { id: started.flowId, name, url: started.url || '', finished: false, timer: null, prompt: null };
|
|
88
|
+
active = flow;
|
|
89
|
+
if (flow.url) store.pushNotice(`Sign in to ${name}: ${flow.url}`, 'info');
|
|
90
|
+
showPrompt(flow);
|
|
91
|
+
poll(flow);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
store.pushNotice(`MCP sign-in failed for ${name}: ${e?.message || e}`, 'error');
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const logoutMcpServer = async (name) => {
|
|
98
|
+
try {
|
|
99
|
+
await store.signOutMcpServer(name);
|
|
100
|
+
store.pushNotice(`MCP signed out of ${name}`, 'info');
|
|
101
|
+
} catch (e) {
|
|
102
|
+
store.pushNotice(`MCP sign-out failed for ${name}: ${e?.message || e}`, 'error');
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return { loginMcpServer, logoutMcpServer };
|
|
107
|
+
}
|
|
@@ -8,8 +8,9 @@ const serverDescription = (server, pending, optimistic) => {
|
|
|
8
8
|
const transport = server.transport || 'unknown';
|
|
9
9
|
if (pending) return `${optimistic.enabled ? 'enabling' : 'disabling'}… · ${transport}`;
|
|
10
10
|
const error = server.error ? ` · ${server.error}` : '';
|
|
11
|
+
const signIn = server.authRequired ? ` · sign-in required: /mcp login ${server.name}` : '';
|
|
11
12
|
return withScope(
|
|
12
|
-
`${server.source || 'config'} · ${server.status || 'unknown'} · ${transport} · ${server.toolCount || 0} tools${error}`,
|
|
13
|
+
`${server.source || 'config'} · ${server.status || 'unknown'} · ${transport} · ${server.toolCount || 0} tools${error}${signIn}`,
|
|
13
14
|
server
|
|
14
15
|
);
|
|
15
16
|
};
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* store state, so they can't be pure. openMcpPicker is exposed for the
|
|
8
8
|
* plugin-detail enable-mcp path (also aliased on the App body).
|
|
9
9
|
*/
|
|
10
|
+
import { createMcpLogin } from './extension-pickers/mcp-login.mjs';
|
|
10
11
|
import { createMcpServersPicker } from './extension-pickers/mcp-servers-picker.mjs';
|
|
11
12
|
import { createPluginsPickers } from './extension-pickers/plugins-pickers.mjs';
|
|
12
13
|
import { createSkillsPickers } from './extension-pickers/skills-pickers.mjs';
|
|
@@ -43,5 +44,5 @@ export function createExtensionPickers({
|
|
|
43
44
|
setSettingsPrompt,
|
|
44
45
|
openMcpPicker: mcp.openMcpPicker,
|
|
45
46
|
});
|
|
46
|
-
return { ...mcp, ...skills, ...plugins };
|
|
47
|
+
return { ...mcp, ...createMcpLogin({ store, setProviderPrompt }), ...skills, ...plugins };
|
|
47
48
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* displayModelName formatter.
|
|
7
7
|
*/
|
|
8
8
|
import { theme } from '../theme.mjs';
|
|
9
|
-
import { displayModelName } from '../../runtime/shared/model-display.mjs';
|
|
9
|
+
import { displayModelName, effortDisplayLabel } from '../../runtime/shared/model-display.mjs';
|
|
10
10
|
import { isRollingModelAlias } from '../../runtime/shared/model-alias.mjs';
|
|
11
11
|
import { DAYBREAK_PROVIDER, daybreakBlueOn } from '../../runtime/shared/llm/openai-daybreak.mjs';
|
|
12
12
|
|
|
@@ -248,18 +248,7 @@ const PROVIDER_DISPLAY_RANKS = {
|
|
|
248
248
|
|
|
249
249
|
export const providerDisplayRank = (provider) => PROVIDER_DISPLAY_RANKS[String(provider || '').toLowerCase()] ?? 900;
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
String(value || '')
|
|
253
|
-
.split(/([\s_-]+)/)
|
|
254
|
-
.map((part) => (/^[\s_-]+$/.test(part) ? part : `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`))
|
|
255
|
-
.join('');
|
|
256
|
-
|
|
257
|
-
export const effortDisplayLabel = (value) => {
|
|
258
|
-
const text = String(value || '').trim();
|
|
259
|
-
if (!text) return '';
|
|
260
|
-
if (text.toLowerCase() === 'xhigh') return 'XHigh';
|
|
261
|
-
return titleCaseOption(text);
|
|
262
|
-
};
|
|
251
|
+
export { effortDisplayLabel };
|
|
263
252
|
|
|
264
253
|
export const fastDisplayLabel = (enabled = true) => `Fast ${enabled ? 'On' : 'Off'}`;
|
|
265
254
|
|
|
@@ -330,9 +319,15 @@ export const buildProviderModelItems = (models, provider, currentRoute = null) =
|
|
|
330
319
|
}));
|
|
331
320
|
};
|
|
332
321
|
|
|
333
|
-
//
|
|
322
|
+
// The configured speed tier: Ultrafast when the fast route asks for it.
|
|
323
|
+
const routeSpeedLabel = (route) => {
|
|
324
|
+
if (!route?.fast) return '';
|
|
325
|
+
return route.modelParameters?.serviceTier === 'ultrafast' ? 'Ultrafast' : 'Fast';
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// Model · Effort · Fast/Ultrafast, dropping the empty parts.
|
|
334
329
|
const routeModelDetails = (route) =>
|
|
335
|
-
[routeModelDisplayName(route), route.effort ? effortDisplayLabel(route.effort) : '', route
|
|
330
|
+
[routeModelDisplayName(route), route.effort ? effortDisplayLabel(route.effort) : '', routeSpeedLabel(route)]
|
|
336
331
|
.filter(Boolean)
|
|
337
332
|
.join(' · ');
|
|
338
333
|
|
|
@@ -348,7 +343,7 @@ export const agentModelProfile = (route) => (route?.model ? routeModelDetails(ro
|
|
|
348
343
|
export const agentModelParts = (route) => [
|
|
349
344
|
{ text: route?.model ? routeModelDisplayName(route) : '', width: 17 },
|
|
350
345
|
{ text: route?.effort ? effortDisplayLabel(route.effort) : '', width: 6 },
|
|
351
|
-
{ text: route
|
|
346
|
+
{ text: routeSpeedLabel(route), width: 9 },
|
|
352
347
|
];
|
|
353
348
|
|
|
354
349
|
export const routeFromModel = (model, effort = null) => ({
|
|
@@ -50,8 +50,13 @@ export const SLASH_COMMANDS = [
|
|
|
50
50
|
{ name: 'theme', usage: '/theme', params: '[id]', description: 'Change the TUI color theme' },
|
|
51
51
|
{ name: 'agents', usage: '/agents', params: '[refresh]', description: 'Show available workflow agents' },
|
|
52
52
|
{ name: 'effort', usage: '/effort', params: '[level]', description: 'Set reasoning effort for the current model' },
|
|
53
|
-
{
|
|
54
|
-
|
|
53
|
+
{
|
|
54
|
+
name: 'fast',
|
|
55
|
+
usage: '/fast',
|
|
56
|
+
params: '[on|off|ultra]',
|
|
57
|
+
description: 'Set Fast or Ultrafast mode for the current model',
|
|
58
|
+
},
|
|
59
|
+
{ name: 'mcp', usage: '/mcp', params: '[login|logout <name>]', description: 'Manage MCP servers and tools' },
|
|
55
60
|
{ name: 'skills', usage: '/skills', description: 'Choose a skill for the next request' },
|
|
56
61
|
{ name: 'memory', usage: '/memory', params: '[status|core ...]', description: 'List and edit core memories' },
|
|
57
62
|
{ name: 'plugins', usage: '/plugins', description: 'Manage local plugin integrations' },
|
|
@@ -14,6 +14,8 @@ const desktopOnlyNotice = (ctx) => {
|
|
|
14
14
|
return true;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
const openMcpPanel = panelCommand('mcp', 'MCP Servers', (deps) => deps.openMcpPicker());
|
|
18
|
+
|
|
17
19
|
const openSettings = panelCommand('settings', 'Settings', (deps) => deps.openSettingsPicker());
|
|
18
20
|
|
|
19
21
|
export const panelCommands = {
|
|
@@ -26,7 +28,20 @@ export const panelCommands = {
|
|
|
26
28
|
ctx.openSlashPanel('project', 'Projects', () => ctx.deps.openProjectPicker());
|
|
27
29
|
return true;
|
|
28
30
|
},
|
|
29
|
-
mcp
|
|
31
|
+
mcp(ctx, arg) {
|
|
32
|
+
const [sub, ...rest] = arg.trim().split(/\s+/);
|
|
33
|
+
const action = sub?.toLowerCase();
|
|
34
|
+
if (action === 'login' || action === 'logout') {
|
|
35
|
+
const name = rest.join(' ');
|
|
36
|
+
if (!name) {
|
|
37
|
+
ctx.store.pushNotice(`usage: /mcp ${action} <name>`, 'warn');
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
void (action === 'login' ? ctx.deps.loginMcpServer(name) : ctx.deps.logoutMcpServer(name));
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return openMcpPanel(ctx);
|
|
44
|
+
},
|
|
30
45
|
skills: panelCommand('skills', 'Skills', (deps) => deps.openSkillsPicker()),
|
|
31
46
|
plugins: panelCommand('plugins', 'Plugins', (deps) => deps.openPluginsPicker()),
|
|
32
47
|
providers: panelCommand('providers', 'Providers', (deps) => deps.openProviderSetupPicker()),
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { rgbSgr } from '../../../runtime/shared/statusline/ansi.mjs';
|
|
6
6
|
import { measuredContextUsage, contextMeasurementLabel } from '../../../runtime/shared/context-measurement.mjs';
|
|
7
|
-
import { displayModelName, shortenModelName } from '../../../runtime/shared/model-display.mjs';
|
|
7
|
+
import { displayModelName, effortDisplayLabel, shortenModelName } from '../../../runtime/shared/model-display.mjs';
|
|
8
8
|
import { theme } from '../../theme.mjs';
|
|
9
9
|
import { normalizeStatuslineAnsi } from '../../statusline-ansi-bridge.mjs';
|
|
10
10
|
// Cache-only synchronous read (never touches fs on the render path), so the
|
|
@@ -160,6 +160,7 @@ export function localBootStatusLine(args = {}) {
|
|
|
160
160
|
model = '',
|
|
161
161
|
effort = '',
|
|
162
162
|
fast = false,
|
|
163
|
+
ultra = false,
|
|
163
164
|
stats = null,
|
|
164
165
|
contextWindow = 0,
|
|
165
166
|
displayContextWindow = 0,
|
|
@@ -168,7 +169,9 @@ export function localBootStatusLine(args = {}) {
|
|
|
168
169
|
const raw = String(model || '').trim();
|
|
169
170
|
const { STATUS, SUBTLE } = statusColors();
|
|
170
171
|
const display = shortenModelName(displayModelName(raw, provider, ''), terminalColumns());
|
|
171
|
-
|
|
172
|
+
let speedLabel = '';
|
|
173
|
+
if (fast === true) speedLabel = ultra === true ? 'Ultrafast' : 'Fast';
|
|
174
|
+
const flags = [effort ? effortDisplayLabel(effort) : '', speedLabel].filter(Boolean);
|
|
172
175
|
const modelBits = [display, ...flags].join(` ${SUBTLE}·${RESET} `);
|
|
173
176
|
const ctxPct = measuredContextUsage({
|
|
174
177
|
stats,
|
|
@@ -29,6 +29,7 @@ export function useStatusLineText({
|
|
|
29
29
|
model,
|
|
30
30
|
effort,
|
|
31
31
|
fast,
|
|
32
|
+
ultra,
|
|
32
33
|
cwd,
|
|
33
34
|
stats,
|
|
34
35
|
contextWindow,
|
|
@@ -52,6 +53,7 @@ export function useStatusLineText({
|
|
|
52
53
|
model,
|
|
53
54
|
effort,
|
|
54
55
|
fast,
|
|
56
|
+
ultra,
|
|
55
57
|
stats,
|
|
56
58
|
contextWindow,
|
|
57
59
|
displayContextWindow,
|
|
@@ -83,6 +85,7 @@ export function useStatusLineText({
|
|
|
83
85
|
model,
|
|
84
86
|
effort,
|
|
85
87
|
fast,
|
|
88
|
+
ultra,
|
|
86
89
|
cwd,
|
|
87
90
|
stats,
|
|
88
91
|
contextWindow,
|
|
@@ -180,7 +183,8 @@ export function useStatusLineText({
|
|
|
180
183
|
prevImmediate.provider !== args.provider ||
|
|
181
184
|
prevImmediate.model !== args.model ||
|
|
182
185
|
prevImmediate.effort !== args.effort ||
|
|
183
|
-
prevImmediate.fast !== args.fast
|
|
186
|
+
prevImmediate.fast !== args.fast ||
|
|
187
|
+
prevImmediate.ultra !== args.ultra;
|
|
184
188
|
// A theme switch must re-tone the footer immediately: the stored `line`
|
|
185
189
|
// holds already-normalized ANSI with the OLD palette, so re-running
|
|
186
190
|
// normalizeStatusLine on it is a no-op. Force a fresh local rebuild (new
|
|
@@ -218,6 +222,7 @@ export function useStatusLineText({
|
|
|
218
222
|
model: args.model,
|
|
219
223
|
effort: args.effort,
|
|
220
224
|
fast: args.fast,
|
|
225
|
+
ultra: args.ultra,
|
|
221
226
|
contextWindow: args.contextWindow,
|
|
222
227
|
displayContextWindow: args.displayContextWindow,
|
|
223
228
|
rawContextWindow: args.rawContextWindow,
|
|
@@ -299,6 +304,7 @@ export function useStatusLineText({
|
|
|
299
304
|
model,
|
|
300
305
|
effort,
|
|
301
306
|
fast,
|
|
307
|
+
ultra,
|
|
302
308
|
cwd,
|
|
303
309
|
statsSignature,
|
|
304
310
|
contextWindow,
|