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
|
@@ -5,7 +5,7 @@ import { readStoredToolListRecord } from './tool-lists.js';
|
|
|
5
5
|
// ═══════════════════════════════════════════
|
|
6
6
|
//
|
|
7
7
|
// A "dispatch" is one worker run launched by the central assistant: a single
|
|
8
|
-
// provider conversation (Claude / Codex / OpenCode) that executes one task,
|
|
8
|
+
// provider conversation (Claude / Codex / OpenCode / Antigravity) that executes one task,
|
|
9
9
|
// then stays warm for follow-up turns until the assistant completes it or the
|
|
10
10
|
// idle timeout fires. It rides on NativeLoopRuntime (ledger, claims, sidepanel
|
|
11
11
|
// attach) through the per-launch buildPrompt/wrapAdapter overrides, so the
|
|
@@ -43,16 +43,19 @@ import { loadPlaybook } from './assistant-playbooks.js';
|
|
|
43
43
|
import { loadStyleGuideForRun } from './style-guide/store.js';
|
|
44
44
|
import { claimCheckWaived, normalizeTestDensity, testingApplies } from './assistant-test-density.js';
|
|
45
45
|
import { normalizeEffort } from './effort-levels.js';
|
|
46
|
-
import { createTurnEvidence, recordProviderEvent, evidenceSnapshot, claimEvidenceLine } from './assistant-evidence.js';
|
|
46
|
+
import { acpCommandOf, createTurnEvidence, recordProviderEvent, evidenceSnapshot, claimEvidenceLine } from './assistant-evidence.js';
|
|
47
47
|
import { BUDGET_MAX_USD, codexUsageCostUsd, floorCents } from './assistant-budget.js';
|
|
48
|
-
import { MIN_SPEND_USD, PROVIDER_LABELS, isFreePrice } from './assistant-budget-split.js';
|
|
48
|
+
import { MIN_SPEND_USD, PROVIDER_LABELS, UNPRICED_PROVIDER_REASON, isFreePrice, isUnpricedProvider } from './assistant-budget-split.js';
|
|
49
49
|
import { createBudgetPlanner } from './assistant-budget-planner.js';
|
|
50
|
-
import { CLAUDE_ESTIMATE_SOURCE, CODEX_USAGE_KEYS, TOKEN_KEYS, addTokens, codexUsageTokens, createClaudeMeter, createCodexMeter, createOpenCodeMeter, totalTokens, withTotal, zeroTokens } from './assistant-usage.js';
|
|
50
|
+
import { CLAUDE_ESTIMATE_SOURCE, CODEX_USAGE_KEYS, TOKEN_KEYS, addTokens, antigravityUsageTokens, codexUsageTokens, createClaudeMeter, createCodexMeter, createOpenCodeMeter, totalTokens, withTotal, zeroTokens } from './assistant-usage.js';
|
|
51
51
|
import { claudeTokensCostUsd } from './assistant-pricing.js';
|
|
52
52
|
import { RemotePolicyError, clampDispatchSpec, getSessionPolicy, readRegisteredProjects } from './remote-policy.js';
|
|
53
|
+
import { validateWorkerReply } from './worker-question.js';
|
|
53
54
|
import { limitFromText } from '../public/shared/assistant/asst-limit.js';
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
// Antigravity (Google's `agy`, over ACP: lib/native-loop-providers.js createAntigravityNativeLoopAdapter) is a worker
|
|
57
|
+
// provider only: never a brain, no accounts, its usage reported in tokens and never priced.
|
|
58
|
+
export const NATIVE_PROVIDERS = new Set(['claude-code', 'codex', 'opencode', 'antigravity']);
|
|
56
59
|
// awaiting_route: an un-routed dispatch held until the user picks a model on
|
|
57
60
|
// the route card (ask modes). It is active but consumes no rails.
|
|
58
61
|
// suspended: a native worker the server suspended as it went down (its CLI
|
|
@@ -85,7 +88,7 @@ export function runStatusFromState(state) {
|
|
|
85
88
|
export const MIN_MAX_MINUTES = 20;
|
|
86
89
|
|
|
87
90
|
export const DEFAULT_LIMITS = Object.freeze({
|
|
88
|
-
perProvider: Object.freeze({ 'claude-code': 3, codex: 4, opencode: 3 }),
|
|
91
|
+
perProvider: Object.freeze({ 'claude-code': 3, codex: 4, opencode: 3, antigravity: 3 }),
|
|
89
92
|
perCodexAccount: 2,
|
|
90
93
|
perSession: 6,
|
|
91
94
|
queuedPerSession: 20,
|
|
@@ -298,9 +301,55 @@ export function summarizeProviderEvent(payload = {}) {
|
|
|
298
301
|
if (/question\.asked/i.test(eventType)) return `question asked: ${clip((event.questions || []).map((q) => q.question).join(' | '), 200)}`;
|
|
299
302
|
return null;
|
|
300
303
|
}
|
|
304
|
+
if (payload.provider === 'antigravity') {
|
|
305
|
+
// eventType is the ACP session/update kind; event the update as the agent sent it.
|
|
306
|
+
if (eventType === 'agent_message_chunk') {
|
|
307
|
+
const text = event.content?.type === 'text' ? event.content.text : '';
|
|
308
|
+
return text && String(text).trim() ? `assistant: ${clip(text, 200)}` : null;
|
|
309
|
+
}
|
|
310
|
+
if (eventType === 'tool_call') return `${acpToolNoun(event)} started: ${acpToolDetail(event)}`;
|
|
311
|
+
if (eventType === 'tool_call_update') {
|
|
312
|
+
// Only the end of a call: its start was the tool_call line, its progress says nothing new.
|
|
313
|
+
if (event.status !== 'completed' && event.status !== 'failed') return null;
|
|
314
|
+
return `${acpToolNoun(event)} ${event.status === 'failed' ? 'failed' : 'done'}: ${acpToolDetail(event)}`;
|
|
315
|
+
}
|
|
316
|
+
if (eventType === 'plan') {
|
|
317
|
+
const entries = Array.isArray(event.entries) ? event.entries : [];
|
|
318
|
+
if (!entries.length) return null;
|
|
319
|
+
const done = entries.filter((step) => step?.status === 'completed').length;
|
|
320
|
+
const current = entries.find((step) => step?.status === 'in_progress');
|
|
321
|
+
return `plan: ${done}/${entries.length} done${current?.content ? `, now ${clip(current.content, 120)}` : ''}`;
|
|
322
|
+
}
|
|
323
|
+
if (eventType === 'synabun.provider_exit') {
|
|
324
|
+
const how = event.signal ? ` (${event.signal})` : event.code !== null && event.code !== undefined ? ` (code ${event.code})` : '';
|
|
325
|
+
return `error: Antigravity exited${how}${event.error ? `: ${clip(event.error, 160)}` : ''}`;
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
301
329
|
return null;
|
|
302
330
|
}
|
|
303
331
|
|
|
332
|
+
/** What an ACP tool call is, for a transcript line: command, file_change or tool (Codex's words). */
|
|
333
|
+
function acpToolNoun(call = {}) {
|
|
334
|
+
const kind = String(call.kind || '');
|
|
335
|
+
// agy names an MCP call call_mcp_tool, kind `execute`: it is a tool, never a command.
|
|
336
|
+
if (call.name === 'call_mcp_tool' || (call.rawInput && typeof call.rawInput === 'object' && call.rawInput.ServerName && call.rawInput.ToolName)) return 'tool';
|
|
337
|
+
if (kind === 'execute') return 'command';
|
|
338
|
+
if (kind === 'edit' || kind === 'delete' || kind === 'move') return 'file_change';
|
|
339
|
+
return 'tool';
|
|
340
|
+
}
|
|
341
|
+
/** The command, the paths or the title of an ACP tool call; its id when an update carries none of them. */
|
|
342
|
+
function acpToolDetail(call = {}) {
|
|
343
|
+
const input = call.rawInput && typeof call.rawInput === 'object' ? call.rawInput : {};
|
|
344
|
+
if (input.ServerName && input.ToolName) return clip(`${input.ServerName}_${input.ToolName}`, 140);
|
|
345
|
+
const command = call.kind === 'execute' ? acpCommandOf(call) : '';
|
|
346
|
+
if (command) return clip(command, 140);
|
|
347
|
+
const paths = (Array.isArray(call.locations) ? call.locations : []).map((location) => location?.path).filter((path) => typeof path === 'string' && path);
|
|
348
|
+
if (paths.length && call.kind !== 'execute' && call.kind !== 'fetch') return clip(paths.join(', '), 160);
|
|
349
|
+
if (call.title) return clip(call.title, 140);
|
|
350
|
+
return call.toolCallId ? `call ${String(call.toolCallId).slice(0, 12)}` : (call.kind || 'tool');
|
|
351
|
+
}
|
|
352
|
+
|
|
304
353
|
// The old five-cap keys a config file may still hold: never limits (the budget planner owns the money).
|
|
305
354
|
const LEGACY_BUDGET_LIMITS = new Set(['defaultBudgetUsd', 'maxBudgetUsd', 'sessionWarnUsd', 'sessionSoftBudgetUsd', 'sessionHardBudgetUsd', 'brainBudgetUsd', 'budgetVersion', 'budgetUpdatedAt']);
|
|
306
355
|
function mergeLimits(overrides = {}) {
|
|
@@ -479,8 +528,10 @@ export function createAssistantDispatcher({
|
|
|
479
528
|
let closing = false;
|
|
480
529
|
|
|
481
530
|
// ── persistence ────────────────────────────────────────────────────────────
|
|
482
|
-
/** Why the original provider account of a run cannot be used now ('' when it can). */
|
|
531
|
+
/** Why the original provider account of a run cannot be used now ('' when it can, or the provider has no accounts). */
|
|
483
532
|
function providerAccountUnusable(accountId, provider = 'claude-code', entry = null) {
|
|
533
|
+
// OpenCode and Antigravity sign in inside their own CLI: SynaBun keeps no account of theirs.
|
|
534
|
+
if (provider !== 'codex' && provider !== 'claude-code') return '';
|
|
484
535
|
if (provider === 'codex') {
|
|
485
536
|
const home = codexAccounts?.homeFor?.(accountId);
|
|
486
537
|
if (home && entry?.codexHome && resolve(home) !== resolve(entry.codexHome)) return 'its CODEX_HOME changed';
|
|
@@ -658,9 +709,11 @@ export function createAssistantDispatcher({
|
|
|
658
709
|
status: runtimeRun.status, turnState: runtimeRun.turnState, stoppedReason: runtimeRun.stoppedReason,
|
|
659
710
|
error: runtimeRun.error, claimedBy: runtimeRun.claimedBy, providerSessionId: runtimeRun.providerSessionId,
|
|
660
711
|
providerThreadId: runtimeRun.providerThreadId,
|
|
712
|
+
...((runtimeRun.antigravityHome || entry.resumeInfo?.antigravityHome || entry.antigravityHome) ? { antigravityHome: runtimeRun.antigravityHome || entry.resumeInfo?.antigravityHome || entry.antigravityHome } : {}),
|
|
661
713
|
} : (entry.runtime || null),
|
|
662
714
|
providerSessionId: runtimeRun?.providerSessionId || entry.providerSessionId || null,
|
|
663
715
|
providerThreadId: runtimeRun?.providerThreadId || entry.providerThreadId || null,
|
|
716
|
+
...((runtimeRun?.antigravityHome || entry.resumeInfo?.antigravityHome || entry.antigravityHome) ? { antigravityHome: runtimeRun?.antigravityHome || entry.resumeInfo?.antigravityHome || entry.antigravityHome } : {}),
|
|
664
717
|
};
|
|
665
718
|
}
|
|
666
719
|
function safeRuntime() { try { return getRuntime(); } catch { return null; } }
|
|
@@ -771,7 +824,7 @@ export function createAssistantDispatcher({
|
|
|
771
824
|
}
|
|
772
825
|
|
|
773
826
|
/** `taskClass`: the run's class (its route's, else the spec's); image / video creation needs a model that makes the medium. */
|
|
774
|
-
function normalizeSpec(spec = {}, { catalogValue = null,
|
|
827
|
+
function normalizeSpec(spec = {}, { catalogValue = null, taskClass = null } = {}) {
|
|
775
828
|
const provider = String(spec.provider || '').trim().toLowerCase();
|
|
776
829
|
if (!NATIVE_PROVIDERS.has(provider)) throw new DispatchError('INVALID_PROVIDER', `provider must be one of ${[...NATIVE_PROVIDERS].join(', ')}`);
|
|
777
830
|
const task = String(spec.task || '').trim();
|
|
@@ -782,12 +835,6 @@ export function createAssistantDispatcher({
|
|
|
782
835
|
let permissionPolicy = normalizePermissionPolicy(spec.permissionPolicy);
|
|
783
836
|
let capability = normalizeCapability(spec.capability || 'full');
|
|
784
837
|
const notes = [];
|
|
785
|
-
if (provider === 'codex' && permissionPolicy === 'ask') {
|
|
786
|
-
// A route card may move an 'ask' dispatch onto Codex: degrade instead of failing.
|
|
787
|
-
if (!codexAskFallback) throw new DispatchError('PERMISSION_POLICY_UNSUPPORTED', 'Codex dispatches cannot use permissionPolicy "ask" (the Codex SDK has no approval channel). Use "restricted" for a workspace-write sandbox or "auto".', { suggested: 'restricted' });
|
|
788
|
-
permissionPolicy = 'restricted';
|
|
789
|
-
notes.push('permission policy "ask" became "restricted" (Codex has no approval channel)');
|
|
790
|
-
}
|
|
791
838
|
// "auto" (or nothing) = let routing pick / use the provider default.
|
|
792
839
|
let model = spec.model && String(spec.model).trim() && String(spec.model).trim().toLowerCase() !== 'auto' ? String(spec.model).trim() : null;
|
|
793
840
|
let modelInfo = null;
|
|
@@ -1070,10 +1117,11 @@ export function createAssistantDispatcher({
|
|
|
1070
1117
|
/**
|
|
1071
1118
|
* Why a run's spend could not be held to the budget (no price for its model), else null. Enforced
|
|
1072
1119
|
* only with a budget and a price source (`pricing`): Claude reports cost, Codex needs a list price,
|
|
1073
|
-
* OpenCode a catalog price (a free model is fine).
|
|
1120
|
+
* OpenCode a catalog price (a free model is fine), Antigravity never has one (tokens, not prices).
|
|
1074
1121
|
*/
|
|
1075
1122
|
function unpricedReason(entry) {
|
|
1076
1123
|
if (!pricing || entry.provider === 'claude-code' || !limitedNow()) return null;
|
|
1124
|
+
if (isUnpricedProvider(entry.provider)) return UNPRICED_PROVIDER_REASON;
|
|
1077
1125
|
const model = entry.pricingModel || entry.model || null;
|
|
1078
1126
|
if (entry.provider === 'codex') {
|
|
1079
1127
|
let price = null;
|
|
@@ -1085,7 +1133,10 @@ export function createAssistantDispatcher({
|
|
|
1085
1133
|
function refuseUnpriced(entry) {
|
|
1086
1134
|
const why = unpricedReason(entry);
|
|
1087
1135
|
if (!why) return;
|
|
1088
|
-
|
|
1136
|
+
const next = isUnpricedProvider(entry.provider)
|
|
1137
|
+
? `Dispatch it to another provider (agent_catalog shows prices), or ask the user to switch the session budget to No limit (Assistant → Budget) to run it on ${providerName(entry.provider)}.`
|
|
1138
|
+
: 'Pick a priced model or a free one (agent_catalog shows prices).';
|
|
1139
|
+
throw new DispatchError('BUDGET_UNPRICED', `Not dispatched: ${why}, so the session budget could not hold its spend. ${next}`, { status: 409, provider: entry.provider, model: entry.pricingModel || entry.model || null });
|
|
1089
1140
|
}
|
|
1090
1141
|
const providerName = (provider) => PROVIDER_LABELS[provider] || provider;
|
|
1091
1142
|
/** The session budget's words for a provider whose share is used up ("codex share used up: $4.00 of $4.00 spent"). */
|
|
@@ -1522,7 +1573,7 @@ export function createAssistantDispatcher({
|
|
|
1522
1573
|
emit(entry, 'route_pending', { routeId: routed.routeId });
|
|
1523
1574
|
return {
|
|
1524
1575
|
ok: true, queued: true, awaitingRoute: true, routeId: routed.routeId, run: view(entry),
|
|
1525
|
-
next: 'This run is waiting for the user to pick its model on the route card. Do not dispatch it again. It starts automatically after the pick; the decision arrives as a [SynaBun Mailbox] route_decided event.',
|
|
1576
|
+
next: 'This run is waiting for the user to pick its model on the route card. Do not dispatch it again. It starts automatically after the pick; the decision arrives as a [SynaBun Mailbox] route_decided event. If the user picks "here" instead, this run never starts and that event asks you to do the task yourself.',
|
|
1526
1577
|
};
|
|
1527
1578
|
}
|
|
1528
1579
|
|
|
@@ -1555,7 +1606,7 @@ export function createAssistantDispatcher({
|
|
|
1555
1606
|
let spec;
|
|
1556
1607
|
// The picked provider may need another clamp (Codex under strict worker approvals), or the level fell meanwhile.
|
|
1557
1608
|
const remoteNotes = [];
|
|
1558
|
-
try { input = clampRemote(input, entry.assistantSessionId, remoteNotes); spec = normalizeSpec(input, { catalogValue,
|
|
1609
|
+
try { input = clampRemote(input, entry.assistantSessionId, remoteNotes); spec = normalizeSpec(input, { catalogValue, taskClass: entry.taskClass || null }); }
|
|
1559
1610
|
catch (error) {
|
|
1560
1611
|
entry.state = 'failed'; entry.completionReason = 'route_invalid'; entry.finishedAt = iso(now()); entry.turnState = 'terminal';
|
|
1561
1612
|
entry.error = error?.message || String(error);
|
|
@@ -1581,7 +1632,10 @@ export function createAssistantDispatcher({
|
|
|
1581
1632
|
const entry = entries.get(runId);
|
|
1582
1633
|
if (!entry || entry.state !== 'awaiting_route') return false;
|
|
1583
1634
|
entry.state = 'stopped'; entry.completionReason = reason; entry.finishedAt = iso(now()); entry.turnState = 'terminal'; entry.pending = [];
|
|
1584
|
-
|
|
1635
|
+
// route_here: the user picked "here" on its card, so the assistant does the task itself and this worker never starts.
|
|
1636
|
+
const status = reason === 'route_expired' ? 'expired' : reason === 'route_here' ? 'here' : 'declined';
|
|
1637
|
+
entry.route = { ...(entry.route || {}), status, decidedBy: reason === 'route_expired' ? 'timeout' : 'user', decidedAt: iso(now()) };
|
|
1638
|
+
if (reason === 'route_here') entry.notes.push('the user chose to do this task here, in the conversation: this worker was not started');
|
|
1585
1639
|
delete entry.rawSpec;
|
|
1586
1640
|
emit(entry, 'stopped');
|
|
1587
1641
|
return true;
|
|
@@ -1605,7 +1659,7 @@ export function createAssistantDispatcher({
|
|
|
1605
1659
|
accountId: to.accountId || (to.provider === entry.provider ? entry.accountId : null),
|
|
1606
1660
|
title: clip(`${entry.title} ${retry ? '↻' : '↑'}`, 80), tags: entry.tags, workflowId: entry.workflowId, parentRunId: runId,
|
|
1607
1661
|
tools: entry.tools?.some(item => item.startsWith('mcp:')) ? retainExternalRefs(entry.toolsRequested ?? dispatchTools({}).items, entry.tools) : entry.toolsRequested ?? undefined, capability: entry.capability,
|
|
1608
|
-
permissionPolicy:
|
|
1662
|
+
permissionPolicy: entry.permissionPolicy,
|
|
1609
1663
|
usesBrowser: entry.usesBrowser, usesComputer: entry.usesComputer, maxMinutes: entry.maxMinutes, budgetUsd: entry.budgetUsd,
|
|
1610
1664
|
outputSchema: entry.outputSchema, taskClass: entry.taskClass || null,
|
|
1611
1665
|
}, { assistantSessionId: entry.assistantSessionId, origin: 'ui', brief: entry.brief || null });
|
|
@@ -1788,8 +1842,11 @@ export function createAssistantDispatcher({
|
|
|
1788
1842
|
const acq = await acquireLoopBrowserAndTab({ requestedBrowserSessionId: entry.requestedBrowserSessionId, terminalSessionId: entry.runId, logTag: 'assistant:browser' });
|
|
1789
1843
|
if (acq?.error) {
|
|
1790
1844
|
entry.state = 'failed'; entry.completionReason = 'browser_unavailable'; entry.finishedAt = iso(now()); entry.notes.push(acq.error);
|
|
1845
|
+
// The native serve boundary's refusal keeps its own code and the serves it names, on the run and the error.
|
|
1846
|
+
const boundary = acq.code === 'NATIVE_SERVE_UNVERIFIED' ? { browserCode: acq.code, offenders: Array.isArray(acq.offenders) ? acq.offenders : [] } : null;
|
|
1847
|
+
if (boundary) entry.browserError = boundary;
|
|
1791
1848
|
emit(entry, 'failed');
|
|
1792
|
-
throw new DispatchError('PROVIDER_UNAVAILABLE', acq.error, { status: 500 });
|
|
1849
|
+
throw new DispatchError('PROVIDER_UNAVAILABLE', acq.error, { status: 500, ...(boundary || {}) });
|
|
1793
1850
|
}
|
|
1794
1851
|
browserSessionId = acq.browserSessionId || null;
|
|
1795
1852
|
browserTabId = acq.browserTabId || null;
|
|
@@ -1808,7 +1865,7 @@ export function createAssistantDispatcher({
|
|
|
1808
1865
|
} else entry.scratchDir = null;
|
|
1809
1866
|
const desktopGrant = mintDesktopGrant(entry);
|
|
1810
1867
|
const state = runState(entry, { browserSessionId, browserTabId, desktopGrant, startedAt: iso(now()) });
|
|
1811
|
-
|
|
1868
|
+
state.permissionBroker = createBroker(entry.runId);
|
|
1812
1869
|
// A class's rules (design), read now so an edited override applies without a restart;
|
|
1813
1870
|
// they go to the prompt only, not into the loop state file.
|
|
1814
1871
|
const playbook = playbookFor(entry);
|
|
@@ -1892,8 +1949,13 @@ export function createAssistantDispatcher({
|
|
|
1892
1949
|
adminSandbox: entry.resumeInfo?.adminSandbox === true,
|
|
1893
1950
|
onCodexSandbox: (value) => { resumeInfoOf(entry).adminSandbox = value === true; persist({ force: true }); },
|
|
1894
1951
|
onSandboxPlan: (plan) => { resumeInfoOf(entry).sandboxPlan = plan; persist({ force: true }); },
|
|
1952
|
+
// Antigravity counts tokens per session (ACP usage): what its session had counted, kept for a resume after a
|
|
1953
|
+
// restart, so the resumed run's first turn books only its own.
|
|
1954
|
+
sessionUsageBase: entry.provider === 'antigravity' ? entry.resumeInfo?.acpSessionUsage || null : null,
|
|
1955
|
+
onSessionUsage: (value) => { if (entry.provider === 'antigravity' && value && typeof value === 'object') resumeInfoOf(entry).acpSessionUsage = value; },
|
|
1895
1956
|
guardMode: entry.guardMode, scratchDir: entry.scratchDir || null, serverPort: Number(serverPort) > 0 ? Number(serverPort) : null,
|
|
1896
1957
|
localPortRange: localPortRange && Number(localPortRange.from) > 0 && Number(localPortRange.to) >= Number(localPortRange.from) ? { from: Number(localPortRange.from), to: Number(localPortRange.to) } : null,
|
|
1958
|
+
questionTimeoutMs: limits.questionTimeoutMs,
|
|
1897
1959
|
idleTimeoutMs: entry.idleTimeoutMs, maxTurns: entry.maxTurns, outputSchema: entry.outputSchema, project: entry.project,
|
|
1898
1960
|
title: entry.title, tags: entry.tags, workflowId: entry.workflowId, parentRunId: entry.parentRunId,
|
|
1899
1961
|
// The user's brief after a clarification (assistant-task-prompt.js briefBlock).
|
|
@@ -1914,6 +1976,8 @@ export function createAssistantDispatcher({
|
|
|
1914
1976
|
if (!Number.isInteger(pid) || pid <= 0) return;
|
|
1915
1977
|
resumeInfoOf(entry).pid = pid;
|
|
1916
1978
|
entry.resumeInfo.pidStartedAt = info.startedAt || iso(now());
|
|
1979
|
+
// An Antigravity run's own home (lib/antigravity/home.js): where its conversation is, for a resume.
|
|
1980
|
+
if (typeof info.home === 'string' && info.home) { entry.resumeInfo.antigravityHome = info.home; entry.antigravityHome = info.home; }
|
|
1917
1981
|
persist({ force: true });
|
|
1918
1982
|
},
|
|
1919
1983
|
...extra,
|
|
@@ -1990,36 +2054,38 @@ export function createAssistantDispatcher({
|
|
|
1990
2054
|
return account?.home || CODEX_DEFAULT_HOME || null;
|
|
1991
2055
|
}
|
|
1992
2056
|
|
|
1993
|
-
// ──
|
|
2057
|
+
// ── request broker (questions in every policy; tool approvals under 'ask') ──────────────────────────────────────
|
|
1994
2058
|
function createBroker(runId) {
|
|
1995
2059
|
return {
|
|
1996
2060
|
request: (request = {}) => new Promise((resolvePermission) => {
|
|
1997
2061
|
const entry = entries.get(runId);
|
|
1998
2062
|
const liveState = live.get(runId);
|
|
1999
|
-
if (!entry || !liveState) { resolvePermission({ behavior: 'deny', message: 'Run is no longer tracked.' }); return; }
|
|
2063
|
+
if (!entry || !liveState || liveState.dead || liveState.suspending || TERMINAL_STATES.has(entry.state) || request.signal?.aborted) { resolvePermission({ behavior: 'deny', message: 'Run is no longer tracked.' }); return; }
|
|
2000
2064
|
const requestId = `perm-${randomBytes(6).toString('hex')}`;
|
|
2001
2065
|
const kind = request.kind === 'ask' ? 'question' : 'permission';
|
|
2002
2066
|
const timeoutMs = kind === 'question' ? limits.questionTimeoutMs : limits.permissionTimeoutMs;
|
|
2003
2067
|
const pending = {
|
|
2004
2068
|
requestId, kind, toolName: request.toolName || null, input: request.input ?? null,
|
|
2069
|
+
protocol: request.protocol || null,
|
|
2005
2070
|
questions: Array.isArray(request.questions) ? request.questions : [], createdAt: iso(now()), expiresAt: iso(now() + timeoutMs),
|
|
2006
2071
|
};
|
|
2007
2072
|
entry.pending = [...(entry.pending || []), pending];
|
|
2008
2073
|
entry.state = 'awaiting_permission';
|
|
2009
2074
|
entry.turnState = 'awaiting_permission';
|
|
2075
|
+
// Install the resolver before publishing: a synchronous listener can answer at once.
|
|
2076
|
+
const timer = setTimeout(() => finishPending(runId, requestId, { behavior: 'deny', message: `No decision within ${Math.round(timeoutMs / 60000)} minutes.`, resolvedBy: 'timeout' }), timeoutMs);
|
|
2077
|
+
timer.unref?.();
|
|
2078
|
+
const abortSignal = request.signal;
|
|
2079
|
+
const onAbort = () => finishPending(runId, requestId, { behavior: 'deny', message: 'Run aborted.', resolvedBy: 'abort' });
|
|
2080
|
+
liveState.pendingResolvers.set(requestId, { resolve: resolvePermission, timer, cleanup: () => abortSignal?.removeEventListener?.('abort', onAbort) });
|
|
2081
|
+
abortSignal?.addEventListener?.('abort', onAbort, { once: true });
|
|
2010
2082
|
// Waiting on the answer is not work: the request's own timeout bounds it.
|
|
2011
2083
|
pauseWork(entry, liveState);
|
|
2084
|
+
liveState.ctx?.setPendingRequests?.(entry.pending);
|
|
2012
2085
|
liveState.ctx?.setTurnState?.('awaiting_permission');
|
|
2013
2086
|
liveState.ctx?.emit?.({ type: 'synabun.permission_request', requestId, kind, toolName: pending.toolName, input: pending.input, questions: pending.questions });
|
|
2014
|
-
const timer = setTimeout(() => finishPending(runId, requestId, { behavior: 'deny', message: `No decision within ${Math.round(timeoutMs / 60000)} minutes.`, resolvedBy: 'timeout' }), timeoutMs);
|
|
2015
|
-
timer.unref?.();
|
|
2016
|
-
liveState.pendingResolvers.set(requestId, { resolve: resolvePermission, timer });
|
|
2017
2087
|
try { broadcastSync({ type: 'assistant:permission-request', runId, assistantSessionId: entry.assistantSessionId, provider: entry.provider, request_id: requestId, request: pending }); } catch {}
|
|
2018
2088
|
emit(entry, kind === 'question' ? 'needs_input' : 'permission_request', { request: pending });
|
|
2019
|
-
const abortSignal = request.signal;
|
|
2020
|
-
if (abortSignal && typeof abortSignal.addEventListener === 'function') {
|
|
2021
|
-
abortSignal.addEventListener('abort', () => finishPending(runId, requestId, { behavior: 'deny', message: 'Run aborted.', resolvedBy: 'abort' }), { once: true });
|
|
2022
|
-
}
|
|
2023
2089
|
}),
|
|
2024
2090
|
};
|
|
2025
2091
|
}
|
|
@@ -2030,10 +2096,12 @@ export function createAssistantDispatcher({
|
|
|
2030
2096
|
const resolver = liveState.pendingResolvers.get(requestId);
|
|
2031
2097
|
if (!resolver) return false;
|
|
2032
2098
|
clearTimeout(resolver.timer);
|
|
2099
|
+
resolver.cleanup?.();
|
|
2033
2100
|
liveState.pendingResolvers.delete(requestId);
|
|
2034
2101
|
const request = (entry.pending || []).find((item) => item.requestId === requestId) || null;
|
|
2035
2102
|
entry.pending = (entry.pending || []).filter((item) => item.requestId !== requestId);
|
|
2036
|
-
|
|
2103
|
+
liveState.ctx?.setPendingRequests?.(entry.pending);
|
|
2104
|
+
if (entry.state === 'awaiting_permission' && entry.pending.length === 0 && !liveState.dead && !liveState.suspending && !liveState.abortReason) {
|
|
2037
2105
|
entry.state = 'running';
|
|
2038
2106
|
entry.turnState = 'running';
|
|
2039
2107
|
startWork(entry, liveState);
|
|
@@ -2056,9 +2124,14 @@ export function createAssistantDispatcher({
|
|
|
2056
2124
|
throw new DispatchError(known ? 'PERMISSION_ALREADY_RESOLVED' : 'NO_PENDING_REQUEST', known ? `Request ${requestId} was already resolved` : `No pending request ${requestId} on run ${runId}`, { status: 409 });
|
|
2057
2125
|
}
|
|
2058
2126
|
const behavior = response.behavior === 'allow' || response.decision === 'allow' || response.decision === 'answer' ? 'allow' : 'deny';
|
|
2127
|
+
// Antigravity saves an "always" answer for every later session of the user's, outside the run: a worker's
|
|
2128
|
+
// answer is for this request only.
|
|
2129
|
+
const always = entry.provider === 'antigravity' ? false : !!response.always;
|
|
2130
|
+
try { validateWorkerReply((entry.pending || []).find((p) => p.requestId === requestId), { ...response, behavior }); }
|
|
2131
|
+
catch (error) { throw new DispatchError('INVALID_QUESTION_ANSWER', error.message); }
|
|
2059
2132
|
finishPending(runId, requestId, {
|
|
2060
|
-
behavior, updatedInput: response.updatedInput, message: response.message || response.note, always
|
|
2061
|
-
answers: response.answers, resolvedBy: origin,
|
|
2133
|
+
behavior, updatedInput: response.updatedInput, message: response.message || response.note, always,
|
|
2134
|
+
answers: response.answers ?? response.updatedInput?.answers, result: response.result, content: response.content, action: response.action, error: response.error, resolvedBy: origin,
|
|
2062
2135
|
}, { origin });
|
|
2063
2136
|
return view(entry);
|
|
2064
2137
|
}
|
|
@@ -2105,6 +2178,7 @@ export function createAssistantDispatcher({
|
|
|
2105
2178
|
liveState.turnMeteredUsd = 0;
|
|
2106
2179
|
let raw = await inner.runTurn(prompt, { ...meta, turn: turnNumber, outputSchema: entry.outputSchema || undefined });
|
|
2107
2180
|
if (entry.provider === 'codex') settleCodexTurn(entry, liveState, raw);
|
|
2181
|
+
else if (entry.provider === 'antigravity') settleAntigravityTurn(entry, liveState, raw);
|
|
2108
2182
|
let text = typeof raw?.text === 'string' ? raw.text : '';
|
|
2109
2183
|
const evalRun = isEvalRun(entry);
|
|
2110
2184
|
let parsed = evalRun ? { found: false } : parseResultContract(text);
|
|
@@ -2142,6 +2216,7 @@ export function createAssistantDispatcher({
|
|
|
2142
2216
|
if (retryParsed.found) { parsed = { ...retryParsed, source: 'retry' }; text = `${text}\n\n${retryText}`; }
|
|
2143
2217
|
retryCost = accumulateCost(entry, retry);
|
|
2144
2218
|
if (entry.provider === 'codex') settleCodexTurn(entry, liveState, retry);
|
|
2219
|
+
else if (entry.provider === 'antigravity') settleAntigravityTurn(entry, liveState, retry);
|
|
2145
2220
|
} catch (error) {
|
|
2146
2221
|
// Stopped during the retry: the run ends here (handleTurnError books the retry), with no
|
|
2147
2222
|
// turn_completed or follow-up after it. The first provider turn was paid for: book it first.
|
|
@@ -2149,6 +2224,7 @@ export function createAssistantDispatcher({
|
|
|
2149
2224
|
if (liveState.abortReason || liveState.suspending) { accumulateCost(entry, raw); throw error; }
|
|
2150
2225
|
retryCost = accumulateCost(entry, error);
|
|
2151
2226
|
if (entry.provider === 'codex') settleCodexTurn(entry, liveState, error);
|
|
2227
|
+
else if (entry.provider === 'antigravity') settleAntigravityTurn(entry, liveState, error);
|
|
2152
2228
|
entry.notes.push(`result retry failed: ${error?.message || error}`);
|
|
2153
2229
|
}
|
|
2154
2230
|
}
|
|
@@ -2174,7 +2250,8 @@ export function createAssistantDispatcher({
|
|
|
2174
2250
|
liveState.turnOpen = false;
|
|
2175
2251
|
stopUsagePolling(liveState);
|
|
2176
2252
|
let turnUsage = raw?.usage || null;
|
|
2177
|
-
|
|
2253
|
+
// Codex and Antigravity: what their provider turns (the retry included) reported, in provider keys.
|
|
2254
|
+
if (entry.provider === 'codex' || entry.provider === 'antigravity') turnUsage = liveState.turnUsage || null;
|
|
2178
2255
|
else if (entry.provider === 'opencode') turnUsage = (await settleOpenCodeTurn(entry, liveState)) || turnUsage;
|
|
2179
2256
|
// A reported cost below an earlier turn's estimate can make this negative: a turn never reads below $0.
|
|
2180
2257
|
const turnCost = Math.max(0, accumulateCost(entry, raw) + retryCost + (Number(liveState.turnMeteredUsd) || 0));
|
|
@@ -2383,6 +2460,8 @@ export function createAssistantDispatcher({
|
|
|
2383
2460
|
if (entry.provider === 'codex') settleCodexTurn(entry, liveState, error);
|
|
2384
2461
|
// OpenCode: what streamed before the failure, as it stands (its serve may be gone: no reconcile).
|
|
2385
2462
|
else if (entry.provider === 'opencode') bookOpenCodeTurn(entry, liveState, { reconciled: false });
|
|
2463
|
+
// Antigravity: a stopped turn's answer still carried its tokens (the adapter puts them on the abort).
|
|
2464
|
+
else if (entry.provider === 'antigravity') settleAntigravityTurn(entry, liveState, error);
|
|
2386
2465
|
announceUnpriced(entry);
|
|
2387
2466
|
checkSession(entry.assistantSessionId, { source: entry });
|
|
2388
2467
|
if (budgetStopReason(entry.completionReason)) return { providerSessionId: null };
|
|
@@ -2465,7 +2544,8 @@ export function createAssistantDispatcher({
|
|
|
2465
2544
|
if (!raw) return 0;
|
|
2466
2545
|
const cost = Number(raw.costUsd);
|
|
2467
2546
|
const added = bookCost(entry, cost, raw.basis || 'reported');
|
|
2468
|
-
|
|
2547
|
+
// Codex and Antigravity usage reaches entry.usage when it is settled (settleCodexTurn, settleAntigravityTurn).
|
|
2548
|
+
if (entry.provider !== 'codex' && entry.provider !== 'antigravity') addLegacyUsage(entry, raw.usage);
|
|
2469
2549
|
return added;
|
|
2470
2550
|
}
|
|
2471
2551
|
|
|
@@ -2752,6 +2832,38 @@ export function createAssistantDispatcher({
|
|
|
2752
2832
|
persist({ force: true });
|
|
2753
2833
|
}
|
|
2754
2834
|
|
|
2835
|
+
// Antigravity: each provider turn's answer carries that turn's own ACP usage (the adapter takes it off the
|
|
2836
|
+
// session's running totals; a failed or stopped turn has none, so the next answer carries it). Tokens only:
|
|
2837
|
+
// Antigravity is billed by its Google sign-in and reports no price, so every row is unpriced, never $0.
|
|
2838
|
+
/**
|
|
2839
|
+
* Book one Antigravity provider turn: its five token classes into the ledger (exact when the agent's own total
|
|
2840
|
+
* agrees with one reading of its classes, else partial: 'antigravity-total-mismatch'), its usage in provider keys
|
|
2841
|
+
* into the run's and the turn's legacy totals, and the run marked unpriced. Nothing without usage.
|
|
2842
|
+
*/
|
|
2843
|
+
function settleAntigravityTurn(entry, liveState, raw) {
|
|
2844
|
+
if (entry.usageState?.closed) return;
|
|
2845
|
+
const usage = raw?.usage && typeof raw.usage === 'object' ? raw.usage : null;
|
|
2846
|
+
if (!usage) return;
|
|
2847
|
+
const { tokens, mismatch } = antigravityUsageTokens(usage);
|
|
2848
|
+
if (totalTokens(tokens) === 0) return;
|
|
2849
|
+
const used = { input_tokens: tokens.input, output_tokens: tokens.output, reasoning_tokens: tokens.reasoning, cache_read_input_tokens: tokens.cacheRead, cache_write_input_tokens: tokens.cacheWrite };
|
|
2850
|
+
addLegacyUsage(entry, used);
|
|
2851
|
+
if (liveState) {
|
|
2852
|
+
const prev = liveState.turnUsage || {};
|
|
2853
|
+
liveState.turnUsage = Object.fromEntries(Object.entries(used).map(([key, value]) => [key, (Number(prev[key]) || 0) + value]));
|
|
2854
|
+
}
|
|
2855
|
+
markUnpriced(entry, UNPRICED_PROVIDER_REASON);
|
|
2856
|
+
// No model named: the one the session runs (the adapter reads it off the session's config options).
|
|
2857
|
+
let model = entry.model || null;
|
|
2858
|
+
if (!model) { try { model = liveState?.inner?.describe?.()?.model || null; } catch { model = null; } }
|
|
2859
|
+
settleUsage(entry, {
|
|
2860
|
+
model, part: 'main', tokens, costUsd: null, costBasis: 'unpriced',
|
|
2861
|
+
fidelity: mismatch ? 'partial' : 'exact', reason: mismatch ? 'antigravity-total-mismatch' : null, source: 'antigravity-acp-usage',
|
|
2862
|
+
});
|
|
2863
|
+
// At once, not debounced: a kill must not leave the saved run behind the ledger.
|
|
2864
|
+
persist({ force: true });
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2755
2867
|
// OpenCode: every session of the run's own serve (the root and the `task` tool's child sessions).
|
|
2756
2868
|
function openCodeMeterOf(entry) {
|
|
2757
2869
|
const held = metersOf(entry);
|
|
@@ -3054,6 +3166,7 @@ export function createAssistantDispatcher({
|
|
|
3054
3166
|
entry.suspendReason = String(reason || 'server_shutdown');
|
|
3055
3167
|
delete entry.crashed;
|
|
3056
3168
|
// An open permission request goes with the process: the resumed worker asks again.
|
|
3169
|
+
for (const requestId of [...liveState.pendingResolvers.keys()]) finishPending(entry.runId, requestId, { behavior: 'deny', message: 'Server restarting; ask again after resume.', resolvedBy: 'server_restart' });
|
|
3057
3170
|
entry.pending = [];
|
|
3058
3171
|
// What the cut-off turn spent so far is booked now (an estimate per call the stream showed: no result will
|
|
3059
3172
|
// count them); the meter takes the resumed CLI's counters as its new baseline (rebaseClaudeUsage).
|
|
@@ -3245,14 +3358,14 @@ export function createAssistantDispatcher({
|
|
|
3245
3358
|
const state = runState(entry, {
|
|
3246
3359
|
browserSessionId, browserTabId, desktopGrant,
|
|
3247
3360
|
startedAt: entry.startedAt || iso(now()),
|
|
3248
|
-
extra: { resumeSessionId: entry.providerSessionId, resumeThreadId: entry.providerThreadId, costBaseUsd, resumingWorker: true },
|
|
3361
|
+
extra: { resumeSessionId: entry.providerSessionId, resumeThreadId: entry.providerThreadId, costBaseUsd, resumingWorker: true, antigravityHome: entry.resumeInfo?.antigravityHome || null },
|
|
3249
3362
|
});
|
|
3250
3363
|
// The CLI's spend stop (--max-budget-usd) counts from the total the resumed CLI restores (costBaseUsd): what the run
|
|
3251
3364
|
// may still spend (its ceiling less what it spent, never more than the session has left) goes on top of that. A
|
|
3252
3365
|
// backstop only: the dispatcher holds the run to its ceiling and its share between calls.
|
|
3253
3366
|
const allowanceUsd = claudeRunCap(entry, { spentUsd: entry.costUsd });
|
|
3254
3367
|
if (Number(allowanceUsd) > 0) state.maxBudgetUsd = Number((Number(allowanceUsd) + Math.max(0, Number(costBaseUsd) || 0)).toFixed(2));
|
|
3255
|
-
|
|
3368
|
+
state.permissionBroker = createBroker(entry.runId);
|
|
3256
3369
|
const at = clockTime(entry.suspendedAt);
|
|
3257
3370
|
const selfRestarts = isInside(entry.cwd, PACKAGE_ROOT);
|
|
3258
3371
|
const playbook = mode === 'fresh' ? playbookFor(entry) : null;
|
|
@@ -3511,6 +3624,57 @@ export function createAssistantDispatcher({
|
|
|
3511
3624
|
}
|
|
3512
3625
|
processQueue().catch(() => {});
|
|
3513
3626
|
}
|
|
3627
|
+
/** One transcript line into a live run's ring (bounded by lines and characters). */
|
|
3628
|
+
function pushRingLine(liveState, line) {
|
|
3629
|
+
liveState.ring.push(line);
|
|
3630
|
+
liveState.ringChars += line.length;
|
|
3631
|
+
// The message line still growing is no longer the last one.
|
|
3632
|
+
liveState.messageLine = null;
|
|
3633
|
+
while (liveState.ring.length > limits.ringSize || liveState.ringChars > limits.ringChars) {
|
|
3634
|
+
const removed = liveState.ring.shift();
|
|
3635
|
+
liveState.ringChars -= removed?.length || 0;
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
/**
|
|
3639
|
+
* An Antigravity message chunk (ACP agent_message_chunk): the text joins the message's line when that line is
|
|
3640
|
+
* still the ring's last one (same messageId), else it starts a new "assistant:" line. Clipped as every line is.
|
|
3641
|
+
*/
|
|
3642
|
+
function growMessageLine(liveState, update = {}) {
|
|
3643
|
+
const text = update?.content?.type === 'text' ? String(update.content.text || '') : '';
|
|
3644
|
+
if (!text) return;
|
|
3645
|
+
const id = update.messageId ?? null;
|
|
3646
|
+
const open = liveState.messageLine;
|
|
3647
|
+
if (open && open.id === id && liveState.ring.length && liveState.ring[liveState.ring.length - 1] === open.line) {
|
|
3648
|
+
open.text += text;
|
|
3649
|
+
const line = `${open.stamp} assistant: ${clip(open.text, 200)}`;
|
|
3650
|
+
if (line !== open.line) {
|
|
3651
|
+
liveState.ring[liveState.ring.length - 1] = line;
|
|
3652
|
+
liveState.ringChars += line.length - open.line.length;
|
|
3653
|
+
open.line = line;
|
|
3654
|
+
}
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3657
|
+
if (!text.trim()) return;
|
|
3658
|
+
const stamp = iso(now()).slice(11, 19);
|
|
3659
|
+
const line = `${stamp} assistant: ${clip(text, 200)}`;
|
|
3660
|
+
pushRingLine(liveState, line);
|
|
3661
|
+
liveState.messageLine = { id, text, stamp, line };
|
|
3662
|
+
}
|
|
3663
|
+
/**
|
|
3664
|
+
* An ACP tool_call_update carries only what changed: the call it updates, as the run has seen it so far, so its
|
|
3665
|
+
* end is said with the call's kind, command and title. Other payloads pass as they are. Bounded per run.
|
|
3666
|
+
*/
|
|
3667
|
+
function withAcpCall(liveState, payload) {
|
|
3668
|
+
if ((payload.eventType !== 'tool_call' && payload.eventType !== 'tool_call_update') || !payload.event?.toolCallId) return payload;
|
|
3669
|
+
const calls = liveState.acpCalls || (liveState.acpCalls = new Map());
|
|
3670
|
+
const id = String(payload.event.toolCallId);
|
|
3671
|
+
const call = { ...(calls.get(id) || {}) };
|
|
3672
|
+
for (const [key, value] of Object.entries(payload.event)) if (value !== undefined && value !== null) call[key] = value;
|
|
3673
|
+
calls.delete(id);
|
|
3674
|
+
calls.set(id, call);
|
|
3675
|
+
if (calls.size > 200) calls.delete(calls.keys().next().value);
|
|
3676
|
+
return { ...payload, event: call };
|
|
3677
|
+
}
|
|
3514
3678
|
function onRuntimeMessage(message) {
|
|
3515
3679
|
const runId = message?.run?.runId;
|
|
3516
3680
|
if (!runId || !entries.has(runId)) return;
|
|
@@ -3529,7 +3693,9 @@ export function createAssistantDispatcher({
|
|
|
3529
3693
|
// trigger reading (lib/skills/eval-trace.js; read with transcript format 'tools').
|
|
3530
3694
|
if (isEvalRun(entry)) { try { recordEvalCalls(liveState, message.event || {}); } catch {} }
|
|
3531
3695
|
if (entry.provider === 'opencode') { try { meterOpenCode(entry, liveState, message.event || {}); } catch (error) { log(runId, 'assistant:meter-error', error?.message || String(error)); } }
|
|
3532
|
-
|
|
3696
|
+
// Antigravity streams each message in small chunks: one transcript line per message, grown as it streams.
|
|
3697
|
+
if (entry.provider === 'antigravity' && message.event?.eventType === 'agent_message_chunk') { growMessageLine(liveState, message.event.event); return; }
|
|
3698
|
+
const summary = summarizeProviderEvent(entry.provider === 'antigravity' ? withAcpCall(liveState, message.event || {}) : message.event || {});
|
|
3533
3699
|
// A guard decision (lib/worker-confinement.js) is also a note of the run: what was refused or warned about,
|
|
3534
3700
|
// with its reason, so the brain and the panel see it without the transcript. The first twenty.
|
|
3535
3701
|
if ((message.event?.eventType || message.event?.event?.type) === 'synabun.confinement') {
|
|
@@ -3537,15 +3703,7 @@ export function createAssistantDispatcher({
|
|
|
3537
3703
|
if (count <= 20) entry.notes.push(`guard ${message.event.event?.action || 'refused'}: ${clip(summary, 300)}`);
|
|
3538
3704
|
else if (count === 21) entry.notes.push('guard: more decisions follow in the transcript');
|
|
3539
3705
|
}
|
|
3540
|
-
if (summary) {
|
|
3541
|
-
const line = `${iso(now()).slice(11, 19)} ${summary}`;
|
|
3542
|
-
liveState.ring.push(line);
|
|
3543
|
-
liveState.ringChars += line.length;
|
|
3544
|
-
while (liveState.ring.length > limits.ringSize || liveState.ringChars > limits.ringChars) {
|
|
3545
|
-
const removed = liveState.ring.shift();
|
|
3546
|
-
liveState.ringChars -= removed?.length || 0;
|
|
3547
|
-
}
|
|
3548
|
-
}
|
|
3706
|
+
if (summary) pushRingLine(liveState, `${iso(now()).slice(11, 19)} ${summary}`);
|
|
3549
3707
|
}
|
|
3550
3708
|
return;
|
|
3551
3709
|
}
|
|
@@ -3556,13 +3714,16 @@ export function createAssistantDispatcher({
|
|
|
3556
3714
|
persist({ force: true });
|
|
3557
3715
|
emit(entry, 'tools_changed');
|
|
3558
3716
|
}
|
|
3559
|
-
|
|
3717
|
+
const runHome = run.antigravityHome || entry.resumeInfo?.antigravityHome || entry.antigravityHome || null;
|
|
3718
|
+
entry.runtime = { status: run.status, turnState: run.turnState, stoppedReason: run.stoppedReason, error: run.error, claimedBy: run.claimedBy, providerSessionId: run.providerSessionId, providerThreadId: run.providerThreadId, ...(runHome ? { antigravityHome: runHome } : {}) };
|
|
3560
3719
|
// A new provider identity is saved at once: a crash before the run's next write (its first alive mark comes up to
|
|
3561
3720
|
// a minute later) would otherwise leave no session to resume.
|
|
3562
3721
|
const newIdentity = (run.providerSessionId && run.providerSessionId !== entry.providerSessionId)
|
|
3563
|
-
|| (run.providerThreadId && run.providerThreadId !== entry.providerThreadId)
|
|
3722
|
+
|| (run.providerThreadId && run.providerThreadId !== entry.providerThreadId)
|
|
3723
|
+
|| (run.antigravityHome && run.antigravityHome !== entry.antigravityHome);
|
|
3564
3724
|
if (run.providerSessionId) entry.providerSessionId = run.providerSessionId;
|
|
3565
3725
|
if (run.providerThreadId) entry.providerThreadId = run.providerThreadId;
|
|
3726
|
+
if (run.antigravityHome) entry.antigravityHome = run.antigravityHome;
|
|
3566
3727
|
if (newIdentity) persist({ force: true });
|
|
3567
3728
|
if (message.type === 'sidepanel:run-updated' && message.reason === 'started' && entry.state === 'starting') {
|
|
3568
3729
|
entry.state = 'running';
|