@adecore/agents-react 0.44.4 → 0.45.1
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/chat/logic/timeline.d.ts +2 -0
- package/dist/chat/logic/timeline.js +16 -6
- package/dist/chat/ui/Composer.js +345 -327
- package/dist/chat/ui/SubagentTimeline.js +4 -1
- package/dist/chat/ui/Timeline.js +5 -3
- package/dist/chat/ui/rows/Rows.js +58 -56
- package/dist/chat/ui/rows/SubagentRow.js +88 -68
- package/dist/chat/ui/rows/WorkRows.d.ts +2 -1
- package/dist/chat/ui/rows/WorkRows.js +116 -83
- package/dist/host-hooks.d.ts +1 -0
- package/dist/host-hooks.js +3 -0
- package/dist/host.d.ts +8 -0
- package/dist/host.js +1 -0
- package/dist/index.d.ts +1 -1
- package/package.json +8 -8
- package/src/chat/logic/timeline.ts +21 -7
- package/src/chat/ui/Composer.tsx +1 -0
- package/src/chat/ui/SubagentTimeline.tsx +5 -1
- package/src/chat/ui/Timeline.tsx +6 -3
- package/src/chat/ui/rows/Rows.tsx +10 -1
- package/src/chat/ui/rows/SubagentRow.tsx +12 -3
- package/src/chat/ui/rows/WorkRows.tsx +22 -11
- package/src/host-hooks.ts +4 -0
- package/src/host.ts +14 -0
- package/src/index.ts +1 -0
|
@@ -1570,133 +1570,166 @@ function _temp4(tool) {
|
|
|
1570
1570
|
return tool.state === "error";
|
|
1571
1571
|
}
|
|
1572
1572
|
export function TurnFoldRow(t0) {
|
|
1573
|
-
const $ = _c(
|
|
1573
|
+
const $ = _c(39);
|
|
1574
1574
|
const {
|
|
1575
1575
|
turn,
|
|
1576
1576
|
label,
|
|
1577
1577
|
work,
|
|
1578
1578
|
expanded,
|
|
1579
|
+
expandable,
|
|
1579
1580
|
onToggle
|
|
1580
1581
|
} = t0;
|
|
1581
1582
|
const {
|
|
1582
1583
|
commandLooks
|
|
1583
1584
|
} = useChatHost();
|
|
1585
|
+
let className;
|
|
1584
1586
|
let t1;
|
|
1585
1587
|
let t2;
|
|
1586
1588
|
let t3;
|
|
1587
|
-
|
|
1588
|
-
let t5;
|
|
1589
|
-
let t6;
|
|
1590
|
-
if ($[0] !== commandLooks || $[1] !== expanded || $[2] !== label || $[3] !== onToggle || $[4] !== turn.state || $[5] !== work) {
|
|
1589
|
+
if ($[0] !== commandLooks || $[1] !== expandable || $[2] !== expanded || $[3] !== label || $[4] !== turn.state || $[5] !== work) {
|
|
1591
1590
|
const brands = commandLooks?.flatMap(_temp5) ?? [];
|
|
1592
|
-
const
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1591
|
+
const t4 = expandable && "hover:bg-surface-hover";
|
|
1592
|
+
const t5 = turn.state === "error" && "text-status-error";
|
|
1593
|
+
let t6;
|
|
1594
|
+
if ($[10] !== t4 || $[11] !== t5) {
|
|
1595
|
+
t6 = clsx("group group/line -mx-1 mb-2 flex h-7 w-[calc(100%+8px)] min-w-0 items-center gap-2 rounded-md px-1 text-left text-xs text-text-muted", t4, t5);
|
|
1596
|
+
$[10] = t4;
|
|
1597
|
+
$[11] = t5;
|
|
1598
|
+
$[12] = t6;
|
|
1597
1599
|
} else {
|
|
1598
|
-
|
|
1600
|
+
t6 = $[12];
|
|
1599
1601
|
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1602
|
+
className = t6;
|
|
1603
|
+
let t7;
|
|
1604
|
+
if ($[13] !== expandable || $[14] !== expanded) {
|
|
1605
|
+
t7 = expandable && /*#__PURE__*/_jsx(Icon, {
|
|
1606
|
+
icon: ChevronRight,
|
|
1607
|
+
size: 12,
|
|
1608
|
+
className: clsx("transition-transform", expanded && "rotate-90")
|
|
1609
|
+
});
|
|
1610
|
+
$[13] = expandable;
|
|
1611
|
+
$[14] = expanded;
|
|
1612
|
+
$[15] = t7;
|
|
1608
1613
|
} else {
|
|
1609
|
-
|
|
1614
|
+
t7 = $[15];
|
|
1610
1615
|
}
|
|
1611
|
-
if ($[16] !==
|
|
1612
|
-
|
|
1616
|
+
if ($[16] !== t7) {
|
|
1617
|
+
t1 = /*#__PURE__*/_jsx("span", {
|
|
1613
1618
|
className: ROW_GUTTER,
|
|
1614
|
-
children:
|
|
1615
|
-
icon: ChevronRight,
|
|
1616
|
-
size: 12,
|
|
1617
|
-
className: t9
|
|
1618
|
-
})
|
|
1619
|
+
children: t7
|
|
1619
1620
|
});
|
|
1620
|
-
$[16] =
|
|
1621
|
-
$[17] =
|
|
1621
|
+
$[16] = t7;
|
|
1622
|
+
$[17] = t1;
|
|
1622
1623
|
} else {
|
|
1623
|
-
|
|
1624
|
+
t1 = $[17];
|
|
1624
1625
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1626
|
+
const t8 = expandable && "group-hover:text-text";
|
|
1627
|
+
let t9;
|
|
1628
|
+
if ($[18] !== t8) {
|
|
1629
|
+
t9 = clsx("shrink-0", t8);
|
|
1630
|
+
$[18] = t8;
|
|
1631
|
+
$[19] = t9;
|
|
1632
|
+
} else {
|
|
1633
|
+
t9 = $[19];
|
|
1634
|
+
}
|
|
1635
|
+
if ($[20] !== label || $[21] !== t9) {
|
|
1636
|
+
t2 = /*#__PURE__*/_jsx("span", {
|
|
1637
|
+
className: t9,
|
|
1628
1638
|
children: label
|
|
1629
1639
|
});
|
|
1630
|
-
$[
|
|
1631
|
-
$[
|
|
1640
|
+
$[20] = label;
|
|
1641
|
+
$[21] = t9;
|
|
1642
|
+
$[22] = t2;
|
|
1632
1643
|
} else {
|
|
1633
|
-
|
|
1644
|
+
t2 = $[22];
|
|
1634
1645
|
}
|
|
1635
|
-
|
|
1646
|
+
t3 = work.length > 0 && /*#__PURE__*/_jsx("span", {
|
|
1636
1647
|
className: "min-w-0 truncate text-text-faint",
|
|
1637
1648
|
children: work.map((part, index) => /*#__PURE__*/_jsxs(Fragment, {
|
|
1638
1649
|
children: [" \xB7 ", withBrand(part, brands)]
|
|
1639
1650
|
}, index))
|
|
1640
1651
|
});
|
|
1641
1652
|
$[0] = commandLooks;
|
|
1642
|
-
$[1] =
|
|
1643
|
-
$[2] =
|
|
1644
|
-
$[3] =
|
|
1653
|
+
$[1] = expandable;
|
|
1654
|
+
$[2] = expanded;
|
|
1655
|
+
$[3] = label;
|
|
1645
1656
|
$[4] = turn.state;
|
|
1646
1657
|
$[5] = work;
|
|
1647
|
-
$[6] =
|
|
1648
|
-
$[7] =
|
|
1649
|
-
$[8] =
|
|
1650
|
-
$[9] =
|
|
1651
|
-
$[10] = t5;
|
|
1652
|
-
$[11] = t6;
|
|
1658
|
+
$[6] = className;
|
|
1659
|
+
$[7] = t1;
|
|
1660
|
+
$[8] = t2;
|
|
1661
|
+
$[9] = t3;
|
|
1653
1662
|
} else {
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
t5 = $[10];
|
|
1659
|
-
t6 = $[11];
|
|
1663
|
+
className = $[6];
|
|
1664
|
+
t1 = $[7];
|
|
1665
|
+
t2 = $[8];
|
|
1666
|
+
t3 = $[9];
|
|
1660
1667
|
}
|
|
1661
|
-
let
|
|
1662
|
-
if ($[
|
|
1663
|
-
|
|
1668
|
+
let t4;
|
|
1669
|
+
if ($[23] === Symbol.for("react.memo_cache_sentinel")) {
|
|
1670
|
+
t4 = /*#__PURE__*/_jsx("span", {
|
|
1664
1671
|
className: "grow"
|
|
1665
1672
|
});
|
|
1666
|
-
$[
|
|
1673
|
+
$[23] = t4;
|
|
1667
1674
|
} else {
|
|
1668
|
-
|
|
1675
|
+
t4 = $[23];
|
|
1669
1676
|
}
|
|
1670
|
-
let
|
|
1671
|
-
if ($[
|
|
1672
|
-
|
|
1677
|
+
let t5;
|
|
1678
|
+
if ($[24] !== turn.createdAt) {
|
|
1679
|
+
t5 = /*#__PURE__*/_jsx(RowTime, {
|
|
1673
1680
|
at: turn.createdAt
|
|
1674
1681
|
});
|
|
1675
|
-
$[
|
|
1676
|
-
$[
|
|
1682
|
+
$[24] = turn.createdAt;
|
|
1683
|
+
$[25] = t5;
|
|
1677
1684
|
} else {
|
|
1678
|
-
|
|
1685
|
+
t5 = $[25];
|
|
1679
1686
|
}
|
|
1680
|
-
let
|
|
1681
|
-
if ($[
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
$[
|
|
1689
|
-
$[
|
|
1690
|
-
|
|
1691
|
-
$[
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1687
|
+
let t6;
|
|
1688
|
+
if ($[26] !== t1 || $[27] !== t2 || $[28] !== t3 || $[29] !== t5) {
|
|
1689
|
+
t6 = /*#__PURE__*/_jsxs(_Fragment, {
|
|
1690
|
+
children: [t1, t2, t3, t4, t5]
|
|
1691
|
+
});
|
|
1692
|
+
$[26] = t1;
|
|
1693
|
+
$[27] = t2;
|
|
1694
|
+
$[28] = t3;
|
|
1695
|
+
$[29] = t5;
|
|
1696
|
+
$[30] = t6;
|
|
1697
|
+
} else {
|
|
1698
|
+
t6 = $[30];
|
|
1699
|
+
}
|
|
1700
|
+
const content = t6;
|
|
1701
|
+
if (!expandable) {
|
|
1702
|
+
let t7;
|
|
1703
|
+
if ($[31] !== className || $[32] !== content) {
|
|
1704
|
+
t7 = /*#__PURE__*/_jsx("div", {
|
|
1705
|
+
className: className,
|
|
1706
|
+
children: content
|
|
1707
|
+
});
|
|
1708
|
+
$[31] = className;
|
|
1709
|
+
$[32] = content;
|
|
1710
|
+
$[33] = t7;
|
|
1711
|
+
} else {
|
|
1712
|
+
t7 = $[33];
|
|
1713
|
+
}
|
|
1714
|
+
return t7;
|
|
1715
|
+
}
|
|
1716
|
+
let t7;
|
|
1717
|
+
if ($[34] !== className || $[35] !== content || $[36] !== expanded || $[37] !== onToggle) {
|
|
1718
|
+
t7 = /*#__PURE__*/_jsx("button", {
|
|
1719
|
+
className: className,
|
|
1720
|
+
"aria-expanded": expanded,
|
|
1721
|
+
onClick: onToggle,
|
|
1722
|
+
children: content
|
|
1723
|
+
});
|
|
1724
|
+
$[34] = className;
|
|
1725
|
+
$[35] = content;
|
|
1726
|
+
$[36] = expanded;
|
|
1727
|
+
$[37] = onToggle;
|
|
1728
|
+
$[38] = t7;
|
|
1729
|
+
} else {
|
|
1730
|
+
t7 = $[38];
|
|
1731
|
+
}
|
|
1732
|
+
return t7;
|
|
1700
1733
|
}
|
|
1701
1734
|
function _temp5(t0) {
|
|
1702
1735
|
const {
|
package/dist/host-hooks.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare function useHostTask(...args: Parameters<ChatHost['tasks']['useTa
|
|
|
12
12
|
export declare function useHostExtraPrompts(...args: Parameters<ChatHost['prompts']['useExtra']>): ReturnType<ChatHost['prompts']['useExtra']>;
|
|
13
13
|
export declare function useHostStreaming(): ReturnType<ChatHost['useStreaming']>;
|
|
14
14
|
export declare function useHostSendDelivery(): ReturnType<ChatHost['useSendDelivery']>;
|
|
15
|
+
export declare function useHostHidesToolCalls(...args: Parameters<ChatHost['useHidesToolCalls']>): ReturnType<ChatHost['useHidesToolCalls']>;
|
|
15
16
|
export declare function useHostTimelineFind(...args: Parameters<ChatHost['useTimelineFind']>): ReturnType<ChatHost['useTimelineFind']>;
|
|
16
17
|
export declare function useHostReferableChats(): ReturnType<ChatHost['useReferableChats']>;
|
|
17
18
|
export declare function useHostReferables(scopeId: string, chatId: string): Referables;
|
package/dist/host-hooks.js
CHANGED
|
@@ -26,6 +26,9 @@ export function useHostStreaming() {
|
|
|
26
26
|
export function useHostSendDelivery() {
|
|
27
27
|
return useChatHost().useSendDelivery();
|
|
28
28
|
}
|
|
29
|
+
export function useHostHidesToolCalls(...args) {
|
|
30
|
+
return useChatHost().useHidesToolCalls(...args);
|
|
31
|
+
}
|
|
29
32
|
export function useHostTimelineFind(...args) {
|
|
30
33
|
return useChatHost().useTimelineFind(...args);
|
|
31
34
|
}
|
package/dist/host.d.ts
CHANGED
|
@@ -111,6 +111,12 @@ export interface ComposerSlotProps {
|
|
|
111
111
|
disabled: boolean;
|
|
112
112
|
insert(text: string): void;
|
|
113
113
|
}
|
|
114
|
+
export interface ComposerHeaderSlotProps {
|
|
115
|
+
scopeId: string;
|
|
116
|
+
chatId: string;
|
|
117
|
+
disabled: boolean;
|
|
118
|
+
focused: boolean;
|
|
119
|
+
}
|
|
114
120
|
export interface ComposerSendDraft {
|
|
115
121
|
scopeId: string;
|
|
116
122
|
chatId: string;
|
|
@@ -252,6 +258,7 @@ export interface ChatHost {
|
|
|
252
258
|
};
|
|
253
259
|
useStreaming(): 'words' | 'blocks' | 'whole';
|
|
254
260
|
useSendDelivery(): 'queue' | 'steer';
|
|
261
|
+
useHidesToolCalls(scopeId: string, chatId: string): boolean;
|
|
255
262
|
fileLinks: {
|
|
256
263
|
target(text: string, cwd: string | null, scopeId?: string | null): FileRef | null;
|
|
257
264
|
open(cwd: string | null, ref: FileRef, scopeId?: string | null): void;
|
|
@@ -306,6 +313,7 @@ export interface ChatHost {
|
|
|
306
313
|
useResumeAtReset(scopeId: string): boolean;
|
|
307
314
|
useComposerPlaceholder(scopeId: string, chatId: string): string | null;
|
|
308
315
|
ComposerSlot: ComponentType<ComposerSlotProps> | null;
|
|
316
|
+
ComposerHeaderSlot?: ComponentType<ComposerHeaderSlotProps> | null;
|
|
309
317
|
useComposerSendActions?(scopeId: string, chatId: string): readonly ComposerSendAction[];
|
|
310
318
|
SubagentSlot: ComponentType<SubagentSlotProps> | null;
|
|
311
319
|
useThreadCards(scopeId: string, chatId: string): readonly ThreadCard[];
|
package/dist/host.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type ChatActions, type FileRef, type ShellCodeBlockContext, type ChatPlace, type SubagentTask, type TimelineFindOptions, type TimelineFind, type ComposerDictationProps, type ComposerSlotProps, type ComposerSendAction, type ComposerSendDraft, type SubagentSlotProps, type ThreadCard, type VisualHost, type HostMenuAction, type ReplyAuthor, type ChatHost, type UiMapSource, type UiModelViewProps, type CommandLook, type CommandLookMatch, type CommandName, ChatHostProvider, useChatHost, type AccentChoice, type ChatToast, type TextareaProps, type ResourceUrl } from './host.js';
|
|
1
|
+
export { type ChatActions, type FileRef, type ShellCodeBlockContext, type ChatPlace, type SubagentTask, type TimelineFindOptions, type TimelineFind, type ComposerDictationProps, type ComposerHeaderSlotProps, type ComposerSlotProps, type ComposerSendAction, type ComposerSendDraft, type SubagentSlotProps, type ThreadCard, type VisualHost, type HostMenuAction, type ReplyAuthor, type ChatHost, type UiMapSource, type UiModelViewProps, type CommandLook, type CommandLookMatch, type CommandName, ChatHostProvider, useChatHost, type AccentChoice, type ChatToast, type TextareaProps, type ResourceUrl } from './host.js';
|
|
2
2
|
export { type ChatScope, ChatScopeContext, useChatScope } from './scope.js';
|
|
3
3
|
export { type ChatRequestMap, type ChatEventMap, type ChatTransportStatus, type ChatTransport, ChatTransportError, errorCode, isConnectionError } from './transport.js';
|
|
4
4
|
export { portTransport, type PortTransport } from './port-transport.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adecore/agents-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.1",
|
|
4
4
|
"description": "React agent chat threads, composers, approvals, provider accounts and usage views.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -72,11 +72,11 @@
|
|
|
72
72
|
"test:visuals": "bun --config=integration.bunfig.toml --conditions=source test ./src/chat/visual-viewport.integration.test.ts"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@adecore/agent-contracts": "0.
|
|
76
|
-
"@adecore/editor": "0.
|
|
77
|
-
"@adecore/editor-react": "0.
|
|
78
|
-
"@adecore/intelligent-ui": "0.
|
|
79
|
-
"@adecore/merge": "0.
|
|
75
|
+
"@adecore/agent-contracts": "0.45.1",
|
|
76
|
+
"@adecore/editor": "0.45.1",
|
|
77
|
+
"@adecore/editor-react": "0.45.1",
|
|
78
|
+
"@adecore/intelligent-ui": "0.45.1",
|
|
79
|
+
"@adecore/merge": "0.45.1",
|
|
80
80
|
"@codemirror/commands": "^6.11.1",
|
|
81
81
|
"@codemirror/language": "^6.13.1",
|
|
82
82
|
"@codemirror/state": "^6.7.6",
|
|
@@ -100,14 +100,14 @@
|
|
|
100
100
|
"zustand": "^5.0.16"
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
103
|
-
"@adecore/ui": "^0.
|
|
103
|
+
"@adecore/ui": "^0.45.1",
|
|
104
104
|
"i18next": "^26.4.2",
|
|
105
105
|
"react": "^19.3.0",
|
|
106
106
|
"react-dom": "^19.3.0",
|
|
107
107
|
"react-i18next": "^17.0.15"
|
|
108
108
|
},
|
|
109
109
|
"devDependencies": {
|
|
110
|
-
"@adecore/ui": "0.
|
|
110
|
+
"@adecore/ui": "0.45.1",
|
|
111
111
|
"@types/bun": "^1.4.4",
|
|
112
112
|
"@types/react": "^19.3.0",
|
|
113
113
|
"@types/react-dom": "^19.3.0",
|
|
@@ -67,7 +67,7 @@ export type TimelineRow =
|
|
|
67
67
|
// Set when `diff` holds back the diffs past its first few, which the host answers whole.
|
|
68
68
|
heldBack?: boolean;
|
|
69
69
|
}
|
|
70
|
-
| { kind: 'turn-fold'; id: string; turn: ChatTurnItem; label: string; work: string[]; hiddenCount: number; expanded: boolean }
|
|
70
|
+
| { kind: 'turn-fold'; id: string; turn: ChatTurnItem; label: string; work: string[]; hiddenCount: number; expanded: boolean; expandable: boolean }
|
|
71
71
|
| { kind: 'forks'; id: string; turnId: string }
|
|
72
72
|
| { kind: 'working'; id: string; startedAt: number }
|
|
73
73
|
// A card of the app's own (`thread-cards.ts`), never derived from an item.
|
|
@@ -84,6 +84,15 @@ interface TimelineOptions {
|
|
|
84
84
|
forkedTurns?: ReadonlySet<string>;
|
|
85
85
|
/* The host's programs drawn as tools of their own, whose runs read as their own sentences. */
|
|
86
86
|
commandLooks?: readonly CommandLook[];
|
|
87
|
+
/* Leaves out the tool lines and their groups, while a settled turn still says how long it worked. */
|
|
88
|
+
hideTools?: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const TOOL_KINDS = new Set<TimelineRow['kind']>(['work', 'work-group', 'work-live']);
|
|
92
|
+
|
|
93
|
+
// A generated image is drawn as a tool call but reads as the turn's result, so it stays.
|
|
94
|
+
function shownRows(rows: TimelineRow[], options: TimelineOptions): TimelineRow[] {
|
|
95
|
+
return options.hideTools === true ? rows.filter((row) => !TOOL_KINDS.has(row.kind) || isImageRow(row)) : rows;
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
/*
|
|
@@ -599,7 +608,7 @@ export function deriveTimelineRows(items: ChatItem[], options: TimelineOptions):
|
|
|
599
608
|
|
|
600
609
|
for (const chunk of chunksByTurn(items)) {
|
|
601
610
|
if (chunk.turnId === null) {
|
|
602
|
-
rows.push(...rowsForItems(chunk.items, options, children));
|
|
611
|
+
rows.push(...shownRows(rowsForItems(chunk.items, options, children), options));
|
|
603
612
|
|
|
604
613
|
continue;
|
|
605
614
|
}
|
|
@@ -616,7 +625,8 @@ export function deriveTimelineRows(items: ChatItem[], options: TimelineOptions):
|
|
|
616
625
|
const active = turnId === options.activeTurnId || turn?.state === 'running';
|
|
617
626
|
|
|
618
627
|
if (!turn || active) {
|
|
619
|
-
|
|
628
|
+
// Without its call, a thought has nothing to stand over and stays where it was thought.
|
|
629
|
+
const live = shownRows(rowsForItems(conversation, options, children), options);
|
|
620
630
|
rows.push(...(active ? withLiveThought(live, conversation) : live));
|
|
621
631
|
|
|
622
632
|
if (active) {
|
|
@@ -628,7 +638,7 @@ export function deriveTimelineRows(items: ChatItem[], options: TimelineOptions):
|
|
|
628
638
|
|
|
629
639
|
// Steering splits the conversation inside a turn. Earlier responses keep their place above it.
|
|
630
640
|
const lastUser = conversation.findLastIndex((item) => item.kind === 'user');
|
|
631
|
-
rows.push(...rowsForItems(conversation.slice(0, lastUser + 1), options, children));
|
|
641
|
+
rows.push(...shownRows(rowsForItems(conversation.slice(0, lastUser + 1), options, children), options));
|
|
632
642
|
const rest = conversation.slice(lastUser + 1);
|
|
633
643
|
const work = rowsForItems(rest, options, children);
|
|
634
644
|
// The closing answer stays visible; the work after the last input folds behind the label.
|
|
@@ -642,7 +652,9 @@ export function deriveTimelineRows(items: ChatItem[], options: TimelineOptions):
|
|
|
642
652
|
);
|
|
643
653
|
|
|
644
654
|
const folded = before.length - standing.length;
|
|
645
|
-
|
|
655
|
+
// With the tool lines left out, a turn that only ran tools has nothing behind its fold to open.
|
|
656
|
+
const expandable = shownRows(before, options).some((row) => !standing.includes(row));
|
|
657
|
+
const expanded = expandable && options.expandedTurns.has(turnId);
|
|
646
658
|
|
|
647
659
|
if (folded > 0) {
|
|
648
660
|
const tools = rest.filter(
|
|
@@ -656,11 +668,13 @@ export function deriveTimelineRows(items: ChatItem[], options: TimelineOptions):
|
|
|
656
668
|
label: turnLabel(turn, chunk.items),
|
|
657
669
|
work: summarizeTurn(tools, options.commandLooks),
|
|
658
670
|
hiddenCount: folded,
|
|
659
|
-
expanded
|
|
671
|
+
expanded,
|
|
672
|
+
expandable
|
|
660
673
|
});
|
|
661
674
|
}
|
|
662
675
|
|
|
663
|
-
|
|
676
|
+
// The fold counts the tool lines it would hide, so a turn of only tools still says how long it worked.
|
|
677
|
+
rows.push(...(expanded ? shownRows(before, options) : standing));
|
|
664
678
|
const changed = changedFilesRow(turn, conversation);
|
|
665
679
|
|
|
666
680
|
if (changed) {
|
package/src/chat/ui/Composer.tsx
CHANGED
|
@@ -247,6 +247,7 @@ export function Composer({ chatId, info, focused, answerPromptsElsewhere, disabl
|
|
|
247
247
|
)}
|
|
248
248
|
{...files.dropTarget}
|
|
249
249
|
>
|
|
250
|
+
{host.ComposerHeaderSlot && <host.ComposerHeaderSlot scopeId={scope.id} chatId={chatId} disabled={!writable} focused={focused} />}
|
|
250
251
|
<PromptComposer
|
|
251
252
|
chatId={chatId}
|
|
252
253
|
pending={pending}
|
|
@@ -18,6 +18,7 @@ import { useChatRow } from '../../state/chats';
|
|
|
18
18
|
import { useChatPreferences } from '../preferences';
|
|
19
19
|
import { readingWidthStyle } from '../reading-width';
|
|
20
20
|
import { useChatHost } from '../../host';
|
|
21
|
+
import { useHostHidesToolCalls } from '../../host-hooks';
|
|
21
22
|
import { useChatScope } from '../../scope';
|
|
22
23
|
import { EmptyState, ContextMenu, Icon } from '@adecore/ui';
|
|
23
24
|
|
|
@@ -67,12 +68,15 @@ export function SubagentTimeline({ chatId, toolUseId }: { chatId: string; toolUs
|
|
|
67
68
|
}
|
|
68
69
|
}, [state.unsupported, scope]);
|
|
69
70
|
|
|
71
|
+
const hideTools = useHostHidesToolCalls(scope.id, chatId);
|
|
72
|
+
|
|
70
73
|
const rows = deriveTimelineRows(state.items, {
|
|
71
74
|
expandedGroups: groups.ids,
|
|
72
75
|
expandedTurns: NO_TURNS,
|
|
73
76
|
expandedSubagents: subagents.ids,
|
|
74
77
|
activeTurnId: null,
|
|
75
|
-
commandLooks
|
|
78
|
+
commandLooks,
|
|
79
|
+
hideTools
|
|
76
80
|
});
|
|
77
81
|
|
|
78
82
|
useLayoutEffect(() => {
|
package/src/chat/ui/Timeline.tsx
CHANGED
|
@@ -80,7 +80,7 @@ import { FileLinkContext } from './file-links';
|
|
|
80
80
|
import { AgentIcon } from '../../agents/AgentIcon';
|
|
81
81
|
import { useModelName } from '../../agents/model-name';
|
|
82
82
|
import { ContextMenu, EmptyState, ErrorBoundary, Icon, SectionLabel } from '@adecore/ui';
|
|
83
|
-
import { useHostContextSources, useHostHidesFolder, useHostThreadCards, useHostTimelineFind, useHostWelcome } from '../../host-hooks';
|
|
83
|
+
import { useHostContextSources, useHostHidesFolder, useHostHidesToolCalls, useHostThreadCards, useHostTimelineFind, useHostWelcome } from '../../host-hooks';
|
|
84
84
|
|
|
85
85
|
const ESTIMATED_ROW_PX = 56;
|
|
86
86
|
// How long a jump waits for a thread that is still loading before it calls the item missing.
|
|
@@ -273,6 +273,8 @@ export function Timeline({
|
|
|
273
273
|
});
|
|
274
274
|
}, [scope, chatId, cursor]);
|
|
275
275
|
|
|
276
|
+
const hideTools = useHostHidesToolCalls(scopeId, chatId);
|
|
277
|
+
|
|
276
278
|
const threadRows = useMemo(() => {
|
|
277
279
|
if (!order || !items) {
|
|
278
280
|
return [];
|
|
@@ -286,10 +288,11 @@ export function Timeline({
|
|
|
286
288
|
expandedSubagents: subagents.ids,
|
|
287
289
|
activeTurnId,
|
|
288
290
|
forkedTurns,
|
|
289
|
-
commandLooks: host.commandLooks
|
|
291
|
+
commandLooks: host.commandLooks,
|
|
292
|
+
hideTools
|
|
290
293
|
}
|
|
291
294
|
);
|
|
292
|
-
}, [order, items, groups.ids, turns.ids, subagents.ids, activeTurnId, forkedTurns, host.commandLooks]);
|
|
295
|
+
}, [order, items, groups.ids, turns.ids, subagents.ids, activeTurnId, forkedTurns, host.commandLooks, hideTools]);
|
|
293
296
|
|
|
294
297
|
const cards = useHostThreadCards(scopeId, chatId);
|
|
295
298
|
const drawsVisuals = host.visuals !== null;
|
|
@@ -78,7 +78,16 @@ export function Row({ row, chatId, toggleGroup, toggleTurn, toggleSubagent, open
|
|
|
78
78
|
case 'subagent':
|
|
79
79
|
return <SubagentBranchRow chatId={chatId} branch={row} onToggle={toggleSubagent} onOpenConversation={openConversation} />;
|
|
80
80
|
case 'turn-fold':
|
|
81
|
-
return
|
|
81
|
+
return (
|
|
82
|
+
<TurnFoldRow
|
|
83
|
+
turn={row.turn}
|
|
84
|
+
label={row.label}
|
|
85
|
+
work={row.work}
|
|
86
|
+
expanded={row.expanded}
|
|
87
|
+
expandable={row.expandable}
|
|
88
|
+
onToggle={() => toggleTurn(row.turn.id)}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
82
91
|
case 'forks':
|
|
83
92
|
return <ForksRow chatId={chatId} turnId={row.turnId} />;
|
|
84
93
|
case 'changed-files':
|
|
@@ -18,7 +18,7 @@ import { canOpenSubagent, crumbOf, type SubagentStep } from '../../subagent-view
|
|
|
18
18
|
import { type SubagentTask } from '../../../host';
|
|
19
19
|
import { useChatScope } from '../../../scope';
|
|
20
20
|
import { useCurrentItem } from '../../../state/chats';
|
|
21
|
-
import { useHostTask } from '../../../host-hooks';
|
|
21
|
+
import { useHostHidesToolCalls, useHostTask } from '../../../host-hooks';
|
|
22
22
|
|
|
23
23
|
// The work of a long-running agent scrolls inside its row instead of pushing the thread away.
|
|
24
24
|
const CHILDREN_MAX_PX = 320;
|
|
@@ -81,25 +81,34 @@ function StatusPill({ item, task }: { item: ChatSubagentItem; task: SubagentTask
|
|
|
81
81
|
/* What the sub-agent did, live: its own tool calls and the text it wrote, as ordinary rows. */
|
|
82
82
|
function SubagentWork({ chatId, item, work }: { chatId: string; item: ChatSubagentItem; work: ChatItem[] }) {
|
|
83
83
|
const { t } = useTranslation('agent-chat');
|
|
84
|
+
const { id } = useChatScope();
|
|
85
|
+
const hideTools = useHostHidesToolCalls(id, chatId);
|
|
84
86
|
const scroller = useRef<HTMLDivElement>(null);
|
|
85
87
|
const running = item.status === 'running';
|
|
88
|
+
// A generated image reads as a result, not as a tool line, so it stays when the lines go.
|
|
89
|
+
const shown = hideTools ? work.filter((child) => child.kind !== 'tool' || isImageGeneration(child)) : work;
|
|
86
90
|
|
|
87
91
|
useLayoutEffect(() => {
|
|
88
92
|
// While it works, the newest line is the one worth seeing.
|
|
89
93
|
if (running && scroller.current) {
|
|
90
94
|
scroller.current.scrollTop = scroller.current.scrollHeight;
|
|
91
95
|
}
|
|
92
|
-
}, [
|
|
96
|
+
}, [shown.length, running]);
|
|
93
97
|
|
|
94
98
|
if (work.length === 0) {
|
|
95
99
|
// A line inside a chat row, where the centered block of an EmptyState would outweigh the row itself.
|
|
96
100
|
return <div className="ml-6 pb-1 text-xs text-text-faint">{t('rows.subagent.nothingYet')}</div>;
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
// It did work, only none that is shown; "nothing yet" would say it did none.
|
|
104
|
+
if (shown.length === 0) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
99
108
|
return (
|
|
100
109
|
<div ref={scroller} className="ml-6 overflow-y-auto" style={{ maxHeight: CHILDREN_MAX_PX }}>
|
|
101
110
|
{item.itemsTruncated && <div className="pb-1 text-xs text-text-faint">{t('rows.subagent.truncated')}</div>}
|
|
102
|
-
{
|
|
111
|
+
{shown.map((child) => (
|
|
103
112
|
<ChildRow key={child.id} chatId={chatId} item={child} />
|
|
104
113
|
))}
|
|
105
114
|
</div>
|
|
@@ -561,30 +561,31 @@ export function TurnFoldRow({
|
|
|
561
561
|
label,
|
|
562
562
|
work,
|
|
563
563
|
expanded,
|
|
564
|
+
expandable,
|
|
564
565
|
onToggle
|
|
565
566
|
}: {
|
|
566
567
|
turn: ChatTurnItem;
|
|
567
568
|
label: string;
|
|
568
569
|
work: readonly string[];
|
|
569
570
|
expanded: boolean;
|
|
571
|
+
/* False when the fold hides nothing a person may see, so it stays a label. */
|
|
572
|
+
expandable: boolean;
|
|
570
573
|
onToggle(): void;
|
|
571
574
|
}) {
|
|
572
575
|
const { commandLooks } = useChatHost();
|
|
573
576
|
const brands = commandLooks?.flatMap(({ brand }) => (brand === undefined ? [] : [brand])) ?? [];
|
|
577
|
+
const className = clsx(
|
|
578
|
+
'group group/line -mx-1 mb-2 flex h-7 w-[calc(100%+8px)] min-w-0 items-center gap-2 rounded-md px-1 text-left text-xs text-text-muted',
|
|
579
|
+
expandable && 'hover:bg-surface-hover',
|
|
580
|
+
turn.state === 'error' && 'text-status-error'
|
|
581
|
+
);
|
|
574
582
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
className={clsx(
|
|
578
|
-
'group group/line -mx-1 mb-2 flex h-7 w-[calc(100%+8px)] min-w-0 items-center gap-2 rounded-md px-1 text-left text-xs text-text-muted hover:bg-surface-hover',
|
|
579
|
-
turn.state === 'error' && 'text-status-error'
|
|
580
|
-
)}
|
|
581
|
-
aria-expanded={expanded}
|
|
582
|
-
onClick={onToggle}
|
|
583
|
-
>
|
|
583
|
+
const content = (
|
|
584
|
+
<>
|
|
584
585
|
<span className={ROW_GUTTER}>
|
|
585
|
-
<Icon icon={ChevronRight} size={12} className={clsx('transition-transform', expanded && 'rotate-90')} />
|
|
586
|
+
{expandable && <Icon icon={ChevronRight} size={12} className={clsx('transition-transform', expanded && 'rotate-90')} />}
|
|
586
587
|
</span>
|
|
587
|
-
<span className=
|
|
588
|
+
<span className={clsx('shrink-0', expandable && 'group-hover:text-text')}>{label}</span>
|
|
588
589
|
{work.length > 0 && (
|
|
589
590
|
<span className="min-w-0 truncate text-text-faint">
|
|
590
591
|
{work.map((part, index) => (
|
|
@@ -597,6 +598,16 @@ export function TurnFoldRow({
|
|
|
597
598
|
)}
|
|
598
599
|
<span className="grow" />
|
|
599
600
|
<RowTime at={turn.createdAt} />
|
|
601
|
+
</>
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
if (!expandable) {
|
|
605
|
+
return <div className={className}>{content}</div>;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return (
|
|
609
|
+
<button className={className} aria-expanded={expanded} onClick={onToggle}>
|
|
610
|
+
{content}
|
|
600
611
|
</button>
|
|
601
612
|
);
|
|
602
613
|
}
|
package/src/host-hooks.ts
CHANGED
|
@@ -39,6 +39,10 @@ export function useHostSendDelivery(): ReturnType<ChatHost['useSendDelivery']> {
|
|
|
39
39
|
return useChatHost().useSendDelivery();
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
export function useHostHidesToolCalls(...args: Parameters<ChatHost['useHidesToolCalls']>): ReturnType<ChatHost['useHidesToolCalls']> {
|
|
43
|
+
return useChatHost().useHidesToolCalls(...args);
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
export function useHostTimelineFind(...args: Parameters<ChatHost['useTimelineFind']>): ReturnType<ChatHost['useTimelineFind']> {
|
|
43
47
|
return useChatHost().useTimelineFind(...args);
|
|
44
48
|
}
|