synabun 2.0.10 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +207 -0
- package/README.md +25 -2
- package/THIRD-PARTY-LICENSES.md +22 -0
- package/bin/synabun.js +71 -24
- package/docs/whatsapp.md +13 -13
- package/hooks/claude-code/pre-websearch.mjs +12 -1
- package/hooks/claude-code/shared.mjs +5 -0
- package/hooks/claude-code/stop.mjs +1 -1
- package/lib/claude-hooks.js +172 -18
- package/lib/client-config-repair.js +30 -17
- package/lib/desktop-app.js +7 -1
- package/lib/external-tools.js +7 -6
- package/lib/first-launch-protection.js +57 -12
- package/lib/mcp-dist.js +176 -0
- package/lib/release-npm-ci.mjs +138 -0
- package/lib/release-publish.mjs +91 -0
- package/lib/release-scan.mjs +50 -0
- package/lib/server-crash-record.js +123 -0
- package/lib/server-exit.js +318 -0
- package/lib/server-supervisor.js +508 -0
- package/lib/start-launcher.js +37 -4
- package/lib/tools-cli.js +45 -0
- package/lib/update-snapshot.js +302 -0
- package/lib/virustotal.mjs +203 -0
- package/lib/windows-crash-event.js +269 -0
- package/mcp-server/dist/http.d.ts +20 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +59 -12
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts +33 -27
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +128 -143
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/preload.js +1 -1
- package/mcp-server/dist/preload.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -1
- package/mcp-server/dist/services/budget-judgments.js +5 -3
- package/mcp-server/dist/services/budget-judgments.js.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts +6 -2
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +81 -28
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +2 -2
- package/mcp-server/dist/services/identity.js +2 -2
- package/mcp-server/dist/services/memory-judge-backfill.d.ts +6 -0
- package/mcp-server/dist/services/memory-judge-backfill.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-judge-backfill.js +7 -1
- package/mcp-server/dist/services/memory-judge-backfill.js.map +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts +1 -1
- package/mcp-server/dist/services/memory-sessions.d.ts.map +1 -1
- package/mcp-server/dist/services/memory-sessions.js +28 -2
- package/mcp-server/dist/services/memory-sessions.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +18 -11
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +67 -11
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/server-instructions.d.ts +7 -2
- package/mcp-server/dist/services/server-instructions.d.ts.map +1 -1
- package/mcp-server/dist/services/server-instructions.js +22 -63
- package/mcp-server/dist/services/server-instructions.js.map +1 -1
- package/mcp-server/dist/services/tool-catalog.d.ts +120 -0
- package/mcp-server/dist/services/tool-catalog.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-catalog.js +339 -0
- package/mcp-server/dist/services/tool-catalog.js.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts +12 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js +40 -0
- package/mcp-server/dist/services/tool-list-refresh-barrier.js.map +1 -0
- package/mcp-server/dist/services/tool-list.d.ts +108 -0
- package/mcp-server/dist/services/tool-list.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-list.js +334 -0
- package/mcp-server/dist/services/tool-list.js.map +1 -0
- package/mcp-server/dist/services/tool-runtime.d.ts +161 -0
- package/mcp-server/dist/services/tool-runtime.d.ts.map +1 -0
- package/mcp-server/dist/services/tool-runtime.js +400 -0
- package/mcp-server/dist/services/tool-runtime.js.map +1 -0
- package/mcp-server/dist/services/typesafe-config.js +1 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.d.ts +26 -2
- package/mcp-server/dist/services/whiteboard-geometry.d.ts.map +1 -1
- package/mcp-server/dist/services/whiteboard-geometry.js +91 -8
- package/mcp-server/dist/services/whiteboard-geometry.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +14 -14
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +29 -19
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +3 -3
- package/mcp-server/dist/tools/choice.d.ts +6 -0
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +18 -13
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +2 -2
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +9 -6
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/skill-tools.d.ts +505 -0
- package/mcp-server/dist/tools/skill-tools.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill-tools.js +642 -0
- package/mcp-server/dist/tools/skill-tools.js.map +1 -0
- package/mcp-server/dist/tools/skill.d.ts +7 -0
- package/mcp-server/dist/tools/skill.d.ts.map +1 -0
- package/mcp-server/dist/tools/skill.js +11 -0
- package/mcp-server/dist/tools/skill.js.map +1 -0
- package/mcp-server/dist/tools/tool-groups.d.ts +68 -0
- package/mcp-server/dist/tools/tool-groups.d.ts.map +1 -0
- package/mcp-server/dist/tools/tool-groups.js +147 -0
- package/mcp-server/dist/tools/tool-groups.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-format.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-format.js +7 -4
- package/mcp-server/dist/tools/whiteboard-format.js.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +64 -13
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +30 -5
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/src/http.ts +75 -12
- package/mcp-server/src/index.ts +147 -154
- package/mcp-server/src/preload.ts +12 -12
- package/mcp-server/src/services/budget-judgments.ts +5 -3
- package/mcp-server/src/services/codex-config-heal.ts +80 -29
- package/mcp-server/src/services/identity.ts +2 -2
- package/mcp-server/src/services/memory-judge-backfill.ts +7 -1
- package/mcp-server/src/services/memory-sessions.ts +24 -3
- package/mcp-server/src/services/neural-interface.ts +81 -21
- package/mcp-server/src/services/server-instructions.ts +29 -64
- package/mcp-server/src/services/tool-catalog.ts +403 -0
- package/mcp-server/src/services/tool-list-refresh-barrier.ts +33 -0
- package/mcp-server/src/services/tool-list.ts +410 -0
- package/mcp-server/src/services/tool-runtime.ts +457 -0
- package/mcp-server/src/services/typesafe-config.ts +1 -1
- package/mcp-server/src/services/whiteboard-geometry.ts +94 -9
- package/mcp-server/src/tools/agents.ts +29 -20
- package/mcp-server/src/tools/choice.ts +17 -12
- package/mcp-server/src/tools/git-tools.ts +2 -2
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +6 -4
- package/mcp-server/src/tools/skill-tools.ts +592 -0
- package/mcp-server/src/tools/skill.ts +13 -0
- package/mcp-server/src/tools/tool-groups.ts +164 -0
- package/mcp-server/src/tools/whiteboard-format.ts +9 -3
- package/mcp-server/src/tools/whiteboard-tools.ts +39 -5
- package/neural-interface/i18n/en.json +3953 -228
- package/neural-interface/i18n/pt-BR.json +3901 -179
- package/neural-interface/lib/agent-sandbox-elevation.js +6 -0
- package/neural-interface/lib/antigravity/acp-client-host.js +554 -0
- package/neural-interface/lib/antigravity/acp-connection.js +299 -0
- package/neural-interface/lib/antigravity/bypass.js +100 -0
- package/neural-interface/lib/antigravity/catalog.js +273 -0
- package/neural-interface/lib/antigravity/home.js +260 -0
- package/neural-interface/lib/antigravity/migrate-gemini.js +139 -0
- package/neural-interface/lib/antigravity/panel-bridge.js +1058 -0
- package/neural-interface/lib/antigravity/policy-hook.mjs +78 -0
- package/neural-interface/lib/antigravity/policy.js +74 -0
- package/neural-interface/lib/antigravity/tool-call.js +178 -0
- package/neural-interface/lib/antigravity/transcripts.js +252 -0
- package/neural-interface/lib/antigravity/usage.js +43 -0
- package/neural-interface/lib/assistant-api.js +128 -28
- package/neural-interface/lib/assistant-brains/antigravity.js +423 -0
- package/neural-interface/lib/assistant-brains/claude.js +4 -3
- package/neural-interface/lib/assistant-brains/codex-browser-policy-hook.mjs +12 -2
- package/neural-interface/lib/assistant-brains/codex-route-gate-hook.mjs +32 -9
- package/neural-interface/lib/assistant-brains/codex.js +9 -5
- package/neural-interface/lib/assistant-brains/opencode.js +120 -54
- package/neural-interface/lib/assistant-brains/opencode2-browser-policy/index.js +28 -13
- package/neural-interface/lib/assistant-brains/opencode2-route-gate/index.js +59 -19
- package/neural-interface/lib/assistant-budget-planner.js +7 -3
- package/neural-interface/lib/assistant-budget-split.js +42 -15
- package/neural-interface/lib/assistant-catalog.js +81 -15
- package/neural-interface/lib/assistant-config.js +6 -3
- package/neural-interface/lib/assistant-dispatch.js +1073 -200
- package/neural-interface/lib/assistant-envelope.js +156 -2
- package/neural-interface/lib/assistant-evidence.js +80 -0
- package/neural-interface/lib/assistant-hidden-models.js +7 -5
- package/neural-interface/lib/assistant-persona.js +93 -19
- package/neural-interface/lib/assistant-plan-permissions.js +25 -9
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -0
- package/neural-interface/lib/assistant-route-gate.js +6 -4
- package/neural-interface/lib/assistant-router.js +245 -38
- package/neural-interface/lib/assistant-runtime.js +214 -30
- package/neural-interface/lib/assistant-task-prompt.js +87 -21
- package/neural-interface/lib/assistant-tools.js +51 -0
- package/neural-interface/lib/assistant-usage.js +29 -0
- package/neural-interface/lib/backup-service.js +626 -4
- package/neural-interface/lib/backup-zip-reader.js +10 -5
- package/neural-interface/lib/backup-zip-writer.js +257 -0
- package/neural-interface/lib/browser-execution.js +10 -2
- package/neural-interface/lib/browser-guard-cdp.js +84 -0
- package/neural-interface/lib/browser-network-guard.js +476 -0
- package/neural-interface/lib/browser-policy-roots.js +64 -0
- package/neural-interface/lib/browser-tool-policy.js +198 -38
- package/neural-interface/lib/claude-agent-bridge.js +11 -2
- package/neural-interface/lib/claude-history-cache.js +210 -0
- package/neural-interface/lib/claude-hooks-status.js +103 -0
- package/neural-interface/lib/claude-model-catalog.js +5 -1
- package/neural-interface/lib/claude-temporary.js +5 -11
- package/neural-interface/lib/cli-install-api.js +1 -1
- package/neural-interface/lib/cli-install.js +163 -154
- package/neural-interface/lib/cli-resolver.js +13 -3
- package/neural-interface/lib/cli-update-planner.js +52 -41
- package/neural-interface/lib/cloudflared/tunnel.js +42 -2
- package/neural-interface/lib/codex-mcp-approvals.js +11 -0
- package/neural-interface/lib/codex-rollout-reader.js +52 -0
- package/neural-interface/lib/codex-worker-app-server.js +191 -0
- package/neural-interface/lib/confined-fs.js +30 -3
- package/neural-interface/lib/crash-notice-api.js +98 -0
- package/neural-interface/lib/editor-completion-opencode.js +68 -68
- package/neural-interface/lib/editor-git-api.js +3 -20
- package/neural-interface/lib/exec-loop-command.js +13 -10
- package/neural-interface/lib/file-explorer-api.js +74 -40
- package/neural-interface/lib/file-explorer-watch.js +9 -3
- package/neural-interface/lib/git-bash/install.js +52 -6
- package/neural-interface/lib/git-bash/manager.js +59 -17
- package/neural-interface/lib/git-bash/pins.js +12 -0
- package/neural-interface/lib/git-read.js +2 -2
- package/neural-interface/lib/git-status-runner.js +390 -0
- package/neural-interface/lib/git-status-watchdog.js +40 -0
- package/neural-interface/lib/greeting/budgets.js +1 -0
- package/neural-interface/lib/greeting/engine.js +2 -2
- package/neural-interface/lib/greeting/sources.js +10 -12
- package/neural-interface/lib/greeting/writer.js +1 -1
- package/neural-interface/lib/http-guards.js +8 -2
- package/neural-interface/lib/install-details.js +4 -1
- package/neural-interface/lib/mcp-agent-servers.js +67 -0
- package/neural-interface/lib/mcp-client-config.js +35 -33
- package/neural-interface/lib/mcp-external-servers.js +332 -0
- package/neural-interface/lib/mcp-installer.js +2 -1
- package/neural-interface/lib/{mcp-profile-refresh-coordinator.js → mcp-tools-refresh-coordinator.js} +4 -4
- package/neural-interface/lib/memory-session-capture.js +17 -3
- package/neural-interface/lib/morelogin.js +177 -25
- package/neural-interface/lib/native-loop-providers.js +965 -66
- package/neural-interface/lib/native-loop-runtime.js +290 -42
- package/neural-interface/lib/opencode-config.js +557 -0
- package/neural-interface/lib/opencode-db-schema.js +17 -34
- package/neural-interface/lib/opencode-history.js +48 -218
- package/neural-interface/lib/opencode-mcp-ready.js +108 -0
- package/neural-interface/lib/opencode-native-boundary.js +53 -0
- package/neural-interface/lib/opencode-protocol.js +249 -112
- package/neural-interface/lib/opencode-runtime-config.js +32 -22
- package/neural-interface/lib/opencode-serve-records.js +39 -21
- package/neural-interface/lib/opencode-v2-client.js +48 -153
- package/neural-interface/lib/opencode-v2-ws-requests.js +36 -210
- package/neural-interface/lib/opencode-worker-data.js +27 -0
- package/neural-interface/lib/opencode2-client.js +73 -37
- package/neural-interface/lib/opencode2-proxy.js +23 -63
- package/neural-interface/lib/opencode2-translate.js +51 -2
- package/neural-interface/lib/owner-authority.js +249 -0
- package/neural-interface/lib/page-session.js +275 -0
- package/neural-interface/lib/path-confine.js +12 -9
- package/neural-interface/lib/peer-identity.js +793 -0
- package/neural-interface/lib/pty-host/core.js +28 -1
- package/neural-interface/lib/pty-host/lifetime.js +12 -0
- package/neural-interface/lib/pty-host/manager.js +12 -1
- package/neural-interface/lib/remote-policy.js +1 -1
- package/neural-interface/lib/request-gate.js +20 -2
- package/neural-interface/lib/rulesets/api.js +7 -6
- package/neural-interface/lib/rulesets/fs-safe.js +1 -1
- package/neural-interface/lib/rulesets/installer.js +180 -36
- package/neural-interface/lib/rulesets/json-edit.js +1 -1
- package/neural-interface/lib/rulesets/render.js +1 -1
- package/neural-interface/lib/session-cache-builder.js +81 -31
- package/neural-interface/lib/session-title-generator.js +58 -36
- package/neural-interface/lib/skills/api-v2.js +495 -0
- package/neural-interface/lib/skills/bundle.js +418 -0
- package/neural-interface/lib/skills/catalog.js +298 -0
- package/neural-interface/lib/skills/creation-api.js +191 -0
- package/neural-interface/lib/skills/creation-brief.js +247 -0
- package/neural-interface/lib/skills/creation-sources.js +285 -0
- package/neural-interface/lib/skills/creation.js +843 -0
- package/neural-interface/lib/skills/deploy.js +569 -0
- package/neural-interface/lib/skills/digest.js +119 -0
- package/neural-interface/lib/skills/discover.js +111 -0
- package/neural-interface/lib/skills/draft-confinement.js +216 -0
- package/neural-interface/lib/skills/eval-confinement.js +399 -0
- package/neural-interface/lib/skills/eval-sandbox.js +280 -0
- package/neural-interface/lib/skills/eval-trace.js +135 -0
- package/neural-interface/lib/skills/evals-api.js +212 -0
- package/neural-interface/lib/skills/evals.js +1573 -0
- package/neural-interface/lib/skills/export.js +203 -0
- package/neural-interface/lib/skills/frontmatter.js +64 -0
- package/neural-interface/lib/skills/health.js +75 -0
- package/neural-interface/lib/skills/json-edit.js +165 -0
- package/neural-interface/lib/skills/known-bridge-digests.json +441 -0
- package/neural-interface/lib/skills/known-bridges.js +96 -0
- package/neural-interface/lib/skills/library.js +823 -0
- package/neural-interface/lib/skills/local-store.js +21 -0
- package/neural-interface/lib/skills/migration.js +433 -0
- package/neural-interface/lib/skills/operations.js +45 -0
- package/neural-interface/lib/skills/ownership.js +427 -0
- package/neural-interface/lib/skills/packages.js +88 -0
- package/neural-interface/lib/skills/reviews.js +212 -0
- package/neural-interface/lib/skills/revision.js +80 -0
- package/neural-interface/lib/skills/roots.js +269 -0
- package/neural-interface/lib/skills/runtime-checks.js +358 -0
- package/neural-interface/lib/skills/scan-assets.js +252 -0
- package/neural-interface/lib/skills/scan-context.js +26 -0
- package/neural-interface/lib/skills/scan-shell.js +324 -0
- package/neural-interface/lib/skills/scan.js +364 -0
- package/neural-interface/lib/skills/scanners.js +54 -0
- package/neural-interface/lib/skills/service.js +530 -0
- package/neural-interface/lib/skills/telemetry.js +119 -0
- package/neural-interface/lib/sqlite-integrity-worker.js +22 -0
- package/neural-interface/lib/synabun-version.js +1 -1
- package/neural-interface/lib/system-activity.js +1 -1
- package/neural-interface/lib/tool-lists.js +124 -0
- package/neural-interface/lib/whatsapp/brain.js +7 -3
- package/neural-interface/lib/whatsapp/bridge.js +6 -3
- package/neural-interface/lib/whatsapp/cards.js +22 -9
- package/neural-interface/lib/whatsapp/config.js +1 -1
- package/neural-interface/lib/worker-confinement.js +1268 -0
- package/neural-interface/lib/worker-guard-setting.js +56 -0
- package/neural-interface/lib/worker-mcp-channel.js +175 -0
- package/neural-interface/lib/worker-question.js +74 -0
- package/neural-interface/lib/worker-relay-ports.js +227 -0
- package/neural-interface/lib/worker-resume.js +238 -0
- package/neural-interface/lib/worker-sandbox.js +329 -0
- package/neural-interface/package-lock.json +34 -12
- package/neural-interface/package.json +3 -2
- package/neural-interface/public/index.html +3 -1
- package/neural-interface/public/index2d.html +19 -11
- package/neural-interface/public/onboarding.html +3 -3
- package/neural-interface/public/shared/account-menu.js +8 -5
- package/neural-interface/public/shared/agent-runtime-options.js +125 -31
- package/neural-interface/public/shared/agy/agy-bypass.js +47 -0
- package/neural-interface/public/shared/agy/agy-client.js +266 -0
- package/neural-interface/public/shared/agy/agy-commands.js +120 -0
- package/neural-interface/public/shared/agy/agy-copy.js +326 -0
- package/neural-interface/public/shared/agy/agy-diff.js +161 -0
- package/neural-interface/public/shared/agy/agy-events.js +469 -0
- package/neural-interface/public/shared/agy/agy-greeting.js +26 -0
- package/neural-interface/public/shared/agy/agy-options.js +232 -0
- package/neural-interface/public/shared/agy/agy-panel.js +2543 -0
- package/neural-interface/public/shared/agy/agy-render.js +392 -0
- package/neural-interface/public/shared/agy/agy-setup.js +149 -0
- package/neural-interface/public/shared/agy/agy-styles.js +604 -0
- package/neural-interface/public/shared/agy/agy-tabs-logic.js +181 -0
- package/neural-interface/public/shared/api.js +1527 -1336
- package/neural-interface/public/shared/app-frame.js +192 -0
- package/neural-interface/public/shared/assistant/asst-agents-dock.js +36 -6
- package/neural-interface/public/shared/assistant/asst-api.js +1 -1
- package/neural-interface/public/shared/assistant/asst-ask-styles.js +270 -0
- package/neural-interface/public/shared/assistant/asst-ask-tail.js +118 -0
- package/neural-interface/public/shared/assistant/asst-ask.js +397 -0
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +105 -44
- package/neural-interface/public/shared/assistant/asst-budget.js +5 -4
- package/neural-interface/public/shared/assistant/asst-control.js +205 -26
- package/neural-interface/public/shared/assistant/asst-followups-card.js +383 -0
- package/neural-interface/public/shared/assistant/asst-followups.js +417 -0
- package/neural-interface/public/shared/assistant/asst-hosts.js +16 -7
- package/neural-interface/public/shared/assistant/asst-limit-dock.js +45 -0
- package/neural-interface/public/shared/assistant/asst-limit-wait.js +327 -0
- package/neural-interface/public/shared/assistant/asst-limit.js +168 -0
- package/neural-interface/public/shared/assistant/asst-mascot.js +49 -12
- package/neural-interface/public/shared/assistant/asst-menu.js +7 -6
- package/neural-interface/public/shared/assistant/asst-panel.js +151 -19
- package/neural-interface/public/shared/assistant/asst-rack-styles.js +66 -49
- package/neural-interface/public/shared/assistant/asst-render.js +445 -82
- package/neural-interface/public/shared/assistant/asst-route.js +304 -53
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +9 -8
- package/neural-interface/public/shared/assistant/asst-scenes.js +56 -1
- package/neural-interface/public/shared/assistant/asst-sidepanel.js +117 -10
- package/neural-interface/public/shared/assistant/asst-slash.js +17 -9
- package/neural-interface/public/shared/assistant/asst-state.js +87 -24
- package/neural-interface/public/shared/assistant/asst-stop.js +127 -0
- package/neural-interface/public/shared/assistant/asst-styles.js +93 -21
- package/neural-interface/public/shared/assistant/asst-tool-kinds.js +190 -23
- package/neural-interface/public/shared/assistant/asst-usage.js +2 -1
- package/neural-interface/public/shared/automation-context.js +5 -2
- package/neural-interface/public/shared/cc-dropdown.js +89 -19
- package/neural-interface/public/shared/cdx/cdx-context-menu.js +10 -9
- package/neural-interface/public/shared/cdx/cdx-copy.js +16 -0
- package/neural-interface/public/shared/cdx/cdx-output-model.js +22 -1
- package/neural-interface/public/shared/cdx/cdx-output.js +34 -10
- package/neural-interface/public/shared/cdx/cdx-panel.js +59 -151
- package/neural-interface/public/shared/cdx/cdx-preview.js +12 -0
- package/neural-interface/public/shared/cdx/cdx-protocol.js +39 -11
- package/neural-interface/public/shared/cdx/cdx-render.js +57 -38
- package/neural-interface/public/shared/cdx/cdx-requests.js +14 -4
- package/neural-interface/public/shared/cdx/cdx-skill-hints.js +54 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +15 -3
- package/neural-interface/public/shared/cdx/cdx-tabs.js +127 -44
- package/neural-interface/public/shared/cdx/cdx-ws.js +0 -1
- package/neural-interface/public/shared/cli-install-controller.js +218 -0
- package/neural-interface/public/shared/cli-install-view.js +11 -12
- package/neural-interface/public/shared/cli-status.js +11 -10
- package/neural-interface/public/shared/constants.js +5 -4
- package/neural-interface/public/shared/context-menu-areas.js +34 -0
- package/neural-interface/public/shared/context-menu-logic.js +243 -0
- package/neural-interface/public/shared/context-menu-model.js +367 -0
- package/neural-interface/public/shared/context-menu.js +372 -0
- package/neural-interface/public/shared/cp/cp-context-menu.js +10 -9
- package/neural-interface/public/shared/cp/cp-restore.js +162 -0
- package/neural-interface/public/shared/cp/cp-tool-cards.js +10 -3
- package/neural-interface/public/shared/crash-notice-view.js +168 -0
- package/neural-interface/public/shared/desktop-bridge.js +17 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +12 -0
- package/neural-interface/public/shared/desktop-titlebar.js +13 -4
- package/neural-interface/public/shared/desktop-update-view.js +21 -0
- package/neural-interface/public/shared/editor/editor-panel-logic.js +2 -2
- package/neural-interface/public/shared/explorer/fx-api.js +4 -4
- package/neural-interface/public/shared/explorer/fx-links.js +3 -3
- package/neural-interface/public/shared/explorer/fx-logic.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +48 -16
- package/neural-interface/public/shared/explorer/fx-tree.js +1 -0
- package/neural-interface/public/shared/greeting/greeting-card.js +2 -2
- package/neural-interface/public/shared/greeting/greeting-view.js +1 -0
- package/neural-interface/public/shared/html-shell.js +30 -15
- package/neural-interface/public/shared/i18n.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-approvals.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-binding.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-changes-logic.js +3 -101
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +2 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-compact-button.js +1 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-composer-logic.js +2 -6
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-menu.js +11 -11
- package/neural-interface/public/shared/ocp-v2/ocp-v2-context-model.js +0 -3
- package/neural-interface/public/shared/ocp-v2/ocp-v2-events.js +8 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-manager.js +7 -7
- package/neural-interface/public/shared/ocp-v2/ocp-v2-outcome-notifications.js +16 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +74 -259
- package/neural-interface/public/shared/ocp-v2/ocp-v2-plan.js +1 -2
- package/neural-interface/public/shared/ocp-v2/ocp-v2-projectbar.js +95 -202
- package/neural-interface/public/shared/ocp-v2/ocp-v2-rehydrate.js +13 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-render.js +13 -22
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +4 -4
- package/neural-interface/public/shared/ocp-v2/ocp-v2-session-actions.js +8 -61
- package/neural-interface/public/shared/ocp-v2/ocp-v2-sessions-logic.js +2 -91
- package/neural-interface/public/shared/ocp-v2/ocp-v2-slash-hints.js +16 -15
- package/neural-interface/public/shared/ocp-v2/ocp-v2-state.js +8 -21
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status-logic.js +1 -99
- package/neural-interface/public/shared/ocp-v2/ocp-v2-status.js +5 -35
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +17 -21
- package/neural-interface/public/shared/ocp-v2/{ocp-v2-profile-inheritance.js → ocp-v2-tools-inheritance.js} +16 -16
- package/neural-interface/public/shared/ocp-v2/ocp-v2-ws.js +60 -50
- package/neural-interface/public/shared/page-session.js +96 -0
- package/neural-interface/public/shared/provider-icons.js +4 -2
- package/neural-interface/public/shared/registry.js +5 -3
- package/neural-interface/public/shared/retired-2d.js +3 -0
- package/neural-interface/public/shared/schedules-view.js +195 -61
- package/neural-interface/public/shared/settings/settings-claude-hooks.js +135 -0
- package/neural-interface/public/shared/settings/settings-data.js +3 -1
- package/neural-interface/public/shared/settings/settings-ia.js +6 -6
- package/neural-interface/public/shared/settings/settings-opencode.js +89 -0
- package/neural-interface/public/shared/settings/settings-rules.js +2 -2
- package/neural-interface/public/shared/settings/settings-skill-folders-pane.js +265 -0
- package/neural-interface/public/shared/settings/settings-skill-folders.js +130 -0
- package/neural-interface/public/shared/settings/settings-worker-guard.js +72 -0
- package/neural-interface/public/shared/showcase-privacy.js +6 -1
- package/neural-interface/public/shared/sidepanel-session-status.js +3 -3
- package/neural-interface/public/shared/skills/ss-args.js +96 -0
- package/neural-interface/public/shared/skills/ss-creation.js +222 -0
- package/neural-interface/public/shared/skills/ss-dialog.js +133 -0
- package/neural-interface/public/shared/skills/ss-doc.js +173 -0
- package/neural-interface/public/shared/skills/ss-eval-adapters.js +269 -0
- package/neural-interface/public/shared/skills/ss-eval-grade.js +359 -0
- package/neural-interface/public/shared/skills/ss-eval-optimise.js +153 -0
- package/neural-interface/public/shared/skills/ss-eval-plan.js +315 -0
- package/neural-interface/public/shared/skills/ss-evals.js +417 -0
- package/neural-interface/public/shared/skills/ss-form.js +159 -0
- package/neural-interface/public/shared/skills/ss-frontmatter.js +376 -0
- package/neural-interface/public/shared/skills/ss-ids.js +32 -0
- package/neural-interface/public/shared/skills/ss-lint.js +149 -0
- package/neural-interface/public/shared/skills/ss-presets.js +117 -0
- package/neural-interface/public/shared/skills/ss-profiles.js +513 -0
- package/neural-interface/public/shared/skills/ss-resources.js +165 -0
- package/neural-interface/public/shared/skills/ss-review-view.js +190 -0
- package/neural-interface/public/shared/skills/ss-tabs.js +79 -0
- package/neural-interface/public/shared/skills/ss-unicode.js +135 -0
- package/neural-interface/public/shared/skills/studio/sx-actions.js +222 -0
- package/neural-interface/public/shared/skills/studio/sx-cm.js +101 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai-view.js +749 -0
- package/neural-interface/public/shared/skills/studio/sx-create-ai.js +237 -0
- package/neural-interface/public/shared/skills/studio/sx-create-view.js +254 -0
- package/neural-interface/public/shared/skills/studio/sx-create.js +126 -0
- package/neural-interface/public/shared/skills/studio/sx-diff-view.js +43 -0
- package/neural-interface/public/shared/skills/studio/sx-diff.js +133 -0
- package/neural-interface/public/shared/skills/studio/sx-docs.js +480 -0
- package/neural-interface/public/shared/skills/studio/sx-dom.js +230 -0
- package/neural-interface/public/shared/skills/studio/sx-edit-view.js +848 -0
- package/neural-interface/public/shared/skills/studio/sx-editor-model.js +193 -0
- package/neural-interface/public/shared/skills/studio/sx-errors.js +22 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes-view.js +359 -0
- package/neural-interface/public/shared/skills/studio/sx-fixes.js +327 -0
- package/neural-interface/public/shared/skills/studio/sx-geometry.js +82 -0
- package/neural-interface/public/shared/skills/studio/sx-import.js +262 -0
- package/neural-interface/public/shared/skills/studio/sx-inspector.js +308 -0
- package/neural-interface/public/shared/skills/studio/sx-install-flow.js +282 -0
- package/neural-interface/public/shared/skills/studio/sx-install.js +313 -0
- package/neural-interface/public/shared/skills/studio/sx-library-view.js +748 -0
- package/neural-interface/public/shared/skills/studio/sx-library.js +479 -0
- package/neural-interface/public/shared/skills/studio/sx-main.js +690 -0
- package/neural-interface/public/shared/skills/studio/sx-operations-view.js +98 -0
- package/neural-interface/public/shared/skills/studio/sx-operations.js +11 -0
- package/neural-interface/public/shared/skills/studio/sx-plan.js +158 -0
- package/neural-interface/public/shared/skills/studio/sx-publish-view.js +621 -0
- package/neural-interface/public/shared/skills/studio/sx-review-view.js +369 -0
- package/neural-interface/public/shared/skills/studio/sx-review.js +148 -0
- package/neural-interface/public/shared/skills/studio/sx-scan.js +35 -0
- package/neural-interface/public/shared/skills/studio/sx-snippets.js +96 -0
- package/neural-interface/public/shared/skills/studio/sx-subject.js +249 -0
- package/neural-interface/public/shared/skills/studio/sx-test-view.js +994 -0
- package/neural-interface/public/shared/skills/studio/sx-test.js +328 -0
- package/neural-interface/public/shared/skills/studio/sx-versions-view.js +241 -0
- package/neural-interface/public/shared/skills/studio/sx-workspace-view.js +426 -0
- package/neural-interface/public/shared/state.js +1 -1
- package/neural-interface/public/shared/streaming-markdown.js +58 -0
- package/neural-interface/public/shared/styleguide/sg-panel.js +4 -2
- package/neural-interface/public/shared/styles.css +1822 -1756
- package/neural-interface/public/shared/synabun-mascot.js +1115 -9
- package/neural-interface/public/shared/text-cache.js +29 -0
- package/neural-interface/public/shared/tool-list-picker.js +55 -0
- package/neural-interface/public/shared/tool-list-storage.js +24 -0
- package/neural-interface/public/shared/tool-list-view.js +112 -0
- package/neural-interface/public/shared/topright-overflow-logic.js +54 -0
- package/neural-interface/public/shared/tour/tour-actor.js +268 -0
- package/neural-interface/public/shared/tour/tour-beats.js +336 -0
- package/neural-interface/public/shared/tour/tour-engine.js +884 -0
- package/neural-interface/public/shared/tour/tour-ink.js +390 -0
- package/neural-interface/public/shared/tour/tour-stage.js +440 -0
- package/neural-interface/public/shared/tour/tour-state.js +40 -0
- package/neural-interface/public/shared/tour/tour-tips.js +99 -0
- package/neural-interface/public/shared/tour/tour.css +144 -0
- package/neural-interface/public/shared/ui-antigravity-panel.js +19 -0
- package/neural-interface/public/shared/ui-assistant.js +2 -0
- package/neural-interface/public/shared/ui-automation-studio.js +111 -140
- package/neural-interface/public/shared/ui-claude-panel.js +376 -82
- package/neural-interface/public/shared/ui-cli-banner.js +23 -167
- package/neural-interface/public/shared/ui-command-runner.js +6 -3
- package/neural-interface/public/shared/ui-context-menu.js +295 -0
- package/neural-interface/public/shared/ui-cost-widget.js +57 -59
- package/neural-interface/public/shared/ui-crash-notice.js +187 -0
- package/neural-interface/public/shared/ui-detail.js +15 -3
- package/neural-interface/public/shared/ui-file-explorer.js +48 -6
- package/neural-interface/public/shared/ui-file-links.js +2 -2
- package/neural-interface/public/shared/ui-games.js +2 -2
- package/neural-interface/public/shared/ui-image-gallery.js +50 -47
- package/neural-interface/public/shared/ui-judgments.js +1 -1
- package/neural-interface/public/shared/ui-keybinds.js +33 -2
- package/neural-interface/public/shared/ui-link.js +3 -3
- package/neural-interface/public/shared/ui-menubar.js +10 -20
- package/neural-interface/public/shared/ui-native-loop-router-state.js +5 -0
- package/neural-interface/public/shared/ui-native-loop-router.js +11 -1
- package/neural-interface/public/shared/ui-navbar.js +49 -24
- package/neural-interface/public/shared/ui-notifications.js +1 -0
- package/neural-interface/public/shared/ui-panels.js +96 -65
- package/neural-interface/public/shared/ui-resume.js +9 -7
- package/neural-interface/public/shared/ui-schedules-studio.js +546 -431
- package/neural-interface/public/shared/ui-scroll-follow.js +16 -5
- package/neural-interface/public/shared/ui-sessions.js +27 -15
- package/neural-interface/public/shared/ui-settings.js +683 -526
- package/neural-interface/public/shared/ui-sidepanel-session-entry.js +9 -2
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +4 -4
- package/neural-interface/public/shared/ui-sidepanel-state.js +23 -12
- package/neural-interface/public/shared/ui-sidepanel-tray.js +48 -7
- package/neural-interface/public/shared/ui-sidepanel-windows.js +18 -13
- package/neural-interface/public/shared/ui-skills.js +33 -2830
- package/neural-interface/public/shared/ui-styleguide.js +5 -2
- package/neural-interface/public/shared/ui-sync.js +15 -5
- package/neural-interface/public/shared/ui-terminal.js +270 -190
- package/neural-interface/public/shared/ui-tool-updates.js +6 -4
- package/neural-interface/public/shared/ui-tooltip.js +81 -51
- package/neural-interface/public/shared/ui-topright-overflow.js +233 -0
- package/neural-interface/public/shared/ui-trash.js +6 -4
- package/neural-interface/public/shared/ui-tutorial-layout.js +3 -117
- package/neural-interface/public/shared/ui-tutorial-preview.js +103 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +3 -65
- package/neural-interface/public/shared/ui-tutorial.css +47 -100
- package/neural-interface/public/shared/ui-tutorial.js +285 -509
- package/neural-interface/public/shared/ui-update.js +38 -2
- package/neural-interface/public/shared/ui-whiteboard.js +312 -59
- package/neural-interface/public/shared/ui-worker-end.js +5 -1
- package/neural-interface/public/shared/ui-workspaces.js +3 -2
- package/neural-interface/public/shared/view-variant.js +66 -0
- package/neural-interface/public/shared/whatsapp/wa-view.js +3 -3
- package/neural-interface/public/shared/whiteboard-list.js +172 -0
- package/neural-interface/public/shared/worker-end.js +10 -3
- package/neural-interface/public/shared/worker-questions.js +34 -0
- package/neural-interface/public/shared/worker-restart.js +12 -0
- package/neural-interface/public/sidepanel-session.html +2 -2
- package/neural-interface/public/variant/2d/background.js +2 -1
- package/neural-interface/public/variant/2d/context-menu.js +8 -2
- package/neural-interface/public/variant/2d/graph.js +42 -20
- package/neural-interface/public/variant/2d/hulls.js +14 -9
- package/neural-interface/public/variant/2d/lasso.js +11 -7
- package/neural-interface/public/variant/2d/main.js +32 -1
- package/neural-interface/public/variant/2d/minimap.js +11 -13
- package/neural-interface/public/variant/3d/main.js +69 -6
- package/neural-interface/public/variant/3d/map-controls.js +24 -9
- package/neural-interface/public/vendor/caveat/OFL.txt +93 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-400-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat-latin-700-normal.woff2 +0 -0
- package/neural-interface/public/vendor/caveat/caveat.css +17 -0
- package/neural-interface/public/vendor/caveat/manifest.json +12 -0
- package/neural-interface/public/vendor/yaml/LICENSE +13 -0
- package/neural-interface/public/vendor/yaml/manifest.json +4 -0
- package/neural-interface/public/vendor/yaml/yaml-2.9.1.min.js +137 -0
- package/neural-interface/scripts/build-vendor.mjs +32 -0
- package/neural-interface/server.js +4553 -2855
- package/neural-interface/templates/rulesets/README.md +1 -1
- package/neural-interface/templates/rulesets/core.md +1 -1
- package/neural-interface/templates/rulesets/legacy-hashes.json +23 -5
- package/neural-interface/templates/rulesets/manifest.json +8 -8
- package/npm-shrinkwrap.json +34 -12
- package/package.json +17 -3
- package/postinstall.js +9 -4
- package/preuninstall.js +1 -1
- package/setup.js +97 -322
- package/skills/skill-forge/SKILL.md +177 -0
- package/skills/skill-forge/evals/evals.json +54 -0
- package/skills/skill-forge/references/package.md +105 -0
- package/skills/skill-forge/references/review-and-recovery.md +63 -0
- package/skills/skill-forge/references/runtime-profiles.md +20 -0
- package/skills/skill-forge/references/sources-and-capabilities.md +51 -0
- package/skills/synabun/SKILL.md +10 -7
- package/skills/synabun/modules/audit.md +1 -5
- package/skills/synabun/modules/changelog.md +1 -5
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/memorize.md +6 -9
- package/skills/synabun/modules/schedule.md +6 -6
- package/mcp-server/dist/services/profiles.d.ts +0 -120
- package/mcp-server/dist/services/profiles.d.ts.map +0 -1
- package/mcp-server/dist/services/profiles.js +0 -387
- package/mcp-server/dist/services/profiles.js.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +0 -97
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +0 -228
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +0 -1
- package/mcp-server/dist/tools/leonardo.d.ts +0 -13
- package/mcp-server/dist/tools/leonardo.d.ts.map +0 -1
- package/mcp-server/dist/tools/leonardo.js +0 -21
- package/mcp-server/dist/tools/leonardo.js.map +0 -1
- package/mcp-server/dist/tools/profile.d.ts +0 -17
- package/mcp-server/dist/tools/profile.d.ts.map +0 -1
- package/mcp-server/dist/tools/profile.js +0 -114
- package/mcp-server/dist/tools/profile.js.map +0 -1
- package/mcp-server/src/services/profiles.ts +0 -428
- package/mcp-server/src/tools/leonardo-browser-tools.ts +0 -291
- package/mcp-server/src/tools/leonardo.ts +0 -30
- package/mcp-server/src/tools/profile.ts +0 -122
- package/neural-interface/lib/assistant-brains/opencode-browser-policy.js +0 -47
- package/neural-interface/lib/assistant-brains/opencode-route-gate.js +0 -62
- package/neural-interface/lib/mcp-profile-servers.js +0 -120
- package/neural-interface/public/shared/ui-tutorial-draw.js +0 -311
- package/skills/leonardo/SKILL.md +0 -168
- package/skills/leonardo/icon.svg +0 -1
- package/skills/leonardo/modules/browser-guide.md +0 -378
- package/skills/leonardo/modules/image-prompter.md +0 -283
- package/skills/leonardo/modules/model-advisor.md +0 -100
- package/skills/leonardo/modules/prompt-library.md +0 -110
- package/skills/leonardo/modules/style-guide.md +0 -186
- package/skills/leonardo/modules/video-prompter.md +0 -317
- /package/neural-interface/templates/rulesets/{gemini.md → antigravity.md} +0 -0
|
@@ -30,6 +30,19 @@
|
|
|
30
30
|
// conversation opened again learns its route from the journal like any other
|
|
31
31
|
// event (no rack of the history moves: none is live) and lends it to the
|
|
32
32
|
// resumed work once its turn runs (lendRouteScene).
|
|
33
|
+
// What a turn leaves for the user to click sits under the row it belongs to:
|
|
34
|
+
// the follow-ups a message offers (its ```synabun-followups block, or best
|
|
35
|
+
// effort its prose: asst-followups.js) and, under an error that is a usage
|
|
36
|
+
// limit, the waiting card with its countdown (asst-limit-wait.js); a limited
|
|
37
|
+
// run's card shows the same waiting card. The user's next prompt answers
|
|
38
|
+
// them or leaves them stale, and so does a new turn (settleOffers). A card
|
|
39
|
+
// that asks and is still open is lifted to the transcript's bottom while it
|
|
40
|
+
// waits (asst-ask-tail.js), so text and tool output streaming in after it
|
|
41
|
+
// never bury it; once answered it goes back under its row.
|
|
42
|
+
// A follow-ups card and the question cards the panel appends (appendNode with
|
|
43
|
+
// an `_ask`) are presented by the mascot (asst-ask.js): the newest one still
|
|
44
|
+
// open is the character on screen, its face a live rig (liveAsk,
|
|
45
|
+
// activateRigs); every other card's face is a painted still.
|
|
33
46
|
|
|
34
47
|
import { assistantEventKey, fmtCost, fmtElapsed, isRemovableRun, isTerminalRunStatus, modelShortName, providerShortLabel, routeTargetLabel, runTone, summarizeMailbox } from './asst-state.js';
|
|
35
48
|
import { createRouteLine } from './asst-route.js';
|
|
@@ -37,14 +50,21 @@ import { closeMenu, isMenuOpen } from './asst-menu.js';
|
|
|
37
50
|
import { computerToolName, describeComputerAction, frameUrls, markerPercent, openFrameLightbox, parseComputerResult, swapImage } from './asst-computer.js';
|
|
38
51
|
import * as mascotLib from '../synabun-mascot.js';
|
|
39
52
|
import { isReducedMotion, subscribe } from '../synabun-ticker.js';
|
|
40
|
-
import { KIND_SPECS, callOutcome, commandOf, editDiff, editPaths, fmtDuration, groupSubject,
|
|
53
|
+
import { KIND_SPECS, callOutcome, canonicalToolInput, commandOf, editDiff, editPaths, fmtDuration, groupSubject, headEllipsis, kindOf, maskEcho, mcpServerOf, middleEllipsis, planState, plural, prettyServer, receiptSentence, redactInput, resultDetail, sayCall, shouldFoldNarration, stationAggregate, stationKey, targetKey, toolWords, verbText, wrapRuns } from './asst-tool-kinds.js';
|
|
41
54
|
import { injectRackStyles } from './asst-rack-styles.js';
|
|
42
55
|
import { injectSceneStyles } from './asst-scene-styles.js';
|
|
43
56
|
import { activityScene, compactActivity, isLivePhase, runChip, sceneOfClass, sceneSvg, stagePhase } from './asst-scenes.js';
|
|
44
57
|
import { STAGE_TIMING, createWorkerStage } from './asst-run-stage.js';
|
|
45
58
|
import { formatRunTokens } from './asst-usage.js';
|
|
46
59
|
import { sanitizeHtmlString, sanitizeInto } from './asst-sanitize.js';
|
|
60
|
+
import { renderStreamingMarkdown } from '../streaming-markdown.js';
|
|
47
61
|
import { automationView } from './asst-automation.js';
|
|
62
|
+
import { guessFollowups, splitFollowups } from './asst-followups.js';
|
|
63
|
+
import { createFollowupsCard } from './asst-followups-card.js';
|
|
64
|
+
import { createAskTail } from './asst-ask-tail.js';
|
|
65
|
+
import { limitFromText } from './asst-limit.js';
|
|
66
|
+
import { createLimitDock } from './asst-limit-dock.js';
|
|
67
|
+
import { createLimitWait } from './asst-limit-wait.js';
|
|
48
68
|
|
|
49
69
|
// ── Markdown (lazy marked, the vendored build the sidepanels use too) ───────────────
|
|
50
70
|
let _marked = null;
|
|
@@ -259,12 +279,15 @@ export function updateRunCard(card, run, opts = {}) {
|
|
|
259
279
|
const readBtn = card.querySelector('[data-action="read"]');
|
|
260
280
|
if (readBtn) readBtn.disabled = run.status === 'queued';
|
|
261
281
|
|
|
282
|
+
// A provider's usage limit stopped it: the waiting card, whose Retry now stands in for "Retry on" the same model.
|
|
283
|
+
const limitShown = syncRunLimit(card, run, opts, t);
|
|
284
|
+
|
|
262
285
|
// Escalation offer ("Escalate to ‹label›", or "Retry on ‹label›" after a
|
|
263
286
|
// temporary failure) when the server proposes one; a note without a button
|
|
264
287
|
// when a stronger model would hit the same wall (access, the user's input).
|
|
265
288
|
let esc8 = card.querySelector('.asst-run-escalate');
|
|
266
289
|
const escalationRaw = run.escalation && typeof run.escalation === 'object' ? run.escalation : null;
|
|
267
|
-
const escalation = escalationRaw?.to ? escalationRaw : null;
|
|
290
|
+
const escalation = escalationRaw?.to && !(limitShown && escalationRaw.kind === 'retry') ? escalationRaw : null;
|
|
268
291
|
const needsNote = escalationRaw && escalationRaw.kind === 'none' ? escalationRaw : null;
|
|
269
292
|
const onEscalate = opts.onEscalate || card._runOpts?.onEscalate;
|
|
270
293
|
if (esc8 && esc8.dataset.kind !== (needsNote ? 'note' : 'offer')) { esc8.remove(); esc8 = null; card._escalation = null; }
|
|
@@ -322,6 +345,49 @@ export function updateRunCard(card, run, opts = {}) {
|
|
|
322
345
|
updateRunMedia(card, run, { t, lightboxT: opts.t || card._runOpts?.t });
|
|
323
346
|
}
|
|
324
347
|
|
|
348
|
+
/** What Retry now runs a limited run on: the server's same-model retry, else the run's own provider and model. */
|
|
349
|
+
function limitRetryTarget(run) {
|
|
350
|
+
const esc = run?.escalation && typeof run.escalation === 'object' ? run.escalation : null;
|
|
351
|
+
if (esc?.kind === 'retry' && esc.to?.provider) return esc.to;
|
|
352
|
+
return { provider: run.provider || 'claude-code', model: run.model || null, effort: run.effort || null, accountId: run.accountId || null };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* A run a provider's usage limit stopped (run.limit, lib/assistant-dispatch.js): the waiting card
|
|
357
|
+
* is registered with the conversation's single bottom dock; a standalone card can own its wait.
|
|
358
|
+
* Retry now runs it again on the same model. Never while running or in the compact agents tray.
|
|
359
|
+
*/
|
|
360
|
+
function syncRunLimit(card, run, opts, t) {
|
|
361
|
+
const limit = run.limit && typeof run.limit === 'object' ? run.limit : null;
|
|
362
|
+
const tone = runTone(run);
|
|
363
|
+
const show = !opts.compact && !!limit && tone !== 'running' && tone !== 'queued' && !run.removed;
|
|
364
|
+
let wait = card._limitWait || null;
|
|
365
|
+
if (!show) {
|
|
366
|
+
(opts.onLimitWait || card._runOpts?.onLimitWait)?.(run, null);
|
|
367
|
+
if (wait) { wait.destroy(); wait.el.remove(); card._limitWait = null; }
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
const shared = opts.onLimitWait || card._runOpts?.onLimitWait;
|
|
371
|
+
if (shared) { shared(run, show && !limit.retriedAt && !run.escalation?.started ? limit : null); return show; }
|
|
372
|
+
if (!wait || !wait.el.isConnected) {
|
|
373
|
+
const onEscalate = opts.onEscalate || card._runOpts?.onEscalate;
|
|
374
|
+
wait = createLimitWait({
|
|
375
|
+
t,
|
|
376
|
+
resetsAt: limit.resetsAt || null,
|
|
377
|
+
since: Date.parse(limit.detectedAt || run.completedAt || '') || Date.now(),
|
|
378
|
+
provider: providerShortLabel(run.provider || 'claude-code'),
|
|
379
|
+
onRetry: typeof onEscalate === 'function' ? () => onEscalate(card.dataset.runId, limitRetryTarget(card._usageRun || run)) : null,
|
|
380
|
+
});
|
|
381
|
+
card._limitWait = wait;
|
|
382
|
+
const meta = card.querySelector('.asst-run-meta');
|
|
383
|
+
if (meta) meta.after(wait.el); else card.appendChild(wait.el);
|
|
384
|
+
} else if (limit.resetsAt) {
|
|
385
|
+
wait.update({ resetsAt: limit.resetsAt });
|
|
386
|
+
}
|
|
387
|
+
if (limit.retriedAt || run.escalation?.started) wait.retried();
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
|
|
325
391
|
const MAX_RUN_MEDIA = 8;
|
|
326
392
|
|
|
327
393
|
/**
|
|
@@ -424,7 +490,7 @@ const PREVIEW_LINES = 5;
|
|
|
424
490
|
const THINK_PREVIEW_LINES = 10;
|
|
425
491
|
const MAX_DIFF_LINES = 400; // an edit's mini diff draws this many lines; "Show full input" draws the rest
|
|
426
492
|
// The empty state's suggestions (asst-panel.js ids) and the kind each one is about.
|
|
427
|
-
const SUGGESTION_KINDS = { computer: 'computer', web: 'web', fix: 'edit', schedule: 'plan', remember: 'memory' };
|
|
493
|
+
const SUGGESTION_KINDS = { computer: 'computer', web: 'web', fix: 'edit', skill: 'skill', schedule: 'plan', remember: 'memory' };
|
|
428
494
|
|
|
429
495
|
/**
|
|
430
496
|
* createRenderer(transcriptEl, hooks)
|
|
@@ -433,7 +499,13 @@ const SUGGESTION_KINDS = { computer: 'computer', web: 'web', fix: 'edit', schedu
|
|
|
433
499
|
* emptySuggestions() → [{ id, label, template }], onSuggestion(template),
|
|
434
500
|
* brainLabel() → string, onChangeRoutes(), onRouteEvent(ev) → bool (true = handled by an open card),
|
|
435
501
|
* heroOrigin() → DOMRect | null (where the empty state's character flies in from: the host's placeholder),
|
|
436
|
-
* onMascotChange() (the character on screen changed: mascot())
|
|
502
|
+
* onMascotChange() (the character on screen changed: mascot()),
|
|
503
|
+
* onFollowup(text, { set, indices }) (a follow-up was clicked: send `text` as the user's message),
|
|
504
|
+
* onFollowupOther() ("Something else…": the composer), limitInfo() → the session's limit notice
|
|
505
|
+
* ({ status, resetsAt } | null), brainProvider() → the brain's provider id, onLimitRetry(text)
|
|
506
|
+
* (Retry now on the Assistant's own limit card: send `text`), onSwitchProvider(anchorEl, { excludeProvider, retryText })
|
|
507
|
+
* (Continue with another provider on the Assistant's own limit card: the host opens its own
|
|
508
|
+
* provider picker anchored on anchorEl and decides what a pick does) }
|
|
437
509
|
*/
|
|
438
510
|
export function createRenderer(transcriptEl, hooks = {}) {
|
|
439
511
|
loadMarked();
|
|
@@ -449,7 +521,11 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
449
521
|
const autoRows = new Map(); // traceId → the turn's automation trace row
|
|
450
522
|
let working = null;
|
|
451
523
|
let workingFace = null; // the "Thinking" row's character: { still, rig?, el, target? } (syncWorkingFace)
|
|
524
|
+
// The open card's slot under every row (asst-ask-tail.js): a card that waits on the user is drawn
|
|
525
|
+
// there, above the "Working…" row, until it is answered; rows appended meanwhile go above it.
|
|
526
|
+
const askTail = createAskTail($msgs, { anchor: () => (working?.parentNode === $msgs ? working : null) });
|
|
452
527
|
let followTail = true;
|
|
528
|
+
let scrollFrame = 0;
|
|
453
529
|
let emptyEl = null;
|
|
454
530
|
let hero = null; // the empty state's character: { rig, el, box, parked, leaving, held, dock, egg, target }
|
|
455
531
|
const mcpWarned = new Set();
|
|
@@ -470,6 +546,15 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
470
546
|
let lastDirect = null; // the last one approved since the user's prompt: a "do it here" continuation turn takes it again
|
|
471
547
|
let turnSeq = 0; // turns this renderer has seen start (showWorking)
|
|
472
548
|
let reattachRunning = null; // what the last reattach said: a turn runs (true), none (false), not asked yet (null)
|
|
549
|
+
// What a turn left for the user to click: follow-up cards under its messages (asst-followups-card.js).
|
|
550
|
+
// The user's next prompt answers them or they go
|
|
551
|
+
// stale, and so does a new turn; live and from the journal alike, so a reload draws the same states.
|
|
552
|
+
const offers = new Set(); // { el, set, settle(text) → answered, stale() }
|
|
553
|
+
// The cards the mascot presents (asst-ask.js): follow-ups cards and the question cards the panel appends.
|
|
554
|
+
// The newest one still open is the character on screen (above any stage); see liveAsk / activateRigs.
|
|
555
|
+
const askCards = new Set(); // { el, presenter, isOpen(), onChange, seq }
|
|
556
|
+
let askSeq = 0;
|
|
557
|
+
let lastTextRow = null; // the newest root message since the user's last prompt: the one a guess reads
|
|
473
558
|
|
|
474
559
|
// ── Activity racks ──
|
|
475
560
|
// Every burst of agent work (tool calls, thinking, short narration) is one
|
|
@@ -539,6 +624,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
539
624
|
if (step.ghost && step.type === 'call' && !Object.keys(step.input || {}).length) { say.text = '…'; say.detail = '…'; say.segments = null; say.mono = false; say.meta = []; }
|
|
540
625
|
return say;
|
|
541
626
|
};
|
|
627
|
+
const limitDock = createLimitDock($msgs, { t });
|
|
542
628
|
const nowMs = () => (clockAt ?? Date.now());
|
|
543
629
|
const isTimed = () => !muted || clockAt != null;
|
|
544
630
|
const locale = () => (typeof document !== 'undefined' && document.documentElement?.lang) || 'en';
|
|
@@ -555,7 +641,8 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
555
641
|
|
|
556
642
|
function scrollEnd(force = false) {
|
|
557
643
|
if (!force && !followTail) return;
|
|
558
|
-
|
|
644
|
+
if (scrollFrame) return;
|
|
645
|
+
scrollFrame = requestAnimationFrame(() => { scrollFrame = 0; $msgs.scrollTop = $msgs.scrollHeight; });
|
|
559
646
|
}
|
|
560
647
|
|
|
561
648
|
function root() { return { container: $msgs, stream: null, key: null, msgId: null, lastAt: 0 }; }
|
|
@@ -739,12 +826,46 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
739
826
|
return best;
|
|
740
827
|
}
|
|
741
828
|
|
|
742
|
-
/** The mascot on screen: the hero, else the newest stage, else the "Thinking" row's, else none. */
|
|
829
|
+
/** The mascot on screen: the hero, else the newest open card's, else the newest stage, else the "Thinking" row's, else none. */
|
|
743
830
|
function visibleMascot() {
|
|
744
831
|
if (hero?.rig) return hero.target;
|
|
832
|
+
const ask = liveAsk();
|
|
833
|
+
if (ask?.presenter?.isLive?.()) return ask.presenter.target;
|
|
745
834
|
return newestStage()?.target || workingFace?.target || null;
|
|
746
835
|
}
|
|
747
836
|
|
|
837
|
+
// ── Cards the mascot presents ──
|
|
838
|
+
/**
|
|
839
|
+
* A follow-ups card or a question card (its `_ask`): its face may be the character on screen while it is
|
|
840
|
+
* open. Every ask — a route card too, which has no face — waits at the transcript's bottom until answered.
|
|
841
|
+
*/
|
|
842
|
+
function registerAsk(handle) {
|
|
843
|
+
if (!handle?.el || askCards.has(handle)) return;
|
|
844
|
+
handle.seq = (askSeq += 1);
|
|
845
|
+
handle.onChange = () => { askTail.sync(); mascotChanged(); };
|
|
846
|
+
askCards.add(handle);
|
|
847
|
+
askTail.pin(handle);
|
|
848
|
+
mascotChanged();
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/** The newest card with a face that is still open (answers can still be picked): the one whose face is live. */
|
|
852
|
+
function liveAsk() {
|
|
853
|
+
let best = null;
|
|
854
|
+
for (const handle of [...askCards]) {
|
|
855
|
+
if (!handle.el?.isConnected) { askCards.delete(handle); handle.presenter?.destroy?.(); continue; }
|
|
856
|
+
// A route card asks, but has no face of its own: it waits at the bottom without taking the character.
|
|
857
|
+
if (!handle.presenter || !handle.isOpen() || (best && best.seq > handle.seq)) continue;
|
|
858
|
+
best = handle;
|
|
859
|
+
}
|
|
860
|
+
return best;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
function dropAsks() {
|
|
864
|
+
for (const handle of askCards) { try { handle.presenter?.destroy?.(); } catch { /* gone */ } }
|
|
865
|
+
askCards.clear();
|
|
866
|
+
askTail.clear();
|
|
867
|
+
}
|
|
868
|
+
|
|
748
869
|
function mascotChanged() {
|
|
749
870
|
activateRigs();
|
|
750
871
|
try { hooks.onMascotChange?.(); } catch { /* the director is gone */ }
|
|
@@ -755,12 +876,12 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
755
876
|
while (rows.length > MAX_MESSAGES && rows[0] !== working) rows[0].remove();
|
|
756
877
|
}
|
|
757
878
|
|
|
758
|
-
/** Insert a row before "Working…" so the indicator stays the last row. */
|
|
879
|
+
/** Insert a row before the open cards (if any) and before "Working…", so the indicator stays the last row. */
|
|
759
880
|
function append(node, scope = rootScope, { park = false } = {}) {
|
|
760
881
|
hideEmpty({ park });
|
|
761
|
-
if (scope.container === $msgs
|
|
882
|
+
if (scope.container === $msgs) askTail.insert(node);
|
|
762
883
|
else scope.container.appendChild(node);
|
|
763
|
-
if (scope === rootScope) prune();
|
|
884
|
+
if (scope === rootScope) { prune(); askTail.sync(); }
|
|
764
885
|
// Text, a card, a notice…: the parked hero would sit on it. The prompt and a rack leave it to settleHero().
|
|
765
886
|
if (hero?.parked && scope === rootScope && !node._rack && !node.classList.contains('msg-user')) dismissHero();
|
|
766
887
|
if (racks.size) syncRacks();
|
|
@@ -825,6 +946,127 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
825
946
|
linkify(body);
|
|
826
947
|
}
|
|
827
948
|
|
|
949
|
+
/**
|
|
950
|
+
* A message's text without its follow-ups block (asst-followups.js). While it streams, a block
|
|
951
|
+
* being typed stays hidden; once the text is final, the block becomes the card under the row
|
|
952
|
+
* (root messages only: a subagent's offers are not the user's to answer).
|
|
953
|
+
*/
|
|
954
|
+
function paintMessage(row, body, text, { streaming = false, scope = rootScope } = {}) {
|
|
955
|
+
const split = splitFollowups(text, { streaming });
|
|
956
|
+
renderStreamingMarkdown(body, split.body, renderMarkdownInto, { streaming });
|
|
957
|
+
if (!streaming && split.block && scope === rootScope) attachFollowups(row, split.block);
|
|
958
|
+
return split;
|
|
959
|
+
}
|
|
960
|
+
/** What a streaming text shows: '' while it is nothing but the start of a block. */
|
|
961
|
+
const shownText = (text) => splitFollowups(text, { streaming: true }).body;
|
|
962
|
+
|
|
963
|
+
// ── Offers ──
|
|
964
|
+
/**
|
|
965
|
+
* The follow-ups card under a message row: once per row, registered as an open offer, and presented by
|
|
966
|
+
* the mascot (its face is live while it is the newest open card: activateRigs).
|
|
967
|
+
*/
|
|
968
|
+
function attachFollowups(row, set) {
|
|
969
|
+
if (!row || !set?.items?.length) return null;
|
|
970
|
+
if (row._followups?.el.isConnected) return row._followups;
|
|
971
|
+
// One turn, one card: a set offered earlier in this turn (another text segment between tool calls, a
|
|
972
|
+
// streamed row the final message replaced) is superseded by this one. `offers` holds only this turn's:
|
|
973
|
+
// the user's prompt and a new turn settle and drop the earlier ones (settleOffers), which stay as history.
|
|
974
|
+
supersedeFollowups(row);
|
|
975
|
+
const card = createFollowupsCard(set, {
|
|
976
|
+
t,
|
|
977
|
+
// A card drawn from the journal comes in quietly; a live one with a nod.
|
|
978
|
+
entrance: calm() ? null : 'nod',
|
|
979
|
+
onSend: (text, meta) => hooks.onFollowup?.(text, meta),
|
|
980
|
+
onOther: () => hooks.onFollowupOther?.(),
|
|
981
|
+
onFocusComposer: () => hooks.onFollowupOther?.(),
|
|
982
|
+
});
|
|
983
|
+
row._followups = card;
|
|
984
|
+
card._row = row;
|
|
985
|
+
(row.querySelector('.asst-msg-content') || row).appendChild(card.el);
|
|
986
|
+
offers.add(card);
|
|
987
|
+
registerAsk(card);
|
|
988
|
+
enterRow(card.el);
|
|
989
|
+
scrollEnd();
|
|
990
|
+
return card;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/** Take out this turn's open follow-ups cards that belong to a row other than `row`. */
|
|
994
|
+
function supersedeFollowups(row) {
|
|
995
|
+
for (const offer of [...offers]) {
|
|
996
|
+
if (!offer._row || offer._row === row) continue;
|
|
997
|
+
offers.delete(offer);
|
|
998
|
+
askCards.delete(offer);
|
|
999
|
+
try { offer.presenter?.destroy?.(); } catch { /* gone */ }
|
|
1000
|
+
if (offer._row._followups === offer) offer._row._followups = null;
|
|
1001
|
+
offer.el?.remove();
|
|
1002
|
+
}
|
|
1003
|
+
askTail.sync();
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* A finished turn whose last message still ends in a list of options or an "I can X, or Y" offer,
|
|
1008
|
+
* with no block: best-effort items read from the prose. Once per message.
|
|
1009
|
+
*/
|
|
1010
|
+
function guessOffer() {
|
|
1011
|
+
const row = lastTextRow;
|
|
1012
|
+
if (!row?.isConnected || row._followups || row._guessed || row.classList.contains('asst-msg-error')) return;
|
|
1013
|
+
// The turn already offered a set from its block: a guess from the prose would be a second card.
|
|
1014
|
+
for (const offer of offers) if (offer._row && offer.el?.isConnected) return;
|
|
1015
|
+
row._guessed = true;
|
|
1016
|
+
const set = guessFollowups(row._text || '');
|
|
1017
|
+
if (set) attachFollowups(row, set);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* The user's next prompt reached the transcript (`text`), or a new turn started (no text): every open
|
|
1022
|
+
* offer is answered by it or goes stale. A guessed set nobody took leaves: its message says it in words.
|
|
1023
|
+
*/
|
|
1024
|
+
function settleOffers(text = null) {
|
|
1025
|
+
for (const offer of [...offers]) {
|
|
1026
|
+
offers.delete(offer);
|
|
1027
|
+
if (!offer.el?.isConnected) continue;
|
|
1028
|
+
const answered = text !== null ? offer.settle(text) : (offer.stale(), false);
|
|
1029
|
+
if (!answered && offer.set?.source === 'heuristic') offer.el.remove();
|
|
1030
|
+
}
|
|
1031
|
+
if (text !== null) lastTextRow = null;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
const sameText = (a, b) => String(a ?? '').replace(/\s+/g, ' ').trim().toLowerCase() === String(b ?? '').replace(/\s+/g, ' ').trim().toLowerCase();
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* A provider's usage limit (asst-limit.js) updates the conversation's single bottom countdown.
|
|
1038
|
+
* The reset time is the session's own limit report while live (hooks.limitInfo: the Claude SDK's
|
|
1039
|
+
* rate_limit_info), else the one the message names, read from when it was written. Retry now sends one
|
|
1040
|
+
* message that retries the blocked work.
|
|
1041
|
+
*/
|
|
1042
|
+
function offerLimitWait(text, { brainError = false, limit = null } = {}) {
|
|
1043
|
+
const read = limitFromText(text, { now: nowMs() });
|
|
1044
|
+
let info = limit;
|
|
1045
|
+
if (!info && !muted) { try { info = hooks.limitInfo?.() || null; } catch { info = null; } }
|
|
1046
|
+
const reported = info?.status === 'rejected';
|
|
1047
|
+
// The brain's own error while the session's report says the limit holds is that limit, whatever its words.
|
|
1048
|
+
if (!read && !(brainError && reported)) return null;
|
|
1049
|
+
const resetsAt = reported ? info.resetsAt : read?.resetsAt;
|
|
1050
|
+
const retryText = t('assistant.limitWait.continue', 'The usage limit has reset. Retry what was blocked and continue where you left off.');
|
|
1051
|
+
let providerId = '';
|
|
1052
|
+
try { providerId = hooks.brainProvider?.() || ''; } catch { /* unknown */ }
|
|
1053
|
+
const provider = providerId ? providerShortLabel(providerId) : '';
|
|
1054
|
+
return limitDock.set('brain', {
|
|
1055
|
+
resetsAt, since: nowMs(), provider,
|
|
1056
|
+
onRetry: () => { limitDock.set('brain', null); hooks.onLimitRetry?.(retryText); },
|
|
1057
|
+
onSwitchProvider: typeof hooks.onSwitchProvider === 'function'
|
|
1058
|
+
? (anchor) => hooks.onSwitchProvider(anchor, { excludeProvider: providerId, retryText })
|
|
1059
|
+
: null,
|
|
1060
|
+
onClose: () => limitDock.set('brain', null),
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/** The session's limit report changed (assistant:limit): this turn's waiting card counts to its reset time. */
|
|
1065
|
+
function noteLimit(limit) {
|
|
1066
|
+
if (limit?.status === 'rejected') offerLimitWait('', { brainError: true, limit });
|
|
1067
|
+
else if (limit?.status && limit.status !== 'rejected') limitDock.set('brain', null);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
828
1070
|
function copyText(text) {
|
|
829
1071
|
const value = String(text ?? '');
|
|
830
1072
|
if (!value) return;
|
|
@@ -857,10 +1099,18 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
857
1099
|
if (!s) return;
|
|
858
1100
|
if (s.mdTimer) { clearTimeout(s.mdTimer); s.mdTimer = null; }
|
|
859
1101
|
if (s.thinkTimer) { clearTimeout(s.thinkTimer); s.thinkTimer = null; }
|
|
860
|
-
if (s.bodyEl && s.textBuf.trim())
|
|
1102
|
+
if (s.bodyEl && s.textBuf.trim()) paintMessage(s.row, s.bodyEl, s.textBuf, { streaming: true, scope });
|
|
861
1103
|
if (s.think) refreshStep(s.think);
|
|
862
1104
|
}
|
|
863
1105
|
|
|
1106
|
+
/** A streamed row that stays as the message (no final message replaced it): its text is final now. */
|
|
1107
|
+
function sealStreamRow(scope) {
|
|
1108
|
+
const s = scope.stream;
|
|
1109
|
+
if (!s?.row || !s.bodyEl || !s.textBuf.trim()) return;
|
|
1110
|
+
s.row.classList.remove('streaming');
|
|
1111
|
+
paintMessage(s.row, s.bodyEl, s.textBuf, { scope });
|
|
1112
|
+
}
|
|
1113
|
+
|
|
864
1114
|
/** The stream's text row goes (the final message renders the text again). */
|
|
865
1115
|
function dropStreamText(scope) {
|
|
866
1116
|
const s = scope.stream;
|
|
@@ -930,10 +1180,12 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
930
1180
|
if (!s.thinkTimer) s.thinkTimer = setTimeout(() => { s.thinkTimer = null; refreshStep(step); if (step.rack.live) paintStage(step.rack); }, STREAM_TICK_MS);
|
|
931
1181
|
} else if (delta.type === 'text_delta' && delta.text != null) {
|
|
932
1182
|
s.textBuf += delta.text;
|
|
933
|
-
|
|
1183
|
+
// A row once there is something to show: a follow-ups block on its own is the card, later.
|
|
1184
|
+
if (!s.bodyEl && shownText(s.textBuf).trim()) {
|
|
934
1185
|
const { row, wrap } = assistantRow(scope, { _text: s.textBuf, _msgId: s.msgId, _claude: s.claude });
|
|
935
1186
|
row.classList.add('streaming');
|
|
936
1187
|
noteAnswer(scope, row);
|
|
1188
|
+
if (scope === rootScope) lastTextRow = row;
|
|
937
1189
|
s.row = row;
|
|
938
1190
|
s.wrap = wrap;
|
|
939
1191
|
s.bodyEl = el('div', 'asst-md');
|
|
@@ -944,7 +1196,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
944
1196
|
s.mdTimer = setTimeout(() => {
|
|
945
1197
|
s.mdTimer = null;
|
|
946
1198
|
if (!s.bodyEl) return;
|
|
947
|
-
|
|
1199
|
+
paintMessage(s.row, s.bodyEl, s.textBuf, { streaming: true, scope });
|
|
948
1200
|
scrollEnd();
|
|
949
1201
|
}, STREAM_TICK_MS);
|
|
950
1202
|
}
|
|
@@ -953,7 +1205,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
953
1205
|
}
|
|
954
1206
|
if (type === 'content_block_stop') {
|
|
955
1207
|
const s = scope.stream;
|
|
956
|
-
if (s?.bodyEl && s.mdTimer) { clearTimeout(s.mdTimer); s.mdTimer = null;
|
|
1208
|
+
if (s?.bodyEl && s.mdTimer) { clearTimeout(s.mdTimer); s.mdTimer = null; paintMessage(s.row, s.bodyEl, s.textBuf, { streaming: true, scope }); }
|
|
957
1209
|
if (s?.think && s.thinkTimer) { clearTimeout(s.thinkTimer); s.thinkTimer = null; refreshStep(s.think); }
|
|
958
1210
|
}
|
|
959
1211
|
}
|
|
@@ -1023,9 +1275,10 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1023
1275
|
function textRow(scope, text, ctx = {}) {
|
|
1024
1276
|
const { row, wrap } = assistantRow(scope, { _text: text, _msgId: ctx.msgId || null, _claude: !!ctx.claude });
|
|
1025
1277
|
const body = el('div', 'asst-md');
|
|
1026
|
-
renderMarkdownInto(body, text);
|
|
1027
1278
|
wrap.appendChild(body);
|
|
1279
|
+
paintMessage(row, body, text, { scope });
|
|
1028
1280
|
noteAnswer(scope, row);
|
|
1281
|
+
if (scope === rootScope) lastTextRow = row;
|
|
1029
1282
|
return row;
|
|
1030
1283
|
}
|
|
1031
1284
|
|
|
@@ -1052,7 +1305,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1052
1305
|
else if (s.row) {
|
|
1053
1306
|
s.row.classList.remove('streaming');
|
|
1054
1307
|
if (!s.wrap.children.length) s.row.remove();
|
|
1055
|
-
else if (s.textBuf.trim()) (scope.kept ||= new Set()).add(textKey(s.textBuf));
|
|
1308
|
+
else if (s.textBuf.trim()) { (scope.kept ||= new Set()).add(textKey(s.textBuf)); sealStreamRow(scope); }
|
|
1056
1309
|
}
|
|
1057
1310
|
if (streamedThink && !thinks.length) finishThink(streamedThink);
|
|
1058
1311
|
scope.stream = null;
|
|
@@ -1148,7 +1401,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1148
1401
|
const kind = kindOf(block.name, block.input);
|
|
1149
1402
|
const step = {
|
|
1150
1403
|
type: 'call', id: block.id || `call-${++stepSeq}`, seq: ++stepSeq, name: block.name || 'tool', kind, key: stationKey(kind, block.name),
|
|
1151
|
-
input: block.input
|
|
1404
|
+
input: canonicalToolInput(block.input), state: 'running', result: '', detail: '', code: null, startedAt: nowMs(), endedAt: 0, progress: 0,
|
|
1152
1405
|
timed: isTimed(), ghost, stale: false, rack, station: null, card: null, parent: parentOf(rack.scope), bodyBuilt: false,
|
|
1153
1406
|
...(extra || {}),
|
|
1154
1407
|
};
|
|
@@ -1163,7 +1416,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1163
1416
|
/** The final block of a streamed call, or the same block replayed: the step learns its input, nothing is added. */
|
|
1164
1417
|
function updateCall(step, block) {
|
|
1165
1418
|
if (block.name) step.name = block.name;
|
|
1166
|
-
if (block.input && typeof block.input === 'object' && Object.keys(block.input).length) step.input = block.input;
|
|
1419
|
+
if (block.input && typeof block.input === 'object' && Object.keys(block.input).length) step.input = canonicalToolInput(block.input);
|
|
1167
1420
|
step.ghost = false;
|
|
1168
1421
|
if (step.card?._computer) { step.card._computer.input = step.input; step.card._computer.description = describeComputerAction(step.input, step.name); }
|
|
1169
1422
|
refreshStep(step);
|
|
@@ -1213,10 +1466,10 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1213
1466
|
return prev;
|
|
1214
1467
|
}
|
|
1215
1468
|
|
|
1216
|
-
/** The scope's last row, skipping "Working…" and rows folding into a rack. */
|
|
1469
|
+
/** The scope's last row, skipping "Working…", the open card's slot and rows folding into a rack. */
|
|
1217
1470
|
function tailRow(scope) {
|
|
1218
1471
|
let node = scope.container.lastElementChild;
|
|
1219
|
-
while (node && (node === working || node.classList.contains('asst-folding'))) node = node.previousElementSibling;
|
|
1472
|
+
while (node && (node === working || node === askTail.slot || node.classList.contains('asst-folding'))) node = node.previousElementSibling;
|
|
1220
1473
|
return node;
|
|
1221
1474
|
}
|
|
1222
1475
|
|
|
@@ -1326,7 +1579,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1326
1579
|
row.innerHTML = `<div class="asst-rack-stage">
|
|
1327
1580
|
<div class="asst-rack-set"><div class="asst-rack-face" aria-hidden="true"></div><div class="asst-rack-scene" aria-hidden="true"></div></div>
|
|
1328
1581
|
<div class="asst-rack-caption"></div>
|
|
1329
|
-
<div class="asst-bubble" aria-live="off" hidden><span class="asst-bubble-verb"></span><span class="asst-bubble-subject" role="button" tabindex="-1" aria-expanded="false"><span class="asst-bubble-text"></span
|
|
1582
|
+
<div class="asst-bubble" aria-live="off" hidden><span class="asst-bubble-line"><span class="asst-bubble-verb"></span><span class="asst-bubble-subject" role="button" tabindex="-1" aria-expanded="false"><span class="asst-bubble-text"></span></span><span class="asst-bubble-meta"></span></span><span class="asst-bubble-err" hidden></span></div>
|
|
1330
1583
|
<div class="asst-rack-sweep" aria-hidden="true"></div>
|
|
1331
1584
|
</div>
|
|
1332
1585
|
<button type="button" class="asst-rack-receipt" aria-expanded="false" aria-controls="${ledgerId}"><span class="asst-rack-receipt-face" aria-hidden="true"></span><span class="asst-rack-receipt-text"></span><span class="asst-rack-receipt-failed" hidden></span><span class="asst-rack-receipt-clock"></span><span class="asst-rack-chev" aria-hidden="true">›</span></button>
|
|
@@ -1340,7 +1593,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1340
1593
|
// The direct route's class whose scene the stage shows: set here, or once when a reopened conversation's route
|
|
1341
1594
|
// resumes (lendRouteScene); a rack never loses its scene.
|
|
1342
1595
|
sceneClass: nested ? '' : directSceneClass(), sceneId: '', sceneAt: -Infinity,
|
|
1343
|
-
stage: { el: q('.asst-rack-stage'), set: q('.asst-rack-set'), face: q('.asst-rack-face'), scene: q('.asst-rack-scene'), caption: q('.asst-rack-caption'), bubble: q('.asst-bubble'), verb: q('.asst-bubble-verb'), subject: q('.asst-bubble-subject'), text: q('.asst-bubble-text'), meta: q('.asst-bubble-meta'), err: q('.asst-bubble-err') },
|
|
1596
|
+
stage: { el: q('.asst-rack-stage'), set: q('.asst-rack-set'), face: q('.asst-rack-face'), scene: q('.asst-rack-scene'), caption: q('.asst-rack-caption'), bubble: q('.asst-bubble'), line: q('.asst-bubble-line'), verb: q('.asst-bubble-verb'), subject: q('.asst-bubble-subject'), text: q('.asst-bubble-text'), meta: q('.asst-bubble-meta'), err: q('.asst-bubble-err') },
|
|
1344
1597
|
receipt: { btn: q('.asst-rack-receipt'), face: q('.asst-rack-receipt-face'), text: q('.asst-rack-receipt-text'), failed: q('.asst-rack-receipt-failed'), clock: q('.asst-rack-receipt-clock'), faceBuilt: false, pose: '', svg: null },
|
|
1345
1598
|
ledger: q('.asst-rack-ledger'), statusEl: q('.asst-rack-status'), alertEl: q('.asst-rack-alert'), timelineEl: null, timelineSig: '',
|
|
1346
1599
|
rig: null, target: null, held: false, holdUntil: 0, holdTimer: 0, pose: null, awake: null, awakeKey: null,
|
|
@@ -1631,6 +1884,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1631
1884
|
s.text.textContent = '';
|
|
1632
1885
|
s.subject.removeAttribute('title');
|
|
1633
1886
|
s.subject.className = 'asst-bubble-subject';
|
|
1887
|
+
s.subject.hidden = false;
|
|
1634
1888
|
s.subject.setAttribute('aria-expanded', 'false');
|
|
1635
1889
|
s.meta.textContent = '';
|
|
1636
1890
|
s.err.textContent = '';
|
|
@@ -1640,15 +1894,21 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1640
1894
|
}
|
|
1641
1895
|
|
|
1642
1896
|
/**
|
|
1643
|
-
* One rig of this transcript animates: the hero, else the newest
|
|
1644
|
-
* the
|
|
1645
|
-
*
|
|
1897
|
+
* One rig of this transcript animates: the hero, else the newest open card
|
|
1898
|
+
* the mascot presents (a follow-ups card, a question card: its face is a rig
|
|
1899
|
+
* only then, and never while calm), else the newest stage, else the
|
|
1900
|
+
* "Thinking" row's. The rule ends at this renderer: every other panel runs
|
|
1901
|
+
* its own, so N live sessions show N moving characters.
|
|
1646
1902
|
*/
|
|
1647
1903
|
function activateRigs() {
|
|
1648
|
-
const
|
|
1904
|
+
const ask = hero?.rig ? null : liveAsk();
|
|
1905
|
+
const askLive = !!ask && !calm();
|
|
1906
|
+
for (const handle of askCards) handle.presenter?.setLive?.(askLive && handle === ask);
|
|
1907
|
+
const asking = askLive && !!ask.presenter?.isLive?.();
|
|
1908
|
+
const newest = hero?.rig || asking ? null : newestStage();
|
|
1649
1909
|
for (const rack of racks) if (rack.rig) rack.rig.setActive?.(rack === newest);
|
|
1650
1910
|
hero?.rig?.setActive?.(true);
|
|
1651
|
-
workingFace?.rig?.setActive?.(!hero?.rig && !newest);
|
|
1911
|
+
workingFace?.rig?.setActive?.(!hero?.rig && !asking && !newest);
|
|
1652
1912
|
}
|
|
1653
1913
|
|
|
1654
1914
|
/** The bubble's call: the awake one, else the newest; it holds 900 ms (3 s for thinking) before the next wins. */
|
|
@@ -1758,29 +2018,38 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1758
2018
|
const held = rack.waiting && step === waitingStep(rack);
|
|
1759
2019
|
const verb = held ? t('assistant.rack.waiting', 'Waiting for you') : verbText(say.verb, running, t);
|
|
1760
2020
|
const grouped = groupedSubject(rack, step);
|
|
1761
|
-
|
|
2021
|
+
// A thought with no words yet says only "Thinking" (the mascot's thought dots are the one busy sign):
|
|
2022
|
+
// the subject's "…" placeholder stays for a call whose input is still streaming.
|
|
2023
|
+
const bare = step.type === 'think' && say.text === '…';
|
|
2024
|
+
// A command as the stage says it: one "…" however many paths its summary shortens (say.stage).
|
|
2025
|
+
const full = bare ? '' : grouped || say.stage || say.text;
|
|
1762
2026
|
const literal = !grouped && say.full ? say.full : full; // the command as written: the title and the opened bubble
|
|
1763
2027
|
const open = rack.bubble.open;
|
|
1764
|
-
// Narrow stages cut from the head, so the file name (the tail) survives. A small budget keeps a
|
|
1765
|
-
// shorter tail (24 chars of it would not fit beside the verb).
|
|
1766
|
-
const cut = subjectCut(rack);
|
|
1767
|
-
const cutTo = (n) => (open ? literal : cut === 'head' ? headEllipsis(full, n) : middleEllipsis(full, n, Math.min(24, Math.max(8, n - 6))));
|
|
1768
|
-
const shownText = cutTo(subjectBudget(rack, verb, cut));
|
|
1769
|
-
const key = `${step.id}|${verb}|${shownText}|${say.muted ? 1 : 0}`;
|
|
1770
2028
|
s.bubble.hidden = !full && !verb;
|
|
1771
2029
|
s.bubble.dataset.state = held ? 'waiting' : running ? 'running' : step.state;
|
|
1772
2030
|
s.bubble.dataset.kind = step.kind;
|
|
2031
|
+
// The numbers first: they share the subject's line, so the cut below measures the room they leave.
|
|
2032
|
+
paintBubbleMeta(rack, step, say, now);
|
|
2033
|
+
// Narrow stages cut from the head, so the file name (the tail) survives. A small budget keeps a
|
|
2034
|
+
// shorter tail (24 chars of it would not fit beside the verb). A subject already cut from its head
|
|
2035
|
+
// (a streamed thought's tail) is cut from the head again: one "…", never two.
|
|
2036
|
+
const cut = full.startsWith('…') ? 'head' : subjectCut(rack);
|
|
2037
|
+
const cutTo = (n) => (open ? literal : cut === 'head' ? headEllipsis(full, n) : middleEllipsis(full, n, Math.min(24, Math.max(8, n - 6))));
|
|
2038
|
+
const shownText = bare ? '' : cutTo(subjectBudget(rack, verb, cut));
|
|
2039
|
+
const key = `${step.id}|${verb}|${shownText}|${say.muted ? 1 : 0}|${bare ? 1 : 0}`;
|
|
1773
2040
|
if (key !== rack.bubble.key) {
|
|
1774
2041
|
const changedStep = !rack.bubble.key.startsWith(`${step.id}|`);
|
|
1775
2042
|
rack.bubble.key = key;
|
|
1776
2043
|
const write = () => {
|
|
1777
2044
|
s.verb.textContent = verb;
|
|
1778
2045
|
s.subject.className = `asst-bubble-subject${say.mono ? ' is-mono' : ''}${say.muted ? ' is-muted' : ''}`;
|
|
2046
|
+
s.subject.hidden = bare;
|
|
1779
2047
|
const segments = grouped || open ? null : say.segments;
|
|
1780
2048
|
writeSubject(s.text, segments, shownText);
|
|
1781
2049
|
// The budget is an estimate (a long token wraps whole to the next line): what the two-line
|
|
1782
2050
|
// clamp would hide is cut here instead, so the subject is never cut a second time.
|
|
1783
|
-
const fitted = open ? shownText : fitSubject(s, shownText, cutTo, segments);
|
|
2051
|
+
const fitted = open || bare ? shownText : fitSubject(s, shownText, cutTo, segments);
|
|
2052
|
+
s.subject.classList.toggle('is-cut', fitted.startsWith('…'));
|
|
1784
2053
|
if (literal !== fitted || open) s.subject.title = literal; else s.subject.removeAttribute('title');
|
|
1785
2054
|
};
|
|
1786
2055
|
if (swap && changedStep && !calm() && typeof s.subject.animate === 'function' && s.text.textContent) {
|
|
@@ -1793,7 +2062,6 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1793
2062
|
};
|
|
1794
2063
|
} else write();
|
|
1795
2064
|
}
|
|
1796
|
-
paintBubbleMeta(rack, step, say, now);
|
|
1797
2065
|
const err = step.state === 'error' ? (step.detail || exitText(step)) : '';
|
|
1798
2066
|
s.err.hidden = !err;
|
|
1799
2067
|
if (s.err.textContent !== err) s.err.textContent = err;
|
|
@@ -1804,10 +2072,11 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1804
2072
|
}
|
|
1805
2073
|
|
|
1806
2074
|
/**
|
|
1807
|
-
* How many characters of subject fit the bubble: two lines of
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1810
|
-
*
|
|
2075
|
+
* How many characters of subject fit the bubble: two lines of its one
|
|
2076
|
+
* text flow, less the verb at the head and the numbers at the end,
|
|
2077
|
+
* measured on the laid-out stage. The text is cut in the middle so its
|
|
2078
|
+
* tail (the file, the last argument) stays; on a narrow stage from the
|
|
2079
|
+
* head (subjectCut).
|
|
1811
2080
|
*/
|
|
1812
2081
|
function subjectBudget(rack, verb, cut = 'middle') {
|
|
1813
2082
|
const s = rack.stage;
|
|
@@ -1817,11 +2086,18 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1817
2086
|
const bubble = s.bubble.hidden ? null : s.bubble.getBoundingClientRect();
|
|
1818
2087
|
const beside = !bubble || bubble.left >= face.right - 1;
|
|
1819
2088
|
const verbPx = Math.max(s.verb.getBoundingClientRect().width, verb.length * 7);
|
|
1820
|
-
|
|
2089
|
+
// The numbers at the end of the line (painted first): a running call keeps a clock's room before it shows.
|
|
2090
|
+
const metaPx = s.meta.textContent ? s.meta.getBoundingClientRect().width : 0;
|
|
2091
|
+
const numbersPx = Math.max(metaPx, rack.bubble.step?.state === 'running' ? 28 : 0);
|
|
2092
|
+
// The bubble's text column: the stage's 14px sides, the notch's 8px, the bubble's 10px sides.
|
|
2093
|
+
const column = width - 28 - 8 - 20 - (beside ? face.width + 8 : 0);
|
|
1821
2094
|
const perLine = Math.max(12, Math.floor(column / 7));
|
|
1822
|
-
// Two lines, less what wrapping at token boundaries wastes (a head cut
|
|
1823
|
-
|
|
1824
|
-
|
|
2095
|
+
// Two lines, less what wrapping at token boundaries wastes (a head cut is one tail: close to two full
|
|
2096
|
+
// lines, a path's pieces wrapping at its separators); a guess on the generous side, since fitSubject
|
|
2097
|
+
// cuts what the clamp would hide or a piece would push past the edge.
|
|
2098
|
+
const lines = getComputedStyle(s.line).whiteSpace === 'nowrap' ? 1 : cut === 'head' ? 2 : 1.85;
|
|
2099
|
+
const taken = Math.ceil((verbPx + 7 + (numbersPx ? numbersPx + 7 : 0)) / 7);
|
|
2100
|
+
return Math.max(24, Math.min(240, Math.floor(perLine * lines) - taken - 1));
|
|
1825
2101
|
}
|
|
1826
2102
|
|
|
1827
2103
|
/** 'head' where the stylesheet says so (≤320 px: keep the tail), else 'middle'. */
|
|
@@ -1830,30 +2106,53 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1830
2106
|
}
|
|
1831
2107
|
|
|
1832
2108
|
/**
|
|
1833
|
-
*
|
|
1834
|
-
*
|
|
2109
|
+
* The subject's text, in its segments' spans (a read's dim folder, its
|
|
2110
|
+
* bold name). A URL or path in it is one `.asst-bubble-piece` (nowrap) per
|
|
2111
|
+
* piece with a <wbr> between them (wrapRuns): it wraps after "/" and at
|
|
2112
|
+
* its other separators, never mid-word.
|
|
1835
2113
|
*/
|
|
1836
|
-
function headEllipsis(text, max) {
|
|
1837
|
-
const s = String(text ?? '');
|
|
1838
|
-
if (s.length <= max) return s;
|
|
1839
|
-
let tail = s.slice(s.length - Math.max(8, max - 1));
|
|
1840
|
-
const cut = tail.search(/[\s/]/);
|
|
1841
|
-
if (cut > 0 && cut < tail.length * 0.4) tail = tail.slice(tail[cut] === '/' ? cut : cut + 1);
|
|
1842
|
-
return joinCut('', tail);
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
2114
|
function writeSubject(node, segments, text) {
|
|
1846
2115
|
node.textContent = '';
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
2116
|
+
const segs = Array.isArray(segments) && segments.length && segments.map(x => x.text).join('') === text ? segments : null;
|
|
2117
|
+
let end = 0;
|
|
2118
|
+
const bounds = segs ? segs.map(seg => [end += seg.text.length, seg.cls]) : [[text.length, null]];
|
|
2119
|
+
// text[from, to) into `host`, one span per segment it crosses.
|
|
2120
|
+
const put = (host, from, to) => {
|
|
2121
|
+
for (const [stop, cls] of bounds) {
|
|
2122
|
+
if (from >= to) break;
|
|
2123
|
+
if (stop <= from) continue;
|
|
2124
|
+
const slice = text.slice(from, Math.min(stop, to));
|
|
2125
|
+
host.appendChild(segs ? el('span', cls ? `is-${cls}` : '', slice) : document.createTextNode(slice));
|
|
2126
|
+
from += slice.length;
|
|
2127
|
+
}
|
|
2128
|
+
};
|
|
2129
|
+
let at = 0;
|
|
2130
|
+
for (const run of wrapRuns(text)) {
|
|
2131
|
+
if (!run.pieces) { put(node, at, at += run.text.length); continue; }
|
|
2132
|
+
run.pieces.forEach((piece, i) => {
|
|
2133
|
+
if (i) node.appendChild(document.createElement('wbr'));
|
|
2134
|
+
put(node.appendChild(el('span', 'asst-bubble-piece')), at, at += piece.length);
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
1850
2137
|
}
|
|
1851
2138
|
|
|
1852
|
-
/**
|
|
2139
|
+
/** A path piece wider than what is left of its line: it runs past the line's edge, where the line's overflow hides it. */
|
|
2140
|
+
function pieceOverflows(s) {
|
|
2141
|
+
const pieces = s.text.getElementsByClassName('asst-bubble-piece');
|
|
2142
|
+
if (!pieces.length) return false;
|
|
2143
|
+
const right = s.line.getBoundingClientRect().right + 0.5;
|
|
2144
|
+
for (const piece of pieces) for (const r of piece.getClientRects()) if (r.right > right) return true;
|
|
2145
|
+
return false;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
/**
|
|
2149
|
+
* The bubble's subject cut shorter (`cutTo`, six steps at most) until the line's clamp hides none of it
|
|
2150
|
+
* (nor the numbers after it) and no path piece runs past its edge; the text it shows.
|
|
2151
|
+
*/
|
|
1853
2152
|
function fitSubject(s, text, cutTo, segments) {
|
|
1854
2153
|
let shown = text;
|
|
1855
2154
|
let budget = shown.length;
|
|
1856
|
-
for (let i = 0; i < 6 && budget > 12 && s.
|
|
2155
|
+
for (let i = 0; i < 6 && budget > 12 && (s.line.scrollHeight > s.line.clientHeight + 1 || pieceOverflows(s)); i += 1) {
|
|
1857
2156
|
budget = Math.floor(budget * 0.85);
|
|
1858
2157
|
shown = cutTo(budget);
|
|
1859
2158
|
writeSubject(s.text, segments, shown);
|
|
@@ -1882,7 +2181,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
1882
2181
|
// A subagent's bubble follows its child: the step the agent is on now.
|
|
1883
2182
|
if (step.kind === 'subagent' && step.children?.length) {
|
|
1884
2183
|
const child = step.children.filter(c => c.type === 'call' || (c.state === 'running' && !c.stale)).reduce((a, b) => (!a || b.startedAt >= a.startedAt ? b : a), null);
|
|
1885
|
-
if (child) parts.push(['m', middleEllipsis(stepSentence(child, child.state === 'running' && !child.stale), 60, 24)]);
|
|
2184
|
+
if (child) parts.push(['m', middleEllipsis(stepSentence(child, child.state === 'running' && !child.stale, { stage: true }), 60, 24)]);
|
|
1886
2185
|
}
|
|
1887
2186
|
// ×N only for the same target again (a re-run, a re-read): a number the ledger agrees with.
|
|
1888
2187
|
// Other calls in the same 800 ms burst read as a sentence instead.
|
|
@@ -2176,10 +2475,10 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2176
2475
|
return t('assistant.rack.state.done', 'done');
|
|
2177
2476
|
}
|
|
2178
2477
|
|
|
2179
|
-
/** "Running npm test" / "Ran npm test". */
|
|
2180
|
-
function stepSentence(step, running) {
|
|
2478
|
+
/** "Running npm test" / "Ran npm test"; `stage`: as the stage bubble says it (say.stage). */
|
|
2479
|
+
function stepSentence(step, running, { stage = false } = {}) {
|
|
2181
2480
|
const say = sayOf(step);
|
|
2182
|
-
return `${verbText(say.verb, running, t)} ${String(say.text || '').replace(/^\$ /, '')}`.trim();
|
|
2481
|
+
return `${verbText(say.verb, running, t)} ${String((stage && say.stage) || say.text || '').replace(/^\$ /, '')}`.trim();
|
|
2183
2482
|
}
|
|
2184
2483
|
|
|
2185
2484
|
/** The receipt: one call says what it did; a burst says it in one sentence. */
|
|
@@ -2681,6 +2980,9 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2681
2980
|
}
|
|
2682
2981
|
|
|
2683
2982
|
function appendUser(text, { images = [], files = [], memories = [], origin = null, imageCount = 0 } = {}) {
|
|
2983
|
+
// The prompt answers what the last turn offered (one of its follow-ups, a retry), or it leaves it behind.
|
|
2984
|
+
settleOffers(String(text ?? ''));
|
|
2985
|
+
limitDock.set('brain', null);
|
|
2684
2986
|
turnFrames = [];
|
|
2685
2987
|
directRoute = null; // a new prompt is a new task: its own route decides
|
|
2686
2988
|
lastDirect = null;
|
|
@@ -2720,17 +3022,21 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2720
3022
|
}
|
|
2721
3023
|
|
|
2722
3024
|
function appendError(text) {
|
|
2723
|
-
|
|
3025
|
+
const row = appendStatus(text || t('assistant.errorGeneric', 'Something went wrong'), 'error');
|
|
3026
|
+
if (text) offerLimitWait(text); // a usage limit: the waiting card under it
|
|
3027
|
+
return row;
|
|
2724
3028
|
}
|
|
2725
3029
|
|
|
2726
3030
|
function appendDivider(label) {
|
|
2727
3031
|
return append(el('div', 'asst-divider', label || ''));
|
|
2728
3032
|
}
|
|
2729
3033
|
|
|
2730
|
-
/** Place an arbitrary node (control / route card) as a transcript row. */
|
|
3034
|
+
/** Place an arbitrary node (control / route card) as a transcript row; a card that asks is presented by the mascot. */
|
|
2731
3035
|
function appendNode(node) {
|
|
2732
3036
|
if (!node) return null;
|
|
2733
|
-
|
|
3037
|
+
const row = append(node);
|
|
3038
|
+
if (node._ask) registerAsk(node._ask);
|
|
3039
|
+
return row;
|
|
2734
3040
|
}
|
|
2735
3041
|
|
|
2736
3042
|
function showWorking(label) {
|
|
@@ -2744,6 +3050,9 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2744
3050
|
hideEmpty({ park: true });
|
|
2745
3051
|
$msgs.appendChild(working);
|
|
2746
3052
|
if (!turnActive) {
|
|
3053
|
+
// A new turn: what the last one offered and nobody took is no longer clickable.
|
|
3054
|
+
settleOffers();
|
|
3055
|
+
limitDock.set('brain', null);
|
|
2747
3056
|
turn = { calls: 0, last: null, errored: false, answerRow: null };
|
|
2748
3057
|
turnSeq += 1;
|
|
2749
3058
|
if (directRoute && directRoute.turn == null) { // a route picked while no turn ran, or one the journal left running, is this turn's
|
|
@@ -2831,6 +3140,12 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2831
3140
|
onStop: hooks.onStopRun,
|
|
2832
3141
|
onRead: hooks.onReadRun,
|
|
2833
3142
|
onEscalate: hooks.onEscalateRun,
|
|
3143
|
+
onLimitWait: (run, limit) => limitDock.set(`run:${run.runId}`, limit ? {
|
|
3144
|
+
resetsAt: limit.resetsAt || null,
|
|
3145
|
+
since: Date.parse(limit.detectedAt || run.completedAt || '') || nowMs(),
|
|
3146
|
+
provider: providerShortLabel(run.provider || 'claude-code'),
|
|
3147
|
+
onRetry: () => { limitDock.set(`run:${run.runId}`, null); hooks.onEscalateRun?.(run.runId, limitRetryTarget(run)); },
|
|
3148
|
+
} : null),
|
|
2834
3149
|
result,
|
|
2835
3150
|
keepSummary: true,
|
|
2836
3151
|
};
|
|
@@ -2876,7 +3191,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
2876
3191
|
if (!run?.runId) return null;
|
|
2877
3192
|
noteRun(run);
|
|
2878
3193
|
const card = runCards.get(run.runId);
|
|
2879
|
-
if (!card?.isConnected) return null;
|
|
3194
|
+
if (!card?.isConnected) { limitDock.set(`run:${run.runId}`, null); return null; }
|
|
2880
3195
|
updateRunCard(card, run, runCardOpts()); // fresh options: the run's own lastResult, never a result the card was built with
|
|
2881
3196
|
syncStage(run.runId, card, run);
|
|
2882
3197
|
return card;
|
|
@@ -3216,8 +3531,8 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3216
3531
|
// Under the reply once the turn has ended; a later snapshot (learning) never moves it again.
|
|
3217
3532
|
const ended = ev.phase !== 'sent';
|
|
3218
3533
|
if (fresh || (ended && !row.atEnd)) {
|
|
3219
|
-
// Placed like any row (before "Working…"), but a status line of the harness never dismisses the hero.
|
|
3220
|
-
if (working && working.parentNode === $msgs)
|
|
3534
|
+
// Placed like any row (above the open cards and before "Working…"), but a status line of the harness never dismisses the hero.
|
|
3535
|
+
if (askTail.slot || (working && working.parentNode === $msgs)) askTail.insert(row.el);
|
|
3221
3536
|
else $msgs.appendChild(row.el);
|
|
3222
3537
|
if (ended) row.atEnd = true;
|
|
3223
3538
|
prune();
|
|
@@ -3231,13 +3546,14 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3231
3546
|
function showEmpty() {
|
|
3232
3547
|
if (emptyEl || $msgs.querySelector(':scope > :not(.asst-working)')) return;
|
|
3233
3548
|
emptyEl = el('div', 'asst-empty');
|
|
3549
|
+
// One column, one hierarchy: the character and the heading, the greeting when there is one,
|
|
3550
|
+
// the starters, then the context line (Project · Budget). No lead: the starters say what it can do.
|
|
3234
3551
|
emptyEl.innerHTML = `
|
|
3235
3552
|
<div class="asst-empty-mascot" aria-hidden="true"></div>
|
|
3236
3553
|
<div class="asst-empty-title" role="heading" aria-level="2">${esc(t('assistant.empty.heading', 'What should we do?'))}</div>
|
|
3237
|
-
<div class="asst-empty-body">${esc(t('assistant.empty.lead', 'I can use your Mac, look things up on the web, edit files and run commands, and remember or schedule things.'))}</div>
|
|
3238
3554
|
<div class="asst-empty-greet"></div>
|
|
3239
|
-
<div class="asst-
|
|
3240
|
-
<div class="asst-
|
|
3555
|
+
<div class="asst-suggestions" role="list" aria-label="${esc(t('assistant.empty.starters', 'Starters'))}"></div>
|
|
3556
|
+
<div class="asst-empty-project" role="group" aria-label="${esc(t('assistant.empty.context', 'Context'))}" hidden></div>`;
|
|
3241
3557
|
const box = emptyEl.querySelector('.asst-empty-mascot');
|
|
3242
3558
|
try {
|
|
3243
3559
|
const rig = mascotLib.createMascot(box, { width: 120, height: 60 });
|
|
@@ -3245,8 +3561,8 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3245
3561
|
hero.target = heroTarget(hero);
|
|
3246
3562
|
wireEgg(hero);
|
|
3247
3563
|
} catch { hero = null; }
|
|
3248
|
-
// The project the conversation works in, under the
|
|
3249
|
-
// picker's project field), so it and the toolbar's Working project are one control.
|
|
3564
|
+
// The project the conversation works in, on the context line under the starters: the host draws
|
|
3565
|
+
// the picker (the brain picker's project field), so it and the toolbar's Working project are one control.
|
|
3250
3566
|
const projectSlot = emptyEl.querySelector('.asst-empty-project');
|
|
3251
3567
|
let picker = null;
|
|
3252
3568
|
try { picker = typeof hooks.mountEmptyProject === 'function' ? hooks.mountEmptyProject(projectSlot) : null; } catch { picker = null; }
|
|
@@ -3263,8 +3579,8 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3263
3579
|
}
|
|
3264
3580
|
|
|
3265
3581
|
/**
|
|
3266
|
-
* The greeting SynaBun prepared for this return (public/shared/greeting/)
|
|
3267
|
-
* The panel calls it once it knows the conversation is new: a restored one is never greeted.
|
|
3582
|
+
* The greeting SynaBun prepared for this return (public/shared/greeting/) sits between the heading
|
|
3583
|
+
* and the starters. The panel calls it once it knows the conversation is new: a restored one is never greeted.
|
|
3268
3584
|
*/
|
|
3269
3585
|
function offerGreeting() {
|
|
3270
3586
|
const owner = emptyEl;
|
|
@@ -3282,12 +3598,16 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3282
3598
|
host.innerHTML = '';
|
|
3283
3599
|
let list = [];
|
|
3284
3600
|
try { list = typeof hooks.emptySuggestions === 'function' ? (hooks.emptySuggestions() || []) : []; } catch { list = []; }
|
|
3601
|
+
let i = 0;
|
|
3285
3602
|
for (const s of list) {
|
|
3286
|
-
//
|
|
3603
|
+
// A ghost row: the kind's prop and the label (no capsule, no seam); it comes in staggered by --i,
|
|
3604
|
+
// and the character acts the kind out while the suggestion is hovered or focused.
|
|
3287
3605
|
const chip = el('button', 'asst-chip asst-suggestion');
|
|
3288
3606
|
chip.type = 'button';
|
|
3289
3607
|
chip.setAttribute('role', 'listitem');
|
|
3290
3608
|
chip.dataset.suggestion = s.id || '';
|
|
3609
|
+
chip.style.setProperty('--i', String(i + 1));
|
|
3610
|
+
i += 1;
|
|
3291
3611
|
const kind = s.kind || SUGGESTION_KINDS[s.id] || null;
|
|
3292
3612
|
if (kind) {
|
|
3293
3613
|
chip.dataset.kind = kind;
|
|
@@ -3301,10 +3621,28 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3301
3621
|
chip.addEventListener('blur', () => previewHero(null));
|
|
3302
3622
|
}
|
|
3303
3623
|
chip.appendChild(el('span', 'asst-chip-text', s.label));
|
|
3304
|
-
chip.
|
|
3624
|
+
if (s.meta) chip.appendChild(el('span', 'asst-chip-meta', s.meta));
|
|
3625
|
+
chip.addEventListener('click', () => {
|
|
3626
|
+
// The picked starter keeps the gold on-state while its text is the draft (setDrafting clears it);
|
|
3627
|
+
// a row with an action (the Workspace tour) fills nothing, so it is never "picked".
|
|
3628
|
+
if (!s.action) {
|
|
3629
|
+
for (const other of host.querySelectorAll('.is-picked')) other.classList.remove('is-picked');
|
|
3630
|
+
chip.classList.add('is-picked');
|
|
3631
|
+
}
|
|
3632
|
+
hooks.onSuggestion?.(s.template, s);
|
|
3633
|
+
});
|
|
3305
3634
|
host.appendChild(chip);
|
|
3306
3635
|
}
|
|
3307
3636
|
host.hidden = !list.length;
|
|
3637
|
+
// The context line comes in after the last starter.
|
|
3638
|
+
emptyEl.querySelector('.asst-empty-project')?.style.setProperty('--i', String(list.length + 1));
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
/** The composer holds a draft: the starters recede (the picked one stays lit) until it is cleared. */
|
|
3642
|
+
function setDrafting(on) {
|
|
3643
|
+
if (!emptyEl) return;
|
|
3644
|
+
emptyEl.classList.toggle('is-drafting', !!on);
|
|
3645
|
+
if (!on) for (const chip of emptyEl.querySelectorAll('.asst-suggestion.is-picked')) chip.classList.remove('is-picked');
|
|
3308
3646
|
}
|
|
3309
3647
|
|
|
3310
3648
|
/** The empty state's character tries a suggestion on (null: back to idle); never over a pose the director holds. */
|
|
@@ -3509,6 +3847,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3509
3847
|
const content = Array.isArray(ev.message.content) ? ev.message.content : [];
|
|
3510
3848
|
lastErrorText = typeof ev.message.content === 'string' ? ev.message.content.trim()
|
|
3511
3849
|
: content.filter((b) => b?.type === 'text').map((b) => b.text).join('\n').trim();
|
|
3850
|
+
if (!parentId) offerLimitWait(lastErrorText, { brainError: true });
|
|
3512
3851
|
}
|
|
3513
3852
|
return true;
|
|
3514
3853
|
}
|
|
@@ -3527,8 +3866,8 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3527
3866
|
}
|
|
3528
3867
|
case 'result': {
|
|
3529
3868
|
flushStream(scope);
|
|
3530
|
-
// Codex/OpenCode also send an `error` packet; the SDK reports only here.
|
|
3531
|
-
const errorText = !parentId && ev.brain !== 'codex' && ev.brain !== 'opencode' ? resultErrorText(ev, t) : '';
|
|
3869
|
+
// Codex/OpenCode/Antigravity also send an `error` packet; the SDK reports only here.
|
|
3870
|
+
const errorText = !parentId && ev.brain !== 'codex' && ev.brain !== 'opencode' && ev.brain !== 'antigravity' ? resultErrorText(ev, t) : '';
|
|
3532
3871
|
if (errorText && !muted) turn.errored = true;
|
|
3533
3872
|
// The stage on screen waits for the turn's verdict (onResult → finishTurn) before it folds.
|
|
3534
3873
|
if (!parentId && !muted) grace = newestStage();
|
|
@@ -3536,6 +3875,8 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3536
3875
|
if (!parentId) endBursts(rootScope);
|
|
3537
3876
|
// A journal's or a replay's turn ended here (live, finishTurn ends it): so did the route it ran.
|
|
3538
3877
|
if (!parentId && muted) directRoute = null;
|
|
3878
|
+
// A turn that ended well: its last message's options in prose become items (no block of its own).
|
|
3879
|
+
if (!parentId && !ev.is_error && !String(ev.subtype || '').startsWith('error')) guessOffer();
|
|
3539
3880
|
hooks.onResult?.(ev);
|
|
3540
3881
|
if (errorText && errorText !== lastErrorText) appendError(errorText);
|
|
3541
3882
|
if (!parentId) { lastErrorText = ''; rootScope.kept = null; releaseGrace(); }
|
|
@@ -3642,10 +3983,14 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3642
3983
|
/**
|
|
3643
3984
|
* The turn ended. `holdMs`: the panel plays a one-shot (success, error) on
|
|
3644
3985
|
* the character on screen, so its stage stays up that long before it folds.
|
|
3986
|
+
* `guess`: the turn ended well, so options its last message offers in prose may become items.
|
|
3645
3987
|
*/
|
|
3646
|
-
function finishTurn({ holdMs = 0 } = {}) {
|
|
3988
|
+
function finishTurn({ holdMs = 0, guess = true } = {}) {
|
|
3647
3989
|
flushStream(rootScope);
|
|
3990
|
+
sealStreamRow(rootScope);
|
|
3648
3991
|
if (rootScope.stream?.row) rootScope.stream.row.classList.remove('streaming');
|
|
3992
|
+
// The `result` usually guessed already; a turn that ended well without one gets it here (never a stopped or failed one).
|
|
3993
|
+
if (guess && !turn.errored) guessOffer();
|
|
3649
3994
|
for (const scope of scopes.values()) flushStream(scope);
|
|
3650
3995
|
closeStreamThinks();
|
|
3651
3996
|
hideWorking();
|
|
@@ -3686,11 +4031,14 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3686
4031
|
}
|
|
3687
4032
|
if (failed) console.warn(`[assistant] ${failed} of ${list.length} transcript entries could not be drawn`, firstError);
|
|
3688
4033
|
flushStream(rootScope);
|
|
4034
|
+
sealStreamRow(rootScope);
|
|
3689
4035
|
if (rootScope.stream?.row) rootScope.stream.row.classList.remove('streaming');
|
|
3690
4036
|
for (const scope of scopes.values()) flushStream(scope);
|
|
3691
4037
|
hideWorking();
|
|
3692
4038
|
turnActive = false;
|
|
3693
4039
|
for (const rack of [...racks]) renderRack(rack);
|
|
4040
|
+
// Drawn quietly: a card the history leaves open (nobody answered it yet) comes to life now.
|
|
4041
|
+
mascotChanged();
|
|
3694
4042
|
scrollEnd(true);
|
|
3695
4043
|
}
|
|
3696
4044
|
|
|
@@ -3728,11 +4076,21 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3728
4076
|
}
|
|
3729
4077
|
|
|
3730
4078
|
function clear() {
|
|
4079
|
+
if (scrollFrame) { cancelAnimationFrame(scrollFrame); scrollFrame = 0; }
|
|
4080
|
+
clearTimeout(recutTimer);
|
|
4081
|
+
recutTimer = 0;
|
|
4082
|
+
toRecut.clear();
|
|
3731
4083
|
for (const scope of scopes.values()) { if (scope.stream?.mdTimer) clearTimeout(scope.stream.mdTimer); if (scope.stream?.thinkTimer) clearTimeout(scope.stream.thinkTimer); }
|
|
3732
4084
|
if (rootScope.stream?.mdTimer) clearTimeout(rootScope.stream.mdTimer);
|
|
3733
4085
|
if (rootScope.stream?.thinkTimer) clearTimeout(rootScope.stream.thinkTimer);
|
|
3734
4086
|
scopes.clear();
|
|
3735
4087
|
dropStages();
|
|
4088
|
+
limitDock.clear();
|
|
4089
|
+
for (const offer of offers) { try { offer.wait?.destroy(); } catch { /* gone */ } }
|
|
4090
|
+
offers.clear();
|
|
4091
|
+
dropAsks();
|
|
4092
|
+
lastTextRow = null;
|
|
4093
|
+
for (const card of runCards.values()) { try { card._limitWait?.destroy(); } catch { /* gone */ } }
|
|
3736
4094
|
runActivity.clear();
|
|
3737
4095
|
runCards.clear();
|
|
3738
4096
|
routeLines.clear();
|
|
@@ -3764,7 +4122,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3764
4122
|
return {
|
|
3765
4123
|
el: $msgs,
|
|
3766
4124
|
clear,
|
|
3767
|
-
destroy: () => {
|
|
4125
|
+
destroy: () => { clear(); stageResizes?.disconnect(); limitDock.destroy(); },
|
|
3768
4126
|
appendUser,
|
|
3769
4127
|
appendAssistantMarkdown,
|
|
3770
4128
|
appendStatus,
|
|
@@ -3803,6 +4161,10 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3803
4161
|
forgetSeen: () => { seen.clear(); snapshot = null; },
|
|
3804
4162
|
/** A card waits on the user: `toolUseId` names the call it holds (the newest live stage when unknown); false clears it. */
|
|
3805
4163
|
setWaiting,
|
|
4164
|
+
/** The session's usage-limit report changed: this turn's waiting card counts to its reset time. */
|
|
4165
|
+
noteLimit,
|
|
4166
|
+
/** Dismiss the Assistant's own limit-wait card (a provider switch already decided what follows). */
|
|
4167
|
+
clearLimitWait: () => limitDock.set('brain', null),
|
|
3806
4168
|
finishTurn,
|
|
3807
4169
|
renderTranscript,
|
|
3808
4170
|
showWorking,
|
|
@@ -3811,6 +4173,7 @@ export function createRenderer(transcriptEl, hooks = {}) {
|
|
|
3811
4173
|
showEmpty,
|
|
3812
4174
|
offerGreeting,
|
|
3813
4175
|
refreshEmpty: renderSuggestions,
|
|
4176
|
+
setDrafting,
|
|
3814
4177
|
isEmpty: () => !!emptyEl,
|
|
3815
4178
|
scrollEnd,
|
|
3816
4179
|
isNearBottom,
|