@steerable/agent-ui 0.2.3 → 0.2.4
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/cards/ActionSegmentCard.d.ts +18 -0
- package/dist/cards/ActionSegmentCard.d.ts.map +1 -0
- package/dist/cards/ActionSegmentCard.js +26 -0
- package/dist/cards/ActionSegmentCard.js.map +1 -0
- package/dist/cards/AnalysisDocumentCard.d.ts +33 -0
- package/dist/cards/AnalysisDocumentCard.d.ts.map +1 -0
- package/dist/cards/AnalysisDocumentCard.js +11 -0
- package/dist/cards/AnalysisDocumentCard.js.map +1 -0
- package/dist/cards/AskUserQuestionsCard.d.ts +43 -0
- package/dist/cards/AskUserQuestionsCard.d.ts.map +1 -0
- package/dist/cards/AskUserQuestionsCard.js +171 -0
- package/dist/cards/AskUserQuestionsCard.js.map +1 -0
- package/dist/cards/CoverageReportCard.d.ts +41 -0
- package/dist/cards/CoverageReportCard.d.ts.map +1 -0
- package/dist/cards/CoverageReportCard.js +55 -0
- package/dist/cards/CoverageReportCard.js.map +1 -0
- package/dist/cards/OrchestrationPlanCard.d.ts +60 -0
- package/dist/cards/OrchestrationPlanCard.d.ts.map +1 -0
- package/dist/cards/OrchestrationPlanCard.js +103 -0
- package/dist/cards/OrchestrationPlanCard.js.map +1 -0
- package/dist/cards/PlanSelectorCard.d.ts +40 -0
- package/dist/cards/PlanSelectorCard.d.ts.map +1 -0
- package/dist/cards/PlanSelectorCard.js +106 -0
- package/dist/cards/PlanSelectorCard.js.map +1 -0
- package/dist/cards/PlanStepsCard.d.ts +18 -0
- package/dist/cards/PlanStepsCard.d.ts.map +1 -0
- package/dist/cards/PlanStepsCard.js +25 -0
- package/dist/cards/PlanStepsCard.js.map +1 -0
- package/dist/cards/QuizCard.d.ts +44 -0
- package/dist/cards/QuizCard.d.ts.map +1 -0
- package/dist/cards/QuizCard.js +113 -0
- package/dist/cards/QuizCard.js.map +1 -0
- package/dist/cards/ResearchPlanCard.d.ts +23 -0
- package/dist/cards/ResearchPlanCard.d.ts.map +1 -0
- package/dist/cards/ResearchPlanCard.js +50 -0
- package/dist/cards/ResearchPlanCard.js.map +1 -0
- package/dist/cards/SearchSourcesCard.d.ts +29 -0
- package/dist/cards/SearchSourcesCard.d.ts.map +1 -0
- package/dist/cards/SearchSourcesCard.js +83 -0
- package/dist/cards/SearchSourcesCard.js.map +1 -0
- package/dist/cards/SuggestedRepliesCard.d.ts +18 -0
- package/dist/cards/SuggestedRepliesCard.d.ts.map +1 -0
- package/dist/cards/SuggestedRepliesCard.js +14 -0
- package/dist/cards/SuggestedRepliesCard.js.map +1 -0
- package/dist/cards/SummaryMessageCard.d.ts +40 -0
- package/dist/cards/SummaryMessageCard.d.ts.map +1 -0
- package/dist/cards/SummaryMessageCard.js +56 -0
- package/dist/cards/SummaryMessageCard.js.map +1 -0
- package/dist/cards/ThinkingProcessCard.d.ts +46 -0
- package/dist/cards/ThinkingProcessCard.d.ts.map +1 -0
- package/dist/cards/ThinkingProcessCard.js +79 -0
- package/dist/cards/ThinkingProcessCard.js.map +1 -0
- package/dist/cards/ToolExecutionCard.d.ts +21 -0
- package/dist/cards/ToolExecutionCard.d.ts.map +1 -0
- package/dist/cards/ToolExecutionCard.js +65 -0
- package/dist/cards/ToolExecutionCard.js.map +1 -0
- package/dist/cards/icons.d.ts +38 -0
- package/dist/cards/icons.d.ts.map +1 -0
- package/dist/cards/icons.js +87 -0
- package/dist/cards/icons.js.map +1 -0
- package/dist/cards/index.d.ts +38 -0
- package/dist/cards/index.d.ts.map +1 -0
- package/dist/cards/index.js +24 -0
- package/dist/cards/index.js.map +1 -0
- package/dist/cards/types.d.ts +24 -0
- package/dist/cards/types.d.ts.map +1 -0
- package/dist/cards/types.js +7 -0
- package/dist/cards/types.js.map +1 -0
- package/dist/components/AgentSelector.d.ts +23 -0
- package/dist/components/AgentSelector.d.ts.map +1 -0
- package/dist/components/AgentSelector.js +63 -0
- package/dist/components/AgentSelector.js.map +1 -0
- package/dist/components/AgentSelector.stories.d.ts +12 -0
- package/dist/components/AgentSelector.stories.d.ts.map +1 -0
- package/dist/components/AgentSelector.stories.js +95 -0
- package/dist/components/AgentSelector.stories.js.map +1 -0
- package/dist/components/ChatPanel.d.ts +138 -21
- package/dist/components/ChatPanel.d.ts.map +1 -1
- package/dist/components/ChatPanel.js +181 -26
- package/dist/components/ChatPanel.js.map +1 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +1 -0
- package/dist/components/index.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/state/ChatSessionProvider.d.ts +59 -0
- package/dist/state/ChatSessionProvider.d.ts.map +1 -0
- package/dist/state/ChatSessionProvider.js +71 -0
- package/dist/state/ChatSessionProvider.js.map +1 -0
- package/dist/state/MockChatStreamTransport.d.ts +54 -0
- package/dist/state/MockChatStreamTransport.d.ts.map +1 -0
- package/dist/state/MockChatStreamTransport.js +86 -0
- package/dist/state/MockChatStreamTransport.js.map +1 -0
- package/dist/state/MockChatStreamTransport.stories.d.ts +13 -0
- package/dist/state/MockChatStreamTransport.stories.d.ts.map +1 -0
- package/dist/state/MockChatStreamTransport.stories.js +60 -0
- package/dist/state/MockChatStreamTransport.stories.js.map +1 -0
- package/dist/state/bridgeLegacySSE.d.ts +42 -0
- package/dist/state/bridgeLegacySSE.d.ts.map +1 -0
- package/dist/state/bridgeLegacySSE.js +102 -0
- package/dist/state/bridgeLegacySSE.js.map +1 -0
- package/dist/state/index.d.ts +32 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +24 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/parseSSE.d.ts +50 -0
- package/dist/state/parseSSE.d.ts.map +1 -0
- package/dist/state/parseSSE.js +112 -0
- package/dist/state/parseSSE.js.map +1 -0
- package/dist/state/useChatComposer.d.ts +51 -0
- package/dist/state/useChatComposer.d.ts.map +1 -0
- package/dist/state/useChatComposer.js +82 -0
- package/dist/state/useChatComposer.js.map +1 -0
- package/dist/state/useChatList.d.ts +71 -0
- package/dist/state/useChatList.d.ts.map +1 -0
- package/dist/state/useChatList.js +158 -0
- package/dist/state/useChatList.js.map +1 -0
- package/dist/state/useChatSession.d.ts +29 -0
- package/dist/state/useChatSession.d.ts.map +1 -0
- package/dist/state/useChatSession.js +37 -0
- package/dist/state/useChatSession.js.map +1 -0
- package/dist/state/useScrollLock.d.ts +35 -0
- package/dist/state/useScrollLock.d.ts.map +1 -0
- package/dist/state/useScrollLock.js +74 -0
- package/dist/state/useScrollLock.js.map +1 -0
- package/dist/state/useToolCallStream.d.ts +38 -0
- package/dist/state/useToolCallStream.d.ts.map +1 -0
- package/dist/state/useToolCallStream.js +74 -0
- package/dist/state/useToolCallStream.js.map +1 -0
- package/package.json +10 -2
- package/src/cards/ActionSegmentCard.tsx +60 -0
- package/src/cards/AnalysisDocumentCard.tsx +81 -0
- package/src/cards/AskUserQuestionsCard.tsx +354 -0
- package/src/cards/CoverageReportCard.tsx +215 -0
- package/src/cards/OrchestrationPlanCard.tsx +328 -0
- package/src/cards/PlanSelectorCard.tsx +312 -0
- package/src/cards/PlanStepsCard.tsx +52 -0
- package/src/cards/QuizCard.tsx +309 -0
- package/src/cards/ResearchPlanCard.tsx +169 -0
- package/src/cards/SearchSourcesCard.tsx +244 -0
- package/src/cards/SuggestedRepliesCard.tsx +48 -0
- package/src/cards/SummaryMessageCard.tsx +145 -0
- package/src/cards/ThinkingProcessCard.tsx +155 -0
- package/src/cards/ToolExecutionCard.tsx +152 -0
- package/src/cards/cards.test.tsx +492 -0
- package/src/cards/icons.tsx +249 -0
- package/src/cards/index.ts +53 -0
- package/src/cards/types.ts +27 -0
- package/src/components/AgentSelector.stories.tsx +113 -0
- package/src/components/AgentSelector.test.tsx +70 -0
- package/src/components/AgentSelector.tsx +147 -0
- package/src/components/ChatPanel.test.tsx +130 -0
- package/src/components/ChatPanel.tsx +612 -138
- package/src/components/index.ts +13 -1
- package/src/index.ts +43 -0
- package/src/state/ChatSessionProvider.test.tsx +85 -0
- package/src/state/ChatSessionProvider.tsx +98 -0
- package/src/state/MockChatStreamTransport.stories.tsx +95 -0
- package/src/state/MockChatStreamTransport.test.ts +163 -0
- package/src/state/MockChatStreamTransport.ts +123 -0
- package/src/state/bridgeLegacySSE.test.ts +134 -0
- package/src/state/bridgeLegacySSE.ts +134 -0
- package/src/state/index.ts +82 -0
- package/src/state/parseSSE.test.ts +123 -0
- package/src/state/parseSSE.ts +123 -0
- package/src/state/useChatComposer.test.ts +145 -0
- package/src/state/useChatComposer.ts +117 -0
- package/src/state/useChatList.test.ts +135 -0
- package/src/state/useChatList.ts +217 -0
- package/src/state/useChatSession.test.ts +61 -0
- package/src/state/useChatSession.ts +64 -0
- package/src/state/useScrollLock.test.ts +99 -0
- package/src/state/useScrollLock.ts +105 -0
- package/src/state/useToolCallStream.test.ts +86 -0
- package/src/state/useToolCallStream.ts +110 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `useScrollLock` — sticky-bottom scrolling for streaming message lists.
|
|
3
|
+
*
|
|
4
|
+
* The hook returns a callback ref to attach to the scroll container and a
|
|
5
|
+
* `scrollToBottom` imperative API. Behaviour:
|
|
6
|
+
* - When the container's scroll position is within `threshold` px of the
|
|
7
|
+
* bottom, the hook treats the user as "anchored" and re-runs
|
|
8
|
+
* `scrollToBottom` whenever the dependency array `revalidateKeys` changes.
|
|
9
|
+
* - As soon as the user scrolls up past `threshold`, the lock releases —
|
|
10
|
+
* the list will no longer auto-scroll until the user scrolls back to the
|
|
11
|
+
* bottom (or `scrollToBottom` is called explicitly, e.g. on send).
|
|
12
|
+
*
|
|
13
|
+
* Uses a callback ref so the listener attaches the moment the element mounts,
|
|
14
|
+
* not after a second effect pass. Matches deeppath's MessageList scroll
|
|
15
|
+
* handling minus the inline MutationObserver+IntersectionObserver soup.
|
|
16
|
+
*/
|
|
17
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
18
|
+
function maxScrollTop(el) {
|
|
19
|
+
return Math.max(0, el.scrollHeight - el.clientHeight);
|
|
20
|
+
}
|
|
21
|
+
export function useScrollLock(options = {}) {
|
|
22
|
+
const threshold = options.threshold ?? 24;
|
|
23
|
+
const elRef = useRef(null);
|
|
24
|
+
const cleanupRef = useRef(null);
|
|
25
|
+
const [isAnchored, setAnchored] = useState(true);
|
|
26
|
+
const anchoredRef = useRef(true);
|
|
27
|
+
anchoredRef.current = isAnchored;
|
|
28
|
+
const scrollToBottom = useCallback((behavior) => {
|
|
29
|
+
const el = elRef.current;
|
|
30
|
+
if (!el)
|
|
31
|
+
return;
|
|
32
|
+
el.scrollTo({
|
|
33
|
+
top: maxScrollTop(el),
|
|
34
|
+
behavior: behavior ?? options.behavior ?? 'auto',
|
|
35
|
+
});
|
|
36
|
+
}, [options.behavior]);
|
|
37
|
+
const setRef = useCallback((el) => {
|
|
38
|
+
if (cleanupRef.current) {
|
|
39
|
+
cleanupRef.current();
|
|
40
|
+
cleanupRef.current = null;
|
|
41
|
+
}
|
|
42
|
+
elRef.current = el;
|
|
43
|
+
if (!el)
|
|
44
|
+
return;
|
|
45
|
+
const handler = () => {
|
|
46
|
+
const distance = maxScrollTop(el) - el.scrollTop;
|
|
47
|
+
const anchored = distance <= threshold;
|
|
48
|
+
if (anchored !== anchoredRef.current) {
|
|
49
|
+
anchoredRef.current = anchored;
|
|
50
|
+
setAnchored(anchored);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
el.addEventListener('scroll', handler, { passive: true });
|
|
54
|
+
cleanupRef.current = () => el.removeEventListener('scroll', handler);
|
|
55
|
+
// Run once to seed `isAnchored` from the current position.
|
|
56
|
+
handler();
|
|
57
|
+
}, [threshold]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
return () => {
|
|
60
|
+
if (cleanupRef.current) {
|
|
61
|
+
cleanupRef.current();
|
|
62
|
+
cleanupRef.current = null;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}, []);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (anchoredRef.current)
|
|
68
|
+
scrollToBottom();
|
|
69
|
+
// Consumer-provided dependency array + the scroll callback.
|
|
70
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
71
|
+
}, options.revalidateKeys ?? []);
|
|
72
|
+
return { setRef, isAnchored, scrollToBottom };
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=useScrollLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useScrollLock.js","sourceRoot":"","sources":["../../src/state/useScrollLock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `useToolCallStream` — turn the assistant message's `toolCalls` / `toolResult`
|
|
3
|
+
* (as maintained by `useChatStream`) into a derived `{ id -> {call, result,
|
|
4
|
+
* status, mode} }` map, so renderers can show per-call cards without
|
|
5
|
+
* threading status through the tree manually.
|
|
6
|
+
*
|
|
7
|
+
* Aggregates `useToolCallStatus` across all tool calls on a single message.
|
|
8
|
+
* Pure derivation, no I/O.
|
|
9
|
+
*/
|
|
10
|
+
import type { ChatMessage, ToolCall, ToolResult } from '@steerable/agent-protocol';
|
|
11
|
+
import { type ToolCallMode, type ToolCallStatus } from '../hooks/useToolCallStatus.js';
|
|
12
|
+
export interface ToolCallEntry {
|
|
13
|
+
call: ToolCall;
|
|
14
|
+
result?: ToolResult;
|
|
15
|
+
status: ToolCallStatus;
|
|
16
|
+
mode: ToolCallMode;
|
|
17
|
+
isDestructive: boolean;
|
|
18
|
+
requiresApproval: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface UseToolCallStreamOptions {
|
|
21
|
+
message: ChatMessage | null | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Optional per-call result lookup — when the runtime emits `tool_result`
|
|
24
|
+
* with a `callId`, we can pair it to the originating call rather than
|
|
25
|
+
* relying on `message.toolResult` (which always holds the last result).
|
|
26
|
+
*/
|
|
27
|
+
resultByCallId?: Record<string, ToolResult>;
|
|
28
|
+
/** Optional mode override per tool name. */
|
|
29
|
+
modeByName?: Record<string, ToolCallMode>;
|
|
30
|
+
}
|
|
31
|
+
export interface UseToolCallStreamReturn {
|
|
32
|
+
entries: ToolCallEntry[];
|
|
33
|
+
pendingCount: number;
|
|
34
|
+
errorCount: number;
|
|
35
|
+
}
|
|
36
|
+
export declare function useToolCallStream(options: UseToolCallStreamOptions): UseToolCallStreamReturn;
|
|
37
|
+
export { useToolCallStatus } from '../hooks/useToolCallStatus.js';
|
|
38
|
+
//# sourceMappingURL=useToolCallStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToolCallStream.d.ts","sourceRoot":"","sources":["../../src/state/useToolCallStream.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,cAAc,EACpB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;IACxC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC5C,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAChC,uBAAuB,CA8BzB;AAgCD,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `useToolCallStream` — turn the assistant message's `toolCalls` / `toolResult`
|
|
3
|
+
* (as maintained by `useChatStream`) into a derived `{ id -> {call, result,
|
|
4
|
+
* status, mode} }` map, so renderers can show per-call cards without
|
|
5
|
+
* threading status through the tree manually.
|
|
6
|
+
*
|
|
7
|
+
* Aggregates `useToolCallStatus` across all tool calls on a single message.
|
|
8
|
+
* Pure derivation, no I/O.
|
|
9
|
+
*/
|
|
10
|
+
import { useMemo } from 'react';
|
|
11
|
+
export function useToolCallStream(options) {
|
|
12
|
+
const message = options.message ?? null;
|
|
13
|
+
return useMemo(() => {
|
|
14
|
+
if (!message || !Array.isArray(message.toolCalls)) {
|
|
15
|
+
return { entries: [], pendingCount: 0, errorCount: 0 };
|
|
16
|
+
}
|
|
17
|
+
const entries = message.toolCalls.map((call) => {
|
|
18
|
+
const lookup = options.resultByCallId?.[call.id];
|
|
19
|
+
const fallback = message.toolResult && message.toolResult.callId === call.id
|
|
20
|
+
? message.toolResult
|
|
21
|
+
: undefined;
|
|
22
|
+
const result = lookup ?? fallback ?? message.toolResult;
|
|
23
|
+
const status = deriveStatus(result);
|
|
24
|
+
const mode = options.modeByName?.[call.name] ?? inferMode(call.name);
|
|
25
|
+
return {
|
|
26
|
+
call,
|
|
27
|
+
result,
|
|
28
|
+
status,
|
|
29
|
+
mode,
|
|
30
|
+
isDestructive: mode === 'destructive',
|
|
31
|
+
requiresApproval: mode === 'local',
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
entries,
|
|
36
|
+
pendingCount: entries.filter((e) => e.status === 'pending').length,
|
|
37
|
+
errorCount: entries.filter((e) => e.status === 'error').length,
|
|
38
|
+
};
|
|
39
|
+
}, [message, options.resultByCallId, options.modeByName]);
|
|
40
|
+
}
|
|
41
|
+
// `useToolCallStatus` is per-call; this hook is the bulk variant. We keep the
|
|
42
|
+
// inference rules in sync by re-using the same heuristics here.
|
|
43
|
+
const READ_PATTERNS = [/^get[_-]/, /^list[_-]/, /^read[_-]/, /^search[_-]/];
|
|
44
|
+
const DESTRUCTIVE_PATTERNS = [
|
|
45
|
+
/^delete[_-]/,
|
|
46
|
+
/^remove[_-]/,
|
|
47
|
+
/^archive[_-]/,
|
|
48
|
+
/^purge[_-]/,
|
|
49
|
+
/^drop[_-]/,
|
|
50
|
+
];
|
|
51
|
+
const SAFE_WRITE_PATTERNS = [/^create[_-]/, /^update[_-]/, /^add[_-]/, /^set[_-]/];
|
|
52
|
+
const LOCAL_PATTERNS = [/^local[_-]/, /^shell[_-]/, /^exec[_-]/];
|
|
53
|
+
function inferMode(name) {
|
|
54
|
+
const lower = name.toLowerCase();
|
|
55
|
+
if (READ_PATTERNS.some((re) => re.test(lower)))
|
|
56
|
+
return 'read';
|
|
57
|
+
if (LOCAL_PATTERNS.some((re) => re.test(lower)))
|
|
58
|
+
return 'local';
|
|
59
|
+
if (DESTRUCTIVE_PATTERNS.some((re) => re.test(lower)))
|
|
60
|
+
return 'destructive';
|
|
61
|
+
if (SAFE_WRITE_PATTERNS.some((re) => re.test(lower)))
|
|
62
|
+
return 'safe_write';
|
|
63
|
+
return 'unknown';
|
|
64
|
+
}
|
|
65
|
+
function deriveStatus(result) {
|
|
66
|
+
if (!result)
|
|
67
|
+
return 'pending';
|
|
68
|
+
if (result.success === false)
|
|
69
|
+
return 'error';
|
|
70
|
+
return 'done';
|
|
71
|
+
}
|
|
72
|
+
// Re-export the per-call hook for convenience.
|
|
73
|
+
export { useToolCallStatus } from '../hooks/useToolCallStatus.js';
|
|
74
|
+
//# sourceMappingURL=useToolCallStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToolCallStream.js","sourceRoot":"","sources":["../../src/state/useToolCallStream.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerable/agent-ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Steerable framework Tier 4 — headless React hooks (useAgentSession, useChatStream, useToolCallStatus) and Tailwind-themed components (ChatPanel, MessageList, OrchestrationPlanCard, ToolCallRenderer, SSEStreamView) for embedding Steerable agents in any React app.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://pathlyapp.github.io/steerable-framework/storybook/",
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
"./components": {
|
|
42
42
|
"types": "./dist/components/index.d.ts",
|
|
43
43
|
"import": "./dist/components/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./state": {
|
|
46
|
+
"types": "./dist/state/index.d.ts",
|
|
47
|
+
"import": "./dist/state/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./cards": {
|
|
50
|
+
"types": "./dist/cards/index.d.ts",
|
|
51
|
+
"import": "./dist/cards/index.js"
|
|
44
52
|
}
|
|
45
53
|
},
|
|
46
54
|
"files": [
|
|
@@ -49,7 +57,7 @@
|
|
|
49
57
|
"README.md"
|
|
50
58
|
],
|
|
51
59
|
"dependencies": {
|
|
52
|
-
"@steerable/agent-protocol": "0.2.
|
|
60
|
+
"@steerable/agent-protocol": "0.2.4"
|
|
53
61
|
},
|
|
54
62
|
"peerDependencies": {
|
|
55
63
|
"react": ">=18.0.0",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<ActionSegmentCard />`
|
|
3
|
+
*
|
|
4
|
+
* Horizontal strip of inline tool / action invocations within a single
|
|
5
|
+
* assistant message. Each segment is rendered with `<ToolExecutionCard />` so
|
|
6
|
+
* the visual behaviour matches the standalone tool-execution card.
|
|
7
|
+
*/
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
import type { ActionSegmentPayload, ToolExecutionPayload } from '@steerable/agent-protocol';
|
|
10
|
+
import { ToolExecutionCard } from './ToolExecutionCard.js';
|
|
11
|
+
|
|
12
|
+
export interface ActionSegmentCardProps {
|
|
13
|
+
payload: ActionSegmentPayload;
|
|
14
|
+
className?: string;
|
|
15
|
+
renderArgs?: (args: unknown) => React.ReactNode;
|
|
16
|
+
renderOutput?: (output: unknown) => React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function segmentToTool(segment: ActionSegmentPayload['segments'][number]): ToolExecutionPayload {
|
|
20
|
+
return {
|
|
21
|
+
id: segment.id,
|
|
22
|
+
name: segment.kind,
|
|
23
|
+
status: segment.status,
|
|
24
|
+
summary: segment.label ?? null,
|
|
25
|
+
args: segment.args,
|
|
26
|
+
output: segment.output,
|
|
27
|
+
error: segment.error ?? null,
|
|
28
|
+
durationMs:
|
|
29
|
+
segment.startedAt && segment.finishedAt
|
|
30
|
+
? Math.max(0, Date.parse(segment.finishedAt) - Date.parse(segment.startedAt))
|
|
31
|
+
: null,
|
|
32
|
+
icon: null,
|
|
33
|
+
expandable: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const ActionSegmentCard: React.FC<ActionSegmentCardProps> = ({
|
|
38
|
+
payload,
|
|
39
|
+
className,
|
|
40
|
+
renderArgs,
|
|
41
|
+
renderOutput,
|
|
42
|
+
}) => {
|
|
43
|
+
const segments = payload.segments ?? [];
|
|
44
|
+
if (segments.length === 0) return null;
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className={['steerable-action-segment space-y-1.5', className].filter(Boolean).join(' ')}>
|
|
48
|
+
{segments.map((s) => (
|
|
49
|
+
<ToolExecutionCard
|
|
50
|
+
key={s.id}
|
|
51
|
+
payload={segmentToTool(s)}
|
|
52
|
+
renderArgs={renderArgs}
|
|
53
|
+
renderOutput={renderOutput}
|
|
54
|
+
/>
|
|
55
|
+
))}
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default ActionSegmentCard;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<AnalysisDocumentCard />`
|
|
3
|
+
*
|
|
4
|
+
* Long-form markdown document card.
|
|
5
|
+
*
|
|
6
|
+
* The framework deliberately does not pull in `react-markdown` / `remark-gfm` /
|
|
7
|
+
* `github-markdown-css` -- the consumer must pass a `renderMarkdown` slot (or
|
|
8
|
+
* a `Markdown` child) to do the actual rendering. Default fallback is a `<pre>`
|
|
9
|
+
* block.
|
|
10
|
+
*
|
|
11
|
+
* Header variants:
|
|
12
|
+
* - `inline` (default): title sits inline with the document icon.
|
|
13
|
+
* - `strip`: title becomes a `bg-muted/50` strip on top, with the icon hidden.
|
|
14
|
+
*
|
|
15
|
+
* Footer slot lets host apps attach an actions bar (deeppath uses this for
|
|
16
|
+
* "转化为行动 / 保存到笔记 / 保存为 PDF / 深入研究 / 复制 / 模型 + 时间").
|
|
17
|
+
*/
|
|
18
|
+
import * as React from 'react';
|
|
19
|
+
import type { AnalysisDocumentPayload } from '@steerable/agent-protocol';
|
|
20
|
+
import { BookIcon } from './icons.js';
|
|
21
|
+
import { asReactNode, type RenderSlotResult } from './types.js';
|
|
22
|
+
|
|
23
|
+
export type AnalysisDocumentHeaderVariant = 'inline' | 'strip';
|
|
24
|
+
|
|
25
|
+
export interface AnalysisDocumentCardProps {
|
|
26
|
+
payload: AnalysisDocumentPayload;
|
|
27
|
+
isComplete?: boolean;
|
|
28
|
+
renderMarkdown?: (body: string) => RenderSlotResult;
|
|
29
|
+
renderFooter?: () => RenderSlotResult;
|
|
30
|
+
className?: string;
|
|
31
|
+
bodyClassName?: string;
|
|
32
|
+
headerVariant?: AnalysisDocumentHeaderVariant;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const AnalysisDocumentCard: React.FC<AnalysisDocumentCardProps> = ({
|
|
36
|
+
payload,
|
|
37
|
+
renderMarkdown,
|
|
38
|
+
renderFooter,
|
|
39
|
+
className,
|
|
40
|
+
bodyClassName,
|
|
41
|
+
headerVariant = 'inline',
|
|
42
|
+
}) => {
|
|
43
|
+
return (
|
|
44
|
+
<article
|
|
45
|
+
className={[
|
|
46
|
+
'steerable-analysis-document overflow-hidden rounded-lg border border-[var(--agent-border,#e5e7eb)] bg-[var(--agent-card-bg,#fff)] text-sm text-[var(--agent-foreground,#111827)]',
|
|
47
|
+
className,
|
|
48
|
+
].filter(Boolean).join(' ')}
|
|
49
|
+
>
|
|
50
|
+
{payload.title && headerVariant === 'strip' ? (
|
|
51
|
+
<div className="border-b border-[var(--agent-border,#e5e7eb)] bg-[var(--agent-muted,#f3f4f6)]/50 px-4 py-2">
|
|
52
|
+
<span className="block truncate text-xs font-medium text-[var(--agent-muted-foreground,#6b7280)]">
|
|
53
|
+
{payload.title}
|
|
54
|
+
</span>
|
|
55
|
+
</div>
|
|
56
|
+
) : (
|
|
57
|
+
<header className="mb-3 flex items-center gap-2 border-b border-[var(--agent-border,#e5e7eb)] px-4 pt-3 pb-2">
|
|
58
|
+
<BookIcon size={14} className="text-[var(--agent-muted-foreground,#6b7280)]" />
|
|
59
|
+
<h3 className="text-sm font-medium">{payload.title || '分析报告'}</h3>
|
|
60
|
+
{payload.modelId && (
|
|
61
|
+
<span className="ml-auto rounded-full bg-[var(--agent-muted,#f3f4f6)] px-2 py-0.5 text-[10px] uppercase tracking-wider text-[var(--agent-muted-foreground,#6b7280)]">
|
|
62
|
+
{payload.modelId}
|
|
63
|
+
</span>
|
|
64
|
+
)}
|
|
65
|
+
</header>
|
|
66
|
+
)}
|
|
67
|
+
|
|
68
|
+
<div className={['p-4', bodyClassName].filter(Boolean).join(' ')}>
|
|
69
|
+
{renderMarkdown ? (
|
|
70
|
+
asReactNode(renderMarkdown(payload.body))
|
|
71
|
+
) : (
|
|
72
|
+
<pre className="whitespace-pre-wrap break-words font-sans">{payload.body}</pre>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{renderFooter && asReactNode(renderFooter())}
|
|
77
|
+
</article>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default AnalysisDocumentCard;
|