@adecore/agents-react 0.38.0 → 0.40.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/AgentModelLabel.d.ts +10 -0
- package/dist/agents/AgentModelLabel.js +98 -0
- package/dist/agents/model-name.js +2 -1
- package/dist/agents/provider-paths.d.ts +2 -1
- package/dist/agents/provider-paths.js +7 -3
- package/dist/chat/account-choice.js +57 -28
- package/dist/chat/bookmarks.js +2 -1
- package/dist/chat/drafts.d.ts +0 -1
- package/dist/chat/drafts.js +9 -28
- package/dist/chat/logic/picker-entries.js +2 -8
- package/dist/chat/logic/resume-compaction.js +2 -5
- package/dist/chat/logic/timeline-copy.js +2 -8
- package/dist/chat/logic/timeline.js +9 -10
- package/dist/chat/logic/workflow.js +2 -4
- package/dist/chat/subagent-conversation.js +2 -1
- package/dist/chat/ui/Composer.js +385 -360
- package/dist/chat/ui/GoalDock.js +2 -1
- package/dist/chat/ui/LimitState.js +4 -6
- package/dist/chat/ui/Pickers.d.ts +2 -1
- package/dist/chat/ui/Pickers.js +126 -123
- package/dist/chat/ui/PromptEditor.d.ts +19 -0
- package/dist/chat/ui/PromptEditor.js +115 -0
- package/dist/chat/ui/Timeline.d.ts +2 -1
- package/dist/chat/ui/Timeline.js +13 -1
- package/dist/chat/ui/composer/ComposerControls.d.ts +3 -0
- package/dist/chat/ui/composer/ComposerControls.js +235 -38
- package/dist/chat/ui/composer/key-handler.d.ts +1 -1
- package/dist/chat/ui/composer/key-handler.js +3 -11
- package/dist/chat/ui/composer/prompt-picks.d.ts +4 -0
- package/dist/chat/ui/composer/prompt-picks.js +14 -0
- package/dist/chat/ui/composer/use-composer-menus.d.ts +4 -49
- package/dist/chat/ui/composer/use-composer-menus.js +65 -519
- package/dist/chat/ui/composer/use-continuable-turn.js +0 -1
- package/dist/chat/ui/composer/use-notice.js +2 -1
- package/dist/chat/ui/composer/use-prompt-history.js +1 -1
- package/dist/chat/ui/composer/use-prompt-menus.d.ts +73 -0
- package/dist/chat/ui/composer/use-prompt-menus.js +499 -0
- package/dist/chat/ui/composer/use-take-back.js +2 -2
- package/dist/chat/ui/diff/ApprovalReview.js +1 -1
- package/dist/chat/ui/highlighter.d.ts +0 -6
- package/dist/chat/ui/highlighter.js +0 -12
- package/dist/chat/ui/icons.d.ts +0 -1
- package/dist/chat/ui/icons.js +0 -3
- package/dist/chat/ui/index.d.ts +3 -0
- package/dist/chat/ui/index.js +2 -0
- package/dist/chat/ui/rows/MessageRows.js +2 -5
- package/dist/chat/ui/rows/row-rhythm.js +2 -5
- package/dist/chat/ui/theme-watch.js +3 -7
- package/dist/host-hooks.d.ts +2 -1
- package/dist/host-hooks.js +6 -0
- package/dist/host.d.ts +17 -1
- package/dist/index.d.ts +1 -1
- package/dist/locales/en/agent-chat.d.ts +1 -0
- package/dist/locales/en/agent-chat.js +1 -0
- package/dist/locales/nl/agent-chat.d.ts +1 -0
- package/dist/locales/nl/agent-chat.js +1 -0
- package/dist/prompts/logic/usePromptSession.js +2 -1
- package/dist/providers/AddAccountForm.js +2 -1
- package/dist/providers/CliDetail.js +104 -130
- package/dist/providers/account-actions.js +6 -9
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/state/chats.d.ts +1 -0
- package/dist/state/chats.js +6 -11
- package/dist/state/provider-accounts.d.ts +1 -1
- package/dist/state/provider-accounts.js +2 -8
- package/dist/theme.css +3 -0
- package/dist/transport.d.ts +2 -0
- package/dist/transport.js +14 -0
- package/dist/usage/ChartLogo.d.ts +5 -0
- package/dist/usage/ChartLogo.js +48 -0
- package/dist/usage/LimitsList.js +6 -16
- package/dist/usage/UsageBreakdown.js +17 -40
- package/dist/usage/UsageLimits.js +183 -211
- package/dist/usage/UsagePage.js +3 -17
- package/dist/usage/UsageSummary.js +4 -9
- package/dist/usage/limits.js +16 -31
- package/dist/usage/summary.d.ts +1 -1
- package/dist/usage/summary.js +3 -5
- package/package.json +26 -26
- package/src/agents/AccountDot.tsx +1 -0
- package/src/agents/AgentIcon.tsx +2 -0
- package/src/agents/AgentModelLabel.tsx +38 -0
- package/src/agents/ModelOptionControl.tsx +8 -0
- package/src/agents/account-limits.ts +3 -0
- package/src/agents/accounts.ts +12 -0
- package/src/agents/model-name.ts +13 -1
- package/src/agents/model-options.ts +2 -0
- package/src/agents/model-requirement.ts +4 -0
- package/src/agents/option-words.ts +2 -0
- package/src/agents/provider-paths.ts +8 -3
- package/src/agents/status-look.ts +1 -0
- package/src/chat/account-choice.ts +25 -19
- package/src/chat/actions.ts +5 -0
- package/src/chat/attachments.ts +8 -0
- package/src/chat/bookmarks.ts +13 -1
- package/src/chat/chat-client.ts +63 -0
- package/src/chat/chat-references.ts +3 -0
- package/src/chat/composer-effort.ts +5 -0
- package/src/chat/composer-send.ts +4 -0
- package/src/chat/drafts.ts +21 -17
- package/src/chat/forks.ts +4 -0
- package/src/chat/guards.ts +2 -0
- package/src/chat/index.ts +7 -0
- package/src/chat/logic/ansi.ts +47 -0
- package/src/chat/logic/bookmarks.ts +7 -0
- package/src/chat/logic/command-looks.ts +29 -0
- package/src/chat/logic/context-usage.ts +3 -0
- package/src/chat/logic/continue-after-stop.ts +5 -0
- package/src/chat/logic/effort.ts +4 -0
- package/src/chat/logic/embedded-code.ts +63 -0
- package/src/chat/logic/fork.ts +22 -0
- package/src/chat/logic/generated-image.ts +7 -0
- package/src/chat/logic/goal.ts +7 -0
- package/src/chat/logic/handback.ts +2 -0
- package/src/chat/logic/limit.ts +8 -0
- package/src/chat/logic/picker-entries.ts +16 -4
- package/src/chat/logic/resume-compaction.ts +22 -5
- package/src/chat/logic/scrubber.ts +38 -5
- package/src/chat/logic/thought.ts +5 -0
- package/src/chat/logic/thread-cards.ts +6 -0
- package/src/chat/logic/timeline-copy.ts +4 -9
- package/src/chat/logic/timeline-target.ts +15 -0
- package/src/chat/logic/timeline.ts +113 -10
- package/src/chat/logic/tools.ts +30 -0
- package/src/chat/logic/visual-height.ts +5 -0
- package/src/chat/logic/visual-viewport.ts +1 -0
- package/src/chat/logic/welcome.ts +3 -0
- package/src/chat/logic/workflow.ts +26 -7
- package/src/chat/mentions.ts +56 -0
- package/src/chat/pending-prompts.ts +16 -0
- package/src/chat/persisted-json.ts +15 -0
- package/src/chat/preferences.ts +16 -0
- package/src/chat/prompt-hold.ts +2 -0
- package/src/chat/quote.ts +7 -0
- package/src/chat/reading-width.ts +1 -0
- package/src/chat/recent-messages.ts +5 -0
- package/src/chat/resume-compaction-dismissals.ts +1 -0
- package/src/chat/stash.ts +12 -0
- package/src/chat/subagent-conversation.ts +24 -4
- package/src/chat/subagent-list.ts +14 -0
- package/src/chat/subagent-support.ts +1 -0
- package/src/chat/subagent-view.ts +9 -0
- package/src/chat/thumbnails.ts +14 -0
- package/src/chat/timeline-flash.ts +2 -0
- package/src/chat/timeline-scroll.ts +23 -0
- package/src/chat/ui/AccountPill.tsx +4 -0
- package/src/chat/ui/AnsiOutput.tsx +1 -0
- package/src/chat/ui/BookmarkMarker.tsx +5 -0
- package/src/chat/ui/BookmarkSubmenu.tsx +1 -0
- package/src/chat/ui/ChatActivity.tsx +13 -0
- package/src/chat/ui/CodeBlock.tsx +8 -0
- package/src/chat/ui/Composer.tsx +27 -3
- package/src/chat/ui/ComposerInput.tsx +22 -0
- package/src/chat/ui/DiffPool.tsx +3 -0
- package/src/chat/ui/EditDiff.tsx +5 -0
- package/src/chat/ui/GoalDock.tsx +13 -1
- package/src/chat/ui/ImageView.tsx +11 -0
- package/src/chat/ui/LimitState.tsx +23 -6
- package/src/chat/ui/Markdown.tsx +41 -0
- package/src/chat/ui/MermaidBlock.tsx +16 -0
- package/src/chat/ui/MessageActions.tsx +8 -0
- package/src/chat/ui/ModelMarks.tsx +1 -0
- package/src/chat/ui/Pickers.tsx +30 -1
- package/src/chat/ui/PromptComposer.tsx +2 -0
- package/src/chat/ui/PromptEditor.tsx +136 -0
- package/src/chat/ui/QuoteButton.tsx +12 -0
- package/src/chat/ui/ReferenceChip.tsx +3 -0
- package/src/chat/ui/ResumeCompactionDock.tsx +1 -0
- package/src/chat/ui/RowTime.tsx +1 -0
- package/src/chat/ui/RunSettings.tsx +38 -0
- package/src/chat/ui/Scrubber.tsx +14 -0
- package/src/chat/ui/SessionLine.tsx +1 -0
- package/src/chat/ui/ShellCommand.tsx +32 -0
- package/src/chat/ui/SubagentControls.tsx +7 -0
- package/src/chat/ui/SubagentInfo.tsx +1 -0
- package/src/chat/ui/SubagentStopButton.tsx +6 -0
- package/src/chat/ui/SubagentTimeline.tsx +10 -0
- package/src/chat/ui/Timeline.tsx +141 -1
- package/src/chat/ui/TimelineMenu.tsx +5 -0
- package/src/chat/ui/UnifiedDiff.tsx +6 -0
- package/src/chat/ui/UploadThumb.tsx +2 -0
- package/src/chat/ui/VisualDialogs.tsx +3 -0
- package/src/chat/ui/VisualFrame.tsx +15 -0
- package/src/chat/ui/Welcome.tsx +1 -0
- package/src/chat/ui/chip-clipboard.ts +20 -0
- package/src/chat/ui/code-lines.ts +14 -0
- package/src/chat/ui/code-theme.ts +1 -0
- package/src/chat/ui/composer/ComposerControls.tsx +70 -15
- package/src/chat/ui/composer/ComposerMenu.tsx +12 -0
- package/src/chat/ui/composer/DraftParts.tsx +2 -0
- package/src/chat/ui/composer/QueuedMessages.tsx +4 -0
- package/src/chat/ui/composer/chip-paste.ts +16 -0
- package/src/chat/ui/composer/chips.ts +6 -0
- package/src/chat/ui/composer/editor.ts +9 -0
- package/src/chat/ui/composer/insert.ts +2 -0
- package/src/chat/ui/composer/key-handler.ts +42 -9
- package/src/chat/ui/composer/keys.ts +23 -0
- package/src/chat/ui/composer/placeholder.ts +6 -0
- package/src/chat/ui/composer/prompt-picks.ts +18 -0
- package/src/chat/ui/composer/surface.ts +5 -0
- package/src/chat/ui/composer/use-chat-controls.ts +13 -0
- package/src/chat/ui/composer/use-compaction-offer.ts +3 -0
- package/src/chat/ui/composer/use-composer-menus.ts +20 -275
- package/src/chat/ui/composer/use-composer-submit.ts +27 -0
- package/src/chat/ui/composer/use-continuable-turn.ts +3 -5
- package/src/chat/ui/composer/use-draft-files.ts +24 -0
- package/src/chat/ui/composer/use-effort-stepper.ts +8 -0
- package/src/chat/ui/composer/use-notice.ts +4 -1
- package/src/chat/ui/composer/use-pending-requests.ts +3 -0
- package/src/chat/ui/composer/use-prompt-history.ts +20 -11
- package/src/chat/ui/composer/use-prompt-hold.ts +3 -0
- package/src/chat/ui/composer/use-prompt-menus.ts +361 -0
- package/src/chat/ui/composer/use-take-back.ts +20 -2
- package/src/chat/ui/diff/ApprovalReview.tsx +18 -15
- package/src/chat/ui/diff/DiffFallbackBoundary.tsx +1 -0
- package/src/chat/ui/diff/DiffOmitted.tsx +1 -0
- package/src/chat/ui/diff/DiffStack.tsx +2 -0
- package/src/chat/ui/diff/DiffUnavailable.tsx +1 -0
- package/src/chat/ui/diff/EditorEditDiff.tsx +13 -0
- package/src/chat/ui/diff/EditorPatchDiff.tsx +4 -0
- package/src/chat/ui/diff/LegacyDiffPool.tsx +2 -0
- package/src/chat/ui/diff/LegacyEditDiff.tsx +2 -0
- package/src/chat/ui/diff/LegacyUnifiedDiff.tsx +8 -0
- package/src/chat/ui/diff/RawDiff.tsx +1 -0
- package/src/chat/ui/diff/ReviewDiff.tsx +1 -0
- package/src/chat/ui/diff/diff-source-budget.ts +8 -0
- package/src/chat/ui/diff/editor-edit-model.ts +4 -0
- package/src/chat/ui/diff/review-plan.ts +9 -0
- package/src/chat/ui/diff/review-settle.ts +1 -0
- package/src/chat/ui/diff/syntax-runtime.ts +52 -0
- package/src/chat/ui/diff/use-review-stack.ts +9 -0
- package/src/chat/ui/diff-syntax.ts +12 -0
- package/src/chat/ui/diff-theme.ts +2 -0
- package/src/chat/ui/file-links.ts +2 -0
- package/src/chat/ui/find-reveal.ts +1 -0
- package/src/chat/ui/full-diff.ts +1 -0
- package/src/chat/ui/highlighter.ts +29 -15
- package/src/chat/ui/icons.tsx +2 -4
- package/src/chat/ui/index.ts +7 -0
- package/src/chat/ui/intelligent-ui/UiBlockFrame.tsx +25 -0
- package/src/chat/ui/intelligent-ui/UiFallbackPart.tsx +1 -0
- package/src/chat/ui/intelligent-ui/UiReply.tsx +63 -0
- package/src/chat/ui/intelligent-ui/block-local.ts +5 -0
- package/src/chat/ui/intelligent-ui/chart-data.ts +15 -0
- package/src/chat/ui/intelligent-ui/node-text.ts +17 -0
- package/src/chat/ui/intelligent-ui/renderers/binding.ts +1 -0
- package/src/chat/ui/intelligent-ui/renderers/chart.tsx +31 -0
- package/src/chat/ui/intelligent-ui/renderers/choices.tsx +4 -0
- package/src/chat/ui/intelligent-ui/renderers/content.tsx +18 -0
- package/src/chat/ui/intelligent-ui/renderers/data.tsx +11 -0
- package/src/chat/ui/intelligent-ui/renderers/inputs.tsx +13 -0
- package/src/chat/ui/intelligent-ui/renderers/links.tsx +9 -0
- package/src/chat/ui/intelligent-ui/renderers/map-slots.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-style.ts +15 -0
- package/src/chat/ui/intelligent-ui/renderers/map-view.tsx +48 -0
- package/src/chat/ui/intelligent-ui/renderers/map.tsx +20 -0
- package/src/chat/ui/intelligent-ui/renderers/place-point.ts +3 -0
- package/src/chat/ui/intelligent-ui/renderers/structure.tsx +3 -0
- package/src/chat/ui/intelligent-ui/renderers/text.tsx +8 -0
- package/src/chat/ui/intelligent-ui/reply-parts.ts +5 -0
- package/src/chat/ui/intelligent-ui/table-data.ts +17 -0
- package/src/chat/ui/intelligent-ui/use-in-view.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-later.ts +4 -0
- package/src/chat/ui/intelligent-ui/use-truncated.ts +5 -0
- package/src/chat/ui/intelligent-ui/use-ui-links.ts +29 -0
- package/src/chat/ui/intelligent-ui/use-ui-queries.ts +60 -0
- package/src/chat/ui/markdown-blocks.ts +64 -0
- package/src/chat/ui/mermaid.ts +4 -0
- package/src/chat/ui/quote-selection.ts +10 -0
- package/src/chat/ui/rehype-chips.ts +5 -0
- package/src/chat/ui/rehype-fade.ts +2 -0
- package/src/chat/ui/remark-html-as-text.ts +5 -0
- package/src/chat/ui/reveal.ts +18 -0
- package/src/chat/ui/rows/ForksRow.tsx +5 -0
- package/src/chat/ui/rows/GeneratedImageRow.tsx +10 -0
- package/src/chat/ui/rows/MessageRows.tsx +29 -5
- package/src/chat/ui/rows/RowContainer.tsx +1 -0
- package/src/chat/ui/rows/Rows.tsx +2 -0
- package/src/chat/ui/rows/SubagentRow.tsx +13 -0
- package/src/chat/ui/rows/TaskRow.tsx +7 -0
- package/src/chat/ui/rows/VisualRow.tsx +4 -0
- package/src/chat/ui/rows/WorkRows.tsx +46 -0
- package/src/chat/ui/rows/WorkflowRow.tsx +10 -0
- package/src/chat/ui/rows/row-rhythm.ts +7 -5
- package/src/chat/ui/theme-watch.ts +8 -7
- package/src/chat/ui/thread-references.ts +7 -0
- package/src/chat/ui/use-diff-source-tokens.ts +8 -0
- package/src/chat/ui/useToggleSet.ts +3 -0
- package/src/chat/ui/visual-theme.ts +30 -0
- package/src/chat/visual-bridge.ts +23 -0
- package/src/chat/visual-viewport.ts +12 -0
- package/src/host-hooks.ts +15 -5
- package/src/host.ts +29 -1
- package/src/index.ts +5 -0
- package/src/lazy.tsx +9 -0
- package/src/locales/en/agent-chat.ts +1 -0
- package/src/locales/nl/agent-chat.ts +1 -0
- package/src/mounted-registry.ts +4 -0
- package/src/port-transport.ts +32 -0
- package/src/prompts/index.ts +2 -0
- package/src/prompts/logic/focus.ts +5 -0
- package/src/prompts/logic/keys.ts +12 -0
- package/src/prompts/logic/prompts.ts +8 -0
- package/src/prompts/logic/subjects.ts +8 -0
- package/src/prompts/logic/useFocusAfterAnswer.ts +2 -0
- package/src/prompts/logic/usePromptSession.ts +13 -1
- package/src/prompts/ui/ApprovalBody.tsx +2 -0
- package/src/prompts/ui/PromptActions.tsx +2 -0
- package/src/prompts/ui/PromptCard.tsx +6 -0
- package/src/prompts/ui/PromptView.tsx +6 -0
- package/src/prompts/ui/QuestionBody.tsx +11 -0
- package/src/providers/AccountColors.tsx +1 -0
- package/src/providers/AccountDetail.tsx +7 -0
- package/src/providers/AccountVariables.tsx +2 -0
- package/src/providers/AddAccountForm.tsx +5 -1
- package/src/providers/CliDetail.tsx +6 -5
- package/src/providers/InstallPage.tsx +11 -0
- package/src/providers/ProviderList.tsx +3 -0
- package/src/providers/ProvidersPane.tsx +18 -0
- package/src/providers/account-actions.ts +10 -8
- package/src/providers/index.ts +1 -0
- package/src/providers/install-steps.ts +3 -0
- package/src/providers/pane-target.ts +5 -0
- package/src/providers/provider-abilities.ts +1 -0
- package/src/providers/provider-groups.ts +6 -0
- package/src/providers/variables.ts +12 -0
- package/src/scope.ts +2 -0
- package/src/state/chats.ts +61 -14
- package/src/state/index.ts +2 -0
- package/src/state/provider-accounts.ts +7 -9
- package/src/state/providers.ts +1 -0
- package/src/state/usage.ts +6 -0
- package/src/storage.ts +18 -0
- package/src/testing/prompts.ts +5 -0
- package/src/theme.css +3 -0
- package/src/transport.ts +18 -0
- package/src/usage/ChartLogo.tsx +12 -0
- package/src/usage/LimitsList.tsx +7 -8
- package/src/usage/UsageBreakdown.tsx +11 -4
- package/src/usage/UsageChart.tsx +5 -0
- package/src/usage/UsageLimits.tsx +10 -7
- package/src/usage/UsageLimitsCard.tsx +2 -0
- package/src/usage/UsagePage.tsx +16 -19
- package/src/usage/UsageSummary.tsx +4 -5
- package/src/usage/UsageTiles.tsx +2 -0
- package/src/usage/format.ts +7 -0
- package/src/usage/limit-groups.ts +14 -0
- package/src/usage/limits.ts +11 -13
- package/src/usage/money.ts +1 -0
- package/src/usage/summary.ts +15 -7
|
@@ -95,12 +95,15 @@ function lookSummary(tools: readonly ChatToolItem[], looks: readonly CommandLook
|
|
|
95
95
|
let look: CommandLook | null = null;
|
|
96
96
|
let group: { key: string; sentence(count: number): string } | undefined;
|
|
97
97
|
let sameKey = true;
|
|
98
|
+
|
|
98
99
|
for (const tool of tools) {
|
|
99
100
|
const command = shellCommand(tool.name, tool.input);
|
|
100
101
|
const run = command === null ? null : matchCommandLook(command, looks);
|
|
102
|
+
|
|
101
103
|
if (run === null || (look !== null && run.look !== look)) {
|
|
102
104
|
return null;
|
|
103
105
|
}
|
|
106
|
+
|
|
104
107
|
if (look === null) {
|
|
105
108
|
look = run.look;
|
|
106
109
|
group = run.match.group;
|
|
@@ -108,35 +111,45 @@ function lookSummary(tools: readonly ChatToolItem[], looks: readonly CommandLook
|
|
|
108
111
|
sameKey = false;
|
|
109
112
|
}
|
|
110
113
|
}
|
|
114
|
+
|
|
111
115
|
if (look === null) {
|
|
112
116
|
return null;
|
|
113
117
|
}
|
|
118
|
+
|
|
114
119
|
if (sameKey && group !== undefined) {
|
|
115
120
|
return { look, summary: group.sentence(tools.length) };
|
|
116
121
|
}
|
|
122
|
+
|
|
117
123
|
return look.sentence === undefined ? null : { look, summary: look.sentence(tools.length) };
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
/* "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. */
|
|
121
127
|
export function summarizeGroup(tools: ChatToolItem[], looks?: readonly CommandLook[]): string {
|
|
122
128
|
const looked = lookSummary(tools, looks);
|
|
129
|
+
|
|
123
130
|
if (looked !== null) {
|
|
124
131
|
return looked.summary;
|
|
125
132
|
}
|
|
133
|
+
|
|
126
134
|
const names = new Set(tools.map((tool) => tool.name));
|
|
127
135
|
const only = names.size === 1 ? tools[0]!.name : null;
|
|
128
136
|
const failed = tools.filter(failedFileChange);
|
|
137
|
+
|
|
129
138
|
if (only !== null && toolEntry(only)?.grouped === true && failed.length === 0) {
|
|
130
139
|
return i18next.t(`agent-chat:group.tools.${only}`, { count: tools.length });
|
|
131
140
|
}
|
|
141
|
+
|
|
132
142
|
// Counts the files rather than the calls: three edits to one file are one file edited.
|
|
133
143
|
if (tools.every((tool) => isFileChange(tool.name)) && failed.length < tools.length) {
|
|
134
144
|
const paths = new Set(tools.filter((tool) => !failedFileChange(tool)).map(editedPath));
|
|
145
|
+
|
|
135
146
|
return i18next.t('agent-chat:group.edited', { count: paths.size });
|
|
136
147
|
}
|
|
148
|
+
|
|
137
149
|
if (only !== null) {
|
|
138
150
|
return i18next.t('agent-chat:group.calls', { name: only, count: tools.length });
|
|
139
151
|
}
|
|
152
|
+
|
|
140
153
|
return i18next.t('agent-chat:group.toolCalls', { count: tools.length });
|
|
141
154
|
}
|
|
142
155
|
|
|
@@ -157,20 +170,25 @@ function editedPath(tool: ChatToolItem): string {
|
|
|
157
170
|
export function summarizeTurn(tools: readonly ChatToolItem[], looks?: readonly CommandLook[]): string[] {
|
|
158
171
|
// Keyed by the sentence, so its place is where the first call of that kind came.
|
|
159
172
|
const counts = new Map<string, { count: number; say(count: number): string }>();
|
|
173
|
+
|
|
160
174
|
const add = (key: string, say: (count: number) => string, total?: number): void => {
|
|
161
175
|
const known = counts.get(key);
|
|
176
|
+
|
|
162
177
|
if (known === undefined) {
|
|
163
178
|
counts.set(key, { count: total ?? 1, say });
|
|
164
179
|
} else {
|
|
165
180
|
known.count = total ?? known.count + 1;
|
|
166
181
|
}
|
|
167
182
|
};
|
|
183
|
+
|
|
168
184
|
const edited = new Set<string>();
|
|
169
185
|
let other = 0;
|
|
186
|
+
|
|
170
187
|
for (const tool of tools) {
|
|
171
188
|
const command = shellCommand(tool.name, tool.input);
|
|
172
189
|
const run = command === null ? null : matchCommandLook(command, looks);
|
|
173
190
|
const group = run?.match.group;
|
|
191
|
+
|
|
174
192
|
if (run !== null && group !== undefined) {
|
|
175
193
|
// Keyed by the program too, so two looks with a key in common keep their own sentences.
|
|
176
194
|
add(`look:${run.look.program}:${group.key}`, group.sentence);
|
|
@@ -186,10 +204,13 @@ export function summarizeTurn(tools: readonly ChatToolItem[], looks?: readonly C
|
|
|
186
204
|
other += 1;
|
|
187
205
|
}
|
|
188
206
|
}
|
|
207
|
+
|
|
189
208
|
const parts = [...counts.values()].map(({ count, say }) => say(count));
|
|
209
|
+
|
|
190
210
|
if (other > 0) {
|
|
191
211
|
parts.push(i18next.t(parts.length === 0 ? 'agent-chat:group.toolCalls' : 'agent-chat:group.otherCalls', { count: other }));
|
|
192
212
|
}
|
|
213
|
+
|
|
193
214
|
return parts;
|
|
194
215
|
}
|
|
195
216
|
|
|
@@ -199,20 +220,24 @@ export function summarizeTurn(tools: readonly ChatToolItem[], looks?: readonly C
|
|
|
199
220
|
*/
|
|
200
221
|
export function agentTurnLabel(turn: ChatTurnItem): string {
|
|
201
222
|
const about = (what: string): string => (turn.label ? i18next.t('agent-chat:turn.about', { what, label: turn.label }) : what);
|
|
223
|
+
|
|
202
224
|
// A turn the machine opened with the results of tasks this chat gave; the label is their titles.
|
|
203
225
|
if (turn.taskIds !== undefined && turn.taskIds.length > 0) {
|
|
204
226
|
return about(i18next.t('agent-chat:turn.wokenByTask', { count: turn.taskIds.length }));
|
|
205
227
|
}
|
|
228
|
+
|
|
206
229
|
// A turn the machine opened over a message another node sent; the label names who sent it.
|
|
207
230
|
if (turn.messageFrom !== undefined && turn.messageFrom.length > 0) {
|
|
208
231
|
return about(i18next.t('agent-chat:turn.wokenByMessage', { count: turn.messageFrom.length }));
|
|
209
232
|
}
|
|
233
|
+
|
|
210
234
|
return turn.label ? i18next.t('agent-chat:turn.subagentFinished', { label: turn.label }) : i18next.t('agent-chat:turn.continued');
|
|
211
235
|
}
|
|
212
236
|
|
|
213
237
|
/* The items of the turn tell a turn a person stopped from one the machine ended after a restart. */
|
|
214
238
|
export function turnLabel(turn: ChatTurnItem, items: readonly ChatItem[] = []): string {
|
|
215
239
|
const duration = formatElapsedShort((turn.endedAt ?? turn.createdAt) - turn.createdAt);
|
|
240
|
+
|
|
216
241
|
switch (turn.state) {
|
|
217
242
|
case 'aborted':
|
|
218
243
|
return abortedByMachine(turn, items) ? i18next.t('agent-chat:turn.stopped', { duration }) : i18next.t('agent-chat:turn.youStopped', { duration });
|
|
@@ -233,6 +258,7 @@ const MAX_NESTING = 8;
|
|
|
233
258
|
|
|
234
259
|
function branchOf(item: ChatSubagentItem, options: TimelineOptions, children: Map<string, ChatItem[]>, depth = 0): SubagentBranch {
|
|
235
260
|
const own = children.get(item.toolUseId) ?? [];
|
|
261
|
+
|
|
236
262
|
return {
|
|
237
263
|
id: item.id,
|
|
238
264
|
item,
|
|
@@ -251,39 +277,50 @@ export function findSubagentBranch(rows: readonly TimelineRow[], toolUseId: stri
|
|
|
251
277
|
if (branch.item.toolUseId === toolUseId) {
|
|
252
278
|
return branch;
|
|
253
279
|
}
|
|
280
|
+
|
|
254
281
|
for (const child of branch.nested) {
|
|
255
282
|
const found = search(child);
|
|
283
|
+
|
|
256
284
|
if (found) {
|
|
257
285
|
return found;
|
|
258
286
|
}
|
|
259
287
|
}
|
|
288
|
+
|
|
260
289
|
return null;
|
|
261
290
|
};
|
|
291
|
+
|
|
262
292
|
for (let index = 0; index < rows.length; index++) {
|
|
263
293
|
const row = rows[index]!;
|
|
264
294
|
const branch = row.kind === 'subagent' ? search(row) : null;
|
|
295
|
+
|
|
265
296
|
if (branch) {
|
|
266
297
|
return { index, branch };
|
|
267
298
|
}
|
|
268
299
|
}
|
|
300
|
+
|
|
269
301
|
return null;
|
|
270
302
|
}
|
|
271
303
|
|
|
272
304
|
/* What each subagent did, keyed by the call that spawned it, in the order it happened. */
|
|
273
305
|
function groupChildren(items: ChatItem[]): Map<string, ChatItem[]> {
|
|
274
306
|
const children = new Map<string, ChatItem[]>();
|
|
307
|
+
|
|
275
308
|
for (const item of items) {
|
|
276
309
|
const parent = parentOf(item);
|
|
310
|
+
|
|
277
311
|
if (parent === null) {
|
|
278
312
|
continue;
|
|
279
313
|
}
|
|
314
|
+
|
|
280
315
|
const bucket = children.get(parent);
|
|
316
|
+
|
|
281
317
|
if (bucket) {
|
|
282
318
|
bucket.push(item);
|
|
283
319
|
} else {
|
|
284
320
|
children.set(parent, [item]);
|
|
285
321
|
}
|
|
286
322
|
}
|
|
323
|
+
|
|
287
324
|
return children;
|
|
288
325
|
}
|
|
289
326
|
|
|
@@ -291,26 +328,31 @@ function flushTools(buffer: ChatToolItem[], rows: TimelineRow[], options: Timeli
|
|
|
291
328
|
if (buffer.length === 0) {
|
|
292
329
|
return;
|
|
293
330
|
}
|
|
331
|
+
|
|
294
332
|
const tools = buffer.splice(0);
|
|
295
333
|
const live = tools.filter((tool) => tool.state === 'running');
|
|
296
334
|
const settled = tools.filter((tool) => tool.state !== 'running');
|
|
335
|
+
|
|
297
336
|
if (settled.length === 1) {
|
|
298
337
|
rows.push({ kind: 'work', id: settled[0]!.id, tool: settled[0]! });
|
|
299
338
|
} else if (settled.length > 1) {
|
|
300
339
|
const id = `group-${settled[0]!.id}`;
|
|
301
340
|
const expanded = options.expandedGroups.has(id);
|
|
302
341
|
const looked = lookSummary(settled, options.commandLooks);
|
|
342
|
+
|
|
303
343
|
rows.push(
|
|
304
344
|
looked === null
|
|
305
345
|
? { kind: 'work-group', id, tools: settled, summary: summarizeGroup(settled), expanded }
|
|
306
346
|
: { kind: 'work-group', id, tools: settled, summary: looked.summary, expanded, look: looked.look }
|
|
307
347
|
);
|
|
348
|
+
|
|
308
349
|
if (expanded) {
|
|
309
350
|
for (const tool of settled) {
|
|
310
351
|
rows.push({ kind: 'work', id: tool.id, tool });
|
|
311
352
|
}
|
|
312
353
|
}
|
|
313
354
|
}
|
|
355
|
+
|
|
314
356
|
for (const tool of live) {
|
|
315
357
|
rows.push({ kind: 'work-live', id: tool.id, tool });
|
|
316
358
|
}
|
|
@@ -324,68 +366,90 @@ function isImageRow(row: TimelineRow): boolean {
|
|
|
324
366
|
function rowsForItems(items: ChatItem[], options: TimelineOptions, children: Map<string, ChatItem[]>): TimelineRow[] {
|
|
325
367
|
const rows: TimelineRow[] = [];
|
|
326
368
|
const tools: ChatToolItem[] = [];
|
|
369
|
+
|
|
327
370
|
for (const item of items) {
|
|
328
371
|
if (parentOf(item) !== null) {
|
|
329
372
|
continue;
|
|
330
373
|
}
|
|
374
|
+
|
|
331
375
|
const report = handbackReportOf(item);
|
|
376
|
+
|
|
332
377
|
if (item.kind === 'tool' && item.workflow !== undefined) {
|
|
333
378
|
flushTools(tools, rows, options);
|
|
334
379
|
rows.push({ kind: 'workflow', id: item.id, tool: item, workflow: item.workflow });
|
|
380
|
+
|
|
335
381
|
continue;
|
|
336
382
|
}
|
|
383
|
+
|
|
337
384
|
if (item.kind === 'tool' && isImageGeneration(item)) {
|
|
338
385
|
flushTools(tools, rows, options);
|
|
339
386
|
rows.push({ kind: item.state === 'running' ? 'work-live' : 'work', id: item.id, tool: item });
|
|
387
|
+
|
|
340
388
|
continue;
|
|
341
389
|
}
|
|
390
|
+
|
|
342
391
|
if (item.kind === 'tool' && report === null) {
|
|
343
392
|
tools.push(item);
|
|
393
|
+
|
|
344
394
|
continue;
|
|
345
395
|
}
|
|
396
|
+
|
|
346
397
|
if (report !== null) {
|
|
347
398
|
flushTools(tools, rows, options);
|
|
348
399
|
rows.push({ kind: 'report', id: item.id, text: report });
|
|
400
|
+
|
|
349
401
|
continue;
|
|
350
402
|
}
|
|
403
|
+
|
|
351
404
|
flushTools(tools, rows, options);
|
|
405
|
+
|
|
352
406
|
switch (item.kind) {
|
|
353
407
|
case 'subagent':
|
|
354
408
|
rows.push({ kind: 'subagent', ...branchOf(item, options, children) });
|
|
409
|
+
|
|
355
410
|
break;
|
|
356
411
|
case 'user':
|
|
357
412
|
rows.push({ kind: 'user', id: item.id, item });
|
|
413
|
+
|
|
358
414
|
break;
|
|
359
415
|
case 'assistant':
|
|
360
416
|
if (item.text !== '' || item.streaming) {
|
|
361
417
|
rows.push({ kind: 'assistant', id: item.id, item });
|
|
362
418
|
}
|
|
419
|
+
|
|
363
420
|
break;
|
|
364
421
|
case 'thinking':
|
|
365
422
|
rows.push({ kind: 'thinking', id: item.id, item });
|
|
423
|
+
|
|
366
424
|
break;
|
|
367
425
|
case 'approval':
|
|
368
426
|
// A pending one sits on the composer; only its outcome belongs in the transcript.
|
|
369
427
|
if (item.decision !== 'pending') {
|
|
370
428
|
rows.push({ kind: 'approval', id: item.id, item });
|
|
371
429
|
}
|
|
430
|
+
|
|
372
431
|
break;
|
|
373
432
|
case 'question':
|
|
374
433
|
if (item.state !== 'pending') {
|
|
375
434
|
rows.push({ kind: 'question', id: item.id, item });
|
|
376
435
|
}
|
|
436
|
+
|
|
377
437
|
break;
|
|
378
438
|
case 'note':
|
|
379
439
|
rows.push({ kind: 'note', id: item.id, level: item.level, text: item.text, ...(item.from === undefined ? {} : { from: item.from }) });
|
|
440
|
+
|
|
380
441
|
break;
|
|
381
442
|
case 'compaction':
|
|
382
443
|
rows.push({ kind: 'compaction', id: item.id, preTokens: item.preTokens });
|
|
444
|
+
|
|
383
445
|
break;
|
|
384
446
|
case 'turn':
|
|
385
447
|
break;
|
|
386
448
|
}
|
|
387
449
|
}
|
|
450
|
+
|
|
388
451
|
flushTools(tools, rows, options);
|
|
452
|
+
|
|
389
453
|
return rows;
|
|
390
454
|
}
|
|
391
455
|
|
|
@@ -400,12 +464,16 @@ function changedFilesRow(turn: ChatTurnItem, items: ChatItem[]): TimelineRow | n
|
|
|
400
464
|
const edits = items.filter(
|
|
401
465
|
(item): item is ChatToolItem => item.kind === 'tool' && item.state === 'done' && isFileChange(item.name) && hasFileChanges(item)
|
|
402
466
|
);
|
|
467
|
+
|
|
403
468
|
const diff = turn.checkpointDiff ?? null;
|
|
404
469
|
const empty = diff === null ? edits.length === 0 : diff.files.length === 0;
|
|
470
|
+
|
|
405
471
|
if (empty) {
|
|
406
472
|
return null;
|
|
407
473
|
}
|
|
474
|
+
|
|
408
475
|
const moved = turn.checkpointHead !== undefined && turn.checkpointHeadAfter !== undefined && turn.checkpointHead !== turn.checkpointHeadAfter;
|
|
476
|
+
|
|
409
477
|
return {
|
|
410
478
|
kind: 'changed-files',
|
|
411
479
|
id: `files-${turn.id}`,
|
|
@@ -423,22 +491,29 @@ function changedFilesRow(turn: ChatTurnItem, items: ChatItem[]): TimelineRow | n
|
|
|
423
491
|
*/
|
|
424
492
|
function lastAssistantRow(rows: TimelineRow[]): TimelineRow | null {
|
|
425
493
|
const refused = new Set<string>();
|
|
494
|
+
|
|
426
495
|
for (let i = rows.length - 1; i >= 0; i--) {
|
|
427
496
|
const row = rows[i]!;
|
|
497
|
+
|
|
428
498
|
if (row.kind === 'assistant') {
|
|
429
499
|
return row;
|
|
430
500
|
}
|
|
501
|
+
|
|
431
502
|
if (row.kind === 'approval' && row.item.decision === 'deny' && row.item.toolUseId !== null) {
|
|
432
503
|
refused.add(row.item.toolUseId);
|
|
504
|
+
|
|
433
505
|
continue;
|
|
434
506
|
}
|
|
507
|
+
|
|
435
508
|
if (row.kind === 'work' && refused.has(row.tool.toolUseId)) {
|
|
436
509
|
continue;
|
|
437
510
|
}
|
|
511
|
+
|
|
438
512
|
if (row.kind !== 'note' && !isImageRow(row)) {
|
|
439
513
|
return null;
|
|
440
514
|
}
|
|
441
515
|
}
|
|
516
|
+
|
|
442
517
|
return null;
|
|
443
518
|
}
|
|
444
519
|
|
|
@@ -451,17 +526,21 @@ function lastAssistantRow(rows: TimelineRow[]): TimelineRow | null {
|
|
|
451
526
|
function withLiveThought(rows: TimelineRow[], items: readonly ChatItem[]): TimelineRow[] {
|
|
452
527
|
const thoughtAt = items.findLastIndex((item) => item.kind === 'thinking' && parentOf(item) === null);
|
|
453
528
|
const thought = items[thoughtAt];
|
|
529
|
+
|
|
454
530
|
if (thought?.kind !== 'thinking' || thought.text === '') {
|
|
455
531
|
return rows;
|
|
456
532
|
}
|
|
533
|
+
|
|
457
534
|
const after = new Set(items.slice(thoughtAt + 1).map((item) => item.id));
|
|
458
535
|
const index = rows.findIndex((row) => row.kind === 'work-live' && after.has(row.tool.id));
|
|
459
536
|
const call = rows[index];
|
|
460
537
|
const shownAt = rows.findIndex((row) => row.kind === 'thinking' && row.id === thought.id);
|
|
461
|
-
const inSight = shownAt !== -1 && shownAt < index && !rows.slice(shownAt + 1, index).some((row) => row.kind === 'work');
|
|
538
|
+
const inSight = shownAt !== -1 && shownAt < index && !rows.slice(shownAt + 1, index).some((row) => row.kind === 'work' || row.kind === 'work-group');
|
|
539
|
+
|
|
462
540
|
if (call?.kind !== 'work-live' || inSight) {
|
|
463
541
|
return rows;
|
|
464
542
|
}
|
|
543
|
+
|
|
465
544
|
return [...rows.slice(0, index), { kind: 'live-thought', id: `live-thought-${thought.id}`, item: thought, at: call.tool.createdAt }, ...rows.slice(index)];
|
|
466
545
|
}
|
|
467
546
|
|
|
@@ -474,24 +553,31 @@ interface TurnChunk {
|
|
|
474
553
|
function chunksByTurn(items: ChatItem[]): TurnChunk[] {
|
|
475
554
|
const chunks: TurnChunk[] = [];
|
|
476
555
|
const byTurn = new Map<string, ChatItem[]>();
|
|
556
|
+
|
|
477
557
|
for (const item of items) {
|
|
478
558
|
if (item.turnId === null) {
|
|
479
|
-
const last = chunks
|
|
480
|
-
|
|
559
|
+
const last = chunks.at(-1);
|
|
560
|
+
|
|
561
|
+
if (last?.turnId === null) {
|
|
481
562
|
last.items.push(item);
|
|
482
563
|
} else {
|
|
483
564
|
chunks.push({ turnId: null, items: [item] });
|
|
484
565
|
}
|
|
566
|
+
|
|
485
567
|
continue;
|
|
486
568
|
}
|
|
569
|
+
|
|
487
570
|
let bucket = byTurn.get(item.turnId);
|
|
571
|
+
|
|
488
572
|
if (!bucket) {
|
|
489
573
|
bucket = [];
|
|
490
574
|
byTurn.set(item.turnId, bucket);
|
|
491
575
|
chunks.push({ turnId: item.turnId, items: bucket });
|
|
492
576
|
}
|
|
577
|
+
|
|
493
578
|
bucket.push(item);
|
|
494
579
|
}
|
|
580
|
+
|
|
495
581
|
return chunks;
|
|
496
582
|
}
|
|
497
583
|
|
|
@@ -499,27 +585,36 @@ function chunksByTurn(items: ChatItem[]): TurnChunk[] {
|
|
|
499
585
|
export function deriveTimelineRows(items: ChatItem[], options: TimelineOptions): TimelineRow[] {
|
|
500
586
|
const rows: TimelineRow[] = [];
|
|
501
587
|
const children = groupChildren(items);
|
|
588
|
+
|
|
502
589
|
for (const chunk of chunksByTurn(items)) {
|
|
503
590
|
if (chunk.turnId === null) {
|
|
504
591
|
rows.push(...rowsForItems(chunk.items, options, children));
|
|
592
|
+
|
|
505
593
|
continue;
|
|
506
594
|
}
|
|
595
|
+
|
|
507
596
|
const turnId = chunk.turnId;
|
|
508
597
|
const turn = chunk.items.find((item): item is ChatTurnItem => item.kind === 'turn');
|
|
509
598
|
const conversation = chunk.items.filter((item) => item.kind !== 'turn');
|
|
599
|
+
|
|
510
600
|
if (turn?.origin === 'agent') {
|
|
511
601
|
// The agent started this one itself, so there is no message of the person to show above it.
|
|
512
602
|
rows.push({ kind: 'turn-start', id: `start-${turnId}`, turn, label: agentTurnLabel(turn) });
|
|
513
603
|
}
|
|
604
|
+
|
|
514
605
|
const active = turnId === options.activeTurnId || turn?.state === 'running';
|
|
606
|
+
|
|
515
607
|
if (!turn || active) {
|
|
516
608
|
const live = rowsForItems(conversation, options, children);
|
|
517
609
|
rows.push(...(active ? withLiveThought(live, conversation) : live));
|
|
610
|
+
|
|
518
611
|
if (active) {
|
|
519
612
|
rows.push({ kind: 'working', id: `working-${turnId}`, startedAt: turn?.createdAt ?? Date.now() });
|
|
520
613
|
}
|
|
614
|
+
|
|
521
615
|
continue;
|
|
522
616
|
}
|
|
617
|
+
|
|
523
618
|
// Steering splits the conversation inside a turn. Earlier responses keep their place above it.
|
|
524
619
|
const lastUser = conversation.findLastIndex((item) => item.kind === 'user');
|
|
525
620
|
rows.push(...rowsForItems(conversation.slice(0, lastUser + 1), options, children));
|
|
@@ -527,38 +622,46 @@ export function deriveTimelineRows(items: ChatItem[], options: TimelineOptions):
|
|
|
527
622
|
const work = rowsForItems(rest, options, children);
|
|
528
623
|
// The closing answer stays visible; the work after the last input folds behind the label.
|
|
529
624
|
const finalAssistant = lastAssistantRow(work);
|
|
530
|
-
const
|
|
625
|
+
const closing = finalAssistant ? work.indexOf(finalAssistant) : work.length;
|
|
626
|
+
const before = work.slice(0, closing);
|
|
627
|
+
|
|
531
628
|
// Images remain a turn's visible result, and a workflow or a background subagent may still be running after its turn ends.
|
|
532
629
|
const standing = before.filter(
|
|
533
630
|
(row) => isImageRow(row) || (row.kind === 'workflow' && row.tool.state === 'running') || (row.kind === 'subagent' && row.item.status === 'running')
|
|
534
631
|
);
|
|
535
|
-
|
|
632
|
+
|
|
633
|
+
const folded = before.length - standing.length;
|
|
536
634
|
const expanded = options.expandedTurns.has(turnId);
|
|
537
|
-
|
|
635
|
+
|
|
636
|
+
if (folded > 0) {
|
|
538
637
|
const tools = rest.filter(
|
|
539
638
|
(item): item is ChatToolItem => item.kind === 'tool' && parentOf(item) === null && handbackReportOf(item) === null && !isImageGeneration(item)
|
|
540
639
|
);
|
|
640
|
+
|
|
541
641
|
rows.push({
|
|
542
642
|
kind: 'turn-fold',
|
|
543
643
|
id: `fold-${turnId}`,
|
|
544
644
|
turn,
|
|
545
645
|
label: turnLabel(turn, chunk.items),
|
|
546
646
|
work: summarizeTurn(tools, options.commandLooks),
|
|
547
|
-
hiddenCount: folded
|
|
647
|
+
hiddenCount: folded,
|
|
548
648
|
expanded
|
|
549
649
|
});
|
|
550
650
|
}
|
|
651
|
+
|
|
551
652
|
rows.push(...(expanded ? before : standing));
|
|
552
653
|
const changed = changedFilesRow(turn, conversation);
|
|
654
|
+
|
|
553
655
|
if (changed) {
|
|
554
656
|
rows.push(changed);
|
|
555
657
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
658
|
+
|
|
659
|
+
rows.push(...work.slice(closing));
|
|
660
|
+
|
|
559
661
|
if (options.forkedTurns?.has(turnId) === true) {
|
|
560
662
|
rows.push({ kind: 'forks', id: `forks-${turnId}`, turnId });
|
|
561
663
|
}
|
|
562
664
|
}
|
|
665
|
+
|
|
563
666
|
return rows;
|
|
564
667
|
}
|
package/src/chat/logic/tools.ts
CHANGED
|
@@ -17,18 +17,24 @@ export function toolSummary(name: string, input: unknown): string {
|
|
|
17
17
|
if (!isRecord(input)) {
|
|
18
18
|
return '';
|
|
19
19
|
}
|
|
20
|
+
|
|
20
21
|
const known = toolEntry(name);
|
|
22
|
+
|
|
21
23
|
if (known === undefined) {
|
|
22
24
|
// A tool nobody wrote down, an MCP one above all, gets its best guess from the first string it was given.
|
|
23
25
|
const first = Object.values(input).find((value) => typeof value === 'string');
|
|
26
|
+
|
|
24
27
|
return typeof first === 'string' ? first : '';
|
|
25
28
|
}
|
|
29
|
+
|
|
26
30
|
for (const key of known.summary) {
|
|
27
31
|
const value = str(input[key]);
|
|
32
|
+
|
|
28
33
|
if (value !== null) {
|
|
29
34
|
return value;
|
|
30
35
|
}
|
|
31
36
|
}
|
|
37
|
+
|
|
32
38
|
return '';
|
|
33
39
|
}
|
|
34
40
|
|
|
@@ -38,10 +44,13 @@ const PATH_KEYS = new Set(['file_path', 'notebook_path']);
|
|
|
38
44
|
/* Whether the one line of a call is the path of a file, which a row cuts in the middle so the name stays. */
|
|
39
45
|
export function toolSummaryIsPath(name: string, input: unknown): boolean {
|
|
40
46
|
const known = toolEntry(name);
|
|
47
|
+
|
|
41
48
|
if (known === undefined || !isRecord(input)) {
|
|
42
49
|
return false;
|
|
43
50
|
}
|
|
51
|
+
|
|
44
52
|
const key = known.summary.find((candidate) => str(input[candidate]) !== null);
|
|
53
|
+
|
|
45
54
|
return key !== undefined && PATH_KEYS.has(key);
|
|
46
55
|
}
|
|
47
56
|
|
|
@@ -50,7 +59,9 @@ export function shellCommand(name: string, input: unknown): string | null {
|
|
|
50
59
|
if (toolEntry(name)?.shell !== true || !isRecord(input)) {
|
|
51
60
|
return null;
|
|
52
61
|
}
|
|
62
|
+
|
|
53
63
|
const command = str(input.command);
|
|
64
|
+
|
|
54
65
|
return command === null || command.trim() === '' ? null : command;
|
|
55
66
|
}
|
|
56
67
|
|
|
@@ -76,21 +87,27 @@ function valueText(value: unknown): string {
|
|
|
76
87
|
export function toolCallLines(name: string, input: unknown): ToolCallLines {
|
|
77
88
|
if (!isRecord(input)) {
|
|
78
89
|
const text = input === null || input === undefined || input === '' ? null : valueText(input);
|
|
90
|
+
|
|
79
91
|
return { command: null, args: [], text };
|
|
80
92
|
}
|
|
93
|
+
|
|
81
94
|
const command = shellCommand(name, input);
|
|
95
|
+
|
|
82
96
|
const args = Object.entries(input)
|
|
83
97
|
.filter(([key, value]) => value !== undefined && !(command !== null && SHELL_SHOWN.has(key)))
|
|
84
98
|
.map(([key, value]) => [key, valueText(value)] as const);
|
|
99
|
+
|
|
85
100
|
return { command, args, text: null };
|
|
86
101
|
}
|
|
87
102
|
|
|
88
103
|
/* A failed call's output with the exit code its tool put on the first line ("Exit code 1"), as Claude Code's shell does. */
|
|
89
104
|
export function exitCodeOf(output: string): { code: number | null; rest: string } {
|
|
90
105
|
const match = /^Exit code (-?\d+)[ \t]*(?:\r?\n|$)/.exec(output);
|
|
106
|
+
|
|
91
107
|
if (match === null || Number(match[1]) === 0) {
|
|
92
108
|
return { code: null, rest: output };
|
|
93
109
|
}
|
|
110
|
+
|
|
94
111
|
return { code: Number(match[1]), rest: output.slice(match[0].length) };
|
|
95
112
|
}
|
|
96
113
|
|
|
@@ -104,7 +121,9 @@ export function hasToolBody(tool: ChatToolItem): boolean {
|
|
|
104
121
|
if (hasFileChanges(tool) || hasOutput(tool.output)) {
|
|
105
122
|
return true;
|
|
106
123
|
}
|
|
124
|
+
|
|
107
125
|
const lines = toolCallLines(tool.name, tool.input);
|
|
126
|
+
|
|
108
127
|
return lines.command !== null || lines.args.length > 0 || lines.text !== null;
|
|
109
128
|
}
|
|
110
129
|
|
|
@@ -116,11 +135,15 @@ export function readImagePath(name: string, input: unknown): string | null {
|
|
|
116
135
|
if (toolEntry(name)?.readsImage !== true) {
|
|
117
136
|
return null;
|
|
118
137
|
}
|
|
138
|
+
|
|
119
139
|
const path = isRecord(input) ? str(input.file_path) : null;
|
|
140
|
+
|
|
120
141
|
if (path === null) {
|
|
121
142
|
return null;
|
|
122
143
|
}
|
|
144
|
+
|
|
123
145
|
const lowered = path.toLowerCase();
|
|
146
|
+
|
|
124
147
|
return IMAGE_SUFFIXES.some((suffix) => lowered.endsWith(suffix)) ? path : null;
|
|
125
148
|
}
|
|
126
149
|
|
|
@@ -129,16 +152,21 @@ export function fileChanges(name: string, input: unknown): FileChange[] {
|
|
|
129
152
|
if (!isRecord(input)) {
|
|
130
153
|
return [];
|
|
131
154
|
}
|
|
155
|
+
|
|
132
156
|
const path = str(input.file_path) ?? '';
|
|
157
|
+
|
|
133
158
|
if (name === 'Edit') {
|
|
134
159
|
return [{ path, before: str(input.old_string) ?? '', after: str(input.new_string) ?? '' }];
|
|
135
160
|
}
|
|
161
|
+
|
|
136
162
|
if (name === 'Write') {
|
|
137
163
|
return [{ path, before: '', after: str(input.content) ?? '' }];
|
|
138
164
|
}
|
|
165
|
+
|
|
139
166
|
if (name === 'MultiEdit' && Array.isArray(input.edits)) {
|
|
140
167
|
return input.edits.filter(isRecord).map((edit) => ({ path, before: str(edit.old_string) ?? '', after: str(edit.new_string) ?? '' }));
|
|
141
168
|
}
|
|
169
|
+
|
|
142
170
|
return [];
|
|
143
171
|
}
|
|
144
172
|
|
|
@@ -154,9 +182,11 @@ export function unifiedChanges(tool: ChatToolItem): ChatFileChange[] {
|
|
|
154
182
|
/* The same, for the copy an approval carries so the person can read what they are approving. */
|
|
155
183
|
export function approvalChanges(input: unknown): ChatFileChange[] {
|
|
156
184
|
const changes = isRecord(input) ? input.changes : null;
|
|
185
|
+
|
|
157
186
|
if (!Array.isArray(changes)) {
|
|
158
187
|
return [];
|
|
159
188
|
}
|
|
189
|
+
|
|
160
190
|
return changes.filter(
|
|
161
191
|
(change): change is ChatFileChange => isRecord(change) && typeof change.path === 'string' && typeof change.diff === 'string' && change.diff !== ''
|
|
162
192
|
);
|
|
@@ -2,6 +2,7 @@ import { VISUAL_LIMITS, visualFrameHeight, type ChatVisual } from '@adecore/agen
|
|
|
2
2
|
|
|
3
3
|
/* A frame before anything measured its page: room for a chart, never taller than the page may grow. */
|
|
4
4
|
export const DEFAULT_HEIGHT_PX = 240;
|
|
5
|
+
|
|
5
6
|
/* Plenty for the visuals of a long session; the oldest goes first. */
|
|
6
7
|
const REMEMBERED_HEIGHTS = 500;
|
|
7
8
|
// Stay within browser layout coordinates even if a page reports a bogus finite size.
|
|
@@ -28,11 +29,14 @@ export function visualContentHeight(height: number): number {
|
|
|
28
29
|
|
|
29
30
|
export function initialVisualContentHeight(visual: Pick<ChatVisual, 'id' | 'heights'>, width: number): number {
|
|
30
31
|
const cached = remembered.get(keyOf(visual.id, width));
|
|
32
|
+
|
|
31
33
|
if (cached !== undefined) {
|
|
32
34
|
return cached;
|
|
33
35
|
}
|
|
36
|
+
|
|
34
37
|
const below = visual.heights?.filter(([measured]) => measured <= width).sort((left, right) => right[0] - left[0])[0];
|
|
35
38
|
const above = visual.heights?.filter(([measured]) => measured >= width).sort((left, right) => left[0] - right[0])[0];
|
|
39
|
+
|
|
36
40
|
return below || above ? visualContentHeight(Math.max(below?.[1] ?? 0, above?.[1] ?? 0)) : DEFAULT_HEIGHT_PX;
|
|
37
41
|
}
|
|
38
42
|
|
|
@@ -48,6 +52,7 @@ export function rememberVisualHeight(visualId: string, width: number, height: nu
|
|
|
48
52
|
const key = keyOf(visualId, width);
|
|
49
53
|
remembered.delete(key);
|
|
50
54
|
remembered.set(key, height);
|
|
55
|
+
|
|
51
56
|
if (remembered.size > REMEMBERED_HEIGHTS) {
|
|
52
57
|
remembered.delete(remembered.keys().next().value!);
|
|
53
58
|
}
|
|
@@ -5,5 +5,6 @@ export interface VisualViewportGeometry {
|
|
|
5
5
|
|
|
6
6
|
export function visualViewportGeometry(contentHeight: number, availableHeight: number, top: number): VisualViewportGeometry {
|
|
7
7
|
const height = Math.max(1, Math.min(contentHeight, availableHeight));
|
|
8
|
+
|
|
8
9
|
return { height, top: Math.max(0, Math.min(-top, contentHeight - height)) };
|
|
9
10
|
}
|
|
@@ -3,11 +3,14 @@ export type DayPart = 'morning' | 'afternoon' | 'evening' | 'night';
|
|
|
3
3
|
|
|
4
4
|
export function dayPartOf(now: Date): DayPart {
|
|
5
5
|
const hour = now.getHours();
|
|
6
|
+
|
|
6
7
|
if (hour < 5) {
|
|
7
8
|
return 'night';
|
|
8
9
|
}
|
|
10
|
+
|
|
9
11
|
if (hour < 12) {
|
|
10
12
|
return 'morning';
|
|
11
13
|
}
|
|
14
|
+
|
|
12
15
|
return hour < 18 ? 'afternoon' : 'evening';
|
|
13
16
|
}
|