mixdog 1.2.2 → 1.2.3
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/README.md +18 -0
- package/package.json +2 -1
- package/scripts/provider-pingpong-live.mjs +509 -0
- package/src/agents/advisor/AGENT.md +1 -1
- package/src/agents/advisor/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -0
- package/src/defaults/skills/setup/SKILL.md +6 -1
- package/src/defaults/skills/setup/references/actions.md +44 -22
- package/src/defaults/skills/setup/references/surfaces.md +10 -8
- package/src/headless-exec.mjs +15 -8
- package/src/lib/keychain-cjs.cjs +7 -4
- package/src/output-styles/common.md +11 -10
- package/src/rules/agent/AGENT.md +5 -5
- package/src/rules/lead/LEAD.md +5 -5
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
- package/src/runtime/agent/orchestrator/config.mjs +5 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +24 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +7 -6
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +29 -10
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +58 -7
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +29 -10
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +16 -3
- package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +10 -61
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +12 -6
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +114 -72
- package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +37 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +43 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +9 -2
- package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +64 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +70 -6
- package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
- package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +25 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +87 -5
- package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +13 -13
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +355 -37
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +93 -47
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +55 -10
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +26 -8
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +246 -42
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
- package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +388 -0
- package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
- package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
- package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +48 -22
- package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +119 -18
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +33 -1
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +121 -61
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +37 -7
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +47 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +151 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +42 -24
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +63 -25
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +172 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +53 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +103 -26
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +23 -4
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +11 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +15 -23
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +25 -4
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
- package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +71 -24
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +44 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -16
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -35
- package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +11 -3
- package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +170 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +75 -70
- package/src/runtime/agent/orchestrator/providers/openrouter.mjs +75 -0
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +147 -8
- package/src/runtime/agent/orchestrator/providers/registry.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
- package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
- package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +157 -0
- package/src/runtime/agent/orchestrator/providers/wire-route.mjs +512 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
- package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +183 -0
- package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +100 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
- package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
- package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +14 -3
- package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +176 -71
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +62 -10
- package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +14 -93
- package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +120 -0
- package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
- package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +91 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
- package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +132 -0
- package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +63 -20
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -4
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +150 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +208 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +25 -3
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +19 -2
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
- package/src/runtime/agent/orchestrator/session/side-question.mjs +192 -0
- package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +17 -3
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
- package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +58 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +41 -28
- package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +8 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +10 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +10 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +19 -3
- package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +14 -10
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +49 -34
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
- package/src/runtime/computer-bridge/action-schema.mjs +44 -21
- package/src/runtime/github/tool.mjs +92 -2
- package/src/runtime/memory/index.mjs +1 -0
- package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
- package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
- package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
- package/src/runtime/memory/lib/pg/process.mjs +31 -26
- package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +108 -3
- package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
- package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/authoring/html-document-author.mjs +2 -0
- package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-measure.mjs +2 -0
- package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +2 -0
- package/src/runtime/office/core/office-actions-read.mjs +3 -0
- package/src/runtime/office/core/office-render-preview.mjs +2 -0
- package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
- package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
- package/src/runtime/office/core/office-sessions.mjs +5 -0
- package/src/runtime/office/core/office-transactions.mjs +3 -0
- package/src/runtime/office/core/office-write-ledger.mjs +98 -0
- package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
- package/src/runtime/office/index.mjs +30 -0
- package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
- package/src/runtime/office/pdf/pdf-render.mjs +23 -7
- package/src/runtime/office/pdf/pdf-security.mjs +2 -0
- package/src/runtime/office/portable/portable-opc.mjs +4 -1
- package/src/runtime/office/quality/visual-diff.mjs +2 -0
- package/src/runtime/shared/bounded-file-read.mjs +13 -0
- package/src/runtime/shared/bounded-line-diff.mjs +182 -0
- package/src/runtime/shared/buffered-appender.mjs +18 -0
- package/src/runtime/shared/config.mjs +78 -18
- package/src/runtime/shared/err-text.mjs +6 -0
- package/src/runtime/shared/file-permissions.d.mts +6 -1
- package/src/runtime/shared/file-permissions.mjs +54 -6
- package/src/runtime/shared/llm/anthropic-effort.mjs +6 -0
- package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
- package/src/runtime/shared/loopback-listener.mjs +9 -0
- package/src/runtime/shared/provider-api-key.mjs +2 -0
- package/src/runtime/shared/review-diff.mjs +52 -0
- package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
- package/src/runtime/shared/session-sidebar-host.mjs +38 -0
- package/src/runtime/shared/slow-span.mjs +114 -0
- package/src/runtime/shared/statusline/statusline-segments.mjs +28 -4
- package/src/runtime/shared/tool-card-model/card-header.mjs +6 -1
- package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
- package/src/runtime/shared/tool-card-model.mjs +5 -2
- package/src/runtime/shared/tool-output-limit.mjs +4 -0
- package/src/runtime/shared/tool-primitives.mjs +13 -0
- package/src/runtime/shared/tool-result-summary.mjs +8 -1
- package/src/runtime/shared/tool-surface.mjs +18 -3
- package/src/runtime/shared/tool-work-units.mjs +40 -4
- package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
- package/src/runtime/shared/transcript-writer.mjs +10 -2
- package/src/runtime/shared/turn-changes.mjs +268 -100
- package/src/runtime/shared/turn-review-reply.mjs +8 -39
- package/src/runtime/shared/turn-snapshot-store.mjs +19 -108
- package/src/runtime/shared/turn-snapshot.mjs +857 -683
- package/src/runtime/shared/turn-worktree-snapshot.mjs +183 -255
- package/src/runtime/shared/user-data-guard.mjs +262 -49
- package/src/runtime/shared/worktrees/carry.mjs +531 -0
- package/src/runtime/shared/worktrees/contract.mjs +34 -0
- package/src/runtime/shared/worktrees/file-recovery.mjs +68 -0
- package/src/runtime/shared/worktrees/git-head.mjs +205 -0
- package/src/runtime/shared/worktrees/git.mjs +8 -3
- package/src/runtime/shared/worktrees/maintenance.mjs +1 -1
- package/src/runtime/shared/worktrees/manager.mjs +156 -8
- package/src/runtime/shared/worktrees/merge.mjs +31 -21
- package/src/runtime/shared/worktrees/paths.d.mts +6 -0
- package/src/runtime/terminal-bridge/client.mjs +44 -11
- package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
- package/src/runtime/tidy/apply.mjs +47 -2
- package/src/runtime/tidy/run-engines.mjs +11 -5
- package/src/session-runtime/agent-tool-routing.mjs +19 -13
- package/src/session-runtime/boot/apis.mjs +13 -5
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +6 -1
- package/src/session-runtime/boot/session.mjs +0 -1
- package/src/session-runtime/boot/tools.mjs +4 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +65 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
- package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +11 -4
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +115 -18
- package/src/session-runtime/internal-tool-executor.mjs +6 -2
- package/src/session-runtime/lifecycle/cold-heir.mjs +188 -0
- package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
- package/src/session-runtime/lifecycle/session-catalog.mjs +12 -3
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +29 -6
- package/src/session-runtime/lifecycle/teardown.mjs +72 -7
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/connect.mjs +17 -5
- package/src/session-runtime/model-route/set-route.mjs +2 -2
- package/src/session-runtime/native-web-search-usage.mjs +51 -0
- package/src/session-runtime/native-web-search.mjs +10 -1
- package/src/session-runtime/new-session-config.mjs +4 -2
- package/src/session-runtime/provider-auth/login.mjs +4 -0
- package/src/session-runtime/resource-scope-decorate.mjs +10 -3
- package/src/session-runtime/runtime-review-api.mjs +8 -2
- package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
- package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +73 -7
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +15 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
- package/src/session-runtime/services/agent-tool/worker-index.mjs +46 -7
- package/src/session-runtime/services/agent-tool.mjs +3 -0
- package/src/session-runtime/services/channel-admin.mjs +19 -3
- package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
- package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
- package/src/session-runtime/services/provider-admin.mjs +81 -15
- package/src/session-runtime/session-transcript-purge.mjs +32 -0
- package/src/session-runtime/session-turn-api.mjs +14 -5
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -0
- package/src/session-runtime/setup-tool/executor.mjs +36 -6
- package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
- package/src/session-runtime/setup-tool/settings-contract.mjs +13 -3
- package/src/session-runtime/setup-tool/tool-defs.mjs +13 -4
- package/src/session-runtime/skills-api.mjs +3 -5
- package/src/session-runtime/tool-surface.mjs +5 -1
- package/src/session-runtime/turn/compaction-gate.mjs +25 -0
- package/src/session-runtime/turn/session-ops.mjs +7 -5
- package/src/session-runtime/turn/side-question-ops.mjs +22 -0
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +5 -1
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +8 -3
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +10 -0
- package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
- package/src/session-runtime/turn/turn-run.mjs +25 -9
- package/src/session-runtime/usage-stats-api.mjs +43 -11
- package/src/session-runtime/workspace/project-branches.mjs +7 -5
- package/src/session-runtime/workspace/transition-steps.mjs +129 -31
- package/src/session-runtime/workspace/transitions.mjs +190 -15
- package/src/standalone/daemon-agent-control.mjs +9 -1
- package/src/standalone/daemon-local-session-bridge.mjs +14 -1
- package/src/standalone/daemon-owner.mjs +17 -4
- package/src/standalone/daemon-pg-stop.mjs +51 -0
- package/src/standalone/daemon-stop-budget.d.mts +2 -0
- package/src/standalone/daemon-stop-budget.mjs +18 -0
- package/src/standalone/daemon-stored-session-views.mjs +2 -0
- package/src/standalone/daemon-telemetry.mjs +19 -0
- package/src/standalone/daemon.mjs +61 -1
- package/src/standalone/desktop-service-registry.mjs +5 -0
- package/src/standalone/local-session-runtime.mjs +70 -19
- package/src/standalone/session-client.mjs +42 -0
- package/src/standalone/session-protocol.mjs +7 -0
- package/src/standalone/session-proxy/projection.mjs +41 -4
- package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
- package/src/standalone/session-runtime-inline-host.mjs +1 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
- package/src/standalone/session-service-api.mjs +6 -1
- package/src/standalone/session-state-patch.d.mts +2 -1
- package/src/standalone/session-state-patch.mjs +41 -1
- package/src/tui/App.jsx +25 -0
- package/src/tui/app/app-view/floating-panel.jsx +26 -1
- package/src/tui/app/btw-controller.mjs +118 -0
- package/src/tui/app/create-app-pickers.mjs +2 -0
- package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
- package/src/tui/app/shell-layout/surfaces.mjs +3 -1
- package/src/tui/app/slash-commands.mjs +13 -3
- package/src/tui/app/slash-dispatch/session-commands.mjs +19 -6
- package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
- package/src/tui/app/use-global-key-input.mjs +5 -12
- package/src/tui/app/use-prompt-handlers.mjs +11 -0
- package/src/tui/components/BtwPanel.jsx +53 -0
- package/src/tui/components/QuestionPanel.jsx +275 -0
- package/src/tui/components/question-panel-model.mjs +156 -0
- package/src/tui/dist/index.mjs +2413 -1465
- package/src/tui/session/context-state.mjs +4 -2
- package/src/tui/session/session-api/commands.mjs +10 -0
- package/src/tui/session/session-api/controls.mjs +2 -2
- package/src/tui/session/session-api/integrations.mjs +9 -2
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle.mjs +2 -1
- package/src/tui/session/session-api/settings.mjs +6 -0
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +10 -7
- package/src/tui/session/transcript-spill.mjs +14 -7
- package/src/tui/session/turn-stream.mjs +174 -31
- package/src/tui/session/turn.mjs +12 -5
- package/src/workflows/default/WORKFLOW.md +3 -1
|
@@ -8,6 +8,35 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { applySessionStatePatch } from '../session-state-patch.mjs';
|
|
10
10
|
|
|
11
|
+
// Desktop-only live fields: `streamingPartial` is the unfinished streamed
|
|
12
|
+
// line the desktop paints ahead of the line-gated tail. This terminal view
|
|
13
|
+
// keeps the line-gated text, so it never holds the field nor re-renders for it.
|
|
14
|
+
const DESKTOP_ONLY_FIELDS = ['streamingPartial'];
|
|
15
|
+
|
|
16
|
+
function terminalState(full) {
|
|
17
|
+
if (!full || typeof full !== 'object' || !DESKTOP_ONLY_FIELDS.some((key) => Object.hasOwn(full, key))) return full;
|
|
18
|
+
const next = { ...full };
|
|
19
|
+
for (const key of DESKTOP_ONLY_FIELDS) delete next[key];
|
|
20
|
+
return next;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The patch without desktop-only fields, or null when nothing else moved. */
|
|
24
|
+
function terminalPatch(patch) {
|
|
25
|
+
const set = patch.set && typeof patch.set === 'object' ? patch.set : {};
|
|
26
|
+
const remove = Array.isArray(patch.remove) ? patch.remove : [];
|
|
27
|
+
if (!patch.partialAppend && !DESKTOP_ONLY_FIELDS.some((key) => Object.hasOwn(set, key) || remove.includes(key))) {
|
|
28
|
+
return patch;
|
|
29
|
+
}
|
|
30
|
+
const nextSet = { ...set };
|
|
31
|
+
for (const key of DESKTOP_ONLY_FIELDS) delete nextSet[key];
|
|
32
|
+
const nextRemove = remove.filter((key) => !DESKTOP_ONLY_FIELDS.includes(key));
|
|
33
|
+
if (Object.keys(nextSet).length === 0 && nextRemove.length === 0 && !patch.itemsAppend && !patch.itemsPrepend) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const { partialAppend: _desktopOnly, ...rest } = patch;
|
|
37
|
+
return { ...rest, set: nextSet, remove: nextRemove };
|
|
38
|
+
}
|
|
39
|
+
|
|
11
40
|
export function createProjection({ binding, log }) {
|
|
12
41
|
let state = {};
|
|
13
42
|
let revision = 0;
|
|
@@ -40,7 +69,7 @@ export function createProjection({ binding, log }) {
|
|
|
40
69
|
const incomingRevision = Number(body.revision);
|
|
41
70
|
if (revisionAttachment !== sourceAttachment || revisionSessionId !== binding.sessionId) {
|
|
42
71
|
if (body.full === undefined || body.full === null) return false;
|
|
43
|
-
state = body.full;
|
|
72
|
+
state = terminalState(body.full);
|
|
44
73
|
revision = Number.isFinite(incomingRevision) ? incomingRevision : 0;
|
|
45
74
|
revisionAttachment = sourceAttachment;
|
|
46
75
|
revisionSessionId = binding.sessionId;
|
|
@@ -49,7 +78,7 @@ export function createProjection({ binding, log }) {
|
|
|
49
78
|
}
|
|
50
79
|
if (Number.isFinite(incomingRevision) && incomingRevision < revision) return true;
|
|
51
80
|
if (body.full !== undefined && body.full !== null) {
|
|
52
|
-
state = body.full;
|
|
81
|
+
state = terminalState(body.full);
|
|
53
82
|
revision = Number.isFinite(incomingRevision) ? incomingRevision : revision;
|
|
54
83
|
emit();
|
|
55
84
|
return true;
|
|
@@ -57,8 +86,16 @@ export function createProjection({ binding, log }) {
|
|
|
57
86
|
if (body.patch) {
|
|
58
87
|
if (Number(body.revision) === revision && Number(body.baseRevision) === revision - 1) return true;
|
|
59
88
|
if (Number(body.baseRevision) !== revision) return false;
|
|
60
|
-
|
|
89
|
+
const patch = terminalPatch(body.patch);
|
|
90
|
+
// Only a desktop-only field moved: the terminal view advances silently.
|
|
91
|
+
if (!patch) {
|
|
92
|
+
revision = Number(body.revision) || revision;
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
const patched = applySessionStatePatch(state, patch);
|
|
96
|
+
if (!patched) return false;
|
|
61
97
|
revision = Number(body.revision) || revision;
|
|
98
|
+
state = patched;
|
|
62
99
|
emit();
|
|
63
100
|
return true;
|
|
64
101
|
}
|
|
@@ -76,7 +113,7 @@ export function createProjection({ binding, log }) {
|
|
|
76
113
|
return {
|
|
77
114
|
// The create result seeds the projection for the current binding.
|
|
78
115
|
seed(created) {
|
|
79
|
-
state = created?.full ?? {};
|
|
116
|
+
state = terminalState(created?.full ?? {});
|
|
80
117
|
revision = Number(created?.revision) || 0;
|
|
81
118
|
revisionAttachment = binding.attachment;
|
|
82
119
|
revisionSessionId = binding.sessionId;
|
|
@@ -50,6 +50,20 @@ export function createInlineBootLoaders({
|
|
|
50
50
|
return localModulePromise;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// Background warm-up of the session-local module graph. Independent of the
|
|
54
|
+
// keychain gate (the ESM cache makes the later localModule() import free);
|
|
55
|
+
// never throws — a failed warm-up leaves create on its lazy path.
|
|
56
|
+
async function warmLocalRuntime() {
|
|
57
|
+
if (isClosed()) return false;
|
|
58
|
+
try {
|
|
59
|
+
const module = await loadLocalModule();
|
|
60
|
+
if (isClosed()) return false;
|
|
61
|
+
return (await module.warmLocalSessionRuntime?.()) !== false;
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
function agentGraph() {
|
|
54
68
|
agentGraphPromise ??= measured('agent-dispatch-graph-import', () => {
|
|
55
69
|
assertOpen();
|
|
@@ -92,5 +106,5 @@ export function createInlineBootLoaders({
|
|
|
92
106
|
await tracked;
|
|
93
107
|
}
|
|
94
108
|
|
|
95
|
-
return { prewarmKeychain, localModule, agentGraph, prepareAgentProviders };
|
|
109
|
+
return { prewarmKeychain, warmLocalRuntime, localModule, agentGraph, prepareAgentProviders };
|
|
96
110
|
}
|
|
@@ -74,6 +74,7 @@ export function createInlineSessionRuntimeHost({
|
|
|
74
74
|
return {
|
|
75
75
|
create,
|
|
76
76
|
prewarmKeychain: loaders.prewarmKeychain,
|
|
77
|
+
warmLocalRuntime: loaders.warmLocalRuntime,
|
|
77
78
|
agentDispatch: dispatch.agentDispatch,
|
|
78
79
|
async agentControl(args = {}, context = {}) {
|
|
79
80
|
if (closed) throw new Error('session runtime host is closed');
|
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
// The generic runtime-action lane (read / configure allowlists) and the
|
|
3
3
|
// session catalog listing.
|
|
4
4
|
import { SESSION_CONFIGURE_ACTION_SET, SESSION_READ_ACTION_SET } from '../../session-protocol.mjs';
|
|
5
|
+
import { annotateSpan, markPhase } from '../../../runtime/shared/slow-span.mjs';
|
|
5
6
|
import { sanitizeForWire } from '../../session-wire-values.mjs';
|
|
6
7
|
|
|
7
8
|
/** Reads a session without a live runtime answers from disk (readStoredReview). */
|
|
8
|
-
const STORED_REVIEW_ACTIONS = new Set([
|
|
9
|
+
const STORED_REVIEW_ACTIONS = new Set([
|
|
10
|
+
'getTurnReviewDiff',
|
|
11
|
+
'getSessionReviewDiff',
|
|
12
|
+
'getTurnChanges',
|
|
13
|
+
'getSessionReviewFilePatch',
|
|
14
|
+
'getTurnReviewFilePatch',
|
|
15
|
+
]);
|
|
9
16
|
|
|
10
17
|
/** One compact description of an action result for the daemon log, never the payload itself. */
|
|
11
18
|
function resultSummary(value) {
|
|
@@ -44,7 +51,8 @@ export function createSessionActionCalls(ctx) {
|
|
|
44
51
|
assertAvailable();
|
|
45
52
|
if (value === null) throw new Error(`session ${id} is not available`);
|
|
46
53
|
log(`session action ${name} session=${id} result=${resultSummary(value)} (stored)`);
|
|
47
|
-
|
|
54
|
+
markPhase('load');
|
|
55
|
+
const result = {
|
|
48
56
|
value: sanitizeForWire(value) ?? null,
|
|
49
57
|
sessionId: id,
|
|
50
58
|
reservedOnly: false,
|
|
@@ -52,6 +60,8 @@ export function createSessionActionCalls(ctx) {
|
|
|
52
60
|
unchanged: true,
|
|
53
61
|
...(Number.isInteger(baseRevision) ? { revision: baseRevision } : {}),
|
|
54
62
|
};
|
|
63
|
+
markPhase('project');
|
|
64
|
+
return result;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
async function runSessionAction(
|
|
@@ -62,6 +72,7 @@ export function createSessionActionCalls(ctx) {
|
|
|
62
72
|
const id = String(sessionId || '');
|
|
63
73
|
if (!id) throw new TypeError('sessionId is required');
|
|
64
74
|
const name = requireSessionAction(action, allowedActions);
|
|
75
|
+
annotateSpan(name);
|
|
65
76
|
// Opening an earlier session asks for its review right away; loading the
|
|
66
77
|
// whole runtime for that held the review bar (and the pane's reveal) for
|
|
67
78
|
// seconds.
|
|
@@ -72,7 +83,9 @@ export function createSessionActionCalls(ctx) {
|
|
|
72
83
|
assertAvailable(entry);
|
|
73
84
|
const target = entry.runtime[name];
|
|
74
85
|
if (typeof target !== 'function') throw new TypeError(`session action ${name} is unavailable`);
|
|
86
|
+
markPhase('load');
|
|
75
87
|
const value = await target.apply(entry.runtime, Array.isArray(args) ? args : []);
|
|
88
|
+
markPhase('action');
|
|
76
89
|
if (name === 'setCwd' && value) {
|
|
77
90
|
try {
|
|
78
91
|
const projects = await loadProjectStore();
|
|
@@ -89,11 +102,13 @@ export function createSessionActionCalls(ctx) {
|
|
|
89
102
|
// Still unwatched: keep it on the retention clock exactly like a runtime
|
|
90
103
|
// released by its view, so an untouched load cannot leak past the idle window.
|
|
91
104
|
retainUnwatched(entry);
|
|
92
|
-
|
|
105
|
+
const result = {
|
|
93
106
|
value: sanitizeForWire(value) ?? null,
|
|
94
107
|
sessionId: responseSessionId(entry, id),
|
|
95
108
|
...bodyForClient(step, Number.isInteger(baseRevision) ? baseRevision : null),
|
|
96
109
|
};
|
|
110
|
+
markPhase('project');
|
|
111
|
+
return result;
|
|
97
112
|
}
|
|
98
113
|
|
|
99
114
|
async function listSessionCatalog(options = {}) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { shortId, slowSpans } from '../runtime/shared/slow-span.mjs';
|
|
2
|
+
|
|
1
3
|
const sessionServiceRoutes = ({ desktop, project, session }) => ({
|
|
2
4
|
'desktop.init': (params, ctx) => desktop.init(params, ctx),
|
|
3
5
|
'desktop.invoke': (params, ctx) => desktop.invoke(params, ctx),
|
|
@@ -32,13 +34,16 @@ export function createSessionServiceApi({
|
|
|
32
34
|
getTurnBusyCount,
|
|
33
35
|
getStatus,
|
|
34
36
|
getExternalClientCount,
|
|
37
|
+
spans = slowSpans,
|
|
35
38
|
}) {
|
|
36
39
|
const routes = sessionServiceRoutes({ desktop, project, session });
|
|
37
40
|
return {
|
|
38
41
|
async handleCall(name, args = {}, ctx = null) {
|
|
39
42
|
const route = routes[String(name || '')];
|
|
40
43
|
if (!route) throw new Error(`unknown session service call ${name}`);
|
|
41
|
-
|
|
44
|
+
const params = args || {};
|
|
45
|
+
const sid = shortId(params.sessionId);
|
|
46
|
+
return spans.trace(sid ? `${name}#${sid}` : String(name), () => route(params, ctx));
|
|
42
47
|
},
|
|
43
48
|
...methods,
|
|
44
49
|
get size() {
|
|
@@ -3,5 +3,6 @@ export function diffSessionState(
|
|
|
3
3
|
next: unknown,
|
|
4
4
|
options?: { prepend?: boolean }
|
|
5
5
|
): Record<string, unknown> | null;
|
|
6
|
-
export function
|
|
6
|
+
export function streamingPartialSuffix(before: unknown, after: unknown): string | null;
|
|
7
|
+
export function applySessionStatePatch(previous: unknown, patch: unknown): Record<string, unknown> | null;
|
|
7
8
|
export function transcriptItemsDigest(items: readonly unknown[]): string;
|
|
@@ -7,16 +7,51 @@
|
|
|
7
7
|
// `transcriptPrepend`) carries rows revealed ABOVE the previous list, an
|
|
8
8
|
// older-history page: the previous rows then start `values.length` rows in,
|
|
9
9
|
// and `itemsAppend.from` indexes the PREVIOUS list.
|
|
10
|
+
//
|
|
11
|
+
// `partialAppend` carries growth of the desktop-only `streamingPartial` (the
|
|
12
|
+
// unfinished streamed line) as just its new text: re-sending the whole line on
|
|
13
|
+
// every tick made a long unfinished line cost O(length²) on the wire. A
|
|
14
|
+
// decoder that predates it keeps its last partial, which the desktop join
|
|
15
|
+
// then ignores or shows behind — never wrong text.
|
|
10
16
|
import { createHash } from 'node:crypto';
|
|
11
17
|
|
|
18
|
+
/** The text `after` appends to `before` when both are the same unfinished
|
|
19
|
+
* line (same row, same offset, same row identity), else null: a retraction,
|
|
20
|
+
* another row or a new line travels as a full replacement. */
|
|
21
|
+
export function streamingPartialSuffix(before, after) {
|
|
22
|
+
if (!before || !after || typeof before !== 'object' || typeof after !== 'object') return null;
|
|
23
|
+
const { text: beforeText, ...beforeRest } = before;
|
|
24
|
+
const { text: afterText, ...afterRest } = after;
|
|
25
|
+
if (typeof beforeText !== 'string' || typeof afterText !== 'string') return null;
|
|
26
|
+
if (afterText.length <= beforeText.length || !afterText.startsWith(beforeText)) return null;
|
|
27
|
+
// id, from and the row identity: everything but the text stays put.
|
|
28
|
+
if (JSON.stringify(beforeRest) !== JSON.stringify(afterRest)) return null;
|
|
29
|
+
return afterText.slice(beforeText.length);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function appendStreamingPartial(state, suffix) {
|
|
33
|
+
const held = state.streamingPartial;
|
|
34
|
+
if (!held || typeof held !== 'object' || typeof held.text !== 'string' || typeof suffix !== 'string') return false;
|
|
35
|
+
state.streamingPartial = { ...held, text: held.text + suffix };
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
12
39
|
export function diffSessionState(previous, next, { prepend = false } = {}) {
|
|
13
40
|
if (!previous || !next || typeof previous !== 'object' || typeof next !== 'object') return null;
|
|
14
41
|
const set = {};
|
|
15
42
|
const remove = [];
|
|
16
43
|
let itemsAppend = null;
|
|
17
44
|
let itemsPrepend = null;
|
|
45
|
+
let partialAppend = null;
|
|
18
46
|
for (const [key, value] of Object.entries(next)) {
|
|
19
47
|
if (previous[key] === value) continue;
|
|
48
|
+
if (key === 'streamingPartial') {
|
|
49
|
+
const suffix = streamingPartialSuffix(previous[key], value);
|
|
50
|
+
if (suffix !== null) {
|
|
51
|
+
partialAppend = { text: suffix };
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
20
55
|
if (key === 'items' && Array.isArray(value) && Array.isArray(previous.items)) {
|
|
21
56
|
let head = 0;
|
|
22
57
|
if (prepend && previous.items.length > 0 && value[0] !== previous.items[0]) {
|
|
@@ -36,13 +71,18 @@ export function diffSessionState(previous, next, { prepend = false } = {}) {
|
|
|
36
71
|
for (const key of Object.keys(previous)) {
|
|
37
72
|
if (!(key in next)) remove.push(key);
|
|
38
73
|
}
|
|
39
|
-
|
|
74
|
+
const patch = itemsPrepend ? { set, remove, itemsAppend, itemsPrepend } : { set, remove, itemsAppend };
|
|
75
|
+
if (partialAppend) patch.partialAppend = partialAppend;
|
|
76
|
+
return patch;
|
|
40
77
|
}
|
|
41
78
|
|
|
79
|
+
/** The patched state, or null when a `partialAppend` has no held line to grow
|
|
80
|
+
* (a broken chain the caller answers with a full resync, never lost text). */
|
|
42
81
|
export function applySessionStatePatch(previous, patch) {
|
|
43
82
|
const base = previous && typeof previous === 'object' ? previous : {};
|
|
44
83
|
if (!patch || typeof patch !== 'object') return base;
|
|
45
84
|
const next = { ...base, ...(patch.set || {}) };
|
|
85
|
+
if (Object.hasOwn(patch, 'partialAppend') && !appendStreamingPartial(next, patch.partialAppend?.text)) return null;
|
|
46
86
|
if (patch.itemsAppend || patch.itemsPrepend) {
|
|
47
87
|
const items = Array.isArray(base.items) ? base.items : [];
|
|
48
88
|
const head = Array.isArray(patch.itemsPrepend?.values) ? patch.itemsPrepend.values : [];
|
package/src/tui/App.jsx
CHANGED
|
@@ -21,6 +21,7 @@ import { useApp, useStdin, useStdout } from 'ink';
|
|
|
21
21
|
import { useSession } from './hooks/useSession.mjs';
|
|
22
22
|
import { pickFolder } from '../standalone/folder-dialog.mjs';
|
|
23
23
|
import { deriveSlashPalette } from './app/slash-palette.mjs';
|
|
24
|
+
import { createBtwController } from './app/btw-controller.mjs';
|
|
24
25
|
import { skillMentionQuery, useSkillMentionSkills } from './app/skill-mention-palette.mjs';
|
|
25
26
|
import { shouldSupersedePanelEpoch, supersedePanelEpoch } from './app/panel-epoch.mjs';
|
|
26
27
|
import { createPanelSurface } from './app/panel-surface.mjs';
|
|
@@ -152,6 +153,24 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
152
153
|
livePickerRef.current = picker;
|
|
153
154
|
const [contextPanel, setContextPanel] = useState(null);
|
|
154
155
|
const [usagePanel, setUsagePanel] = useState(null);
|
|
156
|
+
const [btwPanel, setBtwPanel] = useState(null);
|
|
157
|
+
const btwRef = useRef(null);
|
|
158
|
+
const sessionIdRef = useRef('');
|
|
159
|
+
sessionIdRef.current = state.sessionId || '';
|
|
160
|
+
if (!btwRef.current) {
|
|
161
|
+
btwRef.current = createBtwController({
|
|
162
|
+
getStore: () => store,
|
|
163
|
+
setPanel: setBtwPanel,
|
|
164
|
+
getSessionId: () => sessionIdRef.current,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const btw = btwRef.current;
|
|
168
|
+
// A side answer belongs to the conversation it was asked in: drop it on a
|
|
169
|
+
// session change (new/resume/clear/switch) and cancel it on unmount.
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
btw.sync();
|
|
172
|
+
}, [btw, state.sessionId]);
|
|
173
|
+
useEffect(() => () => btw.dispose(), [btw]);
|
|
155
174
|
// OWNING LAYER (app/panel-surface.mjs): the three sinks above are handed over
|
|
156
175
|
// exactly once and never leave that module. Every panel factory below gets
|
|
157
176
|
// `surface` instead, so painting or delegating without a claim that proves
|
|
@@ -310,6 +329,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
310
329
|
setSettingsPrompt,
|
|
311
330
|
setOnboardingActive,
|
|
312
331
|
closeUsagePanel,
|
|
332
|
+
btw,
|
|
313
333
|
oauthSubmitRef,
|
|
314
334
|
clearModelCaches,
|
|
315
335
|
onboardingRef,
|
|
@@ -518,6 +538,8 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
518
538
|
usagePanel,
|
|
519
539
|
closeUsagePanel,
|
|
520
540
|
contextPanel,
|
|
541
|
+
btwPanel,
|
|
542
|
+
closeBtwPanel: btw.close,
|
|
521
543
|
installPastedImages,
|
|
522
544
|
clearPastedImagesSnapshot,
|
|
523
545
|
registerPastedImage,
|
|
@@ -650,6 +672,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
650
672
|
toolApproval: !!toolApproval,
|
|
651
673
|
contextPanel: !!contextPanel,
|
|
652
674
|
usagePanel: !!usagePanel,
|
|
675
|
+
btwPanel: !!btwPanel,
|
|
653
676
|
providerPrompt: !!providerPrompt,
|
|
654
677
|
settingsPrompt: !!settingsPrompt,
|
|
655
678
|
};
|
|
@@ -711,6 +734,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
711
734
|
picker,
|
|
712
735
|
contextPanel,
|
|
713
736
|
usagePanel,
|
|
737
|
+
btwPanel,
|
|
714
738
|
slashPaletteOpen,
|
|
715
739
|
tuiReady,
|
|
716
740
|
state,
|
|
@@ -822,6 +846,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false, on
|
|
|
822
846
|
activeSlashQuery,
|
|
823
847
|
activeTools,
|
|
824
848
|
agentRevision,
|
|
849
|
+
btwPanel,
|
|
825
850
|
cancelProviderPrompt,
|
|
826
851
|
cancelSettingsPrompt,
|
|
827
852
|
cancelSlashPalette,
|
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
import { theme } from '../../theme.mjs';
|
|
11
11
|
import { toolApprovalDescription } from '../app-format.mjs';
|
|
12
12
|
import { Picker } from '../../components/Picker.jsx';
|
|
13
|
+
import { QuestionPanel } from '../../components/QuestionPanel.jsx';
|
|
14
|
+
import { questionsOf } from '../../components/question-panel-model.mjs';
|
|
13
15
|
import { SlashCommandPalette } from '../../components/SlashCommandPalette.jsx';
|
|
14
16
|
import { ContextPanel } from '../../components/ContextPanel.jsx';
|
|
15
17
|
import { UsagePanel } from '../../components/UsagePanel.jsx';
|
|
18
|
+
import { BtwPanel } from '../../components/BtwPanel.jsx';
|
|
16
19
|
import { TextEntryPanel } from '../../components/TextEntryPanel.jsx';
|
|
17
20
|
import { textEntryClearsByEmpty } from '../text-entry-policy.mjs';
|
|
18
21
|
import { withPickerEnterLatch } from '../picker-enter-latch.mjs';
|
|
@@ -24,7 +27,23 @@ const SKILL_PALETTE_TEXT = {
|
|
|
24
27
|
help: '↑/↓ select · Enter insert · Esc cancel',
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
function renderToolApproval({ toolApproval, store, frameColumns, expandedOptionPanel }) {
|
|
30
|
+
function renderToolApproval({ toolApproval, store, frameColumns, expandedOptionPanel, floatingPanelRows }) {
|
|
31
|
+
if (toolApproval.kind === 'question') {
|
|
32
|
+
return (
|
|
33
|
+
<QuestionPanel
|
|
34
|
+
key={toolApproval.id}
|
|
35
|
+
questions={questionsOf(toolApproval)}
|
|
36
|
+
columns={frameColumns}
|
|
37
|
+
rows={floatingPanelRows}
|
|
38
|
+
onSubmit={(answers) =>
|
|
39
|
+
store.resolveToolApproval?.(toolApproval.id, { approved: true, reason: 'answered by user', answers })
|
|
40
|
+
}
|
|
41
|
+
onDismiss={() =>
|
|
42
|
+
store.resolveToolApproval?.(toolApproval.id, { approved: false, reason: 'dismissed by user' })
|
|
43
|
+
}
|
|
44
|
+
/>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
28
47
|
const resolve = (approved) => {
|
|
29
48
|
store.resolveToolApproval?.(toolApproval.id, {
|
|
30
49
|
approved,
|
|
@@ -144,6 +163,7 @@ export function renderFloatingPanel(ctx) {
|
|
|
144
163
|
slashPaletteOpen,
|
|
145
164
|
toolApproval,
|
|
146
165
|
usagePanel,
|
|
166
|
+
btwPanel,
|
|
147
167
|
} = ctx;
|
|
148
168
|
if (toolApproval) return renderToolApproval(ctx);
|
|
149
169
|
if (picker) return renderPicker(ctx);
|
|
@@ -227,5 +247,10 @@ export function renderFloatingPanel(ctx) {
|
|
|
227
247
|
/>
|
|
228
248
|
);
|
|
229
249
|
}
|
|
250
|
+
if (btwPanel) {
|
|
251
|
+
return (
|
|
252
|
+
<BtwPanel panel={btwPanel} columns={frameColumns} panelRows={floatingPanelRows} themeEpoch={ctx.state?.themeEpoch || 0} />
|
|
253
|
+
);
|
|
254
|
+
}
|
|
230
255
|
return null;
|
|
231
256
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* app/btw-controller.mjs — the transient `/btw` side-question panel.
|
|
3
|
+
*
|
|
4
|
+
* The answer lives only in this panel (never the transcript). `ask` replaces
|
|
5
|
+
* any existing panel, `close` (Esc) cancels a pending request, and a result
|
|
6
|
+
* that arrives after a replace/close is ignored via the request sequence.
|
|
7
|
+
*/
|
|
8
|
+
import { measureMarkdownRenderedRows } from '../markdown/measure-rendered-rows.mjs';
|
|
9
|
+
|
|
10
|
+
export const BTW_USAGE_HINT = 'usage: /btw <question>';
|
|
11
|
+
// Border (2) + header (1) + footer (1).
|
|
12
|
+
export const BTW_CHROME_ROWS = 4;
|
|
13
|
+
const BTW_MAX_BODY_ROWS = 10;
|
|
14
|
+
|
|
15
|
+
/** Rendered row count of the answer body (the scrollable content height). */
|
|
16
|
+
export function btwContentRows(panel, columns) {
|
|
17
|
+
const body =
|
|
18
|
+
panel.status === 'done'
|
|
19
|
+
? measureMarkdownRenderedRows(panel.text, Math.max(1, columns - 4))
|
|
20
|
+
: String(panel.text || '').split('\n').length;
|
|
21
|
+
return Math.max(1, body);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function btwPanelRows(panel, columns) {
|
|
25
|
+
if (!panel) return 0;
|
|
26
|
+
return BTW_CHROME_ROWS + Math.min(BTW_MAX_BODY_ROWS, btwContentRows(panel, columns));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Largest scroll offset for `total` content rows shown through `visible` rows. */
|
|
30
|
+
export function btwScrollMax(total, visible) {
|
|
31
|
+
return Math.max(0, total - Math.max(1, visible));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Next scroll offset for a PageUp/PageDown key (other keys leave it unchanged). */
|
|
35
|
+
export function btwScrollOffset(key, offset, visible, total) {
|
|
36
|
+
const max = btwScrollMax(total, visible);
|
|
37
|
+
const page = Math.max(1, visible - 1);
|
|
38
|
+
if (key.pageUp) return Math.max(0, Math.min(max, offset) - page);
|
|
39
|
+
if (key.pageDown) return Math.min(max, offset + page);
|
|
40
|
+
return Math.min(max, offset);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Each request is bound to the session it was asked in. A settlement whose
|
|
45
|
+
* session no longer matches is ignored; `sync` (session change) and `dispose`
|
|
46
|
+
* (unmount) cancel a pending request for its originating session.
|
|
47
|
+
*/
|
|
48
|
+
export function createBtwController({ getStore, setPanel, getSessionId = () => '' }) {
|
|
49
|
+
let seq = 0;
|
|
50
|
+
let pending = null; // { sessionId, dispatched } while a request is queued or in flight
|
|
51
|
+
let origin = null; // session the visible panel was asked in
|
|
52
|
+
|
|
53
|
+
const cancelPending = () => {
|
|
54
|
+
if (!pending) return;
|
|
55
|
+
const { sessionId, dispatched } = pending;
|
|
56
|
+
pending = null;
|
|
57
|
+
// Not dispatched yet: dropping `pending` stops the queued request, so
|
|
58
|
+
// there is nothing to cancel (a cancel sent first would not hit it).
|
|
59
|
+
if (!dispatched) return;
|
|
60
|
+
try {
|
|
61
|
+
const result = getStore().cancelSide?.(sessionId);
|
|
62
|
+
if (result?.catch) void result.catch(() => {});
|
|
63
|
+
} catch {
|
|
64
|
+
/* best-effort cancel */
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const ask = (question) => {
|
|
69
|
+
cancelPending();
|
|
70
|
+
const id = ++seq;
|
|
71
|
+
const sessionId = getSessionId();
|
|
72
|
+
const request = { sessionId, dispatched: false };
|
|
73
|
+
pending = request;
|
|
74
|
+
origin = sessionId;
|
|
75
|
+
setPanel({ question, status: 'pending', text: '' });
|
|
76
|
+
const settle = (next) => {
|
|
77
|
+
if (id !== seq || getSessionId() !== sessionId) return;
|
|
78
|
+
pending = null;
|
|
79
|
+
setPanel({ question, ...next });
|
|
80
|
+
};
|
|
81
|
+
Promise.resolve()
|
|
82
|
+
.then(() => {
|
|
83
|
+
// askSide runs against the store's current session, so only dispatch
|
|
84
|
+
// while this ask is still live and still in its origin session.
|
|
85
|
+
if (pending !== request || id !== seq || getSessionId() !== sessionId) {
|
|
86
|
+
if (pending === request) pending = null;
|
|
87
|
+
return undefined; // dropped, never sent; `settle` ignores it (stale id/session)
|
|
88
|
+
}
|
|
89
|
+
request.dispatched = true;
|
|
90
|
+
return getStore().askSide(question);
|
|
91
|
+
})
|
|
92
|
+
.then(
|
|
93
|
+
(result) => settle({ status: 'done', text: String(result?.text ?? '') }),
|
|
94
|
+
(error) => settle({ status: 'error', text: String(error?.message || error || 'side question failed') })
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const close = () => {
|
|
99
|
+
cancelPending();
|
|
100
|
+
seq += 1;
|
|
101
|
+
origin = null;
|
|
102
|
+
setPanel(null);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/** Session changed: a panel from another conversation is cancelled and cleared. */
|
|
106
|
+
const sync = () => {
|
|
107
|
+
if (origin !== null && getSessionId() !== origin) close();
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** Unmount: cancel without touching React state. */
|
|
111
|
+
const dispose = () => {
|
|
112
|
+
cancelPending();
|
|
113
|
+
seq += 1;
|
|
114
|
+
origin = null;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return { ask, close, sync, dispose };
|
|
118
|
+
}
|
|
@@ -37,6 +37,7 @@ export function createAppPickers({
|
|
|
37
37
|
setSettingsPrompt,
|
|
38
38
|
setOnboardingActive,
|
|
39
39
|
closeUsagePanel,
|
|
40
|
+
btw,
|
|
40
41
|
oauthSubmitRef,
|
|
41
42
|
clearModelCaches,
|
|
42
43
|
onboardingRef,
|
|
@@ -207,6 +208,7 @@ export function createAppPickers({
|
|
|
207
208
|
normalizeSlashCommandName,
|
|
208
209
|
surface,
|
|
209
210
|
closeUsagePanel,
|
|
211
|
+
btw,
|
|
210
212
|
openModelPicker: (...a) => openModelPicker(...a),
|
|
211
213
|
modelSwitchNotice,
|
|
212
214
|
openWebSearchPicker: (...a) => openWebSearchPicker(...a),
|
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { isLiveSpinnerMetaVisible } from '../live-spinner-visibility.mjs';
|
|
7
7
|
import { promptContentRows, wrappedDetailRows, queuedBandRows } from '../text-layout.mjs';
|
|
8
|
+
import { questionPanelRows, questionsOf } from '../../components/question-panel-model.mjs';
|
|
8
9
|
import { CORE_MULTILINE_TEXT_ENTRY_KINDS } from '../panel-signature.mjs';
|
|
10
|
+
import { btwPanelRows } from '../btw-controller.mjs';
|
|
9
11
|
|
|
10
12
|
const SCROLL_HINT_ROWS = 0;
|
|
11
13
|
const LIVE_STATUS_ROWS = 0;
|
|
@@ -114,20 +116,25 @@ function floatingPanelBudget({
|
|
|
114
116
|
picker,
|
|
115
117
|
contextPanel,
|
|
116
118
|
usagePanel,
|
|
119
|
+
btwPanel,
|
|
117
120
|
slashPaletteOpen,
|
|
118
121
|
hasTextEntryPrompt,
|
|
119
122
|
TEXT_ENTRY_ROWS,
|
|
120
123
|
OPTION_PANEL_EXTRA_ROWS,
|
|
121
124
|
maxFloatingPanelRows,
|
|
125
|
+
frameColumns,
|
|
122
126
|
}) {
|
|
123
127
|
let desiredFloatingPanelRows = 0;
|
|
124
|
-
if (toolApproval
|
|
128
|
+
if (toolApproval?.kind === 'question' && questionsOf(toolApproval).length > 0) {
|
|
129
|
+
desiredFloatingPanelRows = questionPanelRows(questionsOf(toolApproval), frameColumns);
|
|
130
|
+
} else if (toolApproval) desiredFloatingPanelRows = PANEL_CHROME_ROWS + 2 + OPTION_PANEL_EXTRA_ROWS;
|
|
125
131
|
else if (picker?.fillAvailable) desiredFloatingPanelRows = maxFloatingPanelRows;
|
|
126
132
|
else if (picker) desiredFloatingPanelRows = PANEL_BASE_ROWS + OPTION_PANEL_EXTRA_ROWS;
|
|
127
133
|
else if (contextPanel) desiredFloatingPanelRows = PANEL_BASE_ROWS + OPTION_PANEL_EXTRA_ROWS + 3;
|
|
128
134
|
else if (usagePanel) desiredFloatingPanelRows = PANEL_BASE_ROWS + OPTION_PANEL_EXTRA_ROWS;
|
|
129
135
|
else if (slashPaletteOpen) desiredFloatingPanelRows = SLASH_PALETTE_ROWS;
|
|
130
136
|
else if (hasTextEntryPrompt) desiredFloatingPanelRows = TEXT_ENTRY_ROWS;
|
|
137
|
+
else if (btwPanel) desiredFloatingPanelRows = btwPanelRows(btwPanel, frameColumns);
|
|
131
138
|
const floatingPanelRows = desiredFloatingPanelRows > 0 ? Math.min(desiredFloatingPanelRows, maxFloatingPanelRows) : 0;
|
|
132
139
|
// Give the list every content row the panel exposes. The panel already grew
|
|
133
140
|
// by OPTION_PANEL_EXTRA_ROWS; previously that growth was subtracted back out
|
|
@@ -174,6 +181,7 @@ export function computeRowBudgets({
|
|
|
174
181
|
picker,
|
|
175
182
|
contextPanel,
|
|
176
183
|
usagePanel,
|
|
184
|
+
btwPanel,
|
|
177
185
|
slashPaletteOpen,
|
|
178
186
|
}) {
|
|
179
187
|
const { textEntryPrompt, hasTextEntryPrompt, hasFloatingPanel, expandedOptionPanel, inputBoxHidden } = surfaces;
|
|
@@ -211,11 +219,13 @@ export function computeRowBudgets({
|
|
|
211
219
|
picker,
|
|
212
220
|
contextPanel,
|
|
213
221
|
usagePanel,
|
|
222
|
+
btwPanel,
|
|
214
223
|
slashPaletteOpen,
|
|
215
224
|
hasTextEntryPrompt,
|
|
216
225
|
TEXT_ENTRY_ROWS: textEntry.TEXT_ENTRY_ROWS,
|
|
217
226
|
OPTION_PANEL_EXTRA_ROWS,
|
|
218
227
|
maxFloatingPanelRows,
|
|
228
|
+
frameColumns,
|
|
219
229
|
});
|
|
220
230
|
const rows = {
|
|
221
231
|
floatingPanelRows: panel.floatingPanelRows,
|
|
@@ -10,6 +10,7 @@ export function deriveSurfaces({
|
|
|
10
10
|
picker,
|
|
11
11
|
contextPanel,
|
|
12
12
|
usagePanel,
|
|
13
|
+
btwPanel,
|
|
13
14
|
slashPaletteOpen,
|
|
14
15
|
tuiReady,
|
|
15
16
|
state,
|
|
@@ -24,7 +25,8 @@ export function deriveSurfaces({
|
|
|
24
25
|
contextPanel ||
|
|
25
26
|
usagePanel ||
|
|
26
27
|
slashPaletteOpen ||
|
|
27
|
-
hasTextEntryPrompt
|
|
28
|
+
hasTextEntryPrompt ||
|
|
29
|
+
btwPanel
|
|
28
30
|
);
|
|
29
31
|
const expandedOptionPanel = !!(toolApproval || picker || contextPanel || usagePanel || hasTextEntryPrompt);
|
|
30
32
|
if (panelTransitionRef.current.signature.includes('picker:project') && !picker) {
|
|
@@ -15,6 +15,12 @@ export const SLASH_COMMANDS = [
|
|
|
15
15
|
description: 'Switch working directory (project)',
|
|
16
16
|
},
|
|
17
17
|
{ name: 'compact', usage: '/compact', description: 'Compact older conversation context' },
|
|
18
|
+
{
|
|
19
|
+
name: 'btw',
|
|
20
|
+
usage: '/btw',
|
|
21
|
+
params: '<question>',
|
|
22
|
+
description: 'Ask a quick side question without interrupting the current work',
|
|
23
|
+
},
|
|
18
24
|
{
|
|
19
25
|
name: 'goal',
|
|
20
26
|
usage: '/goal',
|
|
@@ -30,9 +36,12 @@ export const SLASH_COMMANDS = [
|
|
|
30
36
|
{ name: 'resume', usage: '/resume', params: '[id]', description: 'Resume a saved chat' },
|
|
31
37
|
{ name: 'context', usage: '/context', description: 'Show current context surface' },
|
|
32
38
|
{
|
|
33
|
-
name: '
|
|
34
|
-
usage: '/
|
|
35
|
-
|
|
39
|
+
name: 'fork',
|
|
40
|
+
usage: '/fork',
|
|
41
|
+
aliases: ['inherit'],
|
|
42
|
+
aliasUsage: ['inherit'],
|
|
43
|
+
showAliasUsage: false,
|
|
44
|
+
description: 'Fork this conversation into a new session on the current model',
|
|
36
45
|
},
|
|
37
46
|
{ name: 'usage', usage: '/usage', params: '[refresh]', description: 'Show total provider quota / balance' },
|
|
38
47
|
{ name: 'model', usage: '/model', params: '[name|refresh]', description: 'Switch model for subsequent turns' },
|
|
@@ -109,6 +118,7 @@ export function overlayBlocksGlobalTranscriptScroll(owner = {}) {
|
|
|
109
118
|
owner.toolApproval ||
|
|
110
119
|
owner.contextPanel ||
|
|
111
120
|
owner.usagePanel ||
|
|
121
|
+
owner.btwPanel ||
|
|
112
122
|
owner.providerPrompt ||
|
|
113
123
|
owner.settingsPrompt
|
|
114
124
|
);
|