synabun 2.0.5 → 2.0.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/CHANGELOG.md +40 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +13 -0
- package/docs/whatsapp.md +151 -16
- package/hooks/claude-code/shared.mjs +6 -3
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/packaged-runtime.js +138 -11
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +10 -7
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +34 -13
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- package/mcp-server/dist/tui.js +2 -1
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +33 -14
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tui.ts +2 -1
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +627 -57
- package/neural-interface/i18n/pt-BR.json +626 -56
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/assistant-api.js +255 -52
- package/neural-interface/lib/assistant-brains/claude.js +15 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-config.js +62 -14
- package/neural-interface/lib/assistant-dispatch.js +432 -125
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-persona.js +68 -11
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +307 -47
- package/neural-interface/lib/assistant-user-writer.js +4 -2
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +90 -3
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +3 -3
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -7
- package/neural-interface/lib/codex-account-auth.js +178 -0
- package/neural-interface/lib/codex-accounts.js +489 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +120 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/git-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-read.js +34 -2
- package/neural-interface/lib/install-details.js +145 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/native-loop-providers.js +16 -3
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +95 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/session-title-generator.js +4 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +2 -2
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/offline.html +16 -1
- package/neural-interface/public/onboarding.html +96 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +539 -0
- package/neural-interface/public/shared/account-usage.js +19 -0
- package/neural-interface/public/shared/account-view.js +335 -0
- package/neural-interface/public/shared/agent-runtime-options.js +28 -8
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +32 -2
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +124 -25
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
- package/neural-interface/public/shared/assistant/asst-menu.js +15 -3
- package/neural-interface/public/shared/assistant/asst-panel.js +48 -14
- package/neural-interface/public/shared/assistant/asst-render.js +38 -7
- package/neural-interface/public/shared/assistant/asst-route.js +72 -14
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +1 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +99 -17
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +48 -107
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +128 -107
- package/neural-interface/public/shared/cdx/cdx-tabs.js +261 -664
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +46 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -0
- package/neural-interface/public/shared/editor/editor-panel.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +1 -1
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/html-shell.js +1 -0
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +32 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +118 -0
- package/neural-interface/public/shared/pwa-install-page.js +3 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -0
- package/neural-interface/public/shared/settings/settings-ia.js +15 -0
- package/neural-interface/public/shared/showcase-privacy.js +489 -0
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +278 -31
- package/neural-interface/public/shared/synabun-mascot.js +48 -2
- package/neural-interface/public/shared/ui-claude-panel.js +212 -213
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-loading.js +30 -6
- package/neural-interface/public/shared/ui-menubar.js +13 -3
- package/neural-interface/public/shared/ui-navbar.js +3 -1
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +55 -3
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +6 -0
- package/neural-interface/public/shared/ui-terminal.js +17 -0
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-update.js +594 -5
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +2 -0
- package/neural-interface/public/sw.js +1 -1
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/server.js +1219 -608
- package/package.json +4 -1
- package/setup.js +9 -8
|
@@ -22,6 +22,8 @@ import { catalogModelRow, fmtModelPrice, hiddenModelFor, ICON_CARET, invalidateM
|
|
|
22
22
|
// The Routes editor's model lists (where they come from, how they stay current): asst-route-models.js.
|
|
23
23
|
import { closeMenu, isMenuOpen, openMenu } from './asst-menu.js';
|
|
24
24
|
import { decorateControlHead } from './asst-control.js';
|
|
25
|
+
import { accountSummary } from '../account-view.js';
|
|
26
|
+
import { accountUsageText, autoAccountLabel } from '../account-usage.js';
|
|
25
27
|
import {
|
|
26
28
|
DEFAULT_ROUTE_MODE,
|
|
27
29
|
fmtConfidence,
|
|
@@ -39,6 +41,18 @@ function obj(v) { return v && typeof v === 'object' && !Array.isArray(v) ? v : n
|
|
|
39
41
|
function arr(v) { return Array.isArray(v) ? v : []; }
|
|
40
42
|
function firstStr(...values) { for (const v of values) { const s = str(v); if (s) return s; } return ''; }
|
|
41
43
|
|
|
44
|
+
export function routeDisplayAccount(target, t = null) {
|
|
45
|
+
if (target?.accountId === 'auto') return autoAccountLabel(t);
|
|
46
|
+
if (target?.account) return [accountSummary(target.account, target.provider === 'codex' ? 'codex' : 'claude'), accountUsageText(target.account, t)].join(' · ');
|
|
47
|
+
return target?.accountId ? tf(t)('assistant.routes.account.removed', '{id} · account removed', { id: target.accountId }) : '';
|
|
48
|
+
}
|
|
49
|
+
function routeAccountError(error, t) {
|
|
50
|
+
if (error?.code === 'ACCOUNT_NO_SUBSCRIPTION') return tf(t)('assistant.accountUsage.noSubscription', 'Auto requires a signed-in subscription account.');
|
|
51
|
+
return error?.code === 'ACCOUNT_NOT_FOUND'
|
|
52
|
+
? tf(t)('assistant.routes.account.removedHint', 'Account removed. Save another account choice before running this route.')
|
|
53
|
+
: tf(t)('assistant.routes.account.signedOut', 'Signed out. Sign in from the provider account menu before running this route.');
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
/** Local translator: hooks.t when it resolves, else the fallback with {param} interpolation. */
|
|
43
57
|
function tf(t) {
|
|
44
58
|
return (key, fallback, params) => {
|
|
@@ -93,6 +107,7 @@ function normalizeRouteOption(o) {
|
|
|
93
107
|
provider: normalizeProvider(src.provider, str(src.provider) || null),
|
|
94
108
|
model: str(src.model),
|
|
95
109
|
effort: str(src.effort) || null,
|
|
110
|
+
...(Object.hasOwn(src, 'accountId') ? { accountId: src.accountId, account: obj(src.account), accountError: obj(src.accountError) } : {}),
|
|
96
111
|
label: str(src.label),
|
|
97
112
|
badge: str(src.badge) || null,
|
|
98
113
|
tier: str(src.tier) || null,
|
|
@@ -101,6 +116,7 @@ function normalizeRouteOption(o) {
|
|
|
101
116
|
contextWindow: num(src.contextWindow),
|
|
102
117
|
reason: str(src.reason),
|
|
103
118
|
warning: str(src.warning) || null,
|
|
119
|
+
budget: str(src.budget) || null,
|
|
104
120
|
disabled: src.disabled === true,
|
|
105
121
|
disabledReason: str(src.disabledReason) || null,
|
|
106
122
|
};
|
|
@@ -174,7 +190,7 @@ export function sameRouteTarget(a, b) {
|
|
|
174
190
|
return (str(a.kind) || 'direct') === (str(b.kind) || 'direct')
|
|
175
191
|
&& str(a.provider) === str(b.provider)
|
|
176
192
|
&& modelKey(a.model) === modelKey(b.model)
|
|
177
|
-
&& (str(a.effort) || '') === (str(b.effort) || '');
|
|
193
|
+
&& (str(a.effort) || '') === (str(b.effort) || '') && str(a.accountId) === str(b.accountId);
|
|
178
194
|
}
|
|
179
195
|
|
|
180
196
|
const BADGE_RANK = { suggested: 0, remembered: 1, current: 2, capable: 3, cheaper: 4, stronger: 5 };
|
|
@@ -269,6 +285,7 @@ export function routeOptions(n, { brainLabel = '', t = null } = {}) {
|
|
|
269
285
|
// "here" is the brain's own model: prefer its display label over a raw id.
|
|
270
286
|
title: isHere ? tr('assistant.routes.doHere', 'Do it here with {model}', { model: str(o.label) || hereModel }) : (label || o.id),
|
|
271
287
|
badgeText: badgeText(o, n, tr),
|
|
288
|
+
disabledReason: o.budget === 'share_used_up' ? routeBudgetReason({ provider: o.provider }, t) : o.disabledReason,
|
|
272
289
|
selected: o.id === defaultId,
|
|
273
290
|
};
|
|
274
291
|
});
|
|
@@ -350,6 +367,16 @@ export function routeCatalogRow(pref, catalog) {
|
|
|
350
367
|
return pref.model ? catalogModelRow(pref.provider, pref.model, catalog) || aliasRow(pref.provider, pref.model, rows) : rows.find(m => m.isDefault) || null;
|
|
351
368
|
}
|
|
352
369
|
|
|
370
|
+
/** Budget messages are UI copy; never show the server's English disabledReason. */
|
|
371
|
+
export function routeBudgetReason({ provider, dry } = {}, t) {
|
|
372
|
+
const tr = tf(t), names = { 'claude-code': 'Claude Code', codex: 'Codex', opencode: 'OpenCode' };
|
|
373
|
+
if (provider) return tr('assistant.routes.budgetProvider', "Budget: the {provider} share of this session's budget is used up.", { provider: names[provider] || provider });
|
|
374
|
+
const list = [...new Set(arr(dry))].map(id => names[id] || id);
|
|
375
|
+
if (!list.length) return tr('assistant.routes.budgetSpent', "Budget: this session's budget is spent.");
|
|
376
|
+
return tr(list.length === 1 ? 'assistant.routes.budgetDryOne' : 'assistant.routes.budgetDryMany',
|
|
377
|
+
list.length === 1 ? 'Budget: the {list} share is used up.' : 'Budget: the {list} shares are used up.', { list: list.join(', ') });
|
|
378
|
+
}
|
|
379
|
+
|
|
353
380
|
/** Text parts of a route line (pure). */
|
|
354
381
|
export function routeLineParts(route, phase = 'decided', { brainLabel = '', t = null } = {}) {
|
|
355
382
|
const tr = tf(t);
|
|
@@ -366,10 +393,12 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
366
393
|
else if (phase === 'declined' && str(r.reasonCode) === 'no_capable_model') {
|
|
367
394
|
state = str(r.needs) === 'vision' ? tr('assistant.routes.reason.noSeeingModel', 'no model can see images') : tr('assistant.routes.reason.noCapableModel', 'no model can make this');
|
|
368
395
|
}
|
|
396
|
+
else if (str(r.reasonCode) === 'budget_exhausted') state = routeBudgetReason({ dry: r.dry }, t);
|
|
369
397
|
else if (phase === 'declined') state = tr('assistant.routes.state.declined', 'declined');
|
|
370
398
|
else if (phase === 'cancelled') state = tr('assistant.routes.state.cancelled', 'cancelled');
|
|
371
399
|
let decided = '';
|
|
372
|
-
switch (str(r.decidedBy)) {
|
|
400
|
+
switch (r.source === 'budget' ? 'budget' : str(r.decidedBy)) {
|
|
401
|
+
case 'budget': decided = tr('assistant.routes.budgetDecision', 'budget re-route'); break;
|
|
373
402
|
case 'brain': decided = tr('assistant.routes.decidedBy', 'decided by {model}', { model: brainLabel || tr('assistant.routes.theBrainLower', 'the assistant') }); break;
|
|
374
403
|
case 'user': decided = tr('assistant.routes.youChose', 'you chose'); break;
|
|
375
404
|
case 'remembered': decided = tr('assistant.routes.remembered', 'your saved choice'); break;
|
|
@@ -377,6 +406,8 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
377
406
|
default: decided = r.remembered ? tr('assistant.routes.remembered', 'your saved choice') : '';
|
|
378
407
|
}
|
|
379
408
|
const meta = [];
|
|
409
|
+
const accountLabel = routeDisplayAccount(target, t);
|
|
410
|
+
if (accountLabel) meta.push(accountLabel);
|
|
380
411
|
if (effort && effort !== 'off') meta.push(effort);
|
|
381
412
|
if (confidence) meta.push(confidence);
|
|
382
413
|
return {
|
|
@@ -390,7 +421,7 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
390
421
|
muted: phase === 'expired' || phase === 'declined' || phase === 'cancelled',
|
|
391
422
|
pending: phase === 'pending' || phase === 'card',
|
|
392
423
|
decidedText: decided,
|
|
393
|
-
reason: str(r.reason),
|
|
424
|
+
reason: target?.accountError ? routeAccountError(target.accountError, t) : str(r.reason),
|
|
394
425
|
alternatives: arr(r.alternatives).map(a => routeTargetLabel(a, { effort: true })).filter(Boolean),
|
|
395
426
|
corrections: arr(r.corrections).map(c => correctionText(c, tr)).filter(Boolean),
|
|
396
427
|
};
|
|
@@ -400,9 +431,15 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
400
431
|
function correctionText(c, tr) {
|
|
401
432
|
if (typeof c === 'string') return c;
|
|
402
433
|
if (!c || typeof c !== 'object') return '';
|
|
434
|
+
if (['account_auto', 'account_limited', 'accounts_all_limited'].includes(c.reason) && c.usage) {
|
|
435
|
+
const key = c.reason === 'account_auto' ? 'autoPicked' : c.reason === 'account_limited' ? 'fallbackPicked' : 'allLimitedPicked';
|
|
436
|
+
const fallbacks = { autoPicked: 'Auto chose {to}: {usage}', fallbackPicked: '{from} out of usage → {to}: {usage}', allLimitedPicked: 'All subscriptions limited; soonest reset → {to}: {usage}' };
|
|
437
|
+
const text = tr(`assistant.accountUsage.${key}`, fallbacks[key], { from: str(c.from), to: str(c.to), usage: accountUsageText({ mode: 'subscription', usage: c.usage }, (key, params) => tr(key, key, params)) });
|
|
438
|
+
return text + (c.workerForAccount ? ' · ' + tr('assistant.accountUsage.directWorkerHint', 'Runs on a worker because this conversation belongs to another account.') : '');
|
|
439
|
+
}
|
|
403
440
|
if (c.reason === 'remembered_route') return tr('assistant.routes.correction.remembered', 'your saved route {to} replaced {from}', { to: str(c.to), from: str(c.from) });
|
|
404
441
|
if (c.reason === 'remembered_disabled') return tr('assistant.routes.correction.rememberedDisabled', 'saved route {from} is disabled, kept the pick', { from: str(c.from) });
|
|
405
|
-
const text = firstStr(c.text, c.summary);
|
|
442
|
+
const text = firstStr(c.text, c.summary, c.message);
|
|
406
443
|
if (text) return text;
|
|
407
444
|
if (c.field && (c.from || c.to)) return `${c.field}: ${str(c.from) || '—'} → ${str(c.to) || 'default'}`;
|
|
408
445
|
return str(c.field);
|
|
@@ -416,6 +453,14 @@ export function routeLineText(route, phase, opts) {
|
|
|
416
453
|
return [`⇄ ${p.taskClass} → ${head}`, ...p.meta].join(' · ');
|
|
417
454
|
}
|
|
418
455
|
|
|
456
|
+
/** Everything the one-line row can cut short (meta, state included): its tooltip. */
|
|
457
|
+
export function routeLineTitle(route, phase, opts) {
|
|
458
|
+
const p = routeLineParts(route, phase, opts);
|
|
459
|
+
const tail = [p.kindLabel, p.targetLabel, ...p.meta, p.stateText].filter(Boolean).join(' · ');
|
|
460
|
+
if (!tail) return `⇄ ${p.taskClass}`;
|
|
461
|
+
return `⇄ ${p.taskClass} ${p.kindLabel || p.targetLabel ? '→' : '·'} ${tail}`;
|
|
462
|
+
}
|
|
463
|
+
|
|
419
464
|
// ═══════════════════════════════════════════
|
|
420
465
|
// DOM
|
|
421
466
|
// ═══════════════════════════════════════════
|
|
@@ -449,8 +494,12 @@ function providerColor(provider) {
|
|
|
449
494
|
let _lineUid = 0;
|
|
450
495
|
|
|
451
496
|
/**
|
|
452
|
-
* The compact route line
|
|
453
|
-
*
|
|
497
|
+
* The compact route line, always one line: the glyph, arrow, kind and mark
|
|
498
|
+
* hold; the rest (model · account · usage, or the state) gives way first,
|
|
499
|
+
* then the class; the title has it all. data-live: "waiting" while it waits
|
|
500
|
+
* for a choice, "working" while a run it dispatched is (setRunning).
|
|
501
|
+
* opts: { phase, t, providerIcon, brainLabel() , onChangeRoutes() }
|
|
502
|
+
* Returns { el, routeId, update(route, phase), setRunning(on) }.
|
|
454
503
|
*/
|
|
455
504
|
export function createRouteLine(route, opts = {}) {
|
|
456
505
|
const tr = tf(opts.t);
|
|
@@ -458,20 +507,19 @@ export function createRouteLine(route, opts = {}) {
|
|
|
458
507
|
const detailId = `asst-route-detail-${++_lineUid}`;
|
|
459
508
|
node.innerHTML = `
|
|
460
509
|
<button type="button" class="asst-route-line-head" aria-expanded="false" aria-controls="${detailId}">
|
|
461
|
-
<span class="asst-route-glyph" aria-hidden="true"
|
|
510
|
+
<span class="asst-route-glyph asst-icon" aria-hidden="true">${ICON_ROUTE}</span>
|
|
462
511
|
<span class="asst-route-class"></span>
|
|
463
512
|
<span class="asst-route-arrow" aria-hidden="true">→</span>
|
|
464
513
|
<span class="asst-route-kind"></span>
|
|
465
514
|
<span class="asst-route-icon asst-icon" aria-hidden="true"></span>
|
|
466
|
-
<span class="asst-route-target"></span>
|
|
467
|
-
<span class="asst-route-meta"></span>
|
|
468
|
-
<span class="asst-route-state"></span>
|
|
515
|
+
<span class="asst-route-rest"><span class="asst-route-target"></span> <span class="asst-route-meta"></span> <span class="asst-route-state"></span></span>
|
|
469
516
|
<span class="asst-route-chev" aria-hidden="true">›</span>
|
|
517
|
+
<span class="asst-route-sweep" aria-hidden="true"></span>
|
|
470
518
|
</button>
|
|
471
519
|
<div class="asst-route-detail" id="${detailId}" hidden></div>`;
|
|
472
520
|
const head = node.querySelector('.asst-route-line-head');
|
|
473
521
|
const detail = node.querySelector('.asst-route-detail');
|
|
474
|
-
let current = { route: obj(route) || {}, phase: opts.phase || 'decided' };
|
|
522
|
+
let current = { route: obj(route) || {}, phase: opts.phase || 'decided', running: false };
|
|
475
523
|
|
|
476
524
|
head.addEventListener('click', () => {
|
|
477
525
|
const open = head.getAttribute('aria-expanded') !== 'true';
|
|
@@ -487,6 +535,8 @@ export function createRouteLine(route, opts = {}) {
|
|
|
487
535
|
node.dataset.phase = current.phase;
|
|
488
536
|
node.classList.toggle('muted', p.muted);
|
|
489
537
|
node.classList.toggle('pending', p.pending);
|
|
538
|
+
const live = p.pending ? 'waiting' : current.running && !p.muted && !p.stateText ? 'working' : '';
|
|
539
|
+
if (live) node.dataset.live = live; else delete node.dataset.live;
|
|
490
540
|
node.querySelector('.asst-route-class').textContent = p.taskClass;
|
|
491
541
|
const arrow = node.querySelector('.asst-route-arrow');
|
|
492
542
|
const kindEl = node.querySelector('.asst-route-kind');
|
|
@@ -494,7 +544,7 @@ export function createRouteLine(route, opts = {}) {
|
|
|
494
544
|
const targetEl = node.querySelector('.asst-route-target');
|
|
495
545
|
const metaEl = node.querySelector('.asst-route-meta');
|
|
496
546
|
const stateEl = node.querySelector('.asst-route-state');
|
|
497
|
-
arrow.hidden = !p.targetLabel && !p.pending;
|
|
547
|
+
arrow.hidden = !p.targetLabel && !p.pending && !p.kindLabel;
|
|
498
548
|
kindEl.textContent = p.kindLabel;
|
|
499
549
|
kindEl.hidden = !p.kindLabel;
|
|
500
550
|
iconEl.innerHTML = p.targetLabel ? providerIconHtml(p.provider, opts) : '';
|
|
@@ -506,7 +556,9 @@ export function createRouteLine(route, opts = {}) {
|
|
|
506
556
|
metaEl.hidden = !p.meta.length;
|
|
507
557
|
stateEl.textContent = p.stateText;
|
|
508
558
|
stateEl.hidden = !p.stateText;
|
|
559
|
+
node.querySelector('.asst-route-rest').hidden = targetEl.hidden && metaEl.hidden && stateEl.hidden;
|
|
509
560
|
head.setAttribute('aria-label', routeLineText(current.route, current.phase, { brainLabel, t: opts.t }));
|
|
561
|
+
head.title = routeLineTitle(current.route, current.phase, { brainLabel, t: opts.t });
|
|
510
562
|
|
|
511
563
|
detail.innerHTML = '';
|
|
512
564
|
const rows = [];
|
|
@@ -535,6 +587,12 @@ export function createRouteLine(route, opts = {}) {
|
|
|
535
587
|
if (phase) current.phase = phase;
|
|
536
588
|
render();
|
|
537
589
|
},
|
|
590
|
+
/** A run this route dispatched is queued or working (the renderer knows from its run cards). */
|
|
591
|
+
setRunning(on) {
|
|
592
|
+
if (current.running === !!on) return;
|
|
593
|
+
current.running = !!on;
|
|
594
|
+
render();
|
|
595
|
+
},
|
|
538
596
|
};
|
|
539
597
|
}
|
|
540
598
|
|
|
@@ -705,7 +763,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
705
763
|
row.setAttribute('aria-disabled', o.disabled ? 'true' : 'false');
|
|
706
764
|
row.tabIndex = -1;
|
|
707
765
|
const kindWord = o.kind && o.kind !== 'direct' ? routeKindLabel(o.kind) : '';
|
|
708
|
-
const sub = [kindWord, o.effort && o.effort !== 'off' ? o.effort : '', o.price ? priceText(o.price) : '', o.vision ? tr('assistant.brain.vision', 'vision') : '', o.reason].filter(Boolean).join(' · ');
|
|
766
|
+
const sub = [kindWord, routeDisplayAccount(o, hooks.t), o.effort && o.effort !== 'off' ? o.effort : '', o.price ? priceText(o.price) : '', o.vision ? tr('assistant.brain.vision', 'vision') : '', o.reason].filter(Boolean).join(' · ');
|
|
709
767
|
row.innerHTML = `
|
|
710
768
|
<span class="asst-route-num" aria-hidden="true">${esc(o.number)}</span>
|
|
711
769
|
<span class="asst-route-opt-icon asst-icon" aria-hidden="true"${o.provider ? ` style="color:${esc(providerColor(o.provider))}"` : ''}>${providerIconHtml(o.provider, hooks)}</span>
|
|
@@ -718,7 +776,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
718
776
|
row.querySelector('.asst-route-opt-title').textContent = o.title;
|
|
719
777
|
if (sub) row.querySelector('.asst-route-opt-sub').textContent = sub;
|
|
720
778
|
const warn = row.querySelector('.asst-route-opt-warn');
|
|
721
|
-
if (warn) warn.textContent = o.disabled && o.disabledReason ? o.disabledReason : o.warning;
|
|
779
|
+
if (warn) warn.textContent = o.accountError ? routeAccountError(o.accountError, hooks.t) : o.disabled && o.disabledReason ? o.disabledReason : o.warning;
|
|
722
780
|
if (o.badgeText) row.querySelector('.asst-route-badge').textContent = o.badgeText;
|
|
723
781
|
row.addEventListener('click', () => { if (!o.disabled) select(o.id, { focus: true }); });
|
|
724
782
|
return row;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Model routes: draft-only editor. All model data comes through asst-route-models.
|
|
2
2
|
// The global default and saved preferences are written together only on Save.
|
|
3
|
-
import { clampEffortId, getEffortLevelsForModel, modelEffortIds } from '../agent-runtime-options.js';
|
|
3
|
+
import { clampEffortId, getEffortLevelsForModel, modelEffortIds, fetchClaudeAccounts, fetchCodexAccounts, getCachedClaudeAccounts, getCachedCodexAccounts, getClaudeWorkerAccountId, getCodexWorkerAccountId } from '../agent-runtime-options.js';
|
|
4
|
+
import { accountSummary, accountName, accountBadge, accountIdentity } from '../account-view.js';
|
|
5
|
+
import { accountUsageText, autoAccountLabel } from '../account-usage.js';
|
|
4
6
|
import { getProviderMeta } from '../provider-icons.js';
|
|
5
7
|
import { getRouting, putRouting } from './asst-api.js';
|
|
6
8
|
import { catalogModelRow, hiddenModelFor, ICON_CARET, openModelMenu } from './asst-brain-picker.js';
|
|
@@ -30,7 +32,33 @@ function translator(t) {
|
|
|
30
32
|
return typeof text === 'string' && text !== key ? text : String(fallback).replace(/\{(\w+)\}/g, (_, k) => params?.[k] ?? `{${k}}`);
|
|
31
33
|
};
|
|
32
34
|
}
|
|
33
|
-
function choiceKey(p) { return p ? JSON.stringify([p.kind || 'direct', p.provider || '', p.model || '', p.effort || '']) : ''; }
|
|
35
|
+
function choiceKey(p) { return p ? JSON.stringify([p.kind || 'direct', p.provider || '', p.model || '', p.effort || '', p.accountId || '']) : ''; }
|
|
36
|
+
|
|
37
|
+
export function routeAccountView(pref, task, brain, accounts, workers = {}, t = null) {
|
|
38
|
+
const tr = translator(t);
|
|
39
|
+
const empty = { label: tr('assistant.routes.editorUnusedValue', '—'), readOnly: true, options: [], hint: '' };
|
|
40
|
+
if (!pref || pref.provider === 'opencode') return empty;
|
|
41
|
+
const direct = task.id === 'chat' || routeExecutionKind(pref, task, brain) === 'direct';
|
|
42
|
+
const provider = task.id === 'chat' ? brain.provider : pref.provider;
|
|
43
|
+
if (!['claude-code', 'codex'].includes(provider)) return empty;
|
|
44
|
+
const rows = accounts[provider] || [];
|
|
45
|
+
const providerView = provider === 'codex' ? 'codex' : 'claude';
|
|
46
|
+
const id = task.id === 'chat' ? brain.accountId || 'default' : pref.accountId || (direct ? brain.accountId || 'default' : null);
|
|
47
|
+
const selected = rows.find(a => a.id === id);
|
|
48
|
+
const missing = id && id !== 'auto' && !selected;
|
|
49
|
+
const removed = tr('assistant.routes.account.removed', '{id} · account removed', { id });
|
|
50
|
+
const warning = missing ? tr('assistant.routes.account.removedHint', 'Account removed. Save another account choice before running this route.') : selected?.signedIn === false ? tr('assistant.routes.account.signedOut', 'Signed out. Sign in from the provider account menu before running this route.') : '';
|
|
51
|
+
const worker = rows.find(a => a.id === (workers[provider] || 'default'));
|
|
52
|
+
const label = id === 'auto' ? autoAccountLabel(t) : selected ? accountSummary(selected, providerView) : missing ? removed : tr('assistant.routes.account.workersDefault', 'Workers default');
|
|
53
|
+
const options = [
|
|
54
|
+
{ id: '', label: direct ? tr('assistant.routes.editorInConversation', 'This conversation') : tr('assistant.routes.account.workersDefault', 'Workers default'), desc: worker ? accountSummary(worker, providerView) : '', selected: !pref.accountId },
|
|
55
|
+
{ id: 'auto', label: autoAccountLabel(t), desc: tr('assistant.accountUsage.autoHint', 'Signed-in subscriptions only. Another account runs on a worker.'), selected: id === 'auto' },
|
|
56
|
+
...rows.map(a => ({ id: a.id, label: accountName(a, providerView), tag: accountBadge(a, providerView).text, desc: a.signedIn === false ? tr('assistant.routes.account.signedOut', 'Signed out. Sign in from the provider account menu before running this route.') : [accountIdentity(a), accountUsageText(a, t)].filter(Boolean).join(' · '), selected: !!pref.accountId && a.id === id })),
|
|
57
|
+
];
|
|
58
|
+
return { label, readOnly: task.id === 'chat', options, warning, hint: warning || (selected ? `${label} · ${accountUsageText(selected, t)}` : label) };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function routeModelAccount(pref, provider) { return pref?.provider === provider ? pref.accountId || null : null; }
|
|
34
62
|
|
|
35
63
|
/** Only actual choices change the draft. Catalog label/by updates are not user edits. */
|
|
36
64
|
export function routeEditorPatch(mode, initialMode, prefs, edits) {
|
|
@@ -307,7 +335,7 @@ export async function openRoutesEditor(hooks = {}) {
|
|
|
307
335
|
const note = el('div', 'asst-routes-catalog-note'); note.setAttribute('role', 'status'); note.append(el('span', '', tr('assistant.routes.editorState.modelsFailed', 'Could not refresh the model list. Showing provider lists where available. Reload models to verify your choices.'))); table.append(note);
|
|
308
336
|
} else if (!rowsOf(state.catalog).length) table.append(el('div', 'asst-routes-catalog-note', tr('assistant.routes.editorState.modelsEmpty', 'No models are available. Check Models or connect a provider, then reload.')));
|
|
309
337
|
const columns = el('div', 'asst-routes-columns'); columns.setAttribute('aria-hidden', 'true');
|
|
310
|
-
for (const key of ['task', 'model', 'effort', 'run', 'reset']) columns.append(el('span', '', tr(`assistant.routes.columns.${key}`, key)));
|
|
338
|
+
for (const key of ['task', 'model', 'effort', 'run', 'account', 'reset']) columns.append(el('span', '', tr(`assistant.routes.columns.${key}`, key)));
|
|
311
339
|
table.append(columns);
|
|
312
340
|
const list = el('div', 'asst-routes-list');
|
|
313
341
|
const taskList = tasks();
|
|
@@ -363,7 +391,7 @@ export async function openRoutesEditor(hooks = {}) {
|
|
|
363
391
|
else if (target?.model) {
|
|
364
392
|
const levels = modelEffortIds(catalogModelRow(target.provider, target.model, state.catalog));
|
|
365
393
|
const effort = !pref?.effort ? null : levels ? clampEffortId(pref.effort, levels) : pref.provider === target.provider ? pref.effort : null;
|
|
366
|
-
setChoice(task.id, { kind: task.dispatchOnly ? 'dispatch' : pref?.kind || task.defaultKind || 'direct', provider: target.provider, model: target.model, effort, label: target.label || target.model }, modelBtn.dataset.focusKey);
|
|
394
|
+
setChoice(task.id, { kind: task.dispatchOnly ? 'dispatch' : pref?.kind || task.defaultKind || 'direct', provider: target.provider, model: target.model, effort, accountId: routeModelAccount(pref, target.provider), label: target.label || target.model }, modelBtn.dataset.focusKey);
|
|
367
395
|
}
|
|
368
396
|
},
|
|
369
397
|
});
|
|
@@ -420,9 +448,20 @@ export async function openRoutesEditor(hooks = {}) {
|
|
|
420
448
|
if (task.dispatchOnly) run.title = tr('assistant.routes.workerOnly', 'Worker only');
|
|
421
449
|
else if (pref && pref.provider !== brain().provider && task.id !== 'chat') run.title = tr('assistant.routes.otherProviderWorker', 'A model from another provider runs on a worker.');
|
|
422
450
|
const runCell = el('div', 'asst-routes-cell asst-routes-run-cell'); runCell.append(el('span', 'asst-routes-cell-label', tr('assistant.routes.columns.run', 'Runs in')), run); node.append(runCell);
|
|
451
|
+
const account = routeAccountView(pref, task, brain(), { 'claude-code': getCachedClaudeAccounts(), codex: getCachedCodexAccounts() }, { 'claude-code': getClaudeWorkerAccountId(), codex: getCodexWorkerAccountId() }, hooks.t);
|
|
452
|
+
const accountCell = el('div', 'asst-routes-cell asst-routes-account-cell');
|
|
453
|
+
const accountBtn = fieldButton('asst-routes-account', task, tr('assistant.routes.columns.account', 'Account'), account.label, () => {
|
|
454
|
+
state.menuFocus = `${task.id}-account`;
|
|
455
|
+
openMenuFor(accountBtn, tr('assistant.routes.columns.account', 'Account'), account.options.map(o => ({ ...o, kind: 'radio', onSelect: () => setChoice(task.id, { ...pref, accountId: o.id || null }, `${task.id}-account`) })));
|
|
456
|
+
});
|
|
457
|
+
accountBtn.dataset.focusKey = `${task.id}-account`; accountBtn.disabled = account.readOnly; accountBtn.title = account.hint;
|
|
458
|
+
if (!account.readOnly) caret(accountBtn);
|
|
459
|
+
if (account.warning) accountBtn.classList.add('asst-dd-warn');
|
|
460
|
+
accountCell.append(el('span', 'asst-routes-cell-label', tr('assistant.routes.columns.account', 'Account')), accountBtn); node.append(accountCell);
|
|
423
461
|
const resetCell = el('div', 'asst-routes-reset-cell');
|
|
424
462
|
if (pref) { const reset = button('asst-iconbtn asst-routes-reset', tr('assistant.routes.reset', 'Reset {taskClass}', { taskClass: task.label }), () => setChoice(task.id, null, `${task.id}-model`)); reset.dataset.focusKey = `${task.id}-reset`; reset.title = reset.getAttribute('aria-label'); reset.innerHTML = ICON_RESET; resetCell.append(reset); }
|
|
425
463
|
node.append(resetCell);
|
|
464
|
+
if (account.warning) { const note = el('div', 'asst-routes-row-warning', account.warning); note.id = `${nameId}-account-warning`; accountBtn.setAttribute('aria-describedby', note.id); node.append(note); }
|
|
426
465
|
if (capabilityNote) node.append(capabilityNote);
|
|
427
466
|
if (issue) { description.hidden = true; node.querySelector('.asst-routes-row-note')?.remove(); const note = el('div', 'asst-routes-row-warning', issueText(issue, task)); note.title = note.textContent; note.id = `${nameId}-issue`; modelBtn.setAttribute('aria-describedby', `${source.id} ${note.id}`); modelBtn.title = `${label} — ${note.textContent}`; node.append(note); }
|
|
428
467
|
if (task.id === 'chat' || task.id === 'vision') {
|
|
@@ -453,7 +492,7 @@ export async function openRoutesEditor(hooks = {}) {
|
|
|
453
492
|
const pending = preserve ? changes() : {};
|
|
454
493
|
state.loading = true; state.error = ''; state.modelsLoading = true; render();
|
|
455
494
|
try {
|
|
456
|
-
const [data, result] = await Promise.all([getRouting(), loadRouteModels().then(catalog => ({ catalog })).catch(() => ({ catalog: state.catalog, error: true }))]);
|
|
495
|
+
const [data, result] = await Promise.all([getRouting(), loadRouteModels().then(catalog => ({ catalog })).catch(() => ({ catalog: state.catalog, error: true })), Promise.allSettled([fetchClaudeAccounts(true), fetchCodexAccounts(true)])]);
|
|
457
496
|
if (!overlay.isConnected) return;
|
|
458
497
|
state.data = data; state.version = data.version ?? null; state.initialMode = normalizeRouteMode(data.routing?.defaultMode, DEFAULT_ROUTE_MODE);
|
|
459
498
|
state.mode = pending.defaultMode || state.initialMode; state.prefs = { ...(data.routing?.preferences || {}) }; state.patch = pending.preferences || {};
|
|
@@ -485,7 +524,7 @@ export async function openRoutesEditor(hooks = {}) {
|
|
|
485
524
|
import { openMenu } from './asst-menu.js';
|
|
486
525
|
function openMenuFor(anchor, title, items) {
|
|
487
526
|
if (currentMenu()) { closeMenu({ restoreFocus: true }); return null; }
|
|
488
|
-
const menu = openMenu(anchor, { title, items, className:
|
|
527
|
+
const menu = openMenu(anchor, { title, items, className: `asst-route-field-menu${anchor.classList.contains('asst-routes-account') ? ' asst-route-account-menu' : ''}`, role: 'menu' });
|
|
489
528
|
const overlay = anchor.closest('.asst-routes-overlay'); if (overlay) adoptMenu(menu, overlay); return menu;
|
|
490
529
|
}
|
|
491
530
|
function adoptMenu(menu, overlay) {
|
|
@@ -15,7 +15,7 @@ export const SLASH_COMMANDS = [
|
|
|
15
15
|
{ name: 'project', usage: '/project <path>', descKey: 'assistant.slash.project', desc: 'Set the working project' },
|
|
16
16
|
{ name: 'routes', usage: '/routes [always|unsure|never]', descKey: 'assistant.slash.routes', desc: 'Set when to ask before routing (or open the model routes)' },
|
|
17
17
|
{ name: 'models', usage: '/models [list | archived | hide <id…> | show <id…>]', descKey: 'assistant.slash.models', desc: 'Switch models off or on for the Assistant (or open the Models list)' },
|
|
18
|
-
{ name: 'budget', usage: '/budget', descKey: 'assistant.slash.budget', desc: 'Set the
|
|
18
|
+
{ name: 'budget', usage: '/budget', descKey: 'assistant.slash.budget', desc: 'Set the session budget and view provider shares' },
|
|
19
19
|
{ name: 'computer', usage: '/computer [on|off]', descKey: 'assistant.slash.computer', desc: 'Let the assistant use your Mac' },
|
|
20
20
|
{ name: 'stop', usage: '/stop [runId|all]', descKey: 'assistant.slash.stop', desc: 'Stop a run, everything, or the current turn' },
|
|
21
21
|
{ name: 'new', usage: '/new [provider]', descKey: 'assistant.slash.new', desc: 'Start a fresh session' },
|
|
@@ -181,7 +181,7 @@ export function resolveBrainDefaults(brain, catalog = {}) {
|
|
|
181
181
|
|
|
182
182
|
if (ACCOUNT_PROVIDERS.has(b.provider)) {
|
|
183
183
|
const accounts = catalog.accounts?.[b.provider] || [];
|
|
184
|
-
if (!b.accountId || (accounts.length && !accounts.some(a => a?.id === b.accountId))) {
|
|
184
|
+
if (!b.accountId || (b.accountId !== 'auto' && accounts.length && !accounts.some(a => a?.id === b.accountId))) {
|
|
185
185
|
b.accountId = accounts.find(a => a?.isDefault)?.id || accounts[0]?.id || 'default';
|
|
186
186
|
}
|
|
187
187
|
} else {
|
|
@@ -367,10 +367,10 @@ export function injectAssistantStyles() {
|
|
|
367
367
|
.asst-chip.asst-suggestion > .asst-icon svg { width: 20px; height: 14px; }
|
|
368
368
|
.asst-chip.asst-suggestion > .asst-icon { color: var(--asst-text-3); }
|
|
369
369
|
.asst-chip.asst-suggestion:is(:hover, :focus-visible) > .asst-icon { color: var(--asst-text); }
|
|
370
|
-
/*
|
|
371
|
-
.asst-empty-project { display: flex; justify-content: center; max-width: 100%; }
|
|
372
|
-
.asst-dd.asst-empty-
|
|
373
|
-
.asst-empty-
|
|
370
|
+
/* Project and Budget: shared md capsules under the lead, like the suggestions. */
|
|
371
|
+
.asst-empty-project { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; max-width: 100%; }
|
|
372
|
+
.asst-dd.asst-empty-control-btn { max-width: min(100%, 360px); }
|
|
373
|
+
.asst-empty-control-btn .asst-dd-label { max-width: 30ch; }
|
|
374
374
|
.asst-empty-project-key { flex-shrink: 0; color: var(--asst-text-3); font-weight: 400; }
|
|
375
375
|
|
|
376
376
|
/* ── Messages ───────────────────────────────────────────────────── */
|
|
@@ -578,32 +578,61 @@ export function injectAssistantStyles() {
|
|
|
578
578
|
color: var(--asst-text-2);
|
|
579
579
|
}
|
|
580
580
|
|
|
581
|
-
/* ── Route lines
|
|
581
|
+
/* ── Route lines ────────────────────────────────────────────────────
|
|
582
|
+
One line at any width, in the cards' column (the rack's 560px, no
|
|
583
|
+
hang): the glyph, arrow, kind and mark hold; the rest (model ·
|
|
584
|
+
account · usage, or the state) takes only the room left (flex-basis 0)
|
|
585
|
+
and ellipsizes first; the class gives way once the rest is down to
|
|
586
|
+
its "…". The head's title carries the whole text. */
|
|
582
587
|
.asst-route-line { font-size: 12.5px; }
|
|
583
588
|
.asst-route-line-head {
|
|
584
|
-
|
|
589
|
+
position: relative;
|
|
590
|
+
display: flex;
|
|
585
591
|
align-items: center;
|
|
586
592
|
gap: 6px;
|
|
587
|
-
|
|
593
|
+
width: fit-content;
|
|
594
|
+
max-width: min(100%, 560px);
|
|
595
|
+
min-width: 0;
|
|
588
596
|
min-height: 26px;
|
|
589
|
-
margin-left: -8px;
|
|
590
597
|
padding: 3px 8px;
|
|
598
|
+
overflow: hidden;
|
|
591
599
|
border: 0;
|
|
592
600
|
border-radius: var(--asst-r-ctl);
|
|
593
601
|
background: transparent;
|
|
594
602
|
color: var(--asst-text-2);
|
|
595
603
|
font: inherit;
|
|
604
|
+
white-space: nowrap;
|
|
596
605
|
text-align: left;
|
|
597
606
|
cursor: pointer;
|
|
598
607
|
}
|
|
599
608
|
.asst-route-line-head:hover { background: var(--asst-fill-1); }
|
|
600
609
|
.asst-route-glyph { color: var(--asst-brand); }
|
|
610
|
+
.asst-route-glyph svg, .asst-route-icon svg { width: 12px; height: 12px; }
|
|
611
|
+
.asst-route-glyph, .asst-route-arrow, .asst-route-kind, .asst-route-icon, .asst-route-chev { flex: none; }
|
|
601
612
|
.asst-route-arrow, .asst-route-chev { color: var(--asst-text-4); }
|
|
602
613
|
.asst-route-kind { color: var(--asst-text-3); font-size: 11.5px; }
|
|
603
|
-
.asst-route-
|
|
604
|
-
.asst-route-
|
|
614
|
+
.asst-route-class { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
615
|
+
.asst-route-rest { flex: 1 1 0; min-width: 1.2em; overflow: hidden; text-overflow: ellipsis; color: var(--asst-text-3); }
|
|
616
|
+
.asst-route-target { color: var(--asst-text); font-weight: 500; }
|
|
605
617
|
.asst-route-meta, .asst-route-state { flex-shrink: 0; color: var(--asst-text-3); white-space: nowrap; }
|
|
618
|
+
.asst-route-meta:not([hidden]) + .asst-route-state::before { content: '· '; color: var(--asst-text-3); }
|
|
606
619
|
.asst-route-line.pending .asst-route-state { color: var(--asst-warn); }
|
|
620
|
+
/* Live: the rack's sweep (asst-rk-sweep, asst-rack-styles.js) on the row's bottom edge, gold while
|
|
621
|
+
a run it dispatched works (the mark ticks like the rack's running tick), the warn tone while it
|
|
622
|
+
waits for your choice. Settled rows hold still. */
|
|
623
|
+
.asst-route-sweep { position: absolute; left: 0; right: 0; bottom: 0; height: 1.5px; overflow: hidden; pointer-events: none; }
|
|
624
|
+
.asst-route-line[data-live] .asst-route-sweep::after {
|
|
625
|
+
content: '';
|
|
626
|
+
position: absolute;
|
|
627
|
+
top: 0;
|
|
628
|
+
bottom: 0;
|
|
629
|
+
left: 0;
|
|
630
|
+
width: 38%;
|
|
631
|
+
background: linear-gradient(90deg, transparent, var(--asst-brand), transparent);
|
|
632
|
+
animation: asst-rk-sweep 1.8s cubic-bezier(.45, 0, .55, 1) infinite;
|
|
633
|
+
}
|
|
634
|
+
.asst-route-line[data-live="waiting"] .asst-route-sweep::after { background: linear-gradient(90deg, transparent, var(--asst-warn), transparent); }
|
|
635
|
+
.asst-route-line[data-live="working"] .asst-route-icon { animation: asst-rk-tick 1.1s ease-in-out infinite; }
|
|
607
636
|
.asst-route-line.muted .asst-route-glyph { color: var(--asst-text-4); }
|
|
608
637
|
.asst-route-line.muted .asst-route-class { color: var(--asst-text-3); }
|
|
609
638
|
.asst-route-chev { transition: transform var(--asst-dur-base) var(--asst-ease-standard); }
|
|
@@ -1079,6 +1108,11 @@ export function injectAssistantStyles() {
|
|
|
1079
1108
|
/* Fields (toolbar and composer): glyph · seam · value ▾ */
|
|
1080
1109
|
.asst-dd { flex: 0 1 auto; justify-content: flex-start; max-width: calc(18ch + 60px); }
|
|
1081
1110
|
.asst-dd .asst-dd-label { min-width: 0; max-width: 18ch; overflow: hidden; text-overflow: ellipsis; }
|
|
1111
|
+
.asst-dd-model .asst-dd-label.asst-model-account-label { display: flex; gap: 4px; max-width: 34ch; }
|
|
1112
|
+
.asst-model-account-label :is(.asst-model-name, .asst-model-account) { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
1113
|
+
.asst-model-account-label .asst-model-name { flex: 1 1 auto; }
|
|
1114
|
+
.asst-model-account-label .asst-model-account { flex: 0 1 auto; min-width: 4ch; max-width: 12ch; }
|
|
1115
|
+
.asst-model-account-label .asst-model-mode { flex-shrink: 0; }
|
|
1082
1116
|
/* A display name ("Sonnet 5", a project) is sans; a raw model id (opus[1m]) is mono. */
|
|
1083
1117
|
.asst-dd.asst-dd-model[data-raw] .asst-dd-label { font-family: var(--asst-mono); font-weight: 400; }
|
|
1084
1118
|
.asst-dd-caret {
|
|
@@ -1503,11 +1537,50 @@ export function injectAssistantStyles() {
|
|
|
1503
1537
|
.asst-account-label { color: var(--asst-text); }
|
|
1504
1538
|
.asst-account-sub { color: var(--asst-text-3); font-size: 11px; }
|
|
1505
1539
|
.asst-account-row input.asst-dd-input { margin: 0; }
|
|
1540
|
+
/* Claude accounts (claude-account-view.js): what each runs on, its credentials and their actions. */
|
|
1541
|
+
.asst-account-row.claude { flex-wrap: wrap; align-items: flex-start; }
|
|
1542
|
+
.asst-account-row.claude .asst-account-main { flex-basis: 60%; }
|
|
1543
|
+
.asst-account-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
|
|
1544
|
+
.asst-account-badge { flex-shrink: 0; padding: 1px 6px; border-radius: var(--asst-pill-r-xs); color: var(--asst-text-2); font-size: 10.5px; box-shadow: inset 0 0 0 1px var(--asst-line-2); }
|
|
1545
|
+
.asst-account-badge[data-tone="ok"] { color: var(--asst-text); box-shadow: inset 0 0 0 1px var(--asst-pill-on-ring); }
|
|
1546
|
+
.asst-account-badge[data-tone="warn"] { color: var(--asst-warn); box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); }
|
|
1547
|
+
.asst-account-creds { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; color: var(--asst-text-3); font-size: 11px; }
|
|
1548
|
+
.asst-account-cred { display: flex; align-items: center; gap: 6px; }
|
|
1549
|
+
.asst-account-cred-k { min-width: 120px; }
|
|
1550
|
+
.asst-account-actions { display: flex; flex-wrap: wrap; flex-basis: 100%; gap: 6px; margin-top: 6px; }
|
|
1551
|
+
.asst-account-form { display: flex; flex-basis: 100%; gap: 6px; margin-top: 6px; }
|
|
1552
|
+
.asst-account-form input { flex: 1; min-width: 0; }
|
|
1553
|
+
.asst-account-msg { flex-basis: 100%; margin-top: 4px; color: var(--asst-text-3); font-size: 11px; }
|
|
1554
|
+
.asst-dd.asst-dd-warn { color: var(--asst-warn); box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); }
|
|
1506
1555
|
.asst-login-box { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-radius: var(--asst-r-field); box-shadow: inset 0 0 0 1px var(--asst-line-2); }
|
|
1507
1556
|
.asst-login-code { color: var(--asst-text); font-family: var(--asst-mono); font-size: 15px; letter-spacing: .1em; }
|
|
1508
1557
|
|
|
1509
1558
|
.asst-routes-modal { width: min(660px, 100%); }
|
|
1510
|
-
.asst-budget-
|
|
1559
|
+
.asst-budget-modal { width: min(560px, calc(100vw - 24px)); }
|
|
1560
|
+
.asst-budget-controls { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; align-items: center; min-width: 0; }
|
|
1561
|
+
.asst-budget-controls > :is(.asst-modal-note, .asst-budget-field-error, .asst-budget-switch-label) { grid-column: 1 / -1; }
|
|
1562
|
+
.asst-budget-controls > .asst-btn { justify-self: start; }
|
|
1563
|
+
.asst-budget-controls .asst-budget-input { width: 96px; min-width: 0; box-sizing: border-box; }
|
|
1564
|
+
.asst-budget-switch-label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--asst-text); cursor: pointer; }
|
|
1565
|
+
.asst-budget-switch { appearance: none; position: relative; width: 30px; height: 18px; margin: 0; border: 0; border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-3); box-shadow: inset 0 0 0 1px var(--asst-line-2); cursor: pointer; }
|
|
1566
|
+
.asst-budget-switch::before { content: ''; position: absolute; width: 12px; height: 12px; top: 3px; left: 3px; border-radius: var(--asst-pill-r-xs); background: var(--asst-text-3); transition: transform var(--asst-dur-fast) var(--asst-ease-standard); }
|
|
1567
|
+
.asst-budget-switch:checked { background: var(--asst-brand); }
|
|
1568
|
+
.asst-budget-switch:checked::before { background: var(--asst-ink); transform: translateX(12px); }
|
|
1569
|
+
.asst-budget-controls :is(input, button):focus-visible { outline: 2px solid var(--asst-focus); outline-offset: 2px; }
|
|
1570
|
+
.asst-budget-controls input:disabled { opacity: .5; }
|
|
1571
|
+
.asst-budget-popover .asst-dd-list { padding: 8px; }
|
|
1572
|
+
.asst-budget-session { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--asst-line); }
|
|
1573
|
+
.asst-budget-total { font-family: var(--asst-mono); }
|
|
1574
|
+
.asst-budget-session[data-tone="warn"] .asst-budget-total, .asst-budget-dry { color: var(--asst-warn); }
|
|
1575
|
+
.asst-budget-session[data-tone="over"] .asst-budget-total { color: var(--asst-live); }
|
|
1576
|
+
.asst-budget-providers { display: flex; flex-direction: column; gap: 8px; }
|
|
1577
|
+
.asst-budget-provider { padding: 8px; border-radius: var(--asst-r-field); background: var(--asst-fill-1); overflow-wrap: anywhere; }
|
|
1578
|
+
.asst-budget-provider[data-eligible="false"] { color: var(--asst-text-3); }
|
|
1579
|
+
.asst-budget-provider-name { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
1580
|
+
.asst-budget-provider-money { margin-top: 4px; font: 12px/1.5 var(--asst-mono); }
|
|
1581
|
+
.asst-budget-brain { padding: 0 6px; border-radius: var(--asst-pill-r-xs); background: var(--asst-fill-2); font-size: 11.5px; }
|
|
1582
|
+
.asst-budget-dry { font-size: 12px; }
|
|
1583
|
+
.asst-budget-meter { width: 100%; accent-color: var(--asst-brand); }
|
|
1511
1584
|
.asst-budget-desc, .asst-budget-meta { color: var(--asst-text-3); font-size: 11.5px; line-height: 1.4; }
|
|
1512
1585
|
.asst-budget-meta { font-variant-numeric: tabular-nums; }
|
|
1513
1586
|
.asst-budget-field-error { color: var(--asst-live); font-size: 11.5px; line-height: 1.4; }
|
|
@@ -1592,7 +1665,7 @@ export function injectAssistantStyles() {
|
|
|
1592
1665
|
/* Model routes editor: Settings density on a desktop pointer. Scoped here
|
|
1593
1666
|
so route cards, the model manager and other Assistant menus keep their UI. */
|
|
1594
1667
|
.asst-routes-overlay { font-family: var(--stg-font, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Inter, sans-serif); }
|
|
1595
|
-
.asst-routes-overlay .asst-routes-modal { width: min(
|
|
1668
|
+
.asst-routes-overlay .asst-routes-modal { width: min(1040px, 100%); max-height: calc(100dvh - 32px); border-radius: var(--asst-r-card); line-height: 1.4; }
|
|
1596
1669
|
.asst-routes-modal :is(.asst-btn, .asst-dd, .asst-iconbtn) { --cap-h: var(--asst-pill-h-sm); --cap-r: var(--asst-pill-r-xs); --cap-px: var(--asst-pill-px-sm); --cap-icon: 12px; --cap-gap: 4px; }
|
|
1597
1670
|
.asst-routes-modal .asst-modal-head { min-height: 40px; padding: 8px 14px 8px 22px; font-size: 13px; }
|
|
1598
1671
|
.asst-routes-modal .asst-modal-head .asst-icon { color: var(--asst-text-2); }
|
|
@@ -1616,7 +1689,7 @@ export function injectAssistantStyles() {
|
|
|
1616
1689
|
.asst-routes-modal .asst-routes-list-actions .asst-btn:hover:not(:disabled) { color: var(--asst-text); background: var(--asst-fill-1); }
|
|
1617
1690
|
/* Reserve the widths the current model names, effort words and destinations
|
|
1618
1691
|
need; give all remaining space to task descriptions instead of a share. */
|
|
1619
|
-
.asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { display: grid; grid-template-columns: minmax(
|
|
1692
|
+
.asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { display: grid; grid-template-columns: minmax(100px, 1fr) minmax(120px, 200px) 70px 116px minmax(120px, 190px) var(--asst-pill-h-sm); column-gap: 6px; }
|
|
1620
1693
|
.asst-routes-columns { position: sticky; top: -14px; z-index: 1; padding: 4px 8px; color: var(--asst-text-3); font-size: 11.5px; background: var(--asst-popover); }
|
|
1621
1694
|
.asst-routes-columns span:last-child { visibility: hidden; }
|
|
1622
1695
|
.asst-routes-modal .asst-routes-list { padding: 0 8px; border: 1px solid var(--asst-line); border-radius: var(--radius-group, 9px); background: var(--asst-field); }
|
|
@@ -1655,6 +1728,11 @@ export function injectAssistantStyles() {
|
|
|
1655
1728
|
.asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-main { flex-direction: row; align-items: center; gap: 8px; }
|
|
1656
1729
|
.asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-label { flex-shrink: 0; max-width: 60%; }
|
|
1657
1730
|
.asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-desc { flex: 1; text-align: end; }
|
|
1731
|
+
/* Account identities and quota windows need a second line in their menu. */
|
|
1732
|
+
.asst-routes-overlay .asst-route-account-menu .asst-dd-item { height: auto; min-height: var(--asst-pill-h-sm); padding: 4px 8px; }
|
|
1733
|
+
.asst-routes-overlay .asst-route-account-menu .asst-dd-item-main { flex-direction: column; align-items: stretch; gap: 2px; }
|
|
1734
|
+
.asst-routes-overlay .asst-route-account-menu :is(.asst-dd-item-label, .asst-dd-item-desc) { white-space: normal; max-width: none; text-align: start; overflow-wrap: anywhere; }
|
|
1735
|
+
.asst-model-menu .asst-dd-item[data-kind="radio"]:not([data-id*=":"]) .asst-dd-item-desc { white-space: normal; overflow-wrap: anywhere; }
|
|
1658
1736
|
.asst-routes-overlay .asst-route-model-menu .asst-dd-input { height: var(--asst-pill-h-sm); min-height: var(--asst-pill-h-sm); font-size: 12px; border-radius: var(--asst-pill-r-xs); }
|
|
1659
1737
|
.asst-routes-overlay .asst-route-model-menu .asst-dd-input:focus-visible { outline: 2px solid var(--asst-focus); outline-offset: -2px; }
|
|
1660
1738
|
.asst-route-menu-foot { flex-shrink: 0; display: flex; flex-wrap: wrap; align-items: center; border-top: 1px solid var(--asst-line-2); padding-top: 4px; }
|
|
@@ -1665,7 +1743,7 @@ export function injectAssistantStyles() {
|
|
|
1665
1743
|
.asst-routes-modal :is(.asst-btn, .asst-dd, .asst-iconbtn) { --cap-h: 44px; }
|
|
1666
1744
|
.asst-routes-modal .asst-routes-mode { height: auto; min-height: 44px; }
|
|
1667
1745
|
.asst-routes-modal .asst-routes-mode-label { white-space: normal; }
|
|
1668
|
-
.asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { grid-template-columns: minmax(0,
|
|
1746
|
+
.asst-routes-modal .asst-routes-columns, .asst-routes-modal .asst-routes-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr) minmax(0, .7fr) minmax(0, .9fr) minmax(0, 1.4fr) 44px; }
|
|
1669
1747
|
.asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) :is(.asst-dd-item, .asst-dd-input) { min-height: 44px; height: auto; padding: 8px; }
|
|
1670
1748
|
.asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) .asst-dd-item-main { flex-direction: column; align-items: stretch; gap: 2px; }
|
|
1671
1749
|
.asst-routes-overlay :is(.asst-route-model-menu, .asst-route-field-menu) :is(.asst-dd-item-label, .asst-dd-item-desc) { white-space: normal; max-width: none; text-align: start; overflow-wrap: anywhere; }
|
|
@@ -1678,7 +1756,7 @@ export function injectAssistantStyles() {
|
|
|
1678
1756
|
.asst-routes-modal .asst-routes-modes { grid-template-columns: 1fr; }
|
|
1679
1757
|
.asst-routes-modal .asst-routes-columns { display: none; }
|
|
1680
1758
|
.asst-routes-modal .asst-routes-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 44px; gap: 8px; padding: 12px 0; }
|
|
1681
|
-
.asst-routes-modal .asst-routes-row-main, .asst-routes-modal .asst-routes-model-cell { grid-column: 1 / -1; }
|
|
1759
|
+
.asst-routes-modal .asst-routes-row-main, .asst-routes-modal .asst-routes-model-cell, .asst-routes-modal .asst-routes-account-cell { grid-column: 1 / -1; }
|
|
1682
1760
|
.asst-routes-cell { flex-direction: column; gap: 4px; }
|
|
1683
1761
|
.asst-routes-cell-label { display: block; color: var(--asst-text-3); font-size: 11.5px; }
|
|
1684
1762
|
.asst-routes-reset-cell { align-self: end; }
|
|
@@ -1790,8 +1868,8 @@ export function injectAssistantStyles() {
|
|
|
1790
1868
|
.asst-suggestions { flex-direction: column; align-items: stretch; width: 100%; }
|
|
1791
1869
|
.asst-chip.asst-suggestion { justify-content: flex-start; max-width: none; }
|
|
1792
1870
|
.asst-empty-project { width: 100%; }
|
|
1793
|
-
.asst-dd.asst-empty-
|
|
1794
|
-
.asst-empty-
|
|
1871
|
+
.asst-dd.asst-empty-control-btn { flex: 1 1 auto; justify-content: flex-start; max-width: none; }
|
|
1872
|
+
.asst-empty-control-btn .asst-dd-label { flex: 1 1 auto; max-width: none; text-align: start; }
|
|
1795
1873
|
.asst-msg.msg-user .asst-msg-content { max-width: 92%; }
|
|
1796
1874
|
.asst-dd-model .asst-dd-label { max-width: 9ch; }
|
|
1797
1875
|
.asst-route-opt-sub { display: none; }
|
|
@@ -1825,6 +1903,8 @@ export function injectAssistantStyles() {
|
|
|
1825
1903
|
@keyframes asst-spin { to { transform: rotate(360deg); } }
|
|
1826
1904
|
|
|
1827
1905
|
body.ui-interacting :is(.asst-live-dot, .asst-spinner, .asst-computer-dot, .syna-cameo-eye) { animation-play-state: paused !important; }
|
|
1906
|
+
body.ui-interacting .asst-route-line[data-live] .asst-route-icon,
|
|
1907
|
+
body.ui-interacting .asst-route-line[data-live] .asst-route-sweep::after { animation-play-state: paused !important; }
|
|
1828
1908
|
|
|
1829
1909
|
@media (prefers-reduced-motion: reduce) {
|
|
1830
1910
|
.asst-transcript { scroll-behavior: auto; }
|
|
@@ -1834,6 +1914,8 @@ export function injectAssistantStyles() {
|
|
|
1834
1914
|
.term-minimized-pill,
|
|
1835
1915
|
.term-minimized-pill::before,
|
|
1836
1916
|
.term-minimized-pill::after { animation: none !important; transition: none !important; }
|
|
1917
|
+
/* A frozen sweep would be a stray stub: the route row draws none (its run card and state words remain). */
|
|
1918
|
+
.asst-route-sweep { display: none; }
|
|
1837
1919
|
}
|
|
1838
1920
|
`;
|
|
1839
1921
|
document.head.appendChild(style);
|
|
@@ -20,6 +20,8 @@ const definitions = [
|
|
|
20
20
|
['hooks', 'Inspect configured hooks', 'hooks_list'],
|
|
21
21
|
['init', 'Create or update AGENTS.md guidance', null, true],
|
|
22
22
|
['logout', 'Sign out of Codex', 'account_logout'],
|
|
23
|
+
['login', 'Sign in with ChatGPT'],
|
|
24
|
+
['account', 'Choose an account or sign in'],
|
|
23
25
|
['mcp', 'Search MCP servers and tools', 'mcp_status'],
|
|
24
26
|
['mention', 'Add a structured file mention', null, true],
|
|
25
27
|
['model', 'Choose model'],
|
|
@@ -146,10 +148,11 @@ export function configChanges(definitions, inputs, original) {
|
|
|
146
148
|
export const SESSION_CONFIG_FIELDS = Object.freeze({
|
|
147
149
|
model: 'model', model_reasoning_effort: 'effort', model_reasoning_summary: 'summary',
|
|
148
150
|
personality: 'personality', service_tier: 'serviceTier', approval_policy: 'approvalPolicy',
|
|
151
|
+
sandbox_mode: 'sandboxPolicy',
|
|
149
152
|
});
|
|
150
153
|
|
|
151
154
|
export function sessionSettings(config) {
|
|
152
|
-
return Object.fromEntries(Object.entries(config).filter(([key]) => SESSION_CONFIG_FIELDS[key]).map(([key, value]) => [SESSION_CONFIG_FIELDS[key], value]));
|
|
155
|
+
return Object.fromEntries(Object.entries(config).filter(([key]) => SESSION_CONFIG_FIELDS[key]).map(([key, value]) => [key === 'sandbox_mode' ? 'sandboxMode' : SESSION_CONFIG_FIELDS[key], value]));
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
export function modelServiceTiers(model) {
|