synabun 2.1.0 → 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 +121 -0
- package/README.md +1 -1
- package/docs/whatsapp.md +11 -11
- package/lib/client-config-repair.js +13 -9
- package/lib/external-tools.js +6 -5
- package/lib/update-snapshot.js +77 -3
- package/lib/virustotal.mjs +12 -1
- package/mcp-server/dist/index.js +1 -1
- package/mcp-server/dist/index.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/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- 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 +2 -1
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +2 -1
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.js +1 -1
- package/mcp-server/dist/services/tool-catalog.js.map +1 -1
- package/mcp-server/dist/services/tool-list.d.ts +1 -1
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.d.ts +2 -2
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -1
- package/mcp-server/dist/services/tool-runtime.js +1 -1
- package/mcp-server/dist/services/tool-runtime.js.map +1 -1
- 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/tools/agents.d.ts +8 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +10 -9
- package/mcp-server/dist/tools/agents.js.map +1 -1
- 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/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 +8 -8
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/skill-tools.js +11 -8
- package/mcp-server/dist/tools/skill-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/index.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +2 -1
- package/mcp-server/src/services/server-instructions.ts +2 -1
- package/mcp-server/src/services/tool-catalog.ts +1 -1
- package/mcp-server/src/services/tool-list.ts +1 -1
- package/mcp-server/src/services/tool-runtime.ts +2 -2
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/tools/agents.ts +10 -9
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +11 -8
- package/neural-interface/i18n/en.json +501 -39
- package/neural-interface/i18n/pt-BR.json +501 -39
- 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 +1 -0
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- 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 +76 -9
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +207 -49
- package/neural-interface/lib/assistant-envelope.js +153 -1
- 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 +16 -11
- package/neural-interface/lib/assistant-plan-permissions.js +15 -0
- package/neural-interface/lib/assistant-router.js +237 -35
- package/neural-interface/lib/assistant-runtime.js +158 -12
- package/neural-interface/lib/assistant-task-prompt.js +10 -8
- package/neural-interface/lib/assistant-tools.js +2 -2
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +6 -1
- package/neural-interface/lib/browser-guard-cdp.js +5 -4
- package/neural-interface/lib/browser-network-guard.js +274 -82
- package/neural-interface/lib/browser-tool-policy.js +12 -7
- package/neural-interface/lib/claude-agent-bridge.js +7 -0
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +160 -31
- package/neural-interface/lib/cli-resolver.js +9 -2
- package/neural-interface/lib/cli-update-planner.js +25 -15
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +25 -6
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +9 -2
- 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/mcp-client-config.js +37 -6
- package/neural-interface/lib/mcp-external-servers.js +13 -4
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +147 -25
- package/neural-interface/lib/native-loop-providers.js +420 -21
- package/neural-interface/lib/native-loop-runtime.js +23 -2
- package/neural-interface/lib/opencode-native-boundary.js +11 -2
- package/neural-interface/lib/owner-authority.js +1 -1
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +32 -2
- 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 +3 -0
- package/neural-interface/lib/rulesets/api.js +6 -5
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +89 -10
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +64 -1
- package/neural-interface/lib/skills/catalog.js +5 -1
- package/neural-interface/lib/skills/creation-brief.js +1 -1
- package/neural-interface/lib/skills/creation-sources.js +19 -1
- package/neural-interface/lib/skills/deploy.js +9 -4
- package/neural-interface/lib/skills/draft-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-confinement.js +3 -1
- package/neural-interface/lib/skills/eval-trace.js +16 -0
- package/neural-interface/lib/skills/export.js +5 -2
- package/neural-interface/lib/skills/known-bridge-digests.json +58 -4
- package/neural-interface/lib/skills/ownership.js +1 -1
- package/neural-interface/lib/skills/roots.js +10 -3
- package/neural-interface/lib/skills/runtime-checks.js +6 -3
- package/neural-interface/lib/skills/service.js +15 -7
- package/neural-interface/lib/skills/telemetry.js +6 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/whatsapp/brain.js +7 -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 +77 -1
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-resume.js +13 -4
- package/neural-interface/lib/worker-sandbox.js +46 -16
- package/neural-interface/package-lock.json +18 -2
- package/neural-interface/package.json +2 -1
- package/neural-interface/public/onboarding.html +1 -1
- package/neural-interface/public/shared/agent-runtime-options.js +89 -11
- 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 +4 -4
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +3 -4
- package/neural-interface/public/shared/assistant/asst-api.js +1 -1
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +56 -9
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-control.js +48 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +6 -1
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +38 -5
- package/neural-interface/public/shared/assistant/asst-panel.js +45 -8
- package/neural-interface/public/shared/assistant/asst-render.js +51 -12
- package/neural-interface/public/shared/assistant/asst-route.js +237 -57
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scenes.js +54 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +51 -7
- package/neural-interface/public/shared/assistant/asst-slash.js +8 -2
- package/neural-interface/public/shared/assistant/asst-state.js +76 -9
- package/neural-interface/public/shared/assistant/asst-styles.js +34 -0
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +34 -7
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +26 -3
- package/neural-interface/public/shared/cdx/cdx-panel.js +14 -2
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +11 -10
- package/neural-interface/public/shared/cdx/cdx-render.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-requests.js +11 -1
- package/neural-interface/public/shared/cdx/cdx-styles.js +4 -0
- package/neural-interface/public/shared/cdx/cdx-tabs.js +75 -5
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +7 -4
- package/neural-interface/public/shared/cli-status.js +9 -8
- package/neural-interface/public/shared/constants.js +3 -2
- package/neural-interface/public/shared/context-menu-model.js +1 -0
- 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/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 +1 -0
- 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 +21 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +27 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +7 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +46 -3
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +1 -1
- package/neural-interface/public/shared/settings/settings-skill-folders.js +2 -2
- 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-creation.js +6 -3
- package/neural-interface/public/shared/skills/ss-dialog.js +5 -2
- package/neural-interface/public/shared/skills/ss-lint.js +9 -1
- package/neural-interface/public/shared/skills/ss-profiles.js +44 -6
- package/neural-interface/public/shared/skills/ss-review-view.js +2 -2
- package/neural-interface/public/shared/skills/studio/sx-actions.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +2 -1
- package/neural-interface/public/shared/skills/studio/sx-dom.js +2 -1
- 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-inspector.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +9 -6
- package/neural-interface/public/shared/skills/studio/sx-install.js +20 -3
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +144 -35
- package/neural-interface/public/shared/skills/studio/sx-library.js +6 -2
- package/neural-interface/public/shared/skills/studio/sx-main.js +11 -2
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +1 -1
- package/neural-interface/public/shared/skills/studio/sx-subject.js +7 -6
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +6 -5
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styles.css +354 -34
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +2 -2
- package/neural-interface/public/shared/tool-list-view.js +2 -2
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -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 +20 -12
- package/neural-interface/public/shared/ui-claude-panel.js +299 -77
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- 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-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +8 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-menubar.js +2 -2
- package/neural-interface/public/shared/ui-native-loop-router.js +4 -0
- package/neural-interface/public/shared/ui-navbar.js +45 -0
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +41 -10
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +22 -12
- package/neural-interface/public/shared/ui-settings.js +90 -65
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -3
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +3 -3
- package/neural-interface/public/shared/ui-terminal.js +146 -10
- package/neural-interface/public/shared/ui-tool-updates.js +3 -3
- package/neural-interface/public/shared/ui-tooltip.js +71 -46
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-whiteboard.js +1 -1
- package/neural-interface/public/shared/ui-worker-end.js +1 -1
- package/neural-interface/public/shared/whatsapp/wa-view.js +2 -2
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/sidepanel-session.html +1 -1
- package/neural-interface/server.js +644 -426
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +5 -5
- package/neural-interface/templates/rulesets/manifest.json +1 -1
- package/npm-shrinkwrap.json +18 -2
- package/package.json +2 -1
- package/skills/skill-forge/SKILL.md +4 -4
- package/skills/synabun/SKILL.md +1 -1
- package/skills/synabun/modules/schedule.md +3 -3
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
import { clampEffortId, getEffortLevelsForModel, modelEffortIds, fetchClaudeAccounts, fetchCodexAccounts, getCachedClaudeAccounts, getCachedCodexAccounts, getClaudeWorkerAccountId, getCodexWorkerAccountId } from '../agent-runtime-options.js';
|
|
4
4
|
import { accountSummary, accountName, accountBadge, accountIdentity } from '../account-view.js';
|
|
5
5
|
import { accountUsageText, autoAccountLabel } from '../account-usage.js';
|
|
6
|
-
import { getProviderMeta } from '../provider-icons.js';
|
|
7
6
|
import { getRouting, putRouting } from './asst-api.js';
|
|
8
7
|
import { catalogModelRow, hiddenModelFor, ICON_CARET, openModelMenu } from './asst-brain-picker.js';
|
|
9
8
|
import { closeMenu, currentMenu } from './asst-menu.js';
|
|
10
9
|
import { loadRouteModels, routeModelLabel, routeModelsStatus, watchRouteModels } from './asst-route-models.js';
|
|
11
10
|
import { designRouteSight, openModelsManager, routeCatalogRow, routeModeLabel } from './asst-route.js';
|
|
12
|
-
import { DEFAULT_ROUTE_MODE, normalizeRouteMode, ROUTE_MODES } from './asst-state.js';
|
|
11
|
+
import { DEFAULT_ROUTE_MODE, normalizeRouteMode, PROVIDERS, providerMeta, ROUTE_MODES } from './asst-state.js';
|
|
13
12
|
|
|
14
13
|
const ICON_ROUTE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h15l-4-4M20 17H5l4 4"/></svg>';
|
|
15
14
|
const ICON_X = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"><path d="m6 6 12 12M18 6 6 18"/></svg>';
|
|
@@ -37,7 +36,8 @@ function choiceKey(p) { return p ? JSON.stringify([p.kind || 'direct', p.provide
|
|
|
37
36
|
export function routeAccountView(pref, task, brain, accounts, workers = {}, t = null) {
|
|
38
37
|
const tr = translator(t);
|
|
39
38
|
const empty = { label: tr('assistant.routes.editorUnusedValue', '—'), readOnly: true, options: [], hint: '' };
|
|
40
|
-
|
|
39
|
+
// Only Claude Code and Codex run under an account (OpenCode and Antigravity sign in inside their own CLIs).
|
|
40
|
+
if (!pref || pref.provider === 'opencode' || pref.provider === 'antigravity') return empty;
|
|
41
41
|
const direct = task.id === 'chat' || routeExecutionKind(pref, task, brain) === 'direct';
|
|
42
42
|
const provider = task.id === 'chat' ? brain.provider : pref.provider;
|
|
43
43
|
if (!['claude-code', 'codex'].includes(provider)) return empty;
|
|
@@ -381,7 +381,8 @@ export async function openRoutesEditor(hooks = {}) {
|
|
|
381
381
|
state.menuFocus = modelBtn.dataset.focusKey;
|
|
382
382
|
const emptyLabel = task.requires ? tr('assistant.routes.noCapableModel', 'No model in your catalog can make {medium} yet.', { medium: tr(`assistant.routes.medium.${task.requires === 'video' ? 'videos' : 'images'}`, task.requires) }) : tr('assistant.routes.noSeeingModel', 'No model in your catalog can see images yet.');
|
|
383
383
|
const menu = await openModelMenu(modelBtn, {
|
|
384
|
-
|
|
384
|
+
// Every provider a route can name (Antigravity included).
|
|
385
|
+
brain: brain(), t: hooks.t, includeHere: true, hereLabel: tr('assistant.routes.brainDecides', 'Assistant chooses'), title: task.label, providers: PROVIDERS,
|
|
385
386
|
current: pref ? { provider: pref.provider, model: pref.model } : null, onToast: hooks.onToast,
|
|
386
387
|
routingEditor: true, onManageModels: () => manageModels(modelBtn.dataset.focusKey),
|
|
387
388
|
...(task.requires ? { accept: (_p, m) => m.outputs?.[task.requires] === true, allowCustom: false, emptyLabel } : {}),
|
|
@@ -400,7 +401,7 @@ export async function openRoutesEditor(hooks = {}) {
|
|
|
400
401
|
// Keys are stable across translations; accessible names use the translated field.
|
|
401
402
|
modelBtn.dataset.focusKey = `${task.id}-model`; caret(modelBtn); modelBtn.setAttribute('aria-haspopup', 'dialog');
|
|
402
403
|
modelBtn.title = label;
|
|
403
|
-
if (pref?.provider) { const mark = el('span', 'asst-icon'); mark.setAttribute('aria-hidden', 'true'); mark.innerHTML =
|
|
404
|
+
if (pref?.provider) { const mark = el('span', 'asst-icon'); mark.setAttribute('aria-hidden', 'true'); mark.innerHTML = providerMeta(pref.provider).icon; mark.style.color = providerMeta(pref.provider).color; modelBtn.prepend(mark); }
|
|
404
405
|
modelCell.append(modelBtn);
|
|
405
406
|
const source = el('span', 'asst-routes-source');
|
|
406
407
|
let sourceText = pref ? tr('assistant.routes.source.yours', 'Your choice') : tr('assistant.routes.source.default', 'Assistant chooses');
|
|
@@ -433,14 +434,14 @@ export async function openRoutesEditor(hooks = {}) {
|
|
|
433
434
|
if (effortValue !== unusedValue && effortValue !== effortText) effortBtn.setAttribute('aria-label', tr('assistant.routes.fieldLabel', '{task}: {field}, {value}', { task: task.label, field: tr('assistant.routes.columns.effort', 'Effort'), value: `${effortValue} · ${effortText}` }));
|
|
434
435
|
const effortCell = el('div', 'asst-routes-cell asst-routes-effort-cell'); effortCell.append(el('span', 'asst-routes-cell-label', tr('assistant.routes.columns.effort', 'Effort')), effortBtn); node.append(effortCell);
|
|
435
436
|
const kind = routeExecutionKind(pref, task, brain());
|
|
436
|
-
const runLabel = task.id === 'chat' ? tr('assistant.routes.inConversation', 'In conversation') : !pref ? task.dispatchOnly ? tr('assistant.routes.workerChooses', 'Worker · assistant chooses') : tr('assistant.routes.brainDecides', 'Assistant chooses') : kind === 'direct' ? tr('assistant.routes.inConversation', 'In conversation') : tr('assistant.routes.onWorker', '{provider} worker', { provider:
|
|
437
|
+
const runLabel = task.id === 'chat' ? tr('assistant.routes.inConversation', 'In conversation') : !pref ? task.dispatchOnly ? tr('assistant.routes.workerChooses', 'Worker · assistant chooses') : tr('assistant.routes.brainDecides', 'Assistant chooses') : kind === 'direct' ? tr('assistant.routes.inConversation', 'In conversation') : tr('assistant.routes.onWorker', '{provider} worker', { provider: providerMeta(pref.provider).label });
|
|
437
438
|
const run = fieldButton('asst-routes-run', task, tr('assistant.routes.columns.run', 'Runs in'), runLabel, () => openMenuFor(run, tr('assistant.routes.columns.run', 'Runs in'), [
|
|
438
439
|
{ kind: 'radio', id: 'direct', label: tr('assistant.routes.inConversation', 'In conversation'), desc: tr('assistant.routes.conversationDesc', 'Continue this conversation on the chosen model for this task.'), selected: kind === 'direct', onSelect: () => setChoice(task.id, { ...pref, kind: 'direct' }, run.dataset.focusKey) },
|
|
439
|
-
{ kind: 'radio', id: 'dispatch', label: tr('assistant.routes.onWorker', '{provider} worker', { provider:
|
|
440
|
+
{ kind: 'radio', id: 'dispatch', label: tr('assistant.routes.onWorker', '{provider} worker', { provider: providerMeta(pref?.provider).label }), desc: tr('assistant.routes.workerDesc', 'Hand the task to a worker and show its result here.'), selected: kind === 'dispatch', onSelect: () => setChoice(task.id, { ...pref, kind: 'dispatch' }, run.dataset.focusKey) },
|
|
440
441
|
]));
|
|
441
442
|
run.dataset.focusKey = `${task.id}-run`; run.disabled = !pref || task.dispatchOnly || pref.provider !== brain().provider || task.id === 'chat';
|
|
442
443
|
const inConversation = task.id === 'chat' || (pref && kind === 'direct');
|
|
443
|
-
const runValue = inConversation ? tr('assistant.routes.editorInConversation', 'This conversation') : pref ?
|
|
444
|
+
const runValue = inConversation ? tr('assistant.routes.editorInConversation', 'This conversation') : pref ? providerMeta(pref.provider).label : unusedValue;
|
|
444
445
|
run.querySelector('.asst-dd-label').textContent = runValue;
|
|
445
446
|
if (inConversation) run.setAttribute('aria-label', tr('assistant.routes.fieldLabel', '{task}: {field}, {value}', { task: task.label, field: tr('assistant.routes.columns.run', 'Runs in'), value: runValue }));
|
|
446
447
|
if (!run.disabled) caret(run);
|
|
@@ -116,6 +116,8 @@ export function runChip(run, t = tFallback) {
|
|
|
116
116
|
}
|
|
117
117
|
if (awaitsAnswer(run)) return { tone: 'action', label: t('assistant.run.waitingInput', 'waiting for your answer') };
|
|
118
118
|
const tone = runTone(run);
|
|
119
|
+
// A held run whose card the user answered "here": the assistant does it, this worker never started.
|
|
120
|
+
if (run?.completionReason === 'route_here') return { tone, label: t('assistant.run.routedHere', 'not started: done here instead') };
|
|
119
121
|
if (run?.turnState === 'awaiting_permission') return { tone, label: t('assistant.run.needsApproval', 'needs approval') };
|
|
120
122
|
if (run?.turnState === 'needs_input') return { tone, label: t('assistant.run.needsInput', 'needs input') };
|
|
121
123
|
// A Claude Code worker a server restart cut off, until the next start has resumed it (lib/assistant-dispatch.js).
|
|
@@ -169,6 +171,46 @@ export const CODEX_ITEM_EVENTS = Object.freeze({
|
|
|
169
171
|
*/
|
|
170
172
|
export const OPENCODE_TOOL_STATES = Object.freeze(['running', 'completed', 'error']);
|
|
171
173
|
|
|
174
|
+
// The path-like fields of an Antigravity tool call's arguments (lib/worker-confinement.js reads the same ones).
|
|
175
|
+
const ACP_PATH_KEYS = ['path', 'file_path', 'filePath', 'Path', 'AbsolutePath', 'TargetFile', 'DirectoryPath', 'SearchPath'];
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* An Antigravity (ACP) tool call as an activity: its `kind` says what it does (read, edit, delete,
|
|
179
|
+
* move, search, execute, think, fetch, switch_mode, other), its `rawInput` holds the tool's own
|
|
180
|
+
* arguments and its `locations` the files it touches. Another tool (an MCP server's) is read by its
|
|
181
|
+
* name, as for the other providers. null for a mode switch.
|
|
182
|
+
*/
|
|
183
|
+
function acpToolActivity(call) {
|
|
184
|
+
const args = obj(call.rawInput);
|
|
185
|
+
const kind = str(call.kind) || 'other';
|
|
186
|
+
const title = str(call.title).trim();
|
|
187
|
+
const paths = [...new Set([
|
|
188
|
+
...(Array.isArray(call.locations) ? call.locations : []).map((location) => str(location?.path)),
|
|
189
|
+
...ACP_PATH_KEYS.map((key) => (typeof args[key] === 'string' ? args[key] : '')),
|
|
190
|
+
].filter(Boolean))];
|
|
191
|
+
const pick = (...values) => values.map((v) => (typeof v === 'string' ? v.trim() : '')).find(Boolean) || '';
|
|
192
|
+
switch (kind) {
|
|
193
|
+
case 'execute': {
|
|
194
|
+
const command = Array.isArray(args.command) ? args.command.map(str).join(' ') : pick(args.command, args.CommandLine, args.cmd);
|
|
195
|
+
return { kind: 'shell', name: 'exec_command', input: { command: command || title } };
|
|
196
|
+
}
|
|
197
|
+
case 'read': return { kind: 'read', name: 'read', input: { file_path: paths[0] || title } };
|
|
198
|
+
case 'edit': case 'delete': case 'move':
|
|
199
|
+
return { kind: 'edit', name: kind === 'edit' ? 'edit' : 'apply_patch', input: paths.length > 1 ? { changes: paths.map((path) => ({ path })) } : { file_path: paths[0] || title } };
|
|
200
|
+
case 'search': return { kind: 'search', name: 'grep', input: { pattern: pick(args.pattern, args.query, args.Query, args.Pattern) || title, path: paths[0] || '' } };
|
|
201
|
+
case 'fetch': {
|
|
202
|
+
const url = pick(args.url, args.Url, args.href);
|
|
203
|
+
return url ? { kind: 'web', name: 'WebFetch', input: { url } } : { kind: 'web', name: 'WebSearch', input: { query: pick(args.query, args.Query) || title } };
|
|
204
|
+
}
|
|
205
|
+
case 'think': return { kind: 'think', name: 'thinking', input: {} };
|
|
206
|
+
case 'switch_mode': return null;
|
|
207
|
+
default: {
|
|
208
|
+
const name = pick(call.name, args.toolName, title) || 'tool';
|
|
209
|
+
return { kind: kindOf(name, args), name, input: args };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
172
214
|
/**
|
|
173
215
|
* A worker's provider event ({ provider, eventType?, event }, the payload of
|
|
174
216
|
* sync:sidepanel:provider-event) as what it is doing: { kind, name, input }
|
|
@@ -177,7 +219,11 @@ export const OPENCODE_TOOL_STATES = Object.freeze(['running', 'completed', 'erro
|
|
|
177
219
|
* stage and the tray keep only compactActivity() of it. OpenCode names its
|
|
178
220
|
* tools bash, read, edit, write, patch, glob, grep, list, webfetch,
|
|
179
221
|
* websearch, codesearch, task, todowrite, and MCP tools `<server>_<tool>`
|
|
180
|
-
* (SynaBun_browser_navigate): kindOf() reads all of them.
|
|
222
|
+
* (SynaBun_browser_navigate): kindOf() reads all of them. An Antigravity
|
|
223
|
+
* worker's events are ACP session updates (eventType = their `sessionUpdate`):
|
|
224
|
+
* a tool call when it is announced (`tool_call`, or an update that starts it
|
|
225
|
+
* and names its kind), a thought chunk as thinking, a plan as planning; its
|
|
226
|
+
* message text is no activity, as for the others.
|
|
181
227
|
*/
|
|
182
228
|
export function activityOf(payload) {
|
|
183
229
|
const provider = str(payload?.provider);
|
|
@@ -222,6 +268,13 @@ export function activityOf(payload) {
|
|
|
222
268
|
const input = obj(part.state?.input);
|
|
223
269
|
return { kind: kindOf(name, input), name, input };
|
|
224
270
|
}
|
|
271
|
+
if (provider === 'antigravity') {
|
|
272
|
+
const type = str(payload.eventType || event.sessionUpdate);
|
|
273
|
+
if (type === 'agent_thought_chunk') return { kind: 'think', name: 'thinking', input: {} };
|
|
274
|
+
if (type === 'plan') return { kind: 'plan', name: 'update_plan', input: { todos: Array.isArray(event.entries) ? event.entries : [] } };
|
|
275
|
+
if (type === 'tool_call' || (type === 'tool_call_update' && str(event.kind) && event.status === 'in_progress')) return acpToolActivity(event);
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
225
278
|
return null;
|
|
226
279
|
}
|
|
227
280
|
|
|
@@ -22,7 +22,7 @@ import { getProviderMeta } from '../provider-icons.js';
|
|
|
22
22
|
import { showGuestToast } from '../ui-sync.js';
|
|
23
23
|
import { registerSidepanel, setSidepanelVisible, syncSidepanelLayout, focusSidepanel } from '../ui-sidepanel-windows.js';
|
|
24
24
|
import {
|
|
25
|
-
assistantBlocked, assistantOwnerOf,
|
|
25
|
+
assistantBlocked, assistantOwnerOf, noteAssistantFocus, openAssistant, readLiveAssistantSessions, registerAssistantHost,
|
|
26
26
|
} from '../ui-assistant.js';
|
|
27
27
|
import { mountAssistant } from './asst-panel.js';
|
|
28
28
|
import { openModelsManager } from './asst-route.js';
|
|
@@ -57,6 +57,11 @@ let _renaming = false;
|
|
|
57
57
|
const _tabs = []; // { sessionId, viewport, toolbar, ctl, pill, label, userRenamed, status, brain }
|
|
58
58
|
let _activeIdx = -1;
|
|
59
59
|
let _tourPresenting = false;
|
|
60
|
+
// The saved tabs ({ activeIdx, tabs }) while the session list could not be read (a server that is
|
|
61
|
+
// starting answers before its Assistant routes exist): they stay saved and are asked about again.
|
|
62
|
+
let _unrestored = null;
|
|
63
|
+
let _restoring = null; // that restore under way: a session start waits for it
|
|
64
|
+
const RESTORE_RETRY_MS = [500, 1000, 2000, 4000, 8000, 15_000];
|
|
60
65
|
|
|
61
66
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
62
67
|
|
|
@@ -94,11 +99,13 @@ function conversationOf(tab) {
|
|
|
94
99
|
|
|
95
100
|
function saveTabs() {
|
|
96
101
|
try {
|
|
97
|
-
|
|
102
|
+
// Saved tabs not restored yet stay saved (their names with them), after the open ones.
|
|
103
|
+
const waiting = (_unrestored?.tabs || []).filter(entry => !findTab(entry.sessionId));
|
|
104
|
+
if (!_tabs.length) { if (!waiting.length) storage.removeItem(KEYS.ASSISTANT_PANEL_TABS); return; }
|
|
98
105
|
storage.setItem(KEYS.ASSISTANT_PANEL_TABS, JSON.stringify({
|
|
99
106
|
v: 1,
|
|
100
107
|
activeIdx: Math.max(0, _activeIdx),
|
|
101
|
-
tabs: _tabs.map(tab => ({ sessionId: tab.sessionId, label: tab.userRenamed ? tab.label : '', userRenamed: !!tab.userRenamed })),
|
|
108
|
+
tabs: [..._tabs.map(tab => ({ sessionId: tab.sessionId, label: tab.userRenamed ? tab.label : '', userRenamed: !!tab.userRenamed })), ...waiting],
|
|
102
109
|
}));
|
|
103
110
|
} catch { /* storage unavailable */ }
|
|
104
111
|
}
|
|
@@ -416,6 +423,16 @@ async function startSession(prompt = '') {
|
|
|
416
423
|
_startError = '';
|
|
417
424
|
renderPlaceholder('starting');
|
|
418
425
|
try {
|
|
426
|
+
// The saved tabs come back before a new session is made: the restore under way (the server is
|
|
427
|
+
// starting), or once more for the ones it could not ask about.
|
|
428
|
+
if (!_restoring && _unrestored) startRestore(_unrestored, []);
|
|
429
|
+
if (_restoring) await _restoring.catch(() => { /* a new session, then */ });
|
|
430
|
+
if (_tabs.length) {
|
|
431
|
+
const tab = activeTab() || _tabs[0];
|
|
432
|
+
switchTo(_tabs.indexOf(tab));
|
|
433
|
+
if (prompt) { tab.ctl?.insertText?.(prompt); tab.ctl?.focus?.(); } // a conversation's draft, never sent for them
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
419
436
|
const res = await openAssistant(prompt ? { host: HOST_ID, prompt } : { host: HOST_ID });
|
|
420
437
|
if (!_tabs.length) renderPlaceholder(res ? null : 'failed');
|
|
421
438
|
} finally {
|
|
@@ -635,7 +652,7 @@ export function beginAssistantTour({ onPreviewSend = () => {} } = {}) {
|
|
|
635
652
|
const wasVisible = _visible;
|
|
636
653
|
const previousPanel = state.lastActivePanel;
|
|
637
654
|
const previousSidepanel = state.lastActiveSidepanel;
|
|
638
|
-
const peers = ['#topright-claude-panel-btn', '#topright-codex-panel-btn', '#topright-opencode-panel-btn']
|
|
655
|
+
const peers = ['#topright-claude-panel-btn', '#topright-codex-panel-btn', '#topright-opencode-panel-btn', '#topright-antigravity-panel-btn']
|
|
639
656
|
.map(s => document.querySelector(s)).filter(b => b?.classList.contains('active'));
|
|
640
657
|
_tourPresenting = true;
|
|
641
658
|
const tab = activeTab();
|
|
@@ -731,7 +748,30 @@ export async function initAssistantPanel() {
|
|
|
731
748
|
|
|
732
749
|
const saved = readSavedTabs();
|
|
733
750
|
if (!saved?.tabs.length) return;
|
|
734
|
-
|
|
751
|
+
await startRestore(saved, RESTORE_RETRY_MS);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function startRestore(saved, retries) {
|
|
755
|
+
_restoring = restoreSavedTabs(saved, retries).finally(() => { _restoring = null; });
|
|
756
|
+
return _restoring;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* The saved tabs come back (hidden, as pills) for the sessions the server still has open. A list
|
|
761
|
+
* that cannot be read restores nothing and forgets nothing: the tabs stay saved, names included
|
|
762
|
+
* (saveTabs), the list is asked again after each wait of `retries`, then at the next session start.
|
|
763
|
+
*/
|
|
764
|
+
async function restoreSavedTabs(saved, retries) {
|
|
765
|
+
_unrestored = saved;
|
|
766
|
+
let live = await readLiveAssistantSessions();
|
|
767
|
+
for (const wait of retries) {
|
|
768
|
+
if (live || assistantBlocked()) break;
|
|
769
|
+
await new Promise(done => setTimeout(done, wait));
|
|
770
|
+
live = await readLiveAssistantSessions();
|
|
771
|
+
}
|
|
772
|
+
if (!live) return;
|
|
773
|
+
_unrestored = null;
|
|
774
|
+
const hadTabs = _tabs.length > 0; // one opened meanwhile stays the one showing
|
|
735
775
|
for (const entry of saved.tabs) {
|
|
736
776
|
const meta = live.get(entry.sessionId);
|
|
737
777
|
if (!meta) continue;
|
|
@@ -746,8 +786,12 @@ export async function initAssistantPanel() {
|
|
|
746
786
|
restoring: true,
|
|
747
787
|
});
|
|
748
788
|
}
|
|
749
|
-
if (_tabs.length) {
|
|
750
|
-
|
|
789
|
+
if (_tabs.length && !hadTabs) {
|
|
790
|
+
// The tab that was showing, by its session: a saved tab before it that did not come back (a draft,
|
|
791
|
+
// an ended session) must not hand the header to its neighbour.
|
|
792
|
+
const shownId = saved.tabs[saved.activeIdx]?.sessionId;
|
|
793
|
+
const shown = _tabs.findIndex(tab => tab.sessionId === shownId);
|
|
794
|
+
_activeIdx = shown >= 0 ? shown : Math.min(Math.max(0, saved.activeIdx), _tabs.length - 1);
|
|
751
795
|
markActive();
|
|
752
796
|
renderHeader();
|
|
753
797
|
renderPills();
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Pure module (no DOM, no i18n import) so node:test can exercise it. The
|
|
5
5
|
// composer resolves `descKey` through i18n and falls back to `desc`.
|
|
6
6
|
|
|
7
|
-
import { normalizeComputerMode, normalizeProvider, normalizeRouteMode } from './asst-state.js';
|
|
7
|
+
import { isBrainProvider, normalizeComputerMode, normalizeProvider, normalizeRouteMode } from './asst-state.js';
|
|
8
8
|
|
|
9
9
|
export const SLASH_COMMANDS = [
|
|
10
10
|
{ name: 'dispatch', usage: '/dispatch <provider> [--model m] [--effort e] [--agent a] [--project p] [--tools a,b] [--account id] [--ask] [--title "t"] <task>', descKey: 'assistant.slash.dispatch', desc: 'Dispatch a task to a sidepanel agent' },
|
|
@@ -143,6 +143,8 @@ function parseModel(rest) {
|
|
|
143
143
|
provider = normalizeProvider(first);
|
|
144
144
|
model = null;
|
|
145
145
|
}
|
|
146
|
+
// /model switches the brain: a provider that only runs dispatched work (none of the four today) is refused, not swapped.
|
|
147
|
+
if (provider && !isBrainProvider(provider)) return { name: 'model', provider, model, effort: tokens[1] || null, error: 'not_brain' };
|
|
146
148
|
return { name: 'model', provider, model, effort: tokens[1] || null };
|
|
147
149
|
}
|
|
148
150
|
|
|
@@ -182,7 +184,11 @@ export function parseSlashCommand(text) {
|
|
|
182
184
|
return enabled == null ? { name, mode: null, error: 'invalid_mode', value: restTrim } : { name, mode: enabled ? 'on' : 'off' };
|
|
183
185
|
}
|
|
184
186
|
case 'stop': return { name, target: restTrim && !/^all$/i.test(restTrim) ? restTrim : null, all: /^all$/i.test(restTrim) };
|
|
185
|
-
case 'new':
|
|
187
|
+
case 'new': {
|
|
188
|
+
const provider = restTrim ? normalizeProvider(restTrim) : null;
|
|
189
|
+
// A new session's brain: a provider that only runs dispatched work cannot be one (Antigravity can, since it runs the brain too).
|
|
190
|
+
return provider && !isBrainProvider(provider) ? { name, provider, error: 'not_brain' } : { name, provider };
|
|
191
|
+
}
|
|
186
192
|
case 'resume': return { name, sessionId: restTrim || null };
|
|
187
193
|
case 'compact':
|
|
188
194
|
case 'clear':
|
|
@@ -8,9 +8,23 @@
|
|
|
8
8
|
import { KEYS } from '../constants.js';
|
|
9
9
|
import { isNewerRunDescriptor } from '../ui-native-loop-router-state.js';
|
|
10
10
|
import { modelEffortIds } from '../agent-runtime-options.js';
|
|
11
|
+
import { getProviderMeta } from '../provider-icons.js';
|
|
11
12
|
import { usagePacketIsStale } from './asst-usage.js';
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
// The providers the Assistant itself can run on (its brain): the brain picker, /model and /new.
|
|
15
|
+
// Antigravity runs over ACP (lib/assistant-brains/antigravity.js), like a worker.
|
|
16
|
+
export const BRAIN_PROVIDERS = ['claude-code', 'codex', 'opencode', 'antigravity'];
|
|
17
|
+
// Providers that only ever run dispatched work. None any more (Antigravity became a brain too); kept so a module
|
|
18
|
+
// or a test that still imports the list reads it as empty.
|
|
19
|
+
export const WORKER_ONLY_PROVIDERS = [];
|
|
20
|
+
// Every provider the Assistant lists models for and can route a task to.
|
|
21
|
+
export const PROVIDERS = [...BRAIN_PROVIDERS, ...WORKER_ONLY_PROVIDERS];
|
|
22
|
+
// Providers that report tokens and no prices (Antigravity bills through its Google sign-in): a budget gives them no
|
|
23
|
+
// share, so a dry share never names them (asst-route.js), and their rows read "unpriced".
|
|
24
|
+
export const UNPRICED_PROVIDERS = ['antigravity'];
|
|
25
|
+
// Providers an older server may not list in its catalog yet: a model section without rows and without an archive
|
|
26
|
+
// for them stays hidden rather than showing an empty provider.
|
|
27
|
+
export const OPTIONAL_PROVIDERS = ['antigravity'];
|
|
14
28
|
|
|
15
29
|
export const PROVIDER_ALIASES = {
|
|
16
30
|
'claude-code': 'claude-code',
|
|
@@ -22,24 +36,38 @@ export const PROVIDER_ALIASES = {
|
|
|
22
36
|
openai: 'codex',
|
|
23
37
|
opencode: 'opencode',
|
|
24
38
|
oc: 'opencode',
|
|
39
|
+
antigravity: 'antigravity',
|
|
40
|
+
agy: 'antigravity',
|
|
41
|
+
'google antigravity': 'antigravity',
|
|
42
|
+
// Antigravity's CLI took the place of Gemini CLI: an old name or saved route means it.
|
|
43
|
+
gemini: 'antigravity',
|
|
25
44
|
};
|
|
26
45
|
|
|
27
46
|
export const PROVIDER_SHORT_LABELS = {
|
|
28
47
|
'claude-code': 'Claude',
|
|
29
48
|
codex: 'Codex',
|
|
30
49
|
opencode: 'OpenCode',
|
|
50
|
+
antigravity: 'Antigravity',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const PROVIDER_LABELS = {
|
|
54
|
+
'claude-code': 'Claude Code',
|
|
55
|
+
codex: 'Codex',
|
|
56
|
+
opencode: 'OpenCode',
|
|
57
|
+
antigravity: 'Antigravity',
|
|
31
58
|
};
|
|
32
59
|
|
|
33
60
|
// Providers whose brain carries an account profile picker.
|
|
34
61
|
export const ACCOUNT_PROVIDERS = new Set(['claude-code', 'codex']);
|
|
35
62
|
|
|
36
63
|
// Approval modes are sent as `permissionMode` (`set_permission_mode {mode}` /
|
|
37
|
-
// `query.permissionMode`). Claude uses the SDK enum; Codex/OpenCode
|
|
38
|
-
// distinguish "ask me" from "auto-approve". Plan mode is a separate
|
|
39
|
-
// (`planMode`) that combines with any approval mode, each provider in its
|
|
40
|
-
// way: Claude runs the SDK 'plan' mode and returns to the approval mode once
|
|
64
|
+
// `query.permissionMode`). Claude uses the SDK enum; Codex/OpenCode/Antigravity
|
|
65
|
+
// brains only distinguish "ask me" from "auto-approve". Plan mode is a separate
|
|
66
|
+
// flag (`planMode`) that combines with any approval mode, each provider in its
|
|
67
|
+
// own way: Claude runs the SDK 'plan' mode and returns to the approval mode once
|
|
41
68
|
// the plan is approved; Codex runs its native plan collaboration mode (read-only
|
|
42
|
-
// sandbox); OpenCode runs its own `plan` agent
|
|
69
|
+
// sandbox); OpenCode runs its own `plan` agent; Antigravity refuses code changes
|
|
70
|
+
// through SynaBun's hook and proposes the plan in a <proposed_plan> block.
|
|
43
71
|
export const PERMISSION_MODES = {
|
|
44
72
|
'claude-code': [
|
|
45
73
|
{ id: 'default', label: 'Default' },
|
|
@@ -54,9 +82,13 @@ export const PERMISSION_MODES = {
|
|
|
54
82
|
{ id: 'default', label: 'Default' },
|
|
55
83
|
{ id: 'auto', label: 'Auto-accept' },
|
|
56
84
|
],
|
|
85
|
+
antigravity: [
|
|
86
|
+
{ id: 'default', label: 'Default' },
|
|
87
|
+
{ id: 'auto', label: 'Auto-accept' },
|
|
88
|
+
],
|
|
57
89
|
};
|
|
58
90
|
|
|
59
|
-
export const PLAN_MODE_PROVIDERS = new Set(['claude-code', 'codex', 'opencode']);
|
|
91
|
+
export const PLAN_MODE_PROVIDERS = new Set(['claude-code', 'codex', 'opencode', 'antigravity']);
|
|
60
92
|
|
|
61
93
|
export const DEFAULT_BRAIN = Object.freeze({
|
|
62
94
|
provider: 'claude-code',
|
|
@@ -80,10 +112,42 @@ export function normalizeProvider(value, fallback = null) {
|
|
|
80
112
|
return PROVIDER_ALIASES[key] || fallback;
|
|
81
113
|
}
|
|
82
114
|
|
|
115
|
+
/** Can the Assistant itself run on `provider` (a canonical id)? */
|
|
116
|
+
export function isBrainProvider(provider) {
|
|
117
|
+
return BRAIN_PROVIDERS.includes(provider);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Does `provider` report tokens and no prices (Antigravity)? */
|
|
121
|
+
export function isUnpricedProvider(provider) {
|
|
122
|
+
return UNPRICED_PROVIDERS.includes(provider);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** `value` as a brain provider, else `fallback`: an unknown name (or a worker-only provider) is never a brain. */
|
|
126
|
+
export function normalizeBrainProvider(value, fallback = null) {
|
|
127
|
+
const provider = normalizeProvider(value);
|
|
128
|
+
return provider && isBrainProvider(provider) ? provider : fallback;
|
|
129
|
+
}
|
|
130
|
+
|
|
83
131
|
export function providerShortLabel(provider) {
|
|
84
132
|
return PROVIDER_SHORT_LABELS[provider] || String(provider || '');
|
|
85
133
|
}
|
|
86
134
|
|
|
135
|
+
/** "Claude Code", "Antigravity": a provider's full name ('' for none). */
|
|
136
|
+
export function providerLabel(provider) {
|
|
137
|
+
return PROVIDER_LABELS[provider] || String(provider || '');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The icon, colour and name of a provider (provider-icons.js). A provider that file does not know
|
|
142
|
+
* yet keeps its own name and shows no icon, never another provider's (getProviderMeta falls back to
|
|
143
|
+
* Claude Code's for an unknown id).
|
|
144
|
+
*/
|
|
145
|
+
export function providerMeta(provider) {
|
|
146
|
+
const meta = getProviderMeta(provider || undefined);
|
|
147
|
+
if (!provider || meta?.id === provider) return meta;
|
|
148
|
+
return { id: provider, label: providerLabel(provider), shortLabel: providerShortLabel(provider), icon: '', color: '' };
|
|
149
|
+
}
|
|
150
|
+
|
|
87
151
|
function str(value) {
|
|
88
152
|
if (value == null) return '';
|
|
89
153
|
return String(value).trim();
|
|
@@ -93,16 +157,19 @@ function str(value) {
|
|
|
93
157
|
export function normalizeBrain(raw, fallback = DEFAULT_BRAIN) {
|
|
94
158
|
const base = { ...DEFAULT_BRAIN, ...(fallback || {}) };
|
|
95
159
|
const src = raw && typeof raw === 'object' ? raw : {};
|
|
96
|
-
|
|
160
|
+
// A provider that cannot be the brain (an unknown name) falls back like any unknown one.
|
|
161
|
+
const provider = normalizeBrainProvider(src.provider ?? src.brain, normalizeBrainProvider(base.provider)) || 'claude-code';
|
|
97
162
|
const modes = PERMISSION_MODES[provider] || PERMISSION_MODES['claude-code'];
|
|
98
163
|
const rawMode = str(src.permissionMode);
|
|
99
164
|
// 'plan' used to be one of Claude's permission modes: it is the plan flag now.
|
|
100
165
|
const legacyPlan = rawMode === 'plan';
|
|
101
166
|
const mode = legacyPlan ? 'default' : rawMode;
|
|
167
|
+
const effort = str(src.effort) || null;
|
|
102
168
|
return {
|
|
103
169
|
provider,
|
|
104
170
|
model: str(src.model),
|
|
105
|
-
|
|
171
|
+
// Antigravity's thinking levels have no "off": no level is the model's own default (null).
|
|
172
|
+
effort: provider === 'antigravity' && effort === 'off' ? null : effort,
|
|
106
173
|
agent: provider === 'opencode' ? (str(src.agent) || null) : null,
|
|
107
174
|
project: str(src.project ?? src.cwd) || null,
|
|
108
175
|
tools: provider === 'opencode' ? (Array.isArray(src.tools) ? [...src.tools] : null) : ['all'],
|
|
@@ -1368,6 +1368,40 @@ export function injectAssistantStyles() {
|
|
|
1368
1368
|
/* Route card */
|
|
1369
1369
|
.asst-route-summary { color: var(--asst-text); }
|
|
1370
1370
|
.asst-route-reasons { margin-top: -4px; color: var(--asst-text-3); font-size: 11.5px; }
|
|
1371
|
+
/* Here / Dispatch: a segmented choice of where the task runs; the list, the model menu and Run follow it.
|
|
1372
|
+
sm capsules in one xs-padded track; the pick takes the gold "on" pair, like the option's key. */
|
|
1373
|
+
.asst-route-sides {
|
|
1374
|
+
display: inline-flex;
|
|
1375
|
+
align-self: flex-start;
|
|
1376
|
+
gap: 2px;
|
|
1377
|
+
padding: 2px;
|
|
1378
|
+
border-radius: var(--asst-pill-r-md);
|
|
1379
|
+
background: var(--asst-fill-1);
|
|
1380
|
+
box-shadow: inset 0 0 0 1px var(--asst-line);
|
|
1381
|
+
}
|
|
1382
|
+
.asst-route-side {
|
|
1383
|
+
display: inline-flex;
|
|
1384
|
+
align-items: center;
|
|
1385
|
+
justify-content: center;
|
|
1386
|
+
min-width: 76px;
|
|
1387
|
+
height: var(--asst-pill-h-sm);
|
|
1388
|
+
padding: 0 var(--asst-pill-px-sm);
|
|
1389
|
+
border: 0;
|
|
1390
|
+
border-radius: var(--asst-pill-r-sm);
|
|
1391
|
+
background: transparent;
|
|
1392
|
+
color: var(--asst-text-2);
|
|
1393
|
+
font: inherit;
|
|
1394
|
+
font-size: 12px;
|
|
1395
|
+
font-weight: 500;
|
|
1396
|
+
white-space: nowrap;
|
|
1397
|
+
cursor: pointer;
|
|
1398
|
+
transition: background-color var(--asst-dur-fast) var(--asst-ease-standard), box-shadow var(--asst-dur-fast) var(--asst-ease-standard), color var(--asst-dur-fast) var(--asst-ease-standard);
|
|
1399
|
+
}
|
|
1400
|
+
.asst-route-side:hover { background: var(--asst-fill-2); color: var(--asst-text); }
|
|
1401
|
+
.asst-route-side.selected { background: var(--asst-pill-on-fill); box-shadow: inset 0 0 0 1px var(--asst-pill-on-ring); color: var(--asst-text); }
|
|
1402
|
+
.asst-route-side[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
|
|
1403
|
+
.asst-route-side[aria-disabled="true"]:not(.selected):hover { background: transparent; color: var(--asst-text-2); }
|
|
1404
|
+
.asst-route-side-note { margin-top: -6px; color: var(--asst-text-3); font-size: 11.5px; }
|
|
1371
1405
|
.asst-route-options { display: flex; flex-direction: column; gap: 4px; }
|
|
1372
1406
|
.asst-route-option {
|
|
1373
1407
|
display: flex;
|
|
@@ -13,16 +13,43 @@
|
|
|
13
13
|
|
|
14
14
|
export const KIND_IDS = Object.freeze(['shell', 'read', 'edit', 'search', 'web', 'subagent', 'plan', 'memory', 'runs', 'computer', 'think', 'mcp']);
|
|
15
15
|
|
|
16
|
-
// Tool names after lowercasing and stripping `mcp__<server>__` / `<server>_`.
|
|
16
|
+
// Tool names after lowercasing and stripping `mcp__<server>__` / `<server>_`. Antigravity's own tools keep their
|
|
17
|
+
// snake_case names (run_command, read_file, write_to_file, grep_search, search_web …).
|
|
17
18
|
const KIND_TOOLS = {
|
|
18
|
-
shell: ['bash', 'bashoutput', 'killshell', 'shell', 'exec_command', 'commandexecution'],
|
|
19
|
-
read: ['read', 'notebookread', 'view'],
|
|
20
|
-
edit: ['edit', 'multiedit', 'write', 'notebookedit', 'patch', 'apply_patch', 'filechange'],
|
|
21
|
-
search: ['grep', 'glob', 'ls', 'list', 'codesearch', 'toolsearch'],
|
|
22
|
-
web: ['webfetch', 'websearch'],
|
|
23
|
-
subagent: ['task', 'agent', 'collabtoolcall', 'taskstop'],
|
|
19
|
+
shell: ['bash', 'bashoutput', 'killshell', 'shell', 'exec_command', 'commandexecution', 'run_command', 'send_command_input', 'command_status', 'read_terminal', 'shell_exec'],
|
|
20
|
+
read: ['read', 'notebookread', 'view', 'read_file', 'view_file', 'view_file_outline', 'view_code_item', 'view_content_chunk', 'read_resource', 'read_notebook'],
|
|
21
|
+
edit: ['edit', 'multiedit', 'write', 'notebookedit', 'patch', 'apply_patch', 'filechange', 'write_to_file', 'write_file', 'create_file', 'replace_file_content', 'multi_replace_file_content', 'edit_file', 'edit_notebook', 'delete_file', 'move_file'],
|
|
22
|
+
search: ['grep', 'glob', 'ls', 'list', 'codesearch', 'toolsearch', 'grep_search', 'find_by_name', 'list_dir', 'list_directory', 'codebase_search', 'code_search'],
|
|
23
|
+
web: ['webfetch', 'websearch', 'search_web', 'read_url_content', 'open_browser_url', 'read_browser_page', 'browser_subagent'],
|
|
24
|
+
subagent: ['task', 'agent', 'collabtoolcall', 'taskstop', 'invoke_subagent'],
|
|
24
25
|
plan: ['todowrite', 'todoread', 'update_plan'],
|
|
25
26
|
};
|
|
27
|
+
|
|
28
|
+
// Antigravity names its arguments in its own words: each alias is filled from the first of them that is set.
|
|
29
|
+
const INPUT_ALIASES = [
|
|
30
|
+
['command', ['CommandLine']],
|
|
31
|
+
['cwd', ['Cwd']],
|
|
32
|
+
['file_path', ['AbsolutePath', 'TargetFile', 'Path']],
|
|
33
|
+
['path', ['DirectoryPath', 'SearchPath', 'SearchDirectory']],
|
|
34
|
+
['query', ['Query']],
|
|
35
|
+
['url', ['Url']],
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A tool call's input with the names the rack reads (command, file_path, path, query, url) filled from
|
|
40
|
+
* Antigravity's own (CommandLine, AbsolutePath, TargetFile, DirectoryPath, Query, Url …). A name that is set
|
|
41
|
+
* already is kept; anything else is left as it came. Not an object: {}.
|
|
42
|
+
*/
|
|
43
|
+
export function canonicalToolInput(input) {
|
|
44
|
+
const i = input && typeof input === 'object' && !Array.isArray(input) ? input : {};
|
|
45
|
+
let out = null;
|
|
46
|
+
for (const [alias, sources] of INPUT_ALIASES) {
|
|
47
|
+
if (i[alias] !== undefined && i[alias] !== null && i[alias] !== '') continue;
|
|
48
|
+
const value = sources.map(key => i[key]).find(v => typeof v === 'string' && v);
|
|
49
|
+
if (value) { out = out || { ...i }; out[alias] = value; }
|
|
50
|
+
}
|
|
51
|
+
return out || i;
|
|
52
|
+
}
|
|
26
53
|
// SynaBun's own tools: only when the call is SynaBun's (or carries no server).
|
|
27
54
|
const MEMORY_TOOLS = ['recall', 'remember', 'reflect', 'forget', 'restore', 'memories', 'sync', 'category'];
|
|
28
55
|
const COMPUTER_TOOLS = ['computer', 'computer_apps', 'computer_ax', 'computer_status'];
|
|
@@ -13,6 +13,7 @@ const REASONS = {
|
|
|
13
13
|
'codex-no-records': ['reason.codex-no-records', 'Codex ended without final usage records.'],
|
|
14
14
|
'codex-rollout-missing': ['reason.codex-rollout-missing', "Codex usage records were missing; the turn's own totals were used."],
|
|
15
15
|
'opencode-not-reconciled': ['reason.opencode-not-reconciled', "OpenCode's final message check did not finish."],
|
|
16
|
+
'antigravity-total-mismatch': ['reason.antigravity-total-mismatch', "Antigravity's token total did not match its token classes; the classes were counted as reported."],
|
|
16
17
|
interrupted: ['reason.interrupted', 'A turn was interrupted; the call it cut off is counted from the stream.'],
|
|
17
18
|
'no-result': ['reason.no-result', 'A run ended mid-turn; its last calls are counted from the stream.'],
|
|
18
19
|
};
|
|
@@ -34,7 +35,7 @@ const node = (tag, cls, value) => {
|
|
|
34
35
|
};
|
|
35
36
|
const valid = (n) => Number.isSafeInteger(n) && n >= 0;
|
|
36
37
|
const safe = (n) => (valid(n) ? n : 0);
|
|
37
|
-
const providerName = (id) => ['claude-code', 'codex', 'opencode'].includes(id) ? getProviderMeta(id).label : id === 'jev' ? 'Jev' : String(id || '');
|
|
38
|
+
const providerName = (id) => ['claude-code', 'codex', 'opencode'].includes(id) ? getProviderMeta(id).label : id === 'antigravity' ? 'Antigravity' : id === 'jev' ? 'Jev' : String(id || '');
|
|
38
39
|
const defaultTr = (_, fallback, params) => String(fallback).replace(/\{(\w+)\}/g, (_m, key) => params?.[key] ?? `{${key}}`);
|
|
39
40
|
export const formatExactTokens = (n) => new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }).format(valid(n) ? n : 0);
|
|
40
41
|
export function formatCompactTokens(n) {
|
|
@@ -6,7 +6,7 @@ const bound = new WeakSet();
|
|
|
6
6
|
const diffStates = new WeakMap();
|
|
7
7
|
const pending = new WeakMap();
|
|
8
8
|
const cleaned = new WeakMap();
|
|
9
|
-
const watched = new
|
|
9
|
+
const watched = new WeakMap(); // transcript -> teardown (including queued mutation work)
|
|
10
10
|
const observer = typeof IntersectionObserver === 'function' ? new IntersectionObserver(entries => {
|
|
11
11
|
for (const entry of entries) if (entry.isIntersecting) { pending.get(entry.target)?.(); observer.unobserve(entry.target); pending.delete(entry.target); }
|
|
12
12
|
}, { rootMargin: '200px' }) : null;
|
|
@@ -23,6 +23,7 @@ const measurements = new WeakMap();
|
|
|
23
23
|
const measurementQueue = new Set();
|
|
24
24
|
let measurementFrame = false, capId = 0;
|
|
25
25
|
function queueMeasurement(el) {
|
|
26
|
+
if (!measurements.has(el)) return;
|
|
26
27
|
measurementQueue.add(el);
|
|
27
28
|
if (measurementFrame) return;
|
|
28
29
|
measurementFrame = true;
|
|
@@ -50,6 +51,7 @@ function measureChanges(el, update) {
|
|
|
50
51
|
}
|
|
51
52
|
function releaseMeasurements(root) {
|
|
52
53
|
for (const el of [root, ...(root.querySelectorAll?.('*') || [])]) {
|
|
54
|
+
observer?.unobserve(el); pending.delete(el);
|
|
53
55
|
sizeObserver?.unobserve(el); contentObservers.get(el)?.disconnect();
|
|
54
56
|
contentObservers.delete(el); measurements.delete(el); measurementQueue.delete(el);
|
|
55
57
|
}
|
|
@@ -249,11 +251,13 @@ export function cleanOutputBlocks(container) {
|
|
|
249
251
|
// Covers generic/legacy cards and request builders which assign pre.textContent
|
|
250
252
|
// directly. Observe each transcript once; our own writes settle after one pass.
|
|
251
253
|
export function observeOutputBlocks(container) {
|
|
252
|
-
if (!container ||
|
|
253
|
-
watched.
|
|
254
|
+
if (!container || typeof MutationObserver !== 'function') return;
|
|
255
|
+
if (watched.has(container)) return watched.get(container);
|
|
254
256
|
let queued = false;
|
|
257
|
+
let disposed = false;
|
|
255
258
|
const dirty = new Set();
|
|
256
259
|
const watcher = new MutationObserver(records => {
|
|
260
|
+
if (disposed) return;
|
|
257
261
|
for (const record of records) {
|
|
258
262
|
for (const removed of record.removedNodes || []) if (removed.nodeType === 1 && !removed.isConnected) releaseMeasurements(removed);
|
|
259
263
|
const target = record.target.nodeType === 1 ? record.target : record.target.parentElement;
|
|
@@ -265,6 +269,7 @@ export function observeOutputBlocks(container) {
|
|
|
265
269
|
queued = true;
|
|
266
270
|
queueMicrotask(() => {
|
|
267
271
|
queued = false;
|
|
272
|
+
if (disposed) return;
|
|
268
273
|
for (const root of dirty) {
|
|
269
274
|
if (root.matches?.('pre, .cxp-card-pre') && !root.closest('.cxp-readable, .cxp-output-source, .cxp-diff-view') && !root.querySelector('.cxp-readable')) {
|
|
270
275
|
const code = root.querySelector('code');
|
|
@@ -276,6 +281,24 @@ export function observeOutputBlocks(container) {
|
|
|
276
281
|
});
|
|
277
282
|
});
|
|
278
283
|
watcher.observe(container, { childList: true, characterData: true, subtree: true });
|
|
284
|
+
const dispose = () => {
|
|
285
|
+
disposed = true;
|
|
286
|
+
watcher.disconnect();
|
|
287
|
+
dirty.clear();
|
|
288
|
+
watched.delete(container);
|
|
289
|
+
releaseMeasurements(container);
|
|
290
|
+
};
|
|
291
|
+
watched.set(container, dispose);
|
|
292
|
+
return dispose;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Removing a whole transcript does not produce a mutation *inside* it. Both
|
|
296
|
+
// global observers must release its outputs and lazy diffs explicitly on close.
|
|
297
|
+
export function disposeOutputBlocks(container) {
|
|
298
|
+
if (!container) return;
|
|
299
|
+
const dispose = watched.get(container);
|
|
300
|
+
if (dispose) dispose();
|
|
301
|
+
else releaseMeasurements(container);
|
|
279
302
|
}
|
|
280
303
|
|
|
281
304
|
function diffRows(file, target, count) {
|