@adecore/agents-react 0.31.11 → 0.32.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/dist/agents/AccountDot.js +57 -6
- package/dist/agents/AgentIcon.js +88 -12
- package/dist/agents/ModelOptionControl.js +153 -18
- package/dist/agents/ProviderLogo.js +49 -4
- package/dist/agents/account-limits.js +17 -14
- package/dist/agents/accounts.js +99 -67
- package/dist/agents/agent-marks.js +5 -3
- package/dist/agents/model-name.js +42 -31
- package/dist/agents/model-options.js +8 -8
- package/dist/agents/model-requirement.js +17 -14
- package/dist/agents/option-words.d.ts +7 -0
- package/dist/agents/option-words.js +35 -0
- package/dist/agents/provider-paths.js +6 -6
- package/dist/agents/status-look.js +32 -12
- package/dist/chat/account-choice.d.ts +13 -0
- package/dist/chat/account-choice.js +90 -20
- package/dist/chat/actions.js +126 -59
- package/dist/chat/activity.js +5 -5
- package/dist/chat/attachments.js +74 -53
- package/dist/chat/bookmarks.js +58 -48
- package/dist/chat/chat-client.js +344 -263
- package/dist/chat/chat-references.js +3 -3
- package/dist/chat/composer-effort.d.ts +5 -0
- package/dist/chat/composer-effort.js +26 -0
- package/dist/chat/composer-send.js +13 -13
- package/dist/chat/drafts.js +115 -79
- package/dist/chat/forks.js +81 -24
- package/dist/chat/guards.js +15 -43
- package/dist/chat/index.d.ts +3 -1
- package/dist/chat/index.js +3 -2
- package/dist/chat/logic/ansi.js +170 -167
- package/dist/chat/logic/answers.js +2 -2
- package/dist/chat/logic/bookmarks.js +19 -19
- package/dist/chat/logic/command-looks.js +138 -96
- package/dist/chat/logic/context-usage.js +13 -9
- package/dist/chat/logic/continue-after-stop.js +15 -19
- package/dist/chat/logic/effort.d.ts +14 -0
- package/dist/chat/logic/effort.js +34 -0
- package/dist/chat/logic/embedded-code.js +387 -326
- package/dist/chat/logic/fork.js +134 -110
- package/dist/chat/logic/generated-image.js +54 -23
- package/dist/chat/logic/goal.js +28 -20
- package/dist/chat/logic/handback.js +6 -6
- package/dist/chat/logic/json.js +2 -2
- package/dist/chat/logic/limit.js +46 -30
- package/dist/chat/logic/picker-entries.d.ts +5 -3
- package/dist/chat/logic/picker-entries.js +83 -43
- package/dist/chat/logic/resume-compaction.d.ts +14 -1
- package/dist/chat/logic/resume-compaction.js +93 -24
- package/dist/chat/logic/scrubber.js +188 -136
- package/dist/chat/logic/thought.js +13 -16
- package/dist/chat/logic/thread-cards.js +60 -48
- package/dist/chat/logic/timeline-copy.js +30 -38
- package/dist/chat/logic/timeline-target.js +92 -63
- package/dist/chat/logic/timeline.js +499 -355
- package/dist/chat/logic/tool-catalog.js +54 -34
- package/dist/chat/logic/tools.js +106 -82
- package/dist/chat/logic/visual-height.js +18 -18
- package/dist/chat/logic/visual-viewport.js +6 -3
- package/dist/chat/logic/welcome.js +9 -9
- package/dist/chat/logic/workflow.js +45 -34
- package/dist/chat/mentions.js +149 -109
- package/dist/chat/persisted-json.js +51 -54
- package/dist/chat/preferences.d.ts +4 -0
- package/dist/chat/preferences.js +162 -84
- package/dist/chat/prompt-hold.js +9 -9
- package/dist/chat/quote.js +21 -30
- package/dist/chat/reading-width.js +12 -10
- package/dist/chat/recent-messages.js +34 -21
- package/dist/chat/resume-compaction-dismissals.js +25 -5
- package/dist/chat/runtime-modes.js +3 -3
- package/dist/chat/send-key.js +11 -7
- package/dist/chat/stash.js +89 -69
- package/dist/chat/subagent-conversation.js +181 -139
- package/dist/chat/subagent-list.js +65 -67
- package/dist/chat/subagent-support.js +12 -7
- package/dist/chat/subagent-view.js +132 -44
- package/dist/chat/thumbnails.js +88 -60
- package/dist/chat/timeline-flash.js +24 -12
- package/dist/chat/timeline-scroll.js +116 -86
- package/dist/chat/ui/AccountPill.js +126 -15
- package/dist/chat/ui/AnsiOutput.js +62 -6
- package/dist/chat/ui/BookmarkMarker.js +231 -41
- package/dist/chat/ui/BookmarkSubmenu.js +165 -18
- package/dist/chat/ui/ChatActivity.js +649 -70
- package/dist/chat/ui/ChatReferenceChip.js +104 -9
- package/dist/chat/ui/CodeBlock.js +132 -58
- package/dist/chat/ui/Composer.js +866 -132
- package/dist/chat/ui/ComposerInput.js +309 -120
- package/dist/chat/ui/DiffPool.js +75 -14
- package/dist/chat/ui/EditDiff.js +160 -21
- package/dist/chat/ui/FadingWords.js +32 -3
- package/dist/chat/ui/GoalDock.js +235 -42
- package/dist/chat/ui/ImageView.js +617 -61
- package/dist/chat/ui/LimitState.js +446 -72
- package/dist/chat/ui/Markdown.js +991 -173
- package/dist/chat/ui/MermaidBlock.js +238 -68
- package/dist/chat/ui/MessageActions.js +251 -39
- package/dist/chat/ui/ModelMarks.js +55 -5
- package/dist/chat/ui/Pickers.js +664 -113
- package/dist/chat/ui/PromptComposer.js +281 -51
- package/dist/chat/ui/QuoteButton.js +151 -57
- package/dist/chat/ui/ResumeCompactionDock.d.ts +6 -2
- package/dist/chat/ui/ResumeCompactionDock.js +141 -6
- package/dist/chat/ui/RowTime.js +59 -5
- package/dist/chat/ui/RunSettings.js +1744 -154
- package/dist/chat/ui/Scrubber.js +517 -88
- package/dist/chat/ui/ShellCommand.js +183 -107
- package/dist/chat/ui/SubagentControls.js +212 -24
- package/dist/chat/ui/SubagentInfo.js +170 -13
- package/dist/chat/ui/SubagentStopButton.js +115 -24
- package/dist/chat/ui/SubagentTimeline.js +357 -91
- package/dist/chat/ui/Timeline.js +988 -476
- package/dist/chat/ui/TimelineMenu.js +552 -37
- package/dist/chat/ui/UnifiedDiff.js +234 -27
- package/dist/chat/ui/UploadThumb.js +37 -7
- package/dist/chat/ui/VisualDialogs.js +251 -20
- package/dist/chat/ui/VisualFrame.js +321 -95
- package/dist/chat/ui/Welcome.js +72 -7
- package/dist/chat/ui/chips.js +1 -1
- package/dist/chat/ui/code-lines.js +36 -36
- package/dist/chat/ui/code-streaming.js +1 -1
- package/dist/chat/ui/code-theme.js +7 -7
- package/dist/chat/ui/composer/ComposerControls.js +329 -21
- package/dist/chat/ui/composer/ComposerMenu.js +358 -30
- package/dist/chat/ui/composer/DraftParts.js +185 -11
- package/dist/chat/ui/composer/QueuedMessages.js +138 -13
- package/dist/chat/ui/composer/chips.js +29 -19
- package/dist/chat/ui/composer/editor.js +79 -63
- package/dist/chat/ui/composer/insert.js +26 -12
- package/dist/chat/ui/composer/key-handler.js +180 -127
- package/dist/chat/ui/composer/keys.js +90 -73
- package/dist/chat/ui/composer/menu-aria.js +18 -9
- package/dist/chat/ui/composer/placeholder.js +78 -31
- package/dist/chat/ui/composer/surface.js +13 -37
- package/dist/chat/ui/composer/use-chat-controls.js +230 -90
- package/dist/chat/ui/composer/use-compaction-offer.js +50 -18
- package/dist/chat/ui/composer/use-composer-draft.js +65 -34
- package/dist/chat/ui/composer/use-composer-menus.js +478 -193
- package/dist/chat/ui/composer/use-composer-submit.js +251 -109
- package/dist/chat/ui/composer/use-continuable-turn.js +33 -6
- package/dist/chat/ui/composer/use-draft-files.js +245 -93
- package/dist/chat/ui/composer/use-effort-stepper.d.ts +13 -0
- package/dist/chat/ui/composer/use-effort-stepper.js +88 -0
- package/dist/chat/ui/composer/use-notice.js +61 -16
- package/dist/chat/ui/composer/use-pending-requests.js +38 -17
- package/dist/chat/ui/composer/use-prompt-history.js +117 -45
- package/dist/chat/ui/composer/use-prompt-hold.js +119 -35
- package/dist/chat/ui/composer/use-take-back.js +94 -72
- package/dist/chat/ui/diff/ApprovalReview.js +101 -23
- package/dist/chat/ui/diff/DiffFallbackBoundary.js +21 -13
- package/dist/chat/ui/diff/DiffOmitted.js +56 -6
- package/dist/chat/ui/diff/DiffStack.js +92 -8
- package/dist/chat/ui/diff/DiffUnavailable.js +56 -6
- package/dist/chat/ui/diff/EditorEditDiff.js +263 -51
- package/dist/chat/ui/diff/EditorPatchDiff.js +39 -14
- package/dist/chat/ui/diff/LegacyDiffPool.js +27 -10
- package/dist/chat/ui/diff/LegacyEditDiff.js +97 -20
- package/dist/chat/ui/diff/LegacyUnifiedDiff.js +172 -42
- package/dist/chat/ui/diff/RawDiff.js +65 -5
- package/dist/chat/ui/diff/ReviewDiff.js +77 -5
- package/dist/chat/ui/diff/diff-props.js +1 -1
- package/dist/chat/ui/diff/diff-source-budget.js +46 -35
- package/dist/chat/ui/diff/editor-edit-model.js +18 -14
- package/dist/chat/ui/diff/review-plan.js +63 -40
- package/dist/chat/ui/diff/review-settle.js +24 -9
- package/dist/chat/ui/diff/syntax-runtime.js +236 -201
- package/dist/chat/ui/diff/use-review-stack.js +146 -25
- package/dist/chat/ui/diff-syntax.js +70 -58
- package/dist/chat/ui/diff-theme.js +31 -12
- package/dist/chat/ui/file-links.js +39 -10
- package/dist/chat/ui/find-reveal.js +29 -8
- package/dist/chat/ui/full-diff.js +17 -12
- package/dist/chat/ui/highlighter.js +143 -113
- package/dist/chat/ui/icons.js +16 -7
- package/dist/chat/ui/index.js +1 -1
- package/dist/chat/ui/intelligent-ui/UiBlockFrame.js +556 -82
- package/dist/chat/ui/intelligent-ui/UiFallbackPart.js +73 -5
- package/dist/chat/ui/intelligent-ui/UiReply.js +666 -262
- package/dist/chat/ui/intelligent-ui/block-local.js +59 -31
- package/dist/chat/ui/intelligent-ui/chart-data.js +77 -67
- package/dist/chat/ui/intelligent-ui/node-text.js +77 -91
- package/dist/chat/ui/intelligent-ui/registry.js +46 -46
- package/dist/chat/ui/intelligent-ui/render-context.js +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/base-land.js +1 -1
- package/dist/chat/ui/intelligent-ui/renderers/binding.js +3 -3
- package/dist/chat/ui/intelligent-ui/renderers/chart.js +1223 -123
- package/dist/chat/ui/intelligent-ui/renderers/choices.js +312 -34
- package/dist/chat/ui/intelligent-ui/renderers/content.js +1241 -86
- package/dist/chat/ui/intelligent-ui/renderers/data.js +619 -54
- package/dist/chat/ui/intelligent-ui/renderers/inputs.js +868 -67
- package/dist/chat/ui/intelligent-ui/renderers/links.js +366 -62
- package/dist/chat/ui/intelligent-ui/renderers/map-slots.js +118 -60
- package/dist/chat/ui/intelligent-ui/renderers/map-style.js +85 -78
- package/dist/chat/ui/intelligent-ui/renderers/map-view.js +282 -217
- package/dist/chat/ui/intelligent-ui/renderers/map.js +621 -69
- package/dist/chat/ui/intelligent-ui/renderers/place-point.js +15 -8
- package/dist/chat/ui/intelligent-ui/renderers/structure.js +262 -20
- package/dist/chat/ui/intelligent-ui/renderers/text.js +674 -49
- package/dist/chat/ui/intelligent-ui/reply-parts.js +24 -15
- package/dist/chat/ui/intelligent-ui/table-data.js +97 -57
- package/dist/chat/ui/intelligent-ui/tones.js +31 -31
- package/dist/chat/ui/intelligent-ui/use-in-view.js +30 -12
- package/dist/chat/ui/intelligent-ui/use-later.js +28 -13
- package/dist/chat/ui/intelligent-ui/use-truncated.js +45 -21
- package/dist/chat/ui/intelligent-ui/use-ui-links.js +296 -109
- package/dist/chat/ui/intelligent-ui/use-ui-queries.js +256 -246
- package/dist/chat/ui/markdown-blocks.js +296 -262
- package/dist/chat/ui/mermaid.js +60 -72
- package/dist/chat/ui/quote-selection.js +32 -29
- package/dist/chat/ui/rehype-chips.js +35 -23
- package/dist/chat/ui/rehype-fade.js +28 -17
- package/dist/chat/ui/remark-html-as-text.js +22 -15
- package/dist/chat/ui/reply-context.js +1 -1
- package/dist/chat/ui/reveal.js +161 -72
- package/dist/chat/ui/rows/ForksRow.js +226 -24
- package/dist/chat/ui/rows/GeneratedImageRow.js +541 -87
- package/dist/chat/ui/rows/MessageRows.js +1706 -159
- package/dist/chat/ui/rows/RowContainer.js +57 -7
- package/dist/chat/ui/rows/Rows.js +463 -54
- package/dist/chat/ui/rows/SubagentRow.js +577 -60
- package/dist/chat/ui/rows/TaskRow.js +359 -40
- package/dist/chat/ui/rows/VisualRow.js +233 -11
- package/dist/chat/ui/rows/WorkRows.js +1946 -163
- package/dist/chat/ui/rows/WorkflowRow.js +293 -34
- package/dist/chat/ui/rows/row-rhythm.js +27 -17
- package/dist/chat/ui/theme-watch.js +26 -23
- package/dist/chat/ui/timestamp-format.js +4 -1
- package/dist/chat/ui/use-diff-source-tokens.js +70 -34
- package/dist/chat/ui/useToggleSet.js +51 -19
- package/dist/chat/ui/visual-theme.js +153 -128
- package/dist/chat/visual-bridge.js +123 -123
- package/dist/chat/visual-viewport.js +94 -83
- package/dist/chat/visuals.js +13 -5
- package/dist/host-hooks.d.ts +29 -0
- package/dist/host-hooks.js +78 -0
- package/dist/host.js +120 -48
- package/dist/index.js +1 -1
- package/dist/lazy.js +38 -32
- package/dist/locales/en/agent-chat.d.ts +35 -0
- package/dist/locales/en/agent-chat.js +747 -712
- package/dist/locales/en/agent-prompts.js +42 -42
- package/dist/locales/en/agent-providers.d.ts +1 -0
- package/dist/locales/en/agent-providers.js +166 -165
- package/dist/locales/en/agent-usage.d.ts +6 -0
- package/dist/locales/en/agent-usage.js +129 -123
- package/dist/locales/nl/agent-chat.d.ts +35 -0
- package/dist/locales/nl/agent-chat.js +747 -712
- package/dist/locales/nl/agent-prompts.js +42 -42
- package/dist/locales/nl/agent-providers.d.ts +1 -0
- package/dist/locales/nl/agent-providers.js +166 -165
- package/dist/locales/nl/agent-usage.d.ts +6 -0
- package/dist/locales/nl/agent-usage.js +129 -123
- package/dist/locales.js +14 -14
- package/dist/mounted-registry.js +31 -30
- package/dist/port-transport.js +122 -111
- package/dist/prompts/index.js +1 -1
- package/dist/prompts/logic/focus.js +15 -14
- package/dist/prompts/logic/keys.js +93 -59
- package/dist/prompts/logic/prompts.js +27 -14
- package/dist/prompts/logic/subjects.js +60 -46
- package/dist/prompts/logic/useFocusAfterAnswer.js +60 -20
- package/dist/prompts/logic/usePromptSession.js +88 -77
- package/dist/prompts/ui/ApprovalBody.js +246 -25
- package/dist/prompts/ui/PromptActions.js +215 -9
- package/dist/prompts/ui/PromptCard.js +215 -36
- package/dist/prompts/ui/PromptView.js +372 -34
- package/dist/prompts/ui/QuestionBody.js +269 -73
- package/dist/providers/AccountColors.js +36 -5
- package/dist/providers/AccountDetail.js +683 -50
- package/dist/providers/AccountVariables.js +139 -32
- package/dist/providers/AddAccountForm.js +125 -38
- package/dist/providers/CliDetail.js +343 -36
- package/dist/providers/InstallPage.js +644 -59
- package/dist/providers/ProviderList.d.ts +2 -1
- package/dist/providers/ProviderList.js +215 -23
- package/dist/providers/ProvidersPane.d.ts +4 -2
- package/dist/providers/ProvidersPane.js +244 -64
- package/dist/providers/account-actions.js +54 -30
- package/dist/providers/index.js +1 -1
- package/dist/providers/install-steps.js +12 -9
- package/dist/providers/pane-target.d.ts +3 -0
- package/dist/providers/pane-target.js +34 -13
- package/dist/providers/parts.js +62 -12
- package/dist/providers/provider-abilities.js +5 -5
- package/dist/providers/provider-groups.js +75 -40
- package/dist/providers/variables.js +73 -39
- package/dist/scope.js +6 -6
- package/dist/settings/sections.js +18 -18
- package/dist/state/chats.d.ts +1 -0
- package/dist/state/chats.js +408 -192
- package/dist/state/index.js +1 -1
- package/dist/state/provider-accounts.js +70 -47
- package/dist/state/providers.js +45 -16
- package/dist/state/usage.js +154 -77
- package/dist/storage.js +69 -68
- package/dist/testing/index.js +1 -1
- package/dist/testing/prompts.js +105 -67
- package/dist/transport.js +10 -10
- package/dist/ui/PlainTextarea.js +27 -3
- package/dist/usage/LimitsList.js +646 -40
- package/dist/usage/UsageBreakdown.js +879 -38
- package/dist/usage/UsageChart.js +311 -32
- package/dist/usage/UsageDialog.js +46 -4
- package/dist/usage/UsageLimits.js +467 -34
- package/dist/usage/UsageLimitsCard.js +95 -11
- package/dist/usage/UsagePage.js +817 -96
- package/dist/usage/UsagePane.js +164 -10
- package/dist/usage/UsageSummary.js +151 -6
- package/dist/usage/UsageTiles.js +164 -14
- package/dist/usage/format.js +32 -32
- package/dist/usage/index.d.ts +1 -1
- package/dist/usage/index.js +2 -1
- package/dist/usage/limit-groups.d.ts +2 -0
- package/dist/usage/limit-groups.js +77 -59
- package/dist/usage/limits.js +78 -43
- package/dist/usage/money.js +20 -4
- package/dist/usage/summary.js +85 -64
- package/package.json +8 -8
- package/src/agents/ModelOptionControl.tsx +3 -1
- package/src/agents/option-words.ts +27 -0
- package/src/chat/account-choice.ts +41 -1
- package/src/chat/composer-effort.ts +32 -0
- package/src/chat/forks.ts +4 -5
- package/src/chat/index.ts +3 -1
- package/src/chat/logic/effort.ts +32 -0
- package/src/chat/logic/picker-entries.ts +45 -10
- package/src/chat/logic/resume-compaction.ts +95 -17
- package/src/chat/logic/timeline.ts +11 -3
- package/src/chat/preferences.ts +35 -0
- package/src/chat/subagent-view.ts +2 -3
- package/src/chat/ui/AnsiOutput.tsx +1 -2
- package/src/chat/ui/BookmarkSubmenu.tsx +3 -3
- package/src/chat/ui/ChatActivity.tsx +14 -22
- package/src/chat/ui/ChatReferenceChip.tsx +2 -3
- package/src/chat/ui/Composer.tsx +15 -7
- package/src/chat/ui/EditDiff.tsx +2 -3
- package/src/chat/ui/LimitState.tsx +2 -2
- package/src/chat/ui/Markdown.tsx +10 -8
- package/src/chat/ui/MermaidBlock.tsx +2 -3
- package/src/chat/ui/Pickers.tsx +49 -29
- package/src/chat/ui/PromptComposer.tsx +7 -9
- package/src/chat/ui/ResumeCompactionDock.tsx +16 -4
- package/src/chat/ui/RunSettings.tsx +67 -28
- package/src/chat/ui/Scrubber.tsx +3 -3
- package/src/chat/ui/ShellCommand.tsx +2 -0
- package/src/chat/ui/SubagentInfo.tsx +2 -3
- package/src/chat/ui/SubagentTimeline.tsx +12 -16
- package/src/chat/ui/Timeline.tsx +6 -6
- package/src/chat/ui/UnifiedDiff.tsx +3 -4
- package/src/chat/ui/VisualFrame.tsx +5 -8
- package/src/chat/ui/code-theme.ts +4 -4
- package/src/chat/ui/composer/placeholder.ts +2 -2
- package/src/chat/ui/composer/use-compaction-offer.ts +36 -16
- package/src/chat/ui/composer/use-composer-menus.ts +4 -9
- package/src/chat/ui/composer/use-composer-submit.ts +2 -1
- package/src/chat/ui/composer/use-effort-stepper.ts +56 -0
- package/src/chat/ui/composer/use-notice.ts +2 -2
- package/src/chat/ui/diff/EditorEditDiff.tsx +9 -13
- package/src/chat/ui/diff/LegacyEditDiff.tsx +12 -17
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +13 -21
- package/src/chat/ui/diff/use-review-stack.ts +4 -4
- package/src/chat/ui/diff-theme.ts +3 -3
- package/src/chat/ui/file-links.ts +2 -2
- package/src/chat/ui/intelligent-ui/UiReply.tsx +2 -0
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +3 -3
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +5 -6
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +2 -1
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +2 -1
- package/src/chat/ui/intelligent-ui/use-truncated.ts +16 -19
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +3 -3
- package/src/chat/ui/rows/ForksRow.tsx +3 -5
- package/src/chat/ui/rows/GeneratedImageRow.tsx +2 -1
- package/src/chat/ui/rows/MessageRows.tsx +12 -21
- package/src/chat/ui/rows/SubagentRow.tsx +3 -3
- package/src/chat/ui/rows/TaskRow.tsx +1 -1
- package/src/chat/ui/rows/WorkRows.tsx +20 -7
- package/src/chat/ui/useToggleSet.ts +15 -18
- package/src/chat/ui/visual-theme.ts +2 -3
- package/src/host-hooks.ts +113 -0
- package/src/lazy.tsx +2 -0
- package/src/locales/en/agent-chat.ts +135 -108
- package/src/locales/en/agent-prompts.ts +7 -7
- package/src/locales/en/agent-providers.ts +35 -34
- package/src/locales/en/agent-usage.ts +26 -20
- package/src/locales/nl/agent-chat.ts +127 -100
- package/src/locales/nl/agent-prompts.ts +5 -5
- package/src/locales/nl/agent-providers.ts +33 -32
- package/src/locales/nl/agent-usage.ts +25 -20
- package/src/prompts/ui/ApprovalBody.tsx +3 -1
- package/src/providers/CliDetail.tsx +2 -1
- package/src/providers/InstallPage.tsx +2 -4
- package/src/providers/ProviderList.tsx +12 -2
- package/src/providers/ProvidersPane.tsx +45 -15
- package/src/providers/pane-target.ts +20 -4
- package/src/state/chats.ts +7 -2
- package/src/usage/LimitsList.tsx +12 -13
- package/src/usage/UsageBreakdown.tsx +17 -6
- package/src/usage/UsageLimits.tsx +2 -1
- package/src/usage/UsagePage.tsx +3 -3
- package/src/usage/index.ts +1 -1
- package/src/usage/limit-groups.ts +12 -0
- package/src/usage/limits.ts +2 -2
- package/src/usage/money.ts +1 -2
|
@@ -13,61 +13,80 @@ import { hasFileChanges, isFileChange, shellCommand } from './tools.js';
|
|
|
13
13
|
*/
|
|
14
14
|
const BLOCK_KINDS = new Set(['assistant', 'report', 'thinking', 'changed-files', 'compaction', 'app-card', 'visual']);
|
|
15
15
|
export function isBlock(row) {
|
|
16
|
-
|
|
16
|
+
return BLOCK_KINDS.has(row.kind) || isImageRow(row);
|
|
17
17
|
}
|
|
18
18
|
/*
|
|
19
19
|
* The sentence of a run of shell calls that are all runs of one look: its group's when they share a key,
|
|
20
20
|
* else the look's own; null for any other run, which reads as shell calls.
|
|
21
21
|
*/
|
|
22
22
|
function lookSummary(tools, looks) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
if (look === null) {
|
|
33
|
-
look = run.look;
|
|
34
|
-
group = run.match.group;
|
|
35
|
-
}
|
|
36
|
-
else if (run.match.group?.key !== group?.key) {
|
|
37
|
-
sameKey = false;
|
|
38
|
-
}
|
|
23
|
+
let look = null;
|
|
24
|
+
let group;
|
|
25
|
+
let sameKey = true;
|
|
26
|
+
for (const tool of tools) {
|
|
27
|
+
const command = shellCommand(tool.name, tool.input);
|
|
28
|
+
const run = command === null ? null : matchCommandLook(command, looks);
|
|
29
|
+
if (run === null || look !== null && run.look !== look) {
|
|
30
|
+
return null;
|
|
39
31
|
}
|
|
40
32
|
if (look === null) {
|
|
41
|
-
|
|
33
|
+
look = run.look;
|
|
34
|
+
group = run.match.group;
|
|
35
|
+
} else if (run.match.group?.key !== group?.key) {
|
|
36
|
+
sameKey = false;
|
|
42
37
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
}
|
|
39
|
+
if (look === null) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (sameKey && group !== undefined) {
|
|
43
|
+
return {
|
|
44
|
+
look,
|
|
45
|
+
summary: group.sentence(tools.length)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return look.sentence === undefined ? null : {
|
|
49
|
+
look,
|
|
50
|
+
summary: look.sentence(tools.length)
|
|
51
|
+
};
|
|
47
52
|
}
|
|
48
53
|
/* "Read 4 files", "Ran 2 commands", "12 tool calls" when the run mixes kinds, or the sentence of a look all its calls are runs of. */
|
|
49
54
|
export function summarizeGroup(tools, looks) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
const looked = lookSummary(tools, looks);
|
|
56
|
+
if (looked !== null) {
|
|
57
|
+
return looked.summary;
|
|
58
|
+
}
|
|
59
|
+
const names = new Set(tools.map(tool => tool.name));
|
|
60
|
+
const only = names.size === 1 ? tools[0].name : null;
|
|
61
|
+
const failed = tools.filter(failedFileChange);
|
|
62
|
+
if (only !== null && toolEntry(only)?.grouped === true && failed.length === 0) {
|
|
63
|
+
return i18next.t(`agent-chat:group.tools.${only}`, {
|
|
64
|
+
count: tools.length
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Counts the files rather than the calls: three edits to one file are one file edited.
|
|
68
|
+
if (tools.every(tool => isFileChange(tool.name)) && failed.length < tools.length) {
|
|
69
|
+
const paths = new Set(tools.filter(tool => !failedFileChange(tool)).map(editedPath));
|
|
70
|
+
return i18next.t('agent-chat:group.edited', {
|
|
71
|
+
count: paths.size
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (only !== null) {
|
|
75
|
+
return i18next.t('agent-chat:group.calls', {
|
|
76
|
+
name: only,
|
|
77
|
+
count: tools.length
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return i18next.t('agent-chat:group.toolCalls', {
|
|
81
|
+
count: tools.length
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/* A file change that failed or was refused changed nothing: it counts as a call, never as an edit. */
|
|
85
|
+
function failedFileChange(tool) {
|
|
86
|
+
return isFileChange(tool.name) && tool.state === 'error';
|
|
68
87
|
}
|
|
69
88
|
function editedPath(tool) {
|
|
70
|
-
|
|
89
|
+
return tool.input?.file_path ?? tool.id;
|
|
71
90
|
}
|
|
72
91
|
/*
|
|
73
92
|
* What a folded turn did, a sentence per kind of call in the order it first came: "Read 11 files",
|
|
@@ -75,272 +94,374 @@ function editedPath(tool) {
|
|
|
75
94
|
* their own are counted together at the end.
|
|
76
95
|
*/
|
|
77
96
|
export function summarizeTurn(tools, looks) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const edited = new Set();
|
|
90
|
-
let other = 0;
|
|
91
|
-
for (const tool of tools) {
|
|
92
|
-
const command = shellCommand(tool.name, tool.input);
|
|
93
|
-
const run = command === null ? null : matchCommandLook(command, looks);
|
|
94
|
-
const group = run?.match.group;
|
|
95
|
-
if (run !== null && group !== undefined) {
|
|
96
|
-
// Keyed by the program too, so two looks with a key in common keep their own sentences.
|
|
97
|
-
add(`look:${run.look.program}:${group.key}`, group.sentence);
|
|
98
|
-
}
|
|
99
|
-
else if (isFileChange(tool.name)) {
|
|
100
|
-
edited.add(editedPath(tool));
|
|
101
|
-
add('agent-chat:group.edited', (count) => i18next.t('agent-chat:group.edited', { count }), edited.size);
|
|
102
|
-
}
|
|
103
|
-
else if (toolEntry(tool.name)?.grouped === true) {
|
|
104
|
-
const key = `agent-chat:group.tools.${tool.name}`;
|
|
105
|
-
add(key, (count) => i18next.t(key, { count }));
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
other += 1;
|
|
109
|
-
}
|
|
97
|
+
// Keyed by the sentence, so its place is where the first call of that kind came.
|
|
98
|
+
const counts = new Map();
|
|
99
|
+
const add = (key, say, total) => {
|
|
100
|
+
const known = counts.get(key);
|
|
101
|
+
if (known === undefined) {
|
|
102
|
+
counts.set(key, {
|
|
103
|
+
count: total ?? 1,
|
|
104
|
+
say
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
known.count = total ?? known.count + 1;
|
|
110
108
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
};
|
|
110
|
+
const edited = new Set();
|
|
111
|
+
let other = 0;
|
|
112
|
+
for (const tool of tools) {
|
|
113
|
+
const command = shellCommand(tool.name, tool.input);
|
|
114
|
+
const run = command === null ? null : matchCommandLook(command, looks);
|
|
115
|
+
const group = run?.match.group;
|
|
116
|
+
if (run !== null && group !== undefined) {
|
|
117
|
+
// Keyed by the program too, so two looks with a key in common keep their own sentences.
|
|
118
|
+
add(`look:${run.look.program}:${group.key}`, group.sentence);
|
|
119
|
+
} else if (failedFileChange(tool)) {
|
|
120
|
+
other += 1;
|
|
121
|
+
} else if (isFileChange(tool.name)) {
|
|
122
|
+
edited.add(editedPath(tool));
|
|
123
|
+
add('agent-chat:group.edited', count => i18next.t('agent-chat:group.edited', {
|
|
124
|
+
count
|
|
125
|
+
}), edited.size);
|
|
126
|
+
} else if (toolEntry(tool.name)?.grouped === true) {
|
|
127
|
+
const key = `agent-chat:group.tools.${tool.name}`;
|
|
128
|
+
add(key, count => i18next.t(key, {
|
|
129
|
+
count
|
|
130
|
+
}));
|
|
131
|
+
} else {
|
|
132
|
+
other += 1;
|
|
114
133
|
}
|
|
115
|
-
|
|
134
|
+
}
|
|
135
|
+
const parts = [...counts.values()].map(({
|
|
136
|
+
count,
|
|
137
|
+
say
|
|
138
|
+
}) => say(count));
|
|
139
|
+
if (other > 0) {
|
|
140
|
+
parts.push(i18next.t(parts.length === 0 ? 'agent-chat:group.toolCalls' : 'agent-chat:group.otherCalls', {
|
|
141
|
+
count: other
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
return parts;
|
|
116
145
|
}
|
|
117
146
|
/*
|
|
118
147
|
* What a turn nobody asked for is about. The CLI wakes the agent when a background subagent settles
|
|
119
148
|
* and hands over its summary; without one all that is known is that the agent went on by itself.
|
|
120
149
|
*/
|
|
121
150
|
export function agentTurnLabel(turn) {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
151
|
+
const about = what => turn.label ? i18next.t('agent-chat:turn.about', {
|
|
152
|
+
what,
|
|
153
|
+
label: turn.label
|
|
154
|
+
}) : what;
|
|
155
|
+
// A turn the machine opened with the results of tasks this chat gave; the label is their titles.
|
|
156
|
+
if (turn.taskIds !== undefined && turn.taskIds.length > 0) {
|
|
157
|
+
return about(i18next.t('agent-chat:turn.wokenByTask', {
|
|
158
|
+
count: turn.taskIds.length
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
// A turn the machine opened over a message another node sent; the label names who sent it.
|
|
162
|
+
if (turn.messageFrom !== undefined && turn.messageFrom.length > 0) {
|
|
163
|
+
return about(i18next.t('agent-chat:turn.wokenByMessage', {
|
|
164
|
+
count: turn.messageFrom.length
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
return turn.label ? i18next.t('agent-chat:turn.subagentFinished', {
|
|
168
|
+
label: turn.label
|
|
169
|
+
}) : i18next.t('agent-chat:turn.continued');
|
|
132
170
|
}
|
|
133
171
|
/* The items of the turn tell a turn a person stopped from one the machine ended after a restart. */
|
|
134
172
|
export function turnLabel(turn, items = []) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
173
|
+
const duration = formatElapsedShort((turn.endedAt ?? turn.createdAt) - turn.createdAt);
|
|
174
|
+
switch (turn.state) {
|
|
175
|
+
case 'aborted':
|
|
176
|
+
return abortedByMachine(turn, items) ? i18next.t('agent-chat:turn.stopped', {
|
|
177
|
+
duration
|
|
178
|
+
}) : i18next.t('agent-chat:turn.youStopped', {
|
|
179
|
+
duration
|
|
180
|
+
});
|
|
181
|
+
case 'error':
|
|
182
|
+
return i18next.t('agent-chat:turn.failed', {
|
|
183
|
+
duration
|
|
184
|
+
});
|
|
185
|
+
default:
|
|
186
|
+
return i18next.t('agent-chat:turn.worked', {
|
|
187
|
+
duration
|
|
188
|
+
});
|
|
189
|
+
}
|
|
144
190
|
}
|
|
145
191
|
// A subagent's own work, and an agent it opened, belong to its row, not to the thread; an old record has no row for it.
|
|
146
192
|
function parentOf(item) {
|
|
147
|
-
|
|
193
|
+
return item.kind === 'tool' || item.kind === 'assistant' || item.kind === 'subagent' ? item.parentToolUseId ?? null : null;
|
|
148
194
|
}
|
|
149
195
|
// Deeper than the CLI forwards any call; it also ends a chain of rows that would name each other.
|
|
150
196
|
const MAX_NESTING = 8;
|
|
151
197
|
function branchOf(item, options, children, depth = 0) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
expanded: options.expandedSubagents.has(item.id)
|
|
161
|
-
};
|
|
198
|
+
const own = children.get(item.toolUseId) ?? [];
|
|
199
|
+
return {
|
|
200
|
+
id: item.id,
|
|
201
|
+
item,
|
|
202
|
+
children: own.filter(child => child.kind !== 'subagent'),
|
|
203
|
+
nested: depth >= MAX_NESTING ? [] : own.filter(child => child.kind === 'subagent').map(child => branchOf(child, options, children, depth + 1)),
|
|
204
|
+
expanded: options.expandedSubagents.has(item.id)
|
|
205
|
+
};
|
|
162
206
|
}
|
|
163
207
|
/* The branch a subagent's row sits in, its own or one it hangs under, anywhere in the thread's rows. */
|
|
164
208
|
export function findSubagentBranch(rows, toolUseId) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
return null;
|
|
176
|
-
};
|
|
177
|
-
for (let index = 0; index < rows.length; index++) {
|
|
178
|
-
const row = rows[index];
|
|
179
|
-
const branch = row.kind === 'subagent' ? search(row) : null;
|
|
180
|
-
if (branch) {
|
|
181
|
-
return { index, branch };
|
|
182
|
-
}
|
|
209
|
+
const search = branch => {
|
|
210
|
+
if (branch.item.toolUseId === toolUseId) {
|
|
211
|
+
return branch;
|
|
212
|
+
}
|
|
213
|
+
for (const child of branch.nested) {
|
|
214
|
+
const found = search(child);
|
|
215
|
+
if (found) {
|
|
216
|
+
return found;
|
|
217
|
+
}
|
|
183
218
|
}
|
|
184
219
|
return null;
|
|
220
|
+
};
|
|
221
|
+
for (let index = 0; index < rows.length; index++) {
|
|
222
|
+
const row = rows[index];
|
|
223
|
+
const branch = row.kind === 'subagent' ? search(row) : null;
|
|
224
|
+
if (branch) {
|
|
225
|
+
return {
|
|
226
|
+
index,
|
|
227
|
+
branch
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
185
232
|
}
|
|
186
233
|
/* What each subagent did, keyed by the call that spawned it, in the order it happened. */
|
|
187
234
|
function groupChildren(items) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
children.set(parent, [item]);
|
|
200
|
-
}
|
|
235
|
+
const children = new Map();
|
|
236
|
+
for (const item of items) {
|
|
237
|
+
const parent = parentOf(item);
|
|
238
|
+
if (parent === null) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
const bucket = children.get(parent);
|
|
242
|
+
if (bucket) {
|
|
243
|
+
bucket.push(item);
|
|
244
|
+
} else {
|
|
245
|
+
children.set(parent, [item]);
|
|
201
246
|
}
|
|
202
|
-
|
|
247
|
+
}
|
|
248
|
+
return children;
|
|
203
249
|
}
|
|
204
250
|
function flushTools(buffer, rows, options) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
251
|
+
if (buffer.length === 0) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const tools = buffer.splice(0);
|
|
255
|
+
const live = tools.filter(tool => tool.state === 'running');
|
|
256
|
+
const settled = tools.filter(tool => tool.state !== 'running');
|
|
257
|
+
if (settled.length === 1) {
|
|
258
|
+
rows.push({
|
|
259
|
+
kind: 'work',
|
|
260
|
+
id: settled[0].id,
|
|
261
|
+
tool: settled[0]
|
|
262
|
+
});
|
|
263
|
+
} else if (settled.length > 1) {
|
|
264
|
+
const id = `group-${settled[0].id}`;
|
|
265
|
+
const expanded = options.expandedGroups.has(id);
|
|
266
|
+
const looked = lookSummary(settled, options.commandLooks);
|
|
267
|
+
rows.push(looked === null ? {
|
|
268
|
+
kind: 'work-group',
|
|
269
|
+
id,
|
|
270
|
+
tools: settled,
|
|
271
|
+
summary: summarizeGroup(settled),
|
|
272
|
+
expanded
|
|
273
|
+
} : {
|
|
274
|
+
kind: 'work-group',
|
|
275
|
+
id,
|
|
276
|
+
tools: settled,
|
|
277
|
+
summary: looked.summary,
|
|
278
|
+
expanded,
|
|
279
|
+
look: looked.look
|
|
280
|
+
});
|
|
281
|
+
if (expanded) {
|
|
282
|
+
for (const tool of settled) {
|
|
283
|
+
rows.push({
|
|
284
|
+
kind: 'work',
|
|
285
|
+
id: tool.id,
|
|
286
|
+
tool
|
|
287
|
+
});
|
|
288
|
+
}
|
|
229
289
|
}
|
|
290
|
+
}
|
|
291
|
+
for (const tool of live) {
|
|
292
|
+
rows.push({
|
|
293
|
+
kind: 'work-live',
|
|
294
|
+
id: tool.id,
|
|
295
|
+
tool
|
|
296
|
+
});
|
|
297
|
+
}
|
|
230
298
|
}
|
|
231
299
|
function isImageRow(row) {
|
|
232
|
-
|
|
300
|
+
return (row.kind === 'work' || row.kind === 'work-live') && isImageGeneration(row.tool);
|
|
233
301
|
}
|
|
234
302
|
/* Rows for a run of items, in order, with tool runs folded and a subagent's work under its own row. */
|
|
235
303
|
function rowsForItems(items, options, children) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
break;
|
|
275
|
-
case 'thinking':
|
|
276
|
-
rows.push({ kind: 'thinking', id: item.id, item });
|
|
277
|
-
break;
|
|
278
|
-
case 'approval':
|
|
279
|
-
// A pending one sits on the composer; only its outcome belongs in the transcript.
|
|
280
|
-
if (item.decision !== 'pending') {
|
|
281
|
-
rows.push({ kind: 'approval', id: item.id, item });
|
|
282
|
-
}
|
|
283
|
-
break;
|
|
284
|
-
case 'question':
|
|
285
|
-
if (item.state !== 'pending') {
|
|
286
|
-
rows.push({ kind: 'question', id: item.id, item });
|
|
287
|
-
}
|
|
288
|
-
break;
|
|
289
|
-
case 'note':
|
|
290
|
-
rows.push({ kind: 'note', id: item.id, level: item.level, text: item.text, ...(item.from === undefined ? {} : { from: item.from }) });
|
|
291
|
-
break;
|
|
292
|
-
case 'compaction':
|
|
293
|
-
rows.push({ kind: 'compaction', id: item.id, preTokens: item.preTokens });
|
|
294
|
-
break;
|
|
295
|
-
case 'turn':
|
|
296
|
-
break;
|
|
297
|
-
}
|
|
304
|
+
const rows = [];
|
|
305
|
+
const tools = [];
|
|
306
|
+
for (const item of items) {
|
|
307
|
+
if (parentOf(item) !== null) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const report = handbackReportOf(item);
|
|
311
|
+
if (item.kind === 'tool' && item.workflow !== undefined) {
|
|
312
|
+
flushTools(tools, rows, options);
|
|
313
|
+
rows.push({
|
|
314
|
+
kind: 'workflow',
|
|
315
|
+
id: item.id,
|
|
316
|
+
tool: item,
|
|
317
|
+
workflow: item.workflow
|
|
318
|
+
});
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (item.kind === 'tool' && isImageGeneration(item)) {
|
|
322
|
+
flushTools(tools, rows, options);
|
|
323
|
+
rows.push({
|
|
324
|
+
kind: item.state === 'running' ? 'work-live' : 'work',
|
|
325
|
+
id: item.id,
|
|
326
|
+
tool: item
|
|
327
|
+
});
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (item.kind === 'tool' && report === null) {
|
|
331
|
+
tools.push(item);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (report !== null) {
|
|
335
|
+
flushTools(tools, rows, options);
|
|
336
|
+
rows.push({
|
|
337
|
+
kind: 'report',
|
|
338
|
+
id: item.id,
|
|
339
|
+
text: report
|
|
340
|
+
});
|
|
341
|
+
continue;
|
|
298
342
|
}
|
|
299
343
|
flushTools(tools, rows, options);
|
|
300
|
-
|
|
344
|
+
switch (item.kind) {
|
|
345
|
+
case 'subagent':
|
|
346
|
+
rows.push({
|
|
347
|
+
kind: 'subagent',
|
|
348
|
+
...branchOf(item, options, children)
|
|
349
|
+
});
|
|
350
|
+
break;
|
|
351
|
+
case 'user':
|
|
352
|
+
rows.push({
|
|
353
|
+
kind: 'user',
|
|
354
|
+
id: item.id,
|
|
355
|
+
item
|
|
356
|
+
});
|
|
357
|
+
break;
|
|
358
|
+
case 'assistant':
|
|
359
|
+
if (item.text !== '' || item.streaming) {
|
|
360
|
+
rows.push({
|
|
361
|
+
kind: 'assistant',
|
|
362
|
+
id: item.id,
|
|
363
|
+
item
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
break;
|
|
367
|
+
case 'thinking':
|
|
368
|
+
rows.push({
|
|
369
|
+
kind: 'thinking',
|
|
370
|
+
id: item.id,
|
|
371
|
+
item
|
|
372
|
+
});
|
|
373
|
+
break;
|
|
374
|
+
case 'approval':
|
|
375
|
+
// A pending one sits on the composer; only its outcome belongs in the transcript.
|
|
376
|
+
if (item.decision !== 'pending') {
|
|
377
|
+
rows.push({
|
|
378
|
+
kind: 'approval',
|
|
379
|
+
id: item.id,
|
|
380
|
+
item
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
break;
|
|
384
|
+
case 'question':
|
|
385
|
+
if (item.state !== 'pending') {
|
|
386
|
+
rows.push({
|
|
387
|
+
kind: 'question',
|
|
388
|
+
id: item.id,
|
|
389
|
+
item
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
break;
|
|
393
|
+
case 'note':
|
|
394
|
+
rows.push({
|
|
395
|
+
kind: 'note',
|
|
396
|
+
id: item.id,
|
|
397
|
+
level: item.level,
|
|
398
|
+
text: item.text,
|
|
399
|
+
...(item.from === undefined ? {} : {
|
|
400
|
+
from: item.from
|
|
401
|
+
})
|
|
402
|
+
});
|
|
403
|
+
break;
|
|
404
|
+
case 'compaction':
|
|
405
|
+
rows.push({
|
|
406
|
+
kind: 'compaction',
|
|
407
|
+
id: item.id,
|
|
408
|
+
preTokens: item.preTokens
|
|
409
|
+
});
|
|
410
|
+
break;
|
|
411
|
+
case 'turn':
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
flushTools(tools, rows, options);
|
|
416
|
+
return rows;
|
|
301
417
|
}
|
|
302
418
|
/* The card of a settled turn: the checkpoint diff when the host took one, else what the tool calls carry. */
|
|
303
419
|
function changedFilesRow(turn, items) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
420
|
+
const edits = items.filter(item => item.kind === 'tool' && item.state === 'done' && isFileChange(item.name) && hasFileChanges(item));
|
|
421
|
+
const diff = turn.checkpointDiff ?? null;
|
|
422
|
+
const empty = diff === null ? edits.length === 0 : diff.files.length === 0;
|
|
423
|
+
if (empty) {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
const moved = turn.checkpointHead !== undefined && turn.checkpointHeadAfter !== undefined && turn.checkpointHead !== turn.checkpointHeadAfter;
|
|
427
|
+
return {
|
|
428
|
+
kind: 'changed-files',
|
|
429
|
+
id: `files-${turn.id}`,
|
|
430
|
+
turnId: turn.id,
|
|
431
|
+
tools: edits,
|
|
432
|
+
diff,
|
|
433
|
+
checkpoint: turn.checkpoint !== undefined,
|
|
434
|
+
...(moved ? {
|
|
435
|
+
baseMoved: {
|
|
436
|
+
from: turn.checkpointHead,
|
|
437
|
+
to: turn.checkpointHeadAfter
|
|
438
|
+
}
|
|
439
|
+
} : {})
|
|
440
|
+
};
|
|
320
441
|
}
|
|
321
442
|
/*
|
|
322
443
|
* The closing answer of a settled turn. A turn may end on a call the person turned down, as an agent that stops
|
|
323
444
|
* there does: the answer before it closes the turn, and the refused call and its denial stay in sight with it.
|
|
324
445
|
*/
|
|
325
446
|
function lastAssistantRow(rows) {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
if (row.kind === 'approval' && row.item.decision === 'deny' && row.item.toolUseId !== null) {
|
|
333
|
-
refused.add(row.item.toolUseId);
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
if (row.kind === 'work' && refused.has(row.tool.toolUseId)) {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
if (row.kind !== 'note' && !isImageRow(row)) {
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
447
|
+
const refused = new Set();
|
|
448
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
449
|
+
const row = rows[i];
|
|
450
|
+
if (row.kind === 'assistant') {
|
|
451
|
+
return row;
|
|
342
452
|
}
|
|
343
|
-
|
|
453
|
+
if (row.kind === 'approval' && row.item.decision === 'deny' && row.item.toolUseId !== null) {
|
|
454
|
+
refused.add(row.item.toolUseId);
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (row.kind === 'work' && refused.has(row.tool.toolUseId)) {
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
if (row.kind !== 'note' && !isImageRow(row)) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return null;
|
|
344
465
|
}
|
|
345
466
|
/*
|
|
346
467
|
* The rows of a running turn with its latest thought over the call it led to, so what the model
|
|
@@ -349,106 +470,129 @@ function lastAssistantRow(rows) {
|
|
|
349
470
|
* came after the call.
|
|
350
471
|
*/
|
|
351
472
|
function withLiveThought(rows, items) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
473
|
+
const thoughtAt = items.findLastIndex(item => item.kind === 'thinking' && parentOf(item) === null);
|
|
474
|
+
const thought = items[thoughtAt];
|
|
475
|
+
if (thought?.kind !== 'thinking' || thought.text === '') {
|
|
476
|
+
return rows;
|
|
477
|
+
}
|
|
478
|
+
const after = new Set(items.slice(thoughtAt + 1).map(item => item.id));
|
|
479
|
+
const index = rows.findIndex(row => row.kind === 'work-live' && after.has(row.tool.id));
|
|
480
|
+
const call = rows[index];
|
|
481
|
+
const shownAt = rows.findIndex(row => row.kind === 'thinking' && row.id === thought.id);
|
|
482
|
+
const inSight = shownAt !== -1 && shownAt < index && !rows.slice(shownAt + 1, index).some(row => row.kind === 'work');
|
|
483
|
+
if (call?.kind !== 'work-live' || inSight) {
|
|
484
|
+
return rows;
|
|
485
|
+
}
|
|
486
|
+
return [...rows.slice(0, index), {
|
|
487
|
+
kind: 'live-thought',
|
|
488
|
+
id: `live-thought-${thought.id}`,
|
|
489
|
+
item: thought,
|
|
490
|
+
at: call.tool.createdAt
|
|
491
|
+
}, ...rows.slice(index)];
|
|
366
492
|
}
|
|
367
493
|
/* Items grouped by turn, in order of first appearance; runs of turnless items keep their place. */
|
|
368
494
|
function chunksByTurn(items) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if (!bucket) {
|
|
384
|
-
bucket = [];
|
|
385
|
-
byTurn.set(item.turnId, bucket);
|
|
386
|
-
chunks.push({ turnId: item.turnId, items: bucket });
|
|
387
|
-
}
|
|
388
|
-
bucket.push(item);
|
|
495
|
+
const chunks = [];
|
|
496
|
+
const byTurn = new Map();
|
|
497
|
+
for (const item of items) {
|
|
498
|
+
if (item.turnId === null) {
|
|
499
|
+
const last = chunks[chunks.length - 1];
|
|
500
|
+
if (last && last.turnId === null) {
|
|
501
|
+
last.items.push(item);
|
|
502
|
+
} else {
|
|
503
|
+
chunks.push({
|
|
504
|
+
turnId: null,
|
|
505
|
+
items: [item]
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
continue;
|
|
389
509
|
}
|
|
390
|
-
|
|
510
|
+
let bucket = byTurn.get(item.turnId);
|
|
511
|
+
if (!bucket) {
|
|
512
|
+
bucket = [];
|
|
513
|
+
byTurn.set(item.turnId, bucket);
|
|
514
|
+
chunks.push({
|
|
515
|
+
turnId: item.turnId,
|
|
516
|
+
items: bucket
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
bucket.push(item);
|
|
520
|
+
}
|
|
521
|
+
return chunks;
|
|
391
522
|
}
|
|
392
523
|
/* The whole thread as rows; items without a turn (older records) are shown as they are. */
|
|
393
524
|
export function deriveTimelineRows(items, options) {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
const turnId = chunk.turnId;
|
|
402
|
-
const turn = chunk.items.find((item) => item.kind === 'turn');
|
|
403
|
-
const conversation = chunk.items.filter((item) => item.kind !== 'turn');
|
|
404
|
-
if (turn?.origin === 'agent') {
|
|
405
|
-
// The agent started this one itself, so there is no message of the person to show above it.
|
|
406
|
-
rows.push({ kind: 'turn-start', id: `start-${turnId}`, turn, label: agentTurnLabel(turn) });
|
|
407
|
-
}
|
|
408
|
-
const active = turnId === options.activeTurnId || turn?.state === 'running';
|
|
409
|
-
if (!turn || active) {
|
|
410
|
-
const live = rowsForItems(conversation, options, children);
|
|
411
|
-
rows.push(...(active ? withLiveThought(live, conversation) : live));
|
|
412
|
-
if (active) {
|
|
413
|
-
rows.push({ kind: 'working', id: `working-${turnId}`, startedAt: turn?.createdAt ?? Date.now() });
|
|
414
|
-
}
|
|
415
|
-
continue;
|
|
416
|
-
}
|
|
417
|
-
// Steering splits the conversation inside a turn. Earlier responses keep their place above it.
|
|
418
|
-
const lastUser = conversation.findLastIndex((item) => item.kind === 'user');
|
|
419
|
-
rows.push(...rowsForItems(conversation.slice(0, lastUser + 1), options, children));
|
|
420
|
-
const rest = conversation.slice(lastUser + 1);
|
|
421
|
-
const work = rowsForItems(rest, options, children);
|
|
422
|
-
// The closing answer stays visible; the work after the last input folds behind the label.
|
|
423
|
-
const finalAssistant = lastAssistantRow(work);
|
|
424
|
-
const before = finalAssistant ? work.slice(0, work.indexOf(finalAssistant)) : work;
|
|
425
|
-
// Images remain a turn's visible result, and a workflow or a background subagent may still be running after its turn ends.
|
|
426
|
-
const standing = before.filter((row) => isImageRow(row) || (row.kind === 'workflow' && row.tool.state === 'running') || (row.kind === 'subagent' && row.item.status === 'running'));
|
|
427
|
-
const folded = before.filter((row) => !standing.includes(row));
|
|
428
|
-
const expanded = options.expandedTurns.has(turnId);
|
|
429
|
-
if (folded.length > 0) {
|
|
430
|
-
const tools = rest.filter((item) => item.kind === 'tool' && parentOf(item) === null && handbackReportOf(item) === null && !isImageGeneration(item));
|
|
431
|
-
rows.push({
|
|
432
|
-
kind: 'turn-fold',
|
|
433
|
-
id: `fold-${turnId}`,
|
|
434
|
-
turn,
|
|
435
|
-
label: turnLabel(turn, chunk.items),
|
|
436
|
-
work: summarizeTurn(tools, options.commandLooks),
|
|
437
|
-
hiddenCount: folded.length,
|
|
438
|
-
expanded
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
rows.push(...(expanded ? before : standing));
|
|
442
|
-
const changed = changedFilesRow(turn, conversation);
|
|
443
|
-
if (changed) {
|
|
444
|
-
rows.push(changed);
|
|
445
|
-
}
|
|
446
|
-
if (finalAssistant) {
|
|
447
|
-
rows.push(...work.slice(work.indexOf(finalAssistant)));
|
|
448
|
-
}
|
|
449
|
-
if (options.forkedTurns?.has(turnId) === true) {
|
|
450
|
-
rows.push({ kind: 'forks', id: `forks-${turnId}`, turnId });
|
|
451
|
-
}
|
|
525
|
+
const rows = [];
|
|
526
|
+
const children = groupChildren(items);
|
|
527
|
+
for (const chunk of chunksByTurn(items)) {
|
|
528
|
+
if (chunk.turnId === null) {
|
|
529
|
+
rows.push(...rowsForItems(chunk.items, options, children));
|
|
530
|
+
continue;
|
|
452
531
|
}
|
|
453
|
-
|
|
454
|
-
|
|
532
|
+
const turnId = chunk.turnId;
|
|
533
|
+
const turn = chunk.items.find(item => item.kind === 'turn');
|
|
534
|
+
const conversation = chunk.items.filter(item => item.kind !== 'turn');
|
|
535
|
+
if (turn?.origin === 'agent') {
|
|
536
|
+
// The agent started this one itself, so there is no message of the person to show above it.
|
|
537
|
+
rows.push({
|
|
538
|
+
kind: 'turn-start',
|
|
539
|
+
id: `start-${turnId}`,
|
|
540
|
+
turn,
|
|
541
|
+
label: agentTurnLabel(turn)
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
const active = turnId === options.activeTurnId || turn?.state === 'running';
|
|
545
|
+
if (!turn || active) {
|
|
546
|
+
const live = rowsForItems(conversation, options, children);
|
|
547
|
+
rows.push(...(active ? withLiveThought(live, conversation) : live));
|
|
548
|
+
if (active) {
|
|
549
|
+
rows.push({
|
|
550
|
+
kind: 'working',
|
|
551
|
+
id: `working-${turnId}`,
|
|
552
|
+
startedAt: turn?.createdAt ?? Date.now()
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
// Steering splits the conversation inside a turn. Earlier responses keep their place above it.
|
|
558
|
+
const lastUser = conversation.findLastIndex(item => item.kind === 'user');
|
|
559
|
+
rows.push(...rowsForItems(conversation.slice(0, lastUser + 1), options, children));
|
|
560
|
+
const rest = conversation.slice(lastUser + 1);
|
|
561
|
+
const work = rowsForItems(rest, options, children);
|
|
562
|
+
// The closing answer stays visible; the work after the last input folds behind the label.
|
|
563
|
+
const finalAssistant = lastAssistantRow(work);
|
|
564
|
+
const before = finalAssistant ? work.slice(0, work.indexOf(finalAssistant)) : work;
|
|
565
|
+
// Images remain a turn's visible result, and a workflow or a background subagent may still be running after its turn ends.
|
|
566
|
+
const standing = before.filter(row => isImageRow(row) || row.kind === 'workflow' && row.tool.state === 'running' || row.kind === 'subagent' && row.item.status === 'running');
|
|
567
|
+
const folded = before.filter(row => !standing.includes(row));
|
|
568
|
+
const expanded = options.expandedTurns.has(turnId);
|
|
569
|
+
if (folded.length > 0) {
|
|
570
|
+
const tools = rest.filter(item => item.kind === 'tool' && parentOf(item) === null && handbackReportOf(item) === null && !isImageGeneration(item));
|
|
571
|
+
rows.push({
|
|
572
|
+
kind: 'turn-fold',
|
|
573
|
+
id: `fold-${turnId}`,
|
|
574
|
+
turn,
|
|
575
|
+
label: turnLabel(turn, chunk.items),
|
|
576
|
+
work: summarizeTurn(tools, options.commandLooks),
|
|
577
|
+
hiddenCount: folded.length,
|
|
578
|
+
expanded
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
rows.push(...(expanded ? before : standing));
|
|
582
|
+
const changed = changedFilesRow(turn, conversation);
|
|
583
|
+
if (changed) {
|
|
584
|
+
rows.push(changed);
|
|
585
|
+
}
|
|
586
|
+
if (finalAssistant) {
|
|
587
|
+
rows.push(...work.slice(work.indexOf(finalAssistant)));
|
|
588
|
+
}
|
|
589
|
+
if (options.forkedTurns?.has(turnId) === true) {
|
|
590
|
+
rows.push({
|
|
591
|
+
kind: 'forks',
|
|
592
|
+
id: `forks-${turnId}`,
|
|
593
|
+
turnId
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return rows;
|
|
598
|
+
}
|