@tetherto/mdk-ui-agent 0.0.0 → 0.12.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/LICENSE +202 -0
- package/README.md +310 -0
- package/dist/branding.d.ts +36 -0
- package/dist/branding.d.ts.map +1 -0
- package/dist/branding.js +37 -0
- package/dist/branding.js.map +1 -0
- package/dist/components/approval-card.d.ts +46 -0
- package/dist/components/approval-card.d.ts.map +1 -0
- package/dist/components/approval-card.js +84 -0
- package/dist/components/approval-card.js.map +1 -0
- package/dist/components/assistant-message.d.ts +25 -0
- package/dist/components/assistant-message.d.ts.map +1 -0
- package/dist/components/assistant-message.js +49 -0
- package/dist/components/assistant-message.js.map +1 -0
- package/dist/components/co-pilot-panel.d.ts +17 -0
- package/dist/components/co-pilot-panel.d.ts.map +1 -0
- package/dist/components/co-pilot-panel.js +33 -0
- package/dist/components/co-pilot-panel.js.map +1 -0
- package/dist/components/code-block.d.ts +17 -0
- package/dist/components/code-block.d.ts.map +1 -0
- package/dist/components/code-block.js +40 -0
- package/dist/components/code-block.js.map +1 -0
- package/dist/components/collapsible-answer.d.ts +26 -0
- package/dist/components/collapsible-answer.d.ts.map +1 -0
- package/dist/components/collapsible-answer.js +30 -0
- package/dist/components/collapsible-answer.js.map +1 -0
- package/dist/components/composer.d.ts +31 -0
- package/dist/components/composer.d.ts.map +1 -0
- package/dist/components/composer.js +82 -0
- package/dist/components/composer.js.map +1 -0
- package/dist/components/conversation-list.d.ts +21 -0
- package/dist/components/conversation-list.d.ts.map +1 -0
- package/dist/components/conversation-list.js +42 -0
- package/dist/components/conversation-list.js.map +1 -0
- package/dist/components/icons.d.ts +88 -0
- package/dist/components/icons.d.ts.map +1 -0
- package/dist/components/icons.js +112 -0
- package/dist/components/icons.js.map +1 -0
- package/dist/components/launcher-fab.d.ts +8 -0
- package/dist/components/launcher-fab.d.ts.map +1 -0
- package/dist/components/launcher-fab.js +6 -0
- package/dist/components/launcher-fab.js.map +1 -0
- package/dist/components/leaked-tool-call-notice.d.ts +18 -0
- package/dist/components/leaked-tool-call-notice.d.ts.map +1 -0
- package/dist/components/leaked-tool-call-notice.js +14 -0
- package/dist/components/leaked-tool-call-notice.js.map +1 -0
- package/dist/components/markdown-link.d.ts +17 -0
- package/dist/components/markdown-link.d.ts.map +1 -0
- package/dist/components/markdown-link.js +18 -0
- package/dist/components/markdown-link.js.map +1 -0
- package/dist/components/markdown-text.d.ts +26 -0
- package/dist/components/markdown-text.d.ts.map +1 -0
- package/dist/components/markdown-text.js +32 -0
- package/dist/components/markdown-text.js.map +1 -0
- package/dist/components/message-list.d.ts +24 -0
- package/dist/components/message-list.d.ts.map +1 -0
- package/dist/components/message-list.js +92 -0
- package/dist/components/message-list.js.map +1 -0
- package/dist/components/no-tools-note.d.ts +26 -0
- package/dist/components/no-tools-note.d.ts.map +1 -0
- package/dist/components/no-tools-note.js +27 -0
- package/dist/components/no-tools-note.js.map +1 -0
- package/dist/components/panel-header.d.ts +26 -0
- package/dist/components/panel-header.d.ts.map +1 -0
- package/dist/components/panel-header.js +13 -0
- package/dist/components/panel-header.js.map +1 -0
- package/dist/components/rehype-highlight-subset.d.ts +17 -0
- package/dist/components/rehype-highlight-subset.d.ts.map +1 -0
- package/dist/components/rehype-highlight-subset.js +77 -0
- package/dist/components/rehype-highlight-subset.js.map +1 -0
- package/dist/components/system-message.d.ts +15 -0
- package/dist/components/system-message.d.ts.map +1 -0
- package/dist/components/system-message.js +12 -0
- package/dist/components/system-message.js.map +1 -0
- package/dist/components/tool-chip.d.ts +19 -0
- package/dist/components/tool-chip.d.ts.map +1 -0
- package/dist/components/tool-chip.js +32 -0
- package/dist/components/tool-chip.js.map +1 -0
- package/dist/components/user-message.d.ts +9 -0
- package/dist/components/user-message.d.ts.map +1 -0
- package/dist/components/user-message.js +7 -0
- package/dist/components/user-message.js.map +1 -0
- package/dist/core/conversation-store.d.ts +107 -0
- package/dist/core/conversation-store.d.ts.map +1 -0
- package/dist/core/conversation-store.js +254 -0
- package/dist/core/conversation-store.js.map +1 -0
- package/dist/core/error-text.d.ts +19 -0
- package/dist/core/error-text.d.ts.map +1 -0
- package/dist/core/error-text.js +35 -0
- package/dist/core/error-text.js.map +1 -0
- package/dist/core/events.d.ts +98 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +50 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/format.d.ts +23 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +49 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/index.d.ts +27 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +20 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/markdown.d.ts +26 -0
- package/dist/core/markdown.d.ts.map +1 -0
- package/dist/core/markdown.js +49 -0
- package/dist/core/markdown.js.map +1 -0
- package/dist/core/prose.d.ts +27 -0
- package/dist/core/prose.d.ts.map +1 -0
- package/dist/core/prose.js +79 -0
- package/dist/core/prose.js.map +1 -0
- package/dist/core/small-talk.d.ts +15 -0
- package/dist/core/small-talk.d.ts.map +1 -0
- package/dist/core/small-talk.js +42 -0
- package/dist/core/small-talk.js.map +1 -0
- package/dist/core/sse.d.ts +108 -0
- package/dist/core/sse.d.ts.map +1 -0
- package/dist/core/sse.js +236 -0
- package/dist/core/sse.js.map +1 -0
- package/dist/core/tool-label.d.ts +16 -0
- package/dist/core/tool-label.d.ts.map +1 -0
- package/dist/core/tool-label.js +31 -0
- package/dist/core/tool-label.js.map +1 -0
- package/dist/core/transport.d.ts +71 -0
- package/dist/core/transport.d.ts.map +1 -0
- package/dist/core/transport.js +147 -0
- package/dist/core/transport.js.map +1 -0
- package/dist/core/turn.d.ts +87 -0
- package/dist/core/turn.d.ts.map +1 -0
- package/dist/core/turn.js +232 -0
- package/dist/core/turn.js.map +1 -0
- package/dist/core/uuid.d.ts +21 -0
- package/dist/core/uuid.d.ts.map +1 -0
- package/dist/core/uuid.js +54 -0
- package/dist/core/uuid.js.map +1 -0
- package/dist/entries/chat-page.d.ts +28 -0
- package/dist/entries/chat-page.d.ts.map +1 -0
- package/dist/entries/chat-page.js +25 -0
- package/dist/entries/chat-page.js.map +1 -0
- package/dist/entries/co-pilot.d.ts +41 -0
- package/dist/entries/co-pilot.d.ts.map +1 -0
- package/dist/entries/co-pilot.js +39 -0
- package/dist/entries/co-pilot.js.map +1 -0
- package/dist/entries/lazy-panel.d.ts +15 -0
- package/dist/entries/lazy-panel.d.ts.map +1 -0
- package/dist/entries/lazy-panel.js +19 -0
- package/dist/entries/lazy-panel.js.map +1 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/use-agent-chat.d.ts +46 -0
- package/dist/hooks/use-agent-chat.d.ts.map +1 -0
- package/dist/hooks/use-agent-chat.js +288 -0
- package/dist/hooks/use-agent-chat.js.map +1 -0
- package/dist/hooks/use-agent-config.d.ts +29 -0
- package/dist/hooks/use-agent-config.d.ts.map +1 -0
- package/dist/hooks/use-agent-config.js +45 -0
- package/dist/hooks/use-agent-config.js.map +1 -0
- package/dist/hooks/use-conversations.d.ts +19 -0
- package/dist/hooks/use-conversations.d.ts.map +1 -0
- package/dist/hooks/use-conversations.js +19 -0
- package/dist/hooks/use-conversations.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/styles.css +1 -0
- package/package.json +105 -1
- package/src/branding.ts +40 -0
- package/src/components/approval-card.tsx +141 -0
- package/src/components/assistant-message.tsx +140 -0
- package/src/components/co-pilot-panel.tsx +109 -0
- package/src/components/code-block.tsx +63 -0
- package/src/components/collapsible-answer.tsx +55 -0
- package/src/components/composer.tsx +147 -0
- package/src/components/conversation-list.tsx +141 -0
- package/src/components/icons.tsx +189 -0
- package/src/components/launcher-fab.tsx +16 -0
- package/src/components/leaked-tool-call-notice.tsx +29 -0
- package/src/components/markdown-link.tsx +30 -0
- package/src/components/markdown-text.tsx +58 -0
- package/src/components/message-list.tsx +164 -0
- package/src/components/no-tools-note.tsx +33 -0
- package/src/components/panel-header.tsx +86 -0
- package/src/components/rehype-highlight-subset.ts +84 -0
- package/src/components/system-message.tsx +19 -0
- package/src/components/tool-chip.tsx +58 -0
- package/src/components/user-message.tsx +18 -0
- package/src/core/conversation-store.ts +342 -0
- package/src/core/error-text.ts +36 -0
- package/src/core/events.ts +127 -0
- package/src/core/format.ts +59 -0
- package/src/core/index.ts +115 -0
- package/src/core/markdown.ts +52 -0
- package/src/core/prose.ts +78 -0
- package/src/core/small-talk.ts +45 -0
- package/src/core/sse.ts +269 -0
- package/src/core/tool-label.ts +33 -0
- package/src/core/transport.ts +208 -0
- package/src/core/turn.ts +299 -0
- package/src/core/uuid.ts +59 -0
- package/src/entries/chat-page.tsx +56 -0
- package/src/entries/co-pilot.tsx +84 -0
- package/src/entries/lazy-panel.tsx +19 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/use-agent-chat.ts +387 -0
- package/src/hooks/use-agent-config.ts +70 -0
- package/src/hooks/use-conversations.ts +39 -0
- package/src/index.ts +30 -0
- package/src/styles/_approval.scss +68 -0
- package/src/styles/_composer.scss +45 -0
- package/src/styles/_conversations.scss +160 -0
- package/src/styles/_messages.scss +444 -0
- package/src/styles/_mixins.scss +27 -0
- package/src/styles/_panel.scss +147 -0
- package/src/styles/_tokens.scss +30 -0
- package/src/styles.scss +16 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@tetherto/mdk-react-devkit/primitives';
|
|
3
|
+
import isNil from 'lodash/isNil';
|
|
4
|
+
import { AGENT_LABELS } from '../branding';
|
|
5
|
+
import { humanizeToolName, toolLabel } from '../core/tool-label';
|
|
6
|
+
/** Args the reference site tools use to name their target, most specific first. */
|
|
7
|
+
const TARGET_KEYS = ['ref', 'deviceId', 'device', 'id', 'name'];
|
|
8
|
+
/**
|
|
9
|
+
* Friendlier names for argument keys the reference tools use.
|
|
10
|
+
*
|
|
11
|
+
* `action` matters most: without it the card shows two rows both labelled
|
|
12
|
+
* "Action" — the tool itself, and the tool's `action` argument — which is what
|
|
13
|
+
* the design calls "Command". Anything not listed is humanized from its key.
|
|
14
|
+
*/
|
|
15
|
+
const ARG_LABELS = {
|
|
16
|
+
action: 'Command',
|
|
17
|
+
mode: 'Mode',
|
|
18
|
+
family: 'Family',
|
|
19
|
+
state: 'State',
|
|
20
|
+
};
|
|
21
|
+
/** Shown for an argument the tool declared but sent no value for. */
|
|
22
|
+
const EMPTY_VALUE = '—';
|
|
23
|
+
const describeValue = (value) => {
|
|
24
|
+
if (isNil(value))
|
|
25
|
+
return EMPTY_VALUE;
|
|
26
|
+
if (typeof value === 'string')
|
|
27
|
+
return value;
|
|
28
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
29
|
+
return String(value);
|
|
30
|
+
return JSON.stringify(value);
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Flattens the tool args into the Action / Device / Command rows the design
|
|
34
|
+
* shows.
|
|
35
|
+
*
|
|
36
|
+
* Tools are supplied by whichever MCP server the operator points the agent at,
|
|
37
|
+
* so no argument name is guaranteed. The target is looked up across the names
|
|
38
|
+
* the reference tools use and everything else is listed as-is, which degrades to
|
|
39
|
+
* a plain key/value dump for an unknown tool rather than to nothing.
|
|
40
|
+
*/
|
|
41
|
+
export function describeApproval(approval, labels) {
|
|
42
|
+
const rows = [{ label: 'Action', value: toolLabel(approval.name, labels) }];
|
|
43
|
+
const targetKey = TARGET_KEYS.find((key) => key in approval.args);
|
|
44
|
+
if (targetKey)
|
|
45
|
+
rows.push({ label: 'Device', value: describeValue(approval.args[targetKey]) });
|
|
46
|
+
for (const [key, value] of Object.entries(approval.args)) {
|
|
47
|
+
if (key === targetKey)
|
|
48
|
+
continue;
|
|
49
|
+
rows.push({ label: ARG_LABELS[key] ?? humanizeToolName(key), value: describeValue(value) });
|
|
50
|
+
}
|
|
51
|
+
return rows;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The one-line "here is what you are about to allow", shown above the card.
|
|
55
|
+
*
|
|
56
|
+
* Synthesized rather than streamed: `pending_approval` arrives between a
|
|
57
|
+
* `tool_call` and its `tool_result`, and the model does not emit a single token
|
|
58
|
+
* until after the tool returns — so at the moment the operator is asked to
|
|
59
|
+
* decide, the agent has said nothing at all. The design shows a sentence there,
|
|
60
|
+
* and it has to be built from the call itself.
|
|
61
|
+
*/
|
|
62
|
+
export function describeApprovalIntent(approval, labels) {
|
|
63
|
+
const action = toolLabel(approval.name, labels).toLowerCase();
|
|
64
|
+
const targetKey = TARGET_KEYS.find((key) => key in approval.args);
|
|
65
|
+
const target = targetKey ? describeValue(approval.args[targetKey]) : null;
|
|
66
|
+
return target
|
|
67
|
+
? `This runs ${action} on ${target}. Review and approve to proceed.`
|
|
68
|
+
: `This runs ${action}. Review and approve to proceed.`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The inline approval prompt. The stream is paused behind this.
|
|
72
|
+
*
|
|
73
|
+
* Rendered in the transcript rather than as a blocking modal, because it is not
|
|
74
|
+
* always the operator who resolves it: an approval left unanswered for
|
|
75
|
+
* `agent.approvalTimeoutMs` is auto-rejected server-side and the stream resumes
|
|
76
|
+
* on its own. The card must be able to disappear because the turn moved on — a
|
|
77
|
+
* modal that only closes on click would strand the operator on a dead prompt.
|
|
78
|
+
* Unmounting is driven by the reducer clearing `pendingApproval`.
|
|
79
|
+
*/
|
|
80
|
+
export const ApprovalCard = ({ approval, toolLabels, pending = false, onDecide, }) => {
|
|
81
|
+
const rows = describeApproval(approval, toolLabels);
|
|
82
|
+
return (_jsxs("section", { className: "mdk-agent-approval", "aria-label": AGENT_LABELS.approvalRequired, children: [_jsx("h3", { className: "mdk-agent-approval__title", children: AGENT_LABELS.approvalRequired }), _jsx("dl", { className: "mdk-agent-approval__details", children: rows.map((row) => (_jsxs("div", { className: "mdk-agent-approval__row", children: [_jsx("dt", { className: "mdk-agent-approval__label", children: row.label }), _jsx("dd", { className: "mdk-agent-approval__value", children: row.value })] }, row.label))) }), _jsxs("div", { className: "mdk-agent-approval__actions", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: pending, onClick: () => onDecide(false), children: "Reject" }), _jsx(Button, { variant: "primary", size: "sm", loading: pending, disabled: pending, onClick: () => onDecide(true), children: "Approve" })] })] }));
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=approval-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-card.js","sourceRoot":"","sources":["../../src/components/approval-card.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import type { ToolLabels } from '../core/tool-label';
|
|
3
|
+
import type { PendingApproval, ToolStep } from '../core/turn';
|
|
4
|
+
export type AssistantMessageProps = {
|
|
5
|
+
text: string;
|
|
6
|
+
/** The operator message this answers — only read to decide whether it was a fleet question. */
|
|
7
|
+
question?: string;
|
|
8
|
+
tools?: ToolStep[];
|
|
9
|
+
/** The `error` the turn ended on. A raw `ERR_*` code is mapped before it is shown. */
|
|
10
|
+
error?: string | null;
|
|
11
|
+
/** The turn was stopped before it finished, so this is a part of an answer. */
|
|
12
|
+
interrupted?: boolean;
|
|
13
|
+
/** The turn is still running: render plain text and the thinking indicator. */
|
|
14
|
+
streaming?: boolean;
|
|
15
|
+
toolLabels?: ToolLabels;
|
|
16
|
+
approval?: PendingApproval | null;
|
|
17
|
+
isDecisionPending?: boolean;
|
|
18
|
+
onDecide?: (approved: boolean) => void;
|
|
19
|
+
/** Offered on a failed turn, so the operator does not retype the question. */
|
|
20
|
+
onRetry?: VoidFunction;
|
|
21
|
+
};
|
|
22
|
+
declare const AssistantMessageView: ({ text, question, tools, error, interrupted, streaming, toolLabels, approval, isDecisionPending, onDecide, onRetry, }: AssistantMessageProps) => JSX.Element;
|
|
23
|
+
export declare const AssistantMessage: import("react").MemoExoticComponent<typeof AssistantMessageView>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=assistant-message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../src/components/assistant-message.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAGhC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,KAAK,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAW7D,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAA;IAClB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,QAAQ,CAAC,EAAE,eAAe,GAAG,IAAI,CAAA;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,8EAA8E;IAC9E,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAED,QAAA,MAAM,oBAAoB,0HAYvB,qBAAqB,KAAG,GAAG,CAAC,OA0F9B,CAAA;AAED,eAAO,MAAM,gBAAgB,kEAA6B,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Loader } from '@tetherto/mdk-react-devkit/primitives';
|
|
3
|
+
import { memo, useMemo } from 'react';
|
|
4
|
+
import { AGENT_LABELS } from '../branding';
|
|
5
|
+
import { describeAgentError } from '../core/error-text';
|
|
6
|
+
import { parseLeakedToolCall } from '../core/prose';
|
|
7
|
+
import { isSmallTalk } from '../core/small-talk';
|
|
8
|
+
import { ApprovalCard, describeApprovalIntent } from './approval-card';
|
|
9
|
+
import { CollapsibleAnswer } from './collapsible-answer';
|
|
10
|
+
import { LeakedToolCallNotice } from './leaked-tool-call-notice';
|
|
11
|
+
import { NoToolsNote } from './no-tools-note';
|
|
12
|
+
import { ToolChip } from './tool-chip';
|
|
13
|
+
const AssistantMessageView = ({ text, question = '', tools = [], error = null, interrupted = false, streaming = false, toolLabels, approval = null, isDecisionPending = false, onDecide, onRetry, }) => {
|
|
14
|
+
// Only worth checking once the turn has settled — a partial answer can look
|
|
15
|
+
// like a bare JSON object simply because the rest has not arrived yet.
|
|
16
|
+
const leaked = useMemo(() => (streaming ? null : parseLeakedToolCall(text)), [streaming, text]);
|
|
17
|
+
const hasText = text.trim().length > 0;
|
|
18
|
+
const isThinking = streaming && !hasText && approval === null;
|
|
19
|
+
// The chip for the gated step is suppressed while its card is up: the card
|
|
20
|
+
// already names the action, the device and the command, so the chip would just
|
|
21
|
+
// restate it directly above.
|
|
22
|
+
const chips = approval ? tools.filter((step) => step.id !== approval.toolStepId) : tools;
|
|
23
|
+
// Stated only once the turn has settled — mid-stream the tools may simply not
|
|
24
|
+
// have been called *yet*. A leaked call and an errored turn already say their
|
|
25
|
+
// own thing, so the note would only add noise there. Nor is it said against a
|
|
26
|
+
// greeting: consulting nothing is the right answer to "hey", and the marker
|
|
27
|
+
// was appearing on every one of them.
|
|
28
|
+
const answeredWithoutTools = !streaming
|
|
29
|
+
&& tools.length === 0
|
|
30
|
+
&& hasText
|
|
31
|
+
&& leaked === null
|
|
32
|
+
&& !error
|
|
33
|
+
&& !isSmallTalk(question);
|
|
34
|
+
return (_jsxs("div", { className: "mdk-agent-message mdk-agent-message--assistant", children: [_jsxs("p", { className: "mdk-agent-message__role", children: [_jsx("span", { className: "mdk-agent-message__dot", "aria-hidden": "true" }), AGENT_LABELS.name, answeredWithoutTools ? _jsx(NoToolsNote, {}) : null] }), chips.length > 0
|
|
35
|
+
? (_jsx("div", { className: "mdk-agent-message__tools", children: chips.map((step) => (_jsx(ToolChip, { step: step, toolLabels: toolLabels }, step.id))) }))
|
|
36
|
+
: null, approval
|
|
37
|
+
? (_jsx("p", { className: "mdk-agent-message__intent", children: describeApprovalIntent(approval, toolLabels) }))
|
|
38
|
+
: null, isThinking ? _jsx(Loader, { inline: true, size: 6, count: 3, color: "orange" }) : null, hasText && leaked === null ? _jsx(CollapsibleAnswer, { text: text, streaming: streaming }) : null, hasText && leaked !== null ? _jsx(LeakedToolCallNotice, { call: leaked }) : null, approval && onDecide
|
|
39
|
+
? (_jsx(ApprovalCard, { approval: approval, toolLabels: toolLabels, pending: isDecisionPending, onDecide: onDecide }))
|
|
40
|
+
: null, interrupted && !error
|
|
41
|
+
? _jsx("p", { className: "mdk-agent-message__note", children: "Stopped early \u2014 this is not the whole answer." })
|
|
42
|
+
: null, error
|
|
43
|
+
? (_jsxs("p", { className: "mdk-agent-message__error", role: "alert", children: [describeAgentError(error), onRetry
|
|
44
|
+
? (_jsx("button", { type: "button", className: "mdk-agent-message__retry", onClick: onRetry, children: "Try again" }))
|
|
45
|
+
: null] }))
|
|
46
|
+
: null] }));
|
|
47
|
+
};
|
|
48
|
+
export const AssistantMessage = memo(AssistantMessageView);
|
|
49
|
+
//# sourceMappingURL=assistant-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../src/components/assistant-message.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
import type { ToolLabels } from '../core/tool-label';
|
|
3
|
+
import type { UseAgentChatResult } from '../hooks/use-agent-chat';
|
|
4
|
+
export type CoPilotPanelProps = {
|
|
5
|
+
/** The single hook instance. Both entries own one and pass it down. */
|
|
6
|
+
chat: UseAgentChatResult;
|
|
7
|
+
title?: string;
|
|
8
|
+
status?: ReactNode;
|
|
9
|
+
toolLabels?: ToolLabels;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
/** Omitted by the route page, which has nothing to collapse into. */
|
|
12
|
+
onClose?: VoidFunction | undefined;
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
/** The panel body, shared by the docked overlay and the full-page route. */
|
|
16
|
+
export declare const CoPilotPanel: ({ chat, title, status, toolLabels, placeholder, onClose, className, }: CoPilotPanelProps) => JSX.Element;
|
|
17
|
+
//# sourceMappingURL=co-pilot-panel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"co-pilot-panel.d.ts","sourceRoot":"","sources":["../../src/components/co-pilot-panel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAG3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AASjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,uEAAuE;IACvE,IAAI,EAAE,kBAAkB,CAAA;IACxB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,OAAO,CAAC,EAAE,YAAY,GAAG,SAAS,CAAA;IAClC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,4EAA4E;AAC5E,eAAO,MAAM,YAAY,0EAQtB,iBAAiB,KAAG,GAAG,CAAC,OA0E1B,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
|
+
import { AGENT_LABELS } from '../branding';
|
|
4
|
+
import { formatConversationSummary } from '../core/format';
|
|
5
|
+
import { Composer } from './composer';
|
|
6
|
+
import { ConversationList } from './conversation-list';
|
|
7
|
+
import { MessageList } from './message-list';
|
|
8
|
+
import { PANEL_VIEW, PanelHeader } from './panel-header';
|
|
9
|
+
/** The panel body, shared by the docked overlay and the full-page route. */
|
|
10
|
+
export const CoPilotPanel = ({ chat, title = AGENT_LABELS.name, status, toolLabels, placeholder, onClose, className, }) => {
|
|
11
|
+
const [view, setView] = useState(PANEL_VIEW.CHAT);
|
|
12
|
+
const toggleConversations = useCallback(() => {
|
|
13
|
+
setView((current) => (current === PANEL_VIEW.CHAT ? PANEL_VIEW.CONVERSATIONS : PANEL_VIEW.CHAT));
|
|
14
|
+
}, []);
|
|
15
|
+
const handleNewConversation = useCallback(() => {
|
|
16
|
+
chat.newConversation();
|
|
17
|
+
setView(PANEL_VIEW.CHAT);
|
|
18
|
+
}, [chat]);
|
|
19
|
+
const handleSelectConversation = useCallback((id) => {
|
|
20
|
+
chat.selectConversation(id);
|
|
21
|
+
setView(PANEL_VIEW.CHAT);
|
|
22
|
+
}, [chat]);
|
|
23
|
+
const isConversations = view === PANEL_VIEW.CONVERSATIONS;
|
|
24
|
+
// The conversations view describes the list rather than the connection — the
|
|
25
|
+
// host-supplied status is about the agent, which is not what is on screen.
|
|
26
|
+
const headerStatus = isConversations
|
|
27
|
+
? formatConversationSummary(chat.conversations.length, chat.isStreaming)
|
|
28
|
+
: status;
|
|
29
|
+
return (_jsxs("section", { className: `mdk-agent-panel${className ? ` ${className}` : ''}`, "aria-label": title, children: [_jsx(PanelHeader, { view: view, title: isConversations ? AGENT_LABELS.conversations : title, status: headerStatus, actionsDisabled: chat.isStreaming, onNewConversation: handleNewConversation, onToggleConversations: toggleConversations, onClose: onClose }), isConversations
|
|
30
|
+
? (_jsx(ConversationList, { conversations: chat.conversations, activeId: chat.active?.id ?? null, runningId: chat.isStreaming ? (chat.active?.id ?? null) : null, onSelect: handleSelectConversation, onCreate: handleNewConversation, onDelete: chat.removeConversation }))
|
|
31
|
+
: (_jsxs(_Fragment, { children: [_jsx(MessageList, { messages: chat.messages, live: chat.live, toolLabels: toolLabels, pendingApproval: chat.pendingApproval, isDecisionPending: chat.isDecisionPending, onDecide: chat.decide, onRetry: chat.retry }), _jsx(Composer, { disabled: !chat.canSend, isStreaming: chat.isStreaming, placeholder: placeholder, focusKey: chat.active?.id ?? null, onSend: chat.send, onStop: chat.abort })] }))] }));
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=co-pilot-panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"co-pilot-panel.js","sourceRoot":"","sources":["../../src/components/co-pilot-panel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, JSX } from 'react';
|
|
2
|
+
import type { ExtraProps } from 'react-markdown';
|
|
3
|
+
export type CodeBlockProps = ComponentPropsWithoutRef<'pre'> & ExtraProps;
|
|
4
|
+
/**
|
|
5
|
+
* A fenced code block, with a copy button.
|
|
6
|
+
*
|
|
7
|
+
* What the panel puts in fences is mostly shell commands and JSON an operator
|
|
8
|
+
* wants to run or paste somewhere, so copying it is the common next action.
|
|
9
|
+
*
|
|
10
|
+
* The text is read off the rendered node rather than reassembled from the hast
|
|
11
|
+
* tree: the highlighter has already replaced the block's children with token
|
|
12
|
+
* spans, and `textContent` is exactly the source again. The button is absent
|
|
13
|
+
* where the Clipboard API is — it needs a secure context, and a gateway on a site
|
|
14
|
+
* LAN is served over plain http.
|
|
15
|
+
*/
|
|
16
|
+
export declare const CodeBlock: ({ children, node, ...rest }: CodeBlockProps) => JSX.Element;
|
|
17
|
+
//# sourceMappingURL=code-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAMhD,MAAM,MAAM,cAAc,GAAG,wBAAwB,CAAC,KAAK,CAAC,GAAG,UAAU,CAAA;AAEzE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,gCAAiC,cAAc,KAAG,GAAG,CAAC,OAyC3E,CAAA"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
/** How long the button confirms the copy before going back to its label. */
|
|
4
|
+
const COPIED_FEEDBACK_MS = 1500;
|
|
5
|
+
/**
|
|
6
|
+
* A fenced code block, with a copy button.
|
|
7
|
+
*
|
|
8
|
+
* What the panel puts in fences is mostly shell commands and JSON an operator
|
|
9
|
+
* wants to run or paste somewhere, so copying it is the common next action.
|
|
10
|
+
*
|
|
11
|
+
* The text is read off the rendered node rather than reassembled from the hast
|
|
12
|
+
* tree: the highlighter has already replaced the block's children with token
|
|
13
|
+
* spans, and `textContent` is exactly the source again. The button is absent
|
|
14
|
+
* where the Clipboard API is — it needs a secure context, and a gateway on a site
|
|
15
|
+
* LAN is served over plain http.
|
|
16
|
+
*/
|
|
17
|
+
export const CodeBlock = ({ children, node, ...rest }) => {
|
|
18
|
+
const codeRef = useRef(null);
|
|
19
|
+
const [copied, setCopied] = useState(false);
|
|
20
|
+
const canCopy = typeof navigator?.clipboard?.writeText === 'function';
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!copied)
|
|
23
|
+
return;
|
|
24
|
+
const timer = setTimeout(() => setCopied(false), COPIED_FEEDBACK_MS);
|
|
25
|
+
return () => clearTimeout(timer);
|
|
26
|
+
}, [copied]);
|
|
27
|
+
const copy = () => {
|
|
28
|
+
const text = codeRef.current?.textContent ?? '';
|
|
29
|
+
if (!text)
|
|
30
|
+
return;
|
|
31
|
+
void navigator.clipboard.writeText(text).then(() => setCopied(true), () => {
|
|
32
|
+
// Denied by permissions policy, or the document is not focused. Nothing
|
|
33
|
+
// to report — the code is still on screen and selectable.
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
return (_jsxs("div", { className: "mdk-agent-code", children: [_jsx("pre", { ref: codeRef, ...rest, children: children }), canCopy
|
|
37
|
+
? (_jsx("button", { type: "button", className: "mdk-agent-code__copy", "aria-label": copied ? 'Copied' : 'Copy code', onClick: copy, children: copied ? 'Copied' : 'Copy' }))
|
|
38
|
+
: null] }));
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=code-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.js","sourceRoot":"","sources":["../../src/components/code-block.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEnD,4EAA4E;AAC5E,MAAM,kBAAkB,GAAG,IAAI,CAAA;AAI/B;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,EAAkB,EAAe,EAAE;IACpF,MAAM,OAAO,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAA;IAC5C,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAE3C,MAAM,OAAO,GAAG,OAAO,SAAS,EAAE,SAAS,EAAE,SAAS,KAAK,UAAU,CAAA;IAErE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM;YAAE,OAAM;QACnB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAC,CAAA;QACpE,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;IAClC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,EAAE,WAAW,IAAI,EAAE,CAAA;QAC/C,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,KAAK,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAC3C,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,EACrB,GAAG,EAAE;YACH,wEAAwE;YACxE,0DAA0D;QAC5D,CAAC,CACF,CAAA;IACH,CAAC,CAAA;IAED,OAAO,CACL,eAAK,SAAS,EAAC,gBAAgB,aAC7B,cAAK,GAAG,EAAE,OAAO,KAAM,IAAI,YAAG,QAAQ,GAAO,EAC5C,OAAO;gBACN,CAAC,CAAC,CACE,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,sBAAsB,gBACpB,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAC3C,OAAO,EAAE,IAAI,YAEZ,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,GACpB,CACV;gBACH,CAAC,CAAC,IAAI,IACJ,CACP,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Where an answer stops being readable in a docked panel.
|
|
4
|
+
*
|
|
5
|
+
* The panel is 420×640, so a few thousand characters is already several screens
|
|
6
|
+
* of scrolling between one question and the next.
|
|
7
|
+
*/
|
|
8
|
+
export declare const COLLAPSE_ABOVE_CHARS = 3000;
|
|
9
|
+
/** How much of it is shown collapsed. */
|
|
10
|
+
export declare const COLLAPSED_CHARS = 1500;
|
|
11
|
+
export type CollapsibleAnswerProps = {
|
|
12
|
+
text: string;
|
|
13
|
+
/** While the turn streams the answer is never collapsed: growth has to stay visible. */
|
|
14
|
+
streaming?: boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* An answer, collapsed behind a toggle when it is long enough to bury the rest of
|
|
18
|
+
* the transcript.
|
|
19
|
+
*
|
|
20
|
+
* Only ever collapsed once the turn has settled — mid-stream, hiding the tail
|
|
21
|
+
* would hide the very thing the operator is watching.
|
|
22
|
+
*/
|
|
23
|
+
declare const CollapsibleAnswerView: ({ text, streaming }: CollapsibleAnswerProps) => JSX.Element;
|
|
24
|
+
export declare const CollapsibleAnswer: import("react").MemoExoticComponent<typeof CollapsibleAnswerView>;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=collapsible-answer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collapsible-answer.d.ts","sourceRoot":"","sources":["../../src/components/collapsible-answer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAMhC;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,OAAO,CAAA;AACxC,yCAAyC;AACzC,eAAO,MAAM,eAAe,OAAO,CAAA;AAEnC,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,wFAAwF;IACxF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;;;GAMG;AACH,QAAA,MAAM,qBAAqB,wBAAiC,sBAAsB,KAAG,GAAG,CAAC,OAuBxF,CAAA;AAED,eAAO,MAAM,iBAAiB,mEAA8B,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useState } from 'react';
|
|
3
|
+
import { clipToBoundary } from '../core/markdown';
|
|
4
|
+
import { MarkdownText } from './markdown-text';
|
|
5
|
+
/**
|
|
6
|
+
* Where an answer stops being readable in a docked panel.
|
|
7
|
+
*
|
|
8
|
+
* The panel is 420×640, so a few thousand characters is already several screens
|
|
9
|
+
* of scrolling between one question and the next.
|
|
10
|
+
*/
|
|
11
|
+
export const COLLAPSE_ABOVE_CHARS = 3000;
|
|
12
|
+
/** How much of it is shown collapsed. */
|
|
13
|
+
export const COLLAPSED_CHARS = 1500;
|
|
14
|
+
/**
|
|
15
|
+
* An answer, collapsed behind a toggle when it is long enough to bury the rest of
|
|
16
|
+
* the transcript.
|
|
17
|
+
*
|
|
18
|
+
* Only ever collapsed once the turn has settled — mid-stream, hiding the tail
|
|
19
|
+
* would hide the very thing the operator is watching.
|
|
20
|
+
*/
|
|
21
|
+
const CollapsibleAnswerView = ({ text, streaming = false }) => {
|
|
22
|
+
const [expanded, setExpanded] = useState(false);
|
|
23
|
+
const isLong = !streaming && text.length > COLLAPSE_ABOVE_CHARS;
|
|
24
|
+
const shown = isLong && !expanded ? clipToBoundary(text, COLLAPSED_CHARS) : text;
|
|
25
|
+
return (_jsxs(_Fragment, { children: [_jsx(MarkdownText, { text: shown, streaming: streaming }), isLong
|
|
26
|
+
? (_jsx("button", { type: "button", className: "mdk-agent-markdown__more", "aria-expanded": expanded, onClick: () => setExpanded(!expanded), children: expanded ? 'Show less' : 'Show full answer' }))
|
|
27
|
+
: null] }));
|
|
28
|
+
};
|
|
29
|
+
export const CollapsibleAnswer = memo(CollapsibleAnswerView);
|
|
30
|
+
//# sourceMappingURL=collapsible-answer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collapsible-answer.js","sourceRoot":"","sources":["../../src/components/collapsible-answer.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAA;AACxC,yCAAyC;AACzC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAA;AAQnC;;;;;;GAMG;AACH,MAAM,qBAAqB,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,GAAG,KAAK,EAA0B,EAAe,EAAE;IACjG,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAE/C,MAAM,MAAM,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,MAAM,GAAG,oBAAoB,CAAA;IAC/D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAEhF,OAAO,CACL,8BACE,KAAC,YAAY,IAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAI,EAClD,MAAM;gBACL,CAAC,CAAC,CACE,iBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,0BAA0B,mBACrB,QAAQ,EACvB,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,YAEpC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,kBAAkB,GACrC,CACV;gBACH,CAAC,CAAC,IAAI,IACP,CACJ,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC,qBAAqB,CAAC,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
export type ComposerProps = {
|
|
3
|
+
disabled?: boolean;
|
|
4
|
+
/**
|
|
5
|
+
* A turn is running. Kept separate from `disabled` — which is about the input —
|
|
6
|
+
* because this swaps Send for Stop, and a composer disabled for any other reason
|
|
7
|
+
* must not offer to stop a turn that is not there.
|
|
8
|
+
*/
|
|
9
|
+
isStreaming?: boolean;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Changing this focuses the input — used when the panel switches conversation,
|
|
13
|
+
* so starting a new one lands the caret where you are about to type. Ignored on
|
|
14
|
+
* first render, so mounting the panel never steals focus from the host page.
|
|
15
|
+
*/
|
|
16
|
+
focusKey?: string | number | null;
|
|
17
|
+
onSend: (text: string) => void;
|
|
18
|
+
/** Aborts the running turn. Without it a turn going wrong can only be escaped by reloading. */
|
|
19
|
+
onStop?: VoidFunction;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The input row: auto-growing, Enter sends, Shift+Enter breaks the line.
|
|
23
|
+
*
|
|
24
|
+
* `disabled` is driven by the session being busy, not by a local guard — the
|
|
25
|
+
* gateway allows one turn at a time per session and answers a second POST with
|
|
26
|
+
* a 409, and a turn paused on an approval still counts as busy.
|
|
27
|
+
*/
|
|
28
|
+
declare const ComposerView: ({ disabled, isStreaming, placeholder, focusKey, onSend, onStop, }: ComposerProps) => JSX.Element;
|
|
29
|
+
export declare const Composer: import("react").MemoExoticComponent<typeof ComposerView>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=composer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/components/composer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAa,GAAG,EAAiB,MAAM,OAAO,CAAA;AAS1D,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACjC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,+FAA+F;IAC/F,MAAM,CAAC,EAAE,YAAY,CAAA;CACtB,CAAA;AAED;;;;;;GAMG;AACH,QAAA,MAAM,YAAY,sEAOf,aAAa,KAAG,GAAG,CAAC,OAoGtB,CAAA;AAED,eAAO,MAAM,QAAQ,0DAAqB,CAAA"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@tetherto/mdk-react-devkit/primitives';
|
|
3
|
+
import { memo, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
4
|
+
import { AGENT_LABELS } from '../branding';
|
|
5
|
+
import { SendIcon, StopIcon } from './icons';
|
|
6
|
+
/** Grows with the text up to this, then scrolls. Keeps the panel usable. */
|
|
7
|
+
const MAX_ROWS = 6;
|
|
8
|
+
/**
|
|
9
|
+
* The input row: auto-growing, Enter sends, Shift+Enter breaks the line.
|
|
10
|
+
*
|
|
11
|
+
* `disabled` is driven by the session being busy, not by a local guard — the
|
|
12
|
+
* gateway allows one turn at a time per session and answers a second POST with
|
|
13
|
+
* a 409, and a turn paused on an approval still counts as busy.
|
|
14
|
+
*/
|
|
15
|
+
const ComposerView = ({ disabled = false, isStreaming = false, placeholder = AGENT_LABELS.placeholder, focusKey = null, onSend, onStop, }) => {
|
|
16
|
+
const [value, setValue] = useState('');
|
|
17
|
+
const textAreaRef = useRef(null);
|
|
18
|
+
const refocusRef = useRef(false);
|
|
19
|
+
/**
|
|
20
|
+
* Hands focus back when the turn releases the input.
|
|
21
|
+
*
|
|
22
|
+
* Disabling a focused `<textarea>` makes the browser blur it, so without this
|
|
23
|
+
* the caret is gone after every message and the next one needs a click. Armed
|
|
24
|
+
* only by an actual submit, so a turn finishing never steals focus from
|
|
25
|
+
* somewhere else the operator has moved to.
|
|
26
|
+
*/
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (disabled || !refocusRef.current)
|
|
29
|
+
return;
|
|
30
|
+
refocusRef.current = false;
|
|
31
|
+
textAreaRef.current?.focus();
|
|
32
|
+
}, [disabled]);
|
|
33
|
+
// Focus on a conversation change, but not on the first render — mounting the
|
|
34
|
+
// panel (or a route page) must not pull focus out of whatever the host had.
|
|
35
|
+
const lastFocusKeyRef = useRef(undefined);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const previous = lastFocusKeyRef.current;
|
|
38
|
+
lastFocusKeyRef.current = focusKey;
|
|
39
|
+
// `disabled` is a dependency only so the focus lands after the input is
|
|
40
|
+
// usable again; a change to it alone must not count as a conversation
|
|
41
|
+
// switch, or every finished turn would grab focus.
|
|
42
|
+
if (previous === undefined || previous === focusKey || disabled)
|
|
43
|
+
return;
|
|
44
|
+
textAreaRef.current?.focus();
|
|
45
|
+
}, [focusKey, disabled]);
|
|
46
|
+
// The devkit `TextArea` is a plain `<textarea>` with no auto-grow, so the
|
|
47
|
+
// height is measured here: reset first, otherwise scrollHeight only ever
|
|
48
|
+
// grows and the box never shrinks back after a deletion.
|
|
49
|
+
useLayoutEffect(() => {
|
|
50
|
+
const node = textAreaRef.current;
|
|
51
|
+
if (!node)
|
|
52
|
+
return;
|
|
53
|
+
node.style.height = 'auto';
|
|
54
|
+
const lineHeight = Number.parseFloat(globalThis.getComputedStyle(node).lineHeight) || 20;
|
|
55
|
+
node.style.height = `${Math.min(node.scrollHeight, lineHeight * MAX_ROWS)}px`;
|
|
56
|
+
}, [value]);
|
|
57
|
+
const submit = () => {
|
|
58
|
+
const text = value.trim();
|
|
59
|
+
if (!text || disabled)
|
|
60
|
+
return;
|
|
61
|
+
setValue('');
|
|
62
|
+
refocusRef.current = true;
|
|
63
|
+
onSend(text);
|
|
64
|
+
};
|
|
65
|
+
const handleSubmit = (event) => {
|
|
66
|
+
event.preventDefault();
|
|
67
|
+
submit();
|
|
68
|
+
};
|
|
69
|
+
const handleKeyDown = (event) => {
|
|
70
|
+
// `isComposing` guards IME candidate selection, where Enter commits the
|
|
71
|
+
// candidate rather than the message.
|
|
72
|
+
if (event.key !== 'Enter' || event.shiftKey || event.nativeEvent.isComposing)
|
|
73
|
+
return;
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
submit();
|
|
76
|
+
};
|
|
77
|
+
return (_jsxs("form", { className: "mdk-agent-composer", onSubmit: handleSubmit, children: [_jsx("textarea", { ref: textAreaRef, className: "mdk-agent-composer__input", rows: 1, value: value, placeholder: placeholder, "aria-label": AGENT_LABELS.composer, disabled: disabled, onChange: (event) => setValue(event.target.value), onKeyDown: handleKeyDown }), isStreaming && onStop
|
|
78
|
+
? (_jsx(Button, { type: "button", variant: "primary", className: "mdk-agent-composer__send", "aria-label": "Stop", icon: _jsx(StopIcon, { size: 18 }), onClick: onStop }))
|
|
79
|
+
: (_jsx(Button, { type: "submit", variant: "primary", className: "mdk-agent-composer__send", "aria-label": "Send", disabled: disabled || value.trim().length === 0, icon: _jsx(SendIcon, { size: 18 }) }))] }));
|
|
80
|
+
};
|
|
81
|
+
export const Composer = memo(ComposerView);
|
|
82
|
+
//# sourceMappingURL=composer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"composer.js","sourceRoot":"","sources":["../../src/components/composer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
import type { Conversation } from '../core/conversation-store';
|
|
3
|
+
export type ConversationListProps = {
|
|
4
|
+
conversations: Conversation[];
|
|
5
|
+
activeId: string | null;
|
|
6
|
+
/** The conversation with a turn in flight, if any. */
|
|
7
|
+
runningId?: string | null;
|
|
8
|
+
onSelect: (id: string) => void;
|
|
9
|
+
onCreate: VoidFunction;
|
|
10
|
+
/** Omit to hide the delete affordance entirely. */
|
|
11
|
+
onDelete?: (id: string) => void;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Past conversations, newest first.
|
|
15
|
+
*
|
|
16
|
+
* "Running" is local truth, not server truth: the gateway keeps sessions in
|
|
17
|
+
* memory per process and exposes no way to list them, so the only turn this
|
|
18
|
+
* client can know about is the one it is streaming itself.
|
|
19
|
+
*/
|
|
20
|
+
export declare const ConversationList: ({ conversations, activeId, runningId, onSelect, onCreate, onDelete, }: ConversationListProps) => JSX.Element;
|
|
21
|
+
//# sourceMappingURL=conversation-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-list.d.ts","sourceRoot":"","sources":["../../src/components/conversation-list.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAGhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAK9D,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,EAAE,YAAY,EAAE,CAAA;IAC7B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,QAAQ,EAAE,YAAY,CAAA;IACtB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,0EAO1B,qBAAqB,KAAG,GAAG,CAAC,OA0G9B,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@tetherto/mdk-react-devkit/primitives';
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { AGENT_LABELS } from '../branding';
|
|
5
|
+
import { formatConversationMeta } from '../core/format';
|
|
6
|
+
import { MessagePlusIcon, TrashIcon } from './icons';
|
|
7
|
+
/**
|
|
8
|
+
* Past conversations, newest first.
|
|
9
|
+
*
|
|
10
|
+
* "Running" is local truth, not server truth: the gateway keeps sessions in
|
|
11
|
+
* memory per process and exposes no way to list them, so the only turn this
|
|
12
|
+
* client can know about is the one it is streaming itself.
|
|
13
|
+
*/
|
|
14
|
+
export const ConversationList = ({ conversations, activeId, runningId = null, onSelect, onCreate, onDelete, }) => {
|
|
15
|
+
// Deleting drops a transcript that exists nowhere else — the gateway keeps no
|
|
16
|
+
// history — so it takes two clicks rather than a modal.
|
|
17
|
+
const [confirmingId, setConfirmingId] = useState(null);
|
|
18
|
+
// A conversation that disappears (deleted elsewhere, or storage cleared) must
|
|
19
|
+
// not leave the row behind it stuck in the confirming state.
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (confirmingId && !conversations.some((c) => c.id === confirmingId))
|
|
22
|
+
setConfirmingId(null);
|
|
23
|
+
}, [conversations, confirmingId]);
|
|
24
|
+
return (_jsxs("div", { className: "mdk-agent-conversations", children: [_jsxs("div", { className: "mdk-agent-conversations__scroll", children: [conversations.length === 0
|
|
25
|
+
? _jsx("p", { className: "mdk-agent-conversations__empty", children: "No conversations yet." })
|
|
26
|
+
: null, conversations.map((conversation) => {
|
|
27
|
+
const isRunning = conversation.id === runningId;
|
|
28
|
+
const isConfirming = conversation.id === confirmingId;
|
|
29
|
+
return (_jsxs("div", { className: `mdk-agent-conversations__row${conversation.id === activeId ? ' mdk-agent-conversations__row--active' : ''}`, children: [_jsxs("button", { type: "button", className: "mdk-agent-conversations__open", onClick: () => onSelect(conversation.id), children: [_jsx("span", { className: "mdk-agent-conversations__title", children: conversation.title }), _jsx("span", { className: "mdk-agent-conversations__meta", children: formatConversationMeta(conversation.updatedAt, conversation.messages.length) })] }), isConfirming
|
|
30
|
+
? (_jsxs("div", { className: "mdk-agent-conversations__confirm", children: [_jsx("button", { type: "button", className: "mdk-agent-conversations__confirm-yes", onClick: () => {
|
|
31
|
+
setConfirmingId(null);
|
|
32
|
+
onDelete?.(conversation.id);
|
|
33
|
+
}, children: "Delete" }), _jsx("button", { type: "button", className: "mdk-agent-conversations__confirm-no", onClick: () => setConfirmingId(null), children: "Cancel" })] }))
|
|
34
|
+
: (_jsxs(_Fragment, { children: [_jsx("span", { className: `mdk-agent-conversations__badge mdk-agent-conversations__badge--${isRunning ? 'running' : 'idle'}`, children: isRunning ? 'Running' : 'Idle' }), onDelete
|
|
35
|
+
? (_jsx("button", { type: "button", className: "mdk-agent-conversations__delete",
|
|
36
|
+
// Deleting the conversation whose turn is streaming would
|
|
37
|
+
// strand the open stream with nowhere to write its result.
|
|
38
|
+
disabled: isRunning, "aria-label": `Delete conversation: ${conversation.title}`, onClick: () => setConfirmingId(conversation.id), children: _jsx(TrashIcon, { size: 14 }) }))
|
|
39
|
+
: null] }))] }, conversation.id));
|
|
40
|
+
})] }), _jsx("div", { className: "mdk-agent-conversations__footer", children: _jsx(Button, { variant: "outline", size: "sm", fullWidth: true, icon: _jsx(MessagePlusIcon, { size: 16 }), onClick: onCreate, children: AGENT_LABELS.newConversation }) })] }));
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=conversation-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-list.js","sourceRoot":"","sources":["../../src/components/conversation-list.tsx"],"names":[],"mappings":"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"}
|