synabun 2.1.0 → 2.2.1
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 +139 -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 +504 -39
- package/neural-interface/i18n/pt-BR.json +504 -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-brains/codex.js +6 -1
- 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 +211 -50
- 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 +40 -7
- 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 +423 -23
- package/neural-interface/lib/native-loop-runtime.js +23 -2
- package/neural-interface/lib/opencode-native-boundary.js +165 -22
- 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 +80 -6
- 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 +15 -3
- 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-computer.js +26 -2
- 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-motion-gate.js +80 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +46 -9
- package/neural-interface/public/shared/assistant/asst-render.js +112 -18
- 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-scene-styles.js +55 -23
- package/neural-interface/public/shared/assistant/asst-scenes.js +279 -80
- package/neural-interface/public/shared/assistant/asst-sidepanel-styles.js +4 -4
- 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-style-cache.js +44 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +58 -4
- 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-permissions.js +12 -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/loading-teardown.js +34 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +6 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +28 -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 +17 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +6 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +101 -68
- 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/storage-retry.js +47 -0
- package/neural-interface/public/shared/storage.js +25 -4
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styles.css +362 -35
- package/neural-interface/public/shared/synabun-mascot.js +27 -8
- package/neural-interface/public/shared/synabun-ticker.js +147 -12
- 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 +374 -134
- 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-loading.js +5 -4
- 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 +731 -466
- 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
|
@@ -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'],
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Assistant — computed styles read once
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
// A value the stylesheet decides and the renderer needs on a hot path (the
|
|
5
|
+
// rack's --rk-cut, where a long subject is cut; its bubble line's
|
|
6
|
+
// white-space), read once and kept until the renderer says it may have
|
|
7
|
+
// changed. getComputedStyle() on a frame's path forces a style recalculation
|
|
8
|
+
// of everything the frame dirtied (a rig's SVG, a scene); these values change
|
|
9
|
+
// only when a container query flips, which happens only when the stage's width
|
|
10
|
+
// changes, and the renderer's ResizeObserver sees every such change and calls
|
|
11
|
+
// invalidate().
|
|
12
|
+
//
|
|
13
|
+
// createStyleCache({ read }?) → { get(el, prop), invalidate(el?), reads() }
|
|
14
|
+
//
|
|
15
|
+
// `read(el, prop)` defaults to getComputedStyle(el).getPropertyValue(prop)
|
|
16
|
+
// (trimmed); a read that throws counts as ''. DOM-free: tested in Node.
|
|
17
|
+
|
|
18
|
+
const defaultRead = (el, prop) => globalThis.getComputedStyle(el).getPropertyValue(prop);
|
|
19
|
+
|
|
20
|
+
export function createStyleCache({ read = defaultRead } = {}) {
|
|
21
|
+
let values = new WeakMap(); // element → Map(prop → value)
|
|
22
|
+
let count = 0;
|
|
23
|
+
return {
|
|
24
|
+
/** The element's computed `prop` (a custom property or a longhand), read once until invalidated. */
|
|
25
|
+
get(el, prop) {
|
|
26
|
+
if (!el) return '';
|
|
27
|
+
let props = values.get(el);
|
|
28
|
+
if (!props) { props = new Map(); values.set(el, props); }
|
|
29
|
+
if (props.has(prop)) return props.get(prop);
|
|
30
|
+
count += 1;
|
|
31
|
+
let value = '';
|
|
32
|
+
try { value = String(read(el, prop) ?? '').trim(); } catch { value = ''; }
|
|
33
|
+
props.set(prop, value);
|
|
34
|
+
return value;
|
|
35
|
+
},
|
|
36
|
+
/** Forget one element's values, or every value (no argument). */
|
|
37
|
+
invalidate(el) {
|
|
38
|
+
if (el) values.delete(el);
|
|
39
|
+
else values = new WeakMap();
|
|
40
|
+
},
|
|
41
|
+
/** How many real reads were made (tests, the idle-CPU suite). */
|
|
42
|
+
reads: () => count,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -252,9 +252,13 @@ export function injectAssistantStyles() {
|
|
|
252
252
|
.asst-count.attention { color: var(--asst-warn); box-shadow: inset 0 0 0 1px var(--asst-pill-warn-ring); }
|
|
253
253
|
/* The running cameo: the mascot's two pill eyes, glancing ±1.2px every 1.4s.
|
|
254
254
|
One form everywhere (the header status, the tray pill, run pills):
|
|
255
|
-
mascotCameoSvg() from synabun-mascot.js, a 10×7 SVG.
|
|
255
|
+
mascotCameoSvg() from synabun-mascot.js, a 10×7 SVG in two spans. The
|
|
256
|
+
glance moves the HTML box around the eyes (.syna-cameo-eyes), along the
|
|
257
|
+
pair's −12° lean (1.2 of the SVG's 10×7 units: 11.738% and 3.564% of the
|
|
258
|
+
box), never a part of the SVG: a box's transform runs on the compositor,
|
|
259
|
+
and a frame the page draws anyway restyles one span and lays nothing out. */
|
|
256
260
|
.syna-cameo { display: inline-block; flex-shrink: 0; overflow: visible; color: var(--asst-brand); }
|
|
257
|
-
.syna-cameo-
|
|
261
|
+
.syna-cameo-eyes { display: block; animation: asst-cameo 2.8s var(--asst-ease-emphasized) infinite; }
|
|
258
262
|
|
|
259
263
|
/* ── Toolbar: flush under the terminal tabs ─────────────────────────
|
|
260
264
|
Edge to edge like the tab bar above it (same hairline), no card and no
|
|
@@ -1088,6 +1092,8 @@ export function injectAssistantStyles() {
|
|
|
1088
1092
|
border-radius: 50%;
|
|
1089
1093
|
animation: asst-spin .8s linear infinite;
|
|
1090
1094
|
}
|
|
1095
|
+
/* A spinner nobody can see never runs (a hidden holder still restyles a running loop every frame). */
|
|
1096
|
+
[hidden] .asst-spinner, .asst-spinner[hidden], [inert] .asst-spinner { animation: none; }
|
|
1091
1097
|
.asst-computer-activity { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 7px 8px 7px 12px; border-bottom: 1px solid var(--asst-line); }
|
|
1092
1098
|
.asst-live-dot { width: 6px; height: 6px; flex-shrink: 0; border-radius: 50%; background: var(--asst-live); animation: asst-live 1.4s var(--asst-ease-standard) infinite; }
|
|
1093
1099
|
.asst-computer-activity[data-state="paused_user"] .asst-live-dot { background: var(--asst-warn); animation: none; }
|
|
@@ -1368,6 +1374,40 @@ export function injectAssistantStyles() {
|
|
|
1368
1374
|
/* Route card */
|
|
1369
1375
|
.asst-route-summary { color: var(--asst-text); }
|
|
1370
1376
|
.asst-route-reasons { margin-top: -4px; color: var(--asst-text-3); font-size: 11.5px; }
|
|
1377
|
+
/* Here / Dispatch: a segmented choice of where the task runs; the list, the model menu and Run follow it.
|
|
1378
|
+
sm capsules in one xs-padded track; the pick takes the gold "on" pair, like the option's key. */
|
|
1379
|
+
.asst-route-sides {
|
|
1380
|
+
display: inline-flex;
|
|
1381
|
+
align-self: flex-start;
|
|
1382
|
+
gap: 2px;
|
|
1383
|
+
padding: 2px;
|
|
1384
|
+
border-radius: var(--asst-pill-r-md);
|
|
1385
|
+
background: var(--asst-fill-1);
|
|
1386
|
+
box-shadow: inset 0 0 0 1px var(--asst-line);
|
|
1387
|
+
}
|
|
1388
|
+
.asst-route-side {
|
|
1389
|
+
display: inline-flex;
|
|
1390
|
+
align-items: center;
|
|
1391
|
+
justify-content: center;
|
|
1392
|
+
min-width: 76px;
|
|
1393
|
+
height: var(--asst-pill-h-sm);
|
|
1394
|
+
padding: 0 var(--asst-pill-px-sm);
|
|
1395
|
+
border: 0;
|
|
1396
|
+
border-radius: var(--asst-pill-r-sm);
|
|
1397
|
+
background: transparent;
|
|
1398
|
+
color: var(--asst-text-2);
|
|
1399
|
+
font: inherit;
|
|
1400
|
+
font-size: 12px;
|
|
1401
|
+
font-weight: 500;
|
|
1402
|
+
white-space: nowrap;
|
|
1403
|
+
cursor: pointer;
|
|
1404
|
+
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);
|
|
1405
|
+
}
|
|
1406
|
+
.asst-route-side:hover { background: var(--asst-fill-2); color: var(--asst-text); }
|
|
1407
|
+
.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); }
|
|
1408
|
+
.asst-route-side[aria-disabled="true"] { opacity: .45; cursor: not-allowed; }
|
|
1409
|
+
.asst-route-side[aria-disabled="true"]:not(.selected):hover { background: transparent; color: var(--asst-text-2); }
|
|
1410
|
+
.asst-route-side-note { margin-top: -6px; color: var(--asst-text-3); font-size: 11.5px; }
|
|
1371
1411
|
.asst-route-options { display: flex; flex-direction: column; gap: 4px; }
|
|
1372
1412
|
.asst-route-option {
|
|
1373
1413
|
display: flex;
|
|
@@ -2029,10 +2069,24 @@ export function injectAssistantStyles() {
|
|
|
2029
2069
|
whole Assistant holds still: the component, its menus and modals, the
|
|
2030
2070
|
sidepanel frame and its header (.asp-*), and the tray pills. */
|
|
2031
2071
|
@keyframes asst-live { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.8); } }
|
|
2032
|
-
@keyframes asst-cameo { 0%, 42% { transform:
|
|
2072
|
+
@keyframes asst-cameo { 0%, 42% { transform: translate(-11.738%, 3.564%); } 50%, 92% { transform: translate(11.738%, -3.564%); } 100% { transform: translate(-11.738%, 3.564%); } }
|
|
2033
2073
|
@keyframes asst-spin { to { transform: rotate(360deg); } }
|
|
2034
2074
|
|
|
2035
|
-
|
|
2075
|
+
/* A moving box gets its layer up front (only where motion is allowed; nothing is promoted under reduced motion). */
|
|
2076
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
2077
|
+
.syna-cameo-eyes { will-change: transform; }
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
body.ui-interacting :is(.asst-live-dot, .asst-spinner, .asst-computer-dot, .syna-cameo-eyes) { animation-play-state: paused !important; }
|
|
2081
|
+
|
|
2082
|
+
/* Off screen, nothing loops. A holder the renderer saw leave the viewport (asst-motion-gate.js marks it
|
|
2083
|
+
data-offscreen: a run card, a rack or a route line scrolled out of its transcript) and a closed side
|
|
2084
|
+
panel (it stays in the page, moved out and faded: .assistant-panel without .open) pause every loop
|
|
2085
|
+
under them; each picks up where it stopped when it is back. Every frame the page draws restyles each
|
|
2086
|
+
running animation, composited or not, so a loop nobody sees is never free. */
|
|
2087
|
+
[data-offscreen], [data-offscreen]::before, [data-offscreen]::after,
|
|
2088
|
+
[data-offscreen] *, [data-offscreen] *::before, [data-offscreen] *::after,
|
|
2089
|
+
.assistant-panel:not(.open) *, .assistant-panel:not(.open) *::before, .assistant-panel:not(.open) *::after { animation-play-state: paused !important; }
|
|
2036
2090
|
body.ui-interacting .asst-route-line[data-live] .asst-route-icon,
|
|
2037
2091
|
body.ui-interacting .asst-route-line[data-live] .asst-route-sweep::after { animation-play-state: paused !important; }
|
|
2038
2092
|
|
|
@@ -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) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { syncWorkerQuestions } from '../worker-questions.js';
|
|
2
|
+
import { handleServerRequest, resolveRequestCard } from './cdx-requests.js';
|
|
1
3
|
import { cxpText, cxpSessionLabel, setCodexTranslator } from './cdx-copy.js';
|
|
2
4
|
// SynaBun — Codex Panel: DOM, Events & Lifecycle
|
|
3
5
|
// ═══════════════════════════════════════════
|
|
@@ -808,6 +810,7 @@ function wireEvents() {
|
|
|
808
810
|
try {
|
|
809
811
|
const statusResponse = await fetch(`/api/git/status?path=${encodeURIComponent(project)}`);
|
|
810
812
|
const status = statusResponse.ok ? await statusResponse.json() : null;
|
|
813
|
+
if (!status?.ok || !status.isGit || status.partial || status.stale || status.timedOut) throw new Error(status?.error || 'Git status is incomplete. Refresh before switching branches.');
|
|
811
814
|
if (status?.changes?.length) {
|
|
812
815
|
const confirmed = window.confirm(`Switch from ${previous || 'the current branch'} to ${branch} with ${status.changes.length} uncommitted change${status.changes.length === 1 ? '' : 's'}? Git will preserve them when possible.`);
|
|
813
816
|
if (!confirmed) {
|
|
@@ -927,11 +930,12 @@ function wireEvents() {
|
|
|
927
930
|
|
|
928
931
|
|
|
929
932
|
on('codex-panel:show', async (data) => {
|
|
930
|
-
setVisible(true);
|
|
931
933
|
if (data?.tabId) {
|
|
932
934
|
const idx = _tabs.findIndex((entry) => entry.id === data.tabId);
|
|
933
935
|
if (idx >= 0) switchTab(idx);
|
|
934
936
|
}
|
|
937
|
+
setVisible(true);
|
|
938
|
+
ensureProjectsLoaded().catch(() => {});
|
|
935
939
|
});
|
|
936
940
|
|
|
937
941
|
const findPlanEditorTab = ({ source, tabId, planId, accountId, threadId } = {}) => {
|
|
@@ -1108,8 +1112,8 @@ export async function toggleCodexPanel() {
|
|
|
1108
1112
|
setVisible(false);
|
|
1109
1113
|
return;
|
|
1110
1114
|
}
|
|
1111
|
-
await ensureProjectsLoaded();
|
|
1112
1115
|
setVisible(true);
|
|
1116
|
+
await ensureProjectsLoaded();
|
|
1113
1117
|
}
|
|
1114
1118
|
|
|
1115
1119
|
export async function attachCodexAutomation(run, {
|
|
@@ -1188,6 +1192,14 @@ export async function attachCodexAutomation(run, {
|
|
|
1188
1192
|
tab._automationLifecycleError = run.error;
|
|
1189
1193
|
withTab(tab, () => appendSystem(run.error, 'error'));
|
|
1190
1194
|
}
|
|
1195
|
+
withTab(tab, () => syncWorkerQuestions(tab, run, {
|
|
1196
|
+
open: (id, pending) => handleServerRequest({ requestId: id, method: pending.protocol?.method, params: pending.input || {}, threadId }),
|
|
1197
|
+
close: (id) => {
|
|
1198
|
+
resolveRequestCard(id, t('workerQuestion.resolved'));
|
|
1199
|
+
tab.heldServerRequests?.delete(String(id));
|
|
1200
|
+
tab.blockingServerRequests?.delete(String(id));
|
|
1201
|
+
},
|
|
1202
|
+
}));
|
|
1191
1203
|
trackWorkerEnd(run, { apply: (view) => { tab._workerEnd = view; placeWorkerEnd(tab); } });
|
|
1192
1204
|
saveTabs();
|
|
1193
1205
|
renderPills();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// The early Codex rollout preview (server.js sendEarlyCodexRolloutPreview): DOM-free decisions.
|
|
2
|
+
|
|
3
|
+
/** A bootstrap asks for the preview only for a saved thread of the tab on screen. */
|
|
4
|
+
export function codexWantsRolloutPreview(tab, shownTab, threadId) {
|
|
5
|
+
return !!threadId && !!tab && tab === shownTab;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** A preview paints only for the thread shown, and only while nothing else (a stored snapshot) is on screen. */
|
|
9
|
+
export function rolloutPreviewApplies(msg, shownThread, transcriptSourceType) {
|
|
10
|
+
const previewFor = String(msg?.threadId || msg?.thread?.id || '');
|
|
11
|
+
return !transcriptSourceType && !!previewFor && previewFor === String(shownThread || '');
|
|
12
|
+
}
|