synabun 2.0.10 → 2.2.0
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 +207 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +13 -13
- package/hooks/claude-code/pre-websearch.mjs +12 -1
- package/hooks/claude-code/shared.mjs +5 -0
- package/hooks/claude-code/stop.mjs +1 -1
- package/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +30 -17
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +7 -6
- package/lib/first-launch-protection.js +57 -12
- package/lib/mcp-dist.js +176 -0
- package/lib/release-npm-ci.mjs +138 -0
- package/lib/release-publish.mjs +91 -0
- package/lib/release-scan.mjs +50 -0
- package/lib/server-crash-record.js +123 -0
- package/lib/server-exit.js +318 -0
- package/lib/server-supervisor.js +508 -0
- package/lib/start-launcher.js +37 -4
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +302 -0
- package/lib/virustotal.mjs +203 -0
- package/lib/windows-crash-event.js +269 -0
- package/mcp-server/dist/http.d.ts +20 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +59 -12
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts +33 -27
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +128 -143
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/preload.js +1 -1
- package/mcp-server/dist/preload.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.js.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +81 -28
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
- package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +18 -11
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +67 -11
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts +7 -2
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +22 -63
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
- package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-catalog.js +339 -0
- package/mcp-server/dist/services/tool-catalog.js.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
- package/mcp-server/dist/services/tool-list.d.ts +108 -0
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list.js +334 -0
- package/mcp-server/dist/services/tool-list.js.map +1 -0
- package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-runtime.js +400 -0
- package/mcp-server/dist/services/tool-runtime.js.map +1 -0
- package/mcp-server/dist/services/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
- package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
- package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +14 -14
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +29 -19
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
- package/mcp-server/dist/tools/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill-tools.js +642 -0
- package/mcp-server/dist/tools/skill-tools.js.map +1 -0
- package/mcp-server/dist/tools/skill.d.ts +7 -0
- package/mcp-server/dist/tools/skill.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill.js +11 -0
- package/mcp-server/dist/tools/skill.js.map +1 -0
- package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
- package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
- package/mcp-server/dist/tools/tool-groups.js +147 -0
- package/mcp-server/dist/tools/tool-groups.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-format.js +7 -4
- package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/http.ts +75 -12
- package/mcp-server/src/index.ts +147 -154
- package/mcp-server/src/preload.ts +12 -12
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +81 -21
- package/mcp-server/src/services/server-instructions.ts +29 -64
- package/mcp-server/src/services/tool-catalog.ts +403 -0
- package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
- package/mcp-server/src/services/tool-list.ts +410 -0
- package/mcp-server/src/services/tool-runtime.ts +457 -0
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +29 -20
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +592 -0
- package/mcp-server/src/tools/skill.ts +13 -0
- package/mcp-server/src/tools/tool-groups.ts +164 -0
- package/mcp-server/src/tools/whiteboard-format.ts +9 -3
- package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
- package/neural-interface/i18n/en.json +3953 -228
- package/neural-interface/i18n/pt-BR.json +3901 -179
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +128 -28
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-brains/claude.js +4 -3
- package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
- package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
- package/neural-interface/lib/assistant-brains/codex.js +9 -5
- package/neural-interface/lib/assistant-brains/opencode.js +120 -54
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +81 -15
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +1073 -200
- package/neural-interface/lib/assistant-envelope.js +156 -2
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +93 -19
- package/neural-interface/lib/assistant-plan-permissions.js +25 -9
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
- package/neural-interface/lib/assistant-route-gate.js +6 -4
- package/neural-interface/lib/assistant-router.js +245 -38
- package/neural-interface/lib/assistant-runtime.js +214 -30
- package/neural-interface/lib/assistant-task-prompt.js +87 -21
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +626 -4
- package/neural-interface/lib/backup-zip-reader.js +10 -5
- package/neural-interface/lib/backup-zip-writer.js +257 -0
- package/neural-interface/lib/browser-execution.js +10 -2
- package/neural-interface/lib/browser-guard-cdp.js +84 -0
- package/neural-interface/lib/browser-network-guard.js +476 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +198 -38
- package/neural-interface/lib/claude-agent-bridge.js +11 -2
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +163 -154
- package/neural-interface/lib/cli-resolver.js +13 -3
- package/neural-interface/lib/cli-update-planner.js +52 -41
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +191 -0
- package/neural-interface/lib/confined-fs.js +30 -3
- package/neural-interface/lib/crash-notice-api.js +98 -0
- package/neural-interface/lib/editor-completion-opencode.js +68 -68
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +13 -10
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +35 -33
- package/neural-interface/lib/mcp-external-servers.js +332 -0
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +177 -25
- package/neural-interface/lib/native-loop-providers.js +965 -66
- package/neural-interface/lib/native-loop-runtime.js +290 -42
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +17 -34
- package/neural-interface/lib/opencode-history.js +48 -218
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +53 -0
- package/neural-interface/lib/opencode-protocol.js +249 -112
- package/neural-interface/lib/opencode-runtime-config.js +32 -22
- package/neural-interface/lib/opencode-serve-records.js +39 -21
- package/neural-interface/lib/opencode-v2-client.js +48 -153
- package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +73 -37
- package/neural-interface/lib/opencode2-proxy.js +23 -63
- package/neural-interface/lib/opencode2-translate.js +51 -2
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +793 -0
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +20 -2
- package/neural-interface/lib/rulesets/api.js +7 -6
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +180 -36
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +81 -31
- package/neural-interface/lib/session-title-generator.js +58 -36
- package/neural-interface/lib/skills/api-v2.js +495 -0
- package/neural-interface/lib/skills/bundle.js +418 -0
- package/neural-interface/lib/skills/catalog.js +298 -0
- package/neural-interface/lib/skills/creation-api.js +191 -0
- package/neural-interface/lib/skills/creation-brief.js +247 -0
- package/neural-interface/lib/skills/creation-sources.js +285 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +569 -0
- package/neural-interface/lib/skills/digest.js +119 -0
- package/neural-interface/lib/skills/discover.js +111 -0
- package/neural-interface/lib/skills/draft-confinement.js +216 -0
- package/neural-interface/lib/skills/eval-confinement.js +399 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +135 -0
- package/neural-interface/lib/skills/evals-api.js +212 -0
- package/neural-interface/lib/skills/evals.js +1573 -0
- package/neural-interface/lib/skills/export.js +203 -0
- package/neural-interface/lib/skills/frontmatter.js +64 -0
- package/neural-interface/lib/skills/health.js +75 -0
- package/neural-interface/lib/skills/json-edit.js +165 -0
- package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
- package/neural-interface/lib/skills/known-bridges.js +96 -0
- package/neural-interface/lib/skills/library.js +823 -0
- package/neural-interface/lib/skills/local-store.js +21 -0
- package/neural-interface/lib/skills/migration.js +433 -0
- package/neural-interface/lib/skills/operations.js +45 -0
- package/neural-interface/lib/skills/ownership.js +427 -0
- package/neural-interface/lib/skills/packages.js +88 -0
- package/neural-interface/lib/skills/reviews.js +212 -0
- package/neural-interface/lib/skills/revision.js +80 -0
- package/neural-interface/lib/skills/roots.js +269 -0
- package/neural-interface/lib/skills/runtime-checks.js +358 -0
- package/neural-interface/lib/skills/scan-assets.js +252 -0
- package/neural-interface/lib/skills/scan-context.js +26 -0
- package/neural-interface/lib/skills/scan-shell.js +324 -0
- package/neural-interface/lib/skills/scan.js +364 -0
- package/neural-interface/lib/skills/scanners.js +54 -0
- package/neural-interface/lib/skills/service.js +530 -0
- package/neural-interface/lib/skills/telemetry.js +119 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +1268 -0
- package/neural-interface/lib/worker-guard-setting.js +56 -0
- package/neural-interface/lib/worker-mcp-channel.js +175 -0
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +238 -0
- package/neural-interface/lib/worker-sandbox.js +329 -0
- package/neural-interface/package-lock.json +34 -12
- package/neural-interface/package.json +3 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +3 -3
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +125 -31
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- package/neural-interface/public/shared/api.js +1527 -1336
- package/neural-interface/public/shared/app-frame.js +192 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
- package/neural-interface/public/shared/assistant/asst-api.js +1 -1
- package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
- package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
- package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-control.js +205 -26
- package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
- package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
- package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
- package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
- package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
- package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +445 -82
- package/neural-interface/public/shared/assistant/asst-route.js +304 -53
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
- package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
- package/neural-interface/public/shared/assistant/asst-state.js +87 -24
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +89 -19
- package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
- package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
- package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
- package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +11 -12
- package/neural-interface/public/shared/cli-status.js +11 -10
- package/neural-interface/public/shared/constants.js +5 -4
- package/neural-interface/public/shared/context-menu-areas.js +34 -0
- package/neural-interface/public/shared/context-menu-logic.js +243 -0
- package/neural-interface/public/shared/context-menu-model.js +367 -0
- package/neural-interface/public/shared/context-menu.js +372 -0
- package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
- package/neural-interface/public/shared/cp/cp-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +21 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +30 -15
- package/neural-interface/public/shared/i18n.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
- package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
- package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
- package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/registry.js +5 -3
- package/neural-interface/public/shared/retired-2d.js +3 -0
- package/neural-interface/public/shared/schedules-view.js +195 -61
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +3 -1
- package/neural-interface/public/shared/settings/settings-ia.js +6 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
- package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
- package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
- package/neural-interface/public/shared/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +222 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
- package/neural-interface/public/shared/skills/ss-doc.js +173 -0
- package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
- package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
- package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
- package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
- package/neural-interface/public/shared/skills/ss-evals.js +417 -0
- package/neural-interface/public/shared/skills/ss-form.js +159 -0
- package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
- package/neural-interface/public/shared/skills/ss-ids.js +32 -0
- package/neural-interface/public/shared/skills/ss-lint.js +149 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
- package/neural-interface/public/shared/skills/ss-resources.js +165 -0
- package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
- package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
- package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
- package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
- package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
- package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
- package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
- package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
- package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
- package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
- package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
- package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
- package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
- package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
- package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
- package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
- package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
- package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
- package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
- package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
- package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
- package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
- package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
- package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
- package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
- package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
- package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1822 -1756
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +55 -0
- package/neural-interface/public/shared/tool-list-storage.js +24 -0
- package/neural-interface/public/shared/tool-list-view.js +112 -0
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/tour/tour-actor.js +268 -0
- package/neural-interface/public/shared/tour/tour-beats.js +336 -0
- package/neural-interface/public/shared/tour/tour-engine.js +884 -0
- package/neural-interface/public/shared/tour/tour-ink.js +390 -0
- package/neural-interface/public/shared/tour/tour-stage.js +440 -0
- package/neural-interface/public/shared/tour/tour-state.js +40 -0
- package/neural-interface/public/shared/tour/tour-tips.js +99 -0
- package/neural-interface/public/shared/tour/tour.css +144 -0
- package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +111 -140
- package/neural-interface/public/shared/ui-claude-panel.js +376 -82
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-command-runner.js +6 -3
- package/neural-interface/public/shared/ui-context-menu.js +295 -0
- package/neural-interface/public/shared/ui-cost-widget.js +57 -59
- package/neural-interface/public/shared/ui-crash-notice.js +187 -0
- package/neural-interface/public/shared/ui-detail.js +15 -3
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +33 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-menubar.js +10 -20
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
- package/neural-interface/public/shared/ui-navbar.js +49 -24
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +27 -15
- package/neural-interface/public/shared/ui-settings.js +683 -526
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
- package/neural-interface/public/shared/ui-skills.js +33 -2830
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +15 -5
- package/neural-interface/public/shared/ui-terminal.js +270 -190
- package/neural-interface/public/shared/ui-tool-updates.js +6 -4
- package/neural-interface/public/shared/ui-tooltip.js +81 -51
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-trash.js +6 -4
- package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
- package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -509
- package/neural-interface/public/shared/ui-update.js +38 -2
- package/neural-interface/public/shared/ui-whiteboard.js +312 -59
- package/neural-interface/public/shared/ui-worker-end.js +5 -1
- package/neural-interface/public/shared/ui-workspaces.js +3 -2
- package/neural-interface/public/shared/view-variant.js +66 -0
- package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
- package/neural-interface/public/shared/whiteboard-list.js +172 -0
- package/neural-interface/public/shared/worker-end.js +10 -3
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +2 -2
- package/neural-interface/public/variant/2d/background.js +2 -1
- package/neural-interface/public/variant/2d/context-menu.js +8 -2
- package/neural-interface/public/variant/2d/graph.js +42 -20
- package/neural-interface/public/variant/2d/hulls.js +14 -9
- package/neural-interface/public/variant/2d/lasso.js +11 -7
- package/neural-interface/public/variant/2d/main.js +32 -1
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +69 -6
- package/neural-interface/public/variant/3d/map-controls.js +24 -9
- package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat.css +17 -0
- package/neural-interface/public/vendor/caveat/manifest.json +12 -0
- package/neural-interface/public/vendor/yaml/LICENSE +13 -0
- package/neural-interface/public/vendor/yaml/manifest.json +4 -0
- package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
- package/neural-interface/scripts/build-vendor.mjs +32 -0
- package/neural-interface/server.js +4553 -2855
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +34 -12
- package/package.json +17 -3
- package/postinstall.js +9 -4
- package/preuninstall.js +1 -1
- package/setup.js +97 -322
- package/skills/skill-forge/SKILL.md +177 -0
- package/skills/skill-forge/evals/evals.json +54 -0
- package/skills/skill-forge/references/package.md +105 -0
- package/skills/skill-forge/references/review-and-recovery.md +63 -0
- package/skills/skill-forge/references/runtime-profiles.md +20 -0
- package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
- package/skills/synabun/SKILL.md +10 -7
- package/skills/synabun/modules/audit.md +1 -5
- package/skills/synabun/modules/changelog.md +1 -5
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/memorize.md +6 -9
- package/skills/synabun/modules/schedule.md +6 -6
- package/mcp-server/dist/services/profiles.d.ts +0 -120
- package/mcp-server/dist/services/profiles.d.ts.map +0 -1
- package/mcp-server/dist/services/profiles.js +0 -387
- package/mcp-server/dist/services/profiles.js.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
- package/mcp-server/dist/tools/leonardo.d.ts +0 -13
- package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo.js +0 -21
- package/mcp-server/dist/tools/leonardo.js.map +0 -1
- package/mcp-server/dist/tools/profile.d.ts +0 -17
- package/mcp-server/dist/tools/profile.d.ts.map +0 -1
- package/mcp-server/dist/tools/profile.js +0 -114
- package/mcp-server/dist/tools/profile.js.map +0 -1
- package/mcp-server/src/services/profiles.ts +0 -428
- package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
- package/mcp-server/src/tools/leonardo.ts +0 -30
- package/mcp-server/src/tools/profile.ts +0 -122
- package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
- package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
- package/neural-interface/lib/mcp-profile-servers.js +0 -120
- package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
- package/skills/leonardo/SKILL.md +0 -168
- package/skills/leonardo/icon.svg +0 -1
- package/skills/leonardo/modules/browser-guide.md +0 -378
- package/skills/leonardo/modules/image-prompter.md +0 -283
- package/skills/leonardo/modules/model-advisor.md +0 -100
- package/skills/leonardo/modules/prompt-library.md +0 -110
- package/skills/leonardo/modules/style-guide.md +0 -186
- package/skills/leonardo/modules/video-prompter.md +0 -317
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -37,7 +37,7 @@ import { effectiveRouting, normalizeRouteMode } from './assistant-config.js';
|
|
|
37
37
|
import { normalizeEffort } from './effort-levels.js';
|
|
38
38
|
import { hasRouteAccounts, routeAccountText } from './assistant-route-accounts.js';
|
|
39
39
|
import { chooseUsageAccount } from './account-usage.js';
|
|
40
|
-
import { PROVIDER_LABELS, coversWork, dryProviders, isFreePrice, listPriceOf, rowOf } from './assistant-budget-split.js';
|
|
40
|
+
import { PROVIDER_LABELS, coversWork, dryProviders, isFreePrice, isUnpricedProvider, listPriceOf, rowOf } from './assistant-budget-split.js';
|
|
41
41
|
|
|
42
42
|
export const TASK_CLASS_META = Object.freeze({
|
|
43
43
|
chat: { label: 'Chat', description: 'Conversation and questions answered from knowledge or memory', defaultKind: 'direct' },
|
|
@@ -75,6 +75,8 @@ export function requiresVision(taskClass) { return TASK_CLASS_META[taskClass]?.v
|
|
|
75
75
|
export const CANNOT_SEE_TEXT = "This model can't see images.";
|
|
76
76
|
export const UNKNOWN_SIGHT_TEXT = "This model isn't known to see images.";
|
|
77
77
|
export const UNAVAILABLE_TEXT = "This model isn't available right now.";
|
|
78
|
+
/** A "here" on an account other than this conversation's: provider histories belong to their account. */
|
|
79
|
+
export const WORKER_FOR_ACCOUNT_TEXT = 'Runs on a worker because this conversation belongs to another account.';
|
|
78
80
|
/** The card / answer text for a model design cannot use (a designFit() verdict). */
|
|
79
81
|
function fitText(fit) { return fit === 'blind' ? CANNOT_SEE_TEXT : fit === 'unavailable' ? UNAVAILABLE_TEXT : UNKNOWN_SIGHT_TEXT; }
|
|
80
82
|
/** What a run of the class collects into its media: the medium it makes, else what it gathers (Design's screenshots). */
|
|
@@ -185,7 +187,8 @@ export function normalizeConfidence(value) {
|
|
|
185
187
|
|
|
186
188
|
/**
|
|
187
189
|
* Normalize the brain's proposal (agent_route body, or a dispatch spec reshaped
|
|
188
|
-
* into one). A "direct" proposal on another provider is really a dispatch
|
|
190
|
+
* into one). A "direct" proposal on another provider is really a dispatch
|
|
191
|
+
* (`coercedProvider` records the first, so the result can say so); for a
|
|
189
192
|
* dispatch-only class (image / video creation, design) every proposal is a dispatch,
|
|
190
193
|
* a "here" one on the brain's own model (`coercedDirect` records the first).
|
|
191
194
|
* Design (`visionOnly`) also always needs a model that can see.
|
|
@@ -201,14 +204,17 @@ export function normalizeRouteRequest(body = {}, { brain = {} } = {}) {
|
|
|
201
204
|
const dispatchOnly = meta.dispatchOnly === true;
|
|
202
205
|
const proposals = [];
|
|
203
206
|
let coercedDirect = false;
|
|
207
|
+
let coercedProvider = null;
|
|
204
208
|
for (const raw of Array.isArray(body.proposals) ? body.proposals.slice(0, 3) : []) {
|
|
205
209
|
if (!raw || typeof raw !== 'object') continue;
|
|
206
210
|
let kind = raw.kind === 'direct' ? 'direct' : 'dispatch';
|
|
207
211
|
let provider = CATALOG_PROVIDERS.includes(raw.provider) ? raw.provider : null;
|
|
208
212
|
let model = raw.model ? String(raw.model).trim().slice(0, 200) : null;
|
|
209
213
|
if (kind === 'direct') {
|
|
210
|
-
if (provider && provider !== brain.provider)
|
|
211
|
-
|
|
214
|
+
if (provider && provider !== brain.provider) {
|
|
215
|
+
kind = 'dispatch';
|
|
216
|
+
if (!proposals.length && !dispatchOnly) coercedProvider = provider;
|
|
217
|
+
} else provider = brain.provider || provider;
|
|
212
218
|
}
|
|
213
219
|
if (!provider) continue;
|
|
214
220
|
if (dispatchOnly && kind === 'direct') {
|
|
@@ -230,9 +236,18 @@ export function normalizeRouteRequest(body = {}, { brain = {} } = {}) {
|
|
|
230
236
|
const request = { taskClass, taskClassLabel: meta.label, summary: clip(body.summary || '', 400), confidence, needsVision, proposals };
|
|
231
237
|
if (needsOutput || dispatchOnly) Object.assign(request, { needsOutput, dispatchOnly, coercedDirect });
|
|
232
238
|
if (visionOnly) request.visionOnly = true;
|
|
239
|
+
if (coercedProvider) request.coercedProvider = coercedProvider;
|
|
233
240
|
return request;
|
|
234
241
|
}
|
|
235
242
|
|
|
243
|
+
/** Why a "here" on `provider` runs on a worker: only the brain's own provider runs in this conversation. */
|
|
244
|
+
function otherProviderCorrection(provider, brain = {}) {
|
|
245
|
+
return {
|
|
246
|
+
field: 'kind', from: 'direct', to: 'dispatch', reason: 'other_provider', provider,
|
|
247
|
+
text: `${providerLabel(provider)} is not this conversation's provider${brain.provider ? ` (${providerLabel(brain.provider)})` : ''}, so it runs on a worker`,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
236
251
|
/** Catalog-backed description of a target: label, tier, vision, outputs, price, context. */
|
|
237
252
|
export function describeTarget(target, { catalog = null, brain = {}, brainInfo = null } = {}) {
|
|
238
253
|
if (!target) return null;
|
|
@@ -478,6 +493,9 @@ export function budgetMove({ target, request, catalog = null, brain = {}, brainI
|
|
|
478
493
|
export function evaluateRoute({ mode, routing = {}, request, brain = {}, brainInfo = null, catalog = null, budget = null }) {
|
|
479
494
|
const reasons = [];
|
|
480
495
|
const corrections = [];
|
|
496
|
+
const effective = normalizeRouteMode(mode) || 'ask-unsure';
|
|
497
|
+
// Always Ask is a task-local choice; the Never Ask table must not seed it.
|
|
498
|
+
if (effective === 'always-ask') routing = { ...routing, preferences: {} };
|
|
481
499
|
const first = request.proposals[0];
|
|
482
500
|
let proposal = { kind: first.kind, provider: first.provider, model: first.model, effort: first.effort };
|
|
483
501
|
if (first.accountId) proposal.accountId = first.accountId;
|
|
@@ -496,6 +514,8 @@ export function evaluateRoute({ mode, routing = {}, request, brain = {}, brainIn
|
|
|
496
514
|
if (request.coercedDirect) {
|
|
497
515
|
corrections.push({ field: 'kind', from: 'direct', to: 'dispatch', reason: 'dispatch_only', text: `${label} always runs on a worker, not here` });
|
|
498
516
|
}
|
|
517
|
+
// "Here" on another provider was always a worker: the result now says so instead of changing the kind silently.
|
|
518
|
+
if (request.coercedProvider) corrections.push(otherProviderCorrection(request.coercedProvider, brain));
|
|
499
519
|
const sees = (target) => describeTarget(target, { catalog, brain, brainInfo })?.vision;
|
|
500
520
|
const fit = (target) => designFit(catalog, target, { brain, brainInfo });
|
|
501
521
|
// Too blind for this task: design needs a confirmed true (null never counts) on an available row; other vision work refuses a confirmed false.
|
|
@@ -546,7 +566,6 @@ export function evaluateRoute({ mode, routing = {}, request, brain = {}, brainIn
|
|
|
546
566
|
if (request.confidence === null) reasons.push('no_confidence');
|
|
547
567
|
else if (request.confidence < (routing.askBelow ?? 0.75)) reasons.push('low_confidence');
|
|
548
568
|
|
|
549
|
-
const effective = normalizeRouteMode(mode) || 'ask-unsure';
|
|
550
569
|
if (effective === 'always-ask' || effective === 'ask-unsure') {
|
|
551
570
|
// The suggestion a card shows (or the pick that runs) is one the budget can pay for.
|
|
552
571
|
const budgeted = applyBudget(proposal);
|
|
@@ -641,7 +660,8 @@ export function evaluateRoute({ mode, routing = {}, request, brain = {}, brainIn
|
|
|
641
660
|
if (dispatchOnly && auto.kind !== 'dispatch') auto = { ...auto, kind: 'dispatch' };
|
|
642
661
|
// A corrected model or provider re-checks the effort.
|
|
643
662
|
auto = effortChecked(auto, catalog, corrections);
|
|
644
|
-
|
|
663
|
+
// The provider rule only names what always happened (a "here" elsewhere is a worker): it corrects nothing.
|
|
664
|
+
if (!bound && corrections.some((c) => c.reason !== 'remembered_disabled' && c.reason !== 'remembered_incapable' && c.reason !== 'other_provider')) source = 'corrected';
|
|
645
665
|
// With a budget the saved route yields: a provider with no share left moves the task on.
|
|
646
666
|
const budgeted = applyBudget(auto);
|
|
647
667
|
if (budgeted.declined) return budgetDeclined(budgeted);
|
|
@@ -669,11 +689,13 @@ function optionFor(id, rawTarget, badge, { catalog, brain, brainInfo, needsVisio
|
|
|
669
689
|
else if (fit !== 'ok') { option.disabled = true; option.disabledReason = fitText(fit); }
|
|
670
690
|
else if (needsVision && d.vision === false) { option.disabled = true; option.disabledReason = 'This model cannot see images, and this task needs to.'; }
|
|
671
691
|
else if (needsVision && d.vision === null) option.warning = 'Image support unknown for this model.';
|
|
672
|
-
// The session budget: a provider with no share left cannot take the task (a free model still can)
|
|
692
|
+
// The session budget: a provider with no share left cannot take the task (a free model still can), and one
|
|
693
|
+
// that reports no prices cannot run under a budget at all.
|
|
673
694
|
if (!option.disabled && coverage && !budgetCovers(coverage, catalog, target)) {
|
|
695
|
+
const unpriced = coverage.leftUsd < 0.01 ? null : unpricedText(target.provider);
|
|
674
696
|
option.disabled = true;
|
|
675
|
-
option.budget = 'share_used_up';
|
|
676
|
-
option.disabledReason = coverage.leftUsd < 0.01 ? "Budget: this session's budget is spent." : `Budget: the ${providerLabel(target.provider)} share of this session's budget is used up
|
|
697
|
+
option.budget = unpriced ? 'unpriced' : 'share_used_up';
|
|
698
|
+
option.disabledReason = unpriced || (coverage.leftUsd < 0.01 ? "Budget: this session's budget is spent." : `Budget: the ${providerLabel(target.provider)} share of this session's budget is used up.`);
|
|
677
699
|
}
|
|
678
700
|
if (here && brainInfo?.contextWindow && d.contextWindow && d.contextWindow < brainInfo.contextWindow) {
|
|
679
701
|
option.warning = `Smaller context (${Math.round(d.contextWindow / 1000)}k) than the current model; a long conversation may not fit.`;
|
|
@@ -682,7 +704,12 @@ function optionFor(id, rawTarget, badge, { catalog, brain, brainInfo, needsVisio
|
|
|
682
704
|
}
|
|
683
705
|
|
|
684
706
|
export function providerLabel(provider) {
|
|
685
|
-
return provider === 'claude-code' ? 'Claude Code' : provider === 'codex' ? 'Codex' : provider === 'opencode' ? 'OpenCode' : String(provider || 'Assistant');
|
|
707
|
+
return provider === 'claude-code' ? 'Claude Code' : provider === 'codex' ? 'Codex' : provider === 'opencode' ? 'OpenCode' : provider === 'antigravity' ? 'Antigravity' : String(provider || 'Assistant');
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/** Why a session with a budget cannot run `provider` at all (it reports tokens, never prices), else null. */
|
|
711
|
+
export function unpricedText(provider) {
|
|
712
|
+
return isUnpricedProvider(provider) ? `Budget: ${providerLabel(provider)} reports tokens, not prices, so a session with a budget cannot run it. Pick another option, or switch the budget to No limit.` : null;
|
|
686
713
|
}
|
|
687
714
|
|
|
688
715
|
/** `seeingOnly` (design): only rows with a confirmed vision: true, where `needsVision` only drops a confirmed false. */
|
|
@@ -723,8 +750,17 @@ function strongestClaude(catalog) {
|
|
|
723
750
|
* Image / video creation: no "here" (always a worker), models that cannot make
|
|
724
751
|
* the medium disabled, and — when the suggestion cannot — the model that would
|
|
725
752
|
* run it instead (badge "capable"). Design: the same with "can see".
|
|
753
|
+
*
|
|
754
|
+
* Both sides, always: the card switches between Here (kind "direct": this
|
|
755
|
+
* conversation, on its model or another of its provider) and Dispatch (a worker).
|
|
756
|
+
* A side the options leave without one that can run gets one: a worker on the
|
|
757
|
+
* brain's own model ("w1", badge "current"), else a capable model of its provider,
|
|
758
|
+
* else elsewhere; a "here" on a model of the brain's provider that can see when the
|
|
759
|
+
* brain cannot ("h1", badge "capable"). Worker-only classes get no "here".
|
|
760
|
+
* → { options, defaultOptionId, sides, defaults } (routeSides).
|
|
726
761
|
*/
|
|
727
762
|
export function buildRouteOptions({ request, evaluation, brain = {}, brainInfo = null, catalog = null, routing = {}, coverage = null }) {
|
|
763
|
+
const alwaysAsk = evaluation.reasons?.includes('always-ask') === true;
|
|
728
764
|
const medium = request.needsOutput || null;
|
|
729
765
|
const dispatchOnly = request.dispatchOnly === true;
|
|
730
766
|
const visionOnly = request.visionOnly === true;
|
|
@@ -732,16 +768,26 @@ export function buildRouteOptions({ request, evaluation, brain = {}, brainInfo =
|
|
|
732
768
|
const options = [];
|
|
733
769
|
const push = (option) => { if (!options.some((o) => sameTarget(o, option))) options.push(option); };
|
|
734
770
|
const hereTarget = { kind: 'direct', provider: brain.provider, model: brain.model || null, effort: brain.effort || null };
|
|
771
|
+
// A direct pick on the brain's own model with no effort is "here" as the brain runs now: one row, not two.
|
|
772
|
+
const onBrainModel = (t) => t.kind === 'direct' && t.provider === brain.provider && String(t.model || '') === String(brain.model || '');
|
|
735
773
|
request.proposals.forEach((p, index) => {
|
|
736
774
|
const target = { kind: p.kind, provider: p.provider, model: p.model || (index === 0 ? evaluation.proposal?.model || null : null), effort: p.effort };
|
|
737
775
|
if (index === 0 && evaluation.proposal) Object.assign(target, evaluation.proposal);
|
|
776
|
+
if (onBrainModel(target) && !target.effort && brain.effort) target.effort = brain.effort;
|
|
738
777
|
push(optionFor(`s${index + 1}`, target, 'suggested', { ...ctx, reason: p.reason }));
|
|
739
778
|
});
|
|
740
779
|
// The session budget moved the suggestion: the brain's own pick stays on the card, disabled with why.
|
|
741
780
|
if (evaluation.budgetFrom && coverage) push(optionFor('b0', evaluation.budgetFrom, 'suggested', { ...ctx, reason: request.proposals[0]?.reason || null }));
|
|
742
781
|
if (!dispatchOnly) push(optionFor('here', hereTarget, 'current', ctx));
|
|
743
782
|
const pref = preferenceTarget(routing.preferences?.[request.taskClass], brain, catalog, { dispatchOnly });
|
|
744
|
-
|
|
783
|
+
// Always ask: the saved route is offered and its side starts on it (never binding: the card still asks).
|
|
784
|
+
if (pref) {
|
|
785
|
+
const saved = optionFor('r1', pref, 'remembered', ctx);
|
|
786
|
+
// A proposal of the same target keeps its row and carries the mark.
|
|
787
|
+
const row = options.find((o) => sameTarget(o, saved)) || saved;
|
|
788
|
+
if (alwaysAsk && !saved.disabled) Object.assign(row, { preferred: true, saved: true });
|
|
789
|
+
push(saved);
|
|
790
|
+
}
|
|
745
791
|
const anchor = evaluation.proposal?.kind === 'dispatch' || dispatchOnly ? evaluation.proposal || request.proposals[0] : hereTarget;
|
|
746
792
|
const cheaper = neighbour(catalog, anchor, 'cheaper', { needsVision: request.needsVision, seeingOnly: visionOnly, needsOutput: medium });
|
|
747
793
|
if (cheaper) push(optionFor('c1', { kind: anchor.provider === brain.provider && anchor.kind === 'direct' ? 'direct' : 'dispatch', provider: anchor.provider, model: cheaper.id, effort: null }, 'cheaper', ctx));
|
|
@@ -763,11 +809,103 @@ export function buildRouteOptions({ request, evaluation, brain = {}, brainInfo =
|
|
|
763
809
|
const option = seer ? optionFor('g1', seer, 'capable', ctx) : null;
|
|
764
810
|
if (option && !option.disabled) push(option);
|
|
765
811
|
}
|
|
812
|
+
// Both sides, always: a side with nothing that can run it gets the first candidate that can
|
|
813
|
+
// (or, with none, the first candidate disabled with why, so the card can say it).
|
|
814
|
+
// A saved route on an account is checked later (it may be signed out): it does not count, so its side keeps a fallback.
|
|
815
|
+
const runnable = (kind) => options.some((o) => o.kind === kind && !o.disabled && !(o.saved && o.accountId));
|
|
816
|
+
const offer = (kind, id, candidates) => {
|
|
817
|
+
let first = null;
|
|
818
|
+
for (const [target, badge] of candidates) {
|
|
819
|
+
if (!target?.provider) continue;
|
|
820
|
+
const option = optionFor(id, target, badge, ctx);
|
|
821
|
+
if (options.some((o) => sameTarget(o, option))) continue;
|
|
822
|
+
if (!option.disabled) { push(option); return; }
|
|
823
|
+
first = first || option;
|
|
824
|
+
}
|
|
825
|
+
if (first && !options.some((o) => o.kind === kind)) push(first);
|
|
826
|
+
};
|
|
827
|
+
if (request.taskClass !== 'chat' && !dispatchOnly && !runnable('direct')) offer('direct', 'h1', hereCandidates({ request, catalog, brain }));
|
|
828
|
+
if (request.taskClass !== 'chat' && !runnable('dispatch')) offer('dispatch', 'w1', workerCandidates({ request, catalog, brain, brainInfo }));
|
|
766
829
|
// Order: suggested first, then here, remembered, a capable stand-in, cheaper, stronger.
|
|
767
830
|
const order = { suggested: 0, current: 1, remembered: 2, capable: 3, cheaper: 4, stronger: 5 };
|
|
768
831
|
options.sort((a, b) => (order[a.badge] ?? 9) - (order[b.badge] ?? 9) || String(a.id).localeCompare(String(b.id)));
|
|
769
|
-
|
|
770
|
-
|
|
832
|
+
return { options, ...routeSides({ options, request }) };
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* "Here" on another model of the brain's provider when "here" itself cannot run the task
|
|
837
|
+
* (the brain cannot see a computer task's screen, or the user hid its model): the
|
|
838
|
+
* cheapest one of that provider that can (one that can see first for vision work).
|
|
839
|
+
*/
|
|
840
|
+
function hereCandidates({ request, catalog, brain }) {
|
|
841
|
+
if (!brain.provider) return [];
|
|
842
|
+
const rows = visibleRows(catalog, brain.provider)
|
|
843
|
+
.filter((row) => row.status !== 'unavailable' && row.toolcall !== false && row.id !== brain.model && (!request.needsVision || row.vision === true))
|
|
844
|
+
.sort((a, b) => outPrice(a) - outPrice(b) || tierRank(a.tier) - tierRank(b.tier));
|
|
845
|
+
return rows.map((row) => [{ kind: 'direct', provider: brain.provider, model: row.id, effort: null }, request.needsVision ? 'capable' : null]);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* A worker for a card whose options have none that can run: the brain's own model on a
|
|
850
|
+
* worker (the same model, off this conversation), else a model of its provider that can
|
|
851
|
+
* (the brain's tier first, one that can see for vision work), else elsewhere (one that
|
|
852
|
+
* can see, or the strongest Claude model).
|
|
853
|
+
*/
|
|
854
|
+
function workerCandidates({ request, catalog, brain, brainInfo }) {
|
|
855
|
+
const candidates = [];
|
|
856
|
+
if (brain.provider) candidates.push([{ kind: 'dispatch', provider: brain.provider, model: brain.model || null, effort: brain.effort || null }, 'current']);
|
|
857
|
+
const brainTier = brainInfo?.tier || (brain.model ? findModel(catalog, brain.provider, brain.model)?.row?.tier : null) || null;
|
|
858
|
+
const fits = (row) => row.status !== 'unavailable' && row.toolcall !== false && (!request.needsVision || row.vision === true);
|
|
859
|
+
const closeness = (row) => (brainTier ? Math.abs(tierRank(row.tier) - tierRank(brainTier)) : 0);
|
|
860
|
+
const badge = request.needsVision ? 'capable' : null;
|
|
861
|
+
if (brain.provider) {
|
|
862
|
+
visibleRows(catalog, brain.provider).filter(fits).filter((row) => row.id !== brain.model)
|
|
863
|
+
.sort((a, b) => closeness(a) - closeness(b) || outPrice(a) - outPrice(b))
|
|
864
|
+
.forEach((row) => candidates.push([{ kind: 'dispatch', provider: brain.provider, model: row.id, effort: null }, badge]));
|
|
865
|
+
}
|
|
866
|
+
if (request.needsVision) {
|
|
867
|
+
CATALOG_PROVIDERS.filter((p) => p !== brain.provider)
|
|
868
|
+
.flatMap((p) => visibleRows(catalog, p).filter(fits).map((row) => ({ provider: p, row })))
|
|
869
|
+
.sort((a, b) => outPrice(a.row) - outPrice(b.row) || tierRank(a.row.tier) - tierRank(b.row.tier))
|
|
870
|
+
.forEach(({ provider, row }) => candidates.push([{ kind: 'dispatch', provider, model: row.id, effort: null }, 'capable']));
|
|
871
|
+
} else if (brain.provider !== 'claude-code') {
|
|
872
|
+
const opus = strongestClaude(catalog);
|
|
873
|
+
if (opus) candidates.push([{ kind: 'dispatch', provider: 'claude-code', model: opus.id, effort: null }, null]);
|
|
874
|
+
}
|
|
875
|
+
return candidates;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Which side of the card's Here / Dispatch switch can run the task, and the option each side
|
|
880
|
+
* starts on: { sides: { direct, dispatch }, defaults: { direct, dispatch }, defaultOptionId }.
|
|
881
|
+
* A side is { available, reason, code } (code: worker_only, account_worker, disabled, none).
|
|
882
|
+
* Read from the options as they are sent, after the account check (a "here" that moved to a
|
|
883
|
+
* worker belongs to the dispatch side: its kind is "dispatch").
|
|
884
|
+
*/
|
|
885
|
+
export function routeSides({ options = [], request = {}, defaultOptionId = null } = {}) {
|
|
886
|
+
const usable = (o) => o && !o.disabled;
|
|
887
|
+
const chosen = options.find((o) => o.id === defaultOptionId && usable(o))
|
|
888
|
+
|| options.find((o) => o.badge === 'suggested' && usable(o)) || options.find(usable) || options[0] || null;
|
|
889
|
+
// A side starts on the saved route when Always ask marked it (preferred), else on the chosen option, else its first.
|
|
890
|
+
const firstOf = (kind) => options.find((o) => o.preferred === true && o.kind === kind && usable(o))
|
|
891
|
+
|| (chosen?.kind === kind && usable(chosen) ? chosen : null) || options.find((o) => o.kind === kind && usable(o)) || null;
|
|
892
|
+
const label = request.taskClassLabel || TASK_CLASS_META[request.taskClass]?.label || 'This task';
|
|
893
|
+
const side = (kind) => {
|
|
894
|
+
if (firstOf(kind)) return { available: true, reason: null, code: null };
|
|
895
|
+
if (kind === 'direct' && request.dispatchOnly) return { available: false, code: 'worker_only', reason: `${label} always runs on a worker.` };
|
|
896
|
+
if (kind === 'direct' && options.some((o) => o.kind === 'dispatch' && o.accountKind === 'direct')) {
|
|
897
|
+
return { available: false, code: 'account_worker', reason: "This conversation's account cannot take it, so it runs on a worker." };
|
|
898
|
+
}
|
|
899
|
+
const blocked = options.find((o) => o.kind === kind && o.disabled);
|
|
900
|
+
if (blocked) return { available: false, code: 'disabled', reason: blocked.disabledReason || null };
|
|
901
|
+
return { available: false, code: 'none', reason: kind === 'direct' ? 'Nothing in this conversation can run this task.' : 'No worker model can run this task.' };
|
|
902
|
+
};
|
|
903
|
+
return {
|
|
904
|
+
// The card opens on the chosen option's side, on that side's default.
|
|
905
|
+
defaultOptionId: (chosen && usable(chosen) ? firstOf(chosen.kind)?.id : null) || chosen?.id || 'here',
|
|
906
|
+
sides: { direct: side('direct'), dispatch: side('dispatch') },
|
|
907
|
+
defaults: { direct: firstOf('direct')?.id || null, dispatch: firstOf('dispatch')?.id || null },
|
|
908
|
+
};
|
|
771
909
|
}
|
|
772
910
|
|
|
773
911
|
/** Validate a route-card answer; returns { declined } or { target, optionId, remember }. */
|
|
@@ -776,13 +914,19 @@ export function targetFromAnswer({ card, response = {}, catalog = null, brain =
|
|
|
776
914
|
const optionId = String(response.optionId || response.option_id || '').trim();
|
|
777
915
|
const remember = response.remember === true || (typeof response.remember === 'string' && response.remember.length > 0);
|
|
778
916
|
const option = optionId && optionId !== 'other' ? (card.options || []).find((o) => o.id === optionId) : null;
|
|
917
|
+
// The model this conversation already runs on needs no catalog entry to stay on (an OpenCode model
|
|
918
|
+
// no longer listed as connected, an id the list spells otherwise): "here" on it changes nothing.
|
|
919
|
+
// A hidden one is still refused, as everywhere.
|
|
920
|
+
const onBrain = (target) => target.kind === 'direct' && !!brain.model && target.provider === brain.provider && String(target.model || '') === String(brain.model);
|
|
779
921
|
// A card can remain open while the catalog or hidden-model list changes.
|
|
780
922
|
// Check the chosen target again instead of trusting the option's old state.
|
|
781
923
|
const ensureAvailable = (target) => {
|
|
782
|
-
|
|
924
|
+
const own = onBrain(target);
|
|
925
|
+
if (target.model && (own ? isDisabled(catalog, target) : findModel(catalog, target.provider, target.model) === null)) {
|
|
783
926
|
if (isDisabled(catalog, target)) throw routeError('MODEL_DISABLED', `${target.model} is disabled in the Assistant's Models list. Pick an enabled model.`);
|
|
784
927
|
throw routeError('ROUTE_INVALID', `Unknown model ${target.model} for ${target.provider}.`);
|
|
785
928
|
}
|
|
929
|
+
if (!own && findModel(catalog, target.provider, target.model)?.row?.status === 'unavailable') throw routeError('ROUTE_INVALID', UNAVAILABLE_TEXT);
|
|
786
930
|
if (!target.model && isDisabled(catalog, target)) throw routeError('MODEL_DISABLED', `Every ${target.provider} model is disabled in the Assistant's Models list. Pick another provider.`);
|
|
787
931
|
// Design: only a confirmed vision: true on an available row (unknown or unavailable is refused too);
|
|
788
932
|
// other vision work: a confirmed false.
|
|
@@ -796,10 +940,16 @@ export function targetFromAnswer({ card, response = {}, catalog = null, brain =
|
|
|
796
940
|
};
|
|
797
941
|
// Image / video creation and design always run on a worker: a "here" pick is a dispatch onto that model.
|
|
798
942
|
const dispatchOnly = card.dispatchOnly === true;
|
|
943
|
+
// Only the brain's own provider runs in this conversation: a "here" on another one (an option made
|
|
944
|
+
// before the brain changed provider, or an "other" pick) is a worker, and the answer says so.
|
|
945
|
+
const elsewhere = (provider) => !!brain.provider && provider !== brain.provider;
|
|
799
946
|
if (option) {
|
|
800
947
|
if (option.disabled && !option.accountError) throw routeError('ROUTE_INVALID', option.disabledReason || 'That option is not available for this task.');
|
|
801
948
|
const corrections = [];
|
|
802
|
-
const
|
|
949
|
+
const moved = !dispatchOnly && option.kind === 'direct' && elsewhere(option.provider);
|
|
950
|
+
if (moved) corrections.push(otherProviderCorrection(option.provider, brain));
|
|
951
|
+
const kind = dispatchOnly || moved ? 'dispatch' : option.kind;
|
|
952
|
+
const target = effortChecked({ kind, provider: option.provider, model: option.model || (dispatchOnly && option.kind === 'direct' ? brain.model || null : null), effort: option.effort, ...(option.accountId ? { accountId: option.accountChoice || option.accountId, ...(option.accountKind ? { accountKind: dispatchOnly || moved ? 'dispatch' : option.accountKind } : {}) } : {}) }, catalog, corrections);
|
|
803
953
|
ensureAvailable(target);
|
|
804
954
|
return { target, optionId, remember, corrections };
|
|
805
955
|
}
|
|
@@ -807,7 +957,12 @@ export function targetFromAnswer({ card, response = {}, catalog = null, brain =
|
|
|
807
957
|
if (!t || typeof t !== 'object' || !CATALOG_PROVIDERS.includes(t.provider)) throw routeError('ROUTE_INVALID', 'Pick one of the options or a catalog model.');
|
|
808
958
|
const kind = t.kind === 'direct' && t.provider === brain.provider && !dispatchOnly ? 'direct' : 'dispatch';
|
|
809
959
|
const corrections = [];
|
|
810
|
-
|
|
960
|
+
if (t.kind === 'direct' && kind === 'dispatch') {
|
|
961
|
+
corrections.push(dispatchOnly
|
|
962
|
+
? { field: 'kind', from: 'direct', to: 'dispatch', reason: 'dispatch_only', text: `${TASK_CLASS_META[card.taskClass]?.label || 'This task'} always runs on a worker, not here` }
|
|
963
|
+
: otherProviderCorrection(t.provider, brain));
|
|
964
|
+
}
|
|
965
|
+
const target = effortChecked({ kind, provider: t.provider, model: t.model ? String(t.model) : null, effort: t.effort ? String(t.effort).trim().slice(0, 40) : null, accountId: t.accountId ? String(t.accountId).trim() : null }, catalog, corrections);
|
|
811
966
|
ensureAvailable(target);
|
|
812
967
|
return { target, optionId: 'other', remember, corrections };
|
|
813
968
|
}
|
|
@@ -1013,16 +1168,20 @@ export function createAssistantRouter({
|
|
|
1013
1168
|
: row.signedIn === false ? `Account ${row.label || row.id} (${row.id}) is signed out. Sign it in from the ${providerLabel(target.provider)} account menu, or save another Account in Model routes.` : '';
|
|
1014
1169
|
const accountError = message ? { code: row ? 'ACCOUNT_SIGNED_OUT' : choice === 'auto' ? 'ACCOUNT_NO_SUBSCRIPTION' : 'ACCOUNT_NOT_FOUND', message } : null;
|
|
1015
1170
|
// Provider histories are account-owned. Another account can do the task on
|
|
1016
|
-
// a worker; it cannot resume this account's conversation.
|
|
1171
|
+
// a worker; it cannot resume this account's conversation. That move is always
|
|
1172
|
+
// said (a pinned account the user picked carries no usage reason of its own).
|
|
1017
1173
|
const moved = target.kind === 'direct' && accountId !== (brain.accountId || 'default');
|
|
1018
|
-
const accountCorrection = why
|
|
1174
|
+
const accountCorrection = why
|
|
1175
|
+
? { reason, from: before?.label || choice, to: row?.label || accountId, usage, workerForAccount: moved, message: why + (moved ? ` ${WORKER_FOR_ACCOUNT_TEXT}` : '') }
|
|
1176
|
+
: moved ? { field: 'kind', reason: 'account_worker', from: rows.find(a => a.id === (brain.accountId || 'default'))?.label || brain.accountId || 'default', to: row?.label || accountId, usage, workerForAccount: true, message: `${WORKER_FOR_ACCOUNT_TEXT.replace(/\.$/, '')} (${row?.label || accountId}).` }
|
|
1177
|
+
: null;
|
|
1019
1178
|
const enriched = { ...target, accountKind: target.accountKind || target.kind, ...(moved ? { kind: 'dispatch' } : {}), accountId, account, accountError, accountChoice: target.accountChoice || choice, accountCorrection };
|
|
1020
1179
|
if (check && accountError) throw routeError(accountError.code, message, 409, { accountId, target: enriched });
|
|
1021
1180
|
return enriched;
|
|
1022
1181
|
}
|
|
1023
1182
|
function accountCorrections(target, base = []) { return target.accountCorrection && !base.some(c => c.reason === target.accountCorrection.reason && c.from === target.accountCorrection.from && c.to === target.accountCorrection.to && c.message === target.accountCorrection.message) ? [...base, target.accountCorrection] : base; }
|
|
1024
|
-
function checkedTarget(target, brain, cat, route = null, fallback = null) {
|
|
1025
|
-
try { return accountTarget(target, brain, cat, { fallback }); }
|
|
1183
|
+
function checkedTarget(target, brain, cat, route = null, fallback = null, exact = false) {
|
|
1184
|
+
try { return accountTarget(target, brain, cat, { fallback, select: !exact || !target.accountId || target.accountId === 'auto' || target.accountChoice === 'auto' }); }
|
|
1026
1185
|
catch (error) {
|
|
1027
1186
|
if (route) call('routeEvent', route.sessionId, 'declined', routeView({ ...route, target: error.target, status: 'declined', reason: error.message, reasonCode: error.code }, { catalog: cat, brain }));
|
|
1028
1187
|
throw error;
|
|
@@ -1081,8 +1240,14 @@ export function createAssistantRouter({
|
|
|
1081
1240
|
return call('continueDirect', sessionId, { target, route: source, text: source.summary || '' }) !== false;
|
|
1082
1241
|
}
|
|
1083
1242
|
|
|
1084
|
-
function resultFor(status, { routeId, mode, source = null, target = null, card = null, runIds = [], corrections = [], cat = null, brain = {}, brainInfo = null, remembered = false, continuation = false, sessionId = null } = {}) {
|
|
1243
|
+
function resultFor(status, { routeId, mode, source = null, target = null, card = null, runIds = [], corrections = [], cat = null, brain = {}, brainInfo = null, remembered = false, continuation = false, sessionId = null, switched = null, droppedRuns = [] } = {}) {
|
|
1085
1244
|
const described = target ? { ...describeTarget(target, { catalog: cat, brain, brainInfo }), continuation } : null;
|
|
1245
|
+
// The user's pick was the other kind than the brain proposed; a "here" pick on another account runs on a worker.
|
|
1246
|
+
const kindLead = source !== 'user' ? ''
|
|
1247
|
+
: target?.kind === 'dispatch' && target.accountKind === 'direct'
|
|
1248
|
+
? 'The user chose to do this here, but on another account than this conversation\'s, so it runs on a worker. '
|
|
1249
|
+
: switched === 'direct' ? `The user chose to do this here, not on a worker${droppedRuns.length ? ` (the held run${droppedRuns.length === 1 ? '' : 's'} ${droppedRuns.join(', ')} ${droppedRuns.length === 1 ? 'was' : 'were'} not started)` : ''}. `
|
|
1250
|
+
: switched === 'dispatch' ? 'The user chose a worker for this, not here. ' : '';
|
|
1086
1251
|
// A saved route that replaced the brain's pick (never mode) says so first.
|
|
1087
1252
|
const binding = (corrections || []).find((c) => c?.reason === 'remembered_route');
|
|
1088
1253
|
const skipped = (corrections || []).find((c) => c?.reason === 'remembered_disabled');
|
|
@@ -1099,7 +1264,7 @@ export function createAssistantRouter({
|
|
|
1099
1264
|
ok: true, status, routeId, mode, source, target: described, remembered, continuation,
|
|
1100
1265
|
corrections, runIds, expiresAt: card?.expiresAt ? new Date(card.expiresAt).toISOString() : null,
|
|
1101
1266
|
...(shares ? { budget: shares } : {}),
|
|
1102
|
-
next: `${lead}${nextText(status, described, { routeId, label: described?.label })}`,
|
|
1267
|
+
next: `${lead}${status === 'approved' ? kindLead : ''}${nextText(status, described, { routeId, label: described?.label })}`,
|
|
1103
1268
|
};
|
|
1104
1269
|
}
|
|
1105
1270
|
|
|
@@ -1151,13 +1316,23 @@ export function createAssistantRouter({
|
|
|
1151
1316
|
const routeId = `route-${randomId()}`;
|
|
1152
1317
|
const requestId = routeId;
|
|
1153
1318
|
const r = routing();
|
|
1154
|
-
const
|
|
1319
|
+
const built = buildRouteOptions({ request, evaluation, brain, brainInfo, catalog: cat, routing: r, coverage: evaluation.coverage || null });
|
|
1320
|
+
const { options } = built;
|
|
1155
1321
|
for (const option of options) {
|
|
1156
1322
|
const first = request.proposals[0];
|
|
1157
1323
|
const fallback = origin === 'dispatch' && option.badge !== 'remembered' && option.provider === first?.provider ? first.accountId : null;
|
|
1158
1324
|
Object.assign(option, accountTarget(option, brain, cat, { check: false, fallback }));
|
|
1159
1325
|
if (option.accountError) { option.disabled = true; option.disabledReason = option.accountError.message; }
|
|
1326
|
+
// A "here" whose account cannot take it (this conversation's is out of usage) runs on a worker
|
|
1327
|
+
// on another account: the card names it as a worker and says why, never "Do it here".
|
|
1328
|
+
if (option.kind === 'dispatch' && option.accountKind === 'direct') {
|
|
1329
|
+
const described = describeTarget(option, { catalog: cat, brain, brainInfo });
|
|
1330
|
+
option.label = `${providerLabel(option.provider)} · ${described?.label || option.model || 'default'}${option.effort ? ` · ${option.effort}` : ''}`;
|
|
1331
|
+
option.warning = option.accountCorrection?.message || WORKER_FOR_ACCOUNT_TEXT;
|
|
1332
|
+
}
|
|
1160
1333
|
}
|
|
1334
|
+
// The sides and defaults as the options are sent (the account check can disable or move one).
|
|
1335
|
+
const { defaultOptionId, sides, defaults } = routeSides({ options, request, defaultOptionId: built.defaultOptionId });
|
|
1161
1336
|
const computer = computerConsent({ session, request, options, defaultOptionId, brain, origin });
|
|
1162
1337
|
const expiresAt = now() + r.cardTimeoutMinutes * 60_000;
|
|
1163
1338
|
const pref = r.preferences?.[request.taskClass];
|
|
@@ -1165,7 +1340,7 @@ export function createAssistantRouter({
|
|
|
1165
1340
|
routeId, requestId, sessionId, origin, status: 'pending', mode, workflowId,
|
|
1166
1341
|
taskClass: request.taskClass, summary: request.summary, confidence: request.confidence, needsVision: request.needsVision,
|
|
1167
1342
|
needsOutput: request.needsOutput || null, dispatchOnly: request.dispatchOnly === true, visionOnly: request.visionOnly === true,
|
|
1168
|
-
reasons: evaluation.reasons, request, options, defaultOptionId, runIds: new Set(runIds), waiters: new Set(),
|
|
1343
|
+
reasons: evaluation.reasons, request, options, defaultOptionId, sides, defaults, runIds: new Set(runIds), waiters: new Set(),
|
|
1169
1344
|
createdAt: now(), expiresAt, timer: null, mailboxOnDecision: origin === 'dispatch', brain,
|
|
1170
1345
|
// The option whose approval is also the computer approval (computerConsent), as this card was sent.
|
|
1171
1346
|
computer,
|
|
@@ -1184,6 +1359,8 @@ export function createAssistantRouter({
|
|
|
1184
1359
|
visionOnly: request.visionOnly === true,
|
|
1185
1360
|
brain: { provider: brain.provider, model: brain.model || null, label: brainInfo?.label || brain.model || providerLabel(brain.provider), vision: brainInfo?.vision ?? null },
|
|
1186
1361
|
options, defaultOptionId,
|
|
1362
|
+
// The card's Here / Dispatch switch: which side can run the task (and why not), and the option each side starts on.
|
|
1363
|
+
sides, defaults,
|
|
1187
1364
|
other: { allowed: true, providers: CATALOG_PROVIDERS, catalogPath: '/api/assistant/catalog' },
|
|
1188
1365
|
remember: { available: request.taskClass !== 'chat', taskClass: request.taskClass, label: request.taskClassLabel, current: pref ? (pref.label || [pref.provider, pref.model].filter(Boolean).join('/')) : null },
|
|
1189
1366
|
decline: { allowed: true, label: 'Cancel' },
|
|
@@ -1384,7 +1561,7 @@ export function createAssistantRouter({
|
|
|
1384
1561
|
return result;
|
|
1385
1562
|
}
|
|
1386
1563
|
const { optionId, remember } = parsed;
|
|
1387
|
-
const target = checkedTarget(parsed.target, brain, cat);
|
|
1564
|
+
const target = checkedTarget(parsed.target, brain, cat, null, null, card.mode === 'always-ask');
|
|
1388
1565
|
const corrections = accountCorrections(target, parsed.corrections || []);
|
|
1389
1566
|
// The session budget must be able to pay for the pick: a provider with no share left divides the
|
|
1390
1567
|
// unspent budget again first; still none, and the card stays open with the reason.
|
|
@@ -1392,21 +1569,28 @@ export function createAssistantRouter({
|
|
|
1392
1569
|
try { await budget.ensureCovered(card.sessionId, { provider: target.provider, forBrain: target.kind === 'direct', free: targetIsFree(cat, target) }); } catch { /* decided on the shares as they are */ }
|
|
1393
1570
|
if (cards.get(routeId) !== card || card.status !== 'pending') openCard(routeId, { sessionId });
|
|
1394
1571
|
if (!budgetCovers(coverageOf(card.sessionId), cat, target)) {
|
|
1572
|
+
const unpriced = unpricedText(target.provider);
|
|
1573
|
+
if (unpriced) throw routeError('BUDGET_UNPRICED', unpriced, 409, { provider: target.provider });
|
|
1395
1574
|
throw routeError('BUDGET_SHARE_EXHAUSTED', `Budget: the ${providerLabel(target.provider)} share of this session's budget is used up. Pick another option (or a free model), or raise the budget.`, 409, { provider: target.provider });
|
|
1396
1575
|
}
|
|
1397
1576
|
}
|
|
1577
|
+
// "Here" on a held dispatch's card: the brain does the task in this conversation, so the held
|
|
1578
|
+
// worker runs are not started (they stop, route_here) and the brain is told to do it itself.
|
|
1579
|
+
const keptHere = card.runIds.size > 0 && target.kind === 'direct';
|
|
1398
1580
|
// Every held run must be able to start on the pick (the dispatcher's checkHeld: a design run's
|
|
1399
1581
|
// sight and browser settings on that provider). A pick one would refuse is not approved, saved
|
|
1400
|
-
// or reported started: the card stays open with the reason.
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1582
|
+
// or reported started: the card stays open with the reason. A "here" pick starts none.
|
|
1583
|
+
if (!keptHere) {
|
|
1584
|
+
for (const runId of card.runIds) {
|
|
1585
|
+
const refusal = call('checkHeld', runId, target);
|
|
1586
|
+
if (refusal) throw routeError(refusal.code || 'ROUTE_INVALID', refusal.message || 'The held run cannot start on that model.', Number(refusal.status) || 400);
|
|
1587
|
+
}
|
|
1404
1588
|
}
|
|
1405
1589
|
// "Do it here" on another model continues in the turn the card was raised in,
|
|
1406
1590
|
// handed over first. The runtime refuses a pick that turn no longer owns (a
|
|
1407
1591
|
// newer prompt, a Stop or the session closing came after it): then nothing is
|
|
1408
1592
|
// approved or remembered, and the card expires on every host.
|
|
1409
|
-
const continuation = !card.runIds.size && continuesHere(target, brain);
|
|
1593
|
+
const continuation = (keptHere || !card.runIds.size) && continuesHere(target, brain);
|
|
1410
1594
|
// The decision is claimed here, before the first side effect (the runtime's continuation, the
|
|
1411
1595
|
// held runs, the mailbox): nothing a sink does can close or decide this card again meanwhile.
|
|
1412
1596
|
card.status = 'approved';
|
|
@@ -1424,21 +1608,23 @@ export function createAssistantRouter({
|
|
|
1424
1608
|
remembered = true;
|
|
1425
1609
|
} catch (error) { log('router:remember-error', error?.message || String(error)); }
|
|
1426
1610
|
}
|
|
1611
|
+
const heldRuns = keptHere ? [] : [...card.runIds];
|
|
1612
|
+
const droppedRuns = keptHere ? [...card.runIds] : [];
|
|
1427
1613
|
const route = {
|
|
1428
1614
|
routeId, status: 'approved', sessionId: card.sessionId, taskClass: card.taskClass, summary: card.summary, confidence: card.confidence,
|
|
1429
|
-
target, decidedBy: 'user', remembered, optionId, corrections, mode: card.mode, runIds:
|
|
1615
|
+
target, decidedBy: 'user', remembered, optionId, corrections, mode: card.mode, runIds: heldRuns,
|
|
1430
1616
|
reason: card.options.find((o) => o.id === optionId)?.reason || null, alternatives: card.alternatives, expiresAt: now() + DECIDED_TTL_MS,
|
|
1431
1617
|
};
|
|
1432
1618
|
decided.set(routeId, route);
|
|
1433
1619
|
noteBudgetTask(card.sessionId, { taskClass: card.taskClass, summary: card.summary }, target);
|
|
1434
|
-
const heldRuns = [...card.runIds];
|
|
1435
1620
|
// A remote session: the held run this card was for is its one dispatch.
|
|
1436
1621
|
if (heldRuns.length && session.remote === true) route.consumed = true;
|
|
1622
|
+
for (const runId of droppedRuns) call('declineHeld', runId, 'route_here');
|
|
1437
1623
|
for (const runId of heldRuns) call('startHeld', runId, target, { routeId, decidedBy: 'user', remembered, optionId });
|
|
1438
1624
|
const view = routeView(route, { catalog: cat, brain, brainInfo });
|
|
1439
1625
|
call('routeEvent', card.sessionId, 'decided', view);
|
|
1440
1626
|
const inTurnWaiter = card.waiters.size > 0;
|
|
1441
|
-
const viaMailbox = !inTurnWaiter && !continuation && (card.mailboxOnDecision ||
|
|
1627
|
+
const viaMailbox = !inTurnWaiter && !continuation && (card.mailboxOnDecision || card.runIds.size > 0);
|
|
1442
1628
|
// The card said the Mac will be controlled, and this is the option it said it for: the yes is
|
|
1443
1629
|
// also the computer approval of the turn that carries the route out (this one, or the mailbox
|
|
1444
1630
|
// turn that executes the decided route). From what the card recorded when it was sent; the
|
|
@@ -1447,9 +1633,19 @@ export function createAssistantRouter({
|
|
|
1447
1633
|
// `authority` is passed on untouched and never kept: the runtime alone can tell whose it is.
|
|
1448
1634
|
call('computerApproved', card.sessionId, { routeId, origin, viaMailbox, authority });
|
|
1449
1635
|
}
|
|
1450
|
-
|
|
1636
|
+
// The brain proposed one kind and the user picked the other (the card's Here / Dispatch switch):
|
|
1637
|
+
// the result says so first, so the brain follows the pick, not its proposal.
|
|
1638
|
+
const proposed = card.request?.proposals?.[0]?.kind || null;
|
|
1639
|
+
const picked = target.accountKind || target.kind;
|
|
1640
|
+
const switched = proposed && proposed !== picked ? picked : null;
|
|
1641
|
+
const result = resultFor('approved', { routeId, mode: card.mode, source: 'user', target, corrections, cat, brain, brainInfo, remembered, runIds: heldRuns, continuation, sessionId: card.sessionId, switched, droppedRuns });
|
|
1451
1642
|
if (viaMailbox) {
|
|
1452
|
-
|
|
1643
|
+
const label = view.target?.label || 'a model';
|
|
1644
|
+
const what = card.summary ? `"${card.summary}"` : 'this task';
|
|
1645
|
+
const text = heldRuns.length ? `The user picked ${label}; the held run${heldRuns.length === 1 ? '' : 's'} started.`
|
|
1646
|
+
: droppedRuns.length ? `The user chose to do ${what} here with ${label} instead of on a worker: the held run${droppedRuns.length === 1 ? '' : 's'} ${droppedRuns.join(', ')} ${droppedRuns.length === 1 ? 'was' : 'were'} not started. Do that task here yourself now; do not dispatch it again.`
|
|
1647
|
+
: `The user picked ${label}. ${result.next}`;
|
|
1648
|
+
call('mailbox', card.sessionId, { kind: 'route_decided', route: view, runIds: heldRuns, ...(droppedRuns.length ? { droppedRunIds: droppedRuns } : {}), text });
|
|
1453
1649
|
}
|
|
1454
1650
|
settle(card, { status: 'approved', result });
|
|
1455
1651
|
closeCard(card);
|
|
@@ -1479,11 +1675,17 @@ export function createAssistantRouter({
|
|
|
1479
1675
|
const remote = session.remote === true;
|
|
1480
1676
|
if (routeId) {
|
|
1481
1677
|
const approved = decided.get(routeId);
|
|
1678
|
+
// The user picked "here" on this route's card: it runs in this conversation, never on a worker
|
|
1679
|
+
// (a brain's own direct route, or a saved one in Never ask, keeps its old dispatch behaviour).
|
|
1680
|
+
if (approved && approved.sessionId === sessionId && approved.target?.kind === 'direct' && approved.decidedBy === 'user') {
|
|
1681
|
+
const where = approved.target.model ? ` on ${approved.target.model}` : '';
|
|
1682
|
+
throw routeError('ROUTE_IS_DIRECT', `Route ${routeId} was decided by the user to run here, in this conversation${where}, not on a worker. Do the task yourself now; do not dispatch it. If it should go to a worker after all, ask the user, then call agent_route again.`, 409, { routeId, target: approved.target });
|
|
1683
|
+
}
|
|
1482
1684
|
if (approved && approved.sessionId === sessionId && approved.target && !approved.consumed) {
|
|
1483
1685
|
// A remote session's route is claimed here, so a second dispatch cannot take it meanwhile.
|
|
1484
1686
|
// `claimed` tells the dispatcher to give it back (releaseRoute) if it refuses the launch
|
|
1485
1687
|
// before a run exists: only the run it starts uses the route up.
|
|
1486
|
-
const t = checkedTarget(approved.target, brain, cat, approved);
|
|
1688
|
+
const t = checkedTarget(approved.target, brain, cat, approved, null, approved.mode === 'always-ask' && approved.decidedBy === 'user');
|
|
1487
1689
|
approved.target = t;
|
|
1488
1690
|
approved.corrections = accountCorrections(t, approved.corrections);
|
|
1489
1691
|
// The approved provider may have run out of share since the approval: the unspent budget is
|
|
@@ -1491,6 +1693,11 @@ export function createAssistantRouter({
|
|
|
1491
1693
|
let runTarget = t;
|
|
1492
1694
|
let budgetCorrection = null;
|
|
1493
1695
|
if (budgeted() && !budgetCovers(coverageOf(sessionId), cat, t)) {
|
|
1696
|
+
if (approved.mode === 'always-ask' && approved.decidedBy === 'user') {
|
|
1697
|
+
const unpriced = unpricedText(t.provider);
|
|
1698
|
+
if (unpriced) throw routeError('BUDGET_UNPRICED', `${unpriced} Choose a new task route.`, 409, { routeId, provider: t.provider });
|
|
1699
|
+
throw routeError('BUDGET_SHARE_EXHAUSTED', 'The selected model has no session budget left. Choose a new task route or raise the budget.', 409, { routeId, provider: t.provider });
|
|
1700
|
+
}
|
|
1494
1701
|
try { await budget.ensureCovered(sessionId, { provider: t.provider, forBrain: false, free: targetIsFree(cat, t), task: { taskClass: approved.taskClass, summary: approved.summary || null, provider: t.provider, model: t.model || null } }); } catch { /* decided on the shares as they are */ }
|
|
1495
1702
|
const request = normalizeRouteRequest({ task_class: approved.taskClass, summary: approved.summary || '', confidence: 1, needs_vision: !!spec.usesComputer, proposals: [{ kind: 'dispatch', provider: t.provider, model: t.model || null, effort: t.effort || null }] }, { brain });
|
|
1496
1703
|
const moved = budgetMove({ target: { ...t, kind: 'dispatch' }, request, catalog: cat, brain, brainInfo, routing: r, coverage: coverageOf(sessionId) });
|
|
@@ -1500,7 +1707,7 @@ export function createAssistantRouter({
|
|
|
1500
1707
|
if (moved.correction) { runTarget = moved.target; budgetCorrection = moved.correction; }
|
|
1501
1708
|
}
|
|
1502
1709
|
if (remote) approved.consumed = true;
|
|
1503
|
-
const applied = { ...spec, provider: runTarget.provider, model: runTarget.model || null, effort: runTarget.effort
|
|
1710
|
+
const applied = { ...spec, provider: runTarget.provider, model: runTarget.model || null, effort: runTarget.effort ?? null, accountId: runTarget.accountId || null, codexAccountId: null, claudeAccountId: null };
|
|
1504
1711
|
const meta = routeMeta(approved, cat, brain, brainInfo);
|
|
1505
1712
|
const route = budgetCorrection ? { ...meta, target: describeTarget(runTarget, { catalog: cat, brain, brainInfo }), corrections: [...(meta.corrections || []), budgetCorrection] } : meta;
|
|
1506
1713
|
return { action: 'start', spec: applied, route: { ...route, requested: { provider: spec.provider || null, model: spec.model || null, effort: spec.effort || null } }, ...(remote ? { claimed: true } : {}) };
|
|
@@ -1630,7 +1837,7 @@ export function createAssistantRouter({
|
|
|
1630
1837
|
const mode = modeFor(sessionId);
|
|
1631
1838
|
const prefs = Object.entries(r.preferences || {}).filter(([, p]) => p?.provider)
|
|
1632
1839
|
.map(([cls, p]) => `${cls} → ${p.kind === 'direct' ? 'here' : p.provider}${p.model ? `/${p.model}` : ''}${p.effort ? ` (${p.effort})` : ''}${hasRouteAccounts(p.provider) ? ` as ${routeAccountText(p, { accounts: liveAccounts(catalog.peek?.()), brain: getSession(sessionId)?.brain, workers: { codex: configStore?.workers?.()?.codexAccount, 'claude-code': configStore?.workers?.()?.claudeAccount } })}` : ''}`);
|
|
1633
|
-
const modeLine = mode === 'always-ask' ? 'always ask (call agent_route before every actionable task; the user picks the model)'
|
|
1840
|
+
const modeLine = mode === 'always-ask' ? 'always ask (call agent_route before every actionable task; the user picks the model, and whether it runs here or on a worker)'
|
|
1634
1841
|
: mode === 'never' ? 'never ask (agent_route approves at once; the saved routes below are binding and agent_route applies them over your pick, so classify the task correctly)'
|
|
1635
1842
|
: `ask when unsure (agent_route asks the user below ${Math.round(r.askBelow * 100)}% confidence or for unknown/missing models)`;
|
|
1636
1843
|
const text = `[SynaBun Router] Route mode: ${modeLine}.${prefs.length ? ` ${mode === 'never' ? 'Saved routes (binding)' : 'Remembered routes'}: ${prefs.join('; ')}.` : ''}`;
|