synabun 2.0.0 → 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 +97 -0
- package/NOTICE +5 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +59 -0
- package/benchmarks/fixtures/browser/README.md +1 -1
- package/benchmarks/fixtures/desktop/README.md +1 -1
- package/docs/whatsapp.md +153 -18
- package/hooks/claude-code/pre-compact.mjs +11 -0
- package/hooks/claude-code/prompt-submit.mjs +60 -184
- package/hooks/claude-code/session-start.mjs +73 -16
- package/hooks/claude-code/shared.mjs +26 -3
- package/hooks/claude-code/stop.mjs +5 -5
- 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/lib/start-launcher.js +88 -1
- 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 -3
- 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 +2126 -209
- package/neural-interface/i18n/pt-BR.json +2123 -207
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/assistant-api.js +316 -57
- package/neural-interface/lib/assistant-brains/claude.js +19 -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-catalog.js +179 -27
- package/neural-interface/lib/assistant-config.js +91 -14
- package/neural-interface/lib/assistant-dispatch.js +494 -138
- package/neural-interface/lib/assistant-hidden-models.js +393 -66
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-memory.js +24 -10
- package/neural-interface/lib/assistant-persona.js +73 -12
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-pricing.js +21 -6
- 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 +488 -59
- package/neural-interface/lib/assistant-task-prompt.js +13 -3
- package/neural-interface/lib/assistant-test-density.js +142 -0
- package/neural-interface/lib/assistant-user-context-budgets.js +123 -0
- package/neural-interface/lib/assistant-user-context.js +908 -0
- package/neural-interface/lib/assistant-user-knowledge.js +328 -0
- package/neural-interface/lib/assistant-user-learn.js +404 -0
- package/neural-interface/lib/assistant-user-threads.js +226 -0
- package/neural-interface/lib/assistant-user-writer.js +183 -0
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/browser-background-tab.js +134 -0
- package/neural-interface/lib/browser-screenshot.js +678 -61
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +123 -9
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +33 -5
- 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/claude-temporary.js +46 -7
- 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/cloudflared/install.js +533 -0
- package/neural-interface/lib/cloudflared/manager.js +545 -0
- package/neural-interface/lib/cloudflared/pins.js +115 -0
- package/neural-interface/lib/cloudflared/pins.json +63 -0
- package/neural-interface/lib/cloudflared/tunnel.js +265 -0
- 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/editor-completion-endpoint.js +264 -0
- package/neural-interface/lib/editor-completion-opencode.js +436 -0
- package/neural-interface/lib/editor-completion.js +714 -0
- package/neural-interface/lib/editor-config.js +149 -0
- package/neural-interface/lib/editor-files-api.js +268 -0
- package/neural-interface/lib/editor-git-api.js +134 -0
- package/neural-interface/lib/file-explorer-api.js +1039 -0
- package/neural-interface/lib/file-explorer-system.js +355 -0
- package/neural-interface/lib/file-explorer-trash.ps1 +85 -0
- package/neural-interface/lib/file-explorer-watch.js +239 -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-diff-api.js +103 -0
- package/neural-interface/lib/git-read.js +234 -0
- package/neural-interface/lib/greeting/api.js +89 -0
- package/neural-interface/lib/greeting/budgets.js +90 -0
- package/neural-interface/lib/greeting/engine.js +461 -0
- package/neural-interface/lib/greeting/facts.js +320 -0
- package/neural-interface/lib/greeting/sources.js +252 -0
- package/neural-interface/lib/greeting/writer.js +139 -0
- package/neural-interface/lib/http-guards.js +189 -13
- package/neural-interface/lib/install-details.js +145 -0
- package/neural-interface/lib/machine-names.js +88 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/memory-api.js +31 -7
- package/neural-interface/lib/native-loop-providers.js +26 -8
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/plugin-installer.js +4 -2
- package/neural-interface/lib/project-storage.js +5 -7
- 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/request-gate.js +589 -0
- 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/ui-blob-api.js +108 -0
- package/neural-interface/lib/ui-blob-store.js +68 -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 +701 -2
- package/neural-interface/package.json +13 -2
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/install-app.html +9 -6
- package/neural-interface/public/offline.html +19 -3
- package/neural-interface/public/onboarding.html +107 -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 +50 -14
- 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-automation.js +286 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +430 -81
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-composer.js +11 -1
- package/neural-interface/public/shared/assistant/asst-mascot.js +140 -61
- package/neural-interface/public/shared/assistant/asst-menu.js +27 -11
- package/neural-interface/public/shared/assistant/asst-panel.js +198 -42
- package/neural-interface/public/shared/assistant/asst-render.js +197 -23
- package/neural-interface/public/shared/assistant/asst-route-models.js +108 -0
- package/neural-interface/public/shared/assistant/asst-route.js +95 -343
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +534 -0
- package/neural-interface/public/shared/assistant/asst-sanitize.js +8 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +31 -9
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +186 -2
- package/neural-interface/public/shared/assistant/asst-styles.js +247 -11
- package/neural-interface/public/shared/blob-store.js +111 -22
- 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 +80 -118
- package/neural-interface/public/shared/cdx/cdx-render.js +25 -89
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +164 -107
- package/neural-interface/public/shared/cdx/cdx-tabs.js +348 -668
- 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/constants.js +6 -1
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-rehydrate.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +61 -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-ai-logic.js +409 -0
- package/neural-interface/public/shared/editor/editor-ai.js +619 -0
- package/neural-interface/public/shared/editor/editor-commands.js +212 -0
- package/neural-interface/public/shared/editor/editor-compare.js +103 -0
- package/neural-interface/public/shared/editor/editor-completion-list.js +172 -0
- package/neural-interface/public/shared/editor/editor-doc.js +257 -0
- package/neural-interface/public/shared/editor/editor-git-logic.js +131 -0
- package/neural-interface/public/shared/editor/editor-git.js +95 -0
- package/neural-interface/public/shared/editor/editor-languages.js +156 -0
- package/neural-interface/public/shared/editor/editor-lint.js +229 -0
- package/neural-interface/public/shared/editor/editor-loader.js +51 -0
- package/neural-interface/public/shared/editor/editor-markdown.js +90 -0
- package/neural-interface/public/shared/editor/editor-menu.js +233 -0
- package/neural-interface/public/shared/editor/editor-minimap.js +371 -0
- package/neural-interface/public/shared/editor/editor-palette-logic.js +192 -0
- package/neural-interface/public/shared/editor/editor-palette.js +125 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +288 -0
- package/neural-interface/public/shared/editor/editor-panel.js +2099 -0
- package/neural-interface/public/shared/editor/editor-prefs.js +224 -0
- package/neural-interface/public/shared/editor/editor-safety.js +500 -0
- package/neural-interface/public/shared/editor/editor-search.js +352 -0
- package/neural-interface/public/shared/editor/editor-statusbar.js +144 -0
- package/neural-interface/public/shared/editor/editor-tabs-logic.js +331 -0
- package/neural-interface/public/shared/editor/editor-tabs.js +233 -0
- package/neural-interface/public/shared/editor/editor-theme.js +244 -0
- package/neural-interface/public/shared/editor/editor-view.js +332 -0
- package/neural-interface/public/shared/explorer/fx-api.js +216 -0
- package/neural-interface/public/shared/explorer/fx-links.js +204 -0
- package/neural-interface/public/shared/explorer/fx-logic.js +537 -0
- package/neural-interface/public/shared/explorer/fx-main.js +865 -0
- package/neural-interface/public/shared/explorer/fx-names.js +87 -0
- package/neural-interface/public/shared/explorer/fx-ops.js +370 -0
- package/neural-interface/public/shared/explorer/fx-popups.js +490 -0
- package/neural-interface/public/shared/explorer/fx-tree.js +790 -0
- package/neural-interface/public/shared/file-links.js +339 -0
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +178 -0
- package/neural-interface/public/shared/greeting/greeting-view.js +148 -0
- package/neural-interface/public/shared/html-shell.js +161 -165
- 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-childpanel.js +4 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-greeting.js +47 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +131 -17
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +22 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +158 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +3 -2
- package/neural-interface/public/shared/pwa-install-page.js +32 -2
- package/neural-interface/public/shared/pwa-install.css +3 -3
- package/neural-interface/public/shared/pwa-install.js +100 -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-code-editor-pane.js +412 -0
- package/neural-interface/public/shared/settings/settings-code-editor.js +328 -0
- package/neural-interface/public/shared/settings/settings-greeting.js +133 -0
- package/neural-interface/public/shared/settings/settings-ia.js +33 -4
- package/neural-interface/public/shared/settings/settings-user-context.js +154 -0
- package/neural-interface/public/shared/showcase-privacy.js +489 -0
- package/neural-interface/public/shared/start-bridge.js +238 -19
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +1710 -770
- package/neural-interface/public/shared/synabun-mascot.js +68 -58
- package/neural-interface/public/shared/tunnel-view.js +167 -0
- package/neural-interface/public/shared/ui-claude-panel.js +369 -229
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-detail.js +15 -0
- package/neural-interface/public/shared/ui-explorer.js +22 -0
- package/neural-interface/public/shared/ui-file-explorer.js +481 -2496
- package/neural-interface/public/shared/ui-file-links.js +103 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-help.js +26 -0
- package/neural-interface/public/shared/ui-invite.js +126 -116
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-keybinds.js +4 -0
- package/neural-interface/public/shared/ui-loading.js +187 -19
- package/neural-interface/public/shared/ui-menubar.js +25 -33
- package/neural-interface/public/shared/ui-native-loop-router-state.js +11 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +15 -2
- package/neural-interface/public/shared/ui-navbar.js +48 -22
- 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 +509 -490
- package/neural-interface/public/shared/ui-sidepanel-runtime.js +2 -1
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +22 -0
- package/neural-interface/public/shared/ui-terminal.js +40 -5
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-tunnel.js +130 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +6 -2
- 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/ui-whiteboard.js +12 -0
- package/neural-interface/public/shared/ui-worker-end.js +245 -0
- package/neural-interface/public/shared/ui-workspaces.js +41 -27
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +253 -0
- package/neural-interface/public/sw.js +2 -2
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/public/vendor/codemirror/chunks/apl-CSDKT72Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asciiarmor-JYYPU5BM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asn1-NRWCZDA6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/asterisk-5OU6D4ZQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/brainfuck-ID4N4SYC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-4RZWGCS2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-ACTJM3JR.js +11 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-S6BMAXLK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-TO2RDOHQ.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-US2GMZZQ.js +13 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-WYYO5ZAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-XALZTDJC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-YRNUTRDV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/chunk-Z3QOKH62.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clike-OWQBSOGL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/clojure-KV75UQWC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cmake-GTEP5Y3C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cobol-WCZ7QEGJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/coffeescript-TGLBOT6B.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/commonlisp-WVVI432V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/crystal-S6R6HLKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/css-PDA2RINP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/cypher-HL5QPL4C.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/d-HHJH2HAJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/diff-MN3A35BY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-25SQLBVS.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-27ZGGJJS.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-4E52OJIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-6FT73XDC.js +2 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-BE6YGRMW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CJSGKW2O.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CRPH2WLI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-CTG7JEQN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-I3FEVELX.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-IM5BBSWZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-KERUKTIF.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-LXTMWZAM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MABASSET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MJVBUZLL.js +3 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-MNB3WZVU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-QYWXOE3U.js +7 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-TEBOFWLZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-UNXFROM3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XL2SLQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-XO42Y3AP.js +6 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-Y2WWDWJK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-YFEZKWMU.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dist-ZY7SWQO6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dockerfile-3Y4R4NCH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dtd-5343GJ4P.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/dylan-FI2CXSW2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ebnf-GNQ77YWA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ecl-MRPUAE2L.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/eiffel-DZROES27.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/elm-4KICRFKQ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/erlang-3IHYC263.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/factor-C6D3E5UK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fcl-VYTWUJMI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/forth-ZBO2EH5M.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/fortran-5NDCRWIK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gas-SKRMOXFP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/gherkin-L45YXZLW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/groovy-GLPTLN44.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haskell-E7NF7P4Y.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/haxe-K2NVDOB6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/http-Q6DRQIJB.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/idl-MZYNJVLN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/javascript-6YRYK2HR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/julia-HXEL2T5G.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/livescript-MV7CI2JZ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/lua-E36NHOOD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mathematica-3XOZZCMC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mbox-ODYGFKS7.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mirc-WB43AWF5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mllike-ZT6ET7ZH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/modelica-LLMZPFGA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mscgen-2OPNNYFH.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/mumps-6P3HWJH6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nginx-TCGKQTRW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/nsis-ATIGIXTJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ntriples-5PLBI5OV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/octave-QF2S3NL6.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/oz-JFIVFFH2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pascal-JG6SW76V.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/perl-4B3IWVVO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pig-ABEBH4MI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/powershell-H32XE7TL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/properties-NN644MMA.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/protobuf-YKWQK2J3.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/pug-RIYXEKFW.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/puppet-JZQ5WPEV.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/python-EBCGGHAC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/q-5UMH2YQY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/r-7XMGG453.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/rpm-IEV7TSLR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ruby-T4SPFPNM.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sas-GEDX67ET.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/scheme-FTLOWKPC.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/shell-HUFRKFSJ.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sieve-P5QUDVYO.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/smalltalk-2SORJNQI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/solr-M22TGAR5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sparql-4M3KL752.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/spreadsheet-3K23LA4N.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/sql-VHRFMUBI.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stex-QZDAQUY2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/stylus-7ZDKAKBD.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/swift-WCLCLGMR.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tcl-JLMU6WOK.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/textile-4CHDKZ4T.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiddlywiki-DKMCZZQ4.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/tiki-F4JIG36Q.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/toml-BHMHTIM2.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/troff-YQ2KV5NT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-ZHRCVFJY.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/ttcn-cfg-KJOVTY4X.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/turtle-535P5N6I.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vb-76UMQHOT.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vbscript-CGNDTK6Z.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/velocity-VSJDO5T5.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/verilog-4T6ETXIP.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/vhdl-T7FNAXUN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/webidl-P6MJSJ6J.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/xquery-UOVILRGN.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/yacas-CV2APROL.js +1 -0
- package/neural-interface/public/vendor/codemirror/chunks/z80-3KFNVM4M.js +1 -0
- package/neural-interface/public/vendor/codemirror/codemirror-6.43.14-75f05e67.min.js +2 -0
- package/neural-interface/public/vendor/codemirror/manifest.json +60 -0
- package/neural-interface/scripts/build-vendor.mjs +78 -2
- package/neural-interface/scripts/vendor-codemirror-entry.js +121 -0
- package/neural-interface/server.js +2085 -1629
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/package.json +5 -8
- package/setup.js +53 -15
- package/benchmarks/browser-live-results.json +0 -79
- package/benchmarks/browser-v2-results.json +0 -258
- package/neural-interface/lib/codex-capabilities.d.ts +0 -41
- package/neural-interface/public/category-logos/claude.svg +0 -1
- package/neural-interface/public/category-logos/gemini-color.svg +0 -1
- package/neural-interface/public/category-logos/openai.svg +0 -1
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude.svg +0 -1
- package/neural-interface/public/gallery.svg +0 -6
- package/neural-interface/public/leonardoai.svg +0 -1
- package/neural-interface/public/mcp.svg +0 -4
- package/neural-interface/public/opencode-logo-dark-square.svg +0 -1
- package/neural-interface/public/pin-svgrepo-com.svg +0 -25
- package/neural-interface/public/sessionicon.svg +0 -8
- package/neural-interface/public/toolbar-album.svg +0 -8
- package/neural-interface/public/toolbar-question.svg +0 -6
- package/neural-interface/public/toolbar-sessions.svg +0 -6
- package/neural-interface/scripts/codex-protocol-fixture.mjs +0 -83
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -16,12 +16,14 @@
|
|
|
16
16
|
// open. `synabun.route` events (phase card|auto|decided|pending|expired|
|
|
17
17
|
// declined|cancelled) update the line in place by routeId.
|
|
18
18
|
|
|
19
|
-
import { clampEffortId, getEffortLevelsForModel, modelEffortIds } from '../agent-runtime-options.js';
|
|
20
19
|
import { getProviderMeta } from '../provider-icons.js';
|
|
21
|
-
import { fetchManageCatalog,
|
|
22
|
-
import { catalogModelRow, fmtModelPrice, hiddenModelFor, ICON_CARET, invalidateModelCatalog,
|
|
20
|
+
import { fetchManageCatalog, patchHiddenModels } from './asst-api.js';
|
|
21
|
+
import { catalogModelRow, fmtModelPrice, hiddenModelFor, ICON_CARET, invalidateModelCatalog, openModelMenu } from './asst-brain-picker.js';
|
|
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) => {
|
|
@@ -63,13 +77,12 @@ export function routeModeLabel(mode, t) {
|
|
|
63
77
|
return tr('assistant.routes.mode.unsure', 'Ask when unsure');
|
|
64
78
|
}
|
|
65
79
|
|
|
66
|
-
export function routeModeDescription(mode, model, t) {
|
|
80
|
+
export function routeModeDescription(mode, model, t, askBelow = 0.75) {
|
|
67
81
|
const tr = tf(t);
|
|
68
|
-
const who = str(model) || tr('assistant.routes.theBrain', 'The assistant');
|
|
69
82
|
const m = normalizeRouteMode(mode, DEFAULT_ROUTE_MODE);
|
|
70
|
-
if (m === 'always-ask') return tr('assistant.routes.mode.alwaysDesc', '
|
|
71
|
-
if (m === 'never') return tr('assistant.routes.mode.neverDesc', '
|
|
72
|
-
return tr('assistant.routes.mode.unsureDesc', '{
|
|
83
|
+
if (m === 'always-ask') return tr('assistant.routes.mode.alwaysDesc', 'Confirm every actionable task.');
|
|
84
|
+
if (m === 'never') return tr('assistant.routes.mode.neverDesc', 'Run saved routes immediately; otherwise the assistant chooses.');
|
|
85
|
+
return tr('assistant.routes.mode.unsureDesc', 'Confirm below {pct}% confidence or when a model cannot run the task.', { pct: Math.round(askBelow * 100) });
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
// ── Pure model ──────────────────────────────────────────────────────────────
|
|
@@ -94,6 +107,7 @@ function normalizeRouteOption(o) {
|
|
|
94
107
|
provider: normalizeProvider(src.provider, str(src.provider) || null),
|
|
95
108
|
model: str(src.model),
|
|
96
109
|
effort: str(src.effort) || null,
|
|
110
|
+
...(Object.hasOwn(src, 'accountId') ? { accountId: src.accountId, account: obj(src.account), accountError: obj(src.accountError) } : {}),
|
|
97
111
|
label: str(src.label),
|
|
98
112
|
badge: str(src.badge) || null,
|
|
99
113
|
tier: str(src.tier) || null,
|
|
@@ -102,6 +116,7 @@ function normalizeRouteOption(o) {
|
|
|
102
116
|
contextWindow: num(src.contextWindow),
|
|
103
117
|
reason: str(src.reason),
|
|
104
118
|
warning: str(src.warning) || null,
|
|
119
|
+
budget: str(src.budget) || null,
|
|
105
120
|
disabled: src.disabled === true,
|
|
106
121
|
disabledReason: str(src.disabledReason) || null,
|
|
107
122
|
};
|
|
@@ -175,7 +190,7 @@ export function sameRouteTarget(a, b) {
|
|
|
175
190
|
return (str(a.kind) || 'direct') === (str(b.kind) || 'direct')
|
|
176
191
|
&& str(a.provider) === str(b.provider)
|
|
177
192
|
&& modelKey(a.model) === modelKey(b.model)
|
|
178
|
-
&& (str(a.effort) || '') === (str(b.effort) || '');
|
|
193
|
+
&& (str(a.effort) || '') === (str(b.effort) || '') && str(a.accountId) === str(b.accountId);
|
|
179
194
|
}
|
|
180
195
|
|
|
181
196
|
const BADGE_RANK = { suggested: 0, remembered: 1, current: 2, capable: 3, cheaper: 4, stronger: 5 };
|
|
@@ -208,7 +223,7 @@ function badgeText(o, n, tr) {
|
|
|
208
223
|
const pct = fmtConfidence(n?.confidence);
|
|
209
224
|
switch (o.badge) {
|
|
210
225
|
case 'suggested': return pct ? tr('assistant.routes.badge.suggestedPct', 'Suggested · {pct}', { pct }) : tr('assistant.routes.badge.suggested', 'Suggested');
|
|
211
|
-
case 'remembered': return tr('assistant.routes.badge.remembered', '
|
|
226
|
+
case 'remembered': return tr('assistant.routes.badge.remembered', 'Your choice');
|
|
212
227
|
case 'current': return tr('assistant.routes.badge.current', 'Current');
|
|
213
228
|
case 'capable':
|
|
214
229
|
if (!n?.requires && n?.visionOnly) return tr('assistant.routes.badge.canSee', 'Can see');
|
|
@@ -270,6 +285,7 @@ export function routeOptions(n, { brainLabel = '', t = null } = {}) {
|
|
|
270
285
|
// "here" is the brain's own model: prefer its display label over a raw id.
|
|
271
286
|
title: isHere ? tr('assistant.routes.doHere', 'Do it here with {model}', { model: str(o.label) || hereModel }) : (label || o.id),
|
|
272
287
|
badgeText: badgeText(o, n, tr),
|
|
288
|
+
disabledReason: o.budget === 'share_used_up' ? routeBudgetReason({ provider: o.provider }, t) : o.disabledReason,
|
|
273
289
|
selected: o.id === defaultId,
|
|
274
290
|
};
|
|
275
291
|
});
|
|
@@ -344,6 +360,23 @@ export function designRouteSight(pref, catalog) {
|
|
|
344
360
|
return row.status === 'unavailable' ? 'unavailable' : 'ok';
|
|
345
361
|
}
|
|
346
362
|
|
|
363
|
+
/** Saved ids may resolve through a listed alias; all metadata still comes from this catalog. */
|
|
364
|
+
export function routeCatalogRow(pref, catalog) {
|
|
365
|
+
if (!pref?.provider || !catalog) return null;
|
|
366
|
+
const rows = Array.isArray(catalog[pref.provider]) ? catalog[pref.provider] : [];
|
|
367
|
+
return pref.model ? catalogModelRow(pref.provider, pref.model, catalog) || aliasRow(pref.provider, pref.model, rows) : rows.find(m => m.isDefault) || null;
|
|
368
|
+
}
|
|
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
|
+
|
|
347
380
|
/** Text parts of a route line (pure). */
|
|
348
381
|
export function routeLineParts(route, phase = 'decided', { brainLabel = '', t = null } = {}) {
|
|
349
382
|
const tr = tf(t);
|
|
@@ -360,17 +393,21 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
360
393
|
else if (phase === 'declined' && str(r.reasonCode) === 'no_capable_model') {
|
|
361
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');
|
|
362
395
|
}
|
|
396
|
+
else if (str(r.reasonCode) === 'budget_exhausted') state = routeBudgetReason({ dry: r.dry }, t);
|
|
363
397
|
else if (phase === 'declined') state = tr('assistant.routes.state.declined', 'declined');
|
|
364
398
|
else if (phase === 'cancelled') state = tr('assistant.routes.state.cancelled', 'cancelled');
|
|
365
399
|
let decided = '';
|
|
366
|
-
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;
|
|
367
402
|
case 'brain': decided = tr('assistant.routes.decidedBy', 'decided by {model}', { model: brainLabel || tr('assistant.routes.theBrainLower', 'the assistant') }); break;
|
|
368
403
|
case 'user': decided = tr('assistant.routes.youChose', 'you chose'); break;
|
|
369
|
-
case 'remembered': decided = tr('assistant.routes.remembered', '
|
|
404
|
+
case 'remembered': decided = tr('assistant.routes.remembered', 'your saved choice'); break;
|
|
370
405
|
case 'timeout': decided = tr('assistant.routes.timeout', 'default after timeout'); break;
|
|
371
|
-
default: decided = r.remembered ? tr('assistant.routes.remembered', '
|
|
406
|
+
default: decided = r.remembered ? tr('assistant.routes.remembered', 'your saved choice') : '';
|
|
372
407
|
}
|
|
373
408
|
const meta = [];
|
|
409
|
+
const accountLabel = routeDisplayAccount(target, t);
|
|
410
|
+
if (accountLabel) meta.push(accountLabel);
|
|
374
411
|
if (effort && effort !== 'off') meta.push(effort);
|
|
375
412
|
if (confidence) meta.push(confidence);
|
|
376
413
|
return {
|
|
@@ -384,7 +421,7 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
384
421
|
muted: phase === 'expired' || phase === 'declined' || phase === 'cancelled',
|
|
385
422
|
pending: phase === 'pending' || phase === 'card',
|
|
386
423
|
decidedText: decided,
|
|
387
|
-
reason: str(r.reason),
|
|
424
|
+
reason: target?.accountError ? routeAccountError(target.accountError, t) : str(r.reason),
|
|
388
425
|
alternatives: arr(r.alternatives).map(a => routeTargetLabel(a, { effort: true })).filter(Boolean),
|
|
389
426
|
corrections: arr(r.corrections).map(c => correctionText(c, tr)).filter(Boolean),
|
|
390
427
|
};
|
|
@@ -394,9 +431,15 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
394
431
|
function correctionText(c, tr) {
|
|
395
432
|
if (typeof c === 'string') return c;
|
|
396
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
|
+
}
|
|
397
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) });
|
|
398
441
|
if (c.reason === 'remembered_disabled') return tr('assistant.routes.correction.rememberedDisabled', 'saved route {from} is disabled, kept the pick', { from: str(c.from) });
|
|
399
|
-
const text = firstStr(c.text, c.summary);
|
|
442
|
+
const text = firstStr(c.text, c.summary, c.message);
|
|
400
443
|
if (text) return text;
|
|
401
444
|
if (c.field && (c.from || c.to)) return `${c.field}: ${str(c.from) || '—'} → ${str(c.to) || 'default'}`;
|
|
402
445
|
return str(c.field);
|
|
@@ -410,6 +453,14 @@ export function routeLineText(route, phase, opts) {
|
|
|
410
453
|
return [`⇄ ${p.taskClass} → ${head}`, ...p.meta].join(' · ');
|
|
411
454
|
}
|
|
412
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
|
+
|
|
413
464
|
// ═══════════════════════════════════════════
|
|
414
465
|
// DOM
|
|
415
466
|
// ═══════════════════════════════════════════
|
|
@@ -443,8 +494,12 @@ function providerColor(provider) {
|
|
|
443
494
|
let _lineUid = 0;
|
|
444
495
|
|
|
445
496
|
/**
|
|
446
|
-
* The compact route line
|
|
447
|
-
*
|
|
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) }.
|
|
448
503
|
*/
|
|
449
504
|
export function createRouteLine(route, opts = {}) {
|
|
450
505
|
const tr = tf(opts.t);
|
|
@@ -452,20 +507,19 @@ export function createRouteLine(route, opts = {}) {
|
|
|
452
507
|
const detailId = `asst-route-detail-${++_lineUid}`;
|
|
453
508
|
node.innerHTML = `
|
|
454
509
|
<button type="button" class="asst-route-line-head" aria-expanded="false" aria-controls="${detailId}">
|
|
455
|
-
<span class="asst-route-glyph" aria-hidden="true"
|
|
510
|
+
<span class="asst-route-glyph asst-icon" aria-hidden="true">${ICON_ROUTE}</span>
|
|
456
511
|
<span class="asst-route-class"></span>
|
|
457
512
|
<span class="asst-route-arrow" aria-hidden="true">→</span>
|
|
458
513
|
<span class="asst-route-kind"></span>
|
|
459
514
|
<span class="asst-route-icon asst-icon" aria-hidden="true"></span>
|
|
460
|
-
<span class="asst-route-target"></span>
|
|
461
|
-
<span class="asst-route-meta"></span>
|
|
462
|
-
<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>
|
|
463
516
|
<span class="asst-route-chev" aria-hidden="true">›</span>
|
|
517
|
+
<span class="asst-route-sweep" aria-hidden="true"></span>
|
|
464
518
|
</button>
|
|
465
519
|
<div class="asst-route-detail" id="${detailId}" hidden></div>`;
|
|
466
520
|
const head = node.querySelector('.asst-route-line-head');
|
|
467
521
|
const detail = node.querySelector('.asst-route-detail');
|
|
468
|
-
let current = { route: obj(route) || {}, phase: opts.phase || 'decided' };
|
|
522
|
+
let current = { route: obj(route) || {}, phase: opts.phase || 'decided', running: false };
|
|
469
523
|
|
|
470
524
|
head.addEventListener('click', () => {
|
|
471
525
|
const open = head.getAttribute('aria-expanded') !== 'true';
|
|
@@ -481,6 +535,8 @@ export function createRouteLine(route, opts = {}) {
|
|
|
481
535
|
node.dataset.phase = current.phase;
|
|
482
536
|
node.classList.toggle('muted', p.muted);
|
|
483
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;
|
|
484
540
|
node.querySelector('.asst-route-class').textContent = p.taskClass;
|
|
485
541
|
const arrow = node.querySelector('.asst-route-arrow');
|
|
486
542
|
const kindEl = node.querySelector('.asst-route-kind');
|
|
@@ -488,7 +544,7 @@ export function createRouteLine(route, opts = {}) {
|
|
|
488
544
|
const targetEl = node.querySelector('.asst-route-target');
|
|
489
545
|
const metaEl = node.querySelector('.asst-route-meta');
|
|
490
546
|
const stateEl = node.querySelector('.asst-route-state');
|
|
491
|
-
arrow.hidden = !p.targetLabel && !p.pending;
|
|
547
|
+
arrow.hidden = !p.targetLabel && !p.pending && !p.kindLabel;
|
|
492
548
|
kindEl.textContent = p.kindLabel;
|
|
493
549
|
kindEl.hidden = !p.kindLabel;
|
|
494
550
|
iconEl.innerHTML = p.targetLabel ? providerIconHtml(p.provider, opts) : '';
|
|
@@ -500,7 +556,9 @@ export function createRouteLine(route, opts = {}) {
|
|
|
500
556
|
metaEl.hidden = !p.meta.length;
|
|
501
557
|
stateEl.textContent = p.stateText;
|
|
502
558
|
stateEl.hidden = !p.stateText;
|
|
559
|
+
node.querySelector('.asst-route-rest').hidden = targetEl.hidden && metaEl.hidden && stateEl.hidden;
|
|
503
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 });
|
|
504
562
|
|
|
505
563
|
detail.innerHTML = '';
|
|
506
564
|
const rows = [];
|
|
@@ -529,6 +587,12 @@ export function createRouteLine(route, opts = {}) {
|
|
|
529
587
|
if (phase) current.phase = phase;
|
|
530
588
|
render();
|
|
531
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
|
+
},
|
|
532
596
|
};
|
|
533
597
|
}
|
|
534
598
|
|
|
@@ -598,7 +662,7 @@ export function createRouteModeControl(hostEl, hooks = {}) {
|
|
|
598
662
|
kind: 'radio',
|
|
599
663
|
id,
|
|
600
664
|
label: routeModeLabel(id, hooks.t),
|
|
601
|
-
desc: routeModeDescription(id, model, hooks.t),
|
|
665
|
+
desc: routeModeDescription(id, model, hooks.t, hooks.getMode?.()?.askBelow ?? 0.75),
|
|
602
666
|
selected: id === mode,
|
|
603
667
|
onSelect: () => { if (id !== mode) hooks.onChange?.(id); },
|
|
604
668
|
})),
|
|
@@ -699,7 +763,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
699
763
|
row.setAttribute('aria-disabled', o.disabled ? 'true' : 'false');
|
|
700
764
|
row.tabIndex = -1;
|
|
701
765
|
const kindWord = o.kind && o.kind !== 'direct' ? routeKindLabel(o.kind) : '';
|
|
702
|
-
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(' · ');
|
|
703
767
|
row.innerHTML = `
|
|
704
768
|
<span class="asst-route-num" aria-hidden="true">${esc(o.number)}</span>
|
|
705
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>
|
|
@@ -712,7 +776,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
712
776
|
row.querySelector('.asst-route-opt-title').textContent = o.title;
|
|
713
777
|
if (sub) row.querySelector('.asst-route-opt-sub').textContent = sub;
|
|
714
778
|
const warn = row.querySelector('.asst-route-opt-warn');
|
|
715
|
-
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;
|
|
716
780
|
if (o.badgeText) row.querySelector('.asst-route-badge').textContent = o.badgeText;
|
|
717
781
|
row.addEventListener('click', () => { if (!o.disabled) select(o.id, { focus: true }); });
|
|
718
782
|
return row;
|
|
@@ -779,7 +843,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
779
843
|
state.other = {
|
|
780
844
|
id: 'other', kind, provider: target.provider, model: target.model, effort: null,
|
|
781
845
|
label: target.label || '', badge: null, number: options.length + 1, here: false,
|
|
782
|
-
title: target.label || target.model, badgeText: tr('assistant.routes.badge.other', 'Your
|
|
846
|
+
title: target.label || target.model, badgeText: tr('assistant.routes.badge.other', 'Your choice'),
|
|
783
847
|
vision: target.vision === true, price: target.price || null, reason: '', warning: null, disabled: false,
|
|
784
848
|
};
|
|
785
849
|
state.selectedId = 'other';
|
|
@@ -798,7 +862,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
798
862
|
rememberInput.type = 'checkbox';
|
|
799
863
|
rememberInput.addEventListener('change', () => { state.remember = rememberInput.checked; });
|
|
800
864
|
// `remember.label` is the task-class label ("Code"), not a sentence.
|
|
801
|
-
const text = tr('assistant.routes.rememberFor', '
|
|
865
|
+
const text = tr('assistant.routes.rememberFor', 'Use this choice for {taskClass} tasks', { taskClass: (n.remember.label || n.taskClassLabel).toLowerCase() });
|
|
802
866
|
label.append(rememberInput, el('span', null, text));
|
|
803
867
|
if (n.remember.current) label.appendChild(el('span', 'asst-route-remember-current', tr('assistant.routes.rememberCurrent', 'now: {current}', { current: n.remember.current })));
|
|
804
868
|
card.appendChild(label);
|
|
@@ -1005,321 +1069,9 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
1005
1069
|
return entry;
|
|
1006
1070
|
}
|
|
1007
1071
|
|
|
1008
|
-
// ── Routes editor (
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
/**
|
|
1013
|
-
* "Model routes…" editor. hooks: { t, providerIcon, getBrain() → brain, onSaved(routing), onClose() }
|
|
1014
|
-
* Returns { el, close } (async: resolves after the first load).
|
|
1015
|
-
*/
|
|
1016
|
-
export async function openRoutesEditor(hooks = {}) {
|
|
1017
|
-
if (_editor) { _editor.close(); }
|
|
1018
|
-
const tr = tf(hooks.t);
|
|
1019
|
-
const overlay = el('div', 'asst-modal-overlay');
|
|
1020
|
-
overlay.setAttribute('role', 'dialog');
|
|
1021
|
-
overlay.setAttribute('aria-modal', 'true');
|
|
1022
|
-
overlay.setAttribute('aria-label', tr('assistant.routes.editorTitle', 'Model routes'));
|
|
1023
|
-
const modal = el('div', 'asst-modal asst-routes-modal');
|
|
1024
|
-
modal.innerHTML = `
|
|
1025
|
-
<div class="asst-modal-head"><span class="asst-icon" aria-hidden="true">${ICON_ROUTE}</span><span>${esc(tr('assistant.routes.editorTitle', 'Model routes'))}</span><button type="button" class="asst-iconbtn asst-modal-close" aria-label="${esc(tr('common.close', 'Close'))}">${ICON_X}</button></div>
|
|
1026
|
-
<div class="asst-modal-body"><div class="asst-modal-note">${esc(tr('common.loading', 'Loading...'))}</div></div>
|
|
1027
|
-
<div class="asst-modal-foot">
|
|
1028
|
-
<button type="button" class="asst-btn asst-btn-secondary asst-routes-reset-all">${esc(tr('assistant.routes.resetAll', 'Reset all'))}</button>
|
|
1029
|
-
<span class="asst-modal-spacer"></span>
|
|
1030
|
-
<button type="button" class="asst-btn asst-btn-secondary asst-routes-cancel">${esc(tr('common.cancel', 'Cancel'))}</button>
|
|
1031
|
-
<button type="button" class="asst-btn asst-btn-primary asst-routes-save">${esc(tr('common.save', 'Save'))}</button>
|
|
1032
|
-
</div>`;
|
|
1033
|
-
overlay.appendChild(modal);
|
|
1034
|
-
const body = modal.querySelector('.asst-modal-body');
|
|
1035
|
-
const saveBtn = modal.querySelector('.asst-routes-save');
|
|
1036
|
-
const resetAllBtn = modal.querySelector('.asst-routes-reset-all');
|
|
1037
|
-
const returnTo = document.activeElement;
|
|
1038
|
-
|
|
1039
|
-
const state = { data: null, catalog: null, version: null, defaultMode: DEFAULT_ROUTE_MODE, prefs: {}, patch: {}, error: '', conflict: false, saving: false };
|
|
1040
|
-
|
|
1041
|
-
function close() {
|
|
1042
|
-
if (!overlay.isConnected) return;
|
|
1043
|
-
closeMenu();
|
|
1044
|
-
document.removeEventListener('keydown', onKey, true);
|
|
1045
|
-
overlay.remove();
|
|
1046
|
-
_editor = null;
|
|
1047
|
-
try { hooks.onClose?.(); } catch { /* ignore */ }
|
|
1048
|
-
if (returnTo?.isConnected) { try { returnTo.focus({ preventScroll: true }); } catch { /* ignore */ } }
|
|
1049
|
-
}
|
|
1050
|
-
function onKey(e) {
|
|
1051
|
-
if (e.key === 'Escape') {
|
|
1052
|
-
if (isMenuOpen()) return; // the menu closes itself first
|
|
1053
|
-
e.preventDefault(); e.stopPropagation(); close();
|
|
1054
|
-
return;
|
|
1055
|
-
}
|
|
1056
|
-
e.stopPropagation();
|
|
1057
|
-
}
|
|
1058
|
-
document.addEventListener('keydown', onKey, true);
|
|
1059
|
-
overlay.addEventListener('mousedown', (e) => { if (e.target === overlay) close(); });
|
|
1060
|
-
modal.querySelector('.asst-modal-close').addEventListener('click', close);
|
|
1061
|
-
modal.querySelector('.asst-routes-cancel').addEventListener('click', close);
|
|
1062
|
-
document.body.appendChild(overlay);
|
|
1063
|
-
_editor = { close };
|
|
1064
|
-
|
|
1065
|
-
function effectivePref(key) {
|
|
1066
|
-
return Object.prototype.hasOwnProperty.call(state.patch, key) ? state.patch[key] : (state.prefs[key] || null);
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
function sourceOf(key) {
|
|
1070
|
-
if (Object.prototype.hasOwnProperty.call(state.patch, key)) return state.patch[key] ? 'yours' : 'default';
|
|
1071
|
-
const pref = state.prefs[key];
|
|
1072
|
-
if (!pref) return 'default';
|
|
1073
|
-
return pref.by === 'user' ? 'yours' : 'remembered';
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
function sourceLabel(source) {
|
|
1077
|
-
if (source === 'disabled') return tr('assistant.routes.source.disabled', 'disabled');
|
|
1078
|
-
if (source === 'incapable') return tr('assistant.routes.source.incapable', 'incapable');
|
|
1079
|
-
if (source === 'yours') return tr('assistant.routes.source.yours', 'yours');
|
|
1080
|
-
if (source === 'remembered') return tr('assistant.routes.source.remembered', 'remembered');
|
|
1081
|
-
return tr('assistant.routes.source.default', 'default');
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
/** Does the route's model generate `medium`? true | false | null (the catalog cannot tell). */
|
|
1085
|
-
function routeMakes(pref, medium) {
|
|
1086
|
-
if (!medium || !pref?.provider || !state.catalog) return null;
|
|
1087
|
-
const rows = Array.isArray(state.catalog[pref.provider]) ? state.catalog[pref.provider] : [];
|
|
1088
|
-
const row = pref.model ? catalogModelRow(pref.provider, pref.model, state.catalog) : rows.find(m => m.isDefault) || null;
|
|
1089
|
-
if (!row?.outputs || typeof row.outputs !== 'object') return null;
|
|
1090
|
-
return row.outputs[medium] === true;
|
|
1091
|
-
}
|
|
1092
|
-
/** No model in the loaded catalog makes `medium` (false while the catalog has no output data). */
|
|
1093
|
-
function noMaker(medium) {
|
|
1094
|
-
const rows = Object.values(state.catalog || {}).flatMap(list => (Array.isArray(list) ? list : []));
|
|
1095
|
-
return rows.some(m => m?.outputs && typeof m.outputs === 'object') && !rows.some(m => m?.outputs?.[medium] === true);
|
|
1096
|
-
}
|
|
1097
|
-
/** No model in the loaded catalog can see (false while no catalog is loaded). */
|
|
1098
|
-
function noSeer() {
|
|
1099
|
-
const rows = Object.values(state.catalog || {}).flatMap(list => (Array.isArray(list) ? list : []));
|
|
1100
|
-
return rows.length > 0 && !rows.some(m => m?.vision === true);
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
function taskRows() {
|
|
1104
|
-
const classes = Array.isArray(state.data?.taskClasses) ? state.data.taskClasses.filter(c => c?.id) : [];
|
|
1105
|
-
const rows = classes.map(c => ({
|
|
1106
|
-
id: String(c.id), label: str(c.label) || humanizeClass(c.id), description: str(c.description), defaultKind: str(c.defaultKind) || 'direct',
|
|
1107
|
-
// Image / video creation: only models that generate the medium, always on a worker; design: only models that can see.
|
|
1108
|
-
requires: c.requires === 'image' || c.requires === 'video' ? c.requires : null, dispatchOnly: c.dispatchOnly === true, visionOnly: c.vision === true,
|
|
1109
|
-
}));
|
|
1110
|
-
if (!rows.some(r => r.id === 'vision')) rows.push({ id: 'vision', label: tr('assistant.routes.visionRow', 'Screenshots & images'), description: tr('assistant.routes.visionDesc', 'Used when a task needs a model that can see.'), defaultKind: 'direct' });
|
|
1111
|
-
return rows;
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
function render() {
|
|
1115
|
-
body.innerHTML = '';
|
|
1116
|
-
if (state.error) {
|
|
1117
|
-
const banner = el('div', 'asst-routes-error');
|
|
1118
|
-
banner.setAttribute('role', 'alert');
|
|
1119
|
-
banner.appendChild(el('span', null, state.error));
|
|
1120
|
-
if (state.conflict) {
|
|
1121
|
-
const reload = el('button', 'asst-btn asst-btn-secondary', tr('assistant.routes.reload', 'Reload'));
|
|
1122
|
-
reload.type = 'button';
|
|
1123
|
-
reload.addEventListener('click', () => load());
|
|
1124
|
-
banner.appendChild(reload);
|
|
1125
|
-
}
|
|
1126
|
-
body.appendChild(banner);
|
|
1127
|
-
}
|
|
1128
|
-
if (!state.data) return;
|
|
1129
|
-
|
|
1130
|
-
// When to ask (default route mode)
|
|
1131
|
-
const modeSection = el('section', 'asst-routes-section');
|
|
1132
|
-
modeSection.appendChild(el('div', 'asst-routes-section-title', tr('assistant.routes.whenToAsk', 'When to ask')));
|
|
1133
|
-
const modes = el('div', 'asst-routes-modes');
|
|
1134
|
-
modes.setAttribute('role', 'radiogroup');
|
|
1135
|
-
modes.setAttribute('aria-label', tr('assistant.routes.whenToAsk', 'When to ask'));
|
|
1136
|
-
const brainName = hooks.brainName?.() || '';
|
|
1137
|
-
for (const id of ROUTE_MODES) {
|
|
1138
|
-
const b = el('button', 'asst-routes-mode');
|
|
1139
|
-
b.type = 'button';
|
|
1140
|
-
b.setAttribute('role', 'radio');
|
|
1141
|
-
b.setAttribute('aria-checked', state.defaultMode === id ? 'true' : 'false');
|
|
1142
|
-
b.dataset.mode = id;
|
|
1143
|
-
b.innerHTML = `<span class="asst-routes-mode-label">${esc(routeModeLabel(id, hooks.t))}</span><span class="asst-routes-mode-desc">${esc(routeModeDescription(id, brainName, hooks.t))}</span>`;
|
|
1144
|
-
b.addEventListener('click', () => { state.defaultMode = id; render(); modal.querySelector(`.asst-routes-mode[data-mode="${id}"]`)?.focus(); });
|
|
1145
|
-
modes.appendChild(b);
|
|
1146
|
-
}
|
|
1147
|
-
modeSection.appendChild(modes);
|
|
1148
|
-
body.appendChild(modeSection);
|
|
1149
|
-
|
|
1150
|
-
// Task routes
|
|
1151
|
-
const section = el('section', 'asst-routes-section');
|
|
1152
|
-
section.appendChild(el('div', 'asst-routes-section-title', tr('assistant.routes.taskRoutes', 'Task routes')));
|
|
1153
|
-
const list = el('div', 'asst-routes-list');
|
|
1154
|
-
for (const row of taskRows()) list.appendChild(taskRow(row));
|
|
1155
|
-
section.appendChild(list);
|
|
1156
|
-
body.appendChild(section);
|
|
1157
|
-
saveBtn.disabled = state.saving;
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
function taskRow(row) {
|
|
1161
|
-
const pref = effectivePref(row.id);
|
|
1162
|
-
const medium = row.requires || null;
|
|
1163
|
-
const seeing = !medium && row.visionOnly === true;
|
|
1164
|
-
// A saved route onto a model the user hid is never applied (the brain decides); nor, for
|
|
1165
|
-
// image / video creation, one onto a model that cannot generate the medium, or for design one
|
|
1166
|
-
// the router would skip (not known to see, missing from the catalog, unavailable).
|
|
1167
|
-
const sight = seeing && pref ? designRouteSight(pref, state.catalog) : null;
|
|
1168
|
-
const source = pref?.model && hiddenModelFor(pref.provider, pref.model) ? 'disabled'
|
|
1169
|
-
: medium && pref && routeMakes(pref, medium) === false ? 'incapable'
|
|
1170
|
-
: sight && sight !== 'ok' ? 'incapable' : sourceOf(row.id);
|
|
1171
|
-
const emptyLabel = medium ? tr('assistant.routes.noCapableModel', 'No model in your catalog can make {medium} yet.', { medium: mediumWords(medium, tr) })
|
|
1172
|
-
: seeing ? tr('assistant.routes.noSeeingModel', 'No model in your catalog can see images yet.') : '';
|
|
1173
|
-
const node = el('div', 'asst-routes-row');
|
|
1174
|
-
node.dataset.taskClass = row.id;
|
|
1175
|
-
const main = el('div', 'asst-routes-row-main');
|
|
1176
|
-
main.appendChild(el('div', 'asst-routes-row-label', row.label));
|
|
1177
|
-
if (row.description) main.appendChild(el('div', 'asst-routes-row-desc', row.description));
|
|
1178
|
-
if ((medium && noMaker(medium)) || (seeing && noSeer())) main.appendChild(el('div', 'asst-routes-row-note', emptyLabel));
|
|
1179
|
-
node.appendChild(main);
|
|
1180
|
-
|
|
1181
|
-
const modelBtn = el('button', 'asst-dd asst-routes-model');
|
|
1182
|
-
modelBtn.type = 'button';
|
|
1183
|
-
modelBtn.setAttribute('aria-haspopup', 'menu');
|
|
1184
|
-
const label = pref ? routeTargetLabel(pref) : tr('assistant.routes.brainDecides', 'Let the brain decide');
|
|
1185
|
-
modelBtn.innerHTML = `${pref?.provider ? `<span class="asst-icon" aria-hidden="true" style="color:${esc(providerColor(pref.provider))}">${providerIconHtml(pref.provider, hooks)}</span>` : ''}<span class="asst-dd-label"></span>`;
|
|
1186
|
-
modelBtn.querySelector('.asst-dd-label').textContent = label;
|
|
1187
|
-
modelBtn.setAttribute('aria-label', `${row.label}: ${label}`);
|
|
1188
|
-
modelBtn.addEventListener('click', (e) => {
|
|
1189
|
-
e.stopPropagation();
|
|
1190
|
-
if (isMenuOpen(modelBtn)) { closeMenu(); return; }
|
|
1191
|
-
openModelMenu(modelBtn, {
|
|
1192
|
-
brain: hooks.getBrain?.() || null,
|
|
1193
|
-
t: hooks.t,
|
|
1194
|
-
includeHere: true,
|
|
1195
|
-
hereLabel: tr('assistant.routes.brainDecides', 'Let the brain decide'),
|
|
1196
|
-
title: row.label,
|
|
1197
|
-
current: pref ? { provider: pref.provider, model: pref.model } : null,
|
|
1198
|
-
onToast: hooks.onToast,
|
|
1199
|
-
// Image / video creation: only models that generate the medium (no custom id: its output is unknown);
|
|
1200
|
-
// design: only models that can see.
|
|
1201
|
-
...(medium ? { accept: (_p, m) => m?.outputs?.[medium] === true, allowCustom: false, emptyLabel } : {}),
|
|
1202
|
-
...(seeing ? { accept: (_p, m) => m?.vision === true && m?.status !== 'unavailable', allowCustom: false, emptyLabel } : {}),
|
|
1203
|
-
onSelect: (target) => {
|
|
1204
|
-
if (target?.here) state.patch[row.id] = null;
|
|
1205
|
-
else if (target?.model) {
|
|
1206
|
-
// The effort survives when the new model runs it; otherwise the nearest level it does.
|
|
1207
|
-
const ids = modelEffortIds(catalogModelRow(target.provider, target.model, state.catalog));
|
|
1208
|
-
const effort = !pref?.effort ? null : (ids ? clampEffortId(pref.effort, ids) : (pref.provider === target.provider ? pref.effort : null));
|
|
1209
|
-
state.patch[row.id] = { kind: row.defaultKind || 'direct', provider: target.provider, model: target.model, effort, label: target.label || '' };
|
|
1210
|
-
}
|
|
1211
|
-
render();
|
|
1212
|
-
modal.querySelector(`.asst-routes-row[data-task-class="${CSS.escape(row.id)}"] .asst-routes-model`)?.focus();
|
|
1213
|
-
},
|
|
1214
|
-
});
|
|
1215
|
-
});
|
|
1216
|
-
node.appendChild(modelBtn);
|
|
1217
|
-
|
|
1218
|
-
// Exactly the selected model's levels (the provider list only for an unknown model).
|
|
1219
|
-
const modelRow = pref?.provider ? catalogModelRow(pref.provider, pref.model, state.catalog) : null;
|
|
1220
|
-
const efforts = pref?.provider ? getEffortLevelsForModel(pref.provider, modelRow) : [];
|
|
1221
|
-
if (pref && efforts.some(e => e.id !== 'off')) {
|
|
1222
|
-
const effortBtn = el('button', 'asst-dd asst-routes-effort');
|
|
1223
|
-
effortBtn.type = 'button';
|
|
1224
|
-
effortBtn.setAttribute('aria-haspopup', 'menu');
|
|
1225
|
-
const level = efforts.find(e => e.id === pref.effort);
|
|
1226
|
-
const autoLabel = modelRow?.defaultEffort
|
|
1227
|
-
? tr('assistant.routes.effortAutoDefault', 'auto ({effort})', { effort: efforts.find(e => e.id === modelRow.defaultEffort)?.label?.toLowerCase() || modelRow.defaultEffort })
|
|
1228
|
-
: tr('assistant.routes.effortAuto', 'auto effort');
|
|
1229
|
-
const effortLabel = level ? level.label
|
|
1230
|
-
: pref.effort ? tr('assistant.routes.effortUnsupported', '{effort} · unsupported', { effort: pref.effort })
|
|
1231
|
-
: autoLabel;
|
|
1232
|
-
effortBtn.innerHTML = '<span class="asst-dd-label"></span>';
|
|
1233
|
-
effortBtn.querySelector('.asst-dd-label').textContent = effortLabel;
|
|
1234
|
-
effortBtn.setAttribute('aria-label', `${tr('assistant.brain.effort', 'Thinking effort')}: ${effortLabel}`);
|
|
1235
|
-
effortBtn.addEventListener('click', (e) => {
|
|
1236
|
-
e.stopPropagation();
|
|
1237
|
-
if (isMenuOpen(effortBtn)) { closeMenu(); return; }
|
|
1238
|
-
openMenu(effortBtn, {
|
|
1239
|
-
title: tr('assistant.brain.effort', 'Thinking effort'),
|
|
1240
|
-
role: 'menu',
|
|
1241
|
-
items: [
|
|
1242
|
-
{ kind: 'radio', id: '', label: autoLabel, selected: !pref.effort, onSelect: () => { state.patch[row.id] = { ...pref, effort: null }; render(); } },
|
|
1243
|
-
...efforts.filter(x => x.id !== 'off').map(x => ({ kind: 'radio', id: x.id, label: x.label, desc: x.desc || '', selected: pref.effort === x.id, onSelect: () => { state.patch[row.id] = { ...pref, effort: x.id }; render(); } })),
|
|
1244
|
-
],
|
|
1245
|
-
});
|
|
1246
|
-
});
|
|
1247
|
-
node.appendChild(effortBtn);
|
|
1248
|
-
}
|
|
1249
|
-
|
|
1250
|
-
const badge = el('span', 'asst-routes-source', sourceLabel(source));
|
|
1251
|
-
badge.dataset.source = source;
|
|
1252
|
-
if (source === 'incapable') {
|
|
1253
|
-
badge.setAttribute('data-tooltip', !seeing ? tr('assistant.routes.incapableTip', "This model can't generate {medium}, so the route is not applied.", { medium: mediumWords(medium, tr) })
|
|
1254
|
-
: sight === 'blind' ? tr('assistant.routes.incapableSeeTip', "This model can't see images, so the route is not applied.")
|
|
1255
|
-
: sight === 'unavailable' ? tr('assistant.routes.unavailableTip', "This model isn't available right now, so the route is not applied.")
|
|
1256
|
-
: tr('assistant.routes.unknownSeeTip', "This model isn't known to see images, so the route is not applied."));
|
|
1257
|
-
}
|
|
1258
|
-
node.appendChild(badge);
|
|
1259
|
-
|
|
1260
|
-
const reset = el('button', 'asst-iconbtn asst-routes-reset', '↺');
|
|
1261
|
-
reset.type = 'button';
|
|
1262
|
-
reset.setAttribute('aria-label', tr('assistant.routes.reset', 'Reset {taskClass}', { taskClass: row.label }));
|
|
1263
|
-
reset.setAttribute('data-tooltip', tr('assistant.routes.resetTip', 'Back to default'));
|
|
1264
|
-
reset.hidden = source === 'default';
|
|
1265
|
-
reset.addEventListener('click', () => { state.patch[row.id] = null; render(); });
|
|
1266
|
-
node.appendChild(reset);
|
|
1267
|
-
return node;
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
async function load() {
|
|
1271
|
-
state.error = '';
|
|
1272
|
-
state.conflict = false;
|
|
1273
|
-
try {
|
|
1274
|
-
const [data, catalog] = await Promise.all([getRouting(), loadModelCatalog().catch(() => null)]);
|
|
1275
|
-
state.catalog = catalog || null;
|
|
1276
|
-
state.data = data || {};
|
|
1277
|
-
state.version = data?.version ?? null;
|
|
1278
|
-
state.defaultMode = normalizeRouteMode(data?.routing?.defaultMode, DEFAULT_ROUTE_MODE);
|
|
1279
|
-
state.prefs = obj(data?.routing?.preferences) ? { ...data.routing.preferences } : {};
|
|
1280
|
-
state.patch = {};
|
|
1281
|
-
} catch (err) {
|
|
1282
|
-
state.data = null;
|
|
1283
|
-
state.error = err?.status === 404
|
|
1284
|
-
? tr('assistant.routes.unavailable', 'Model routing is not available on this server yet.')
|
|
1285
|
-
: `${tr('assistant.routes.loadFailed', 'Could not load model routes')}: ${err?.message || err}`;
|
|
1286
|
-
}
|
|
1287
|
-
render();
|
|
1288
|
-
modal.querySelector('.asst-routes-mode[aria-checked="true"]')?.focus();
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
resetAllBtn.addEventListener('click', () => {
|
|
1292
|
-
for (const key of new Set([...Object.keys(state.prefs), ...Object.keys(state.patch)])) state.patch[key] = null;
|
|
1293
|
-
render();
|
|
1294
|
-
});
|
|
1295
|
-
|
|
1296
|
-
saveBtn.addEventListener('click', async () => {
|
|
1297
|
-
if (!state.data || state.saving) return;
|
|
1298
|
-
state.saving = true;
|
|
1299
|
-
saveBtn.disabled = true;
|
|
1300
|
-
const patch = { defaultMode: state.defaultMode };
|
|
1301
|
-
if (Object.keys(state.patch).length) patch.preferences = { ...state.patch };
|
|
1302
|
-
try {
|
|
1303
|
-
const res = await putRouting(patch, state.version);
|
|
1304
|
-
try { hooks.onSaved?.(res?.routing || null, res); } catch { /* ignore */ }
|
|
1305
|
-
close();
|
|
1306
|
-
} catch (err) {
|
|
1307
|
-
state.saving = false;
|
|
1308
|
-
if (err?.status === 409 || err?.code === 'VERSION_CONFLICT') {
|
|
1309
|
-
state.error = tr('assistant.routes.conflict', 'Changed elsewhere — reload');
|
|
1310
|
-
state.conflict = true;
|
|
1311
|
-
} else if (err?.code === 'ROUTING_INVALID') {
|
|
1312
|
-
state.error = tr('assistant.routes.invalidField', 'Invalid routing setting: {field}', { field: err?.message || '' });
|
|
1313
|
-
} else {
|
|
1314
|
-
state.error = `${tr('assistant.routes.saveFailed', 'Could not save')}: ${err?.message || err}`;
|
|
1315
|
-
}
|
|
1316
|
-
render();
|
|
1317
|
-
}
|
|
1318
|
-
});
|
|
1319
|
-
|
|
1320
|
-
await load();
|
|
1321
|
-
return { el: overlay, close };
|
|
1322
|
-
}
|
|
1072
|
+
// ── Routes editor (draft, modes, task choices) ───────────────────────────────
|
|
1073
|
+
// Its model lists still come only through asst-route-models.js.
|
|
1074
|
+
export { openRoutesEditor } from './asst-routes-editor.js';
|
|
1323
1075
|
|
|
1324
1076
|
// ── Models manager (Assistant → Models…) ───────────────────────────────────
|
|
1325
1077
|
|