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
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tool selection of one MCP server runtime: which SynaBun groups it advertises, and how.
|
|
3
|
+
*
|
|
4
|
+
* strict: the runtime advertises exactly its groups (OpenCode, Antigravity, any other stdio client). focus: it
|
|
5
|
+
* advertises every group and its groups only steer the instructions (Claude Code's ToolSearch, Codex's deferred
|
|
6
|
+
* catalog, which Codex snapshots at the start of a turn). Gated groups follow the caller's role and grant in both
|
|
7
|
+
* modes, never a list.
|
|
8
|
+
*
|
|
9
|
+
* Each MCP server owns one ToolRuntime: the HTTP transport keeps several servers in one process, and a
|
|
10
|
+
* module-global selection once made one client's change land on another client's server.
|
|
11
|
+
*/
|
|
12
|
+
import type { RegisteredTool } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'fs';
|
|
14
|
+
import { basename, dirname, join } from 'path';
|
|
15
|
+
import { config } from '../config.js';
|
|
16
|
+
import { isHttpMode, sanitizeDesktopGrant, sanitizeRole, type CallerRole } from './identity.js';
|
|
17
|
+
import { ALWAYS_ON_TOOLS, SELECTABLE_GROUP_IDS, TOOL_LIST_LIMITS, TOOL_GROUPS, isGatedGroup, isSelectableGroup, sortGroups } from './tool-catalog.js';
|
|
18
|
+
import { parseToolList, resolveToolList, type AddedGroup, type CustomPresets } from './tool-list.js';
|
|
19
|
+
|
|
20
|
+
export type ToolMode = 'strict' | 'focus';
|
|
21
|
+
export type CallerCapability = 'computer';
|
|
22
|
+
|
|
23
|
+
/** The list of this runtime (comma-joined group ids) and the file its tool_groups changes go to. */
|
|
24
|
+
export const TOOLS_ENV = 'SYNABUN_TOOLS';
|
|
25
|
+
export const TOOLS_PATH_ENV = 'SYNABUN_TOOLS_PATH';
|
|
26
|
+
/** deferred / focus: a focus host; profiled / strict: a strict one. */
|
|
27
|
+
export const TOOL_CATALOG_MODE_ENV = 'SYNABUN_TOOL_CATALOG_MODE';
|
|
28
|
+
/** Read as aliases through the legacy conversion (an older Neural Interface, a user's own config.toml or an Antigravity mcp_config.json); never written. */
|
|
29
|
+
export const LEGACY_PROFILE_ENV = 'SYNABUN_PROFILE';
|
|
30
|
+
export const LEGACY_RUNTIME_PROFILE_PATH_ENV = 'SYNABUN_RUNTIME_PROFILE_PATH';
|
|
31
|
+
|
|
32
|
+
/** The owner's Default tools: Settings and `synabun tools set` write it; a runtime reads it when it starts, never later. */
|
|
33
|
+
export const DEFAULT_TOOLS_PATH = join(config.dataHome, 'data', 'default-tools.json');
|
|
34
|
+
const LEGACY_DEFAULT_PROFILE_PATHS = [join(config.dataDir, 'active-profile.json'), join(config.dataHome, 'data', 'active-profile.json')];
|
|
35
|
+
const REGISTRY_PATH = join(config.dataHome, 'data', 'mcp-registry.json');
|
|
36
|
+
|
|
37
|
+
/** Groups only a caller with this role sees: the role decides, so no list can reveal or hide them. */
|
|
38
|
+
export const ROLE_GATED_GROUPS: Readonly<Record<string, CallerRole>> = Object.freeze(Object.fromEntries(
|
|
39
|
+
TOOL_GROUPS.filter((group) => group.gate === 'role:assistant').map((group) => [group.id, 'assistant' as const]),
|
|
40
|
+
));
|
|
41
|
+
/** Groups a capability the caller proves unlocks: `computer` needs a desktop grant (and macOS). */
|
|
42
|
+
export const CAPABILITY_GATED_GROUPS: Readonly<Record<string, CallerCapability>> = Object.freeze(Object.fromEntries(
|
|
43
|
+
TOOL_GROUPS.filter((group) => group.gate === 'grant:computer').map((group) => [group.id, 'computer' as const]),
|
|
44
|
+
));
|
|
45
|
+
|
|
46
|
+
export function isRoleGatedGroup(group: string): boolean {
|
|
47
|
+
return Object.prototype.hasOwnProperty.call(ROLE_GATED_GROUPS, group);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function isCapabilityGatedGroup(group: string): boolean {
|
|
51
|
+
return Object.prototype.hasOwnProperty.call(CAPABILITY_GATED_GROUPS, group);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Where a runtime's selection came from (tool_groups get shows it). */
|
|
55
|
+
export type ToolSelectionSource =
|
|
56
|
+
| 'tools-file' | 'tools-env' | 'legacy-runtime-file' | 'claude-code' | 'legacy-profile-env'
|
|
57
|
+
| 'default-tools' | 'legacy-default' | 'all' | 'http' | 'header' | 'channel' | 'explicit' | 'tool_groups';
|
|
58
|
+
|
|
59
|
+
export interface ToolSelection {
|
|
60
|
+
/** Selectable groups, catalog order. */
|
|
61
|
+
groups: string[];
|
|
62
|
+
mode: ToolMode;
|
|
63
|
+
/** The host's own mode, when a list that could not be read made this selection strict. */
|
|
64
|
+
hostMode?: ToolMode;
|
|
65
|
+
/** Why the list could not be read: the runtime then has its always-on and gated tools only. */
|
|
66
|
+
listError: string | null;
|
|
67
|
+
source: ToolSelectionSource;
|
|
68
|
+
/** Groups the runtime got without naming them (browser for a browser group). */
|
|
69
|
+
added: AddedGroup[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const SOURCE_NAMES: Partial<Record<ToolSelectionSource, string>> = {
|
|
73
|
+
'tools-file': 'SYNABUN_TOOLS_PATH',
|
|
74
|
+
'tools-env': 'SYNABUN_TOOLS',
|
|
75
|
+
'legacy-runtime-file': 'SYNABUN_RUNTIME_PROFILE_PATH',
|
|
76
|
+
'legacy-profile-env': 'SYNABUN_PROFILE',
|
|
77
|
+
'default-tools': 'default-tools.json',
|
|
78
|
+
'legacy-default': 'active-profile.json',
|
|
79
|
+
header: 'X-Synabun-Tools',
|
|
80
|
+
channel: 'the worker channel',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** The selection of a host's own list, with its host mode. */
|
|
84
|
+
export function selectionOf(groups: readonly string[], mode: ToolMode, source: ToolSelectionSource, added: AddedGroup[] = []): ToolSelection {
|
|
85
|
+
return { groups: sortGroups(groups).filter(isSelectableGroup), mode, listError: null, source, added };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** What a list that could not be read leaves: memory tools only (and the gated ones), strict until tool_groups succeeds. */
|
|
89
|
+
export function unreadableSelection(source: ToolSelectionSource, reason: string, hostMode: ToolMode): ToolSelection {
|
|
90
|
+
const name = SOURCE_NAMES[source];
|
|
91
|
+
return { groups: [], mode: 'strict', hostMode, listError: name ? `${name}: ${reason}` : reason, source, added: [] };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function isClaudeCode(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
95
|
+
return env.CLAUDECODE === '1';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** focus for Codex (SYNABUN_TOOL_CATALOG_MODE=deferred, or a managed Codex sidepanel), strict otherwise. */
|
|
99
|
+
export function readHostMode(env: NodeJS.ProcessEnv = process.env): ToolMode {
|
|
100
|
+
const configured = String(env[TOOL_CATALOG_MODE_ENV] || '').trim().toLowerCase();
|
|
101
|
+
if (configured === 'deferred' || configured === 'focus') return 'focus';
|
|
102
|
+
if (configured === 'profiled' || configured === 'strict') return 'strict';
|
|
103
|
+
// Managed Codex sidepanels have always used this runtime prefix: one spawned before its launch environment
|
|
104
|
+
// named the catalog mode is a focus host all the same.
|
|
105
|
+
if (/^codex-sp-/.test(String(env.SYNABUN_TERMINAL_SESSION || ''))) return 'focus';
|
|
106
|
+
return 'strict';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const loggedProblems = new Set<string>();
|
|
110
|
+
function logOnce(message: string): void {
|
|
111
|
+
if (loggedProblems.has(message)) return;
|
|
112
|
+
loggedProblems.add(message);
|
|
113
|
+
console.error(`[SynaBun] ${message}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The registry's presets, read only to convert a stored profile name. */
|
|
117
|
+
export function readCustomPresets(): CustomPresets | null {
|
|
118
|
+
try {
|
|
119
|
+
if (!existsSync(REGISTRY_PATH)) return null;
|
|
120
|
+
const registry = JSON.parse(readFileSync(REGISTRY_PATH, 'utf-8'));
|
|
121
|
+
const profiles = registry?.profiles;
|
|
122
|
+
return profiles && typeof profiles === 'object' && !Array.isArray(profiles) ? profiles as CustomPresets : null;
|
|
123
|
+
} catch (err) {
|
|
124
|
+
logOnce(`Could not read the MCP registry for stored profile names: ${err instanceof Error ? err.message : String(err)}`);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function fromList(value: unknown, source: ToolSelectionSource, hostMode: ToolMode, legacy: boolean): ToolSelection {
|
|
130
|
+
const parsed = parseToolList(value, legacy ? { legacy: true, customPresets: readCustomPresets() } : {});
|
|
131
|
+
if (!parsed.ok) return unreadableSelection(source, parsed.message, hostMode);
|
|
132
|
+
const resolved = resolveToolList(parsed.items, { source: 'runtime' });
|
|
133
|
+
if (!resolved.ok) return unreadableSelection(source, resolved.message, hostMode);
|
|
134
|
+
return selectionOf(resolved.groups, hostMode, source, resolved.added);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* The selection of a list given in code or by a request (a header, the worker channel), resolved like a runtime's
|
|
139
|
+
* own: `all` expanded, a browser group's `browser` added. One that cannot be read is memory tools only, strict.
|
|
140
|
+
*/
|
|
141
|
+
export function selectionFromList(value: unknown, mode: ToolMode, source: ToolSelectionSource, { legacy = false }: { legacy?: boolean } = {}): ToolSelection {
|
|
142
|
+
return fromList(value, source, mode, legacy);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** A runtime file holds `{ "tools": [...] }`; one an older Neural Interface wrote holds `{ "profile": "..." }`. */
|
|
146
|
+
function fromFile(path: string | undefined, source: ToolSelectionSource, hostMode: ToolMode): ToolSelection | null {
|
|
147
|
+
const file = String(path || '').trim();
|
|
148
|
+
if (!file) return null;
|
|
149
|
+
let text: string;
|
|
150
|
+
try {
|
|
151
|
+
if (!existsSync(file)) return null;
|
|
152
|
+
text = readFileSync(file, 'utf-8');
|
|
153
|
+
} catch (err) {
|
|
154
|
+
return unreadableSelection(source, `${basename(file)} could not be read (${err instanceof Error ? err.message : String(err)})`, hostMode);
|
|
155
|
+
}
|
|
156
|
+
let data: unknown;
|
|
157
|
+
try {
|
|
158
|
+
data = JSON.parse(text);
|
|
159
|
+
} catch {
|
|
160
|
+
return unreadableSelection(source, `${basename(file)} is not JSON`, hostMode);
|
|
161
|
+
}
|
|
162
|
+
const record = data && typeof data === 'object' && !Array.isArray(data) ? data as Record<string, unknown> : null;
|
|
163
|
+
if (record && Array.isArray(record.tools)) return fromList(record.tools, source, hostMode, false);
|
|
164
|
+
if (record && typeof record.profile === 'string') return fromList(record.profile, source, hostMode, true);
|
|
165
|
+
return unreadableSelection(source, `${basename(file)} names no tools`, hostMode);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function fromEnv(value: string | undefined, source: ToolSelectionSource, hostMode: ToolMode, legacy: boolean): ToolSelection | null {
|
|
169
|
+
if (value === undefined || !value.trim()) return null;
|
|
170
|
+
return fromList(value, source, hostMode, legacy);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The selection a runtime starts with, the first of: its runtime file (SYNABUN_TOOLS_PATH), its list
|
|
175
|
+
* (SYNABUN_TOOLS), the runtime file of an older Neural Interface (SYNABUN_RUNTIME_PROFILE_PATH), Claude Code
|
|
176
|
+
* (CLAUDECODE=1: focus on every group), the old SYNABUN_PROFILE, the owner's Default tools, the old
|
|
177
|
+
* active-profile.json, every group. The runtime's own pins come before CLAUDECODE, so a strict runtime that
|
|
178
|
+
* inherited it from a parent shell keeps its list. A list that cannot be read is not skipped: the runtime starts
|
|
179
|
+
* with memory tools only and says why (fail closed, never every group).
|
|
180
|
+
*/
|
|
181
|
+
export function readInitialTools(env: NodeJS.ProcessEnv = process.env): ToolSelection {
|
|
182
|
+
const hostMode = readHostMode(env);
|
|
183
|
+
const steps: Array<() => ToolSelection | null> = [
|
|
184
|
+
() => fromFile(env[TOOLS_PATH_ENV], 'tools-file', hostMode),
|
|
185
|
+
() => fromEnv(env[TOOLS_ENV], 'tools-env', hostMode, false),
|
|
186
|
+
() => fromFile(env[LEGACY_RUNTIME_PROFILE_PATH_ENV], 'legacy-runtime-file', hostMode),
|
|
187
|
+
() => (isClaudeCode(env) ? selectionOf(SELECTABLE_GROUP_IDS, 'focus', 'claude-code') : null),
|
|
188
|
+
() => fromEnv(env[LEGACY_PROFILE_ENV], 'legacy-profile-env', hostMode, true),
|
|
189
|
+
() => fromFile(DEFAULT_TOOLS_PATH, 'default-tools', hostMode),
|
|
190
|
+
() => {
|
|
191
|
+
for (const path of LEGACY_DEFAULT_PROFILE_PATHS) {
|
|
192
|
+
const selection = fromFile(path, 'legacy-default', hostMode);
|
|
193
|
+
if (selection) return selection;
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
for (const step of steps) {
|
|
199
|
+
const selection = step();
|
|
200
|
+
if (!selection) continue;
|
|
201
|
+
if (selection.listError) logOnce(`Could not read this runtime's tool list (${selection.listError}); only memory tools are loaded.`);
|
|
202
|
+
return selection;
|
|
203
|
+
}
|
|
204
|
+
return selectionOf(SELECTABLE_GROUP_IDS, hostMode, 'all');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The value an older Neural Interface's /api/mcp/runtime-profile and runtime file understand for these groups: a
|
|
209
|
+
* comma list (every selectable id is one of its group names), or its always-on pseudo-group `memory` for none.
|
|
210
|
+
*/
|
|
211
|
+
export function legacyProfileValue(groups: readonly string[]): string {
|
|
212
|
+
const ids = sortGroups(groups).filter(isSelectableGroup);
|
|
213
|
+
return ids.length ? ids.join(',') : 'memory';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function writeJsonFile(path: string, value: unknown): boolean {
|
|
217
|
+
const temp = `${path}.synabun-${process.pid}-${Date.now()}.tmp`;
|
|
218
|
+
try {
|
|
219
|
+
const dir = dirname(path);
|
|
220
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
221
|
+
writeFileSync(temp, JSON.stringify(value, null, 2) + '\n', 'utf-8');
|
|
222
|
+
renameSync(temp, path);
|
|
223
|
+
return true;
|
|
224
|
+
} catch (err) {
|
|
225
|
+
try { unlinkSync(temp); } catch { /* never written */ }
|
|
226
|
+
console.error(`[SynaBun] Failed to save this runtime's tools at ${path}:`, err);
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Save this runtime's groups where a restarted child of the same runtime reads them, never the shared default.
|
|
233
|
+
* Over HTTP there is no such file: process.env is the Neural Interface's own, not a caller's.
|
|
234
|
+
*/
|
|
235
|
+
export function runtimeToolsWithExternalRefs(groups: readonly string[]): string[] {
|
|
236
|
+
const tools = sortGroups(groups).filter(isSelectableGroup);
|
|
237
|
+
if (isHttpMode()) return tools; // HTTP identity belongs to the request; NI preserves the outer run's refs.
|
|
238
|
+
const path = String(process.env[TOOLS_PATH_ENV] || process.env[LEGACY_RUNTIME_PROFILE_PATH_ENV] || '').trim();
|
|
239
|
+
let previous: unknown = process.env[TOOLS_ENV] || '';
|
|
240
|
+
try { if (path && existsSync(path)) previous = JSON.parse(readFileSync(path, 'utf8')).tools ?? previous; } catch { /* launch env remains the fallback */ }
|
|
241
|
+
const parsed = parseToolList(previous);
|
|
242
|
+
const refs = parsed.ok ? parsed.items.filter(item => item.startsWith('mcp:')) : [];
|
|
243
|
+
const compact = tools.length + refs.length > TOOL_LIST_LIMITS.maxItems && SELECTABLE_GROUP_IDS.every(id => tools.includes(id));
|
|
244
|
+
return [...(compact ? ['all'] : tools), ...refs];
|
|
245
|
+
}
|
|
246
|
+
export function persistRuntimeTools(groups: readonly string[]): 'tools-file' | 'legacy-runtime-file' | null {
|
|
247
|
+
if (isHttpMode()) return null;
|
|
248
|
+
const tools = runtimeToolsWithExternalRefs(groups);
|
|
249
|
+
const toolsPath = String(process.env[TOOLS_PATH_ENV] || '').trim();
|
|
250
|
+
if (toolsPath) return writeJsonFile(toolsPath, { tools, updatedAt: new Date().toISOString() }) ? 'tools-file' : null;
|
|
251
|
+
const legacyPath = String(process.env[LEGACY_RUNTIME_PROFILE_PATH_ENV] || '').trim();
|
|
252
|
+
if (legacyPath) return writeJsonFile(legacyPath, { profile: legacyProfileValue(groups), tools }) ? 'legacy-runtime-file' : null;
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function logCodexConfigNoticeOnce(): void {
|
|
257
|
+
logOnce(
|
|
258
|
+
'Changing this runtime\'s tools never rewrites ~/.codex/config.toml: the canonical Codex registration loads every '
|
|
259
|
+
+ 'SynaBun tool (SYNABUN_TOOLS=all, deferred catalog) and a tool list only sets a Codex runtime\'s focus.',
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface ToolApplyResult {
|
|
264
|
+
groups: string[];
|
|
265
|
+
/** The groups the instructions name: the selection plus the gated groups the role and grant unlock. */
|
|
266
|
+
activeGroups: string[];
|
|
267
|
+
/** Groups that became active, and groups that stopped being active. */
|
|
268
|
+
enabled: string[];
|
|
269
|
+
disabled: string[];
|
|
270
|
+
added: AddedGroup[];
|
|
271
|
+
/** Tools this server advertises now. */
|
|
272
|
+
totalTools: number;
|
|
273
|
+
changed: boolean;
|
|
274
|
+
/** Whether any tool appeared in or left the host's tool list. */
|
|
275
|
+
advertisedChanged: boolean;
|
|
276
|
+
mode: ToolMode;
|
|
277
|
+
listError: string | null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface ToolRuntimeOptions {
|
|
281
|
+
role?: CallerRole | null;
|
|
282
|
+
capabilities?: CallerCapability[] | null;
|
|
283
|
+
platform?: string;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function sameList(a: readonly string[], b: readonly string[]): boolean {
|
|
287
|
+
return a.length === b.length && a.every((item, index) => item === b[index]);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export class ToolRuntime {
|
|
291
|
+
private groups: string[];
|
|
292
|
+
private mode: ToolMode;
|
|
293
|
+
private hostMode: ToolMode;
|
|
294
|
+
private listError: string | null;
|
|
295
|
+
private source: ToolSelectionSource;
|
|
296
|
+
private added: AddedGroup[];
|
|
297
|
+
private role: CallerRole | null;
|
|
298
|
+
private capabilities: Set<CallerCapability>;
|
|
299
|
+
private readonly platform: string;
|
|
300
|
+
private activeGroups: Set<string>;
|
|
301
|
+
private readonly toolGroups = new Map<string, RegisteredTool[]>();
|
|
302
|
+
private ungroupedTools: number = ALWAYS_ON_TOOLS.length;
|
|
303
|
+
private onToolsChanged: (() => void | Promise<void>) | null = null;
|
|
304
|
+
private notificationTimer: ReturnType<typeof setTimeout> | null = null;
|
|
305
|
+
|
|
306
|
+
constructor(selection: ToolSelection = readInitialTools(), options: ToolRuntimeOptions = {}) {
|
|
307
|
+
// stdio children of the Codex/OpenCode assistant brains receive the role through their launch env; HTTP
|
|
308
|
+
// sessions bind it later (setRole). The desktop grant the same way (setCapabilities).
|
|
309
|
+
this.role = options.role !== undefined ? options.role : sanitizeRole(process.env.SYNABUN_ROLE);
|
|
310
|
+
this.capabilities = new Set(options.capabilities ?? (sanitizeDesktopGrant(process.env.SYNABUN_DESKTOP_GRANT) ? ['computer'] : []));
|
|
311
|
+
this.platform = options.platform || process.platform;
|
|
312
|
+
this.groups = sortGroups(selection.groups).filter(isSelectableGroup);
|
|
313
|
+
this.mode = selection.mode;
|
|
314
|
+
this.hostMode = selection.hostMode ?? selection.mode;
|
|
315
|
+
this.listError = selection.listError ?? null;
|
|
316
|
+
this.source = selection.source;
|
|
317
|
+
this.added = [...(selection.added || [])];
|
|
318
|
+
this.activeGroups = this.resolveActive(this.groups);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
getGroups(): string[] { return [...this.groups]; }
|
|
322
|
+
/** The groups the instructions name: the selection plus the gated groups the role and grant unlock. */
|
|
323
|
+
getActiveGroups(): Set<string> { return this.activeGroups; }
|
|
324
|
+
getMode(): ToolMode { return this.mode; }
|
|
325
|
+
/** The mode the host has once its list reads: focus for Codex and Claude Code, strict otherwise. */
|
|
326
|
+
getHostMode(): ToolMode { return this.hostMode; }
|
|
327
|
+
getListError(): string | null { return this.listError; }
|
|
328
|
+
getSource(): ToolSelectionSource { return this.source; }
|
|
329
|
+
getAdded(): AddedGroup[] { return [...this.added]; }
|
|
330
|
+
getRole(): CallerRole | null { return this.role; }
|
|
331
|
+
getCapabilities(): CallerCapability[] { return [...this.capabilities]; }
|
|
332
|
+
|
|
333
|
+
getSelection(): ToolSelection {
|
|
334
|
+
return { groups: this.getGroups(), mode: this.mode, hostMode: this.hostMode, listError: this.listError, source: this.source, added: this.getAdded() };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Plain setter: callers apply() afterwards so the gated groups follow (index.ts setServerRole does both). */
|
|
338
|
+
setRole(role: CallerRole | null): void { this.role = role; }
|
|
339
|
+
/** Plain setter, like setRole. */
|
|
340
|
+
setCapabilities(capabilities: CallerCapability[]): void { this.capabilities = new Set(capabilities); }
|
|
341
|
+
|
|
342
|
+
setToolGroup(name: string, tools: RegisteredTool[]): void {
|
|
343
|
+
this.toolGroups.set(name, tools);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/** How many registered tools sit outside every group (the always-on tools, and any compatibility name). */
|
|
347
|
+
setUngroupedToolCount(count: number): void {
|
|
348
|
+
this.ungroupedTools = count;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
setOnToolsChanged(fn: (() => void | Promise<void>) | null): void {
|
|
352
|
+
this.onToolsChanged = fn;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// HTTP clients can refresh over their open notification stream without destroying an in-flight call.
|
|
356
|
+
private inBandRefresh: (() => Promise<boolean>) | null = null;
|
|
357
|
+
setInBandRefresh(fn: () => Promise<boolean>): void { this.inBandRefresh = fn; }
|
|
358
|
+
hasInBandRefresh(): boolean { return !!this.inBandRefresh; }
|
|
359
|
+
refreshBeforeReply(): Promise<boolean> { return this.inBandRefresh?.() ?? Promise.resolve(false); }
|
|
360
|
+
|
|
361
|
+
dispose(): void {
|
|
362
|
+
if (this.notificationTimer) clearTimeout(this.notificationTimer);
|
|
363
|
+
this.notificationTimer = null;
|
|
364
|
+
this.onToolsChanged = null;
|
|
365
|
+
this.inBandRefresh = null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Coalesce one delayed tools/list_changed. The delay matters: OpenCode used to get the notification while the
|
|
370
|
+
* tool call that caused it was still in flight, and could abort or cache the old tool round trip.
|
|
371
|
+
*/
|
|
372
|
+
scheduleToolsChangedNotification(delayMs = 10): boolean {
|
|
373
|
+
if (!this.onToolsChanged) return false;
|
|
374
|
+
if (this.notificationTimer) clearTimeout(this.notificationTimer);
|
|
375
|
+
this.notificationTimer = setTimeout(() => {
|
|
376
|
+
this.notificationTimer = null;
|
|
377
|
+
try {
|
|
378
|
+
void Promise.resolve(this.onToolsChanged?.()).catch((err) => {
|
|
379
|
+
console.error('[SynaBun] tools/list_changed notifier failed:', err);
|
|
380
|
+
});
|
|
381
|
+
} catch (err) {
|
|
382
|
+
console.error('[SynaBun] tools/list_changed notifier failed:', err);
|
|
383
|
+
}
|
|
384
|
+
}, Math.max(0, delayMs));
|
|
385
|
+
this.notificationTimer.unref?.();
|
|
386
|
+
return true;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** The selection with the gates applied: a gated group is active iff this runtime's role or grant unlocks it. */
|
|
390
|
+
private resolveActive(groups: readonly string[]): Set<string> {
|
|
391
|
+
const active = new Set(groups.filter(isSelectableGroup));
|
|
392
|
+
for (const [group, role] of Object.entries(ROLE_GATED_GROUPS)) if (this.role === role) active.add(group);
|
|
393
|
+
for (const [group, capability] of Object.entries(CAPABILITY_GATED_GROUPS)) {
|
|
394
|
+
if (this.capabilities.has(capability) && (capability !== 'computer' || this.platform === 'darwin')) active.add(group);
|
|
395
|
+
}
|
|
396
|
+
return active;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Tools this server advertises: the ones outside every group and each group's enabled tools. */
|
|
400
|
+
getAdvertisedToolCount(): number {
|
|
401
|
+
let total = this.ungroupedTools;
|
|
402
|
+
for (const tools of this.toolGroups.values()) total += tools.filter((tool) => tool.enabled).length;
|
|
403
|
+
return total;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Apply a new selection (or re-apply the current one after a role or grant change). Tools are flipped directly:
|
|
408
|
+
* RegisteredTool.enable()/disable() would each send a notification, and the caller sends one after its answer.
|
|
409
|
+
*/
|
|
410
|
+
apply(next: Partial<ToolSelection> = {}): ToolApplyResult {
|
|
411
|
+
const groups = next.groups ? sortGroups(next.groups).filter(isSelectableGroup) : this.groups;
|
|
412
|
+
const mode = next.mode ?? this.mode;
|
|
413
|
+
const listError = next.listError !== undefined ? next.listError : this.listError;
|
|
414
|
+
const previous = { groups: this.groups, mode: this.mode, listError: this.listError, active: this.activeGroups };
|
|
415
|
+
const active = this.resolveActive(groups);
|
|
416
|
+
|
|
417
|
+
let advertisedChanged = false;
|
|
418
|
+
for (const [group, tools] of this.toolGroups) {
|
|
419
|
+
// A gated group ignores the mode: a focus host gets every other group up front, never one its role or grant does not unlock.
|
|
420
|
+
const advertise = isGatedGroup(group) ? active.has(group) : mode === 'focus' || active.has(group);
|
|
421
|
+
for (const tool of tools) {
|
|
422
|
+
if (tool.enabled === advertise) continue;
|
|
423
|
+
tool.enabled = advertise;
|
|
424
|
+
advertisedChanged = true;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const enabled = sortGroups([...active].filter((group) => !previous.active.has(group)));
|
|
429
|
+
const disabled = sortGroups([...previous.active].filter((group) => !active.has(group)));
|
|
430
|
+
this.groups = groups;
|
|
431
|
+
this.mode = mode;
|
|
432
|
+
this.listError = listError;
|
|
433
|
+
this.activeGroups = active;
|
|
434
|
+
if (next.hostMode) this.hostMode = next.hostMode;
|
|
435
|
+
if (next.source) this.source = next.source;
|
|
436
|
+
if (next.added) this.added = [...next.added];
|
|
437
|
+
|
|
438
|
+
const totalTools = this.getAdvertisedToolCount();
|
|
439
|
+
const changed = enabled.length > 0 || disabled.length > 0 || previous.mode !== mode
|
|
440
|
+
|| previous.listError !== listError || !sameList(previous.groups, groups);
|
|
441
|
+
if (changed) {
|
|
442
|
+
console.error(`[SynaBun] Tools (${mode}): ${groups.join(',') || 'memory only'} (${totalTools} tools, +${enabled.join(',') || 'none'}, -${disabled.join(',') || 'none'})`);
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
groups: [...groups],
|
|
446
|
+
activeGroups: sortGroups(active),
|
|
447
|
+
enabled,
|
|
448
|
+
disabled,
|
|
449
|
+
added: [...this.added],
|
|
450
|
+
totalTools,
|
|
451
|
+
changed,
|
|
452
|
+
advertisedChanged,
|
|
453
|
+
mode,
|
|
454
|
+
listError,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
}
|
|
@@ -94,7 +94,7 @@ export const SURFACE_META: Record<SurfaceName, SurfaceMeta> = {
|
|
|
94
94
|
'worker-outcome': { label: 'Worker outcome', description: 'Assistant workers: reads done / blocked / needs input from a final message that has no ## Result block (instead of spending a retry turn), and what kept a failed or blocked run from finishing (capability, access, needs the user, transient), which decides whether escalation is offered.', fallback: 'one result-retry worker turn; every failed, blocked or result-less run offers a one-tier escalation' },
|
|
95
95
|
'worker-claim': { label: 'Worker claim', description: 'Assistant workers: whether a run reported done while claiming success (tests pass, fixed, verified) that its own command output does not show. Flags the run so the Assistant verifies before reporting; asked only when the turn exposed commands or tool results.', fallback: 'no check', ridesWith: 'worker-outcome' },
|
|
96
96
|
'desktop-target': { label: 'Desktop target', description: 'Computer use: ranks the visible controls of the front window against a computer_ax intent. Advisory; one low-risk press by intent only behind the desktop benchmark gate.', fallback: 'the full accessibility tree, or a word-overlap ranking when an intent is given' },
|
|
97
|
-
'budget-split': { label: 'Budget split', description: 'Assistant sessions with a budget: how much of the money still to be spent will land on each provider (Claude Code, Codex, OpenCode), asked at the first route and at checkpoints (a share used up, a budget change, a run ending). It only divides what the user allowed: shares never add up to more than is left, and it never picks a model.', fallback: 'weights from the saved routes, the session\'s tasks and live runs, times the list price of the routed models; the brain keeps its reserve either way' },
|
|
97
|
+
'budget-split': { label: 'Budget split', description: 'Assistant sessions with a budget: how much of the money still to be spent will land on each priced provider (Claude Code, Codex, OpenCode; Antigravity reports tokens, not prices, so it takes no share and its runs do not run under a budget), asked at the first route and at checkpoints (a share used up, a budget change, a run ending). It only divides what the user allowed: shares never add up to more than is left, and it never picks a model.', fallback: 'weights from the saved routes, the session\'s tasks and live runs, times the list price of the routed models; the brain keeps its reserve either way' },
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
const SURFACE_DEFAULTS: Record<SurfaceName, SurfaceSettings> = {
|
|
@@ -28,7 +28,7 @@ export interface WbRect { x: number; y: number; width: number; height: number }
|
|
|
28
28
|
/** Persisted whiteboard element — the union of every element type's fields. */
|
|
29
29
|
export interface WbElement {
|
|
30
30
|
id: string; type: string; x?: number; y?: number; width?: number; height?: number;
|
|
31
|
-
content?: string; items?: string[]; ordered?: boolean; fontSize?: number; color?: string;
|
|
31
|
+
content?: string; items?: string[]; levels?: number[]; ordered?: boolean; fontSize?: number; color?: string;
|
|
32
32
|
bold?: boolean; italic?: boolean; shape?: string; points?: WbPoint[]; pathD?: string;
|
|
33
33
|
startAnchor?: string | null; endAnchor?: string | null; strokeWidth?: number; rotation?: number;
|
|
34
34
|
zIndex?: number; dataUrl?: string; dataUrlBytes?: number; url?: string; sectionType?: string; label?: string;
|
|
@@ -145,8 +145,91 @@ function contentString(v: unknown): string | undefined {
|
|
|
145
145
|
return v == null ? undefined : String(v);
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
// ── List nesting ──
|
|
149
|
+
// A list stores `items: string[]` and, only while some item is nested, `levels: number[]` beside them (levels[i]
|
|
150
|
+
// is the depth of items[i], 0 = top level; no `levels` = all 0, so older boards read unchanged). Callers may send
|
|
151
|
+
// items as { text, level } objects. Rules: the first item is level 0, an item is at most one level deeper than
|
|
152
|
+
// the item above it, no item is deeper than LIST_MAX_LEVEL. The browser and server.js read the same format
|
|
153
|
+
// through neural-interface/public/shared/whiteboard-list.js; keep the two on the same rules.
|
|
154
|
+
|
|
155
|
+
/** Deepest list level (the client's WB_LIST_MAX_LEVEL). */
|
|
156
|
+
export const LIST_MAX_LEVEL = 4;
|
|
157
|
+
|
|
158
|
+
/** One list item as a caller may send it: a plain string (level 0) or { text, level }. */
|
|
159
|
+
export type WbListItemInput = string | { text: string; level?: number };
|
|
160
|
+
|
|
161
|
+
function listItemText(item: unknown): string {
|
|
162
|
+
if (typeof item === 'string') return item;
|
|
163
|
+
if (item && typeof item === 'object') {
|
|
164
|
+
const t = (item as { text?: unknown }).text;
|
|
165
|
+
return t == null ? '' : String(t);
|
|
166
|
+
}
|
|
167
|
+
return item == null ? '' : String(item);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function listLevelOf(value: unknown): number {
|
|
171
|
+
const n = Math.floor(Number(value));
|
|
172
|
+
return Number.isFinite(n) && n > 0 ? Math.min(n, LIST_MAX_LEVEL) : 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A list's items in either form -> `{ items, levels }` that keep the nesting rules (levels as long as items).
|
|
177
|
+
* A plain string takes `levels[i]` (0 without one); a { text, level } object's own level wins.
|
|
178
|
+
*/
|
|
179
|
+
export function normalizeListItems(items: unknown, levels?: unknown): { items: string[]; levels: number[] } {
|
|
180
|
+
const list = Array.isArray(items) ? items : [];
|
|
181
|
+
const given = Array.isArray(levels) ? levels : [];
|
|
182
|
+
const out: number[] = [];
|
|
183
|
+
let above = -1;
|
|
184
|
+
list.forEach((item, i) => {
|
|
185
|
+
const own = item && typeof item === 'object' ? (item as { level?: unknown }).level : undefined;
|
|
186
|
+
const level = Math.min(listLevelOf(own !== undefined ? own : given[i]), above + 1);
|
|
187
|
+
out.push(level);
|
|
188
|
+
above = level;
|
|
189
|
+
});
|
|
190
|
+
return { items: list.map(listItemText), levels: out };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** True when some item of the list is nested (only then is `levels` stored). */
|
|
194
|
+
export function hasNestedLevels(levels: readonly number[] | undefined): boolean {
|
|
195
|
+
return Array.isArray(levels) && levels.some(level => level > 0);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The marker before each list item: a bullet per level (disc, circle, square, cycling) or the item's number in
|
|
200
|
+
* its level's style (1, a, i, cycling) with ".". Numbering restarts under every item above, as in the browser.
|
|
201
|
+
*/
|
|
202
|
+
export function listMarkers(levels: readonly number[], ordered: boolean): string[] {
|
|
203
|
+
const bullets = ['•', '◦', '▪'];
|
|
204
|
+
const counters: number[] = [];
|
|
205
|
+
return levels.map((raw) => {
|
|
206
|
+
const level = listLevelOf(raw);
|
|
207
|
+
if (!ordered) return bullets[level % 3];
|
|
208
|
+
counters.length = level + 1;
|
|
209
|
+
counters[level] = (counters[level] || 0) + 1;
|
|
210
|
+
const n = counters[level];
|
|
211
|
+
const style = level % 3;
|
|
212
|
+
return `${style === 1 ? alphaCounter(n) : style === 2 ? romanCounter(n) : String(n)}.`;
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function alphaCounter(n: number): string {
|
|
217
|
+
let out = '';
|
|
218
|
+
while (n > 0) { n -= 1; out = String.fromCharCode(97 + (n % 26)) + out; n = Math.floor(n / 26); }
|
|
219
|
+
return out;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function romanCounter(n: number): string {
|
|
223
|
+
const table: [number, string][] = [[1000, 'm'], [900, 'cm'], [500, 'd'], [400, 'cd'], [100, 'c'], [90, 'xc'], [50, 'l'], [40, 'xl'], [10, 'x'], [9, 'ix'], [5, 'v'], [4, 'iv'], [1, 'i']];
|
|
224
|
+
let out = '';
|
|
225
|
+
for (const [value, digits] of table) while (n >= value) { out += digits; n -= value; }
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Size estimate of a list element from its stored items and levels. */
|
|
230
|
+
function estimateListElement(el: WbElement): { width: number; height: number } {
|
|
231
|
+
const list = normalizeListItems(el.items, el.levels);
|
|
232
|
+
return estimateListSize(Array.isArray(el.items) ? list.items : undefined, positiveDim(el.fontSize) ?? 18, list.levels);
|
|
150
233
|
}
|
|
151
234
|
|
|
152
235
|
/** Stored size, or the type's fallback when a dimension is missing (see `elementBox`). */
|
|
@@ -160,7 +243,7 @@ function storedOrDefaultSize(el: WbElement): { width: number; height: number } {
|
|
|
160
243
|
return { width: w ?? est.width, height: h ?? est.height };
|
|
161
244
|
}
|
|
162
245
|
case 'list': {
|
|
163
|
-
const est =
|
|
246
|
+
const est = estimateListElement(el);
|
|
164
247
|
return { width: w ?? est.width, height: h ?? est.height };
|
|
165
248
|
}
|
|
166
249
|
case 'shape':
|
|
@@ -240,14 +323,16 @@ export function estimateTextSize(content: string | undefined, fontSize = 22): {
|
|
|
240
323
|
|
|
241
324
|
/**
|
|
242
325
|
* Estimate the rendered size of a list element before the browser measures it.
|
|
243
|
-
* Same font metrics as text, plus the 1.4em bullet indent
|
|
326
|
+
* Same font metrics as text, plus the 1.4em bullet indent and 1.4em more per
|
|
327
|
+
* nesting level (`levels[i]`, 0 when missing); line-height 1.5.
|
|
244
328
|
*/
|
|
245
|
-
export function estimateListSize(items: string[] | undefined, fontSize = 18): { width: number; height: number } {
|
|
329
|
+
export function estimateListSize(items: string[] | undefined, fontSize = 18, levels?: readonly number[]): { width: number; height: number } {
|
|
246
330
|
const list = items ?? [];
|
|
247
331
|
const rows = Math.max(1, list.length);
|
|
248
|
-
const
|
|
332
|
+
const indents = list.map((item, i) => codePointLength(item) * 0.6 + (1 + listLevelOf(levels?.[i])) * 1.4);
|
|
333
|
+
const widest = list.length ? Math.max(...indents) : 1.4;
|
|
249
334
|
return {
|
|
250
|
-
width: Math.max(120, Math.round(
|
|
335
|
+
width: Math.max(120, Math.round(widest * fontSize + 30)),
|
|
251
336
|
height: Math.max(30, Math.round(rows * fontSize * 1.5 + 22)),
|
|
252
337
|
};
|
|
253
338
|
}
|
|
@@ -277,7 +362,7 @@ export function ensureElementGeometry(el: WbElement, opts: { force?: boolean } =
|
|
|
277
362
|
if (force || el.measured !== true || missing) {
|
|
278
363
|
const size = el.type === 'text'
|
|
279
364
|
? estimateTextSize(contentString(el.content), positiveDim(el.fontSize) ?? 22)
|
|
280
|
-
:
|
|
365
|
+
: estimateListElement(el);
|
|
281
366
|
el.width = size.width;
|
|
282
367
|
el.height = size.height;
|
|
283
368
|
el.estimated = true;
|