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
|
@@ -29,11 +29,16 @@ import { emit, on, state as appState } from '../state.js';
|
|
|
29
29
|
import { storage } from '../storage.js';
|
|
30
30
|
import { KEYS } from '../constants.js';
|
|
31
31
|
import { t } from '../i18n.js';
|
|
32
|
+
// The account words of the brain picker and the accounts manager (account-view.js) in the page's language.
|
|
33
|
+
// Here, not in those modules: node tests load them, and i18n.js reads the page's storage.
|
|
34
|
+
import '../account-i18n.js';
|
|
35
|
+
import { forgetGreeting, mountGreeting } from '../greeting/greeting-card.js';
|
|
32
36
|
import { notify, NOTIF_TYPE } from '../ui-notifications.js';
|
|
33
37
|
import { getProviderMeta } from '../provider-icons.js';
|
|
34
38
|
import { isGuest, hasPermission } from '../ui-sync.js';
|
|
35
39
|
import { getWhiteboardElementById } from '../ui-whiteboard.js';
|
|
36
40
|
import { focusNativeLoopRun } from '../ui-native-loop-router.js';
|
|
41
|
+
import { wireFileLinks } from '../ui-file-links.js';
|
|
37
42
|
import { injectAssistantStyles } from './asst-styles.js';
|
|
38
43
|
import { createAssistantSocket } from './asst-ws.js';
|
|
39
44
|
import { createRenderer, ICON_ASSISTANT, mdInto } from './asst-render.js';
|
|
@@ -42,19 +47,19 @@ import { lastInputAt, onKey, onPointer } from '../synabun-ticker.js';
|
|
|
42
47
|
import { buildControlResponse, closedControlState, normalizeControlRequest, renderControlCard } from './asst-control.js';
|
|
43
48
|
import { buildPromptText, createComposer } from './asst-composer.js';
|
|
44
49
|
import { buildDispatchSpec, parseSlashCommand, slashHelpMarkdown } from './asst-slash.js';
|
|
45
|
-
import { createBrainPicker } from './asst-brain-picker.js';
|
|
50
|
+
import { cachedProjects, createBrainPicker, testDensityLabel } from './asst-brain-picker.js';
|
|
46
51
|
import { createRovingToolbar } from './asst-roving.js';
|
|
47
52
|
import { openAccountsManager } from './asst-accounts.js';
|
|
48
53
|
import { createAgentsDock } from './asst-agents-dock.js';
|
|
49
54
|
import { createUsageGauge } from './asst-usage.js';
|
|
50
55
|
import { closeMenu, isMenuOpen, openMenu } from './asst-menu.js';
|
|
51
56
|
import { createRouteModeControl, openModelsManager, openRoutesEditor, refreshModelsManager, renderRouteCard, routeModeLabel } from './asst-route.js';
|
|
52
|
-
import { budgetChipText, budgetTone, openBudgetEditor } from './asst-budget.js';
|
|
57
|
+
import { budgetChipText, budgetSpendText, budgetTone, closeBudgetEditor, createBudgetController, mountBudgetChip, openBudgetEditor } from './asst-budget.js';
|
|
53
58
|
import { computerRemoteView, createComputerPanel, createComputerToggle, normalizeComputerRemote, openFrameLightbox, closeFrameLightbox, requestDesktopStop } from './asst-computer.js';
|
|
54
59
|
import {
|
|
55
|
-
applyLimit, applyLimitPacket, applyUsagePacket, brainLabel, createTabState, DEFAULT_ROUTE_MODE, deserializeTabState, fmtCost, fmtRunLine, limitExpiresIn, limitNoticeText, modelShortName,
|
|
56
|
-
brainModePatch, normalizeBrain, normalizeComputerMode, permissionModeLabel, normalizeRouteMode, providerShortLabel, readStoredBrain, runFromPayload,
|
|
57
|
-
serializeTabState, tabStorageKey,
|
|
60
|
+
applyLimit, applyLimitPacket, applyUsagePacket, brainLabel, createRenameWriter, createTabState, DEFAULT_ROUTE_MODE, deserializeTabState, fmtCost, fmtRunLine, limitExpiresIn, limitNoticeText, modelShortName,
|
|
61
|
+
brainModePatch, normalizeBrain, normalizeComputerMode, permissionModeLabel, normalizeRouteMode, normalizeTestDensity, projectLabel, providerShortLabel, readStoredBrain, runFromPayload,
|
|
62
|
+
serializeTabState, tabStorageKey, userSessionTitle,
|
|
58
63
|
} from './asst-state.js';
|
|
59
64
|
import {
|
|
60
65
|
closeAssistantSession, dispatchAssistantRun, escalateAssistantRun, focusAssistantRun, getAssistantRun,
|
|
@@ -93,10 +98,6 @@ function num(value) {
|
|
|
93
98
|
return value != null && value !== '' && Number.isFinite(n) ? n : null;
|
|
94
99
|
}
|
|
95
100
|
|
|
96
|
-
function basename(path) {
|
|
97
|
-
return String(path || '').split(/[\\/]/).filter(Boolean).pop() || String(path || '');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
101
|
function relativeTime(value) {
|
|
101
102
|
const ms = new Date(value || 0).getTime();
|
|
102
103
|
if (!ms) return '';
|
|
@@ -109,6 +110,10 @@ function relativeTime(value) {
|
|
|
109
110
|
return `${Math.floor(hrs / 24)}d`;
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
// Renames on their way to a session record. One writer for the page, not one per tab: a write
|
|
114
|
+
// outlives the tab that asked for it, and every Sessions menu waits for all of them.
|
|
115
|
+
const renames = createRenameWriter({ patch: patchAssistantSession });
|
|
116
|
+
|
|
112
117
|
function clipCopy(text) {
|
|
113
118
|
if (navigator.clipboard?.writeText) navigator.clipboard.writeText(text).catch(() => {});
|
|
114
119
|
}
|
|
@@ -116,17 +121,21 @@ function clipCopy(text) {
|
|
|
116
121
|
/**
|
|
117
122
|
* mountAssistant(viewport, { sessionId, brain, host, session })
|
|
118
123
|
* host: { id: 'terminal'|'sidepanel', notifySource: 'cli'|'panel',
|
|
119
|
-
* setLabel(text),
|
|
124
|
+
* setLabel(text), setName?(text, renamed) (a rename that was not saved: the tab's name and its
|
|
125
|
+
* renamed flag go back, saved label included), setStatus('idle'|'working'|'action'|'done'), setBrain(brain),
|
|
120
126
|
* isVisible() → bool, toolbarSlot?: Element (where the toolbar goes; default the root),
|
|
121
127
|
* takeHeroOrigin?() → DOMRect | null (a character the host showed while starting: the empty state's flies in from it) }
|
|
122
128
|
* The hooks may fire during mount, before this returns.
|
|
123
|
-
* The tab label is always "SynaBun"; a user rename arrives via controller.setTitle
|
|
129
|
+
* The tab label is always "SynaBun"; a user rename arrives via controller.setTitle
|
|
130
|
+
* and is written to the session record (saveRename), where every session list reads it.
|
|
124
131
|
*/
|
|
125
132
|
export function mountAssistant(viewport, { sessionId, brain, host = {}, session = null } = {}) {
|
|
126
133
|
injectAssistantStyles();
|
|
127
134
|
const saved = deserializeTabState(storage.getItem(tabStorageKey(sessionId)));
|
|
135
|
+
let greetingId = null; // the greeting this conversation opened with, until its first prompt carries it
|
|
136
|
+
const defaultTitle = tt('assistant.defaultTitle', 'SynaBun');
|
|
128
137
|
const st = createTabState(sessionId, brain || session?.brain || saved?.brain || readStoredBrain(storage), {
|
|
129
|
-
title:
|
|
138
|
+
title: defaultTitle,
|
|
130
139
|
// The agents tray starts collapsed; it only expands on request.
|
|
131
140
|
dockOpen: saved ? saved.dockOpen : storage.getItem(KEYS.ASSISTANT_DOCK_OPEN) === '1',
|
|
132
141
|
dockShowAll: saved ? saved.dockShowAll : storage.getItem(KEYS.ASSISTANT_DOCK_SHOW_ALL) === '1',
|
|
@@ -135,6 +144,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
135
144
|
providerSessionId: session?.providerSessionId || null,
|
|
136
145
|
routeMode: saved?.routeMode ?? null,
|
|
137
146
|
computerUse: saved?.computerUse ?? null,
|
|
147
|
+
testDensity: saved?.testDensity ?? null,
|
|
138
148
|
usageView: saved?.usageView || null,
|
|
139
149
|
});
|
|
140
150
|
Object.assign(st, {
|
|
@@ -144,7 +154,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
144
154
|
engine: '',
|
|
145
155
|
sdkVersion: '',
|
|
146
156
|
routing: null, // server view { mode, effectiveMode, defaultMode, askBelow }
|
|
147
|
-
features: { routing: null, computer: null }, // null = unknown yet
|
|
157
|
+
features: { routing: null, computer: null, testDensity: null, rename: null }, // null = unknown yet
|
|
158
|
+
testDensityEffective: null, // server view: the level in effect
|
|
148
159
|
brainCapabilities: null,
|
|
149
160
|
computerEffective: st.computerUse === true,
|
|
150
161
|
desktopSupported: null,
|
|
@@ -152,8 +163,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
152
163
|
desktopActive: false,
|
|
153
164
|
desktopProbe: false,
|
|
154
165
|
routingProbe: false,
|
|
155
|
-
sessionTitle: String(session?.title || ''), // the server's name (first prompt): history + notifications
|
|
166
|
+
sessionTitle: String(session?.title || ''), // the server's name (the user's rename, else the first prompt): history + notifications
|
|
156
167
|
});
|
|
168
|
+
let recordName = userSessionTitle(session); // the name the session record holds as the user's ('' = never renamed)
|
|
157
169
|
const controls = new Map(); // requestId → { card, origin, kind }
|
|
158
170
|
// Cards answered here (requestId → card): one the server closed before the answer
|
|
159
171
|
// arrived (control_cancelled after it) says so instead of keeping "Allowed".
|
|
@@ -253,6 +265,45 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
253
265
|
try { host.setLabel?.(st.title); } catch { /* ignore */ }
|
|
254
266
|
}
|
|
255
267
|
|
|
268
|
+
/** What the session record says about its name (a session view: the rename's answer, a broadcast, an attach). */
|
|
269
|
+
function applyRecordName(meta) {
|
|
270
|
+
if (!meta || typeof meta !== 'object' || !meta.title || (meta.id && meta.id !== sessionId)) return;
|
|
271
|
+
st.sessionTitle = String(meta.title);
|
|
272
|
+
recordName = userSessionTitle(meta);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* A rename names the session, not this tab: it goes to the session record, which the Sessions
|
|
277
|
+
* menu and every other list read, through the page's one writer (`renames`). A host also calls
|
|
278
|
+
* setTitle when it mounts a renamed tab (`restored`): the name the record already holds is not
|
|
279
|
+
* written again, one it lacks (a rename made before the record kept names) is, and it never
|
|
280
|
+
* replaces a name on its way. "SynaBun" is no name: it is written only to take a rename back.
|
|
281
|
+
* `from` is the name the tab had before: a write that fails puts it back (renameFailed).
|
|
282
|
+
*/
|
|
283
|
+
function saveRename(from, restored = false) {
|
|
284
|
+
if (destroyed || (restored && renames.pending(sessionId))) return;
|
|
285
|
+
renames.save(sessionId, st.title, {
|
|
286
|
+
from, recordName, recordTitle: st.sessionTitle, keepsRenames: st.features.rename === true, defaultTitle,
|
|
287
|
+
onSaved: applyRecordName,
|
|
288
|
+
onFailed: renameFailed,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* A rename that did not reach the record: the tab goes back to the name the record answers to,
|
|
294
|
+
* in the host and in the label it saved, and the transcript says so. A name with nothing to go
|
|
295
|
+
* back to (a restored one, or the name the tab already shows) stays: the next mount sends it again.
|
|
296
|
+
*/
|
|
297
|
+
function renameFailed({ name, back }) {
|
|
298
|
+
const previous = back || defaultTitle;
|
|
299
|
+
if (destroyed || previous === st.title) return;
|
|
300
|
+
st.userRenamed = previous !== defaultTitle;
|
|
301
|
+
st.title = previous;
|
|
302
|
+
try { host.setName?.(previous, st.userRenamed); } catch { /* ignore */ }
|
|
303
|
+
persist();
|
|
304
|
+
renderer.appendStatus(tt('assistant.status.renameFailed', 'Could not save the name “{name}”. The session is still called “{previous}”.', { name, previous }), 'warn');
|
|
305
|
+
}
|
|
306
|
+
|
|
256
307
|
/** Notifications say which conversation: the rename, else the session's first-prompt title. */
|
|
257
308
|
function notifyLabel() {
|
|
258
309
|
return st.userRenamed ? st.title : (st.sessionTitle || st.title);
|
|
@@ -317,11 +368,11 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
317
368
|
}
|
|
318
369
|
|
|
319
370
|
function renderCost() {
|
|
320
|
-
// The
|
|
321
|
-
const budget = st.budget && Number.isFinite(Number(st.budget.
|
|
322
|
-
const total = budget ? Number(budget.
|
|
323
|
-
costEl.textContent = fmtCost(total);
|
|
324
|
-
costEl.hidden = !(total > 0);
|
|
371
|
+
// The confirmed SessionBudget includes both brain and worker spend.
|
|
372
|
+
const budget = st.budget && Number.isFinite(Number(st.budget.spentUsd)) ? st.budget : null;
|
|
373
|
+
const total = budget ? Number(budget.spentUsd) : totalCost();
|
|
374
|
+
costEl.textContent = budget ? budgetSpendText(budget, { t }) : fmtCost(total);
|
|
375
|
+
costEl.hidden = !budget && !(total > 0);
|
|
325
376
|
const tone = budgetTone(budget);
|
|
326
377
|
if (tone && tone !== 'ok') costEl.dataset.tone = tone;
|
|
327
378
|
else delete costEl.dataset.tone;
|
|
@@ -331,6 +382,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
331
382
|
? tt('assistant.topbar.costSplit', 'Session {session} · agents {agents}', { session: fmtCost(st.costUsd || 0), agents: fmtCost(st.dispatchUsd) })
|
|
332
383
|
: tt('assistant.topbar.cost', 'Session cost');
|
|
333
384
|
costEl.setAttribute('data-tooltip', tip);
|
|
385
|
+
costEl.setAttribute('aria-label', `${tt('assistant.budget.title', 'Budget')}: ${tip}`);
|
|
334
386
|
}
|
|
335
387
|
|
|
336
388
|
function brainModelLabel() {
|
|
@@ -378,7 +430,27 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
378
430
|
return st.features.routing === true;
|
|
379
431
|
}
|
|
380
432
|
|
|
433
|
+
// ── File links ──
|
|
434
|
+
// A link to a file the code editor can open opens it there (shared with the other panels): one listener
|
|
435
|
+
// for the transcript, whatever drew the message. A relative path is a path in the brain's project.
|
|
436
|
+
wireFileLinks(transcriptEl, {
|
|
437
|
+
cwd: () => st.brain?.project || '',
|
|
438
|
+
textLinks: 'a.file-link',
|
|
439
|
+
// A path the editor does not open keeps what a click on it did: it is copied.
|
|
440
|
+
copyPath: (link) => {
|
|
441
|
+
clipCopy((link.textContent || '').replace(/:\d+$/, ''));
|
|
442
|
+
link.style.opacity = '0.5';
|
|
443
|
+
setTimeout(() => { link.style.opacity = ''; }, 300);
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
|
|
381
447
|
// ── Renderer ──
|
|
448
|
+
let emptyBudgetChip = null;
|
|
449
|
+
const budgetController = createBudgetController({ t, sessionId, onSaved: (data) => {
|
|
450
|
+
if (destroyed) return;
|
|
451
|
+
if (data?.session) st.budget = data.session;
|
|
452
|
+
renderCost();
|
|
453
|
+
} });
|
|
382
454
|
const renderer = createRenderer(transcriptEl, {
|
|
383
455
|
t,
|
|
384
456
|
providerIcon,
|
|
@@ -389,10 +461,28 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
389
461
|
onReadRun: (runId) => readRun(runId),
|
|
390
462
|
onEscalateRun: (runId, target) => escalateRun(runId, target),
|
|
391
463
|
onMemoryClick: (id) => { if (id) emit('memory:open', { id }); },
|
|
464
|
+
// The greeting this conversation opens with (lib/greeting/ decides whether there is one): its id
|
|
465
|
+
// goes back with the first prompt, so the brain is told once what was said.
|
|
466
|
+
greeting: (slot) => (guestBlocked() ? null : mountGreeting(slot, {
|
|
467
|
+
surface: 'assistant', session: sessionId, cwd: st.brain?.project || null, t,
|
|
468
|
+
onOpenMemory: (id) => { if (id) emit('memory:open', { id }); },
|
|
469
|
+
}).then((greeting) => { greetingId = greeting?.id || null; return greeting; })),
|
|
392
470
|
brainLabel: () => brainModelLabel(),
|
|
393
471
|
onChangeRoutes: () => openRoutes(),
|
|
394
472
|
onRouteEvent: (ev) => handleRouteCardEvent(ev),
|
|
395
473
|
emptySuggestions: () => emptySuggestions(),
|
|
474
|
+
// "Choose a project" in the empty state is the toolbar's Working project field (asst-brain-picker.js).
|
|
475
|
+
mountEmptyProject: (slot) => picker.mountEmptyProject(slot),
|
|
476
|
+
mountEmptyBudget: (slot) => {
|
|
477
|
+
emptyBudgetChip?.destroy();
|
|
478
|
+
emptyBudgetChip = mountBudgetChip(slot, budgetController, { t, mascot: () => director });
|
|
479
|
+
return emptyBudgetChip.el;
|
|
480
|
+
},
|
|
481
|
+
unmountEmptyControls: () => {
|
|
482
|
+
picker.mountEmptyProject(null);
|
|
483
|
+
emptyBudgetChip?.destroy();
|
|
484
|
+
emptyBudgetChip = null;
|
|
485
|
+
},
|
|
396
486
|
onSuggestion: (template) => composer.fillTemplate(template),
|
|
397
487
|
heroOrigin: () => { try { return host.takeHeroOrigin?.() || null; } catch { return null; } },
|
|
398
488
|
onMascotChange: () => director?.retarget(),
|
|
@@ -424,7 +514,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
424
514
|
});
|
|
425
515
|
|
|
426
516
|
function emptySuggestions() {
|
|
427
|
-
const project = st.brain.project
|
|
517
|
+
const project = projectLabel(st.brain.project, cachedProjects()) || tt('assistant.empty.thisProject', 'this project');
|
|
428
518
|
const list = [];
|
|
429
519
|
if (computerAvailable()) list.push({ id: 'computer', label: tt('assistant.empty.suggestComputer', 'Use my Mac to…'), template: tt('assistant.empty.templateComputer', 'Use my Mac to {caret}') });
|
|
430
520
|
list.push({ id: 'web', label: tt('assistant.empty.suggestWeb', 'Look up … on the web'), template: tt('assistant.empty.templateWeb', 'Look up {caret} on the web') });
|
|
@@ -455,8 +545,6 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
455
545
|
director = createMascotDirector({
|
|
456
546
|
getTarget: () => renderer.mascot(),
|
|
457
547
|
caret: () => composer.caretPoint(),
|
|
458
|
-
setEnabled: (on) => renderer.setRigsEnabled(on),
|
|
459
|
-
isVisible: () => visible(),
|
|
460
548
|
env: {
|
|
461
549
|
input: (fn) => { const offPointer = onPointer(fn); const offKey = onKey(fn); return () => { offPointer(); offKey(); }; },
|
|
462
550
|
lastInputAt,
|
|
@@ -481,7 +569,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
481
569
|
// Model · effort · permission mode · account sit in the composer row next to attach; project and MCP in the toolbar.
|
|
482
570
|
const picker = createBrainPicker({ brain: composer.el.querySelector('.asst-composer-brain'), context: barGroups.context }, {
|
|
483
571
|
t,
|
|
572
|
+
mascot: () => director,
|
|
484
573
|
getBrain: () => st.brain,
|
|
574
|
+
getRunning: () => st.running,
|
|
485
575
|
onChange: (next, meta) => applyBrain(next, meta),
|
|
486
576
|
onAddAccount: (provider) => openAccountsManager({ provider, ...accountHooks() }).openAdd(),
|
|
487
577
|
onManageAccounts: (provider) => openAccountsManager({ provider, ...accountHooks() }),
|
|
@@ -490,6 +580,11 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
490
580
|
getMenuAnchor: () => moreBtn,
|
|
491
581
|
// Composer fields open upward, toolbar fields downward.
|
|
492
582
|
menuPlacement: 'auto',
|
|
583
|
+
// Test density sits right after the permission mode; hidden on a server that does not know it.
|
|
584
|
+
getTestDensity: () => testDensityView(),
|
|
585
|
+
onTestDensity: (level) => setTestDensity(level),
|
|
586
|
+
// The registered projects arrived: their labels name the project in the fields and the "Fix … in" suggestion.
|
|
587
|
+
onProjects: () => { picker.render(); renderer.refreshEmpty(); },
|
|
493
588
|
});
|
|
494
589
|
|
|
495
590
|
const computerToggle = createComputerToggle(barGroups.computer, {
|
|
@@ -552,7 +647,10 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
552
647
|
function fetchUsage() {
|
|
553
648
|
const seq = ++usageFetchSeq;
|
|
554
649
|
getAssistantSessionUsage(sessionId).then((data) => {
|
|
555
|
-
if (!destroyed && seq === usageFetchSeq && data?.ok
|
|
650
|
+
if (!destroyed && seq === usageFetchSeq && data?.ok) {
|
|
651
|
+
if (data.budget) budgetController.receiveSession(data.budget);
|
|
652
|
+
if (data.usage) applyUsage(data.usage);
|
|
653
|
+
}
|
|
556
654
|
}).catch(() => { /* older servers do not expose usage */ });
|
|
557
655
|
}
|
|
558
656
|
function applyUsage(packet) {
|
|
@@ -740,6 +838,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
740
838
|
case 'assistant:mailbox':
|
|
741
839
|
renderer.appendMailbox(packet.items || packet.events || packet.mailbox || []);
|
|
742
840
|
return;
|
|
841
|
+
case 'assistant:budget':
|
|
842
|
+
budgetController.receiveSession(packet.budget);
|
|
843
|
+
return;
|
|
743
844
|
case 'assistant:cost':
|
|
744
845
|
applyCost(packet);
|
|
745
846
|
return;
|
|
@@ -812,7 +913,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
812
913
|
if (session != null) st.costUsd = session;
|
|
813
914
|
else if (!packet?.runId && num(packet?.costUsd) != null) st.costUsd = num(packet.costUsd);
|
|
814
915
|
if (num(packet?.dispatchUsd) != null) st.dispatchUsd = num(packet.dispatchUsd);
|
|
815
|
-
if (packet?.budget && typeof packet.budget === 'object')
|
|
916
|
+
if (packet?.budget && typeof packet.budget === 'object') budgetController.receiveSession(packet.budget);
|
|
816
917
|
if (typeof packet?.brainMetered === 'boolean') st.brainMetered = packet.brainMetered;
|
|
817
918
|
if (packet?.runId && num(packet.costUsd) != null) dock.patchRun(packet.runId, { costUsd: num(packet.costUsd) });
|
|
818
919
|
if (session != null) st.serverCost = true;
|
|
@@ -823,8 +924,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
823
924
|
function applySessionMeta(meta) {
|
|
824
925
|
if (!meta || typeof meta !== 'object') return;
|
|
825
926
|
if (meta.id && meta.id !== sessionId) return;
|
|
826
|
-
// The server's title names the conversation (history, notifications)
|
|
827
|
-
|
|
927
|
+
// The server's title names the conversation (history, notifications); the tab's name is the host's.
|
|
928
|
+
applyRecordName(meta);
|
|
828
929
|
if (meta.brain && typeof meta.brain === 'object') {
|
|
829
930
|
const next = normalizeBrain({ ...st.brain, ...meta.brain, provider: meta.brain.provider || meta.brain.brain || st.brain.provider });
|
|
830
931
|
if (JSON.stringify(next) !== JSON.stringify(st.brain)) { st.brain = next; picker.render(); host.setBrain?.(next); persist(); }
|
|
@@ -832,7 +933,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
832
933
|
const next = normalizeBrain({ ...st.brain, provider: meta.brain, model: meta.model ?? st.brain.model, effort: meta.effort ?? st.brain.effort, accountId: meta.accountId ?? st.brain.accountId, mcpProfile: meta.mcpProfile ?? st.brain.mcpProfile, project: meta.cwd ?? st.brain.project });
|
|
833
934
|
if (JSON.stringify(next) !== JSON.stringify(st.brain)) { st.brain = next; picker.render(); host.setBrain?.(next); persist(); }
|
|
834
935
|
}
|
|
835
|
-
if (meta.budget && typeof meta.budget === 'object')
|
|
936
|
+
if (meta.budget && typeof meta.budget === 'object') budgetController.receiveSession(meta.budget);
|
|
836
937
|
if (Number.isFinite(Number(meta.costUsd))) { st.costUsd = Number(meta.costUsd); renderCost(); }
|
|
837
938
|
if (meta.providerSessionId) st.providerSessionId = meta.providerSessionId;
|
|
838
939
|
if (meta.routing && typeof meta.routing === 'object') {
|
|
@@ -847,7 +948,11 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
847
948
|
if (meta.features && typeof meta.features === 'object') {
|
|
848
949
|
if (typeof meta.features.routing === 'boolean') st.features.routing = meta.features.routing;
|
|
849
950
|
if (typeof meta.features.computer === 'boolean') st.features.computer = meta.features.computer;
|
|
951
|
+
if (typeof meta.features.testDensity === 'boolean') st.features.testDensity = meta.features.testDensity;
|
|
952
|
+
if (typeof meta.features.rename === 'boolean') st.features.rename = meta.features.rename;
|
|
850
953
|
}
|
|
954
|
+
if ('testDensityExplicit' in meta) st.testDensity = normalizeTestDensity(meta.testDensityExplicit, null);
|
|
955
|
+
if (meta.testDensity) st.testDensityEffective = normalizeTestDensity(meta.testDensity);
|
|
851
956
|
if (typeof meta.computerUse === 'boolean') st.computerEffective = meta.computerUse;
|
|
852
957
|
if ('computerUseExplicit' in meta) st.computerUse = normalizeComputerMode(meta.computerUseExplicit, null);
|
|
853
958
|
// A WhatsApp conversation: what is in effect and why (not a per-conversation switch). null on the desktop.
|
|
@@ -872,6 +977,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
872
977
|
if (destroyed) return;
|
|
873
978
|
routeChip.setVisible(routingAvailable());
|
|
874
979
|
routeChip.render();
|
|
980
|
+
picker.render();
|
|
875
981
|
const showComputer = st.features.computer === true || st.desktopSupported === true;
|
|
876
982
|
computerToggle.set({
|
|
877
983
|
visible: showComputer,
|
|
@@ -920,7 +1026,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
920
1026
|
|
|
921
1027
|
function routeModeView() {
|
|
922
1028
|
const r = st.routing || {};
|
|
923
|
-
return { mode: normalizeRouteMode(st.routeMode, null) || r.mode || null, effectiveMode: r.effectiveMode, defaultMode: r.defaultMode };
|
|
1029
|
+
return { mode: normalizeRouteMode(st.routeMode, null) || r.mode || null, effectiveMode: r.effectiveMode, defaultMode: r.defaultMode, askBelow: r.askBelow };
|
|
924
1030
|
}
|
|
925
1031
|
|
|
926
1032
|
function setRouteMode(mode, { quiet = false } = {}) {
|
|
@@ -943,6 +1049,31 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
943
1049
|
if (last && last !== st.routing.effectiveMode) setRouteMode(last, { quiet: true });
|
|
944
1050
|
}
|
|
945
1051
|
|
|
1052
|
+
/** The level the composer shows: this session's, else the server's; null while the server does not know the setting. */
|
|
1053
|
+
function testDensityView() {
|
|
1054
|
+
if (st.features.testDensity !== true) return null;
|
|
1055
|
+
return normalizeTestDensity(st.testDensity || st.testDensityEffective);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
function setTestDensity(level, { quiet = false } = {}) {
|
|
1059
|
+
const next = normalizeTestDensity(level);
|
|
1060
|
+
st.testDensity = next;
|
|
1061
|
+
st.testDensityEffective = next;
|
|
1062
|
+
picker.render();
|
|
1063
|
+
persist();
|
|
1064
|
+
sock.send({ type: 'set_test_density', level: next });
|
|
1065
|
+
try { storage.setItem(KEYS.ASSISTANT_TEST_DENSITY, next); } catch { /* storage unavailable */ }
|
|
1066
|
+
if (!quiet) renderer.appendStatus(tt('assistant.status.testDensity', 'Test density: {level}', { level: testDensityLabel(next, t) }));
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/** Fresh tabs inherit the last-used test density (like the route mode). */
|
|
1070
|
+
function applyLastUsedTestDensity() {
|
|
1071
|
+
if (saved || st.features.testDensity !== true || st.testDensity != null) return;
|
|
1072
|
+
let last = null;
|
|
1073
|
+
try { last = normalizeTestDensity(storage.getItem(KEYS.ASSISTANT_TEST_DENSITY), null); } catch { last = null; }
|
|
1074
|
+
if (last && last !== testDensityView()) setTestDensity(last, { quiet: true });
|
|
1075
|
+
}
|
|
1076
|
+
|
|
946
1077
|
/** A WhatsApp conversation's Computer switch says why it is what it is (the status line of the transcript). */
|
|
947
1078
|
function explainRemoteComputer() {
|
|
948
1079
|
const view = computerRemoteView(st.computerRemote, t);
|
|
@@ -1170,6 +1301,10 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1170
1301
|
request_id: `q-${Date.now().toString(36)}-${(querySeq += 1)}`,
|
|
1171
1302
|
};
|
|
1172
1303
|
if (payload.images?.length) msg.images = payload.images.map(i => ({ base64: i.base64, mediaType: i.mediaType }));
|
|
1304
|
+
// The typed words apart from long pastes (asst-composer.js): what the server may learn about the user from.
|
|
1305
|
+
if (typeof payload.typed === 'string') msg.typed = payload.typed;
|
|
1306
|
+
// The greeting shown when this conversation opened: echoed once, with the first prompt.
|
|
1307
|
+
if (greetingId) { msg.greetingId = greetingId; greetingId = null; forgetGreeting('assistant', sessionId); }
|
|
1173
1308
|
const row = renderer.appendUser(payload.display ?? payload.text, { images: payload.images, files: [...(payload.files || []), ...(payload.uploads || [])], memories: payload.memories });
|
|
1174
1309
|
director.sent();
|
|
1175
1310
|
sentQueries.set(msg.request_id, { payload, row });
|
|
@@ -1178,7 +1313,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1178
1313
|
setRunning(true);
|
|
1179
1314
|
sock.send(msg);
|
|
1180
1315
|
// Name the conversation like the server will (its first prompt) — not the tab.
|
|
1181
|
-
if (!st.sessionTitle || /^(?:SynaBun|Assistant(?: \d+)?)$/.test(st.sessionTitle)) {
|
|
1316
|
+
if (!st.userRenamed && (!st.sessionTitle || /^(?:SynaBun|Assistant(?: \d+)?)$/.test(st.sessionTitle))) {
|
|
1182
1317
|
const guess = String(payload.text || '').split('\n')[0].trim().slice(0, 60);
|
|
1183
1318
|
if (guess) st.sessionTitle = guess;
|
|
1184
1319
|
}
|
|
@@ -1222,7 +1357,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1222
1357
|
return true;
|
|
1223
1358
|
case 'resume':
|
|
1224
1359
|
if (parsed.sessionId) resume(parsed.sessionId);
|
|
1225
|
-
else sock.send({ type: 'list_sessions' });
|
|
1360
|
+
else renames.settled().then(() => { if (!destroyed) sock.send({ type: 'list_sessions' }); }); // a rename on its way is in the list
|
|
1226
1361
|
return true;
|
|
1227
1362
|
case 'stop':
|
|
1228
1363
|
if (parsed.all) await stopAll();
|
|
@@ -1357,23 +1492,24 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1357
1492
|
picker.render();
|
|
1358
1493
|
host.setBrain?.(st.brain);
|
|
1359
1494
|
persist();
|
|
1495
|
+
if (before.project !== st.brain.project) renderer.refreshEmpty();
|
|
1360
1496
|
if (meta.silent) return;
|
|
1361
1497
|
if (meta.requiresSwitch) {
|
|
1362
1498
|
const b = st.brain;
|
|
1363
|
-
sock.send({ type: 'switch_brain', brain: b.provider, model: b.model || undefined, effort: b.effort || undefined, accountId: b.accountId || undefined, mcpProfile: b.mcpProfile || undefined, cwd: b.project || undefined, agent: b.agent || undefined, permissionMode: b.permissionMode, planMode: b.planMode === true });
|
|
1499
|
+
if (!meta.restartBrain) sock.send({ type: 'switch_brain', brain: b.provider, model: b.model || undefined, effort: b.effort || undefined, accountId: b.accountId || undefined, mcpProfile: b.mcpProfile || undefined, cwd: b.project || undefined, agent: b.agent || undefined, permissionMode: b.permissionMode, planMode: b.planMode === true });
|
|
1364
1500
|
renderer.appendDivider(`${tt('assistant.status.brain', 'brain')} → ${brainLabel(b)}`);
|
|
1365
1501
|
renderer.forgetSeen(); // the new brain's ids start over
|
|
1366
1502
|
} else if (before.permissionMode !== st.brain.permissionMode || !!before.planMode !== !!st.brain.planMode) {
|
|
1367
1503
|
sock.send({ type: 'set_permission_mode', mode: st.brain.permissionMode, planMode: st.brain.planMode === true });
|
|
1368
1504
|
}
|
|
1369
|
-
|
|
1370
|
-
patchAssistantSession(sessionId, { brain: st.brain }).catch((err) => {
|
|
1505
|
+
patchAssistantSession(sessionId, { brain: st.brain, ...(meta.restartBrain ? { restartBrain: true } : {}) }).catch((err) => {
|
|
1371
1506
|
// Refused (a model disabled in Assistant → Models): undo, so the refused
|
|
1372
1507
|
// brain is neither shown nor saved as the last used one — the next start
|
|
1373
1508
|
// would ask for it again. A brain switch already got the error on the socket.
|
|
1374
1509
|
if (err?.code !== 'MODEL_DISABLED' || destroyed || st.brain !== applied) return;
|
|
1375
1510
|
st.brain = before;
|
|
1376
1511
|
picker.render();
|
|
1512
|
+
if (applied.project !== before.project) renderer.refreshEmpty();
|
|
1377
1513
|
host.setBrain?.(before);
|
|
1378
1514
|
persist();
|
|
1379
1515
|
if (!meta.requiresSwitch) renderer.appendError(err.message || String(err));
|
|
@@ -1384,7 +1520,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1384
1520
|
async function focusRun(runId) {
|
|
1385
1521
|
if (!runId) return;
|
|
1386
1522
|
focusAssistantRun(runId).catch(() => {});
|
|
1387
|
-
|
|
1523
|
+
// The user's own Focus: a run whose tab was closed opens again.
|
|
1524
|
+
try { await focusNativeLoopRun(runId, { reopen: true }); } catch { /* router not ready */ }
|
|
1388
1525
|
}
|
|
1389
1526
|
|
|
1390
1527
|
async function stopRun(runId) {
|
|
@@ -1544,7 +1681,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1544
1681
|
items.push({ id: 'compact', label: tt('assistant.topbar.compact', 'Compact context'), onSelect: () => compact() });
|
|
1545
1682
|
if (routingAvailable()) items.push({ id: 'routes', label: tt('assistant.routes.editor', 'Model routes…'), icon: ICON_ROUTE, onSelect: () => openRoutes() });
|
|
1546
1683
|
items.push({ id: 'models', label: tt('assistant.models.menu', 'Models…'), onSelect: () => openModels() });
|
|
1547
|
-
items.push({ id: 'budget', label: tt('assistant.budget.menu', 'Budget…'), desc: st.budget
|
|
1684
|
+
items.push({ id: 'budget', label: tt('assistant.budget.menu', 'Budget…'), desc: budgetSpendText(st.budget, { t }), onSelect: () => openBudget() });
|
|
1548
1685
|
items.push({ id: 'clear', label: tt('assistant.topbar.clear', 'Clear transcript'), onSelect: () => clearTranscript() });
|
|
1549
1686
|
items.push({ id: 'help', label: tt('assistant.topbar.helpShortcuts', 'Help & shortcuts'), onSelect: () => openHelp() });
|
|
1550
1687
|
items.push({ kind: 'separator' });
|
|
@@ -1598,12 +1735,12 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1598
1735
|
}
|
|
1599
1736
|
}
|
|
1600
1737
|
|
|
1601
|
-
// Assistant → Budget:
|
|
1738
|
+
// Assistant → Budget: one saved amount plus the current session's live split.
|
|
1602
1739
|
function openBudget() {
|
|
1603
1740
|
openBudgetEditor({
|
|
1604
1741
|
t,
|
|
1605
1742
|
sessionId,
|
|
1606
|
-
|
|
1743
|
+
controller: budgetController,
|
|
1607
1744
|
onToast: toast,
|
|
1608
1745
|
onSaved: (res) => { if (res?.session) { st.budget = res.session; renderCost(); } },
|
|
1609
1746
|
}).catch(() => {});
|
|
@@ -1615,6 +1752,7 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1615
1752
|
providerIcon,
|
|
1616
1753
|
getBrain: () => st.brain,
|
|
1617
1754
|
brainName: () => brainModelLabel(),
|
|
1755
|
+
getRouting: () => st.routing,
|
|
1618
1756
|
onToast: toast,
|
|
1619
1757
|
onSaved: (routing) => {
|
|
1620
1758
|
if (routing?.defaultMode) {
|
|
@@ -1667,7 +1805,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1667
1805
|
e.stopPropagation();
|
|
1668
1806
|
if (isMenuOpen(sessionsBtn)) { closeMenu(); return; }
|
|
1669
1807
|
let list = [];
|
|
1670
|
-
|
|
1808
|
+
// A rename still on its way to a record is in the list this opens: this tab's, another tab's, a closed one's.
|
|
1809
|
+
try { await renames.settled(); list = await listAssistantSessions(); } catch { /* server not ready */ }
|
|
1671
1810
|
if (!destroyed) showSessionsMenu(list);
|
|
1672
1811
|
});
|
|
1673
1812
|
function toggleDock() {
|
|
@@ -1710,6 +1849,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1710
1849
|
|
|
1711
1850
|
// ── Bus ──
|
|
1712
1851
|
unsubs.push(on('sync:assistant:session-updated', (msg) => { const s = msg?.session || msg; if (s?.id === sessionId) applySessionMeta(s); }));
|
|
1852
|
+
unsubs.push(on('sync:assistant:budget', (msg) => { if ((msg?.assistantSessionId || msg?.sessionId) === sessionId) budgetController.receiveSession(msg.budget); }));
|
|
1853
|
+
unsubs.push(on('sync:assistant:budget-changed', () => budgetController.load()));
|
|
1713
1854
|
unsubs.push(on('sync:assistant:cost', (msg) => { if (!msg?.assistantSessionId || msg.assistantSessionId === sessionId) applyCost(msg); }));
|
|
1714
1855
|
unsubs.push(on('sync:assistant:mailbox', (msg) => { if (msg?.assistantSessionId === sessionId) renderer.appendMailbox(msg.items || msg.events || []); }));
|
|
1715
1856
|
unsubs.push(on('sync:assistant:permission-request', (msg) => {
|
|
@@ -1755,6 +1896,8 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1755
1896
|
if (force) renderer.clear();
|
|
1756
1897
|
if (Array.isArray(transcript) && transcript.length) renderer.renderTranscript(transcript);
|
|
1757
1898
|
renderer.showEmpty(); // no-op once the transcript has rows
|
|
1899
|
+
// A conversation with nothing in it yet may be greeted; a restored one never is.
|
|
1900
|
+
if (Array.isArray(transcript) && !transcript.length && !(Number(meta?.turns) > 0)) renderer.offerGreeting();
|
|
1758
1901
|
for (const run of (Array.isArray(data?.runs) ? data.runs : [])) dock.upsertRun(run);
|
|
1759
1902
|
} catch (err) {
|
|
1760
1903
|
console.warn('[assistant] could not restore the conversation', err);
|
|
@@ -1774,9 +1917,11 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1774
1917
|
if (destroyed) return;
|
|
1775
1918
|
probeFeatures();
|
|
1776
1919
|
applyLastUsedRouteMode();
|
|
1920
|
+
applyLastUsedTestDensity();
|
|
1777
1921
|
sock.connect();
|
|
1778
1922
|
});
|
|
1779
1923
|
fetchUsage();
|
|
1924
|
+
budgetController.load();
|
|
1780
1925
|
|
|
1781
1926
|
const controller = {
|
|
1782
1927
|
sessionId,
|
|
@@ -1784,7 +1929,15 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1784
1929
|
getBrain: () => st.brain,
|
|
1785
1930
|
setBrain: (b, meta) => applyBrain(b, meta || { requiresSwitch: false, field: 'external' }),
|
|
1786
1931
|
getTitle: () => st.title,
|
|
1787
|
-
|
|
1932
|
+
/** A rename (the host's tab, float header or ✎), or with `restored` the name a host puts back when it mounts a renamed tab. */
|
|
1933
|
+
setTitle: (text, { restored = false } = {}) => {
|
|
1934
|
+
const next = String(text || '').trim();
|
|
1935
|
+
if (!next) return;
|
|
1936
|
+
const from = restored ? next : st.title; // a restored name has none to go back to
|
|
1937
|
+
st.userRenamed = next !== defaultTitle; // the default name is no rename
|
|
1938
|
+
setTitle(next);
|
|
1939
|
+
saveRename(from, restored);
|
|
1940
|
+
},
|
|
1788
1941
|
isRunning: () => st.running,
|
|
1789
1942
|
isConnected: () => st.connected,
|
|
1790
1943
|
send: (text, extra = {}) => submit({ text: String(text ?? ''), images: extra.images || [], files: extra.files || [], uploads: extra.uploads || [], memories: extra.memories || [] }),
|
|
@@ -1799,12 +1952,12 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1799
1952
|
insertText: (text) => composer.insertText(text),
|
|
1800
1953
|
/** A file path from the explorer ("send to panel") → the composer, as inline code. */
|
|
1801
1954
|
attachPath: (path) => { if (path) { composer.insertText(`\`${path}\` `); composer.focus(); } },
|
|
1802
|
-
/** The server's name for the conversation (
|
|
1955
|
+
/** The server's name for the conversation (the user's rename, else the first prompt): tooltips. */
|
|
1803
1956
|
getConversationTitle: () => st.sessionTitle || '',
|
|
1804
1957
|
getStatus: () => st.status || 'idle',
|
|
1805
1958
|
focus: () => composer.focus(),
|
|
1806
|
-
onShown: () => { syncTabLabel(); renderer.scrollEnd(true); if (visible()) composer.focus(); director.touch();
|
|
1807
|
-
onHidden: () => { closeMenu(); picker.close(); director.stopTyping();
|
|
1959
|
+
onShown: () => { syncTabLabel(); renderer.scrollEnd(true); if (visible()) composer.focus(); director.touch(); },
|
|
1960
|
+
onHidden: () => { closeMenu(); picker.close(); director.stopTyping(); },
|
|
1808
1961
|
counts: () => dock.counts(),
|
|
1809
1962
|
destroy: ({ closeServer = false } = {}) => {
|
|
1810
1963
|
if (destroyed) return;
|
|
@@ -1812,6 +1965,9 @@ export function mountAssistant(viewport, { sessionId, brain, host = {}, session
|
|
|
1812
1965
|
if (persistTimer) { clearTimeout(persistTimer); persistTimer = null; }
|
|
1813
1966
|
clearTimeout(announceTimer);
|
|
1814
1967
|
if (replay) { clearTimeout(replay.timer); replay = null; }
|
|
1968
|
+
closeBudgetEditor(budgetController);
|
|
1969
|
+
emptyBudgetChip?.destroy();
|
|
1970
|
+
budgetController.destroy();
|
|
1815
1971
|
director.destroy();
|
|
1816
1972
|
usage.destroy();
|
|
1817
1973
|
clearTimeout(limitTimer);
|