@alumbwe/anvil 1.0.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/README.md +84 -0
- package/bin/anvil +2 -0
- package/bin/tree-sitter.wasm +0 -0
- package/package.json +67 -0
- package/src/__tests__/README.md +256 -0
- package/src/__tests__/bash-mode.test.ts +442 -0
- package/src/__tests__/cli-args.test.ts +181 -0
- package/src/__tests__/cli-harness.test.ts +52 -0
- package/src/__tests__/e2e/first-time-login.test.ts +173 -0
- package/src/__tests__/e2e/logout-relogin-flow.test.ts +110 -0
- package/src/__tests__/e2e/returning-user-auth.test.ts +113 -0
- package/src/__tests__/e2e-cli.test.ts +183 -0
- package/src/__tests__/helpers/launcher-disconnect-fixture.cjs +176 -0
- package/src/__tests__/helpers/mock-api-client.ts +64 -0
- package/src/__tests__/helpers/renderer-cleanup-fixture.tsx +89 -0
- package/src/__tests__/helpers/terminal-watchdog-fixture.ts +75 -0
- package/src/__tests__/home-directory-detection.test.ts +213 -0
- package/src/__tests__/integration/api-integration.test.ts +288 -0
- package/src/__tests__/integration/credentials-storage.test.ts +486 -0
- package/src/__tests__/integration/local-agents.test.ts +1187 -0
- package/src/__tests__/integration/login-flow-instrumentation.test.ts +134 -0
- package/src/__tests__/integration/login-polling-working.test.ts +285 -0
- package/src/__tests__/integration/queue-editing.test.tsx +261 -0
- package/src/__tests__/integration/usage-refresh-on-completion.test.ts +168 -0
- package/src/__tests__/integration-tmux.test.ts +188 -0
- package/src/__tests__/launcher-avx2-fallback.test.ts +630 -0
- package/src/__tests__/launcher-disconnect.test.ts +104 -0
- package/src/__tests__/mocks/hover-toggle-controller.ts +71 -0
- package/src/__tests__/open-tui-native-bundle.test.ts +378 -0
- package/src/__tests__/path-completion.test.ts +243 -0
- package/src/__tests__/project-files-chat-id.test.ts +34 -0
- package/src/__tests__/release/proxy-http-get.test.ts +667 -0
- package/src/__tests__/release/wrapper-safety.test.ts +412 -0
- package/src/__tests__/renderer-cleanup.test.ts +174 -0
- package/src/__tests__/rerender-perf.integration.test.ts +283 -0
- package/src/__tests__/terminal-reset-sequences.test.ts +36 -0
- package/src/__tests__/terminal-watchdog.test.ts +388 -0
- package/src/__tests__/test-utils.ts +271 -0
- package/src/__tests__/tmux-poc.ts +150 -0
- package/src/__tests__/unit/agent-mode-toggle.test.ts +137 -0
- package/src/__tests__/unit/copy-button.test.ts +157 -0
- package/src/__tests__/unit/create-run-config.test.ts +66 -0
- package/src/__tests__/unit/publish-confirmation.test.ts +70 -0
- package/src/__tests__/unit/segmented-control.test.ts +152 -0
- package/src/__tests__/utils/env.test.ts +169 -0
- package/src/__tests__/utils/project-picker.test.ts +39 -0
- package/src/agents/bundled-agents.generated.d.ts +14 -0
- package/src/agents/bundled-agents.generated.ts +3692 -0
- package/src/app.tsx +399 -0
- package/src/chat.tsx +1698 -0
- package/src/cli-args.ts +134 -0
- package/src/commands/__tests__/bash-command.test.ts +445 -0
- package/src/commands/__tests__/command-args.test.ts +349 -0
- package/src/commands/__tests__/copy-conversation-clipboard.test.ts +108 -0
- package/src/commands/__tests__/copy-conversation.test.ts +226 -0
- package/src/commands/__tests__/diagnostics-command.test.ts +35 -0
- package/src/commands/__tests__/freebuff-command-aliases.test.ts +56 -0
- package/src/commands/__tests__/image.test.ts +99 -0
- package/src/commands/__tests__/init.test.ts +456 -0
- package/src/commands/__tests__/new-command-rotates-chat.test.ts +91 -0
- package/src/commands/__tests__/process-diagnostics.test.ts +54 -0
- package/src/commands/__tests__/prompt-builders.test.ts +28 -0
- package/src/commands/__tests__/router-input.test.ts +298 -0
- package/src/commands/ads.ts +44 -0
- package/src/commands/command-registry.ts +726 -0
- package/src/commands/copy-conversation.ts +392 -0
- package/src/commands/help.ts +13 -0
- package/src/commands/image.ts +20 -0
- package/src/commands/init.ts +121 -0
- package/src/commands/process-diagnostics.ts +120 -0
- package/src/commands/prompt-builders.ts +100 -0
- package/src/commands/publish.ts +203 -0
- package/src/commands/router-utils.ts +80 -0
- package/src/commands/router.ts +487 -0
- package/src/commands/usage.ts +26 -0
- package/src/components/__tests__/ad-banner.test.tsx +83 -0
- package/src/components/__tests__/chat-input-bar.test.tsx +130 -0
- package/src/components/__tests__/freebuff-landing-heading-row.test.tsx +91 -0
- package/src/components/__tests__/freebuff-model-selector.test.tsx +519 -0
- package/src/components/__tests__/freebuff-offer-invariants.test.ts +54 -0
- package/src/components/__tests__/freebuff-takeover-prompt.test.tsx +212 -0
- package/src/components/__tests__/grid-layout.integration.test.tsx +304 -0
- package/src/components/__tests__/grid-layout.test.tsx +1051 -0
- package/src/components/__tests__/message-block.completion.test.tsx +104 -0
- package/src/components/__tests__/message-block.streaming.test.tsx +78 -0
- package/src/components/__tests__/message-with-agents.test.tsx +579 -0
- package/src/components/__tests__/multiline-input.test.tsx +1132 -0
- package/src/components/__tests__/queue-panel.test.tsx +398 -0
- package/src/components/__tests__/selectable-list.test.ts +232 -0
- package/src/components/__tests__/status-bar.test.tsx +44 -0
- package/src/components/__tests__/status-indicator.test.tsx +204 -0
- package/src/components/__tests__/terminal-thinking-orb.test.tsx +111 -0
- package/src/components/__tests__/user-error-banner.test.tsx +102 -0
- package/src/components/ad-banner.tsx +323 -0
- package/src/components/agent-checklist.tsx +350 -0
- package/src/components/agent-mode-toggle.tsx +241 -0
- package/src/components/anvil-mark.tsx +52 -0
- package/src/components/ask-user/__tests__/multiple-choice-form.test.ts +432 -0
- package/src/components/ask-user/__tests__/validation.test.ts +213 -0
- package/src/components/ask-user/components/accordion-question.tsx +153 -0
- package/src/components/ask-user/components/custom-answer-input.tsx +66 -0
- package/src/components/ask-user/components/options-list.tsx +133 -0
- package/src/components/ask-user/components/question-header.tsx +80 -0
- package/src/components/ask-user/components/question-option.tsx +89 -0
- package/src/components/ask-user/constants.ts +38 -0
- package/src/components/ask-user/index.tsx +631 -0
- package/src/components/ask-user/utils/validation.ts +85 -0
- package/src/components/attachment-card.tsx +65 -0
- package/src/components/blocks/agent-block-grid.tsx +58 -0
- package/src/components/blocks/agent-branch-item.tsx +311 -0
- package/src/components/blocks/agent-branch-wrapper.tsx +493 -0
- package/src/components/blocks/agent-list-branch.tsx +77 -0
- package/src/components/blocks/ask-user-branch.tsx +81 -0
- package/src/components/blocks/block-helpers.ts +11 -0
- package/src/components/blocks/blocks-renderer.tsx +289 -0
- package/src/components/blocks/content-with-markdown.tsx +33 -0
- package/src/components/blocks/image-block.tsx +128 -0
- package/src/components/blocks/implementor-row.tsx +429 -0
- package/src/components/blocks/single-block.tsx +201 -0
- package/src/components/blocks/thinking-block.tsx +67 -0
- package/src/components/blocks/tool-block-group.tsx +57 -0
- package/src/components/blocks/tool-branch.tsx +206 -0
- package/src/components/blocks/user-content-copy.tsx +157 -0
- package/src/components/bottom-banner.tsx +136 -0
- package/src/components/build-mode-buttons.tsx +110 -0
- package/src/components/button.tsx +66 -0
- package/src/components/chat-header.tsx +77 -0
- package/src/components/chat-history-screen.tsx +413 -0
- package/src/components/chat-input-bar.tsx +523 -0
- package/src/components/clickable-title-box.tsx +66 -0
- package/src/components/clickable.tsx +119 -0
- package/src/components/collapse-button.tsx +34 -0
- package/src/components/copy-button.tsx +203 -0
- package/src/components/elapsed-timer.tsx +55 -0
- package/src/components/error-boundary.tsx +55 -0
- package/src/components/feedback-container.tsx +181 -0
- package/src/components/feedback-icon-button.tsx +89 -0
- package/src/components/feedback-input-mode.tsx +344 -0
- package/src/components/file-attachment-card.tsx +98 -0
- package/src/components/freebuff-active-session-summary.tsx +63 -0
- package/src/components/freebuff-landing-screen.tsx +865 -0
- package/src/components/freebuff-model-selector.tsx +1087 -0
- package/src/components/freebuff-referral-banner.tsx +604 -0
- package/src/components/freebuff-superseded-screen.tsx +62 -0
- package/src/components/grid-layout.tsx +80 -0
- package/src/components/help-banner.tsx +127 -0
- package/src/components/highlighted-text.tsx +52 -0
- package/src/components/image-card.tsx +167 -0
- package/src/components/image-thumbnail.tsx +114 -0
- package/src/components/input-cursor.tsx +77 -0
- package/src/components/input-mode-banner.tsx +57 -0
- package/src/components/load-previous-button.tsx +48 -0
- package/src/components/login-modal.tsx +475 -0
- package/src/components/message-block.tsx +346 -0
- package/src/components/message-footer.tsx +258 -0
- package/src/components/message-with-agents.tsx +506 -0
- package/src/components/mode-divider.tsx +43 -0
- package/src/components/multiline-input.tsx +1241 -0
- package/src/components/out-of-credits-banner.tsx +160 -0
- package/src/components/pending-attachments-banner.tsx +116 -0
- package/src/components/pending-bash-message.tsx +59 -0
- package/src/components/progress-bar.tsx +81 -0
- package/src/components/project-picker-screen.tsx +485 -0
- package/src/components/publish-confirmation.tsx +493 -0
- package/src/components/publish-container.tsx +687 -0
- package/src/components/queue-panel.tsx +310 -0
- package/src/components/raised-pill.tsx +95 -0
- package/src/components/renderers/plan-box.tsx +59 -0
- package/src/components/review-screen.tsx +112 -0
- package/src/components/scroll-to-bottom-button.tsx +34 -0
- package/src/components/segmented-control.tsx +187 -0
- package/src/components/selectable-list.tsx +248 -0
- package/src/components/selected-chips.tsx +80 -0
- package/src/components/separator.tsx +40 -0
- package/src/components/session-ended-banner.tsx +226 -0
- package/src/components/shimmer-text.tsx +248 -0
- package/src/components/status-bar.tsx +313 -0
- package/src/components/subscription-limit-banner.tsx +206 -0
- package/src/components/suggested-prompts.tsx +184 -0
- package/src/components/suggestion-menu.tsx +215 -0
- package/src/components/terminal-command-display.tsx +151 -0
- package/src/components/terminal-link.tsx +97 -0
- package/src/components/terminal-thinking-orb.tsx +95 -0
- package/src/components/text-attachment-card.tsx +77 -0
- package/src/components/thinking.tsx +109 -0
- package/src/components/tools/__tests__/apply-patch.test.tsx +84 -0
- package/src/components/tools/__tests__/code-search.test.tsx +45 -0
- package/src/components/tools/__tests__/gravity-index.test.ts +76 -0
- package/src/components/tools/__tests__/render-ui.test.tsx +92 -0
- package/src/components/tools/__tests__/run-terminal-command.test.ts +233 -0
- package/src/components/tools/apply-patch.tsx +95 -0
- package/src/components/tools/code-search.tsx +45 -0
- package/src/components/tools/composio.tsx +152 -0
- package/src/components/tools/diff-viewer.tsx +76 -0
- package/src/components/tools/glob.tsx +61 -0
- package/src/components/tools/gravity-index.tsx +77 -0
- package/src/components/tools/list-directory.tsx +61 -0
- package/src/components/tools/read-docs.tsx +37 -0
- package/src/components/tools/read-files.tsx +91 -0
- package/src/components/tools/read-subtree.tsx +44 -0
- package/src/components/tools/read-url.tsx +33 -0
- package/src/components/tools/registry.ts +126 -0
- package/src/components/tools/render-ui.tsx +122 -0
- package/src/components/tools/run-terminal-command.tsx +79 -0
- package/src/components/tools/skill.tsx +29 -0
- package/src/components/tools/str-replace.tsx +77 -0
- package/src/components/tools/suggest-followups.tsx +369 -0
- package/src/components/tools/task-completed.tsx +16 -0
- package/src/components/tools/tool-call-item.tsx +273 -0
- package/src/components/tools/types.ts +55 -0
- package/src/components/tools/web-search.tsx +33 -0
- package/src/components/tools/write-file.tsx +16 -0
- package/src/components/tools/write-todos.tsx +95 -0
- package/src/components/top-banner.tsx +178 -0
- package/src/components/usage-banner.tsx +244 -0
- package/src/components/user-error-banner.tsx +56 -0
- package/src/components/validation-error-popover.tsx +192 -0
- package/src/contexts/__tests__/chat-runtime-context.test.tsx +149 -0
- package/src/contexts/chat-runtime-context.tsx +240 -0
- package/src/data/slash-commands.test.ts +24 -0
- package/src/data/slash-commands.ts +249 -0
- package/src/entry.ts +12 -0
- package/src/hooks/__tests__/holds-live-freebuff-slot.test.ts +49 -0
- package/src/hooks/__tests__/session-fetch-signal.test.ts +63 -0
- package/src/hooks/__tests__/use-activity-query.test.ts +1000 -0
- package/src/hooks/__tests__/use-ask-user-bridge.test.ts +176 -0
- package/src/hooks/__tests__/use-auth-query.test.ts +21 -0
- package/src/hooks/__tests__/use-chat-input-remount.test.tsx +66 -0
- package/src/hooks/__tests__/use-clipboard.test.tsx +73 -0
- package/src/hooks/__tests__/use-connection-status.test.ts +112 -0
- package/src/hooks/__tests__/use-directory-browser.test.ts +203 -0
- package/src/hooks/__tests__/use-gravity-ad.test.ts +73 -0
- package/src/hooks/__tests__/use-grid-layout.test.ts +349 -0
- package/src/hooks/__tests__/use-input-history.test.ts +699 -0
- package/src/hooks/__tests__/use-message-queue-editing.test.tsx +209 -0
- package/src/hooks/__tests__/use-message-queue.test.tsx +71 -0
- package/src/hooks/__tests__/use-path-tab-completion.test.ts +315 -0
- package/src/hooks/__tests__/use-queue-controls.test.ts +68 -0
- package/src/hooks/__tests__/use-queue-ui.test.tsx +83 -0
- package/src/hooks/__tests__/use-searchable-list.test.ts +359 -0
- package/src/hooks/__tests__/use-send-message-timer.test.ts +146 -0
- package/src/hooks/__tests__/use-suggestion-engine-mention.test.ts +361 -0
- package/src/hooks/__tests__/use-suggestion-engine.test.ts +485 -0
- package/src/hooks/__tests__/use-terminal-focus.test.ts +66 -0
- package/src/hooks/__tests__/use-terminal-layout.test.ts +675 -0
- package/src/hooks/__tests__/use-timeout.test.ts +330 -0
- package/src/hooks/__tests__/use-usage-query.test.ts +502 -0
- package/src/hooks/__tests__/use-user-details-query.test.ts +195 -0
- package/src/hooks/helpers/__tests__/send-message.test.ts +1918 -0
- package/src/hooks/helpers/send-message.ts +654 -0
- package/src/hooks/stream-state.ts +83 -0
- package/src/hooks/use-activity-query.ts +668 -0
- package/src/hooks/use-agent-validation.ts +74 -0
- package/src/hooks/use-ask-user-bridge.ts +92 -0
- package/src/hooks/use-auth-query.ts +253 -0
- package/src/hooks/use-auth-state.ts +152 -0
- package/src/hooks/use-chat-input.ts +105 -0
- package/src/hooks/use-chat-keyboard.ts +321 -0
- package/src/hooks/use-chat-messages.ts +254 -0
- package/src/hooks/use-chat-state.ts +165 -0
- package/src/hooks/use-chat-streaming.ts +180 -0
- package/src/hooks/use-chat-ui.ts +131 -0
- package/src/hooks/use-clipboard.ts +151 -0
- package/src/hooks/use-connection-status.ts +142 -0
- package/src/hooks/use-directory-browser.ts +88 -0
- package/src/hooks/use-elapsed-time.ts +116 -0
- package/src/hooks/use-event.ts +34 -0
- package/src/hooks/use-exit-handler.ts +66 -0
- package/src/hooks/use-fetch-login-url.ts +65 -0
- package/src/hooks/use-fingerprint.ts +61 -0
- package/src/hooks/use-freebuff-ctrl-c-exit.ts +23 -0
- package/src/hooks/use-freebuff-session-progress.ts +34 -0
- package/src/hooks/use-freebuff-session.ts +806 -0
- package/src/hooks/use-freebuff-streak-query.ts +61 -0
- package/src/hooks/use-gravity-ad.ts +656 -0
- package/src/hooks/use-grid-layout.ts +73 -0
- package/src/hooks/use-input-history.ts +173 -0
- package/src/hooks/use-login-keyboard-handlers.ts +75 -0
- package/src/hooks/use-login-polling.ts +114 -0
- package/src/hooks/use-logo.tsx +161 -0
- package/src/hooks/use-message-queue.ts +387 -0
- package/src/hooks/use-now.ts +20 -0
- package/src/hooks/use-path-tab-completion.ts +88 -0
- package/src/hooks/use-publish-mutation.ts +60 -0
- package/src/hooks/use-queue-controls.ts +68 -0
- package/src/hooks/use-queue-ui.ts +74 -0
- package/src/hooks/use-scroll-management.ts +184 -0
- package/src/hooks/use-searchable-list.ts +98 -0
- package/src/hooks/use-send-message.ts +773 -0
- package/src/hooks/use-sheen-animation.tsx +88 -0
- package/src/hooks/use-subscription-query.ts +71 -0
- package/src/hooks/use-suggestion-engine.ts +783 -0
- package/src/hooks/use-terminal-breakpoints.ts +77 -0
- package/src/hooks/use-terminal-dimensions.ts +46 -0
- package/src/hooks/use-terminal-focus.ts +29 -0
- package/src/hooks/use-terminal-layout.ts +189 -0
- package/src/hooks/use-theme.tsx +140 -0
- package/src/hooks/use-timeout.ts +85 -0
- package/src/hooks/use-update-preference.ts +66 -0
- package/src/hooks/use-usage-monitor.ts +55 -0
- package/src/hooks/use-usage-query.ts +124 -0
- package/src/hooks/use-user-details-query.ts +93 -0
- package/src/hooks/use-why-did-you-update.ts +177 -0
- package/src/index.tsx +441 -0
- package/src/init/__tests__/init-direnv.test.ts +526 -0
- package/src/init/init-app.ts +34 -0
- package/src/init/init-direnv.ts +133 -0
- package/src/login/constants.ts +60 -0
- package/src/login/login-flow.ts +204 -0
- package/src/login/plain-login.ts +108 -0
- package/src/login/utils.ts +183 -0
- package/src/native/ripgrep.ts +80 -0
- package/src/polyfills/bun-strip-ansi.ts +12 -0
- package/src/pre-init/tree-sitter-wasm.ts +96 -0
- package/src/project-files.ts +102 -0
- package/src/smoke/terminal-command-broker.ts +439 -0
- package/src/state/__tests__/chat-store-focus.test.ts +22 -0
- package/src/state/__tests__/feedback-store.test.ts +257 -0
- package/src/state/chat-history-store.ts +30 -0
- package/src/state/chat-store.ts +544 -0
- package/src/state/feedback-store.ts +160 -0
- package/src/state/freebuff-model-store.ts +42 -0
- package/src/state/freebuff-session-store.ts +53 -0
- package/src/state/login-store.ts +150 -0
- package/src/state/message-block-store.ts +149 -0
- package/src/state/publish-store.ts +146 -0
- package/src/state/queue-panel-store.ts +26 -0
- package/src/state/review-store.ts +24 -0
- package/src/testing/env.ts +46 -0
- package/src/types/chat-state.ts +15 -0
- package/src/types/chat.ts +241 -0
- package/src/types/contracts/send-message.ts +12 -0
- package/src/types/env.ts +98 -0
- package/src/types/freebuff-session.ts +17 -0
- package/src/types/function-params.ts +28 -0
- package/src/types/react19-compat.d.ts +19 -0
- package/src/types/store.ts +111 -0
- package/src/types/theme-system.ts +144 -0
- package/src/types/utils.ts +3 -0
- package/src/utils/__tests__/active-run.test.ts +140 -0
- package/src/utils/__tests__/activity-tracker.test.ts +440 -0
- package/src/utils/__tests__/agent-display.test.ts +139 -0
- package/src/utils/__tests__/analytics-client.test.ts +262 -0
- package/src/utils/__tests__/anonymous-id.test.ts +74 -0
- package/src/utils/__tests__/anvil-api.test.ts +578 -0
- package/src/utils/__tests__/arrays.test.ts +264 -0
- package/src/utils/__tests__/bash-context-processor.test.ts +38 -0
- package/src/utils/__tests__/block-processor.test.ts +810 -0
- package/src/utils/__tests__/chat-history.test.ts +203 -0
- package/src/utils/__tests__/chat-input-key-intercept.test.ts +40 -0
- package/src/utils/__tests__/chat-meta.test.ts +130 -0
- package/src/utils/__tests__/clipboard-linux.test.ts +359 -0
- package/src/utils/__tests__/clipboard.test.ts +910 -0
- package/src/utils/__tests__/code-search-summary.test.ts +84 -0
- package/src/utils/__tests__/collapse-helpers.test.ts +1109 -0
- package/src/utils/__tests__/error-handling.test.ts +658 -0
- package/src/utils/__tests__/exit-cleanly.test.ts +110 -0
- package/src/utils/__tests__/feedback-helpers.test.ts +444 -0
- package/src/utils/__tests__/feedback-submission.test.ts +26 -0
- package/src/utils/__tests__/fetch-usage.test.ts +355 -0
- package/src/utils/__tests__/fingerprint.test.ts +144 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-child.ts +7 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-entry.ts +3 -0
- package/src/utils/__tests__/fixtures/terminal-command-broker-owner.ts +33 -0
- package/src/utils/__tests__/format-elapsed-time.test.ts +103 -0
- package/src/utils/__tests__/format-timeout.test.ts +87 -0
- package/src/utils/__tests__/freebuff-instance-owner.test.ts +69 -0
- package/src/utils/__tests__/freebuff-model-navigation.test.ts +102 -0
- package/src/utils/__tests__/freebuff-premium-reset.test.ts +79 -0
- package/src/utils/__tests__/freebuff-referral-cache.test.ts +116 -0
- package/src/utils/__tests__/freebuff-session-api.test.ts +215 -0
- package/src/utils/__tests__/freebuff-session-display.test.ts +23 -0
- package/src/utils/__tests__/freebuff-streak-line.test.ts +217 -0
- package/src/utils/__tests__/image-dimensions.test.ts +249 -0
- package/src/utils/__tests__/image-processor.test.ts +209 -0
- package/src/utils/__tests__/implementor-helpers.test.ts +1458 -0
- package/src/utils/__tests__/keyboard-actions.test.ts +660 -0
- package/src/utils/__tests__/layout-helpers.test.ts +36 -0
- package/src/utils/__tests__/lazy-response-ads.test.ts +167 -0
- package/src/utils/__tests__/log-shipper.test.ts +53 -0
- package/src/utils/__tests__/markdown-renderer.test.tsx +421 -0
- package/src/utils/__tests__/message-block-helpers.test.ts +1263 -0
- package/src/utils/__tests__/message-updater.test.ts +645 -0
- package/src/utils/__tests__/osc-timeout-scenarios.test.ts +172 -0
- package/src/utils/__tests__/pending-attachments.test.ts +277 -0
- package/src/utils/__tests__/polling-backoff.test.ts +51 -0
- package/src/utils/__tests__/queue-panel-actions.test.ts +148 -0
- package/src/utils/__tests__/run-state-storage.test.ts +919 -0
- package/src/utils/__tests__/safe-json.test.ts +77 -0
- package/src/utils/__tests__/sdk-event-handlers.test.ts +685 -0
- package/src/utils/__tests__/send-message-helpers.test.ts +1898 -0
- package/src/utils/__tests__/send-message-timer.test.ts +64 -0
- package/src/utils/__tests__/settings.test.ts +99 -0
- package/src/utils/__tests__/strings.test.ts +192 -0
- package/src/utils/__tests__/terminal-color-detection.test.ts +425 -0
- package/src/utils/__tests__/terminal-command-broker.test.ts +528 -0
- package/src/utils/__tests__/terminal-enter-detection.test.ts +77 -0
- package/src/utils/__tests__/terminal-io.test.ts +137 -0
- package/src/utils/__tests__/terminal-protocol-controller.test.ts +119 -0
- package/src/utils/__tests__/text-layout.test.ts +82 -0
- package/src/utils/__tests__/theme-platform-detection.test.ts +125 -0
- package/src/utils/__tests__/think-tag-parser.test.ts +159 -0
- package/src/utils/__tests__/trace-writer.test.ts +223 -0
- package/src/utils/__tests__/trim-chat-logs.test.ts +78 -0
- package/src/utils/__tests__/usage-banner-state.test.ts +298 -0
- package/src/utils/__tests__/validation-error-formatting.test.ts +126 -0
- package/src/utils/__tests__/windows-terminal-health.test.ts +101 -0
- package/src/utils/__tests__/write-file-atomic.test.ts +127 -0
- package/src/utils/active-run.ts +131 -0
- package/src/utils/activity-tracker.ts +66 -0
- package/src/utils/agent-display.ts +87 -0
- package/src/utils/agent-helpers.ts +27 -0
- package/src/utils/agent-id-utils.ts +14 -0
- package/src/utils/ai-message-id.ts +12 -0
- package/src/utils/analytics.ts +361 -0
- package/src/utils/anonymous-id.ts +87 -0
- package/src/utils/anvil-api.ts +612 -0
- package/src/utils/anvil-client.ts +217 -0
- package/src/utils/arrays.ts +117 -0
- package/src/utils/auth.ts +258 -0
- package/src/utils/bash-context-processor.ts +47 -0
- package/src/utils/bash-messages.ts +108 -0
- package/src/utils/block-margins.ts +35 -0
- package/src/utils/block-operations.ts +523 -0
- package/src/utils/block-processor.ts +213 -0
- package/src/utils/chat-history.ts +253 -0
- package/src/utils/chat-input-key-intercept.ts +52 -0
- package/src/utils/chat-meta.ts +90 -0
- package/src/utils/chat-scroll-accel.ts +151 -0
- package/src/utils/clipboard-image.ts +588 -0
- package/src/utils/clipboard.ts +397 -0
- package/src/utils/code-search-summary.ts +70 -0
- package/src/utils/collapse-helpers.ts +256 -0
- package/src/utils/config-dir.ts +24 -0
- package/src/utils/constants.ts +190 -0
- package/src/utils/create-event-handler-state.ts +73 -0
- package/src/utils/create-run-config.ts +121 -0
- package/src/utils/detect-shell.ts +116 -0
- package/src/utils/directory-browser.ts +72 -0
- package/src/utils/engagement.ts +55 -0
- package/src/utils/env.ts +92 -0
- package/src/utils/error-handling.ts +255 -0
- package/src/utils/error-messages.ts +62 -0
- package/src/utils/exit-cleanly.ts +86 -0
- package/src/utils/feedback-helpers.ts +103 -0
- package/src/utils/feedback-submission.ts +22 -0
- package/src/utils/fetch-usage.ts +76 -0
- package/src/utils/fingerprint.ts +245 -0
- package/src/utils/format-elapsed-time.ts +33 -0
- package/src/utils/format-session-units.ts +6 -0
- package/src/utils/format-timeout.ts +28 -0
- package/src/utils/format-validation-errors-for-message.ts +84 -0
- package/src/utils/freebuff-agent-selection.ts +33 -0
- package/src/utils/freebuff-instance-owner.ts +66 -0
- package/src/utils/freebuff-model-navigation.ts +50 -0
- package/src/utils/freebuff-premium-reset.ts +55 -0
- package/src/utils/freebuff-referral-cache.ts +51 -0
- package/src/utils/freebuff-session-api.ts +243 -0
- package/src/utils/freebuff-session-display.ts +21 -0
- package/src/utils/freebuff-streak-line.ts +93 -0
- package/src/utils/git.ts +17 -0
- package/src/utils/helpers.ts +47 -0
- package/src/utils/image-display.ts +67 -0
- package/src/utils/image-handler.ts +325 -0
- package/src/utils/image-processor.ts +133 -0
- package/src/utils/image-thumbnail.ts +78 -0
- package/src/utils/implementor-helpers.ts +787 -0
- package/src/utils/input-modes.ts +174 -0
- package/src/utils/keyboard-actions.ts +392 -0
- package/src/utils/keypad-keys.ts +47 -0
- package/src/utils/layout-helpers.ts +33 -0
- package/src/utils/lazy-response-ads.ts +1 -0
- package/src/utils/local-agent-registry.ts +474 -0
- package/src/utils/log-shipper.ts +126 -0
- package/src/utils/logger.ts +327 -0
- package/src/utils/markdown-renderer.tsx +1090 -0
- package/src/utils/math.ts +3 -0
- package/src/utils/message-block-helpers.ts +684 -0
- package/src/utils/message-history.ts +143 -0
- package/src/utils/message-tree-utils.ts +67 -0
- package/src/utils/message-updater.ts +279 -0
- package/src/utils/open-file.ts +146 -0
- package/src/utils/open-url.ts +63 -0
- package/src/utils/path-completion.ts +111 -0
- package/src/utils/path-helpers.ts +38 -0
- package/src/utils/pending-attachments.ts +360 -0
- package/src/utils/polling-backoff.ts +59 -0
- package/src/utils/project-picker.ts +12 -0
- package/src/utils/queue-panel-actions.ts +70 -0
- package/src/utils/recent-projects.ts +156 -0
- package/src/utils/renderer-cleanup.ts +233 -0
- package/src/utils/response-ad-positions.ts +1 -0
- package/src/utils/run-state-storage.ts +603 -0
- package/src/utils/safe-json.ts +153 -0
- package/src/utils/sdk-event-handlers.ts +571 -0
- package/src/utils/send-message-helpers.ts +210 -0
- package/src/utils/send-message-timer.ts +110 -0
- package/src/utils/settings.ts +226 -0
- package/src/utils/skill-registry.ts +94 -0
- package/src/utils/spawn-agent-matcher.ts +55 -0
- package/src/utils/status-indicator-state.ts +107 -0
- package/src/utils/stream-chunk-processor.ts +57 -0
- package/src/utils/strings.ts +271 -0
- package/src/utils/subscription.ts +31 -0
- package/src/utils/syntax-highlighter.tsx +19 -0
- package/src/utils/terminal-color-detection.ts +474 -0
- package/src/utils/terminal-command-broker.ts +467 -0
- package/src/utils/terminal-enter-detection.ts +63 -0
- package/src/utils/terminal-images.ts +229 -0
- package/src/utils/terminal-io.ts +85 -0
- package/src/utils/terminal-protocol-controller.ts +173 -0
- package/src/utils/terminal-reset-sequences.ts +33 -0
- package/src/utils/terminal-title.ts +84 -0
- package/src/utils/terminal-watchdog.ts +366 -0
- package/src/utils/text-layout.ts +149 -0
- package/src/utils/theme-config.ts +141 -0
- package/src/utils/theme-system.ts +1206 -0
- package/src/utils/think-tag-parser.ts +95 -0
- package/src/utils/time-format.ts +21 -0
- package/src/utils/trace-writer.ts +162 -0
- package/src/utils/ui-constants.ts +69 -0
- package/src/utils/update-checker.ts +110 -0
- package/src/utils/usage-banner-state.ts +146 -0
- package/src/utils/validation-error-formatting.ts +86 -0
- package/src/utils/validation-error-helpers.ts +19 -0
- package/src/utils/windows-terminal-health.ts +124 -0
- package/src/utils/word-wrap-utils.ts +53 -0
- package/src/utils/write-file-atomic.ts +54 -0
- package/src/utils/yield-to-event-loop.ts +9 -0
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
import { isEqual } from 'lodash'
|
|
2
|
+
|
|
3
|
+
import { formatToolOutput } from './anvil-client'
|
|
4
|
+
import {
|
|
5
|
+
shouldCollapseByDefault,
|
|
6
|
+
shouldCollapseForParent,
|
|
7
|
+
shouldHideAgent,
|
|
8
|
+
} from './constants'
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
ContentBlock,
|
|
12
|
+
AgentContentBlock,
|
|
13
|
+
AskUserContentBlock,
|
|
14
|
+
} from '../types/chat'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extracts the base agent name from a potentially scoped/versioned agent type string.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* getAgentBaseName('anvil/file-picker@0.0.2') // 'file-picker'
|
|
21
|
+
* getAgentBaseName('file-picker@1.0.0') // 'file-picker'
|
|
22
|
+
* getAgentBaseName('file-picker') // 'file-picker'
|
|
23
|
+
* getAgentBaseName('file_picker') // 'file-picker'
|
|
24
|
+
*/
|
|
25
|
+
export const getAgentBaseName = (type: string): string => {
|
|
26
|
+
const segment = type.split('/').pop() ?? type
|
|
27
|
+
return segment.split('@')[0].replace(/_/g, '-')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Extracts plan content from a buffer containing <PLAN>...</PLAN> tags.
|
|
32
|
+
* Returns the trimmed content between tags, or null if not found.
|
|
33
|
+
*/
|
|
34
|
+
export const extractPlanFromBuffer = (buffer: string): string | null => {
|
|
35
|
+
const openIdx = buffer.indexOf('<PLAN>')
|
|
36
|
+
const closeIdx = buffer.indexOf('</PLAN>')
|
|
37
|
+
if (openIdx !== -1 && closeIdx !== -1 && closeIdx > openIdx) {
|
|
38
|
+
return buffer.slice(openIdx + '<PLAN>'.length, closeIdx).trim()
|
|
39
|
+
}
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const scrubPlanTags = (s: string): string => {
|
|
44
|
+
// Support both the canonical </PLAN> tag and the legacy </cb_plan> tag.
|
|
45
|
+
const closingTagPattern = '(?:<\\/PLAN>|<\\/cb_plan>)'
|
|
46
|
+
return s
|
|
47
|
+
.replace(new RegExp(`<PLAN>[\\s\\S]*?${closingTagPattern}`, 'g'), '')
|
|
48
|
+
.replace(/<PLAN>[\s\S]*$/g, '')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const scrubPlanTagsInBlocks = (
|
|
52
|
+
blocks: ContentBlock[],
|
|
53
|
+
): ContentBlock[] => {
|
|
54
|
+
return blocks
|
|
55
|
+
.map((block) => {
|
|
56
|
+
if (block.type !== 'text') {
|
|
57
|
+
return block
|
|
58
|
+
}
|
|
59
|
+
const newContent = scrubPlanTags(block.content)
|
|
60
|
+
return { ...block, content: newContent }
|
|
61
|
+
})
|
|
62
|
+
.filter((block) => block.type !== 'text' || block.content.trim() !== '')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const insertPlanBlock = (
|
|
66
|
+
blocks: ContentBlock[],
|
|
67
|
+
planContent: string,
|
|
68
|
+
): ContentBlock[] => {
|
|
69
|
+
const cleanedBlocks = scrubPlanTagsInBlocks(blocks)
|
|
70
|
+
return [
|
|
71
|
+
...cleanedBlocks,
|
|
72
|
+
{
|
|
73
|
+
type: 'plan',
|
|
74
|
+
content: planContent,
|
|
75
|
+
},
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Recursively removes agent blocks that should never be rendered (e.g.
|
|
81
|
+
* context-pruner). Live streams filter these at the event handlers;
|
|
82
|
+
* sanitizeRestoredMessages uses this to clean transcripts persisted before
|
|
83
|
+
* that filter matched the bundled agent ids. Returns the same array when
|
|
84
|
+
* nothing needs stripping. Operates on persisted data, so corrupted shapes
|
|
85
|
+
* (non-array input, null entries, missing fields) pass through untouched
|
|
86
|
+
* rather than throwing.
|
|
87
|
+
*/
|
|
88
|
+
export const stripHiddenAgentBlocks = (
|
|
89
|
+
blocks: ContentBlock[],
|
|
90
|
+
): ContentBlock[] => {
|
|
91
|
+
if (!Array.isArray(blocks)) {
|
|
92
|
+
return blocks
|
|
93
|
+
}
|
|
94
|
+
let changed = false
|
|
95
|
+
const result: ContentBlock[] = []
|
|
96
|
+
for (const block of blocks) {
|
|
97
|
+
if (block?.type !== 'agent') {
|
|
98
|
+
result.push(block)
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
if (shouldHideAgent(block.agentType ?? '')) {
|
|
102
|
+
changed = true
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
const strippedChildren = Array.isArray(block.blocks)
|
|
106
|
+
? stripHiddenAgentBlocks(block.blocks)
|
|
107
|
+
: undefined
|
|
108
|
+
if (strippedChildren && strippedChildren !== block.blocks) {
|
|
109
|
+
changed = true
|
|
110
|
+
result.push({ ...block, blocks: strippedChildren })
|
|
111
|
+
} else {
|
|
112
|
+
result.push(block)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return changed ? result : blocks
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Recursively collapses blocks that weren't manually opened by the user.
|
|
120
|
+
* Preserves user intent by keeping blocks open if userOpened is true.
|
|
121
|
+
*/
|
|
122
|
+
export const autoCollapseBlocks = (blocks: ContentBlock[]): ContentBlock[] => {
|
|
123
|
+
return blocks.map((block) => {
|
|
124
|
+
// Handle thinking blocks (grouped text blocks)
|
|
125
|
+
if (block.type === 'text' && block.thinkingId) {
|
|
126
|
+
return block.userOpened
|
|
127
|
+
? block
|
|
128
|
+
: { ...block, thinkingCollapseState: 'hidden' as const }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Handle agent blocks
|
|
132
|
+
if (block.type === 'agent') {
|
|
133
|
+
const updatedBlock = block.userOpened
|
|
134
|
+
? block
|
|
135
|
+
: { ...block, isCollapsed: true }
|
|
136
|
+
|
|
137
|
+
// Recursively update nested blocks
|
|
138
|
+
if (updatedBlock.blocks) {
|
|
139
|
+
return {
|
|
140
|
+
...updatedBlock,
|
|
141
|
+
blocks: autoCollapseBlocks(updatedBlock.blocks),
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return updatedBlock
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Handle tool blocks
|
|
148
|
+
if (block.type === 'tool') {
|
|
149
|
+
return block.userOpened ? block : { ...block, isCollapsed: true }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle agent-list blocks
|
|
153
|
+
if (block.type === 'agent-list') {
|
|
154
|
+
return block.userOpened ? block : { ...block, isCollapsed: true }
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return block
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Result of extracting content from a spawn_agents result value.
|
|
163
|
+
*/
|
|
164
|
+
export interface SpawnAgentResultContent {
|
|
165
|
+
content: string
|
|
166
|
+
hasError: boolean
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Extracts text content from a Message object's content array.
|
|
171
|
+
* Handles assistant messages with TextPart content.
|
|
172
|
+
*/
|
|
173
|
+
const extractTextFromMessageContent = (content: unknown): string => {
|
|
174
|
+
if (!Array.isArray(content)) {
|
|
175
|
+
return ''
|
|
176
|
+
}
|
|
177
|
+
return content
|
|
178
|
+
.filter(
|
|
179
|
+
(part: any) => part?.type === 'text' && typeof part?.text === 'string',
|
|
180
|
+
)
|
|
181
|
+
.map((part: any) => part.text)
|
|
182
|
+
.join('')
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Extracts displayable content from a spawn_agents result value.
|
|
187
|
+
* Handles various nested structures that can come back from agent spawns.
|
|
188
|
+
*/
|
|
189
|
+
export const extractSpawnAgentResultContent = (
|
|
190
|
+
resultValue: unknown,
|
|
191
|
+
): SpawnAgentResultContent => {
|
|
192
|
+
// Handle null/undefined
|
|
193
|
+
if (!resultValue) {
|
|
194
|
+
return { content: '', hasError: false }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Handle direct string
|
|
198
|
+
if (typeof resultValue === 'string') {
|
|
199
|
+
return { content: resultValue, hasError: false }
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (typeof resultValue !== 'object') {
|
|
203
|
+
return { content: '', hasError: false }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const obj = resultValue as Record<string, unknown>
|
|
207
|
+
|
|
208
|
+
// Handle empty object
|
|
209
|
+
if (Object.keys(obj).length === 0) {
|
|
210
|
+
return { content: '', hasError: false }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Handle error messages (check both top-level and nested)
|
|
214
|
+
if (obj.errorMessage) {
|
|
215
|
+
return { content: String(obj.errorMessage), hasError: true }
|
|
216
|
+
}
|
|
217
|
+
if ((obj.value as any)?.errorMessage) {
|
|
218
|
+
return { content: String((obj.value as any).errorMessage), hasError: true }
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Handle lastMessage and allMessages output modes: { type: "lastMessage"|"allMessages", value: [Message array] }
|
|
222
|
+
// This is common for agents like researcher-web
|
|
223
|
+
if (
|
|
224
|
+
(obj.type === 'lastMessage' || obj.type === 'allMessages') &&
|
|
225
|
+
Array.isArray(obj.value)
|
|
226
|
+
) {
|
|
227
|
+
const messages = obj.value as Array<{ role?: string; content?: unknown }>
|
|
228
|
+
const textContent = messages
|
|
229
|
+
.filter((msg) => msg?.role === 'assistant')
|
|
230
|
+
.map((msg) => extractTextFromMessageContent(msg?.content))
|
|
231
|
+
.filter(Boolean)
|
|
232
|
+
.join('\n')
|
|
233
|
+
return { content: textContent, hasError: false }
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Handle structuredOutput mode: { type: "structuredOutput", value: any }
|
|
237
|
+
if (obj.type === 'structuredOutput') {
|
|
238
|
+
const value = obj.value
|
|
239
|
+
// Check for message field in structured output
|
|
240
|
+
if (value && typeof value === 'object') {
|
|
241
|
+
const valueObj = value as Record<string, unknown>
|
|
242
|
+
if (typeof valueObj.message === 'string') {
|
|
243
|
+
return { content: valueObj.message, hasError: false }
|
|
244
|
+
}
|
|
245
|
+
// Check for data.message pattern
|
|
246
|
+
if (valueObj.data && typeof valueObj.data === 'object') {
|
|
247
|
+
const dataObj = valueObj.data as Record<string, unknown>
|
|
248
|
+
if (typeof dataObj.message === 'string') {
|
|
249
|
+
return { content: dataObj.message, hasError: false }
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Fall through to format as JSON
|
|
254
|
+
return {
|
|
255
|
+
content: formatToolOutput([{ type: 'json', value: obj.value }]),
|
|
256
|
+
hasError: false,
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Handle nested string value: { value: "..." }
|
|
261
|
+
if (typeof obj.value === 'string') {
|
|
262
|
+
return { content: obj.value, hasError: false }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Handle message field (top-level or nested)
|
|
266
|
+
if (obj.message) {
|
|
267
|
+
return { content: String(obj.message), hasError: false }
|
|
268
|
+
}
|
|
269
|
+
if ((obj.value as any)?.message) {
|
|
270
|
+
return { content: String((obj.value as any).message), hasError: false }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Fallback to formatted output
|
|
274
|
+
return {
|
|
275
|
+
content: formatToolOutput([{ type: 'json', value: resultValue }]),
|
|
276
|
+
hasError: false,
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Appends an interruption notice to blocks, either by modifying the last
|
|
282
|
+
* text block or adding a new one.
|
|
283
|
+
*/
|
|
284
|
+
export const appendInterruptionNotice = (
|
|
285
|
+
blocks: ContentBlock[],
|
|
286
|
+
): ContentBlock[] => {
|
|
287
|
+
const lastBlock = blocks[blocks.length - 1]
|
|
288
|
+
|
|
289
|
+
if (lastBlock && lastBlock.type === 'text') {
|
|
290
|
+
const interruptedBlock: ContentBlock = {
|
|
291
|
+
...lastBlock,
|
|
292
|
+
content: `${lastBlock.content}\n\n[response interrupted]`,
|
|
293
|
+
}
|
|
294
|
+
return [...blocks.slice(0, -1), interruptedBlock]
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const interruptionNotice: ContentBlock = {
|
|
298
|
+
type: 'text',
|
|
299
|
+
content: '[response interrupted]',
|
|
300
|
+
}
|
|
301
|
+
return [...blocks, interruptionNotice]
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Recursively finds an agent block by ID and returns its agent type.
|
|
306
|
+
* Returns undefined if not found.
|
|
307
|
+
*/
|
|
308
|
+
export const findAgentTypeById = (
|
|
309
|
+
blocks: ContentBlock[],
|
|
310
|
+
agentId: string,
|
|
311
|
+
): string | undefined => {
|
|
312
|
+
for (const block of blocks) {
|
|
313
|
+
if (block.type === 'agent') {
|
|
314
|
+
if (block.agentId === agentId) {
|
|
315
|
+
return block.agentType
|
|
316
|
+
}
|
|
317
|
+
if (block.blocks) {
|
|
318
|
+
const found = findAgentTypeById(block.blocks, agentId)
|
|
319
|
+
if (found) {
|
|
320
|
+
return found
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return undefined
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Options for creating an agent content block.
|
|
330
|
+
*/
|
|
331
|
+
export interface CreateAgentBlockOptions {
|
|
332
|
+
agentId: string
|
|
333
|
+
agentType: string
|
|
334
|
+
prompt?: string
|
|
335
|
+
params?: Record<string, unknown>
|
|
336
|
+
/** The spawn_agents tool call ID that created this block */
|
|
337
|
+
spawnToolCallId?: string
|
|
338
|
+
/** The index within the spawn_agents call */
|
|
339
|
+
spawnIndex?: number
|
|
340
|
+
/** The agent type of the parent agent that spawned this one */
|
|
341
|
+
parentAgentType?: string
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Creates a new agent content block with standard defaults.
|
|
346
|
+
*/
|
|
347
|
+
export const createAgentBlock = (
|
|
348
|
+
options: CreateAgentBlockOptions,
|
|
349
|
+
): AgentContentBlock => {
|
|
350
|
+
const {
|
|
351
|
+
agentId,
|
|
352
|
+
agentType,
|
|
353
|
+
prompt,
|
|
354
|
+
params,
|
|
355
|
+
spawnToolCallId,
|
|
356
|
+
spawnIndex,
|
|
357
|
+
parentAgentType,
|
|
358
|
+
} = options
|
|
359
|
+
const shouldCollapse =
|
|
360
|
+
shouldCollapseByDefault(agentType || '') ||
|
|
361
|
+
shouldCollapseForParent(agentType || '', parentAgentType)
|
|
362
|
+
return {
|
|
363
|
+
type: 'agent',
|
|
364
|
+
agentId,
|
|
365
|
+
agentName: agentType || 'Agent',
|
|
366
|
+
agentType: agentType || 'unknown',
|
|
367
|
+
content: '',
|
|
368
|
+
status: 'running' as const,
|
|
369
|
+
blocks: [] as ContentBlock[],
|
|
370
|
+
initialPrompt: prompt || '',
|
|
371
|
+
...(params && { params }),
|
|
372
|
+
...(spawnToolCallId && { spawnToolCallId }),
|
|
373
|
+
...(spawnIndex !== undefined && { spawnIndex }),
|
|
374
|
+
...(shouldCollapse && { isCollapsed: true }),
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Helper function to recursively update blocks by target agent ID.
|
|
380
|
+
*/
|
|
381
|
+
export const updateBlocksRecursively = (
|
|
382
|
+
blocks: ContentBlock[],
|
|
383
|
+
targetAgentId: string,
|
|
384
|
+
updateFn: (block: ContentBlock) => ContentBlock,
|
|
385
|
+
): ContentBlock[] => {
|
|
386
|
+
let foundTarget = false
|
|
387
|
+
const result = blocks.map((block) => {
|
|
388
|
+
if (block.type === 'agent' && block.agentId === targetAgentId) {
|
|
389
|
+
foundTarget = true
|
|
390
|
+
return updateFn(block)
|
|
391
|
+
}
|
|
392
|
+
if (block.type === 'agent' && block.blocks) {
|
|
393
|
+
const updatedBlocks = updateBlocksRecursively(
|
|
394
|
+
block.blocks,
|
|
395
|
+
targetAgentId,
|
|
396
|
+
updateFn,
|
|
397
|
+
)
|
|
398
|
+
if (updatedBlocks !== block.blocks) {
|
|
399
|
+
foundTarget = true
|
|
400
|
+
return {
|
|
401
|
+
...block,
|
|
402
|
+
blocks: updatedBlocks,
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return block
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
return foundTarget ? result : blocks
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Result from nestBlockUnderParent indicating whether the parent was found.
|
|
414
|
+
*/
|
|
415
|
+
export interface NestBlockResult {
|
|
416
|
+
blocks: ContentBlock[]
|
|
417
|
+
parentFound: boolean
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Nests a block under a parent agent, or returns it at top level if parent not found.
|
|
422
|
+
*/
|
|
423
|
+
export const nestBlockUnderParent = (
|
|
424
|
+
blocks: ContentBlock[],
|
|
425
|
+
parentAgentId: string,
|
|
426
|
+
blockToNest: ContentBlock,
|
|
427
|
+
): NestBlockResult => {
|
|
428
|
+
let parentFound = false
|
|
429
|
+
const updatedBlocks = updateBlocksRecursively(
|
|
430
|
+
blocks,
|
|
431
|
+
parentAgentId,
|
|
432
|
+
(parentBlock) => {
|
|
433
|
+
if (parentBlock.type !== 'agent') {
|
|
434
|
+
return parentBlock
|
|
435
|
+
}
|
|
436
|
+
parentFound = true
|
|
437
|
+
return {
|
|
438
|
+
...parentBlock,
|
|
439
|
+
blocks: [...(parentBlock.blocks || []), blockToNest],
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
return { blocks: updatedBlocks, parentFound }
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Checks if a block with the given targetId exists anywhere in the children of the blocks.
|
|
449
|
+
*/
|
|
450
|
+
const findBlockInChildren = (
|
|
451
|
+
blocks: ContentBlock[],
|
|
452
|
+
targetId: string,
|
|
453
|
+
): boolean => {
|
|
454
|
+
for (const block of blocks) {
|
|
455
|
+
if (block.type === 'agent' && block.agentId === targetId) {
|
|
456
|
+
return true
|
|
457
|
+
}
|
|
458
|
+
if (block.type === 'agent' && block.blocks) {
|
|
459
|
+
if (findBlockInChildren(block.blocks, targetId)) {
|
|
460
|
+
return true
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return false
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Checks if a block with the given agentId is already nested under the specified parent.
|
|
469
|
+
*/
|
|
470
|
+
const checkBlockIsUnderParent = (
|
|
471
|
+
blocks: ContentBlock[],
|
|
472
|
+
targetAgentId: string,
|
|
473
|
+
parentAgentId: string,
|
|
474
|
+
): boolean => {
|
|
475
|
+
for (const block of blocks) {
|
|
476
|
+
if (block.type === 'agent' && block.agentId === parentAgentId) {
|
|
477
|
+
// Found the parent, check if target is anywhere in its children
|
|
478
|
+
return findBlockInChildren(block.blocks || [], targetAgentId)
|
|
479
|
+
} else if (block.type === 'agent' && block.blocks) {
|
|
480
|
+
// Recurse into other agent blocks to find the parent
|
|
481
|
+
if (checkBlockIsUnderParent(block.blocks, targetAgentId, parentAgentId)) {
|
|
482
|
+
return true
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return false
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Extracts a block with given agentId from nested blocks structure.
|
|
491
|
+
* Returns the remaining blocks and the extracted block (if found).
|
|
492
|
+
*/
|
|
493
|
+
export const extractBlockById = (
|
|
494
|
+
blocks: ContentBlock[],
|
|
495
|
+
targetAgentId: string,
|
|
496
|
+
): { remainingBlocks: ContentBlock[]; extractedBlock: ContentBlock | null } => {
|
|
497
|
+
let extractedBlock: ContentBlock | null = null
|
|
498
|
+
|
|
499
|
+
const extractRecursively = (blocks: ContentBlock[]): ContentBlock[] => {
|
|
500
|
+
const result: ContentBlock[] = []
|
|
501
|
+
for (const block of blocks) {
|
|
502
|
+
if (block.type === 'agent' && block.agentId === targetAgentId) {
|
|
503
|
+
extractedBlock = block
|
|
504
|
+
// Don't add to result - we're extracting it
|
|
505
|
+
} else if (block.type === 'agent' && block.blocks) {
|
|
506
|
+
result.push({
|
|
507
|
+
...block,
|
|
508
|
+
blocks: extractRecursively(block.blocks),
|
|
509
|
+
})
|
|
510
|
+
} else {
|
|
511
|
+
result.push(block)
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return result
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const remainingBlocks = extractRecursively(blocks)
|
|
518
|
+
return { remainingBlocks, extractedBlock }
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export const moveSpawnAgentBlock = (
|
|
522
|
+
blocks: ContentBlock[],
|
|
523
|
+
tempId: string,
|
|
524
|
+
realId: string,
|
|
525
|
+
parentId?: string,
|
|
526
|
+
params?: Record<string, unknown>,
|
|
527
|
+
prompt?: string,
|
|
528
|
+
realAgentType?: string,
|
|
529
|
+
): ContentBlock[] => {
|
|
530
|
+
const updateAgentBlock = (block: ContentBlock): ContentBlock => {
|
|
531
|
+
if (block.type !== 'agent') {
|
|
532
|
+
return block
|
|
533
|
+
}
|
|
534
|
+
const updatedBlock: ContentBlock = {
|
|
535
|
+
...block,
|
|
536
|
+
agentId: realId,
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (params) {
|
|
540
|
+
updatedBlock.params = params
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (prompt && block.initialPrompt === '') {
|
|
544
|
+
updatedBlock.initialPrompt = prompt
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (realAgentType) {
|
|
548
|
+
updatedBlock.agentType = realAgentType
|
|
549
|
+
updatedBlock.agentName = realAgentType
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return updatedBlock
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// If there's a parentId, we need to move the block under the parent.
|
|
556
|
+
// First check if the block is already under the correct parent.
|
|
557
|
+
if (parentId) {
|
|
558
|
+
const isAlreadyUnderParent = checkBlockIsUnderParent(
|
|
559
|
+
blocks,
|
|
560
|
+
tempId,
|
|
561
|
+
parentId,
|
|
562
|
+
)
|
|
563
|
+
if (isAlreadyUnderParent) {
|
|
564
|
+
// Block is already under the correct parent, just update it in place
|
|
565
|
+
return updateBlocksRecursively(blocks, tempId, updateAgentBlock)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Block needs to be moved under the parent - extract and nest
|
|
569
|
+
const { remainingBlocks, extractedBlock } = extractBlockById(blocks, tempId)
|
|
570
|
+
if (extractedBlock && extractedBlock.type === 'agent') {
|
|
571
|
+
const blockToMove = updateAgentBlock(extractedBlock)
|
|
572
|
+
const { blocks: nestedBlocks, parentFound } = nestBlockUnderParent(
|
|
573
|
+
remainingBlocks,
|
|
574
|
+
parentId,
|
|
575
|
+
blockToMove,
|
|
576
|
+
)
|
|
577
|
+
if (parentFound) {
|
|
578
|
+
return nestedBlocks
|
|
579
|
+
}
|
|
580
|
+
// Parent not found, update in place instead of appending to end
|
|
581
|
+
return updateBlocksRecursively(blocks, tempId, updateAgentBlock)
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// No parentId or block not found - just update in place to preserve order
|
|
586
|
+
return updateBlocksRecursively(blocks, tempId, updateAgentBlock)
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Options for transforming ask_user tool blocks to ask-user content blocks.
|
|
591
|
+
*/
|
|
592
|
+
export interface TransformAskUserOptions {
|
|
593
|
+
toolCallId: string
|
|
594
|
+
resultValue: unknown
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Transforms ask_user tool blocks into ask-user content blocks when tool results arrive.
|
|
599
|
+
* Recursively processes nested agent blocks.
|
|
600
|
+
*/
|
|
601
|
+
export const transformAskUserBlocks = (
|
|
602
|
+
blocks: ContentBlock[],
|
|
603
|
+
options: TransformAskUserOptions,
|
|
604
|
+
): ContentBlock[] => {
|
|
605
|
+
const { toolCallId, resultValue } = options
|
|
606
|
+
|
|
607
|
+
return blocks.map((block) => {
|
|
608
|
+
if (
|
|
609
|
+
block.type === 'tool' &&
|
|
610
|
+
block.toolCallId === toolCallId &&
|
|
611
|
+
block.toolName === 'ask_user'
|
|
612
|
+
) {
|
|
613
|
+
const skipped = (resultValue as any)?.skipped
|
|
614
|
+
const answers = (resultValue as any)?.answers
|
|
615
|
+
const questions = block.input.questions
|
|
616
|
+
|
|
617
|
+
if (!answers && !skipped) {
|
|
618
|
+
// If no result data, keep as tool block (fallback)
|
|
619
|
+
return block
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return {
|
|
623
|
+
type: 'ask-user',
|
|
624
|
+
toolCallId,
|
|
625
|
+
questions,
|
|
626
|
+
answers,
|
|
627
|
+
skipped,
|
|
628
|
+
} as AskUserContentBlock
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (block.type === 'agent' && block.blocks) {
|
|
632
|
+
const updatedBlocks = transformAskUserBlocks(block.blocks, options)
|
|
633
|
+
if (updatedBlocks !== block.blocks) {
|
|
634
|
+
return { ...block, blocks: updatedBlocks }
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return block
|
|
638
|
+
})
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Options for updating tool blocks with output.
|
|
643
|
+
*/
|
|
644
|
+
export interface UpdateToolBlockOptions {
|
|
645
|
+
toolCallId: string
|
|
646
|
+
toolOutput: unknown[]
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Updates tool blocks with their output when tool results arrive.
|
|
651
|
+
* Handles special formatting for terminal command output.
|
|
652
|
+
* Recursively processes nested agent blocks.
|
|
653
|
+
*/
|
|
654
|
+
export const updateToolBlockWithOutput = (
|
|
655
|
+
blocks: ContentBlock[],
|
|
656
|
+
options: UpdateToolBlockOptions,
|
|
657
|
+
): ContentBlock[] => {
|
|
658
|
+
const { toolCallId, toolOutput } = options
|
|
659
|
+
|
|
660
|
+
return blocks.map((block) => {
|
|
661
|
+
if (block.type === 'tool' && block.toolCallId === toolCallId) {
|
|
662
|
+
let output: string
|
|
663
|
+
if (block.toolName === 'run_terminal_command') {
|
|
664
|
+
const parsed = (toolOutput?.[0] as any)?.value
|
|
665
|
+
if (parsed?.stdout || parsed?.stderr) {
|
|
666
|
+
output = (parsed.stdout || '') + (parsed.stderr || '')
|
|
667
|
+
} else {
|
|
668
|
+
output = formatToolOutput(toolOutput)
|
|
669
|
+
}
|
|
670
|
+
} else {
|
|
671
|
+
output = formatToolOutput(toolOutput)
|
|
672
|
+
}
|
|
673
|
+
return { ...block, output }
|
|
674
|
+
} else if (block.type === 'agent' && block.blocks) {
|
|
675
|
+
const updatedBlocks = updateToolBlockWithOutput(block.blocks, options)
|
|
676
|
+
// Avoid creating new block if nested blocks didn't change
|
|
677
|
+
if (isEqual(block.blocks, updatedBlocks)) {
|
|
678
|
+
return block
|
|
679
|
+
}
|
|
680
|
+
return { ...block, blocks: updatedBlocks }
|
|
681
|
+
}
|
|
682
|
+
return block
|
|
683
|
+
})
|
|
684
|
+
}
|