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
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun — Antigravity panel: what an ACP session says, as a transcript (no DOM)
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// The server forwards every ACP `session/update` of a tab's session as it came
|
|
6
|
+
// (lib/antigravity/panel-bridge.js). This module folds them into one state the
|
|
7
|
+
// renderer draws: rows in order (user, agent, thought, tool, plan, notice,
|
|
8
|
+
// compaction, subagent, message), the tool calls by id (a `tool_call_update`
|
|
9
|
+
// changes its row in place), turns (a user message and what answered it, with
|
|
10
|
+
// its usage, stop reason and duration), the agent's slash commands, mode, config
|
|
11
|
+
// options, title, context gauge and token usage, and the live output of the
|
|
12
|
+
// terminals SynaBun runs for the agent. Imported by the page and by `node --test`;
|
|
13
|
+
// it never touches the DOM.
|
|
14
|
+
|
|
15
|
+
import { stripGreetingNote } from './agy-greeting.js';
|
|
16
|
+
|
|
17
|
+
export const ROW_KINDS = Object.freeze(['user', 'agent', 'thought', 'tool', 'plan', 'notice', 'compaction', 'subagent', 'message']);
|
|
18
|
+
const MAX_ROWS = 2000;
|
|
19
|
+
const MAX_TURNS = 500;
|
|
20
|
+
const MAX_TEXT = 400_000;
|
|
21
|
+
const MAX_TERMINAL = 256 * 1024;
|
|
22
|
+
|
|
23
|
+
const now = () => Date.now();
|
|
24
|
+
|
|
25
|
+
export function createTranscript() {
|
|
26
|
+
return {
|
|
27
|
+
rows: [],
|
|
28
|
+
tools: new Map(), // toolCallId -> row
|
|
29
|
+
plans: new Map(), // planId -> row (plan_update); the older `plan` update has no id
|
|
30
|
+
compactions: new Map(), // compactionId -> row
|
|
31
|
+
subagents: new Map(), // child sessionId -> row
|
|
32
|
+
messages: new Map(), // messageId -> row (session_message)
|
|
33
|
+
terminals: new Map(), // terminalId -> { output, truncated, exitStatus }
|
|
34
|
+
turns: [], // { id, userRowId, startedAt, endedAt, usage, stopReason, replay }
|
|
35
|
+
turn: null, // the id of the turn rows are added to
|
|
36
|
+
commands: [],
|
|
37
|
+
modes: null, // { currentModeId, availableModes }
|
|
38
|
+
configOptions: [],
|
|
39
|
+
title: null,
|
|
40
|
+
context: null, // { used, size, cost }
|
|
41
|
+
usage: null, // last turn's ACP usage block
|
|
42
|
+
plan: null,
|
|
43
|
+
running: false,
|
|
44
|
+
seq: 0,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function textOf(content) {
|
|
49
|
+
if (!content || typeof content !== 'object') return '';
|
|
50
|
+
if (content.type === 'text') return typeof content.text === 'string' ? content.text : '';
|
|
51
|
+
if (content.type === 'resource_link') return `[${content.name || content.uri || 'file'}]`;
|
|
52
|
+
if (content.type === 'resource') return content.resource?.text ? String(content.resource.text) : `[${content.resource?.uri || 'resource'}]`;
|
|
53
|
+
if (content.type === 'image') return '[image]';
|
|
54
|
+
if (content.type === 'audio') return '[audio]';
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function attachmentOf(content) {
|
|
59
|
+
if (!content || typeof content !== 'object') return null;
|
|
60
|
+
if (content.type === 'resource_link') return { kind: 'link', name: content.name || content.uri, uri: content.uri, mimeType: content.mimeType || '' };
|
|
61
|
+
if (content.type === 'image') return { kind: 'image', mimeType: content.mimeType || 'image/png', data: content.data || '', uri: content.uri || '' };
|
|
62
|
+
if (content.type === 'audio') return { kind: 'audio', mimeType: content.mimeType || '', name: 'audio' };
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const blocksText = (blocks) => (Array.isArray(blocks) ? blocks.map(textOf).filter(Boolean).join('') : '');
|
|
67
|
+
|
|
68
|
+
function turnById(state, id) {
|
|
69
|
+
if (!id) return null;
|
|
70
|
+
for (let i = state.turns.length - 1; i >= 0; i -= 1) if (state.turns[i].id === id) return state.turns[i];
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function startTurn(state, { userRowId = null, replay = false } = {}) {
|
|
75
|
+
state.seq += 1;
|
|
76
|
+
const turn = { id: `t${state.seq}`, userRowId, startedAt: replay ? null : now(), endedAt: null, usage: null, stopReason: null, replay };
|
|
77
|
+
state.turns.push(turn);
|
|
78
|
+
if (state.turns.length > MAX_TURNS) state.turns.splice(0, state.turns.length - MAX_TURNS);
|
|
79
|
+
state.turn = turn.id;
|
|
80
|
+
return turn;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function pushRow(state, row) {
|
|
84
|
+
state.seq += 1;
|
|
85
|
+
const next = { id: `r${state.seq}`, turn: state.turn, ...row };
|
|
86
|
+
state.rows.push(next);
|
|
87
|
+
if (state.rows.length > MAX_ROWS) {
|
|
88
|
+
const dropped = state.rows.splice(0, state.rows.length - MAX_ROWS);
|
|
89
|
+
for (const old of dropped) {
|
|
90
|
+
if (old.kind === 'tool') state.tools.delete(old.toolCallId);
|
|
91
|
+
else if (old.kind === 'plan' && old.planId) state.plans.delete(old.planId);
|
|
92
|
+
else if (old.kind === 'compaction') state.compactions.delete(old.compactionId);
|
|
93
|
+
else if (old.kind === 'subagent') state.subagents.delete(old.sessionId);
|
|
94
|
+
else if (old.kind === 'message') state.messages.delete(old.messageId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return next;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function closeRow(row) {
|
|
101
|
+
if (!row || row.closed) return;
|
|
102
|
+
row.closed = true;
|
|
103
|
+
if (row.kind === 'thought' && row.startedAt && !row.endedAt) row.endedAt = now();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Appends a streamed chunk to the last row of the same kind, or starts a new one. */
|
|
107
|
+
// A user message as Antigravity keeps it may hold the greeting's note (agy-greeting.js): never shown.
|
|
108
|
+
const shownText = (kind, value) => (kind === 'user' ? stripGreetingNote(value) : value);
|
|
109
|
+
|
|
110
|
+
function appendChunk(state, kind, content, { replay = false } = {}) {
|
|
111
|
+
const text = textOf(content);
|
|
112
|
+
const attachment = attachmentOf(content);
|
|
113
|
+
const last = state.rows[state.rows.length - 1];
|
|
114
|
+
if (last && last.kind === kind && !last.closed) {
|
|
115
|
+
if (text && content?.type === 'text') last.text = shownText(kind, last.text + text).slice(-MAX_TEXT);
|
|
116
|
+
if (attachment) (last.attachments ||= []).push(attachment);
|
|
117
|
+
last.replay = last.replay && replay;
|
|
118
|
+
return last;
|
|
119
|
+
}
|
|
120
|
+
// agy echoes a typed prompt back as live user_message_chunk updates: the row
|
|
121
|
+
// addUserMessage drew is the message, so its echo is not a second one.
|
|
122
|
+
if (kind === 'user' && !replay && last?.kind === 'user' && last.typed && last.turn === state.turn) return last;
|
|
123
|
+
if (last && last.kind !== kind) closeRow(last);
|
|
124
|
+
if (kind === 'user') {
|
|
125
|
+
// A user message the agent replays starts a turn of its own, as a typed one does.
|
|
126
|
+
const turn = startTurn(state, { replay });
|
|
127
|
+
const row = pushRow(state, { kind, text: content?.type === 'text' ? shownText(kind, text).slice(-MAX_TEXT) : '', attachments: attachment ? [attachment] : [], replay });
|
|
128
|
+
turn.userRowId = row.id;
|
|
129
|
+
return row;
|
|
130
|
+
}
|
|
131
|
+
return pushRow(state, {
|
|
132
|
+
kind,
|
|
133
|
+
text: content?.type === 'text' ? text.slice(-MAX_TEXT) : '',
|
|
134
|
+
attachments: attachment ? [attachment] : [],
|
|
135
|
+
replay,
|
|
136
|
+
...(kind === 'thought' && !replay ? { startedAt: now(), endedAt: null } : {}),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Closes the streaming row so the next chunk starts a new bubble (a turn boundary). */
|
|
141
|
+
export function closeOpenRow(state) {
|
|
142
|
+
const last = state.rows[state.rows.length - 1];
|
|
143
|
+
if (last && (last.kind === 'agent' || last.kind === 'thought' || last.kind === 'user' || last.kind === 'message')) closeRow(last);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function mergeToolContent(content) {
|
|
147
|
+
if (!Array.isArray(content)) return [];
|
|
148
|
+
return content.map((item) => {
|
|
149
|
+
if (!item || typeof item !== 'object') return null;
|
|
150
|
+
if (item.type === 'diff') return { type: 'diff', path: String(item.path || ''), oldText: item.oldText ?? null, newText: String(item.newText ?? '') };
|
|
151
|
+
if (item.type === 'terminal') return { type: 'terminal', terminalId: String(item.terminalId || '') };
|
|
152
|
+
if (item.type === 'content') return { type: 'content', text: textOf(item.content), attachment: attachmentOf(item.content) };
|
|
153
|
+
return null;
|
|
154
|
+
}).filter(Boolean);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const FINAL_TOOL = new Set(['completed', 'failed']);
|
|
158
|
+
|
|
159
|
+
function applyTool(state, update, { replay = false } = {}) {
|
|
160
|
+
const id = typeof update.toolCallId === 'string' ? update.toolCallId : '';
|
|
161
|
+
if (!id) return null;
|
|
162
|
+
let row = state.tools.get(id);
|
|
163
|
+
if (!row) {
|
|
164
|
+
closeOpenRow(state);
|
|
165
|
+
row = pushRow(state, { kind: 'tool', toolCallId: id, title: '', name: '', toolKind: 'other', status: 'pending', content: [], locations: [], rawInput: null, rawOutput: null, replay, startedAt: replay ? null : now(), endedAt: null });
|
|
166
|
+
state.tools.set(id, row);
|
|
167
|
+
}
|
|
168
|
+
if (typeof update.title === 'string' && update.title) row.title = update.title;
|
|
169
|
+
if (typeof update.name === 'string' && update.name) row.name = update.name;
|
|
170
|
+
if (typeof update.kind === 'string' && update.kind) row.toolKind = update.kind;
|
|
171
|
+
if (typeof update.status === 'string' && update.status) {
|
|
172
|
+
row.status = update.status;
|
|
173
|
+
if (FINAL_TOOL.has(update.status) && row.startedAt && !row.endedAt) row.endedAt = now();
|
|
174
|
+
}
|
|
175
|
+
if (Array.isArray(update.content)) row.content = mergeToolContent(update.content);
|
|
176
|
+
if (Array.isArray(update.locations)) row.locations = update.locations.filter((l) => l && typeof l.path === 'string').map((l) => ({ path: l.path, line: Number.isInteger(l.line) ? l.line : null }));
|
|
177
|
+
if (update.rawInput !== undefined) row.rawInput = update.rawInput;
|
|
178
|
+
if (update.rawOutput !== undefined) row.rawOutput = update.rawOutput;
|
|
179
|
+
return row;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const PLAN_ENTRY = (e) => e && typeof e.content === 'string';
|
|
183
|
+
const planEntries = (entries) => (Array.isArray(entries) ? entries.filter(PLAN_ENTRY).map((e) => ({ content: e.content, priority: e.priority || 'medium', status: e.status || 'pending' })) : []);
|
|
184
|
+
|
|
185
|
+
function applyPlanUpdate(state, plan, { replay = false } = {}) {
|
|
186
|
+
if (!plan || typeof plan !== 'object' || typeof plan.planId !== 'string' || !plan.planId) return null;
|
|
187
|
+
const fields = plan.type === 'items'
|
|
188
|
+
? { format: 'items', entries: planEntries(plan.entries) }
|
|
189
|
+
: plan.type === 'markdown'
|
|
190
|
+
? { format: 'markdown', content: String(plan.content ?? '').slice(0, MAX_TEXT) }
|
|
191
|
+
: plan.type === 'file'
|
|
192
|
+
? { format: 'file', uri: String(plan.uri || '') }
|
|
193
|
+
: null;
|
|
194
|
+
if (!fields) return null;
|
|
195
|
+
let row = state.plans.get(plan.planId);
|
|
196
|
+
if (row) Object.assign(row, { entries: undefined, content: undefined, uri: undefined }, fields);
|
|
197
|
+
else {
|
|
198
|
+
closeOpenRow(state);
|
|
199
|
+
row = pushRow(state, { kind: 'plan', planId: plan.planId, replay, ...fields });
|
|
200
|
+
state.plans.set(plan.planId, row);
|
|
201
|
+
}
|
|
202
|
+
if (fields.format === 'items') state.plan = fields.entries;
|
|
203
|
+
return row;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function removeRow(state, row) {
|
|
207
|
+
const at = state.rows.indexOf(row);
|
|
208
|
+
if (at >= 0) state.rows.splice(at, 1);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function applySubagent(state, update, { replay = false } = {}) {
|
|
212
|
+
const id = typeof update.sessionId === 'string' ? update.sessionId : '';
|
|
213
|
+
if (!id) return null;
|
|
214
|
+
let row = state.subagents.get(id);
|
|
215
|
+
if (!row) {
|
|
216
|
+
closeOpenRow(state);
|
|
217
|
+
row = pushRow(state, { kind: 'subagent', sessionId: id, title: '', description: '', state: 'unknown', cwd: '', replay });
|
|
218
|
+
state.subagents.set(id, row);
|
|
219
|
+
}
|
|
220
|
+
// Omitted means unchanged; null clears.
|
|
221
|
+
if (update.title !== undefined) row.title = typeof update.title === 'string' ? update.title : '';
|
|
222
|
+
if (update.description !== undefined) row.description = typeof update.description === 'string' ? update.description : '';
|
|
223
|
+
if (update.cwd !== undefined) row.cwd = typeof update.cwd === 'string' ? update.cwd : '';
|
|
224
|
+
if (update.state !== undefined) row.state = update.state && typeof update.state.state === 'string' ? update.state.state : 'unknown';
|
|
225
|
+
if (update.state?.usage && typeof update.state.usage === 'object') row.usage = update.state.usage;
|
|
226
|
+
return row;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Folds one ACP SessionUpdate into the transcript. Returns what changed:
|
|
231
|
+
* 'rows' (draw the transcript), 'meta' (header: mode, options, title, gauge), or null.
|
|
232
|
+
*/
|
|
233
|
+
export function applyUpdate(state, update, { replay = false } = {}) {
|
|
234
|
+
if (!update || typeof update !== 'object') return null;
|
|
235
|
+
switch (update.sessionUpdate) {
|
|
236
|
+
case 'user_message_chunk':
|
|
237
|
+
appendChunk(state, 'user', update.content, { replay });
|
|
238
|
+
return 'rows';
|
|
239
|
+
case 'agent_message_chunk':
|
|
240
|
+
appendChunk(state, 'agent', update.content, { replay });
|
|
241
|
+
return 'rows';
|
|
242
|
+
case 'agent_thought_chunk':
|
|
243
|
+
appendChunk(state, 'thought', update.content, { replay });
|
|
244
|
+
return 'rows';
|
|
245
|
+
case 'tool_call':
|
|
246
|
+
case 'tool_call_update':
|
|
247
|
+
return applyTool(state, update, { replay }) ? 'rows' : null;
|
|
248
|
+
case 'plan': {
|
|
249
|
+
const entries = planEntries(update.entries);
|
|
250
|
+
state.plan = entries;
|
|
251
|
+
const last = state.rows[state.rows.length - 1];
|
|
252
|
+
if (last?.kind === 'plan' && !last.planId) last.entries = entries;
|
|
253
|
+
else { closeOpenRow(state); pushRow(state, { kind: 'plan', format: 'items', entries, replay }); }
|
|
254
|
+
return 'rows';
|
|
255
|
+
}
|
|
256
|
+
case 'plan_update':
|
|
257
|
+
return applyPlanUpdate(state, update.plan, { replay }) ? 'rows' : null;
|
|
258
|
+
case 'plan_removed': {
|
|
259
|
+
const row = state.plans.get(update.planId);
|
|
260
|
+
if (!row) return null;
|
|
261
|
+
state.plans.delete(update.planId);
|
|
262
|
+
removeRow(state, row);
|
|
263
|
+
return 'rows';
|
|
264
|
+
}
|
|
265
|
+
case 'available_commands_update':
|
|
266
|
+
state.commands = Array.isArray(update.availableCommands) ? update.availableCommands.filter((c) => c && typeof c.name === 'string').map((c) => ({ name: c.name, description: c.description || '', hint: c.input?.hint || '' })) : [];
|
|
267
|
+
return 'meta';
|
|
268
|
+
case 'current_mode_update':
|
|
269
|
+
if (typeof update.currentModeId === 'string') state.modes = { ...(state.modes || { availableModes: [] }), currentModeId: update.currentModeId };
|
|
270
|
+
return 'meta';
|
|
271
|
+
case 'config_option_update':
|
|
272
|
+
if (Array.isArray(update.configOptions)) state.configOptions = update.configOptions;
|
|
273
|
+
return 'meta';
|
|
274
|
+
case 'session_info_update':
|
|
275
|
+
if (typeof update.title === 'string' && update.title.trim()) state.title = update.title.trim();
|
|
276
|
+
return 'meta';
|
|
277
|
+
case 'usage_update':
|
|
278
|
+
if (Number.isFinite(update.used) && Number.isFinite(update.size)) {
|
|
279
|
+
const cost = update.cost && Number.isFinite(update.cost.amount) && typeof update.cost.currency === 'string' ? { amount: update.cost.amount, currency: update.cost.currency } : null;
|
|
280
|
+
state.context = { used: update.used, size: update.size, cost };
|
|
281
|
+
}
|
|
282
|
+
return 'meta';
|
|
283
|
+
case 'notice': {
|
|
284
|
+
const text = [update.title, update.message || update.description].filter((s) => typeof s === 'string' && s).join(' — ');
|
|
285
|
+
if (text) { closeOpenRow(state); pushRow(state, { kind: 'notice', text, severity: update.severity || 'info', replay }); return 'rows'; }
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
case 'compaction_update': {
|
|
289
|
+
const id = typeof update.compactionId === 'string' ? update.compactionId : '';
|
|
290
|
+
if (!id) return null;
|
|
291
|
+
let row = state.compactions.get(id);
|
|
292
|
+
if (!row) {
|
|
293
|
+
closeOpenRow(state);
|
|
294
|
+
row = pushRow(state, { kind: 'compaction', compactionId: id, status: 'in_progress', summary: '', error: '', replay });
|
|
295
|
+
state.compactions.set(id, row);
|
|
296
|
+
}
|
|
297
|
+
if (typeof update.status === 'string' && update.status) row.status = update.status;
|
|
298
|
+
if (Array.isArray(update.summary)) row.summary = blocksText(update.summary).slice(0, MAX_TEXT);
|
|
299
|
+
if (update.error !== undefined) row.error = typeof update.error === 'string' ? update.error : '';
|
|
300
|
+
return 'rows';
|
|
301
|
+
}
|
|
302
|
+
case 'compaction_summary_chunk': {
|
|
303
|
+
const row = state.compactions.get(update.compactionId);
|
|
304
|
+
if (!row) return null;
|
|
305
|
+
row.summary = (row.summary + textOf(update.content)).slice(-MAX_TEXT);
|
|
306
|
+
return 'rows';
|
|
307
|
+
}
|
|
308
|
+
case 'subagent_update':
|
|
309
|
+
return applySubagent(state, update, { replay }) ? 'rows' : null;
|
|
310
|
+
case 'session_message':
|
|
311
|
+
case 'session_message_chunk': {
|
|
312
|
+
const id = typeof update.messageId === 'string' ? update.messageId : '';
|
|
313
|
+
if (!id) return null;
|
|
314
|
+
let row = state.messages.get(id);
|
|
315
|
+
if (!row) {
|
|
316
|
+
closeOpenRow(state);
|
|
317
|
+
row = pushRow(state, { kind: 'message', messageId: id, sender: update.senderSessionId || null, recipient: update.recipientSessionId || null, text: '', replay });
|
|
318
|
+
state.messages.set(id, row);
|
|
319
|
+
}
|
|
320
|
+
if (update.sessionUpdate === 'session_message') {
|
|
321
|
+
if (Array.isArray(update.content)) row.text = blocksText(update.content).slice(-MAX_TEXT);
|
|
322
|
+
if (update.senderSessionId !== undefined) row.sender = update.senderSessionId || null;
|
|
323
|
+
if (update.recipientSessionId !== undefined) row.recipient = update.recipientSessionId || null;
|
|
324
|
+
} else {
|
|
325
|
+
row.text = (row.text + textOf(update.content)).slice(-MAX_TEXT);
|
|
326
|
+
}
|
|
327
|
+
return 'rows';
|
|
328
|
+
}
|
|
329
|
+
default:
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** The user's own message, drawn at once (agy echoes it as live user chunks, which fold into this row). Starts a turn. */
|
|
335
|
+
export function addUserMessage(state, text, attachments = []) {
|
|
336
|
+
closeOpenRow(state);
|
|
337
|
+
const turn = startTurn(state);
|
|
338
|
+
const row = pushRow(state, { kind: 'user', text: String(text || ''), attachments: attachments.map((a) => ({ kind: a.kind || 'file', name: a.name || '', mimeType: a.mimeType || '', data: a.preview || '' })), replay: false, typed: true });
|
|
339
|
+
row.closed = true;
|
|
340
|
+
turn.userRowId = row.id;
|
|
341
|
+
return row;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** A turn ended: the streaming bubble closes; failures and refusals become a notice row; the turn keeps its usage. */
|
|
345
|
+
export function endTurn(state, { stopReason, usage, error } = {}) {
|
|
346
|
+
closeOpenRow(state);
|
|
347
|
+
state.running = false;
|
|
348
|
+
if (usage && typeof usage === 'object') state.usage = usage;
|
|
349
|
+
const turn = turnById(state, state.turn);
|
|
350
|
+
if (turn && !turn.endedAt) {
|
|
351
|
+
turn.endedAt = now();
|
|
352
|
+
turn.stopReason = stopReason || 'end_turn';
|
|
353
|
+
if (usage && typeof usage === 'object') turn.usage = usage;
|
|
354
|
+
}
|
|
355
|
+
if (stopReason === 'error' && error?.message) pushRow(state, { kind: 'notice', text: error.message, severity: 'error' });
|
|
356
|
+
else if (stopReason === 'refusal') pushRow(state, { kind: 'notice', text: 'The model declined to continue.', severity: 'warning', code: 'refusal' });
|
|
357
|
+
else if (stopReason === 'max_tokens') pushRow(state, { kind: 'notice', text: 'The reply reached the model\'s output limit.', severity: 'warning', code: 'max_tokens' });
|
|
358
|
+
else if (stopReason === 'max_turn_requests') pushRow(state, { kind: 'notice', text: 'The turn reached its request limit.', severity: 'warning', code: 'max_turn_requests' });
|
|
359
|
+
else if (stopReason === 'cancelled') pushRow(state, { kind: 'notice', text: 'Stopped.', severity: 'info', code: 'cancelled' });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Tool rows still pending or running when a turn ends did not finish: shown as stopped. */
|
|
363
|
+
export function settleOpenTools(state) {
|
|
364
|
+
for (const row of state.tools.values()) {
|
|
365
|
+
if (row.status === 'pending' || row.status === 'in_progress') {
|
|
366
|
+
row.status = 'failed';
|
|
367
|
+
if (row.startedAt && !row.endedAt) row.endedAt = now();
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
for (const row of state.compactions.values()) if (row.status === 'in_progress') row.status = 'cancelled';
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** The live output of a terminal SynaBun runs for the agent (the bridge's `terminal:output`). True when a row shows it. */
|
|
374
|
+
export function applyTerminalOutput(state, { terminalId, output, truncated, exitStatus } = {}) {
|
|
375
|
+
if (typeof terminalId !== 'string' || !terminalId) return false;
|
|
376
|
+
const text = String(output ?? '');
|
|
377
|
+
state.terminals.set(terminalId, {
|
|
378
|
+
output: text.length > MAX_TERMINAL ? text.slice(-MAX_TERMINAL) : text,
|
|
379
|
+
truncated: !!truncated || text.length > MAX_TERMINAL,
|
|
380
|
+
exitStatus: exitStatus && typeof exitStatus === 'object' ? { exitCode: Number.isInteger(exitStatus.exitCode) ? exitStatus.exitCode : null, signal: exitStatus.signal || null } : null,
|
|
381
|
+
});
|
|
382
|
+
if (state.terminals.size > 64) state.terminals.delete(state.terminals.keys().next().value);
|
|
383
|
+
for (const row of state.tools.values()) if (row.content.some((item) => item.type === 'terminal' && item.terminalId === terminalId)) return true;
|
|
384
|
+
return false;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** A one-line summary of a tool row for its header (command, path or title). */
|
|
388
|
+
export function toolSummary(row) {
|
|
389
|
+
const input = row?.rawInput && typeof row.rawInput === 'object' ? row.rawInput : {};
|
|
390
|
+
const command = typeof input.command === 'string' ? input.command : Array.isArray(input.command) ? input.command.join(' ') : '';
|
|
391
|
+
if (row?.toolKind === 'execute' && command) return command;
|
|
392
|
+
const path = row?.locations?.[0]?.path || (typeof input.path === 'string' ? input.path : '') || (typeof input.file_path === 'string' ? input.file_path : '');
|
|
393
|
+
return row?.title || path || command || row?.toolKind || 'tool';
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/** Context gauge: { percent, label, used, size, cost } from the last usage_update, or null. */
|
|
397
|
+
export function contextGauge(state) {
|
|
398
|
+
const context = state?.context;
|
|
399
|
+
if (!context || !(context.size > 0)) return null;
|
|
400
|
+
const percent = Math.max(0, Math.min(100, Math.round((context.used / context.size) * 100)));
|
|
401
|
+
return { percent, label: `${compactNumber(context.used)} / ${compactNumber(context.size)}`, used: context.used, size: context.size, cost: context.cost || null };
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** 1234 → "1.2k", 50000 → "50k", 1000000 → "1M". */
|
|
405
|
+
export function compactNumber(n) {
|
|
406
|
+
const value = Number(n) || 0;
|
|
407
|
+
if (value >= 1_000_000) return `${(value / 1_000_000).toFixed(value % 1_000_000 ? 1 : 0)}M`;
|
|
408
|
+
if (value >= 10_000) return `${Math.round(value / 1000)}k`;
|
|
409
|
+
if (value >= 1000) return `${(value / 1000).toFixed(1).replace(/\.0$/, '')}k`;
|
|
410
|
+
return String(Math.round(value));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/** The turn a row belongs to, or null. */
|
|
414
|
+
export function turnOf(state, row) {
|
|
415
|
+
return turnById(state, row?.turn);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** The rows of one turn, in order. */
|
|
419
|
+
export function rowsOfTurn(state, turnId) {
|
|
420
|
+
return state.rows.filter((row) => row.turn === turnId);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** The text of what the agent answered in a turn (its message rows, joined): what Copy copies. */
|
|
424
|
+
export function answerOfTurn(state, turnId) {
|
|
425
|
+
return rowsOfTurn(state, turnId).filter((row) => row.kind === 'agent' && row.text).map((row) => row.text).join('\n\n');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** The last thing the user asked: { text, turnId } (Retry sends it again), or null. */
|
|
429
|
+
export function lastPrompt(state) {
|
|
430
|
+
for (let i = state.rows.length - 1; i >= 0; i -= 1) {
|
|
431
|
+
const row = state.rows[i];
|
|
432
|
+
if (row.kind === 'user' && row.text && row.text.trim()) return { text: row.text, turnId: row.turn };
|
|
433
|
+
}
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** The id of the last row of every finished turn: where its footer (usage, Copy, Retry) goes. */
|
|
438
|
+
export function turnFooters(state) {
|
|
439
|
+
const last = new Map();
|
|
440
|
+
for (const row of state.rows) if (row.turn) last.set(row.turn, row.id);
|
|
441
|
+
const out = new Map();
|
|
442
|
+
for (const turn of state.turns) {
|
|
443
|
+
if (!turn.endedAt && !turn.replay) continue;
|
|
444
|
+
const rowId = last.get(turn.id);
|
|
445
|
+
if (rowId) out.set(rowId, turn);
|
|
446
|
+
}
|
|
447
|
+
return out;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** "3s", "1m 05s", or '' for a missing or negative span. */
|
|
451
|
+
export function durationLabel(startedAt, endedAt) {
|
|
452
|
+
if (!startedAt || !endedAt || endedAt < startedAt) return '';
|
|
453
|
+
const seconds = Math.round((endedAt - startedAt) / 1000);
|
|
454
|
+
if (seconds < 60) return `${Math.max(seconds, 1)}s`;
|
|
455
|
+
const minutes = Math.floor(seconds / 60);
|
|
456
|
+
return `${minutes}m ${String(seconds % 60).padStart(2, '0')}s`;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** A turn's usage as "1.2k in · 300 out · 40 thinking · 800 cached" parts, or [] when it has none. */
|
|
460
|
+
export function usageParts(usage) {
|
|
461
|
+
if (!usage || typeof usage !== 'object') return [];
|
|
462
|
+
const parts = [];
|
|
463
|
+
const add = (value, key) => { if (Number.isFinite(value) && value > 0) parts.push({ key, value: compactNumber(value) }); };
|
|
464
|
+
add(usage.inputTokens, 'in');
|
|
465
|
+
add(usage.outputTokens, 'out');
|
|
466
|
+
add(usage.thoughtTokens, 'thinking');
|
|
467
|
+
add(usage.cachedReadTokens, 'cached');
|
|
468
|
+
return parts;
|
|
469
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun — Antigravity panel: the greeting's note in a prompt (no DOM, no imports)
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// What SynaBun greeted the user with when a conversation opened (lib/greeting/) is told to the model once, with
|
|
6
|
+
// the first prompt: the bridge (lib/antigravity/panel-bridge.js) puts the note before the typed words, as a text
|
|
7
|
+
// block of its own between these markers. Antigravity keeps it in the conversation's history, so the transcript
|
|
8
|
+
// (agy-events.js) and SynaBun's session capture (mcp-server/src/services/memory-sessions.ts, which repeats the
|
|
9
|
+
// pattern) take it out again. The Codex panel does the same with markers of its own.
|
|
10
|
+
|
|
11
|
+
export const AGY_GREETING_MARKER_START = '[SYNABUN_GREETING_START]';
|
|
12
|
+
export const AGY_GREETING_MARKER_END = '[SYNABUN_GREETING_END]';
|
|
13
|
+
|
|
14
|
+
const NOTE_RE = /\[SYNABUN_GREETING_START\][\s\S]*?\[SYNABUN_GREETING_END\][ \t]*(?:\r?\n)*/g;
|
|
15
|
+
|
|
16
|
+
/** The text block a note is sent as ('' for no note). */
|
|
17
|
+
export function greetingNoteBlock(note) {
|
|
18
|
+
const text = String(note ?? '').trim();
|
|
19
|
+
return text ? `${AGY_GREETING_MARKER_START}\n${text}\n${AGY_GREETING_MARKER_END}` : '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A user message without the greeting's note (the whole marked block, wherever it is). */
|
|
23
|
+
export function stripGreetingNote(text) {
|
|
24
|
+
const value = String(text ?? '');
|
|
25
|
+
return value.includes(AGY_GREETING_MARKER_START) ? value.replace(NOTE_RE, '') : value;
|
|
26
|
+
}
|