@adecore/agents-react 0.39.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.js +2 -1
- 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/GoalDock.js +2 -1
- package/dist/chat/ui/LimitState.js +4 -6
- 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.js +216 -253
- 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/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/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/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/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 +4 -6
- 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 +15 -0
- 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 +26 -0
- package/src/chat/ui/PromptComposer.tsx +2 -0
- package/src/chat/ui/PromptEditor.tsx +6 -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 +119 -0
- 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 +5 -0
- 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 +39 -0
- 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 +1 -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 +2 -0
- 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 +34 -14
- 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 +4 -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 +6 -4
- package/src/host.ts +2 -0
- package/src/index.ts +3 -0
- package/src/lazy.tsx +9 -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/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/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
|
@@ -32,11 +32,13 @@ interface OfferInput {
|
|
|
32
32
|
/* The newest turn by when it started, so a thread that arrives out of order still names the turn that ran last. */
|
|
33
33
|
function lastTurn(items: readonly ChatItem[]): Turn | null {
|
|
34
34
|
let last: Turn | null = null;
|
|
35
|
+
|
|
35
36
|
for (const item of items) {
|
|
36
37
|
if (item.kind === 'turn' && (last === null || item.createdAt >= last.createdAt)) {
|
|
37
38
|
last = item;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
41
|
+
|
|
40
42
|
return last;
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -45,24 +47,27 @@ function offerableTurn({ info, compaction, items, dismissedTurnId }: OfferInput)
|
|
|
45
47
|
if (compaction === undefined || compaction === 'none') {
|
|
46
48
|
return null;
|
|
47
49
|
}
|
|
50
|
+
|
|
48
51
|
if (info.activeTurnId !== null || (info.queue ?? []).length > 0) {
|
|
49
52
|
return null;
|
|
50
53
|
}
|
|
54
|
+
|
|
51
55
|
if (info.usage.contextTokens < RESUME_COMPACTION_TOKENS) {
|
|
52
56
|
return null;
|
|
53
57
|
}
|
|
58
|
+
|
|
54
59
|
const turn = lastTurn(items);
|
|
60
|
+
|
|
55
61
|
if (turn === null || turn.endedAt === null || turn.id === dismissedTurnId) {
|
|
56
62
|
return null;
|
|
57
63
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* person, which is why this looks at the turn and not at the last thing that was said.
|
|
62
|
-
*/
|
|
64
|
+
|
|
65
|
+
// `contextTokens` keeps the old number until the next request, so the offer would return right after
|
|
66
|
+
// a compaction. A native one is a turn without a message of the person, hence the look at the turn.
|
|
63
67
|
if (items.some((item) => item.kind === 'compaction' && item.turnId === turn.id)) {
|
|
64
68
|
return null;
|
|
65
69
|
}
|
|
70
|
+
|
|
66
71
|
return turn;
|
|
67
72
|
}
|
|
68
73
|
|
|
@@ -77,15 +82,18 @@ function dueAtOf(info: ChatInfo, endedAt: number): number {
|
|
|
77
82
|
*/
|
|
78
83
|
export function resumeCompactionDueAt(input: OfferInput): number | null {
|
|
79
84
|
const endedAt = offerableTurn(input)?.endedAt ?? null;
|
|
85
|
+
|
|
80
86
|
return endedAt === null ? null : dueAtOf(input.info, endedAt);
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
/* Whether to offer compacting before the next message. */
|
|
84
90
|
export function resumeCompactionOffer(input: OfferInput & { now: number }): ResumeCompactionOffer | null {
|
|
85
91
|
const turn = offerableTurn(input);
|
|
92
|
+
|
|
86
93
|
if (turn === null || turn.endedAt === null || input.now < dueAtOf(input.info, turn.endedAt)) {
|
|
87
94
|
return null;
|
|
88
95
|
}
|
|
96
|
+
|
|
89
97
|
return {
|
|
90
98
|
turnId: turn.id,
|
|
91
99
|
tokens: input.info.usage.contextTokens,
|
|
@@ -109,30 +117,39 @@ export interface DueClock {
|
|
|
109
117
|
export function watchDueAt(dueAt: number, due: (now: number) => void, clock: DueClock): () => void {
|
|
110
118
|
let timer: unknown = null;
|
|
111
119
|
let done = false;
|
|
120
|
+
|
|
112
121
|
const check = (): void => {
|
|
113
122
|
if (timer !== null) {
|
|
114
123
|
clock.clearTimer(timer);
|
|
115
124
|
timer = null;
|
|
116
125
|
}
|
|
126
|
+
|
|
117
127
|
if (done) {
|
|
118
128
|
return;
|
|
119
129
|
}
|
|
130
|
+
|
|
120
131
|
const now = clock.now();
|
|
132
|
+
|
|
121
133
|
if (now >= dueAt) {
|
|
122
134
|
done = true;
|
|
123
135
|
due(now);
|
|
136
|
+
|
|
124
137
|
return;
|
|
125
138
|
}
|
|
139
|
+
|
|
126
140
|
timer = clock.setTimer(() => {
|
|
127
141
|
timer = null;
|
|
128
142
|
check();
|
|
129
143
|
}, dueAt - now);
|
|
130
144
|
};
|
|
145
|
+
|
|
131
146
|
const stopWake = clock.onWake(check);
|
|
132
147
|
check();
|
|
148
|
+
|
|
133
149
|
return () => {
|
|
134
150
|
done = true;
|
|
135
151
|
stopWake();
|
|
152
|
+
|
|
136
153
|
if (timer !== null) {
|
|
137
154
|
clock.clearTimer(timer);
|
|
138
155
|
}
|
|
@@ -2,11 +2,7 @@ import type { ChatBookmark } from '@adecore/agent-contracts';
|
|
|
2
2
|
import type { TimelineRow } from './timeline';
|
|
3
3
|
import { DEFAULT_READING_WIDTH, readingMeasure } from '../reading-width';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
* The strip at the left edge of a chat view with a tick per message a person sent, as numbers: which
|
|
7
|
-
* rows get a tick, where the ticks go in the height the strip has, which messages are on screen, and
|
|
8
|
-
* which one a pointer means. The component only draws what comes out of here.
|
|
9
|
-
*/
|
|
5
|
+
// The strip of ticks at the left edge of a chat view, as numbers; the component only draws what comes out of here.
|
|
10
6
|
|
|
11
7
|
/* A message the person sent, a turn the machine opened with the results of tasks, or any other message with a bookmark on it. */
|
|
12
8
|
export type TickKind = 'person' | 'wake' | 'bookmark';
|
|
@@ -33,6 +29,7 @@ const MIN_PITCH_PX = 4;
|
|
|
33
29
|
|
|
34
30
|
/* The scroller's padding around the thread's content column (`px-4`). */
|
|
35
31
|
const THREAD_PADDING_PX = 16;
|
|
32
|
+
|
|
36
33
|
export const STRIP_WIDTH_PX = 24;
|
|
37
34
|
/* The strip's distance from the view's left edge, and the air it keeps between itself and the thread's text. */
|
|
38
35
|
export const STRIP_INSET_PX = 8;
|
|
@@ -48,23 +45,30 @@ const WIDTH_WAKE_PX = 5;
|
|
|
48
45
|
export function ticksOf(rows: readonly TimelineRow[], marks: ReadonlyMap<string, ChatBookmark> = new Map()): ScrubberTick[] {
|
|
49
46
|
return rows.flatMap((row, rowIndex): ScrubberTick[] => {
|
|
50
47
|
const bookmark = marks.get(row.id) ?? null;
|
|
48
|
+
|
|
51
49
|
if (row.kind === 'user') {
|
|
52
50
|
// A message of only attachments still has a place in the thread; its names stand in for the text.
|
|
53
51
|
const text = row.item.text !== '' ? row.item.text : (row.item.attachments ?? []).map((attachment) => attachment.name).join(', ');
|
|
52
|
+
|
|
54
53
|
return [{ id: row.id, rowIndex, kind: 'person', text, createdAt: row.item.createdAt, turnId: row.item.turnId, bookmark }];
|
|
55
54
|
}
|
|
55
|
+
|
|
56
56
|
// The other turns nobody asked for (a sub-agent that finished, the CLI going on) are no place to return to.
|
|
57
57
|
if (row.kind === 'turn-start' && ((row.turn.taskIds?.length ?? 0) > 0 || (row.turn.messageFrom?.length ?? 0) > 0)) {
|
|
58
58
|
return [{ id: row.id, rowIndex, kind: 'wake', text: row.label, createdAt: row.turn.createdAt, turnId: row.turn.id, bookmark: null }];
|
|
59
59
|
}
|
|
60
|
+
|
|
60
61
|
if (bookmark === null) {
|
|
61
62
|
return [];
|
|
62
63
|
}
|
|
64
|
+
|
|
63
65
|
if (row.kind === 'assistant') {
|
|
64
66
|
return [{ id: row.id, rowIndex, kind: 'bookmark', text: row.item.text, createdAt: row.item.createdAt, turnId: row.item.turnId, bookmark }];
|
|
65
67
|
}
|
|
68
|
+
|
|
66
69
|
// The fold of a turn a marked reply is hidden in; the card reads the start the bookmark kept.
|
|
67
70
|
const turnId = row.kind === 'turn-fold' ? row.turn.id : null;
|
|
71
|
+
|
|
68
72
|
return [{ id: row.id, rowIndex, kind: 'bookmark', text: bookmark.excerpt, createdAt: bookmark.createdAt, turnId, bookmark }];
|
|
69
73
|
});
|
|
70
74
|
}
|
|
@@ -94,19 +98,23 @@ export function layoutTicks(kinds: readonly TickKind[], height: number, marked:
|
|
|
94
98
|
const count = kinds.length;
|
|
95
99
|
const room = Math.max(0, Math.floor(height));
|
|
96
100
|
const slotCount = Math.min(count, Math.floor(room / MIN_PITCH_PX));
|
|
101
|
+
|
|
97
102
|
if (slotCount === 0) {
|
|
98
103
|
return { top: 0, pitch: MIN_PITCH_PX, count, slots: [] };
|
|
99
104
|
}
|
|
105
|
+
|
|
100
106
|
const pitch = Math.min(PITCH_PX, Math.floor(room / slotCount));
|
|
101
107
|
const top = Math.floor((room - slotCount * pitch) / 2);
|
|
102
108
|
const inset = Math.floor((pitch - TICK_HEIGHT_PX) / 2);
|
|
103
109
|
const slots: TickSlot[] = [];
|
|
110
|
+
|
|
104
111
|
for (let i = 0; i < slotCount; i++) {
|
|
105
112
|
const first = Math.floor((i * count) / slotCount);
|
|
106
113
|
const last = Math.floor(((i + 1) * count) / slotCount) - 1;
|
|
107
114
|
// A merged tick is the person's as soon as one message in it is, since those are what the strip is for.
|
|
108
115
|
const run = kinds.slice(first, last + 1);
|
|
109
116
|
const kind = run.includes('person') ? 'person' : run.includes('bookmark') ? 'bookmark' : 'wake';
|
|
117
|
+
|
|
110
118
|
slots.push({
|
|
111
119
|
first,
|
|
112
120
|
last,
|
|
@@ -116,6 +124,7 @@ export function layoutTicks(kinds: readonly TickKind[], height: number, marked:
|
|
|
116
124
|
y: top + i * pitch + inset
|
|
117
125
|
});
|
|
118
126
|
}
|
|
127
|
+
|
|
119
128
|
return { top, pitch, count, slots };
|
|
120
129
|
}
|
|
121
130
|
|
|
@@ -127,6 +136,7 @@ export function tickOfRow(ticks: readonly ScrubberTick[], rowIndex: number): num
|
|
|
127
136
|
if (ticks.length === 0) {
|
|
128
137
|
return null;
|
|
129
138
|
}
|
|
139
|
+
|
|
130
140
|
return Math.max(
|
|
131
141
|
0,
|
|
132
142
|
lastIndexAtOrBelow(ticks.length, (index) => ticks[index]!.rowIndex, rowIndex)
|
|
@@ -136,25 +146,31 @@ export function tickOfRow(ticks: readonly ScrubberTick[], rowIndex: number): num
|
|
|
136
146
|
/* Per tick, whether any of the rows has a find hit in its stretch. */
|
|
137
147
|
export function ticksWithHits(ticks: readonly ScrubberTick[], hitRows: readonly number[]): boolean[] {
|
|
138
148
|
const found = ticks.map(() => false);
|
|
149
|
+
|
|
139
150
|
for (const row of hitRows) {
|
|
140
151
|
const tick = tickOfRow(ticks, row);
|
|
152
|
+
|
|
141
153
|
if (tick !== null) {
|
|
142
154
|
found[tick] = true;
|
|
143
155
|
}
|
|
144
156
|
}
|
|
157
|
+
|
|
145
158
|
return found;
|
|
146
159
|
}
|
|
147
160
|
|
|
148
161
|
/* The message under a height in the strip; inside a merged tick the height picks among its messages. */
|
|
149
162
|
export function messageAt(layout: ScrubberLayout, y: number): number | null {
|
|
150
163
|
const { slots, top, pitch } = layout;
|
|
164
|
+
|
|
151
165
|
if (slots.length === 0) {
|
|
152
166
|
return null;
|
|
153
167
|
}
|
|
168
|
+
|
|
154
169
|
const offset = y - top;
|
|
155
170
|
const index = Math.min(slots.length - 1, Math.max(0, Math.floor(offset / pitch)));
|
|
156
171
|
const slot = slots[index]!;
|
|
157
172
|
const fraction = Math.min(1, Math.max(0, (offset - index * pitch) / pitch));
|
|
173
|
+
|
|
158
174
|
return slot.first + Math.min(slot.last - slot.first, Math.floor(fraction * (slot.last - slot.first + 1)));
|
|
159
175
|
}
|
|
160
176
|
|
|
@@ -162,9 +178,11 @@ export function slotOf(layout: ScrubberLayout, message: number): number | null {
|
|
|
162
178
|
const { slots } = layout;
|
|
163
179
|
let low = 0;
|
|
164
180
|
let high = slots.length - 1;
|
|
181
|
+
|
|
165
182
|
while (low <= high) {
|
|
166
183
|
const middle = (low + high) >> 1;
|
|
167
184
|
const slot = slots[middle]!;
|
|
185
|
+
|
|
168
186
|
if (message < slot.first) {
|
|
169
187
|
high = middle - 1;
|
|
170
188
|
} else if (message > slot.last) {
|
|
@@ -173,6 +191,7 @@ export function slotOf(layout: ScrubberLayout, message: number): number | null {
|
|
|
173
191
|
return middle;
|
|
174
192
|
}
|
|
175
193
|
}
|
|
194
|
+
|
|
176
195
|
return null;
|
|
177
196
|
}
|
|
178
197
|
|
|
@@ -184,9 +203,11 @@ export function tickWidth(kind: TickKind, distanceFromPointer: number | null): n
|
|
|
184
203
|
if (distanceFromPointer === 0) {
|
|
185
204
|
return WIDTH_HOVERED_PX;
|
|
186
205
|
}
|
|
206
|
+
|
|
187
207
|
if (distanceFromPointer === 1) {
|
|
188
208
|
return kind === 'wake' ? WIDTH_NEAR_WAKE_PX : WIDTH_NEAR_PERSON_PX;
|
|
189
209
|
}
|
|
210
|
+
|
|
190
211
|
return kind === 'wake' ? WIDTH_WAKE_PX : WIDTH_PERSON_PX;
|
|
191
212
|
}
|
|
192
213
|
|
|
@@ -195,8 +216,10 @@ function lastIndexAtOrBelow(length: number, valueAt: (index: number) => number,
|
|
|
195
216
|
let low = 0;
|
|
196
217
|
let high = length - 1;
|
|
197
218
|
let found = -1;
|
|
219
|
+
|
|
198
220
|
while (low <= high) {
|
|
199
221
|
const middle = (low + high) >> 1;
|
|
222
|
+
|
|
200
223
|
if (valueAt(middle) <= limit) {
|
|
201
224
|
found = middle;
|
|
202
225
|
low = middle + 1;
|
|
@@ -204,6 +227,7 @@ function lastIndexAtOrBelow(length: number, valueAt: (index: number) => number,
|
|
|
204
227
|
high = middle - 1;
|
|
205
228
|
}
|
|
206
229
|
}
|
|
230
|
+
|
|
207
231
|
return found;
|
|
208
232
|
}
|
|
209
233
|
|
|
@@ -233,15 +257,19 @@ export interface MessageRange {
|
|
|
233
257
|
*/
|
|
234
258
|
export function messagesInView({ starts, scrollTop, visibleHeight }: ReadingPosition): MessageRange | null {
|
|
235
259
|
const bottom = scrollTop + visibleHeight;
|
|
260
|
+
|
|
236
261
|
if (starts.length === 0 || visibleHeight <= 0 || starts.at(0)! >= bottom) {
|
|
237
262
|
return null;
|
|
238
263
|
}
|
|
264
|
+
|
|
239
265
|
const first = Math.max(0, lastAtOrAbove(starts, scrollTop));
|
|
240
266
|
let last = lastAtOrAbove(starts, bottom);
|
|
267
|
+
|
|
241
268
|
// A row that starts on the very last line has not a pixel on screen yet.
|
|
242
269
|
if (starts.at(last)! >= bottom) {
|
|
243
270
|
last--;
|
|
244
271
|
}
|
|
272
|
+
|
|
245
273
|
return { first, last: Math.max(first, last) };
|
|
246
274
|
}
|
|
247
275
|
|
|
@@ -262,13 +290,16 @@ export function stepMessage(starts: MessageStarts, eligible: (index: number) =>
|
|
|
262
290
|
return i;
|
|
263
291
|
}
|
|
264
292
|
}
|
|
293
|
+
|
|
265
294
|
return null;
|
|
266
295
|
}
|
|
296
|
+
|
|
267
297
|
for (let i = lastAtOrAbove(starts, scrollTop + 1) + 1; i < starts.length; i++) {
|
|
268
298
|
if (eligible(i)) {
|
|
269
299
|
return i;
|
|
270
300
|
}
|
|
271
301
|
}
|
|
302
|
+
|
|
272
303
|
return null;
|
|
273
304
|
}
|
|
274
305
|
|
|
@@ -282,7 +313,9 @@ export function threadPaddingLeft(width: number, strip: boolean, measure = readi
|
|
|
282
313
|
if (!strip) {
|
|
283
314
|
return THREAD_PADDING_PX;
|
|
284
315
|
}
|
|
316
|
+
|
|
285
317
|
// The column's margin is half of what the padding leaves, so the padding counts the clearance twice.
|
|
286
318
|
const padding = 2 * STRIP_CLEARANCE_PX + measure + THREAD_PADDING_PX - Math.floor(width);
|
|
319
|
+
|
|
287
320
|
return Math.min(STRIP_CLEARANCE_PX, Math.max(THREAD_PADDING_PX, padding));
|
|
288
321
|
}
|
|
@@ -18,6 +18,7 @@ function headingOf(line: string): string | null {
|
|
|
18
18
|
/* What a folded thought shows on its one line: the heading it opens with, or the whole thought flattened for the row to cut off. */
|
|
19
19
|
export function thoughtPreview(text: string): string {
|
|
20
20
|
const heading = headingOf(text.trimStart().split('\n', 1)[0] ?? '');
|
|
21
|
+
|
|
21
22
|
return flattenThought(heading ?? text);
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -30,11 +31,15 @@ export function thoughtLine(text: string): string {
|
|
|
30
31
|
.split('\n')
|
|
31
32
|
.map(headingOf)
|
|
32
33
|
.filter((heading): heading is string => heading !== null);
|
|
34
|
+
|
|
33
35
|
const last = headings.at(-1);
|
|
36
|
+
|
|
34
37
|
if (last !== undefined) {
|
|
35
38
|
return flattenThought(last);
|
|
36
39
|
}
|
|
40
|
+
|
|
37
41
|
const flat = flattenThought(text);
|
|
38
42
|
const end = SENTENCE_END.exec(flat);
|
|
43
|
+
|
|
39
44
|
return end === null ? flat : flat.slice(0, end.index + end[0].length);
|
|
40
45
|
}
|
|
@@ -67,21 +67,27 @@ export function withTimedRows(rows: TimelineRow[], timed: readonly TimedRow[]):
|
|
|
67
67
|
if (timed.length === 0) {
|
|
68
68
|
return rows;
|
|
69
69
|
}
|
|
70
|
+
|
|
70
71
|
// Sorting is stable, which is what keeps rows of the same moment in the order they came.
|
|
71
72
|
const pending = [...timed].sort((first, second) => first.at - second.at);
|
|
72
73
|
const merged: TimelineRow[] = [];
|
|
73
74
|
let next = 0;
|
|
75
|
+
|
|
74
76
|
for (const row of rows) {
|
|
75
77
|
const time = rowTime(row);
|
|
78
|
+
|
|
76
79
|
while (time !== null && next < pending.length && pending[next]!.at < time) {
|
|
77
80
|
merged.push(pending[next]!.row);
|
|
78
81
|
next += 1;
|
|
79
82
|
}
|
|
83
|
+
|
|
80
84
|
merged.push(row);
|
|
81
85
|
}
|
|
86
|
+
|
|
82
87
|
for (; next < pending.length; next++) {
|
|
83
88
|
merged.push(pending[next]!.row);
|
|
84
89
|
}
|
|
90
|
+
|
|
85
91
|
return merged;
|
|
86
92
|
}
|
|
87
93
|
|
|
@@ -2,15 +2,8 @@ import type { TimelineRow } from './timeline';
|
|
|
2
2
|
import { stripAnsi } from './ansi';
|
|
3
3
|
|
|
4
4
|
/*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* source itself stays available as "Copy as Markdown".
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/*
|
|
11
|
-
* The marks that only exist to be rendered come off, what they marked stays. `_` is left alone on
|
|
12
|
-
* purpose: it is a letter in half the identifiers an agent writes, and no italic is worth mangling
|
|
13
|
-
* `file_path`.
|
|
5
|
+
* An answer is markdown on the wire and rendered text on screen, so a copy as text undoes the marks the
|
|
6
|
+
* renderer swallowed. `_` stays: it is a letter in half the identifiers an agent writes (`file_path`).
|
|
14
7
|
*/
|
|
15
8
|
export function stripMarkdown(text: string): string {
|
|
16
9
|
return text
|
|
@@ -28,6 +21,7 @@ export function stripMarkdown(text: string): string {
|
|
|
28
21
|
/* What a tool call has to show for itself: what it answered, or what it has answered so far. */
|
|
29
22
|
function outputOf(tool: { output: string | null; progress?: { output: string | null } }): string | null {
|
|
30
23
|
const output = tool.output ?? tool.progress?.output ?? null;
|
|
24
|
+
|
|
31
25
|
return output === null ? null : stripAnsi(output);
|
|
32
26
|
}
|
|
33
27
|
|
|
@@ -48,6 +42,7 @@ export function messageTextOf(row: TimelineRow): string | null {
|
|
|
48
42
|
return outputOf(row.tool);
|
|
49
43
|
case 'work-group': {
|
|
50
44
|
const outputs = row.tools.map(outputOf).filter((output): output is string => output !== null);
|
|
45
|
+
|
|
51
46
|
return outputs.length > 0 ? outputs.join('\n\n') : null;
|
|
52
47
|
}
|
|
53
48
|
case 'subagent':
|
|
@@ -28,14 +28,18 @@ export function withCurrentText(rows: TimelineRow[], items: Record<string, ChatI
|
|
|
28
28
|
if (!items) {
|
|
29
29
|
return rows;
|
|
30
30
|
}
|
|
31
|
+
|
|
31
32
|
return rows.map((row) => {
|
|
32
33
|
const item = items[row.id];
|
|
34
|
+
|
|
33
35
|
if (row.kind === 'assistant' && item?.kind === 'assistant') {
|
|
34
36
|
return { ...row, item };
|
|
35
37
|
}
|
|
38
|
+
|
|
36
39
|
if (row.kind === 'thinking' && item?.kind === 'thinking') {
|
|
37
40
|
return { ...row, item };
|
|
38
41
|
}
|
|
42
|
+
|
|
39
43
|
return row;
|
|
40
44
|
});
|
|
41
45
|
}
|
|
@@ -48,6 +52,7 @@ export function readTimelineTarget(element: HTMLElement, scroller: HTMLElement |
|
|
|
48
52
|
const column = positiveInteger(file?.dataset.fileColumn);
|
|
49
53
|
const endLine = positiveInteger(file?.dataset.fileEndLine);
|
|
50
54
|
const path = file?.dataset.filePath ?? null;
|
|
55
|
+
|
|
51
56
|
return {
|
|
52
57
|
selection: selectionWithin(scroller),
|
|
53
58
|
row: rows.find((row) => row.id === id) ?? null,
|
|
@@ -73,6 +78,7 @@ export function readTimelineTarget(element: HTMLElement, scroller: HTMLElement |
|
|
|
73
78
|
|
|
74
79
|
function positiveInteger(value: string | undefined): number | null {
|
|
75
80
|
const number = Number(value);
|
|
81
|
+
|
|
76
82
|
return Number.isSafeInteger(number) && number > 0 ? number : null;
|
|
77
83
|
}
|
|
78
84
|
|
|
@@ -87,31 +93,40 @@ export function uiReplyJump(
|
|
|
87
93
|
hasEarlier: boolean
|
|
88
94
|
): UiReplyJump {
|
|
89
95
|
const item = items?.[target.itemId];
|
|
96
|
+
|
|
90
97
|
if (!item) {
|
|
91
98
|
return { kind: hasEarlier ? 'earlier' : 'missing' };
|
|
92
99
|
}
|
|
100
|
+
|
|
93
101
|
if (item.kind !== 'assistant' || !item.ui?.some((block) => block.id === target.blockId && block.revision === target.revision)) {
|
|
94
102
|
return { kind: 'missing' };
|
|
95
103
|
}
|
|
104
|
+
|
|
96
105
|
const index = rows.findIndex((row) => row.kind === 'assistant' && row.id === target.itemId);
|
|
106
|
+
|
|
97
107
|
if (index !== -1) {
|
|
98
108
|
return { kind: 'row', index };
|
|
99
109
|
}
|
|
110
|
+
|
|
100
111
|
return item.turnId && !expandedTurns.has(item.turnId) ? { kind: 'turn', turnId: item.turnId } : { kind: 'missing' };
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
export function revealUiReplyBlock(scroller: HTMLElement, target: UiReplyTarget): boolean {
|
|
104
115
|
// Compare attributes instead of interpolating provider ids into CSS selectors.
|
|
105
116
|
const row = Array.from(scroller.querySelectorAll<HTMLElement>('[data-item-id]')).find((element) => element.dataset.itemId === target.itemId);
|
|
117
|
+
|
|
106
118
|
const block =
|
|
107
119
|
row &&
|
|
108
120
|
Array.from(row.querySelectorAll<HTMLElement>('[data-ui-block]')).find(
|
|
109
121
|
(element) => element.dataset.uiBlock === target.blockId && element.dataset.uiRevision === target.revision
|
|
110
122
|
);
|
|
123
|
+
|
|
111
124
|
if (!block) {
|
|
112
125
|
return false;
|
|
113
126
|
}
|
|
127
|
+
|
|
114
128
|
scroller.scrollTop += block.getBoundingClientRect().top - scroller.getBoundingClientRect().top;
|
|
115
129
|
block.focus({ preventScroll: true });
|
|
130
|
+
|
|
116
131
|
return true;
|
|
117
132
|
}
|