synabun 2.0.10 → 2.1.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 +86 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +2 -2
- 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 +17 -8
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +1 -1
- 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 +228 -0
- package/lib/virustotal.mjs +192 -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/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/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/neural-interface.d.ts +16 -10
- 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 +20 -62
- 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/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 +10 -10
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +22 -13
- 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/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +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 +639 -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/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/neural-interface.ts +79 -20
- package/mcp-server/src/services/server-instructions.ts +27 -63
- 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/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +22 -14
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/skill-tools.ts +589 -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 +3463 -200
- package/neural-interface/i18n/pt-BR.json +3411 -151
- package/neural-interface/lib/assistant-api.js +127 -28
- 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-catalog.js +6 -7
- package/neural-interface/lib/assistant-dispatch.js +871 -156
- package/neural-interface/lib/assistant-envelope.js +3 -1
- package/neural-interface/lib/assistant-persona.js +80 -11
- package/neural-interface/lib/assistant-plan-permissions.js +10 -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 +13 -8
- package/neural-interface/lib/assistant-runtime.js +56 -18
- package/neural-interface/lib/assistant-task-prompt.js +78 -14
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/backup-service.js +620 -3
- 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 +83 -0
- package/neural-interface/lib/browser-network-guard.js +284 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +186 -31
- package/neural-interface/lib/claude-agent-bridge.js +4 -2
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install.js +18 -138
- package/neural-interface/lib/cli-resolver.js +4 -1
- package/neural-interface/lib/cli-update-planner.js +32 -31
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-worker-app-server.js +172 -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/exec-loop-command.js +4 -8
- 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 +0 -29
- package/neural-interface/lib/mcp-external-servers.js +323 -0
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/morelogin.js +30 -0
- package/neural-interface/lib/native-loop-providers.js +547 -47
- package/neural-interface/lib/native-loop-runtime.js +268 -41
- 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 +44 -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/peer-identity.js +763 -0
- package/neural-interface/lib/request-gate.js +17 -2
- package/neural-interface/lib/rulesets/api.js +1 -1
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +92 -27
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +17 -30
- 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 +294 -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 +267 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +564 -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 +214 -0
- package/neural-interface/lib/skills/eval-confinement.js +397 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +119 -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 +200 -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 +387 -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 +262 -0
- package/neural-interface/lib/skills/runtime-checks.js +355 -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 +522 -0
- package/neural-interface/lib/skills/telemetry.js +113 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/worker-confinement.js +1192 -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-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +229 -0
- package/neural-interface/lib/worker-sandbox.js +299 -0
- package/neural-interface/package-lock.json +18 -12
- package/neural-interface/package.json +2 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +2 -2
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +35 -19
- 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 +33 -2
- 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 +49 -35
- package/neural-interface/public/shared/assistant/asst-control.js +157 -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-limit-dock.js +40 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +294 -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 +106 -11
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +401 -77
- package/neural-interface/public/shared/assistant/asst-route.js +83 -12
- package/neural-interface/public/shared/assistant/asst-scenes.js +2 -0
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +67 -4
- package/neural-interface/public/shared/assistant/asst-slash.js +9 -7
- package/neural-interface/public/shared/assistant/asst-state.js +11 -15
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +59 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +156 -16
- 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 +8 -7
- package/neural-interface/public/shared/cdx/cdx-panel.js +45 -149
- package/neural-interface/public/shared/cdx/cdx-protocol.js +28 -1
- package/neural-interface/public/shared/cdx/cdx-render.js +47 -29
- package/neural-interface/public/shared/cdx/cdx-requests.js +3 -3
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +11 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +52 -39
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-view.js +4 -8
- package/neural-interface/public/shared/cli-status.js +2 -2
- package/neural-interface/public/shared/constants.js +2 -2
- 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 +366 -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/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/desktop-update-view.js +20 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/html-shell.js +9 -11
- 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 +47 -252
- 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-render.js +6 -21
- 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 +0 -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 +14 -47
- package/neural-interface/public/shared/page-session.js +96 -0
- 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-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/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +219 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +130 -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 +141 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +475 -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 +236 -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 +229 -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-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 +279 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +296 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +639 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +475 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +681 -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 +248 -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 +425 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1525 -1779
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- 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/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-automation-studio.js +91 -128
- package/neural-interface/public/shared/ui-claude-panel.js +77 -5
- 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-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-keybinds.js +26 -1
- package/neural-interface/public/shared/ui-menubar.js +8 -18
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +7 -1
- package/neural-interface/public/shared/ui-navbar.js +4 -24
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-schedules-studio.js +507 -423
- package/neural-interface/public/shared/ui-sessions.js +5 -3
- package/neural-interface/public/shared/ui-settings.js +594 -462
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +1 -1
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-windows.js +16 -11
- 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 +124 -180
- package/neural-interface/public/shared/ui-tool-updates.js +3 -1
- package/neural-interface/public/shared/ui-tooltip.js +10 -5
- 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 +311 -58
- package/neural-interface/public/shared/ui-worker-end.js +4 -0
- 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 +1 -1
- 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-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- 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 +3934 -2454
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +18 -0
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +18 -12
- package/package.json +16 -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 +4 -4
- 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
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Assistant — the cards the mascot presents: the shared kit
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// What a finished turn offers (the follow-ups card, asst-followups-card.js)
|
|
5
|
+
// and what the brain asks (the question cards of asst-control.js: agent_clarify,
|
|
6
|
+
// AskUserQuestion, Codex's choice and requestUserInput, OpenCode's question)
|
|
7
|
+
// are drawn the same way: the mascot beside the card, presenting, asking and
|
|
8
|
+
// reacting, its words in a bubble and the answers under it. This module is the
|
|
9
|
+
// part they share:
|
|
10
|
+
//
|
|
11
|
+
// - createPresenter(): the mascot's slot. A painted still (paintMascot +
|
|
12
|
+
// stillFrame, never a rig) until the renderer makes it live (setLive): then a
|
|
13
|
+
// rig from synabun-mascot.js, drawn by the one shared ticker and parked off
|
|
14
|
+
// screen by the rig's own observer. Only the newest open card of a panel is
|
|
15
|
+
// live, and none while calm (replay, reduced motion, a drag): asst-render.js
|
|
16
|
+
// activateRigs. `target` is what the panel's director (asst-mascot.js) dresses
|
|
17
|
+
// while this presenter is the character on screen; `waits` marks a card that
|
|
18
|
+
// waits on the user itself (a question card), which the director never
|
|
19
|
+
// poses "blocked" at its own card.
|
|
20
|
+
// - askOption() / setOptionChecked(): one option row (key · label · description
|
|
21
|
+
// · badge), the same markup in both kinds of card.
|
|
22
|
+
// - optionKeyIntent() (pure) and the wiring around it: one tab stop per option
|
|
23
|
+
// group (arrows, Home, End), 1-9 pick an option while focus is in the card,
|
|
24
|
+
// Esc goes back to the composer; the mascot looks at the option pointed at
|
|
25
|
+
// or focused (wireGaze) and watches a text field being typed in (wireWatch).
|
|
26
|
+
// Nothing touches the DOM at import: the server imports asst-control.js
|
|
27
|
+
// (WhatsApp cards) and node:test draws cards with a stand-in document.
|
|
28
|
+
|
|
29
|
+
import * as mascotLib from '../synabun-mascot.js';
|
|
30
|
+
import { isReducedMotion } from '../synabun-ticker.js';
|
|
31
|
+
import { lookToward } from './asst-mascot.js';
|
|
32
|
+
import { injectAskStyles } from './asst-ask-styles.js';
|
|
33
|
+
|
|
34
|
+
export { injectAskStyles };
|
|
35
|
+
|
|
36
|
+
/** The presenter's SVG, before the sheet's container queries shrink it. */
|
|
37
|
+
export const PRESENTER_SIZE = Object.freeze({ width: 64, height: 32 });
|
|
38
|
+
const HOP_MS = 320; // the hop (300 ms) and a frame to land on
|
|
39
|
+
const WATCH_IDLE_MS = 1500; // a field nobody typed in for this long: back to the card's own pose
|
|
40
|
+
const WATCH_EVERY_MS = 120; // the caret is looked at no more often than this
|
|
41
|
+
// Where a still looks: toward what the card offers (the answers sit right of the face and under it).
|
|
42
|
+
const STILL_LOOK = Object.freeze({ present: { x: 0.5, y: 0.35 }, pick: { x: 0.5, y: 0.25 }, ask: { x: 0.35, y: -0.15 }, success: { x: 0, y: 0 }, idle: { x: 0, y: 0 } });
|
|
43
|
+
|
|
44
|
+
function el(tag, className, text) {
|
|
45
|
+
const node = document.createElement(tag);
|
|
46
|
+
if (className) node.className = className;
|
|
47
|
+
if (text != null) node.textContent = text;
|
|
48
|
+
return node;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function reduced() {
|
|
52
|
+
try { return !!isReducedMotion(); } catch { return false; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const centre = (node) => {
|
|
56
|
+
try {
|
|
57
|
+
const r = node?.getBoundingClientRect?.();
|
|
58
|
+
return r && r.width ? { x: r.left + r.width / 2, y: r.top + r.height / 2 } : null;
|
|
59
|
+
} catch { return null; }
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** A text field: typing there never picks an option (1-9 are its characters). */
|
|
63
|
+
export function isTextField(node) {
|
|
64
|
+
const tag = String(node?.tagName || '').toUpperCase();
|
|
65
|
+
return tag === 'INPUT' || tag === 'TEXTAREA' || !!node?.isContentEditable;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* createPresenter({ pose, options, waits, entrance })
|
|
70
|
+
* → { el, target, setLive(on), isLive(), setPose(name, opts), react(name), lookAt(node|null), setHome(node),
|
|
71
|
+
* watch(point), unwatch(), settle(pose, opts, { hop }), destroy() }
|
|
72
|
+
* `pose` / `options`: the card's own pose (present, pick, ask). `entrance`: the reaction it plays the first
|
|
73
|
+
* time it goes live (none on a card drawn while calm). settle(): the card is answered or left behind: the
|
|
74
|
+
* still it ends on ('success' is the "got it" face), after the hop when `hop` and it is live.
|
|
75
|
+
*/
|
|
76
|
+
export function createPresenter({ pose = 'present', options = null, waits = false, entrance = null } = {}) {
|
|
77
|
+
const face = el('span', 'asst-ask-face');
|
|
78
|
+
face.setAttribute('aria-hidden', 'true');
|
|
79
|
+
let want = { pose, options: options && typeof options === 'object' ? { ...options } : {} };
|
|
80
|
+
let rig = null;
|
|
81
|
+
let held = false; // the director dresses the rig (asst-mascot.js hold / restore)
|
|
82
|
+
let watching = false;
|
|
83
|
+
let watchedAt = -Infinity;
|
|
84
|
+
let final = null; // { pose, opts }: settled, a still from now on
|
|
85
|
+
let leaving = 0; // the hop plays out before the settled still
|
|
86
|
+
let destroyed = false;
|
|
87
|
+
let home = null; // what it looks at when nothing is pointed at
|
|
88
|
+
let looking = null; // what it looks at now
|
|
89
|
+
let entered = false;
|
|
90
|
+
|
|
91
|
+
const stillOpts = (name, opts = {}) => ({ ...opts, look: opts.look || STILL_LOOK[name] || { x: 0, y: 0 } });
|
|
92
|
+
|
|
93
|
+
function dropRig() {
|
|
94
|
+
if (!rig) return;
|
|
95
|
+
try { rig.destroy(); } catch { /* gone */ }
|
|
96
|
+
rig = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function paintStill(name, opts) {
|
|
100
|
+
dropRig();
|
|
101
|
+
try {
|
|
102
|
+
face.innerHTML = mascotLib.mascotSvg({ width: PRESENTER_SIZE.width, height: PRESENTER_SIZE.height, className: 'synabun-mascot asst-still' });
|
|
103
|
+
const svg = face.firstElementChild;
|
|
104
|
+
if (svg && typeof svg.setAttribute === 'function') {
|
|
105
|
+
svg.setAttribute('data-pose', name);
|
|
106
|
+
mascotLib.paintMascot(svg, mascotLib.stillFrame(name, stillOpts(name, opts)));
|
|
107
|
+
}
|
|
108
|
+
} catch { /* no SVG here (a stand-in document): the card reads without its face */ }
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function look(node) {
|
|
112
|
+
looking = node || null;
|
|
113
|
+
if (!rig || held || watching || final) return;
|
|
114
|
+
const point = centre(looking || home);
|
|
115
|
+
if (!point) return;
|
|
116
|
+
let box = null;
|
|
117
|
+
try { box = rig.el.getBoundingClientRect(); } catch { box = null; }
|
|
118
|
+
const to = lookToward(box, point);
|
|
119
|
+
try { rig.look(to.x, to.y); } catch { /* gone */ }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function applyWant() {
|
|
123
|
+
if (!rig || held || watching || final) return;
|
|
124
|
+
try { rig.setPose(want.pose, want.options); } catch { /* keeps its pose */ }
|
|
125
|
+
look(looking);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function setLive(on) {
|
|
129
|
+
if (destroyed || final) return;
|
|
130
|
+
if (on && !rig) {
|
|
131
|
+
try {
|
|
132
|
+
face.textContent = '';
|
|
133
|
+
rig = mascotLib.createMascot(face, { width: PRESENTER_SIZE.width, height: PRESENTER_SIZE.height, pose: want.pose, poseOptions: want.options });
|
|
134
|
+
} catch { rig = null; paintStill(want.pose, want.options); return; }
|
|
135
|
+
look(looking);
|
|
136
|
+
if (!entered) {
|
|
137
|
+
entered = true;
|
|
138
|
+
if (entrance && !reduced()) { try { rig.react(entrance); } catch { /* still */ } }
|
|
139
|
+
}
|
|
140
|
+
} else if (!on && rig) {
|
|
141
|
+
watching = false;
|
|
142
|
+
paintStill(want.pose, want.options);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function settle(name = 'success', opts = {}, { hop = false } = {}) {
|
|
147
|
+
if (destroyed) return;
|
|
148
|
+
final = { pose: name, opts };
|
|
149
|
+
watching = false;
|
|
150
|
+
face.classList.add('is-settled');
|
|
151
|
+
if (leaving) { clearTimeout(leaving); leaving = 0; }
|
|
152
|
+
if (hop && rig && !reduced()) {
|
|
153
|
+
try { rig.react('hop'); } catch { /* lands at once */ }
|
|
154
|
+
leaving = setTimeout(() => { leaving = 0; if (!destroyed && final) paintStill(final.pose, final.opts); }, HOP_MS);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
paintStill(name, opts);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const target = {
|
|
161
|
+
kind: 'card',
|
|
162
|
+
waits: !!waits,
|
|
163
|
+
get rig() { return rig; },
|
|
164
|
+
get el() { return rig ? rig.el : face; },
|
|
165
|
+
hold(on) { held = !!on; },
|
|
166
|
+
restore() { applyWant(); },
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
paintStill(want.pose, want.options);
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
el: face,
|
|
173
|
+
target,
|
|
174
|
+
setLive,
|
|
175
|
+
isLive: () => !!rig && !final,
|
|
176
|
+
/** The card's own pose; while the director holds the rig (or a field is watched) it waits for its turn. */
|
|
177
|
+
setPose(name, opts = null) {
|
|
178
|
+
want = { pose: name, options: opts && typeof opts === 'object' ? { ...opts } : {} };
|
|
179
|
+
if (final || destroyed) return;
|
|
180
|
+
if (rig) applyWant();
|
|
181
|
+
else paintStill(want.pose, want.options);
|
|
182
|
+
},
|
|
183
|
+
react(name) {
|
|
184
|
+
if (!rig || final || destroyed || reduced()) return;
|
|
185
|
+
try { rig.react(name); } catch { /* still */ }
|
|
186
|
+
},
|
|
187
|
+
/** Eyes on `node` (an option, a button); null: back to what it offers first. */
|
|
188
|
+
lookAt: look,
|
|
189
|
+
setHome(node) { home = node || null; if (!looking) look(null); },
|
|
190
|
+
/** Someone types in the card's field: the watch pose, its eyes on the caret (`point`, client px). */
|
|
191
|
+
watch(point) {
|
|
192
|
+
if (!rig || held || final || !point) return;
|
|
193
|
+
if (!watching) {
|
|
194
|
+
watching = true;
|
|
195
|
+
try { rig.setPose('watch'); } catch { /* keeps its pose */ }
|
|
196
|
+
}
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
if (now - watchedAt < WATCH_EVERY_MS) return;
|
|
199
|
+
watchedAt = now;
|
|
200
|
+
let box = null;
|
|
201
|
+
try { box = rig.el.getBoundingClientRect(); } catch { box = null; }
|
|
202
|
+
const to = lookToward(box, point, { horizontal: true });
|
|
203
|
+
try { rig.look(to.x, to.y); } catch { /* gone */ }
|
|
204
|
+
},
|
|
205
|
+
unwatch() {
|
|
206
|
+
if (!watching) return;
|
|
207
|
+
watching = false;
|
|
208
|
+
applyWant();
|
|
209
|
+
},
|
|
210
|
+
settle,
|
|
211
|
+
destroy() {
|
|
212
|
+
if (destroyed) return;
|
|
213
|
+
destroyed = true;
|
|
214
|
+
if (leaving) { clearTimeout(leaving); leaving = 0; }
|
|
215
|
+
dropRig();
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── Options ──
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* One option row: [key] label · description [badge] [↵]. `role` 'radio' / 'checkbox' (or none: a plain
|
|
224
|
+
* action button). The key shows its number shortcut (1-9); a checked checkbox shows ✓ instead.
|
|
225
|
+
*/
|
|
226
|
+
export function askOption({ label, description = '', index = 0, role = '', badge = '', go = false, title = '' } = {}) {
|
|
227
|
+
const btn = el('button', 'asst-ask-option');
|
|
228
|
+
btn.type = 'button';
|
|
229
|
+
btn.dataset.index = String(index);
|
|
230
|
+
btn.setAttribute('data-ask-option', '');
|
|
231
|
+
if (role) {
|
|
232
|
+
btn.setAttribute('role', role);
|
|
233
|
+
btn.setAttribute('aria-checked', 'false');
|
|
234
|
+
}
|
|
235
|
+
if (title) btn.title = title;
|
|
236
|
+
const key = el('span', 'asst-ask-key', index < 9 ? String(index + 1) : '');
|
|
237
|
+
key.setAttribute('aria-hidden', 'true');
|
|
238
|
+
btn.appendChild(key);
|
|
239
|
+
const main = el('span', 'asst-ask-main');
|
|
240
|
+
main.appendChild(el('span', 'asst-ask-label', label));
|
|
241
|
+
if (description) main.appendChild(el('span', 'asst-ask-desc', description));
|
|
242
|
+
btn.appendChild(main);
|
|
243
|
+
if (badge) btn.appendChild(el('span', 'asst-ask-badge', badge));
|
|
244
|
+
if (go) {
|
|
245
|
+
const arrow = el('span', 'asst-ask-go', '↵');
|
|
246
|
+
arrow.setAttribute('aria-hidden', 'true');
|
|
247
|
+
btn.appendChild(arrow);
|
|
248
|
+
}
|
|
249
|
+
btn._askKey = key;
|
|
250
|
+
return btn;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Check or clear an option row (aria-checked, the gold, a checkbox's ✓). */
|
|
254
|
+
export function setOptionChecked(btn, on) {
|
|
255
|
+
if (!btn) return;
|
|
256
|
+
btn.classList.toggle('selected', !!on);
|
|
257
|
+
if (btn.getAttribute?.('role') || btn.attributes?.role) btn.setAttribute('aria-checked', on ? 'true' : 'false');
|
|
258
|
+
const key = btn._askKey;
|
|
259
|
+
const role = btn.getAttribute?.('role') || btn.attributes?.role || '';
|
|
260
|
+
if (key && role === 'checkbox') {
|
|
261
|
+
const index = Number(btn.dataset.index);
|
|
262
|
+
key.textContent = on ? '✓' : (index < 9 ? String(index + 1) : '');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* What a key does in a card: { composer } (Esc: back to the message box), { pick: i } (1-9, never while
|
|
268
|
+
* typing in a field), { move: i } (arrows, Home, End inside an option group: `index` is the focused option,
|
|
269
|
+
* -1 when focus is elsewhere), or null.
|
|
270
|
+
*/
|
|
271
|
+
export function optionKeyIntent({ key, index = -1, count = 0, typing = false, alt = false, ctrl = false, meta = false } = {}) {
|
|
272
|
+
if (key === 'Escape') return { composer: true };
|
|
273
|
+
if (typing || alt || ctrl || meta) return null;
|
|
274
|
+
if (/^[1-9]$/.test(String(key))) {
|
|
275
|
+
const i = Number(key) - 1;
|
|
276
|
+
return i < count ? { pick: i } : null;
|
|
277
|
+
}
|
|
278
|
+
if (index < 0 || count < 1) return null;
|
|
279
|
+
switch (key) {
|
|
280
|
+
case 'ArrowDown':
|
|
281
|
+
case 'ArrowRight': return { move: (index + 1) % count };
|
|
282
|
+
case 'ArrowUp':
|
|
283
|
+
case 'ArrowLeft': return { move: (index - 1 + count) % count };
|
|
284
|
+
case 'Home': return { move: 0 };
|
|
285
|
+
case 'End': return { move: count - 1 };
|
|
286
|
+
default: return null;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const usable = (btn) => !!btn && !btn.disabled && !btn.hidden;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* One tab stop for an option group (roving tabindex): the checked option, else the first. Arrows, Home and
|
|
294
|
+
* End move focus; `onMove(button, index)` follows it (a radio group checks the option, as radios do).
|
|
295
|
+
*/
|
|
296
|
+
export function wireOptionGroup(group, { options = () => [], onMove = null } = {}) {
|
|
297
|
+
const all = () => (typeof options === 'function' ? options() : []) || [];
|
|
298
|
+
function sync(current = null) {
|
|
299
|
+
const list = all();
|
|
300
|
+
const checked = list.find((b) => (b.getAttribute?.('aria-checked') || b.attributes?.['aria-checked']) === 'true');
|
|
301
|
+
const pick = list.includes(current) ? current : (checked || list.find(usable) || list[0]);
|
|
302
|
+
for (const b of list) b.tabIndex = b === pick ? 0 : -1;
|
|
303
|
+
}
|
|
304
|
+
group.addEventListener('keydown', (e) => {
|
|
305
|
+
const list = all().filter(usable);
|
|
306
|
+
const index = list.indexOf(e.target);
|
|
307
|
+
if (index < 0) return;
|
|
308
|
+
const intent = optionKeyIntent({ key: e.key, index, count: list.length, alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey });
|
|
309
|
+
if (!intent || intent.move == null) return;
|
|
310
|
+
e.preventDefault();
|
|
311
|
+
e.stopPropagation();
|
|
312
|
+
const next = list[intent.move];
|
|
313
|
+
sync(next);
|
|
314
|
+
try { next.focus(); } catch { /* not focusable here */ }
|
|
315
|
+
onMove?.(next, intent.move);
|
|
316
|
+
});
|
|
317
|
+
group.addEventListener('focusin', (e) => { if (all().includes(e.target)) sync(e.target); });
|
|
318
|
+
sync();
|
|
319
|
+
return { sync };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The card's own keys, wherever focus is inside it: 1-9 pick an option of `options()` (the group in play),
|
|
324
|
+
* Esc goes back to the composer, Enter is `onEnter(event)`'s (true: handled). A key a group or a field
|
|
325
|
+
* already handled (defaultPrevented) is left alone.
|
|
326
|
+
*/
|
|
327
|
+
export function wireCardKeys(card, { options = () => [], onPick = null, onComposer = null, onEnter = null } = {}) {
|
|
328
|
+
card.addEventListener('keydown', (e) => {
|
|
329
|
+
if (e.defaultPrevented) return;
|
|
330
|
+
const list = ((typeof options === 'function' ? options() : []) || []).filter(usable);
|
|
331
|
+
const typing = isTextField(e.target);
|
|
332
|
+
const intent = optionKeyIntent({ key: e.key, count: list.length, typing, alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey });
|
|
333
|
+
if (intent?.composer) {
|
|
334
|
+
e.preventDefault();
|
|
335
|
+
e.stopPropagation();
|
|
336
|
+
onComposer?.();
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (intent?.pick != null) {
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
e.stopPropagation();
|
|
342
|
+
onPick?.(intent.pick, list[intent.pick]);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (e.key === 'Enter' && typeof onEnter === 'function' && onEnter(e) === true) {
|
|
346
|
+
e.preventDefault();
|
|
347
|
+
e.stopPropagation();
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** The presenter looks at the option pointed at or focused inside `root`, and at what it offers first when none is. */
|
|
353
|
+
export function wireGaze(root, presenter, { selector = '[data-ask-option]' } = {}) {
|
|
354
|
+
const optionOf = (node) => {
|
|
355
|
+
const hit = node?.closest?.(selector);
|
|
356
|
+
return hit && root.contains?.(hit) ? hit : null;
|
|
357
|
+
};
|
|
358
|
+
const focusedOption = () => {
|
|
359
|
+
try { return optionOf(document.activeElement); } catch { return null; }
|
|
360
|
+
};
|
|
361
|
+
root.addEventListener('pointerover', (e) => { const hit = optionOf(e.target); if (hit) presenter.lookAt(hit); });
|
|
362
|
+
root.addEventListener('pointerleave', () => presenter.lookAt(focusedOption()));
|
|
363
|
+
root.addEventListener('focusin', (e) => presenter.lookAt(optionOf(e.target)));
|
|
364
|
+
root.addEventListener('focusout', (e) => { if (!root.contains?.(e.relatedTarget)) presenter.lookAt(null); });
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/** Where a text field's caret is, roughly (client px): its left edge plus the typed width, mid-height. */
|
|
368
|
+
function caretOf(input) {
|
|
369
|
+
try {
|
|
370
|
+
const r = input.getBoundingClientRect();
|
|
371
|
+
const typed = String(input.value || '').slice(0, input.selectionStart ?? undefined).length;
|
|
372
|
+
return { x: r.left + Math.min(r.width - 12, 12 + typed * 7), y: r.top + r.height / 2 };
|
|
373
|
+
} catch { return null; }
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** The presenter watches `input` while it is typed in (its eyes on the caret), and goes back to its pose 1.5 s after. */
|
|
377
|
+
export function wireWatch(input, presenter) {
|
|
378
|
+
let timer = 0;
|
|
379
|
+
const stop = () => { if (timer) clearTimeout(timer); timer = 0; presenter.unwatch(); };
|
|
380
|
+
input.addEventListener('input', () => {
|
|
381
|
+
presenter.watch(caretOf(input));
|
|
382
|
+
if (timer) clearTimeout(timer);
|
|
383
|
+
timer = setTimeout(stop, WATCH_IDLE_MS);
|
|
384
|
+
});
|
|
385
|
+
input.addEventListener('blur', stop);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** "1–4" for four options, "1" for one: the keys a hint names. */
|
|
389
|
+
export function keyRange(count) {
|
|
390
|
+
const n = Math.max(0, Math.min(9, Number(count) || 0));
|
|
391
|
+
return n > 1 ? `1–${n}` : n === 1 ? '1' : '';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** The key that sends a multi-select from anywhere in its card, as the platform names it. */
|
|
395
|
+
export function sendModifier() {
|
|
396
|
+
try { return /Mac|iPhone|iPad/.test(globalThis.navigator?.platform || globalThis.navigator?.userAgent || '') ? '⌘' : 'Ctrl'; } catch { return 'Ctrl'; }
|
|
397
|
+
}
|
|
@@ -1,23 +1,26 @@
|
|
|
1
|
+
import { wireDropdowns, closeDropdowns, setDropdownDisabled, dropdownOf } from '../cc-dropdown.js';
|
|
2
|
+
import { configureToolPickerTranslation, toolPickerHTML, updateToolPicker, readToolPicker, toolWord } from '../tool-list-picker.js';
|
|
3
|
+
import { toolListView } from '../tool-list-view.js';
|
|
1
4
|
// ═══════════════════════════════════════════
|
|
2
5
|
// SynaBun Assistant — brain picker (toolbar fields)
|
|
3
6
|
// ═══════════════════════════════════════════
|
|
4
7
|
// Dropdown fields for the brain: model (provider icon + model; switching
|
|
5
8
|
// provider happens inside the model menu, grouped by provider), effort (or the
|
|
6
9
|
// OpenCode agent), permission mode and account (Claude/Codex, inline only when
|
|
7
|
-
// there is more than one to pick) in the composer row; project and
|
|
10
|
+
// there is more than one to pick) in the composer row; project and SynaBun tools
|
|
8
11
|
// in the toolbar. Every field opens an asst-menu popover (it flips when there
|
|
9
|
-
// is no room). A provider/account/
|
|
12
|
+
// is no room). A provider/account/tools change reports
|
|
10
13
|
// { requiresSwitch: true } so the panel sends `switch_brain`.
|
|
11
14
|
|
|
12
15
|
import {
|
|
13
16
|
ensureModelsForProfile,
|
|
14
17
|
fetchClaudeAccounts,
|
|
15
18
|
fetchCodexAccounts,
|
|
16
|
-
|
|
19
|
+
fetchToolCatalog,
|
|
17
20
|
fetchOpencodeAgents,
|
|
18
21
|
getCachedClaudeAccounts,
|
|
19
22
|
getCachedCodexAccounts,
|
|
20
|
-
|
|
23
|
+
getCachedToolCatalog,
|
|
21
24
|
getCachedOpencodeAgents,
|
|
22
25
|
getClaudeWorkerAccountId,
|
|
23
26
|
getCodexWorkerAccountId,
|
|
@@ -445,7 +448,7 @@ on('sync:assistant:catalog-changed', modelListsChanged);
|
|
|
445
448
|
// Launch: the menus' lists are read once the page has painted, so the first model menu opens from them.
|
|
446
449
|
if (typeof document !== 'undefined') {
|
|
447
450
|
registerLaunchPrefetch('assistant-model-catalog', () => loadModelCatalog(), { order: 10, owner: true });
|
|
448
|
-
registerLaunchPrefetch('assistant-accounts', () => Promise.allSettled([fetchClaudeAccounts(), fetchCodexAccounts(),
|
|
451
|
+
registerLaunchPrefetch('assistant-accounts', () => Promise.allSettled([fetchClaudeAccounts(), fetchCodexAccounts(), fetchToolCatalog(), fetchOpencodeAgents()]), { order: 14, owner: true });
|
|
449
452
|
}
|
|
450
453
|
|
|
451
454
|
function sameModel(selected, m) {
|
|
@@ -507,6 +510,7 @@ export async function openModelMenu(anchor, opts = {}) {
|
|
|
507
510
|
try { catalog = await loadModelCatalog({ force: opts.forceReload === true }); } catch { failed = true; } finally { anchor.removeAttribute('aria-busy'); }
|
|
508
511
|
if (!anchor.isConnected || (loading && currentMenu() !== loading.el)) return null;
|
|
509
512
|
}
|
|
513
|
+
if (opts.shouldOpen && !opts.shouldOpen()) return null;
|
|
510
514
|
const items = buildModelMenuItems(catalog);
|
|
511
515
|
const menu = openMenu(anchor, {
|
|
512
516
|
title: title || tr('assistant.brain.model', 'Model'),
|
|
@@ -635,7 +639,7 @@ function menuItemsSignature(items) {
|
|
|
635
639
|
// Creation order is DOM order within a slot: model · effort · variant · mode · tests · account in the composer.
|
|
636
640
|
// `effort` is the OpenCode agent for OpenCode; `variant` is OpenCode's effort (the model's variants).
|
|
637
641
|
// `tests` is the session's Test density, not a brain field: it shows only while the host supplies a level.
|
|
638
|
-
const FIELDS = ['model', 'effort', 'variant', 'mode', 'tests', 'account', 'project'
|
|
642
|
+
const FIELDS = ['model', 'effort', 'variant', 'mode', 'tests', 'account', 'project'];
|
|
639
643
|
// Slot of each field: the composer row (the brain and how it acts) · the toolbar (where it works).
|
|
640
644
|
const FIELD_GROUP = { model: 'brain', effort: 'brain', variant: 'brain', mode: 'brain', tests: 'brain', account: 'brain', project: 'context', mcp: 'context' };
|
|
641
645
|
|
|
@@ -650,6 +654,7 @@ const FIELD_GROUP = { model: 'brain', effort: 'brain', variant: 'brain', mode: '
|
|
|
650
654
|
* onProjects(projects) — the registered projects were (re)loaded }
|
|
651
655
|
*/
|
|
652
656
|
export function createBrainPicker(host, hooks = {}) {
|
|
657
|
+
configureToolPickerTranslation(hooks.t);
|
|
653
658
|
const t = tf(hooks.t);
|
|
654
659
|
const placement = hooks.menuPlacement || 'below';
|
|
655
660
|
|
|
@@ -674,6 +679,31 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
674
679
|
buttons[field] = btn;
|
|
675
680
|
}
|
|
676
681
|
|
|
682
|
+
const toolsSlot = el('div', 'asst-tools-picker');
|
|
683
|
+
(single ? root : host.context || root).appendChild(toolsSlot);
|
|
684
|
+
const toolsLife = new AbortController();
|
|
685
|
+
wireDropdowns(toolsSlot, { signal: toolsLife.signal });
|
|
686
|
+
let toolsSessionId = null;
|
|
687
|
+
toolsSlot.addEventListener('cc-dropdown-open', () => { toolsSessionId = hooks.getSessionId?.() ?? null; }, { signal: toolsLife.signal });
|
|
688
|
+
toolsSlot.addEventListener('change', (event) => {
|
|
689
|
+
if (currentBrain().provider !== 'opencode' || toolsSessionId !== (hooks.getSessionId?.() ?? null)) return;
|
|
690
|
+
commit({ tools: readToolPicker(event.target, { inherit: false }) }, 'tools');
|
|
691
|
+
}, { signal: toolsLife.signal });
|
|
692
|
+
function renderTools(brain) {
|
|
693
|
+
if (toolsSessionId !== (hooks.getSessionId?.() ?? null)) closeDropdowns(toolsSlot, { cancel: true });
|
|
694
|
+
const catalog = getCachedToolCatalog();
|
|
695
|
+
toolsSlot.hidden = brain.provider !== 'opencode' || !catalog;
|
|
696
|
+
if (toolsSlot.hidden) { closeDropdowns(toolsSlot, { cancel: true }); toolsSlot.innerHTML = ''; return; }
|
|
697
|
+
let input = toolsSlot.querySelector('input');
|
|
698
|
+
const options = { catalog, includeServers: false, label: t('assistant.brain.mcp') };
|
|
699
|
+
const tools = brain.tools ?? [...new Set([...(catalog.default || []).filter(id => id !== 'git'), 'browser'])];
|
|
700
|
+
if (!input) {
|
|
701
|
+
toolsSlot.innerHTML = toolPickerHTML(`asst-tools-${globalThis.crypto.randomUUID()}`, tools, options);
|
|
702
|
+
input = toolsSlot.querySelector('input');
|
|
703
|
+
} else if (!dropdownOf(input)?.classList.contains('open')) updateToolPicker(input, tools, options);
|
|
704
|
+
setDropdownDisabled(input, hooks.getRunning?.() === true);
|
|
705
|
+
}
|
|
706
|
+
|
|
677
707
|
let destroyed = false;
|
|
678
708
|
let loading = null;
|
|
679
709
|
// The empty state's "Choose a project" (mountEmptyProject): the project field's second button.
|
|
@@ -694,8 +724,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
694
724
|
codex: getCachedCodexAccounts(),
|
|
695
725
|
},
|
|
696
726
|
agents: getCachedOpencodeAgents(),
|
|
697
|
-
|
|
698
|
-
defaultMcpProfile: 'full',
|
|
727
|
+
toolCatalog: getCachedToolCatalog(),
|
|
699
728
|
projects: _projectsCache || [],
|
|
700
729
|
};
|
|
701
730
|
}
|
|
@@ -768,7 +797,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
768
797
|
const efforts = effortsFor(brain);
|
|
769
798
|
const level = efforts.find(e => e.id === brain.effort);
|
|
770
799
|
const hasEfforts = efforts.some(e => e.id !== 'off');
|
|
771
|
-
const
|
|
800
|
+
const toolCatalog = getCachedToolCatalog();
|
|
772
801
|
return {
|
|
773
802
|
meta,
|
|
774
803
|
model: modelLabel(brain),
|
|
@@ -780,7 +809,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
780
809
|
variant: level ? level.label : (brain.effort || t('assistant.brain.variantDefault', 'default variant')),
|
|
781
810
|
project: brain.project ? projectLabel(brain.project, _projectsCache || []) : t('assistant.brain.projectHome', 'Home'),
|
|
782
811
|
mode: permissionModeLabel(brain, (id, fallback) => t(`assistant.brain.modes.${id}`, fallback)),
|
|
783
|
-
mcp:
|
|
812
|
+
mcp: toolListView(toolCatalog, brain.tools, { translate: hooks.t }).summary,
|
|
784
813
|
account: ACCOUNT_PROVIDERS.has(brain.provider) ? accountLabel(brain) : '',
|
|
785
814
|
};
|
|
786
815
|
}
|
|
@@ -955,7 +984,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
955
984
|
const testsLabel = tests ? testDensityLabel(tests, hooks.t) : '';
|
|
956
985
|
setBtn('tests', { label: testsLabel, icon: testsIcon(testDensityBars(tests)), hidden: !tests, tooltip: `${t('assistant.tests.title', 'Test density')}: ${testsLabel}` });
|
|
957
986
|
if (tests) buttons.tests.dataset.level = tests; else delete buttons.tests.dataset.level;
|
|
958
|
-
|
|
987
|
+
renderTools(brain);
|
|
959
988
|
renderEmptyProject(brain);
|
|
960
989
|
}
|
|
961
990
|
|
|
@@ -1022,7 +1051,6 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1022
1051
|
{ field: 'project', label: t('assistant.brain.project', 'Working project'), value: brain.project || v.project, icon: ICON_FOLDER, hidden: false },
|
|
1023
1052
|
{ field: 'mode', label: t('assistant.brain.mode', 'Permission mode'), value: v.mode, icon: ICON_SHIELD, hidden: false },
|
|
1024
1053
|
{ field: 'tests', label: t('assistant.tests.title', 'Test density'), value: testDensity() ? testDensityLabel(testDensity(), hooks.t) : '', icon: testsIcon(testDensityBars(testDensity())), hidden: !testDensity() },
|
|
1025
|
-
{ field: 'mcp', label: t('assistant.brain.mcp', 'MCP tool profile'), value: v.mcp, icon: ICON_PLUG, hidden: false },
|
|
1026
1054
|
{ field: 'account', label: t('assistant.brain.accountShort', 'Account'), value: accountValue(brain), icon: ICON_ACCOUNT, hidden: !ACCOUNT_PROVIDERS.has(brain.provider) },
|
|
1027
1055
|
];
|
|
1028
1056
|
}
|
|
@@ -1038,7 +1066,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1038
1066
|
|
|
1039
1067
|
async function ensureLoaded(provider, { force = false } = {}) {
|
|
1040
1068
|
// The catalog too: defaults resolve against the menu's list (context variants, disabled models out).
|
|
1041
|
-
const jobs = [ensureModelsForProfile(provider, force),
|
|
1069
|
+
const jobs = [ensureModelsForProfile(provider, force), fetchToolCatalog(force), loadModelCatalog()];
|
|
1042
1070
|
if (provider === 'codex') jobs.push(fetchCodexAccounts(force));
|
|
1043
1071
|
if (provider === 'claude-code') jobs.push(fetchClaudeAccounts(force));
|
|
1044
1072
|
if (provider === 'opencode') jobs.push(fetchOpencodeAgents(force));
|
|
@@ -1077,7 +1105,8 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1077
1105
|
* Open a field menu; `anchorOverride` (e.g. the toolbar "⋯") when the inline
|
|
1078
1106
|
* button is hidden. `restoreFocus`: a pick sends focus back to the anchor.
|
|
1079
1107
|
*/
|
|
1080
|
-
async function openField(field, anchorOverride = null, { restoreFocus = false } = {}) {
|
|
1108
|
+
async function openField(field, anchorOverride = null, { restoreFocus = false, shouldOpen = () => true } = {}) {
|
|
1109
|
+
if (!shouldOpen()) return;
|
|
1081
1110
|
const anchor = anchorFor(field, anchorOverride);
|
|
1082
1111
|
const brain = currentBrain();
|
|
1083
1112
|
if (field === 'model') {
|
|
@@ -1088,6 +1117,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1088
1117
|
}
|
|
1089
1118
|
await openModelMenu(anchor, {
|
|
1090
1119
|
brain,
|
|
1120
|
+
shouldOpen,
|
|
1091
1121
|
t: hooks.t,
|
|
1092
1122
|
placement,
|
|
1093
1123
|
current: { provider: brain.provider, model: brain.model },
|
|
@@ -1126,7 +1156,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1126
1156
|
if (!_warmProviders.has(brain.provider)) {
|
|
1127
1157
|
anchor.setAttribute('aria-busy', 'true');
|
|
1128
1158
|
try { await ensureLoaded(brain.provider); } finally { anchor.removeAttribute('aria-busy'); }
|
|
1129
|
-
if (destroyed || !anchor.isConnected) return;
|
|
1159
|
+
if (destroyed || !anchor.isConnected || !shouldOpen()) return;
|
|
1130
1160
|
} else {
|
|
1131
1161
|
ensureLoaded(brain.provider).catch(() => {});
|
|
1132
1162
|
}
|
|
@@ -1141,7 +1171,7 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1141
1171
|
if (!warm) {
|
|
1142
1172
|
anchor.setAttribute('aria-busy', 'true');
|
|
1143
1173
|
try { await ensureLoaded(brain.provider); } finally { anchor.removeAttribute('aria-busy'); }
|
|
1144
|
-
if (destroyed || !anchor.isConnected) return;
|
|
1174
|
+
if (destroyed || !anchor.isConnected || !shouldOpen()) return;
|
|
1145
1175
|
}
|
|
1146
1176
|
let shown = null;
|
|
1147
1177
|
drawField(field, brain, (title, items, extra = {}) => {
|
|
@@ -1208,21 +1238,6 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1208
1238
|
return;
|
|
1209
1239
|
}
|
|
1210
1240
|
|
|
1211
|
-
if (field === 'mcp') {
|
|
1212
|
-
const presets = getCachedMcpProfilePresets();
|
|
1213
|
-
const items = Object.entries(presets).map(([id, preset]) => ({
|
|
1214
|
-
kind: 'radio',
|
|
1215
|
-
id,
|
|
1216
|
-
label: preset?.label || id,
|
|
1217
|
-
desc: preset?.description || '',
|
|
1218
|
-
tag: preset?.tools != null ? `${preset.tools} ${t('assistant.brain.tools', 'tools')}` : '',
|
|
1219
|
-
selected: id === brain.mcpProfile,
|
|
1220
|
-
onSelect: () => commit({ mcpProfile: id }, 'mcp'),
|
|
1221
|
-
}));
|
|
1222
|
-
menu(t('assistant.brain.mcp', 'MCP tool profile'), items);
|
|
1223
|
-
return;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
1241
|
if (field === 'mode') {
|
|
1227
1242
|
const modes = PERMISSION_MODES[brain.provider] || PERMISSION_MODES['claude-code'];
|
|
1228
1243
|
// Plan mode is its own switch: it combines with every approval mode below.
|
|
@@ -1319,15 +1334,14 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1319
1334
|
if (provider === before.provider) return;
|
|
1320
1335
|
await ensureLoaded(provider);
|
|
1321
1336
|
if (destroyed) return;
|
|
1322
|
-
const draft = { ...before, provider, model: model || '', effort: null, agent: null, accountId: null };
|
|
1337
|
+
const draft = { ...before, provider, model: model || '', effort: null, agent: null, accountId: null, tools: null };
|
|
1323
1338
|
// resolveBrainDefaults reads the new provider's catalog.
|
|
1324
1339
|
const next = resolveBrainDefaults(draft, {
|
|
1325
1340
|
models: { [provider]: resolvableModels(provider, draft.model) },
|
|
1326
1341
|
efforts: { [provider]: effortsFor(normalizeBrain(draft)) },
|
|
1327
1342
|
accounts: { 'claude-code': getCachedClaudeAccounts(), codex: getCachedCodexAccounts() },
|
|
1328
1343
|
agents: getCachedOpencodeAgents(),
|
|
1329
|
-
|
|
1330
|
-
defaultMcpProfile: 'full',
|
|
1344
|
+
toolCatalog: getCachedToolCatalog(),
|
|
1331
1345
|
projects: _projectsCache || [],
|
|
1332
1346
|
});
|
|
1333
1347
|
if (model && !next.model) next.model = model;
|
|
@@ -1360,6 +1374,6 @@ export function createBrainPicker(host, hooks = {}) {
|
|
|
1360
1374
|
mountEmptyProject,
|
|
1361
1375
|
setBrain() { render(); },
|
|
1362
1376
|
close: closeMenu,
|
|
1363
|
-
destroy() { destroyed = true; fitObserver?.disconnect(); if (fitFrame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(fitFrame); for (const off of offAccounts) { try { off?.(); } catch {} } closeMenu(); heroProjectMascot?.destroy(); Object.values(buttons).forEach((b) => b.remove()); heroProject?.remove(); heroProject = null; root.remove(); },
|
|
1377
|
+
destroy() { toolsLife.abort(); toolsSlot.remove(); destroyed = true; fitObserver?.disconnect(); if (fitFrame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(fitFrame); for (const off of offAccounts) { try { off?.(); } catch {} } closeMenu(); heroProjectMascot?.destroy(); Object.values(buttons).forEach((b) => b.remove()); heroProject?.remove(); heroProject = null; root.remove(); },
|
|
1364
1378
|
};
|
|
1365
1379
|
}
|