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
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
// ═══════════════════════════════════════════
|
|
2
|
-
// SynaBun — Assistant
|
|
2
|
+
// SynaBun — Assistant budget (the one setting · list prices · Codex usage)
|
|
3
3
|
// ═══════════════════════════════════════════
|
|
4
4
|
//
|
|
5
|
-
//
|
|
5
|
+
// One setting for every Assistant session: { unlimited, amountUsd, version }.
|
|
6
|
+
// It lives in assistant-config.json under `budget` and is read live. Every new
|
|
7
|
+
// session starts with it; a change applies to the open sessions at once.
|
|
6
8
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// unlimited false the session may spend `amountUsd` (brain + every run, at
|
|
10
|
+
// list price). The warning is fixed at 80 %; at 100 % the
|
|
11
|
+
// session stops. assistant-budget-split.js divides what is
|
|
12
|
+
// left across the providers (Jev or a fixed heuristic).
|
|
13
|
+
// unlimited true No limit: no caps, no shares, no Jev call. Spend is still
|
|
14
|
+
// metered.
|
|
12
15
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
16
|
+
// Older files kept five caps in `limits` (default/largest per run, session
|
|
17
|
+
// warning, session hard cap) and `brains.claude.maxBudgetUsd`. They are read
|
|
18
|
+
// once (migrateBudget): the session hard cap becomes the amount, the rest is
|
|
19
|
+
// dropped.
|
|
17
20
|
//
|
|
18
21
|
// Prices: Claude and OpenCode report what a turn cost. Codex reports tokens
|
|
19
22
|
// only, so its runs are priced from the list-price table (assistant-pricing.js,
|
|
@@ -28,17 +31,15 @@ import { homedir } from 'node:os';
|
|
|
28
31
|
import { join } from 'node:path';
|
|
29
32
|
import { PRICING_SOURCES, PRICING_VERIFIED, listPrice, pricingRows } from './assistant-pricing.js';
|
|
30
33
|
|
|
31
|
-
export const BUDGET_FIELDS = Object.freeze(['defaultRunUsd', 'maxRunUsd', 'sessionWarnUsd', 'sessionHardUsd', 'brainUsd']);
|
|
32
|
-
export const BUDGET_DEFAULTS = Object.freeze({ defaultRunUsd: 5, maxRunUsd: 25, sessionWarnUsd: 8, sessionHardUsd: 25, brainUsd: 10 });
|
|
33
34
|
export const BUDGET_MIN_USD = 0.1;
|
|
34
35
|
export const BUDGET_MAX_USD = 1000;
|
|
35
|
-
export const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
export const BUDGET_DEFAULT_USD = 25;
|
|
37
|
+
/** The one warning comes at this share of the budget. */
|
|
38
|
+
export const BUDGET_WARN_RATIO = 0.8;
|
|
39
|
+
export const BUDGET_FIELDS = Object.freeze(['unlimited', 'amountUsd']);
|
|
40
|
+
export const BUDGET_DEFAULTS = Object.freeze({ unlimited: false, amountUsd: BUDGET_DEFAULT_USD });
|
|
41
|
+
/** The keys of the old five-cap budget (`limits.*`, `brains.claude.maxBudgetUsd`), dropped by the migration. */
|
|
42
|
+
export const LEGACY_LIMIT_KEYS = Object.freeze(['defaultBudgetUsd', 'maxBudgetUsd', 'sessionWarnUsd', 'sessionSoftBudgetUsd', 'sessionHardBudgetUsd', 'budgetVersion', 'budgetUpdatedAt']);
|
|
42
43
|
|
|
43
44
|
function num(value) {
|
|
44
45
|
if (value === null || value === undefined || value === '' || typeof value === 'boolean') return null;
|
|
@@ -47,6 +48,8 @@ function num(value) {
|
|
|
47
48
|
}
|
|
48
49
|
function obj(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }
|
|
49
50
|
export function roundUsd(value) { return Math.round(Number(value) * 100) / 100; }
|
|
51
|
+
/** Whole cents, never more than `value` (a budget never rounds up). */
|
|
52
|
+
export function floorCents(value) { return Math.max(0, Math.floor(Number(value) * 100 + 1e-6)); }
|
|
50
53
|
|
|
51
54
|
function budgetError(field, message) {
|
|
52
55
|
const error = new Error(message);
|
|
@@ -55,109 +58,105 @@ function budgetError(field, message) {
|
|
|
55
58
|
error.status = 400;
|
|
56
59
|
return error;
|
|
57
60
|
}
|
|
61
|
+
const inRange = (n) => n !== null && n >= BUDGET_MIN_USD && n <= BUDGET_MAX_USD;
|
|
58
62
|
|
|
59
|
-
/**
|
|
60
|
-
export function
|
|
63
|
+
/** Whether a config file still carries the old five-cap budget and no `budget` of its own. */
|
|
64
|
+
export function needsBudgetMigration(raw = {}) {
|
|
65
|
+
if (raw?.budget && typeof raw.budget === 'object' && !Array.isArray(raw.budget)) return false;
|
|
61
66
|
const limits = obj(raw?.limits);
|
|
62
|
-
|
|
63
|
-
const warn = num(limits.sessionWarnUsd);
|
|
64
|
-
return {
|
|
65
|
-
defaultRunUsd: num(limits.defaultBudgetUsd),
|
|
66
|
-
maxRunUsd: num(limits.maxBudgetUsd),
|
|
67
|
-
sessionWarnUsd: warn ?? (soft !== null ? roundUsd(soft * 0.8) : null),
|
|
68
|
-
sessionHardUsd: num(limits.sessionHardBudgetUsd),
|
|
69
|
-
brainUsd: num(obj(obj(raw?.brains).claude).maxBudgetUsd),
|
|
70
|
-
};
|
|
67
|
+
return LEGACY_LIMIT_KEYS.some((key) => Object.hasOwn(limits, key)) || Object.hasOwn(obj(obj(raw?.brains).claude), 'maxBudgetUsd');
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
/**
|
|
74
|
-
* The
|
|
75
|
-
*
|
|
71
|
+
* The budget a config file holds: its `budget` when it has one, else what an
|
|
72
|
+
* older file says (the saved session hard cap, else the default).
|
|
73
|
+
* → { unlimited, amountUsd, version, updatedAt, source: 'saved' | 'migrated' | 'default' }
|
|
76
74
|
*/
|
|
77
|
-
export function
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
75
|
+
export function effectiveBudget(raw = {}) {
|
|
76
|
+
const stored = raw?.budget && typeof raw.budget === 'object' && !Array.isArray(raw.budget) ? raw.budget : null;
|
|
77
|
+
if (stored) {
|
|
78
|
+
const amount = num(stored.amountUsd);
|
|
79
|
+
return {
|
|
80
|
+
unlimited: stored.unlimited === true,
|
|
81
|
+
amountUsd: inRange(amount) ? roundUsd(amount) : BUDGET_DEFAULT_USD,
|
|
82
|
+
version: Math.max(0, Math.floor(num(stored.version) || 0)),
|
|
83
|
+
updatedAt: typeof stored.updatedAt === 'string' ? stored.updatedAt : null,
|
|
84
|
+
source: 'saved',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const limits = obj(raw?.limits);
|
|
88
|
+
const hard = num(limits.sessionHardBudgetUsd);
|
|
89
|
+
const migrated = needsBudgetMigration(raw);
|
|
90
|
+
return {
|
|
91
|
+
unlimited: false,
|
|
92
|
+
// The saved session hard cap is the amount (a hand edit outside the bounds is brought inside them).
|
|
93
|
+
amountUsd: hard !== null && hard > 0 ? roundUsd(Math.min(BUDGET_MAX_USD, Math.max(BUDGET_MIN_USD, hard))) : BUDGET_DEFAULT_USD,
|
|
94
|
+
version: Math.max(0, Math.floor(num(limits.budgetVersion) || 0)),
|
|
95
|
+
updatedAt: typeof limits.budgetUpdatedAt === 'string' ? limits.budgetUpdatedAt : null,
|
|
96
|
+
source: migrated ? 'migrated' : 'default',
|
|
97
|
+
};
|
|
83
98
|
}
|
|
84
99
|
|
|
85
|
-
/**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const sources = {};
|
|
93
|
-
for (const field of BUDGET_FIELDS) {
|
|
94
|
-
const value = stored[field];
|
|
95
|
-
const ok = value !== null && value >= BUDGET_MIN_USD && value <= BUDGET_MAX_USD;
|
|
96
|
-
out[field] = ok ? roundUsd(value) : BUDGET_DEFAULTS[field];
|
|
97
|
-
sources[field] = !ok ? 'default' : (field === 'sessionWarnUsd' && num(obj(raw?.limits).sessionWarnUsd) === null ? 'legacy' : 'saved');
|
|
100
|
+
/** A config object without the old five-cap keys (every other key kept). */
|
|
101
|
+
export function withoutLegacyBudget(raw = {}) {
|
|
102
|
+
const base = { ...obj(raw) };
|
|
103
|
+
if (base.limits && typeof base.limits === 'object') {
|
|
104
|
+
const limits = { ...base.limits };
|
|
105
|
+
for (const key of LEGACY_LIMIT_KEYS) delete limits[key];
|
|
106
|
+
base.limits = limits;
|
|
98
107
|
}
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
repairs.push(broken.field);
|
|
104
|
-
if (broken.field === 'maxRunUsd') out.maxRunUsd = out.sessionHardUsd;
|
|
105
|
-
else if (broken.field === 'defaultRunUsd') out.defaultRunUsd = out.maxRunUsd;
|
|
106
|
-
else if (broken.field === 'sessionWarnUsd') out.sessionWarnUsd = roundUsd(out.sessionHardUsd * 0.8);
|
|
107
|
-
else if (broken.field === 'brainUsd') out.brainUsd = out.sessionHardUsd;
|
|
108
|
+
const brains = obj(base.brains);
|
|
109
|
+
if (brains.claude && typeof brains.claude === 'object' && Object.hasOwn(brains.claude, 'maxBudgetUsd')) {
|
|
110
|
+
const { maxBudgetUsd: _cap, ...claude } = brains.claude;
|
|
111
|
+
base.brains = { ...brains, claude };
|
|
108
112
|
}
|
|
109
|
-
return
|
|
113
|
+
return base;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
116
|
/**
|
|
113
|
-
* Validate a patch
|
|
114
|
-
*
|
|
115
|
-
*
|
|
117
|
+
* Validate a patch ({ unlimited?, amountUsd? }) against the current budget and
|
|
118
|
+
* return the full next one. Throws BUDGET_INVALID (status 400, `field`) on an
|
|
119
|
+
* unknown field, a bad value or an empty patch. Amounts are kept in cents.
|
|
116
120
|
*/
|
|
117
121
|
export function validateBudgetPatch(patch = {}, current = BUDGET_DEFAULTS) {
|
|
118
122
|
if (!patch || typeof patch !== 'object' || Array.isArray(patch)) throw budgetError('budget', 'budget must be an object');
|
|
119
|
-
const next = {};
|
|
120
|
-
|
|
123
|
+
const next = { unlimited: current?.unlimited === true, amountUsd: inRange(num(current?.amountUsd)) ? roundUsd(current.amountUsd) : BUDGET_DEFAULT_USD };
|
|
124
|
+
let changed = 0;
|
|
121
125
|
for (const [field, value] of Object.entries(patch)) {
|
|
122
|
-
if (!BUDGET_FIELDS.includes(field)) throw budgetError(field, `Unknown budget field "${field}"
|
|
123
|
-
|
|
126
|
+
if (!BUDGET_FIELDS.includes(field)) throw budgetError(field, `Unknown budget field "${field}". Send unlimited and/or amountUsd.`);
|
|
127
|
+
changed += 1;
|
|
128
|
+
if (field === 'unlimited') {
|
|
129
|
+
if (typeof value !== 'boolean') throw budgetError('unlimited', 'unlimited must be true or false');
|
|
130
|
+
next.unlimited = value;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
124
133
|
const n = typeof value === 'number' || (typeof value === 'string' && value.trim() !== '') ? Number(value) : NaN;
|
|
125
|
-
if (!Number.isFinite(n)) throw budgetError(
|
|
126
|
-
if (n < BUDGET_MIN_USD || n > BUDGET_MAX_USD) throw budgetError(
|
|
127
|
-
next
|
|
134
|
+
if (!Number.isFinite(n)) throw budgetError('amountUsd', 'The budget must be an amount in US dollars');
|
|
135
|
+
if (n < BUDGET_MIN_USD || n > BUDGET_MAX_USD) throw budgetError('amountUsd', `The budget must be between $${BUDGET_MIN_USD.toFixed(2)} and $${BUDGET_MAX_USD}`);
|
|
136
|
+
next.amountUsd = roundUsd(n);
|
|
128
137
|
}
|
|
129
|
-
|
|
130
|
-
if (broken) throw budgetError(broken.field, broken.message);
|
|
138
|
+
if (!changed) throw budgetError('budget', 'Nothing to change: send unlimited and/or amountUsd');
|
|
131
139
|
return next;
|
|
132
140
|
}
|
|
133
141
|
|
|
134
|
-
/** A config object with `budget` written
|
|
142
|
+
/** A config object with `budget` written (the old caps dropped, every other key kept). */
|
|
135
143
|
export function writeBudgetInto(raw = {}, budget, { version = 0, at = new Date().toISOString() } = {}) {
|
|
136
|
-
const base = obj(raw);
|
|
137
|
-
const brains = obj(base.brains);
|
|
138
144
|
return {
|
|
139
|
-
...
|
|
140
|
-
|
|
141
|
-
...obj(base.limits),
|
|
142
|
-
defaultBudgetUsd: budget.defaultRunUsd,
|
|
143
|
-
maxBudgetUsd: budget.maxRunUsd,
|
|
144
|
-
sessionWarnUsd: budget.sessionWarnUsd,
|
|
145
|
-
sessionHardBudgetUsd: budget.sessionHardUsd,
|
|
146
|
-
budgetVersion: version,
|
|
147
|
-
budgetUpdatedAt: at,
|
|
148
|
-
},
|
|
149
|
-
brains: { ...brains, claude: { ...obj(brains.claude), maxBudgetUsd: budget.brainUsd } },
|
|
145
|
+
...withoutLegacyBudget(raw),
|
|
146
|
+
budget: { unlimited: budget.unlimited === true, amountUsd: roundUsd(budget.amountUsd), version, updatedAt: at },
|
|
150
147
|
};
|
|
151
148
|
}
|
|
152
149
|
|
|
153
|
-
/**
|
|
154
|
-
export function
|
|
150
|
+
/** What the Assistant's budget limits at `spentUsd` (null amounts under No limit). */
|
|
151
|
+
export function budgetLimitsAt(budget, spentUsd = 0) {
|
|
152
|
+
if (!budget || budget.unlimited) return { unlimited: true, amountUsd: null, warnAtUsd: null, leftUsd: null, warned: false, exceeded: false };
|
|
153
|
+
const amount = Number(budget.amountUsd) || 0;
|
|
154
|
+
const spent = Math.max(0, Number(spentUsd) || 0);
|
|
155
|
+
const warnAt = roundUsd(amount * BUDGET_WARN_RATIO);
|
|
155
156
|
return {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
sessionHardBudgetUsd: budget.sessionHardUsd,
|
|
160
|
-
brainBudgetUsd: budget.brainUsd,
|
|
157
|
+
unlimited: false, amountUsd: amount, warnAtUsd: warnAt,
|
|
158
|
+
leftUsd: Math.max(0, Number((amount - spent).toFixed(8))),
|
|
159
|
+
warned: spent >= warnAt, exceeded: spent >= amount,
|
|
161
160
|
};
|
|
162
161
|
}
|
|
163
162
|
|
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
// planner) and the router can pick cheap-first, send image work to a model that
|
|
16
16
|
// can see, and image / video creation to a model that makes them. Everything
|
|
17
17
|
// I/O is injected; parsers are pure.
|
|
18
|
+
//
|
|
19
|
+
// Which models exist is never decided here. Each provider's rows are the list
|
|
20
|
+
// its own model selector reads (/api/claude/models, /api/codex/models,
|
|
21
|
+
// /api/opencode/providers/full); the static tables (prices, tier hints, the 1M
|
|
22
|
+
// rule) only describe a row. A model the provider lists and no table knows is
|
|
23
|
+
// offered with what is unknown left unknown; a model only a table knows is
|
|
24
|
+
// never offered. `sources` says how each provider's list was obtained.
|
|
18
25
|
|
|
19
26
|
import { sortEfforts, variantNames } from './effort-levels.js';
|
|
20
27
|
import { listPrice } from './assistant-pricing.js';
|
|
@@ -82,6 +89,43 @@ function resolvedKey(row) {
|
|
|
82
89
|
return `${baseModelId(row.upstream)}${contextSuffix(row.id)}`.toLowerCase();
|
|
83
90
|
}
|
|
84
91
|
|
|
92
|
+
// ── moving aliases ───────────────────────────────────────────────────────────
|
|
93
|
+
// "opus", "haiku", "default" name whatever model Claude Code points them at, and
|
|
94
|
+
// that changes with a CLI update. A hidden alias is pinned to the model it named
|
|
95
|
+
// when it was hidden (lib/assistant-hidden-models.js); these say what a row names.
|
|
96
|
+
|
|
97
|
+
/** A model id without its snapshot date ("claude-haiku-4-5-20251001", "…@20251001"), lower-case: what makes two ids one model. */
|
|
98
|
+
export function modelIdentity(id) {
|
|
99
|
+
return baseModelId(id).trim().toLowerCase().replace(/[-@]\d{8}$/, '');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* What a moving alias row names right now: { model, variant } ("opus[1m]" →
|
|
104
|
+
* { model: 'claude-opus-5-5', variant: '1m' }), lower-case. null for a row that
|
|
105
|
+
* is its own model (a concrete id, every Codex row), for OpenCode (its
|
|
106
|
+
* `upstream` is the provider, not a model) and whenever the provider did not
|
|
107
|
+
* name a versioned model behind the alias (the static stand-in list, an alias
|
|
108
|
+
* resolving to another alias): nothing is guessed.
|
|
109
|
+
*/
|
|
110
|
+
export function aliasTargetOf(row) {
|
|
111
|
+
if (!row?.id || row.provider === 'opencode' || String(row.id).includes('/')) return null;
|
|
112
|
+
const model = baseModelId(row.upstream || '').trim().toLowerCase();
|
|
113
|
+
if (!model || !/\d/.test(model) || modelIdentity(model) === modelIdentity(row.id)) return null;
|
|
114
|
+
return { model, variant: contextVariantOf(row.id) };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The id of the model a pin or an alias target names, context included ("claude-opus-5-5[1m]"), lower-case. */
|
|
118
|
+
export function pinnedModelId(pin) {
|
|
119
|
+
const model = baseModelId(pin?.model || '').trim().toLowerCase();
|
|
120
|
+
return model ? `${model}${pin.variant ? `[${String(pin.variant).toLowerCase()}]` : ''}` : '';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Do a pin and an alias target name the same model at the same context window? (A new snapshot date is the same model.) */
|
|
124
|
+
export function sameModelPin(a, b) {
|
|
125
|
+
return !!a && !!b && !!modelIdentity(a.model) && modelIdentity(a.model) === modelIdentity(b.model)
|
|
126
|
+
&& String(a.variant || '').toLowerCase() === String(b.variant || '').toLowerCase();
|
|
127
|
+
}
|
|
128
|
+
|
|
85
129
|
/**
|
|
86
130
|
* Every Claude row that can run at 1M gets a "[1m]" sibling right after it,
|
|
87
131
|
* unless the CLI already lists one: the CLI accepts "<id>[1m]" for those
|
|
@@ -305,6 +349,9 @@ export function rowIsHidden(catalog, row) {
|
|
|
305
349
|
* "opus[1m]"). Context variants hide independently: hiding "opus" leaves
|
|
306
350
|
* "opus[1m]" alone and the other way round; same for "gpt-x" / "gpt-x[extended]".
|
|
307
351
|
* OpenCode: the full "provider/model" id, or a bare model id.
|
|
352
|
+
* An entry with a pin record (catalog.hiddenPins: an alias hidden as the model
|
|
353
|
+
* it named, or that model's own id once the alias moved on) hides its own row
|
|
354
|
+
* only: it never reaches another alias through the resolved model.
|
|
308
355
|
*/
|
|
309
356
|
export function hiddenModelId(catalog, provider, id, { row = null } = {}) {
|
|
310
357
|
if (!provider || !id) return null;
|
|
@@ -322,7 +369,8 @@ export function hiddenModelId(catalog, provider, id, { row = null } = {}) {
|
|
|
322
369
|
.find((r) => String(r?.id || '').toLowerCase() === want);
|
|
323
370
|
const resolved = resolvedKey(own);
|
|
324
371
|
if (!resolved || resolved === want) return null;
|
|
325
|
-
|
|
372
|
+
const pins = catalog?.hiddenPins?.[provider] || null;
|
|
373
|
+
return hidden.find((h) => String(h).toLowerCase() === resolved && !pins?.[resolved]) || null;
|
|
326
374
|
}
|
|
327
375
|
|
|
328
376
|
/**
|
|
@@ -442,7 +490,9 @@ function ctxText(ctx) {
|
|
|
442
490
|
|
|
443
491
|
/** One routing-sheet line: `id · tier · $in/$out · vision · image-out · video-out · ctx · efforts`. */
|
|
444
492
|
export function formatModelLine(row) {
|
|
445
|
-
|
|
493
|
+
// Only Codex bills a plan; any other row without a price is a model no table prices yet.
|
|
494
|
+
const planBilled = row.provider === 'codex' || !!row.listPrice;
|
|
495
|
+
const parts = [row.id, row.tier || '?', row.price || planBilled ? priceText(row.price, row.listPrice) : 'price unknown'];
|
|
446
496
|
if (row.vision) parts.push('vision');
|
|
447
497
|
if (rowMakes(row, 'image')) parts.push('image-out');
|
|
448
498
|
if (rowMakes(row, 'video')) parts.push('video-out');
|
|
@@ -549,6 +599,12 @@ export function compactRow(row) {
|
|
|
549
599
|
* @param {()=>object} [deps.readFilter] → { opencode:[globs] } (routing.catalogFilter)
|
|
550
600
|
* @param {()=>object} [deps.readHidden] → { 'claude-code': [], codex: [], opencode: [] } ids the user
|
|
551
601
|
* hid (Assistant → Models; OpenCode's list is Settings → OpenCode's). A bare array = OpenCode ids.
|
|
602
|
+
* @param {(args:{provider:string, rows:object[], source:string})=>{ids:string[], pins:object}|null} [deps.reconcileHidden]
|
|
603
|
+
* settles a provider's hidden list against the rows just listed (hidden aliases are pinned to a model:
|
|
604
|
+
* lib/assistant-hidden-models.js `reconcile`); its ids and pins replace readHidden's for that build
|
|
605
|
+
* @param {(change:{providers:string[], generatedAt:string})=>void} [deps.onChange] a build listed other
|
|
606
|
+
* models than the build before it (hiding has its own event and does not count)
|
|
607
|
+
* @param {number} [deps.retryMs] how soon a build is redone while Claude Code or Codex gave no verified list
|
|
552
608
|
*/
|
|
553
609
|
export function createAssistantCatalog({
|
|
554
610
|
fetchJson,
|
|
@@ -557,24 +613,53 @@ export function createAssistantCatalog({
|
|
|
557
613
|
listAccounts = () => ({}),
|
|
558
614
|
readFilter = () => ({}),
|
|
559
615
|
readHidden = () => [],
|
|
616
|
+
reconcileHidden = null,
|
|
617
|
+
onChange = null,
|
|
560
618
|
ttlMs = 30_000,
|
|
619
|
+
retryMs = 5_000,
|
|
561
620
|
now = Date.now,
|
|
562
621
|
log = () => {},
|
|
563
622
|
} = {}) {
|
|
564
623
|
if (typeof fetchJson !== 'function') throw new Error('createAssistantCatalog requires fetchJson');
|
|
565
624
|
let cache = null;
|
|
566
625
|
let inflight = null;
|
|
626
|
+
// The last verified list Claude Code and Codex gave: what their rows are built
|
|
627
|
+
// from while the provider does not answer, or answers with its static stand-in.
|
|
628
|
+
const lastLive = {};
|
|
629
|
+
let degradedBuilds = 0;
|
|
630
|
+
let epoch = 0; // bumped by invalidate(): a build that began before it is not kept
|
|
631
|
+
let lastListed = null; // provider → signature of the rows the last build listed
|
|
632
|
+
|
|
633
|
+
const pickArray = (value, keys = []) => {
|
|
634
|
+
if (Array.isArray(value)) return value;
|
|
635
|
+
for (const key of keys) if (Array.isArray(value?.[key])) return value[key];
|
|
636
|
+
return [];
|
|
637
|
+
};
|
|
567
638
|
|
|
568
|
-
|
|
639
|
+
/**
|
|
640
|
+
* One provider's answer → the list to build rows from and how it was obtained:
|
|
641
|
+
* 'live' (the provider answered), 'stale' (it did not: its last verified list),
|
|
642
|
+
* 'fallback' (never verified: the endpoint's static stand-in), 'unavailable'.
|
|
643
|
+
*/
|
|
644
|
+
function settle(provider, payload) {
|
|
645
|
+
const list = pickArray(payload, ['models']);
|
|
646
|
+
const verified = !!payload && payload.ok !== false && payload.source !== 'fallback' && list.length > 0;
|
|
647
|
+
if (verified) { lastLive[provider] = list; return { list, state: 'live' }; }
|
|
648
|
+
if (lastLive[provider]) return { list: lastLive[provider], state: 'stale' };
|
|
649
|
+
return list.length ? { list, state: 'fallback' } : { list: [], state: 'unavailable' };
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function listedSignature(rows) {
|
|
653
|
+
return JSON.stringify(arr(rows).map((row) => [row.id, row.label, row.upstream || '', row.status || '', row.contextWindow || 0, arr(row.efforts).join(',')]));
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async function build({ deep = false } = {}) {
|
|
657
|
+
// `deep` also drops the providers' own discovery caches (a CLI or app-server round trip).
|
|
658
|
+
const fresh = deep ? '?refresh=1' : '';
|
|
569
659
|
const [claudeModels, codexModels, opencodeProviders, projects, mcpProfile] = await Promise.all([
|
|
570
|
-
fetchJson(
|
|
660
|
+
fetchJson(`/api/claude/models${fresh}`), fetchJson(`/api/codex/models${fresh}`), fetchJson('/api/opencode/providers/full'),
|
|
571
661
|
fetchJson('/api/projects'), fetchJson('/api/mcp/profile'),
|
|
572
662
|
]);
|
|
573
|
-
const pickArray = (value, keys = []) => {
|
|
574
|
-
if (Array.isArray(value)) return value;
|
|
575
|
-
for (const key of keys) if (Array.isArray(value?.[key])) return value[key];
|
|
576
|
-
return [];
|
|
577
|
-
};
|
|
578
663
|
const filter = (() => { try { return readFilter() || {}; } catch { return {}; } })();
|
|
579
664
|
const presets = mcpProfile?.presets && typeof mcpProfile.presets === 'object'
|
|
580
665
|
? Object.entries(mcpProfile.presets).map(([name, preset]) => ({ name, label: preset?.label || name, groups: preset?.groups || [], tools: preset?.tools || null }))
|
|
@@ -590,12 +675,34 @@ export function createAssistantCatalog({
|
|
|
590
675
|
if (Array.isArray(raw)) raw = { opencode: raw };
|
|
591
676
|
return Object.fromEntries(CATALOG_PROVIDERS.map((p) => [p, arr(raw[p]).map(String)]));
|
|
592
677
|
})();
|
|
678
|
+
const claude = settle('claude-code', claudeModels);
|
|
679
|
+
const codex = settle('codex', codexModels);
|
|
680
|
+
// OpenCode is read from its serve on every build; while it is down its models are unknown, not remembered.
|
|
681
|
+
const sources = {
|
|
682
|
+
'claude-code': claude.state,
|
|
683
|
+
codex: codex.state,
|
|
684
|
+
opencode: opencodeProviders && opencodeProviders.ok !== false ? 'live' : 'unavailable',
|
|
685
|
+
};
|
|
686
|
+
for (const p of ['claude-code', 'codex']) if (sources[p] !== 'live') log('catalog:source', `${p}: ${sources[p]}`);
|
|
593
687
|
const all = {
|
|
594
|
-
'claude-code': withClaudeContextVariants(normalizeClaudeRows(
|
|
595
|
-
codex: normalizeCodexRows(
|
|
688
|
+
'claude-code': withClaudeContextVariants(normalizeClaudeRows(claude.list, { pricing: claudePricing })),
|
|
689
|
+
codex: normalizeCodexRows(codex.list, { priceOf: codexPrice }),
|
|
596
690
|
opencode: parseOpenCodeProviders(opencodeProviders, { filter: filter.opencode || null }),
|
|
597
691
|
};
|
|
598
|
-
|
|
692
|
+
// A hidden alias hides the model it named when it was hidden. With the rows at hand the
|
|
693
|
+
// store settles that: an alias that moved on is shown again and its old model stays hidden.
|
|
694
|
+
const hiddenPins = Object.fromEntries(CATALOG_PROVIDERS.map((p) => [p, {}]));
|
|
695
|
+
if (typeof reconcileHidden === 'function') {
|
|
696
|
+
for (const p of CATALOG_PROVIDERS) {
|
|
697
|
+
try {
|
|
698
|
+
const settled = reconcileHidden({ provider: p, rows: all[p], source: sources[p] });
|
|
699
|
+
if (!settled || !Array.isArray(settled.ids)) continue;
|
|
700
|
+
hidden[p] = settled.ids.map(String);
|
|
701
|
+
hiddenPins[p] = settled.pins && typeof settled.pins === 'object' ? settled.pins : {};
|
|
702
|
+
} catch (error) { log('catalog:hidden-pins-error', `${p}: ${error?.message || String(error)}`); }
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const lookup = { hidden, hiddenPins };
|
|
599
706
|
const models = {};
|
|
600
707
|
const hiddenRows = {};
|
|
601
708
|
const listedCounts = {};
|
|
@@ -606,11 +713,20 @@ export function createAssistantCatalog({
|
|
|
606
713
|
for (const row of all[p]) (hidden[p].length && hiddenModelId(lookup, p, row.id, { row }) ? hiddenRows[p] : models[p]).push(row);
|
|
607
714
|
}
|
|
608
715
|
const opencode = models.opencode;
|
|
716
|
+
const listedNow = Object.fromEntries(CATALOG_PROVIDERS.map((p) => [p, listedSignature(all[p])]));
|
|
717
|
+
const changed = lastListed ? CATALOG_PROVIDERS.filter((p) => listedNow[p] !== lastListed[p]) : [];
|
|
718
|
+
lastListed = listedNow;
|
|
609
719
|
return {
|
|
610
720
|
generatedAt: new Date(now()).toISOString(),
|
|
721
|
+
// Providers whose listed models differ from the build before (not part of the value's meaning: full() reports it).
|
|
722
|
+
changedProviders: changed,
|
|
611
723
|
models,
|
|
612
724
|
hidden,
|
|
725
|
+
// provider → { hidden id (lower-case) → { model, variant, label, at, … } }: the entries that hide one row only.
|
|
726
|
+
hiddenPins,
|
|
613
727
|
hiddenRows,
|
|
728
|
+
// How each provider's list was obtained: live | stale | fallback | unavailable.
|
|
729
|
+
sources,
|
|
614
730
|
listed: listedCounts,
|
|
615
731
|
// The provider answered with an authoritative list, even an empty one (OpenCode's `connected`).
|
|
616
732
|
known: { opencode: openCodeConnectedKnown(opencodeProviders) },
|
|
@@ -630,28 +746,57 @@ export function createAssistantCatalog({
|
|
|
630
746
|
};
|
|
631
747
|
}
|
|
632
748
|
|
|
633
|
-
|
|
634
|
-
|
|
749
|
+
/**
|
|
750
|
+
* `force` rebuilds now from the providers' endpoints (each answers from its own
|
|
751
|
+
* discovery cache, exactly as it does for its panel's selector); `deep` makes
|
|
752
|
+
* those endpoints ask the CLI / app-server again as well.
|
|
753
|
+
*/
|
|
754
|
+
async function full({ force = false, deep = false } = {}) {
|
|
755
|
+
if (!force && !deep && cache && now() - cache.at < cache.ttl) return cache.value;
|
|
635
756
|
if (inflight) return inflight;
|
|
636
|
-
|
|
757
|
+
const started = epoch;
|
|
758
|
+
inflight = build({ deep }).then((built) => {
|
|
759
|
+
const { changedProviders, ...value } = built;
|
|
760
|
+
// No verified list from Claude Code or Codex: ask again soon, backing off to the
|
|
761
|
+
// normal lifetime (a provider that is not installed must not be spawned every few seconds).
|
|
762
|
+
const degraded = ['claude-code', 'codex'].some((p) => value.sources[p] !== 'live');
|
|
763
|
+
degradedBuilds = degraded ? degradedBuilds + 1 : 0;
|
|
764
|
+
const ttl = degraded ? Math.min(ttlMs, retryMs * 2 ** (degradedBuilds - 1)) : ttlMs;
|
|
765
|
+
cache = { at: now(), value, ttl: started === epoch ? ttl : 0 };
|
|
766
|
+
if (changedProviders.length && typeof onChange === 'function') {
|
|
767
|
+
try { onChange({ providers: changedProviders, generatedAt: value.generatedAt }); } catch (error) { log('catalog:onchange-error', error?.message || String(error)); }
|
|
768
|
+
}
|
|
769
|
+
return value;
|
|
770
|
+
}).finally(() => { inflight = null; });
|
|
637
771
|
return inflight;
|
|
638
772
|
}
|
|
639
773
|
|
|
640
|
-
/**
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
774
|
+
/**
|
|
775
|
+
* Every row, hidden ones included, per provider, and how each provider's list was obtained:
|
|
776
|
+
* { rows, sources, generatedAt } of one build, so what a row says is never read against another build's source.
|
|
777
|
+
*/
|
|
778
|
+
async function manageSnapshot({ force = false, deep = false } = {}) {
|
|
779
|
+
const value = await full({ force, deep });
|
|
780
|
+
return {
|
|
781
|
+
rows: Object.fromEntries(CATALOG_PROVIDERS.map((p) => [p, [...arr(value.models?.[p]), ...arr(value.hiddenRows?.[p])]])),
|
|
782
|
+
sources: { ...value.sources },
|
|
783
|
+
generatedAt: value.generatedAt,
|
|
784
|
+
};
|
|
644
785
|
}
|
|
645
786
|
|
|
787
|
+
/** Every row, hidden ones included, per provider (the Models manager, id resolution). */
|
|
788
|
+
async function manageRows(options = {}) { return (await manageSnapshot(options)).rows; }
|
|
789
|
+
|
|
646
790
|
/**
|
|
647
791
|
* view 'full' (default): every visible row. view 'brain': compact rows,
|
|
648
792
|
* OpenCode capped; `provider` + `q` narrow the list (agent_catalog search).
|
|
649
793
|
* view 'manage': every row with hidden: true|false and hiddenBy (the list
|
|
650
794
|
* entry that hides it), plus `archived`: every hidden id per provider with
|
|
651
|
-
* the rows it covers; listed:false = no current row (still restorable)
|
|
795
|
+
* the rows it covers; listed:false = no current row (still restorable); `pin`
|
|
796
|
+
* = the model an alias entry was hidden as (see hiddenPins).
|
|
652
797
|
*/
|
|
653
|
-
async function get({ force = false, view = 'full', provider = null, q = null, brain = null, preferences = {} } = {}) {
|
|
654
|
-
const value = await full({ force });
|
|
798
|
+
async function get({ force = false, deep = false, view = 'full', provider = null, q = null, brain = null, preferences = {} } = {}) {
|
|
799
|
+
const value = await full({ force, deep });
|
|
655
800
|
if (view === 'manage') {
|
|
656
801
|
const models = {};
|
|
657
802
|
const archived = {};
|
|
@@ -672,10 +817,11 @@ export function createAssistantCatalog({
|
|
|
672
817
|
});
|
|
673
818
|
archived[p] = arr(value.hidden?.[p]).map((id) => {
|
|
674
819
|
const covered = covers.get(String(id).toLowerCase()) || [];
|
|
675
|
-
|
|
820
|
+
const pin = value.hiddenPins?.[p]?.[String(id).toLowerCase()] || null;
|
|
821
|
+
return { id, listed: covered.length > 0, group: p === 'opencode' ? String(id).split('/')[0] : null, rows: covered, ...(pin ? { pin } : {}) };
|
|
676
822
|
});
|
|
677
823
|
}
|
|
678
|
-
return { view: 'manage', generatedAt: value.generatedAt, models, archived, hidden: value.hidden, listed: value.listed };
|
|
824
|
+
return { view: 'manage', generatedAt: value.generatedAt, models, archived, hidden: value.hidden, hiddenPins: value.hiddenPins, listed: value.listed, sources: value.sources };
|
|
679
825
|
}
|
|
680
826
|
if (view !== 'brain' && !provider && !q) return value;
|
|
681
827
|
const needle = q ? String(q).toLowerCase() : null;
|
|
@@ -690,7 +836,7 @@ export function createAssistantCatalog({
|
|
|
690
836
|
const sheet = selectSheetModels(value, { brain, preferences, max: 40 });
|
|
691
837
|
for (const p of CATALOG_PROVIDERS) models[p] = (p === 'opencode' ? sheet[p] : value.models[p]).map(compactRow);
|
|
692
838
|
}
|
|
693
|
-
const { opencodeHidden, hidden, hiddenRows, ...rest } = value;
|
|
839
|
+
const { opencodeHidden, hidden, hiddenPins, hiddenRows, ...rest } = value;
|
|
694
840
|
const hiddenCount = Object.fromEntries(CATALOG_PROVIDERS.map((p) => [p, arr(hiddenRows?.[p]).length]));
|
|
695
841
|
return { ...rest, view: 'brain', models, opencodeTotal: value.opencodeTotal, hiddenCount, opencodeHiddenCount: arr(opencodeHidden).length };
|
|
696
842
|
}
|
|
@@ -725,7 +871,13 @@ export function createAssistantCatalog({
|
|
|
725
871
|
return { vision: row.vision ?? null, tier: row.tier || null, price: row.price || null, label: row.label || row.id, contextWindow: row.contextWindow || null, source: hit?.match || 'default', id: row.id };
|
|
726
872
|
}
|
|
727
873
|
|
|
728
|
-
function invalidate() { cache = null; }
|
|
874
|
+
function invalidate() { cache = null; epoch += 1; }
|
|
875
|
+
/** A provider's own list changed: drop the copy and build the next one now (onChange reports the difference). */
|
|
876
|
+
function refresh() {
|
|
877
|
+
invalidate();
|
|
878
|
+
const pending = inflight; // began before the change: let it end, then build again
|
|
879
|
+
return pending ? pending.catch(() => null).then(() => full()) : full();
|
|
880
|
+
}
|
|
729
881
|
|
|
730
|
-
return { get, full, peek, find, hiddenId, manageRows, brainInfo, invalidate };
|
|
882
|
+
return { get, full, peek, find, hiddenId, manageRows, manageSnapshot, brainInfo, invalidate, refresh };
|
|
731
883
|
}
|