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
|
@@ -10,13 +10,15 @@
|
|
|
10
10
|
// normalizeRouteRequest / routeOptions / buildRouteResponse / routeLineParts
|
|
11
11
|
// are pure (node:test covers them); nothing touches `document` at import time.
|
|
12
12
|
//
|
|
13
|
+
// The card's Here / Dispatch switch reads the packet's `sides` / `defaults`
|
|
14
|
+
// (routeCardSides; derived from the options' kinds for an older server).
|
|
13
15
|
// Wire contract: answer = control_response { kind:'route', optionId, target?,
|
|
14
16
|
// remember, decline? }; "Other…" sends optionId:'other' + target. Invalid
|
|
15
17
|
// answers come back as { type:'error', code:'ROUTE_INVALID' } — the card stays
|
|
16
18
|
// open. `synabun.route` events (phase card|auto|decided|pending|expired|
|
|
17
19
|
// declined|cancelled) update the line in place by routeId.
|
|
18
20
|
|
|
19
|
-
import {
|
|
21
|
+
import { fetchClaudeAccounts, fetchCodexAccounts, getEffortLevelsForModel } from '../agent-runtime-options.js';
|
|
20
22
|
import { fetchManageCatalog, patchHiddenModels } from './asst-api.js';
|
|
21
23
|
import { catalogModelRow, fmtModelPrice, hiddenModelFor, ICON_CARET, invalidateModelCatalog, openModelMenu } from './asst-brain-picker.js';
|
|
22
24
|
// The Routes editor's model lists (where they come from, how they stay current): asst-route-models.js.
|
|
@@ -27,12 +29,17 @@ import { accountUsageText, autoAccountLabel } from '../account-usage.js';
|
|
|
27
29
|
import {
|
|
28
30
|
DEFAULT_ROUTE_MODE,
|
|
29
31
|
fmtConfidence,
|
|
32
|
+
modelShortName,
|
|
30
33
|
normalizeBrain,
|
|
31
34
|
normalizeProvider,
|
|
32
35
|
normalizeRouteMode,
|
|
36
|
+
providerLabel,
|
|
37
|
+
providerMeta,
|
|
33
38
|
ROUTE_MODES,
|
|
34
39
|
routeKindLabel,
|
|
35
40
|
routeTargetLabel,
|
|
41
|
+
OPTIONAL_PROVIDERS,
|
|
42
|
+
UNPRICED_PROVIDERS,
|
|
36
43
|
} from './asst-state.js';
|
|
37
44
|
|
|
38
45
|
function str(v) { return v == null ? '' : String(v).trim(); }
|
|
@@ -107,9 +114,13 @@ function normalizeRouteOption(o) {
|
|
|
107
114
|
provider: normalizeProvider(src.provider, str(src.provider) || null),
|
|
108
115
|
model: str(src.model),
|
|
109
116
|
effort: str(src.effort) || null,
|
|
110
|
-
...(Object.hasOwn(src, 'accountId') ? { accountId: src.accountId, account: obj(src.account), accountError: obj(src.accountError) } : {}),
|
|
117
|
+
...(Object.hasOwn(src, 'accountId') ? { accountId: src.accountId, accountChoice: src.accountChoice || null, account: obj(src.account), accountError: obj(src.accountError) } : {}),
|
|
118
|
+
// 'direct' on a worker ("here" on an account other than this conversation's): it runs on a worker.
|
|
119
|
+
accountKind: str(src.accountKind) || null,
|
|
111
120
|
label: str(src.label),
|
|
112
121
|
badge: str(src.badge) || null,
|
|
122
|
+
// Always ask: the user's saved route for the class (its side starts on it; the card still asks).
|
|
123
|
+
saved: src.saved === true,
|
|
113
124
|
tier: str(src.tier) || null,
|
|
114
125
|
vision: src.vision === true,
|
|
115
126
|
price: priceOf(src.price),
|
|
@@ -122,6 +133,13 @@ function normalizeRouteOption(o) {
|
|
|
122
133
|
};
|
|
123
134
|
}
|
|
124
135
|
|
|
136
|
+
function normalizeRouteSides(sides) {
|
|
137
|
+
const src = obj(sides);
|
|
138
|
+
if (!src) return null;
|
|
139
|
+
const side = (s) => (obj(s) ? { available: s.available !== false, reason: str(s.reason) || null, code: str(s.code) || null } : null);
|
|
140
|
+
return { direct: side(src.direct), dispatch: side(src.dispatch) };
|
|
141
|
+
}
|
|
142
|
+
|
|
125
143
|
/** Normalize a route control packet (or its `request`) into the card model. */
|
|
126
144
|
export function normalizeRouteRequest(packet) {
|
|
127
145
|
const p = obj(packet);
|
|
@@ -161,6 +179,9 @@ export function normalizeRouteRequest(packet) {
|
|
|
161
179
|
},
|
|
162
180
|
options: arr(req.options).map(normalizeRouteOption).filter(Boolean),
|
|
163
181
|
defaultOptionId: str(req.defaultOptionId) || null,
|
|
182
|
+
// The Here / Dispatch switch, as the router read it (null from a server older than the switch).
|
|
183
|
+
sides: normalizeRouteSides(req.sides),
|
|
184
|
+
defaults: obj(req.defaults) ? { direct: str(req.defaults.direct) || null, dispatch: str(req.defaults.dispatch) || null } : null,
|
|
164
185
|
other: {
|
|
165
186
|
allowed: other.allowed !== false,
|
|
166
187
|
providers: arr(other.providers).map(String),
|
|
@@ -219,15 +240,25 @@ export function routeReasonLabels(n, t) {
|
|
|
219
240
|
return arr(n?.reasons).map(r => reasonLabel(r, tr, n?.requires)).filter(Boolean);
|
|
220
241
|
}
|
|
221
242
|
|
|
243
|
+
/** The word for a route kind in the user's language: "here", "dispatch", "computer". */
|
|
244
|
+
export function routeKindWord(kind, t = null) {
|
|
245
|
+
const key = str(kind).toLowerCase();
|
|
246
|
+
const word = key === 'direct' ? 'here' : key;
|
|
247
|
+
return ['here', 'dispatch', 'computer'].includes(word) ? tf(t)(`assistant.routes.kind.${word}`, routeKindLabel(key)) : routeKindLabel(key);
|
|
248
|
+
}
|
|
249
|
+
|
|
222
250
|
function badgeText(o, n, tr) {
|
|
223
251
|
const pct = fmtConfidence(n?.confidence);
|
|
252
|
+
if (o.saved) return tr('assistant.routes.badge.saved', 'Saved route');
|
|
224
253
|
switch (o.badge) {
|
|
225
254
|
case 'suggested': return pct ? tr('assistant.routes.badge.suggestedPct', 'Suggested · {pct}', { pct }) : tr('assistant.routes.badge.suggested', 'Suggested');
|
|
226
255
|
case 'remembered': return tr('assistant.routes.badge.remembered', 'Your choice');
|
|
227
|
-
|
|
256
|
+
// On a worker, the model this conversation runs on is "the same model"; here it is the current one.
|
|
257
|
+
case 'current': return o.kind === 'dispatch' ? tr('assistant.routes.badge.sameModel', 'Same model') : tr('assistant.routes.badge.current', 'Current');
|
|
228
258
|
case 'capable':
|
|
229
|
-
if (
|
|
230
|
-
|
|
259
|
+
if (n?.requires) return n.requires === 'video' ? tr('assistant.routes.badge.makesVideo', 'Makes video') : tr('assistant.routes.badge.makesImages', 'Makes images');
|
|
260
|
+
if (n?.visionOnly || n?.needsVision) return tr('assistant.routes.badge.canSee', 'Can see');
|
|
261
|
+
return tr('assistant.routes.badge.canRun', 'Can run it');
|
|
231
262
|
case 'cheaper': return tr('assistant.routes.badge.cheaper', 'Cheaper');
|
|
232
263
|
case 'stronger': return tr('assistant.routes.badge.stronger', 'Stronger');
|
|
233
264
|
default: return o.badge ? humanizeClass(o.badge) : '';
|
|
@@ -271,26 +302,78 @@ export function routeOptions(n, { brainLabel = '', t = null } = {}) {
|
|
|
271
302
|
if (!o.isHere && o.id !== 'here' && o.kind === 'direct' && brainProvider && o.provider === brainProvider
|
|
272
303
|
&& modelKey(o.model) === modelKey(n.brain?.model)) o.isHere = true;
|
|
273
304
|
}
|
|
274
|
-
|
|
305
|
+
// Only a direct option is "here": a "here" the account rule moved to a worker (kind "dispatch") is a worker row.
|
|
306
|
+
const hereRow = (o) => o.kind === 'direct' && (o.id === 'here' || !!o.isHere);
|
|
307
|
+
const rank = (o) => (o.id === defaultId ? -2 : hereRow(o) ? -1 : (BADGE_RANK[o.badge] ?? 9));
|
|
275
308
|
kept.sort((a, b) => rank(a) - rank(b));
|
|
276
309
|
if (!kept.some(o => o.id === defaultId && !o.disabled)) defaultId = kept.find(o => !o.disabled)?.id || null;
|
|
277
310
|
const hereModel = brainLabel || routeTargetLabel(n.brain) || tr('assistant.routes.currentModel', 'the current model');
|
|
311
|
+
// The router labels every option done here "Do it here with <model>" (English): the title names the model once,
|
|
312
|
+
// in the user's language ("Do it here with Do it here with Sonnet 5" before).
|
|
313
|
+
const labelOf = (o) => str(o.label).replace(/^do it here with\s+/i, '');
|
|
278
314
|
return kept.map((o, i) => {
|
|
279
|
-
const isHere = o
|
|
315
|
+
const isHere = hereRow(o);
|
|
280
316
|
const label = routeTargetLabel(o);
|
|
281
317
|
return {
|
|
282
318
|
...o,
|
|
283
319
|
number: i + 1,
|
|
284
320
|
here: isHere,
|
|
285
|
-
// "here" is the brain's own model: prefer its display label over a raw id.
|
|
286
|
-
|
|
321
|
+
// "here" is the brain's own model: prefer its display label over a raw id. Another model of its
|
|
322
|
+
// provider (a continuation) is done here too, and says so the same way.
|
|
323
|
+
title: isHere ? tr('assistant.routes.doHere', 'Do it here with {model}', { model: labelOf(o) || hereModel })
|
|
324
|
+
: o.kind === 'direct' ? tr('assistant.routes.doHere', 'Do it here with {model}', { model: labelOf(o) || modelShortName(o.model) || o.id }) : (label || o.id),
|
|
287
325
|
badgeText: badgeText(o, n, tr),
|
|
288
|
-
disabledReason: o.budget === 'share_used_up' ? routeBudgetReason({ provider: o.provider }, t) : o.disabledReason,
|
|
326
|
+
disabledReason: o.budget === 'share_used_up' || o.budget === 'unpriced' ? routeBudgetReason({ provider: o.provider, unpriced: o.budget === 'unpriced' }, t) : o.disabledReason,
|
|
289
327
|
selected: o.id === defaultId,
|
|
290
328
|
};
|
|
291
329
|
});
|
|
292
330
|
}
|
|
293
331
|
|
|
332
|
+
export const ROUTE_SIDES = Object.freeze(['direct', 'dispatch']);
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* The card's Here / Dispatch switch (pure): `options` (routeOptions) split by where they run, each
|
|
336
|
+
* side numbered from 1, with whether it can be chosen, why not, and the option it starts on.
|
|
337
|
+
* → { initial: 'direct' | 'dispatch', sides: { direct, dispatch } }, a side being
|
|
338
|
+
* { kind, options, available, reason, code, needsPick, defaultId }.
|
|
339
|
+
* The router says which side can run (n.sides) and where each starts (n.defaults); a card from a
|
|
340
|
+
* server older than the switch has neither, so both are read from the options: there, a card with
|
|
341
|
+
* no worker option still has a Dispatch side, empty, that asks for a model (`needsPick`: the model
|
|
342
|
+
* picker answers optionId "other" with a dispatch target, which every server takes).
|
|
343
|
+
*/
|
|
344
|
+
export function routeCardSides(n, options, { t = null } = {}) {
|
|
345
|
+
const tr = tf(t);
|
|
346
|
+
const server = n?.sides || null;
|
|
347
|
+
const side = (kind) => {
|
|
348
|
+
const list = arr(options).filter((o) => (o.kind === 'direct' ? 'direct' : 'dispatch') === kind).map((o, i) => ({ ...o, number: i + 1 }));
|
|
349
|
+
const usable = list.filter((o) => !o.disabled);
|
|
350
|
+
const said = server?.[kind] || null;
|
|
351
|
+
const out = { kind, options: list, available: usable.length > 0, reason: null, code: null, needsPick: false, defaultId: null };
|
|
352
|
+
out.defaultId = (usable.find((o) => o.id === n?.defaultOptionId) || usable.find((o) => o.id === n?.defaults?.[kind]) || usable.find((o) => o.selected) || usable[0])?.id || null;
|
|
353
|
+
if (out.available) return out;
|
|
354
|
+
if (kind === 'direct' && n?.dispatchOnly) {
|
|
355
|
+
return { ...out, code: 'worker_only', reason: tr('assistant.routes.side.workerOnly', '{taskClass} always runs on a worker.', { taskClass: n.taskClassLabel || humanizeClass(n.taskClass) }) };
|
|
356
|
+
}
|
|
357
|
+
if (kind === 'direct' && (said?.code === 'account_worker' || arr(options).some((o) => o.kind === 'dispatch' && o.accountKind === 'direct'))) {
|
|
358
|
+
return { ...out, code: 'account_worker', reason: tr('assistant.routes.side.accountWorker', "This conversation's account can't take it now, so it runs on a worker.") };
|
|
359
|
+
}
|
|
360
|
+
// A worker can still be picked by model (an older server sent none, or every one offered is blocked).
|
|
361
|
+
if (kind === 'dispatch' && n?.other?.allowed !== false) {
|
|
362
|
+
return { ...out, available: true, needsPick: true, code: list.length ? 'disabled' : 'none', reason: tr('assistant.routes.side.pickWorker', 'Pick a worker model to run it.') };
|
|
363
|
+
}
|
|
364
|
+
const blocked = list.find((o) => o.disabled);
|
|
365
|
+
return {
|
|
366
|
+
...out, code: said?.code || (blocked ? 'disabled' : 'none'),
|
|
367
|
+
reason: blocked?.disabledReason || said?.reason || (kind === 'direct' ? tr('assistant.routes.side.noHere', 'Nothing in this conversation can run this task.') : tr('assistant.routes.side.noWorker', 'No worker model can run this task.')),
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
const sides = { direct: side('direct'), dispatch: side('dispatch') };
|
|
371
|
+
const chosen = arr(options).find((o) => o.selected && !o.disabled);
|
|
372
|
+
let initial = chosen ? (chosen.kind === 'direct' ? 'direct' : 'dispatch') : null;
|
|
373
|
+
if (!initial || !sides[initial].available) initial = sides.direct.available && sides.direct.defaultId ? 'direct' : 'dispatch';
|
|
374
|
+
return { initial, sides };
|
|
375
|
+
}
|
|
376
|
+
|
|
294
377
|
/** control_response payload for a route card. `decline:true` is the Cancel answer. */
|
|
295
378
|
export function buildRouteResponse(n, choice = {}) {
|
|
296
379
|
if (choice.decline) return { kind: 'route', optionId: null, remember: false, decline: true };
|
|
@@ -303,6 +386,7 @@ export function buildRouteResponse(n, choice = {}) {
|
|
|
303
386
|
provider: normalizeProvider(target.provider, str(target.provider) || null),
|
|
304
387
|
model: str(target.model),
|
|
305
388
|
effort: str(target.effort) || null,
|
|
389
|
+
accountId: str(target.accountId) || null,
|
|
306
390
|
};
|
|
307
391
|
}
|
|
308
392
|
return out;
|
|
@@ -367,11 +451,16 @@ export function routeCatalogRow(pref, catalog) {
|
|
|
367
451
|
return pref.model ? catalogModelRow(pref.provider, pref.model, catalog) || aliasRow(pref.provider, pref.model, rows) : rows.find(m => m.isDefault) || null;
|
|
368
452
|
}
|
|
369
453
|
|
|
370
|
-
/**
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
454
|
+
/**
|
|
455
|
+
* Budget messages are UI copy; never show the server's English disabledReason. `unpriced` (option.budget
|
|
456
|
+
* 'unpriced'): the provider reports tokens and no price, so a session with a budget cannot hold its spend.
|
|
457
|
+
*/
|
|
458
|
+
export function routeBudgetReason({ provider, dry, unpriced = false } = {}, t) {
|
|
459
|
+
const tr = tf(t);
|
|
460
|
+
if (provider && unpriced) return tr('assistant.routes.budgetUnpriced', 'Budget: {provider} reports tokens, not prices, so a session with a budget cannot run it. Pick another option, or switch the budget to No limit.', { provider: providerLabel(provider) });
|
|
461
|
+
if (provider) return tr('assistant.routes.budgetProvider', "Budget: the {provider} share of this session's budget is used up.", { provider: providerLabel(provider) });
|
|
462
|
+
// A provider that reports no prices (Antigravity) never had a share under a budget: it is not "used up".
|
|
463
|
+
const list = [...new Set(arr(dry))].filter(id => !UNPRICED_PROVIDERS.includes(id)).map(id => providerLabel(id));
|
|
375
464
|
if (!list.length) return tr('assistant.routes.budgetSpent', "Budget: this session's budget is spent.");
|
|
376
465
|
return tr(list.length === 1 ? 'assistant.routes.budgetDryOne' : 'assistant.routes.budgetDryMany',
|
|
377
466
|
list.length === 1 ? 'Budget: the {list} share is used up.' : 'Budget: the {list} shares are used up.', { list: list.join(', ') });
|
|
@@ -413,7 +502,7 @@ export function routeLineParts(route, phase = 'decided', { brainLabel = '', t =
|
|
|
413
502
|
return {
|
|
414
503
|
taskClass: cls,
|
|
415
504
|
kind,
|
|
416
|
-
kindLabel: kind && kind !== 'direct' ?
|
|
505
|
+
kindLabel: kind && kind !== 'direct' ? routeKindWord(kind, t) : '',
|
|
417
506
|
provider: target ? normalizeProvider(target.provider, str(target.provider) || null) : null,
|
|
418
507
|
targetLabel: target && !state ? routeTargetLabel(target) : '',
|
|
419
508
|
meta,
|
|
@@ -439,6 +528,9 @@ function correctionText(c, tr) {
|
|
|
439
528
|
}
|
|
440
529
|
if (c.reason === 'remembered_route') return tr('assistant.routes.correction.remembered', 'your saved route {to} replaced {from}', { to: str(c.to), from: str(c.from) });
|
|
441
530
|
if (c.reason === 'remembered_disabled') return tr('assistant.routes.correction.rememberedDisabled', 'saved route {from} is disabled, kept the pick', { from: str(c.from) });
|
|
531
|
+
// A "here" pick that runs on a worker: another account, or another provider than this conversation's.
|
|
532
|
+
if (c.reason === 'account_worker') return tr('assistant.routes.correction.accountWorker', '{to}: runs on a worker, because this conversation belongs to another account', { to: str(c.to) });
|
|
533
|
+
if (c.reason === 'other_provider' && c.provider) return tr('assistant.routes.correction.otherProvider', "{provider} is not this conversation's provider, so it runs on a worker", { provider: providerLabel(c.provider) });
|
|
442
534
|
const text = firstStr(c.text, c.summary, c.message);
|
|
443
535
|
if (text) return text;
|
|
444
536
|
if (c.field && (c.from || c.to)) return `${c.field}: ${str(c.from) || '—'} → ${str(c.to) || 'default'}`;
|
|
@@ -483,12 +575,12 @@ function providerIconHtml(provider, hooks) {
|
|
|
483
575
|
if (!provider) return '';
|
|
484
576
|
try {
|
|
485
577
|
if (typeof hooks.providerIcon === 'function') return hooks.providerIcon(provider) || '';
|
|
486
|
-
return
|
|
578
|
+
return providerMeta(provider).icon || '';
|
|
487
579
|
} catch { return ''; }
|
|
488
580
|
}
|
|
489
581
|
|
|
490
582
|
function providerColor(provider) {
|
|
491
|
-
try { return
|
|
583
|
+
try { return providerMeta(provider).color || ''; } catch { return ''; }
|
|
492
584
|
}
|
|
493
585
|
|
|
494
586
|
let _lineUid = 0;
|
|
@@ -709,9 +801,12 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
709
801
|
const tr = tf(hooks.t);
|
|
710
802
|
const brainLabel = () => (typeof hooks.brainLabel === 'function' ? hooks.brainLabel() : '') || routeTargetLabel(n.brain) || tr('assistant.routes.theBrainLower', 'the assistant');
|
|
711
803
|
const options = routeOptions(n, { brainLabel: brainLabel(), t: hooks.t });
|
|
804
|
+
// Here / Dispatch: each side has its own list, pick and "other" model; the switch decides which is shown and answered.
|
|
805
|
+
const view = routeCardSides(n, options, { t: hooks.t });
|
|
712
806
|
const state = {
|
|
713
|
-
|
|
714
|
-
|
|
807
|
+
side: view.initial,
|
|
808
|
+
picks: { direct: view.sides.direct.defaultId, dispatch: view.sides.dispatch.defaultId },
|
|
809
|
+
others: { direct: null, dispatch: null },
|
|
715
810
|
remember: false,
|
|
716
811
|
pending: false,
|
|
717
812
|
resolved: false,
|
|
@@ -750,6 +845,25 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
750
845
|
card.appendChild(note);
|
|
751
846
|
}
|
|
752
847
|
|
|
848
|
+
// The switch: Here (this conversation) or Dispatch (a worker). A side that cannot run the task stays
|
|
849
|
+
// in view, marked unavailable, with why (worker-only classes, a blind or hidden model, another account).
|
|
850
|
+
const sidesEl = el('div', 'asst-route-sides');
|
|
851
|
+
sidesEl.setAttribute('role', 'radiogroup');
|
|
852
|
+
sidesEl.setAttribute('aria-label', tr('assistant.routes.side.label', 'Where it runs'));
|
|
853
|
+
const sideBtns = {};
|
|
854
|
+
for (const kind of ROUTE_SIDES) {
|
|
855
|
+
const btn = el('button', 'asst-route-side', kind === 'direct' ? tr('assistant.routes.side.here', 'Here') : tr('assistant.routes.side.dispatch', 'Dispatch'));
|
|
856
|
+
btn.type = 'button';
|
|
857
|
+
btn.dataset.side = kind;
|
|
858
|
+
btn.setAttribute('role', 'radio');
|
|
859
|
+
btn.addEventListener('click', () => setSide(kind));
|
|
860
|
+
sideBtns[kind] = btn;
|
|
861
|
+
sidesEl.appendChild(btn);
|
|
862
|
+
}
|
|
863
|
+
const sideNote = el('div', 'asst-route-side-note');
|
|
864
|
+
sideNote.setAttribute('aria-live', 'polite');
|
|
865
|
+
card.append(sidesEl, sideNote);
|
|
866
|
+
|
|
753
867
|
const list = el('div', 'asst-route-options');
|
|
754
868
|
list.setAttribute('role', 'radiogroup');
|
|
755
869
|
list.setAttribute('aria-label', tr('assistant.routes.chooseModel', 'Where should this run?'));
|
|
@@ -762,7 +876,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
762
876
|
row.setAttribute('aria-checked', 'false');
|
|
763
877
|
row.setAttribute('aria-disabled', o.disabled ? 'true' : 'false');
|
|
764
878
|
row.tabIndex = -1;
|
|
765
|
-
const kindWord = o.kind && o.kind !== 'direct' ?
|
|
879
|
+
const kindWord = o.kind && o.kind !== 'direct' ? routeKindWord(o.kind, hooks.t) : '';
|
|
766
880
|
const sub = [kindWord, routeDisplayAccount(o, hooks.t), o.effort && o.effort !== 'off' ? o.effort : '', o.price ? priceText(o.price) : '', o.vision ? tr('assistant.brain.vision', 'vision') : '', o.reason].filter(Boolean).join(' · ');
|
|
767
881
|
row.innerHTML = `
|
|
768
882
|
<span class="asst-route-num" aria-hidden="true">${esc(o.number)}</span>
|
|
@@ -789,7 +903,11 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
789
903
|
return b != null ? `$${a != null ? f(a) : '?'}/$${f(b)}` : `$${f(a)}`;
|
|
790
904
|
}
|
|
791
905
|
|
|
792
|
-
|
|
906
|
+
// The shown side's options (numbered from 1), then its "other" model when one was picked.
|
|
907
|
+
const sideOptions = () => view.sides[state.side].options;
|
|
908
|
+
const allOptions = () => (state.others[state.side] ? [...sideOptions(), state.others[state.side]] : sideOptions());
|
|
909
|
+
const selectedId = () => state.picks[state.side] || null;
|
|
910
|
+
const busy = () => state.pending || state.resolved || state.expired;
|
|
793
911
|
|
|
794
912
|
function renderOptions() {
|
|
795
913
|
list.innerHTML = '';
|
|
@@ -799,25 +917,120 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
799
917
|
|
|
800
918
|
function syncSelection() {
|
|
801
919
|
for (const row of list.querySelectorAll('.asst-route-option')) {
|
|
802
|
-
const on = row.dataset.optionId ===
|
|
920
|
+
const on = row.dataset.optionId === selectedId();
|
|
803
921
|
row.setAttribute('aria-checked', on ? 'true' : 'false');
|
|
804
922
|
row.classList.toggle('selected', on);
|
|
805
923
|
row.tabIndex = on ? 0 : -1;
|
|
806
924
|
}
|
|
807
|
-
|
|
925
|
+
syncFields();
|
|
926
|
+
syncSides();
|
|
927
|
+
runBtn.disabled = busy() || !selectedId();
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/** The switch, its note (why the other side is unavailable, what a worker side still needs, else what the side means) and the Run label. */
|
|
931
|
+
function syncSides() {
|
|
932
|
+
card.dataset.side = state.side;
|
|
933
|
+
for (const kind of ROUTE_SIDES) {
|
|
934
|
+
const btn = sideBtns[kind];
|
|
935
|
+
const side = view.sides[kind];
|
|
936
|
+
const on = state.side === kind;
|
|
937
|
+
btn.setAttribute('aria-checked', on ? 'true' : 'false');
|
|
938
|
+
btn.classList.toggle('selected', on);
|
|
939
|
+
btn.setAttribute('aria-disabled', side.available && !busy() ? 'false' : 'true');
|
|
940
|
+
btn.tabIndex = on ? 0 : -1;
|
|
941
|
+
if (!side.available && side.reason) btn.setAttribute('title', side.reason);
|
|
942
|
+
else btn.removeAttribute('title');
|
|
943
|
+
}
|
|
944
|
+
const current = view.sides[state.side];
|
|
945
|
+
const other = view.sides[state.side === 'direct' ? 'dispatch' : 'direct'];
|
|
946
|
+
sideNote.textContent = !other.available && other.reason ? other.reason
|
|
947
|
+
: current.needsPick && !selectedId() ? current.reason
|
|
948
|
+
: state.side === 'direct' ? tr('assistant.routes.side.hereDesc', 'Runs in this conversation.') : tr('assistant.routes.side.dispatchDesc', 'A worker runs it; the result comes back here.');
|
|
949
|
+
runBtn.textContent = state.side === 'direct' ? tr('assistant.routes.runHere', 'Run here') : tr('assistant.routes.runDispatch', 'Dispatch');
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/** Show the other side (its own pick stays as it was). `focus`: 'switch' (arrow keys on it) or 'option'. */
|
|
953
|
+
function setSide(kind, { focus = null } = {}) {
|
|
954
|
+
if (busy() || !view.sides[kind]?.available) return;
|
|
955
|
+
if (state.side !== kind) {
|
|
956
|
+
state.side = kind;
|
|
957
|
+
error.hidden = true;
|
|
958
|
+
renderOptions();
|
|
959
|
+
}
|
|
960
|
+
if (focus === 'switch') sideBtns[kind].focus();
|
|
961
|
+
else if (focus === 'option') entry.focus();
|
|
808
962
|
}
|
|
809
963
|
|
|
810
964
|
function select(id, { focus = false } = {}) {
|
|
811
|
-
if (
|
|
965
|
+
if (busy()) return;
|
|
812
966
|
const o = allOptions().find(x => x.id === id);
|
|
813
967
|
if (!o || o.disabled) return;
|
|
814
|
-
state.
|
|
968
|
+
state.picks[state.side] = id;
|
|
815
969
|
syncSelection();
|
|
816
970
|
if (focus) list.querySelector(`.asst-route-option[data-option-id="${CSS.escape(id)}"]`)?.focus();
|
|
817
971
|
}
|
|
818
972
|
|
|
819
|
-
//
|
|
820
|
-
const
|
|
973
|
+
// Task-local controls; these never edit the routing preference table.
|
|
974
|
+
const fields = el('div', 'asst-route-fields');
|
|
975
|
+
function taskField(className) {
|
|
976
|
+
const button = el('button', `asst-dd ${className}`);
|
|
977
|
+
button.type = 'button';
|
|
978
|
+
button.setAttribute('aria-haspopup', 'menu');
|
|
979
|
+
button.innerHTML = `<span class="asst-dd-label"></span><span class="asst-dd-caret" aria-hidden="true">${ICON_CARET}</span>`;
|
|
980
|
+
return button;
|
|
981
|
+
}
|
|
982
|
+
const effortBtn = taskField('asst-route-effort');
|
|
983
|
+
const accountBtn = taskField('asst-route-account');
|
|
984
|
+
const setFieldLabel = (button, text) => { button.querySelector('.asst-dd-label').textContent = text; button.title = text; };
|
|
985
|
+
const selected = () => allOptions().find(o => o.id === selectedId());
|
|
986
|
+
function editTarget(patch) {
|
|
987
|
+
if (busy()) return;
|
|
988
|
+
const from = selected();
|
|
989
|
+
if (!from) return;
|
|
990
|
+
state.others[state.side] = { ...from, accountId: from.accountChoice || from.accountId || null, accountChoice: Object.hasOwn(patch, 'accountId') ? patch.accountId : from.accountChoice || null, disabled: false, disabledReason: null, accountError: null, ...patch, id: 'other', number: sideOptions().length + 1, badge: null, badgeText: tr('assistant.routes.badge.other', 'Your choice') };
|
|
991
|
+
state.picks[state.side] = 'other';
|
|
992
|
+
renderOptions();
|
|
993
|
+
}
|
|
994
|
+
function syncFields() {
|
|
995
|
+
const target = selected();
|
|
996
|
+
setFieldLabel(otherBtn, target ? routeTargetLabel(target) : tr('assistant.routes.other', 'Other model…'));
|
|
997
|
+
otherBtn.setAttribute('aria-label', tr('assistant.brain.model', 'Model'));
|
|
998
|
+
otherBtn.disabled = busy();
|
|
999
|
+
setFieldLabel(effortBtn, target?.effort || tr('assistant.routes.editorEffortAuto', 'Auto'));
|
|
1000
|
+
effortBtn.setAttribute('aria-label', tr('assistant.brain.effort', 'Thinking effort'));
|
|
1001
|
+
setFieldLabel(accountBtn, routeDisplayAccount(target, hooks.t) || tr('assistant.routes.account.workersDefault', 'Workers default'));
|
|
1002
|
+
accountBtn.setAttribute('aria-label', tr('assistant.brain.account', 'Account profile'));
|
|
1003
|
+
const levels = target ? getEffortLevelsForModel(target.provider, catalogModelRow(target.provider, target.model)) : [];
|
|
1004
|
+
effortBtn.disabled = busy() || !target || !levels.some(e => e.id !== 'off');
|
|
1005
|
+
// Here runs on this conversation's own account (another one is a worker): only a worker picks one.
|
|
1006
|
+
accountBtn.hidden = state.side === 'direct' || !['claude-code', 'codex'].includes(target?.provider);
|
|
1007
|
+
accountBtn.disabled = busy();
|
|
1008
|
+
}
|
|
1009
|
+
effortBtn.addEventListener('click', () => {
|
|
1010
|
+
const target = selected();
|
|
1011
|
+
if (!target) return;
|
|
1012
|
+
const levels = getEffortLevelsForModel(target.provider, catalogModelRow(target.provider, target.model));
|
|
1013
|
+
openMenu(effortBtn, { title: tr('assistant.brain.effort', 'Thinking effort'), placement: 'auto', items: [
|
|
1014
|
+
{ kind: 'radio', label: tr('assistant.routes.editorEffortAuto', 'Auto'), selected: !target.effort, onSelect: () => editTarget({ effort: null }) },
|
|
1015
|
+
...levels.filter(e => e.id !== 'off').map(e => ({ kind: 'radio', label: e.label || e.id, selected: target.effort === e.id, onSelect: () => editTarget({ effort: e.id }) })),
|
|
1016
|
+
] });
|
|
1017
|
+
});
|
|
1018
|
+
accountBtn.addEventListener('click', async () => {
|
|
1019
|
+
const target = selected();
|
|
1020
|
+
if (!target) return;
|
|
1021
|
+
try {
|
|
1022
|
+
const rows = await (target.provider === 'codex' ? fetchCodexAccounts() : fetchClaudeAccounts());
|
|
1023
|
+
if (!card.isConnected || state.pending || state.resolved || state.expired || selected() !== target) return;
|
|
1024
|
+
openMenu(accountBtn, { title: tr('assistant.brain.account', 'Account profile'), placement: 'auto', items: [
|
|
1025
|
+
{ kind: 'radio', label: tr('assistant.routes.account.workersDefault', 'Workers default'), selected: !(target.accountChoice || target.accountId), onSelect: () => editTarget({ accountId: null, account: null }) },
|
|
1026
|
+
{ kind: 'radio', label: autoAccountLabel(hooks.t), selected: (target.accountChoice || target.accountId) === 'auto', onSelect: () => editTarget({ accountId: 'auto', account: null }) },
|
|
1027
|
+
...(Array.isArray(rows) ? rows : []).map(a => ({ kind: 'radio', label: accountSummary(a, target.provider === 'codex' ? 'codex' : 'claude'), disabled: a.signedIn === false, selected: (target.accountChoice || target.accountId) === a.id, onSelect: () => editTarget({ accountId: a.id, account: a }) })),
|
|
1028
|
+
] });
|
|
1029
|
+
} catch (error) { hooks.onToast?.(error.message); }
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
// Model menu, scoped to this task.
|
|
1033
|
+
const otherBtn = taskField('asst-route-other');
|
|
821
1034
|
otherBtn.type = 'button';
|
|
822
1035
|
otherBtn.setAttribute('aria-haspopup', 'menu');
|
|
823
1036
|
otherBtn.hidden = !n.other.allowed;
|
|
@@ -825,34 +1038,50 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
825
1038
|
e.stopPropagation();
|
|
826
1039
|
if (isMenuOpen(otherBtn)) { closeMenu(); return; }
|
|
827
1040
|
const brain = normalizeBrain(hooks.getBrain?.() || { provider: n.brain.provider || 'claude-code', model: n.brain.model });
|
|
1041
|
+
// The menu follows the switch: Here lists this conversation's provider (another model continues it there);
|
|
1042
|
+
// Dispatch lists every provider, and its pick is always a worker.
|
|
1043
|
+
const here = state.side === 'direct' && !n.dispatchOnly;
|
|
1044
|
+
// A worker: the providers the server lists (its CATALOG_PROVIDERS).
|
|
1045
|
+
const workerMenu = {
|
|
1046
|
+
providers: n.other.providers.length ? n.other.providers.map(p => normalizeProvider(p, p)) : null,
|
|
1047
|
+
};
|
|
828
1048
|
openModelMenu(otherBtn, {
|
|
829
1049
|
brain,
|
|
1050
|
+
shouldOpen: () => card.isConnected && !busy(),
|
|
830
1051
|
t: hooks.t,
|
|
831
|
-
title: tr('assistant.routes.otherTitle', 'Run on another model'),
|
|
1052
|
+
title: here ? tr('assistant.routes.otherHereTitle', 'Continue here on another model') : tr('assistant.routes.otherTitle', 'Run on another model'),
|
|
832
1053
|
placement: 'auto',
|
|
833
|
-
|
|
834
|
-
current:
|
|
1054
|
+
...(here ? { providers: [brain.provider] } : workerMenu),
|
|
1055
|
+
current: selected() ? { provider: selected().provider, model: selected().model } : null,
|
|
835
1056
|
onToast: hooks.onToast,
|
|
836
1057
|
// Image / video creation: only models that generate the medium (no custom id: its output is unknown).
|
|
837
1058
|
...(n.requires ? { accept: (_p, m) => m?.outputs?.[n.requires] === true, allowCustom: false, emptyLabel: tr('assistant.routes.noCapableModel', 'No model in your catalog can make {medium} yet.', { medium: mediumWords(n.requires, tr) }) } : {}),
|
|
838
1059
|
// Design: only models that can see.
|
|
839
1060
|
...(!n.requires && n.visionOnly ? { accept: (_p, m) => m?.vision === true && m?.status !== 'unavailable', allowCustom: false, emptyLabel: tr('assistant.routes.noSeeingModel', 'No model in your catalog can see images yet.') } : {}),
|
|
1061
|
+
// Computer use: never a model known to be blind.
|
|
1062
|
+
...(!n.requires && !n.visionOnly && n.needsVision ? { accept: (_p, m) => m?.vision !== false } : {}),
|
|
840
1063
|
onSelect: (target) => {
|
|
841
|
-
if (!target?.model) return;
|
|
842
|
-
const
|
|
843
|
-
|
|
1064
|
+
if (!target?.model || !card.isConnected || busy()) return;
|
|
1065
|
+
const previous = selected();
|
|
1066
|
+
const kind = here && target.provider === brain.provider ? 'direct' : 'dispatch';
|
|
1067
|
+
const side = kind === 'direct' ? 'direct' : 'dispatch';
|
|
1068
|
+
state.others[side] = {
|
|
844
1069
|
id: 'other', kind, provider: target.provider, model: target.model, effort: null,
|
|
845
|
-
|
|
846
|
-
|
|
1070
|
+
...(kind === 'dispatch' ? { accountId: previous?.provider === target.provider ? previous.accountChoice || previous.accountId || null : null, account: previous?.provider === target.provider ? previous.account || null : null } : {}),
|
|
1071
|
+
label: target.label || '', badge: null, number: view.sides[side].options.length + 1, here: false,
|
|
1072
|
+
title: kind === 'direct' ? tr('assistant.routes.doHere', 'Do it here with {model}', { model: target.label || target.model }) : (target.label || target.model),
|
|
1073
|
+
badgeText: tr('assistant.routes.badge.other', 'Your choice'),
|
|
847
1074
|
vision: target.vision === true, price: target.price || null, reason: '', warning: null, disabled: false,
|
|
848
1075
|
};
|
|
849
|
-
state.
|
|
1076
|
+
state.picks[side] = 'other';
|
|
1077
|
+
state.side = side;
|
|
850
1078
|
renderOptions();
|
|
851
1079
|
list.querySelector('.asst-route-option[data-option-id="other"]')?.focus();
|
|
852
1080
|
},
|
|
853
1081
|
});
|
|
854
1082
|
});
|
|
855
|
-
|
|
1083
|
+
fields.append(otherBtn, effortBtn, accountBtn);
|
|
1084
|
+
card.appendChild(fields);
|
|
856
1085
|
|
|
857
1086
|
// Remember for ‹class› tasks
|
|
858
1087
|
let rememberInput = null;
|
|
@@ -880,7 +1109,8 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
880
1109
|
cancelBtn.hidden = !n.decline.allowed;
|
|
881
1110
|
const runBtn = el('button', 'asst-btn asst-btn-primary asst-route-run', tr('assistant.routes.run', 'Run'));
|
|
882
1111
|
runBtn.type = 'button';
|
|
883
|
-
const
|
|
1112
|
+
const bothSides = view.sides.direct.available && view.sides.dispatch.available;
|
|
1113
|
+
const hint = el('span', 'asst-route-keys', bothSides ? tr('assistant.routes.keysSides', '← → Here or Dispatch · 1–9 choose · Enter runs · Esc cancels') : tr('assistant.routes.keys', '1–4 choose · Enter runs · Esc cancels'));
|
|
884
1114
|
actions.append(hint, cancelBtn, runBtn);
|
|
885
1115
|
card.appendChild(actions);
|
|
886
1116
|
|
|
@@ -896,7 +1126,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
896
1126
|
}
|
|
897
1127
|
|
|
898
1128
|
function chosenRoute() {
|
|
899
|
-
const o =
|
|
1129
|
+
const o = selected() || null;
|
|
900
1130
|
return {
|
|
901
1131
|
routeId: n.routeId,
|
|
902
1132
|
taskClass: n.taskClass,
|
|
@@ -904,10 +1134,10 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
904
1134
|
summary: n.summary,
|
|
905
1135
|
confidence: n.confidence,
|
|
906
1136
|
decidedBy: 'user',
|
|
907
|
-
optionId:
|
|
1137
|
+
optionId: selectedId(),
|
|
908
1138
|
remembered: state.remember,
|
|
909
|
-
target: o ? { kind: o.kind || 'direct', provider: o.provider, model: o.model, effort: o.effort, label: o.label || (o.here ? routeTargetLabel(o) : o.title) } : null,
|
|
910
|
-
alternatives: allOptions().filter(x => x.id !==
|
|
1139
|
+
target: o ? { kind: o.kind || 'direct', provider: o.provider, model: o.model, effort: o.effort, accountId: o.accountId || null, label: (o.kind === 'direct' ? str(o.label).replace(/^do it here with\s+/i, '') : o.label) || (o.here ? routeTargetLabel(o) : o.title) } : null,
|
|
1140
|
+
alternatives: allOptions().filter(x => x.id !== selectedId()).map(x => ({ provider: x.provider, model: x.model, effort: x.effort, label: x.label })),
|
|
911
1141
|
};
|
|
912
1142
|
}
|
|
913
1143
|
|
|
@@ -926,6 +1156,9 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
926
1156
|
state.line = line;
|
|
927
1157
|
state.collapsed = true;
|
|
928
1158
|
state.resolved = true;
|
|
1159
|
+
// No longer waits: the renderer puts it back from the open card's slot at the transcript's bottom
|
|
1160
|
+
// (asst-ask-tail.js) before it becomes the route line in its own place.
|
|
1161
|
+
try { card._ask?.onChange?.(); } catch { /* the renderer is gone */ }
|
|
929
1162
|
if (card.isConnected) card.replaceWith(line.el);
|
|
930
1163
|
try { hooks.onCollapse?.(n.routeId, line); } catch { /* ignore */ }
|
|
931
1164
|
if (hadFocus) { try { hooks.onDone?.(); } catch { /* ignore */ } }
|
|
@@ -937,6 +1170,8 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
937
1170
|
if (state.line?.el?.isConnected) state.line.el.replaceWith(card);
|
|
938
1171
|
state.collapsed = false;
|
|
939
1172
|
state.resolved = false;
|
|
1173
|
+
// Open again (ROUTE_INVALID): it waits on the user, so it goes back to the bottom slot.
|
|
1174
|
+
try { card._ask?.onChange?.(); } catch { /* the renderer is gone */ }
|
|
940
1175
|
}
|
|
941
1176
|
|
|
942
1177
|
function clearTimers() {
|
|
@@ -945,8 +1180,8 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
945
1180
|
}
|
|
946
1181
|
|
|
947
1182
|
function submit() {
|
|
948
|
-
if (
|
|
949
|
-
const response = buildRouteResponse(n, { optionId:
|
|
1183
|
+
if (busy() || !selectedId()) return;
|
|
1184
|
+
const response = buildRouteResponse(n, { optionId: selectedId(), target: state.others[state.side], remember: state.remember });
|
|
950
1185
|
error.hidden = true;
|
|
951
1186
|
setPending(true, tr('assistant.routes.routing', 'Routing…'));
|
|
952
1187
|
try { hooks.onRespond?.(response, n); } catch { /* ignore */ }
|
|
@@ -955,7 +1190,7 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
955
1190
|
}
|
|
956
1191
|
|
|
957
1192
|
function cancel() {
|
|
958
|
-
if (state.pending || state.resolved) return;
|
|
1193
|
+
if (state.pending || state.resolved || state.expired) return;
|
|
959
1194
|
if (!n.decline.allowed) {
|
|
960
1195
|
// Nothing to cancel on the server: leave the card and hand focus back.
|
|
961
1196
|
try { hooks.onCancel?.(buildRouteCancel(n, 'user'), n); } catch { /* ignore */ }
|
|
@@ -979,11 +1214,18 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
979
1214
|
if (o && !o.disabled) { e.preventDefault(); e.stopPropagation(); select(o.id, { focus: true }); }
|
|
980
1215
|
return;
|
|
981
1216
|
}
|
|
1217
|
+
// ← → switch Here / Dispatch, from the switch itself or from the list.
|
|
1218
|
+
if ((e.key === 'ArrowLeft' || e.key === 'ArrowRight') && !e.metaKey && !e.ctrlKey && !e.altKey
|
|
1219
|
+
&& (e.target?.classList?.contains('asst-route-side') || e.target?.classList?.contains('asst-route-option') || e.target === card)) {
|
|
1220
|
+
e.preventDefault(); e.stopPropagation();
|
|
1221
|
+
setSide(e.key === 'ArrowLeft' ? 'direct' : 'dispatch', { focus: e.target?.classList?.contains('asst-route-side') ? 'switch' : 'option' });
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
982
1224
|
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
983
1225
|
const enabled = all.filter(o => !o.disabled);
|
|
984
1226
|
if (!enabled.length) return;
|
|
985
1227
|
e.preventDefault(); e.stopPropagation();
|
|
986
|
-
const idx = Math.max(0, enabled.findIndex(o => o.id ===
|
|
1228
|
+
const idx = Math.max(0, enabled.findIndex(o => o.id === selectedId()));
|
|
987
1229
|
const next = enabled[(idx + (e.key === 'ArrowDown' ? 1 : -1) + enabled.length) % enabled.length];
|
|
988
1230
|
select(next.id, { focus: true });
|
|
989
1231
|
return;
|
|
@@ -1021,11 +1263,16 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
1021
1263
|
state.timer = null;
|
|
1022
1264
|
countdown.textContent = tr('assistant.routes.state.expired', 'route expired');
|
|
1023
1265
|
syncSelection();
|
|
1266
|
+
// No answer is coming from the user any more: it goes back to its own place (asst-ask-tail.js).
|
|
1267
|
+
try { card._ask?.onChange?.(); } catch { /* the renderer is gone */ }
|
|
1024
1268
|
}
|
|
1025
1269
|
}
|
|
1026
|
-
if (expires && Number.isFinite(expires)) { tick(); state.timer = setInterval(tick, 1000); }
|
|
1270
|
+
if (expires && Number.isFinite(expires)) { tick(); if (!state.expired) state.timer = setInterval(tick, 1000); }
|
|
1027
1271
|
|
|
1028
1272
|
renderOptions();
|
|
1273
|
+
// The renderer lifts a card that waits on the user to the transcript's bottom while it is open
|
|
1274
|
+
// (asst-ask-tail.js): a route card waits like a follow-ups set or a question does.
|
|
1275
|
+
card._ask = { el: card, presenter: null, isOpen: () => !state.collapsed && !state.resolved && !state.expired, onChange: null };
|
|
1029
1276
|
if (container) container.appendChild(card);
|
|
1030
1277
|
try { hooks.scrollEnd?.(); } catch { /* ignore */ }
|
|
1031
1278
|
|
|
@@ -1039,7 +1286,8 @@ export function renderRouteCard(container, input, hooks = {}) {
|
|
|
1039
1286
|
get line() { return state.line; },
|
|
1040
1287
|
/** Focus the preselected option (the panel calls this when the composer is empty). */
|
|
1041
1288
|
focus() {
|
|
1042
|
-
|
|
1289
|
+
// A side with nothing listed yet (a worker to pick by model) focuses the switch instead.
|
|
1290
|
+
const row = list.querySelector('.asst-route-option[aria-checked="true"]') || list.querySelector('.asst-route-option') || sideBtns[state.side];
|
|
1043
1291
|
try { row?.focus({ preventScroll: false }); } catch { row?.focus(); }
|
|
1044
1292
|
},
|
|
1045
1293
|
focusIfIdle() { if (typeof hooks.shouldFocus !== 'function' || hooks.shouldFocus()) entry.focus(); },
|
|
@@ -1075,7 +1323,7 @@ export { openRoutesEditor } from './asst-routes-editor.js';
|
|
|
1075
1323
|
|
|
1076
1324
|
// ── Models manager (Assistant → Models…) ───────────────────────────────────
|
|
1077
1325
|
|
|
1078
|
-
const MODEL_SECTIONS = ['claude-code', 'codex', 'opencode'];
|
|
1326
|
+
const MODEL_SECTIONS = ['claude-code', 'codex', 'opencode', 'antigravity'];
|
|
1079
1327
|
const MODEL_TABS = ['available', 'archived'];
|
|
1080
1328
|
let _modelsManager = null;
|
|
1081
1329
|
|
|
@@ -1300,7 +1548,7 @@ export async function openModelsManager(hooks = {}) {
|
|
|
1300
1548
|
}
|
|
1301
1549
|
|
|
1302
1550
|
function sectionHead(provider, countText) {
|
|
1303
|
-
const meta =
|
|
1551
|
+
const meta = providerMeta(provider);
|
|
1304
1552
|
const section = el('section', 'asst-routes-section asst-models-section');
|
|
1305
1553
|
section.dataset.provider = provider;
|
|
1306
1554
|
const head = el('div', 'asst-routes-section-title asst-models-head');
|
|
@@ -1310,8 +1558,11 @@ export async function openModelsManager(hooks = {}) {
|
|
|
1310
1558
|
return { section, head };
|
|
1311
1559
|
}
|
|
1312
1560
|
|
|
1561
|
+
// An optional provider's section shows once the server lists it (an older server knows no Antigravity).
|
|
1562
|
+
const shownSections = () => MODEL_SECTIONS.filter(p => !OPTIONAL_PROVIDERS.includes(p) || Array.isArray(state.models?.[p]) || arr(state.archived?.[p]).length > 0);
|
|
1563
|
+
|
|
1313
1564
|
function renderAvailable() {
|
|
1314
|
-
for (const provider of
|
|
1565
|
+
for (const provider of shownSections()) {
|
|
1315
1566
|
const rows = arr(state.models[provider]);
|
|
1316
1567
|
const { section } = sectionHead(provider, modelsOnText(rows, hooks.t));
|
|
1317
1568
|
if (provider === 'opencode') section.appendChild(el('div', 'asst-modal-note', tr('assistant.models.opencodeNote', 'Connected OpenCode models only. Settings → OpenCode edits the same list.')));
|
|
@@ -1326,7 +1577,7 @@ export async function openModelsManager(hooks = {}) {
|
|
|
1326
1577
|
|
|
1327
1578
|
function renderArchived() {
|
|
1328
1579
|
let any = false;
|
|
1329
|
-
for (const provider of
|
|
1580
|
+
for (const provider of shownSections()) {
|
|
1330
1581
|
const entries = arr(state.archived[provider]);
|
|
1331
1582
|
if (!entries.length) continue;
|
|
1332
1583
|
any = true;
|