@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,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator-readable text for the failures a turn can end on.
|
|
3
|
+
*
|
|
4
|
+
* `reduceTurn` stores whatever the `error` event carried, which is a bare code
|
|
5
|
+
* like `ERR_AGENT_STREAM_TRUNCATED` — useful in a bug report, meaningless in a
|
|
6
|
+
* transcript. The raw code stays on the message; this is applied at render time,
|
|
7
|
+
* so the stored history keeps the diagnosable value.
|
|
8
|
+
*/
|
|
9
|
+
/** Shown when a turn dies before the gateway said anything useful. */
|
|
10
|
+
export declare const GENERIC_TURN_ERROR = "The agent could not complete that request.";
|
|
11
|
+
/**
|
|
12
|
+
* Maps a code to a sentence, passing anything that is not one through unchanged.
|
|
13
|
+
*
|
|
14
|
+
* The producer's codes are all `ERR_*`, so a value in that shape that is not in
|
|
15
|
+
* the table is a code this release has never seen — it becomes the generic
|
|
16
|
+
* sentence rather than being shown verbatim. Anything else is already prose.
|
|
17
|
+
*/
|
|
18
|
+
export declare function describeAgentError(error: string | null | undefined): string | null;
|
|
19
|
+
//# sourceMappingURL=error-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-text.d.ts","sourceRoot":"","sources":["../../src/core/error-text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,+CAA+C,CAAA;AAW9E;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAIlF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator-readable text for the failures a turn can end on.
|
|
3
|
+
*
|
|
4
|
+
* `reduceTurn` stores whatever the `error` event carried, which is a bare code
|
|
5
|
+
* like `ERR_AGENT_STREAM_TRUNCATED` — useful in a bug report, meaningless in a
|
|
6
|
+
* transcript. The raw code stays on the message; this is applied at render time,
|
|
7
|
+
* so the stored history keeps the diagnosable value.
|
|
8
|
+
*/
|
|
9
|
+
import { ERR_STREAM_IDLE, ERR_STREAM_TRUNCATED } from './sse';
|
|
10
|
+
import { AGENT_ERROR } from './transport';
|
|
11
|
+
/** Shown when a turn dies before the gateway said anything useful. */
|
|
12
|
+
export const GENERIC_TURN_ERROR = 'The agent could not complete that request.';
|
|
13
|
+
const TEXT_FOR_CODE = {
|
|
14
|
+
[AGENT_ERROR.UNAVAILABLE]: 'The agent is not configured on this gateway.',
|
|
15
|
+
[AGENT_ERROR.TURN_ACTIVE]: 'A turn is already running in this conversation.',
|
|
16
|
+
[AGENT_ERROR.TEXT_REQUIRED]: 'Type a message first.',
|
|
17
|
+
[AGENT_ERROR.SESSION_NOT_FOUND]: 'That conversation is no longer open on the gateway.',
|
|
18
|
+
[ERR_STREAM_TRUNCATED]: 'The connection to the agent ended before the answer finished.',
|
|
19
|
+
[ERR_STREAM_IDLE]: 'The agent stopped responding, so the connection was assumed lost.',
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Maps a code to a sentence, passing anything that is not one through unchanged.
|
|
23
|
+
*
|
|
24
|
+
* The producer's codes are all `ERR_*`, so a value in that shape that is not in
|
|
25
|
+
* the table is a code this release has never seen — it becomes the generic
|
|
26
|
+
* sentence rather than being shown verbatim. Anything else is already prose.
|
|
27
|
+
*/
|
|
28
|
+
export function describeAgentError(error) {
|
|
29
|
+
if (!error)
|
|
30
|
+
return null;
|
|
31
|
+
if (TEXT_FOR_CODE[error])
|
|
32
|
+
return TEXT_FOR_CODE[error];
|
|
33
|
+
return error.startsWith('ERR_') ? GENERIC_TURN_ERROR : error;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=error-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-text.js","sourceRoot":"","sources":["../../src/core/error-text.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,4CAA4C,CAAA;AAE9E,MAAM,aAAa,GAA2B;IAC5C,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,8CAA8C;IACzE,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,iDAAiD;IAC5E,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,uBAAuB;IACpD,CAAC,WAAW,CAAC,iBAAiB,CAAC,EAAE,qDAAqD;IACtF,CAAC,oBAAoB,CAAC,EAAE,+DAA+D;IACvF,CAAC,eAAe,CAAC,EAAE,mEAAmE;CACvF,CAAA;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAgC;IACjE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,IAAI,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,aAAa,CAAC,KAAK,CAAC,CAAA;IACrD,OAAO,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,CAAA;AAC9D,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent event contract, redeclared for the browser.
|
|
3
|
+
*
|
|
4
|
+
* The producer of record is `backend/core/agent/src/events.js`. That module is
|
|
5
|
+
* Node-only ESM inside a private package with no `exports` map, and importing it
|
|
6
|
+
* would pull the model provider and the MCP client into the bundle — so the six
|
|
7
|
+
* events are restated here instead. `src/core/events.contract.test.ts` reads the
|
|
8
|
+
* backend file and fails if the two ever drift.
|
|
9
|
+
*/
|
|
10
|
+
/** Bump only alongside the producer's `CONTRACT_VERSION`. */
|
|
11
|
+
export declare const AGENT_CONTRACT_VERSION = "v1";
|
|
12
|
+
/** Reference these constants, never the raw strings, so a typo is a type error. */
|
|
13
|
+
export declare const EVENT: {
|
|
14
|
+
readonly TOKEN: 'token';
|
|
15
|
+
readonly TOOL_CALL: 'tool_call';
|
|
16
|
+
readonly TOOL_RESULT: 'tool_result';
|
|
17
|
+
readonly PENDING_APPROVAL: 'pending_approval';
|
|
18
|
+
readonly ERROR: 'error';
|
|
19
|
+
readonly DONE: 'done';
|
|
20
|
+
};
|
|
21
|
+
export type EventType = (typeof EVENT)[keyof typeof EVENT];
|
|
22
|
+
/** `done` and `error` only. Exactly one of them ends a turn, and it is always last. */
|
|
23
|
+
export declare const TERMINAL_EVENTS: readonly ["done", "error"];
|
|
24
|
+
export type TerminalEventType = (typeof TERMINAL_EVENTS)[number];
|
|
25
|
+
export type ToolArgs = Record<string, unknown>;
|
|
26
|
+
/** A chunk of the answer, to append to the open bubble. */
|
|
27
|
+
export type TokenEvent = {
|
|
28
|
+
type: typeof EVENT.TOKEN;
|
|
29
|
+
text: string;
|
|
30
|
+
};
|
|
31
|
+
/** The model chose a tool. Always followed by a `tool_result` with the same `name`. */
|
|
32
|
+
export type ToolCallEvent = {
|
|
33
|
+
type: typeof EVENT.TOOL_CALL;
|
|
34
|
+
name: string;
|
|
35
|
+
args: ToolArgs;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The tool answered. `isError` means the call itself failed; `contractViolation`
|
|
39
|
+
* — which the producer only ever emits together with `isError` — means the tool
|
|
40
|
+
* answered but the answer broke its verb's contract.
|
|
41
|
+
*/
|
|
42
|
+
export type ToolResultEvent = {
|
|
43
|
+
type: typeof EVENT.TOOL_RESULT;
|
|
44
|
+
name: string;
|
|
45
|
+
text: string;
|
|
46
|
+
isError?: boolean;
|
|
47
|
+
contractViolation?: string;
|
|
48
|
+
/**
|
|
49
|
+
* How long the operator held the turn at the approval prompt, on a gated call only.
|
|
50
|
+
* Subtract it before showing a duration: the gap between a `tool_call` and its
|
|
51
|
+
* `tool_result` spans the wait, so a card left open for a minute reads as a minute
|
|
52
|
+
* of tool.
|
|
53
|
+
*/
|
|
54
|
+
approvalWaitMs?: number;
|
|
55
|
+
};
|
|
56
|
+
/** A write tool is waiting on the operator. The stream PAUSES here. */
|
|
57
|
+
export type PendingApprovalEvent = {
|
|
58
|
+
type: typeof EVENT.PENDING_APPROVAL;
|
|
59
|
+
name: string;
|
|
60
|
+
args: ToolArgs;
|
|
61
|
+
};
|
|
62
|
+
/** Terminal. Distinct from a decline, which is tokens + `done` with no `tool_call`. */
|
|
63
|
+
export type ErrorEvent = {
|
|
64
|
+
type: typeof EVENT.ERROR;
|
|
65
|
+
error: string;
|
|
66
|
+
};
|
|
67
|
+
/** Terminal. */
|
|
68
|
+
export type DoneEvent = {
|
|
69
|
+
type: typeof EVENT.DONE;
|
|
70
|
+
text?: string;
|
|
71
|
+
usage?: Record<string, unknown>;
|
|
72
|
+
};
|
|
73
|
+
export type AgentEvent = TokenEvent | ToolCallEvent | ToolResultEvent | PendingApprovalEvent | ErrorEvent | DoneEvent;
|
|
74
|
+
/**
|
|
75
|
+
* What actually arrives on the wire: the event's own fields with the gateway's
|
|
76
|
+
* envelope spread onto the same flat object, not nested under a key.
|
|
77
|
+
*/
|
|
78
|
+
export type WireEvent = AgentEvent & {
|
|
79
|
+
/** Correlates every event of one turn. */
|
|
80
|
+
turnId: string;
|
|
81
|
+
/** Monotonic per turn — ordering and dedup. */
|
|
82
|
+
seq: number;
|
|
83
|
+
/** Stamped by the gateway on every event, despite being optional in the docs. */
|
|
84
|
+
ts: number;
|
|
85
|
+
/** Present ONLY on `pending_approval`. Echo it back to decide. */
|
|
86
|
+
approvalId?: string;
|
|
87
|
+
};
|
|
88
|
+
export declare function isTerminal(event: Pick<AgentEvent, 'type'>): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Structural check only — enough to decide whether we know how to render it.
|
|
91
|
+
*
|
|
92
|
+
* Contract invariant 5: unknown event types MUST be ignored, not thrown on. That
|
|
93
|
+
* is what lets the producer add event types without a breaking release, so this
|
|
94
|
+
* returns false rather than raising.
|
|
95
|
+
*/
|
|
96
|
+
export declare function isAgentEvent(value: unknown): value is AgentEvent;
|
|
97
|
+
export declare function isWireEvent(value: unknown): value is WireEvent;
|
|
98
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,6DAA6D;AAC7D,eAAO,MAAM,sBAAsB,OAAO,CAAA;AAE1C,mFAAmF;AACnF,eAAO,MAAM,KAAK;aAChB,KAAK,EAAE,OAAO;aACd,SAAS,EAAE,WAAW;aACtB,WAAW,EAAE,aAAa;aAC1B,gBAAgB,EAAE,kBAAkB;aACpC,KAAK,EAAE,OAAO;aACd,IAAI,EAAE,MAAM;CACJ,CAAA;AAEV,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,OAAO,KAAK,CAAC,CAAA;AAE1D,uFAAuF;AACvF,eAAO,MAAM,eAAe,4BAAqC,CAAA;AAEjE,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAEhE,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE9C,2DAA2D;AAC3D,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,OAAO,KAAK,CAAC,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnE,uFAAuF;AACvF,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,OAAO,KAAK,CAAC,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AAE1F;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,OAAO,KAAK,CAAC,WAAW,CAAA;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,uEAAuE;AACvE,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,OAAO,KAAK,CAAC,gBAAgB,CAAA;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,QAAQ,CAAA;CACf,CAAA;AAED,uFAAuF;AACvF,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,OAAO,KAAK,CAAC,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpE,gBAAgB;AAChB,MAAM,MAAM,SAAS,GAAG;IACtB,IAAI,EAAE,OAAO,KAAK,CAAC,IAAI,CAAA;IACvB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,UAAU,GAClB,UAAU,GACV,aAAa,GACb,eAAe,GACf,oBAAoB,GACpB,UAAU,GACV,SAAS,CAAA;AAEb;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG;IACnC,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAA;IACd,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAA;IACX,iFAAiF;IACjF,EAAE,EAAE,MAAM,CAAA;IACV,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAKD,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,OAAO,CAEnE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAMhE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAI9D"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The agent event contract, redeclared for the browser.
|
|
3
|
+
*
|
|
4
|
+
* The producer of record is `backend/core/agent/src/events.js`. That module is
|
|
5
|
+
* Node-only ESM inside a private package with no `exports` map, and importing it
|
|
6
|
+
* would pull the model provider and the MCP client into the bundle — so the six
|
|
7
|
+
* events are restated here instead. `src/core/events.contract.test.ts` reads the
|
|
8
|
+
* backend file and fails if the two ever drift.
|
|
9
|
+
*/
|
|
10
|
+
import isPlainObject from 'lodash/isPlainObject';
|
|
11
|
+
/** Bump only alongside the producer's `CONTRACT_VERSION`. */
|
|
12
|
+
export const AGENT_CONTRACT_VERSION = 'v1';
|
|
13
|
+
/** Reference these constants, never the raw strings, so a typo is a type error. */
|
|
14
|
+
export const EVENT = {
|
|
15
|
+
TOKEN: 'token',
|
|
16
|
+
TOOL_CALL: 'tool_call',
|
|
17
|
+
TOOL_RESULT: 'tool_result',
|
|
18
|
+
PENDING_APPROVAL: 'pending_approval',
|
|
19
|
+
ERROR: 'error',
|
|
20
|
+
DONE: 'done',
|
|
21
|
+
};
|
|
22
|
+
/** `done` and `error` only. Exactly one of them ends a turn, and it is always last. */
|
|
23
|
+
export const TERMINAL_EVENTS = [EVENT.DONE, EVENT.ERROR];
|
|
24
|
+
const EVENT_TYPES = new Set(Object.values(EVENT));
|
|
25
|
+
const TERMINAL_TYPES = new Set(TERMINAL_EVENTS);
|
|
26
|
+
export function isTerminal(event) {
|
|
27
|
+
return TERMINAL_TYPES.has(event.type);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Structural check only — enough to decide whether we know how to render it.
|
|
31
|
+
*
|
|
32
|
+
* Contract invariant 5: unknown event types MUST be ignored, not thrown on. That
|
|
33
|
+
* is what lets the producer add event types without a breaking release, so this
|
|
34
|
+
* returns false rather than raising.
|
|
35
|
+
*/
|
|
36
|
+
export function isAgentEvent(value) {
|
|
37
|
+
// Every event arrives from `JSON.parse`, so a plain object is exactly the shape
|
|
38
|
+
// to accept — anything else on the wire is malformed by definition.
|
|
39
|
+
if (!isPlainObject(value))
|
|
40
|
+
return false;
|
|
41
|
+
const type = value.type;
|
|
42
|
+
return typeof type === 'string' && EVENT_TYPES.has(type);
|
|
43
|
+
}
|
|
44
|
+
export function isWireEvent(value) {
|
|
45
|
+
if (!isAgentEvent(value))
|
|
46
|
+
return false;
|
|
47
|
+
const { turnId, seq } = value;
|
|
48
|
+
return typeof turnId === 'string' && typeof seq === 'number';
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/core/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,aAAa,MAAM,sBAAsB,CAAA;AAEhD,6DAA6D;AAC7D,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAA;AAE1C,mFAAmF;AACnF,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,KAAK,EAAE,OAAO;IACd,SAAS,EAAE,WAAW;IACtB,WAAW,EAAE,aAAa;IAC1B,gBAAgB,EAAE,kBAAkB;IACpC,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;CACJ,CAAA;AAIV,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAU,CAAA;AAwEjE,MAAM,WAAW,GAAG,IAAI,GAAG,CAAS,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;AACzD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAS,eAAe,CAAC,CAAA;AAEvD,MAAM,UAAU,UAAU,CAAC,KAA+B;IACxD,OAAO,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AACvC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,gFAAgF;IAChF,oEAAoE;IACpE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACvC,MAAM,IAAI,GAAI,KAA4B,CAAC,IAAI,CAAA;IAC/C,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AAC1D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACtC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,KAA2B,CAAA;IACnD,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,GAAG,KAAK,QAAQ,CAAA;AAC9D,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting for the conversation list.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately local-time, not the fleet timezone from `timezoneStore`: these
|
|
5
|
+
* are records of when the operator had a conversation in this browser, not
|
|
6
|
+
* telemetry timestamps.
|
|
7
|
+
*/
|
|
8
|
+
/** `Today` / `Yesterday` / `Jul 28`. */
|
|
9
|
+
export declare function formatDay(at: number, now?: number): string;
|
|
10
|
+
/** `14:32`, 24-hour so it lines up in a monospace column. */
|
|
11
|
+
export declare function formatClock(at: number): string;
|
|
12
|
+
export declare function formatMessageCount(count: number): string;
|
|
13
|
+
/**
|
|
14
|
+
* `4 conversations · 1 running` — the header line for the conversations view.
|
|
15
|
+
*
|
|
16
|
+
* "Running" counts only what this client is streaming: the gateway holds
|
|
17
|
+
* sessions in memory per process and exposes no way to list them, so a turn
|
|
18
|
+
* started in another tab is invisible here.
|
|
19
|
+
*/
|
|
20
|
+
export declare function formatConversationSummary(count: number, running: boolean): string;
|
|
21
|
+
/** `Today · 14:32 · 6 messages` */
|
|
22
|
+
export declare function formatConversationMeta(updatedAt: number, messageCount: number, now?: number): string;
|
|
23
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/core/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAUH,wCAAwC;AACxC,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAKtE;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAGjF;AAED,mCAAmC;AACnC,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,CAIR"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting for the conversation list.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately local-time, not the fleet timezone from `timezoneStore`: these
|
|
5
|
+
* are records of when the operator had a conversation in this browser, not
|
|
6
|
+
* telemetry timestamps.
|
|
7
|
+
*/
|
|
8
|
+
const DAY_MS = 86_400_000;
|
|
9
|
+
function startOfDay(at) {
|
|
10
|
+
const date = new Date(at);
|
|
11
|
+
date.setHours(0, 0, 0, 0);
|
|
12
|
+
return date.getTime();
|
|
13
|
+
}
|
|
14
|
+
/** `Today` / `Yesterday` / `Jul 28`. */
|
|
15
|
+
export function formatDay(at, now = Date.now()) {
|
|
16
|
+
const days = Math.round((startOfDay(now) - startOfDay(at)) / DAY_MS);
|
|
17
|
+
if (days <= 0)
|
|
18
|
+
return 'Today';
|
|
19
|
+
if (days === 1)
|
|
20
|
+
return 'Yesterday';
|
|
21
|
+
return new Date(at).toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
|
|
22
|
+
}
|
|
23
|
+
/** `14:32`, 24-hour so it lines up in a monospace column. */
|
|
24
|
+
export function formatClock(at) {
|
|
25
|
+
return new Date(at).toLocaleTimeString(undefined, {
|
|
26
|
+
hour: '2-digit',
|
|
27
|
+
minute: '2-digit',
|
|
28
|
+
hour12: false,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function formatMessageCount(count) {
|
|
32
|
+
return count === 1 ? '1 message' : `${count} messages`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* `4 conversations · 1 running` — the header line for the conversations view.
|
|
36
|
+
*
|
|
37
|
+
* "Running" counts only what this client is streaming: the gateway holds
|
|
38
|
+
* sessions in memory per process and exposes no way to list them, so a turn
|
|
39
|
+
* started in another tab is invisible here.
|
|
40
|
+
*/
|
|
41
|
+
export function formatConversationSummary(count, running) {
|
|
42
|
+
const conversations = count === 1 ? '1 conversation' : `${count} conversations`;
|
|
43
|
+
return running ? `${conversations} · 1 running` : conversations;
|
|
44
|
+
}
|
|
45
|
+
/** `Today · 14:32 · 6 messages` */
|
|
46
|
+
export function formatConversationMeta(updatedAt, messageCount, now) {
|
|
47
|
+
return [formatDay(updatedAt, now), formatClock(updatedAt), formatMessageCount(messageCount)].join(' · ');
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/core/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,GAAG,UAAU,CAAA;AAEzB,SAAS,UAAU,CAAC,EAAU;IAC5B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAA;IACzB,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACzB,OAAO,IAAI,CAAC,OAAO,EAAE,CAAA;AACvB,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,SAAS,CAAC,EAAU,EAAE,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;IAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAA;IACpE,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,OAAO,CAAA;IAC7B,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,WAAW,CAAA;IAClC,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAA;AACvF,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,kBAAkB,CAAC,SAAS,EAAE;QAChD,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,KAAK;KACd,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,WAAW,CAAA;AACxD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAa,EAAE,OAAgB;IACvE,MAAM,aAAa,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,KAAK,gBAAgB,CAAA;IAC/E,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,aAAa,cAAc,CAAC,CAAC,CAAC,aAAa,CAAA;AACjE,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,sBAAsB,CACpC,SAAiB,EACjB,YAAoB,EACpB,GAAY;IAEZ,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,EAAE,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAC/F,KAAK,CACN,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The headless half of the package: contract types, SSE transport, the turn
|
|
3
|
+
* reducer and the conversation store. No React, no DOM.
|
|
4
|
+
*
|
|
5
|
+
* Exported as a subpath (`@tetherto/mdk-ui-agent/core`) so a consumer building
|
|
6
|
+
* its own chat surface can reuse the protocol work without the components.
|
|
7
|
+
*/
|
|
8
|
+
export type { ChatMessage, Conversation, ConversationActions, ConversationState, ConversationStorage, ConversationStore, ConversationStoreOptions, MessageRole, } from './conversation-store';
|
|
9
|
+
export { CONVERSATION_STORAGE_KEY, conversationStore, createConversationStore, deriveTitle, MAX_CONVERSATIONS, MAX_MESSAGES_PER_CONVERSATION, MAX_PERSISTED_TOOL_TEXT_CHARS, mergeConversations, MESSAGE_ROLE, newConversation, } from './conversation-store';
|
|
10
|
+
export { describeAgentError, GENERIC_TURN_ERROR } from './error-text';
|
|
11
|
+
export type { AgentEvent, DoneEvent, ErrorEvent, EventType, PendingApprovalEvent, TerminalEventType, TokenEvent, ToolArgs, ToolCallEvent, ToolResultEvent, WireEvent, } from './events';
|
|
12
|
+
export { AGENT_CONTRACT_VERSION, EVENT, isAgentEvent, isTerminal, isWireEvent, TERMINAL_EVENTS, } from './events';
|
|
13
|
+
export { formatClock, formatConversationMeta, formatConversationSummary, formatDay, formatMessageCount, } from './format';
|
|
14
|
+
export { clipToBoundary, unwrapWholeAnswerFence } from './markdown';
|
|
15
|
+
export type { LeakedToolCall } from './prose';
|
|
16
|
+
export { parseLeakedToolCall } from './prose';
|
|
17
|
+
export { isSmallTalk } from './small-talk';
|
|
18
|
+
export type { ReadAgentStreamOptions, ReadFramesOptions } from './sse';
|
|
19
|
+
export { APPROVAL_IDLE_TIMEOUT_MS, ERR_STREAM_IDLE, ERR_STREAM_TRUNCATED, FIRST_EVENT_IDLE_TIMEOUT_MS, parseFrame, readAgentStream, readFrames, STREAM_IDLE_TIMEOUT_MS, StreamIdleError, } from './sse';
|
|
20
|
+
export type { ToolLabels } from './tool-label';
|
|
21
|
+
export { formatDuration, humanizeToolName, toolLabel } from './tool-label';
|
|
22
|
+
export type { AgentErrorCode, AgentTransportOptions, StreamTurnOptions, TokenSource, } from './transport';
|
|
23
|
+
export { AGENT_ERROR, AGENT_ROUTE_PREFIX, AgentApiError, createSession, decideApproval, deleteSession, streamTurn, } from './transport';
|
|
24
|
+
export type { AgentTurnState, PendingApproval, ToolStatus, ToolStep, TurnStatus } from './turn';
|
|
25
|
+
export { abortTurn, createTurnState, failTurn, isRejectionResult, isTurnSettled, reduceTurn, TOOL_STATUS, TURN_STATUS, } from './turn';
|
|
26
|
+
export { uuid } from './uuid';
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,YAAY,EACV,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,wBAAwB,EACxB,WAAW,GACZ,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,uBAAuB,EACvB,WAAW,EACX,iBAAiB,EACjB,6BAA6B,EAC7B,6BAA6B,EAC7B,kBAAkB,EAClB,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAErE,YAAY,EACV,UAAU,EACV,SAAS,EACT,UAAU,EACV,SAAS,EACT,oBAAoB,EACpB,iBAAiB,EACjB,UAAU,EACV,QAAQ,EACR,aAAa,EACb,eAAe,EACf,SAAS,GACV,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,sBAAsB,EACtB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,WAAW,EACX,eAAe,GAChB,MAAM,UAAU,CAAA;AAEjB,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,yBAAyB,EACzB,SAAS,EACT,kBAAkB,GACnB,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAEnE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAE7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,YAAY,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAA;AACtE,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,oBAAoB,EACpB,2BAA2B,EAC3B,UAAU,EACV,eAAe,EACf,UAAU,EACV,sBAAsB,EACtB,eAAe,GAChB,MAAM,OAAO,CAAA;AAEd,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE1E,YAAY,EACV,cAAc,EACd,qBAAqB,EACrB,iBAAiB,EACjB,WAAW,GACZ,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,EACb,UAAU,GACX,MAAM,aAAa,CAAA;AAEpB,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAC/F,OAAO,EACL,SAAS,EACT,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,WAAW,EACX,WAAW,GACZ,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The headless half of the package: contract types, SSE transport, the turn
|
|
3
|
+
* reducer and the conversation store. No React, no DOM.
|
|
4
|
+
*
|
|
5
|
+
* Exported as a subpath (`@tetherto/mdk-ui-agent/core`) so a consumer building
|
|
6
|
+
* its own chat surface can reuse the protocol work without the components.
|
|
7
|
+
*/
|
|
8
|
+
export { CONVERSATION_STORAGE_KEY, conversationStore, createConversationStore, deriveTitle, MAX_CONVERSATIONS, MAX_MESSAGES_PER_CONVERSATION, MAX_PERSISTED_TOOL_TEXT_CHARS, mergeConversations, MESSAGE_ROLE, newConversation, } from './conversation-store';
|
|
9
|
+
export { describeAgentError, GENERIC_TURN_ERROR } from './error-text';
|
|
10
|
+
export { AGENT_CONTRACT_VERSION, EVENT, isAgentEvent, isTerminal, isWireEvent, TERMINAL_EVENTS, } from './events';
|
|
11
|
+
export { formatClock, formatConversationMeta, formatConversationSummary, formatDay, formatMessageCount, } from './format';
|
|
12
|
+
export { clipToBoundary, unwrapWholeAnswerFence } from './markdown';
|
|
13
|
+
export { parseLeakedToolCall } from './prose';
|
|
14
|
+
export { isSmallTalk } from './small-talk';
|
|
15
|
+
export { APPROVAL_IDLE_TIMEOUT_MS, ERR_STREAM_IDLE, ERR_STREAM_TRUNCATED, FIRST_EVENT_IDLE_TIMEOUT_MS, parseFrame, readAgentStream, readFrames, STREAM_IDLE_TIMEOUT_MS, StreamIdleError, } from './sse';
|
|
16
|
+
export { formatDuration, humanizeToolName, toolLabel } from './tool-label';
|
|
17
|
+
export { AGENT_ERROR, AGENT_ROUTE_PREFIX, AgentApiError, createSession, decideApproval, deleteSession, streamTurn, } from './transport';
|
|
18
|
+
export { abortTurn, createTurnState, failTurn, isRejectionResult, isTurnSettled, reduceTurn, TOOL_STATUS, TURN_STATUS, } from './turn';
|
|
19
|
+
export { uuid } from './uuid';
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAYH,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,uBAAuB,EACvB,WAAW,EACX,iBAAiB,EACjB,6BAA6B,EAC7B,6BAA6B,EAC7B,kBAAkB,EAClB,YAAY,EACZ,eAAe,GAChB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAerE,OAAO,EACL,sBAAsB,EACtB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,WAAW,EACX,eAAe,GAChB,MAAM,UAAU,CAAA;AAEjB,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,yBAAyB,EACzB,SAAS,EACT,kBAAkB,GACnB,MAAM,UAAU,CAAA;AAEjB,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAInE,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG1C,OAAO,EACL,wBAAwB,EACxB,eAAe,EACf,oBAAoB,EACpB,2BAA2B,EAC3B,UAAU,EACV,eAAe,EACf,UAAU,EACV,sBAAsB,EACtB,eAAe,GAChB,MAAM,OAAO,CAAA;AAGd,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAQ1E,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,EACb,UAAU,GACX,MAAM,aAAa,CAAA;AAGpB,OAAO,EACL,SAAS,EACT,eAAe,EACf,QAAQ,EACR,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,WAAW,EACX,WAAW,GACZ,MAAM,QAAQ,CAAA;AAEf,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two text repairs applied before an answer is rendered as markdown.
|
|
3
|
+
*
|
|
4
|
+
* Both exist because the model decides its own output format and nothing in the
|
|
5
|
+
* charter constrains it: the panel has to read whatever comes back.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Unwraps an answer the model wrapped entirely in a ```markdown fence.
|
|
9
|
+
*
|
|
10
|
+
* A common quirk: asked for a formatted answer, the model returns the formatted
|
|
11
|
+
* answer inside a fence, and the panel renders the whole reply as one code block
|
|
12
|
+
* — table syntax, bullets and all. Only the whole-answer case is unwrapped, and
|
|
13
|
+
* only when the fence actually closes at the end: an ordinary code fence, an
|
|
14
|
+
* unclosed fence mid-stream, and a fence with prose after it are all left alone.
|
|
15
|
+
*/
|
|
16
|
+
export declare function unwrapWholeAnswerFence(text: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Cuts `text` to roughly `max` characters, on the last paragraph or line break
|
|
19
|
+
* before the limit.
|
|
20
|
+
*
|
|
21
|
+
* Slicing markdown at an arbitrary character can split a fence or a table row and
|
|
22
|
+
* make the preview render as something the answer never said, so the cut lands on
|
|
23
|
+
* a block boundary where there is one within reach.
|
|
24
|
+
*/
|
|
25
|
+
export declare function clipToBoundary(text: string, max: number): string;
|
|
26
|
+
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/core/markdown.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW3D;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAShE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two text repairs applied before an answer is rendered as markdown.
|
|
3
|
+
*
|
|
4
|
+
* Both exist because the model decides its own output format and nothing in the
|
|
5
|
+
* charter constrains it: the panel has to read whatever comes back.
|
|
6
|
+
*/
|
|
7
|
+
/** Opening fence of a whole-answer wrapper: ```markdown / ```md, nothing else on the line. */
|
|
8
|
+
const MARKDOWN_FENCE_OPEN = /^`{3,}\s*(?:markdown|md)\s*$/i;
|
|
9
|
+
/** Any closing fence line. */
|
|
10
|
+
const FENCE_CLOSE = /^`{3,}\s*$/;
|
|
11
|
+
/**
|
|
12
|
+
* Unwraps an answer the model wrapped entirely in a ```markdown fence.
|
|
13
|
+
*
|
|
14
|
+
* A common quirk: asked for a formatted answer, the model returns the formatted
|
|
15
|
+
* answer inside a fence, and the panel renders the whole reply as one code block
|
|
16
|
+
* — table syntax, bullets and all. Only the whole-answer case is unwrapped, and
|
|
17
|
+
* only when the fence actually closes at the end: an ordinary code fence, an
|
|
18
|
+
* unclosed fence mid-stream, and a fence with prose after it are all left alone.
|
|
19
|
+
*/
|
|
20
|
+
export function unwrapWholeAnswerFence(text) {
|
|
21
|
+
const lines = text.trim().split('\n');
|
|
22
|
+
const first = lines[0];
|
|
23
|
+
if (lines.length < 2 || first === undefined || !MARKDOWN_FENCE_OPEN.test(first))
|
|
24
|
+
return text;
|
|
25
|
+
// Paired with the last fence line, which is a whole-answer wrapper only if it is
|
|
26
|
+
// also the final line: an unclosed fence, or one with prose after it, is not.
|
|
27
|
+
const last = lines[lines.length - 1];
|
|
28
|
+
if (last === undefined || !FENCE_CLOSE.test(last))
|
|
29
|
+
return text;
|
|
30
|
+
return lines.slice(1, -1).join('\n');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Cuts `text` to roughly `max` characters, on the last paragraph or line break
|
|
34
|
+
* before the limit.
|
|
35
|
+
*
|
|
36
|
+
* Slicing markdown at an arbitrary character can split a fence or a table row and
|
|
37
|
+
* make the preview render as something the answer never said, so the cut lands on
|
|
38
|
+
* a block boundary where there is one within reach.
|
|
39
|
+
*/
|
|
40
|
+
export function clipToBoundary(text, max) {
|
|
41
|
+
if (text.length <= max)
|
|
42
|
+
return text;
|
|
43
|
+
const head = text.slice(0, max);
|
|
44
|
+
const paragraph = head.lastIndexOf('\n\n');
|
|
45
|
+
const line = head.lastIndexOf('\n');
|
|
46
|
+
const cut = paragraph > max / 2 ? paragraph : line > max / 2 ? line : max;
|
|
47
|
+
return head.slice(0, cut).trimEnd();
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/core/markdown.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8FAA8F;AAC9F,MAAM,mBAAmB,GAAG,+BAA+B,CAAA;AAC3D,8BAA8B;AAC9B,MAAM,WAAW,GAAG,YAAY,CAAA;AAEhC;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACtB,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAE5F,iFAAiF;IACjF,8EAA8E;IAC9E,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IACpC,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAE9D,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACtC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,GAAW;IACtD,IAAI,IAAI,CAAC,MAAM,IAAI,GAAG;QAAE,OAAO,IAAI,CAAA;IAEnC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;IACnC,MAAM,GAAG,GAAG,SAAS,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAA;IAEzE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAA;AACrC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects a tool call that leaked into the answer.
|
|
3
|
+
*
|
|
4
|
+
* The agent loop is prompt-based, not native function calling: the model is told
|
|
5
|
+
* to reply with only `{"tool": "<name>", "args": { ... }}`, and
|
|
6
|
+
* `backend/core/agent/src/loop.js` decides prose-vs-tool-call from the first
|
|
7
|
+
* non-whitespace character being `{`. A model that wraps its JSON in a ```json
|
|
8
|
+
* fence therefore leads with a backtick, is classified as prose, and the raw
|
|
9
|
+
* tool call is streamed to the operator as if it were the answer. Measured at
|
|
10
|
+
* roughly one read in eight against `gemma3:4b` — common enough to handle.
|
|
11
|
+
*
|
|
12
|
+
* Rendering that verbatim is the single most confusing failure mode in the
|
|
13
|
+
* stack, so it is detected and labelled instead.
|
|
14
|
+
*/
|
|
15
|
+
export type LeakedToolCall = {
|
|
16
|
+
tool: string;
|
|
17
|
+
args: Record<string, unknown>;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Returns the leaked call when `text` is nothing but a tool call, else null.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately strict: the JSON must be the whole message once code fences are
|
|
23
|
+
* stripped. A genuine answer that merely quotes some JSON keeps rendering as
|
|
24
|
+
* prose.
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseLeakedToolCall(text: string): LeakedToolCall | null;
|
|
27
|
+
//# sourceMappingURL=prose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prose.d.ts","sourceRoot":"","sources":["../../src/core/prose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAqCH,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAA;AAE5E;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAkBvE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects a tool call that leaked into the answer.
|
|
3
|
+
*
|
|
4
|
+
* The agent loop is prompt-based, not native function calling: the model is told
|
|
5
|
+
* to reply with only `{"tool": "<name>", "args": { ... }}`, and
|
|
6
|
+
* `backend/core/agent/src/loop.js` decides prose-vs-tool-call from the first
|
|
7
|
+
* non-whitespace character being `{`. A model that wraps its JSON in a ```json
|
|
8
|
+
* fence therefore leads with a backtick, is classified as prose, and the raw
|
|
9
|
+
* tool call is streamed to the operator as if it were the answer. Measured at
|
|
10
|
+
* roughly one read in eight against `gemma3:4b` — common enough to handle.
|
|
11
|
+
*
|
|
12
|
+
* Rendering that verbatim is the single most confusing failure mode in the
|
|
13
|
+
* stack, so it is detected and labelled instead.
|
|
14
|
+
*/
|
|
15
|
+
const FENCE = /^```[a-z]*\s*|\n?```$/gi;
|
|
16
|
+
/**
|
|
17
|
+
* The first balanced JSON object in `text`, string-aware so a brace inside a
|
|
18
|
+
* string literal does not close it. Mirrors `extractJsonObject` in the producer.
|
|
19
|
+
*/
|
|
20
|
+
function extractJsonObject(text) {
|
|
21
|
+
const start = text.indexOf('{');
|
|
22
|
+
if (start === -1)
|
|
23
|
+
return null;
|
|
24
|
+
let depth = 0;
|
|
25
|
+
let inString = false;
|
|
26
|
+
let escaped = false;
|
|
27
|
+
for (let index = start; index < text.length; index += 1) {
|
|
28
|
+
const char = text[index];
|
|
29
|
+
if (inString) {
|
|
30
|
+
if (escaped)
|
|
31
|
+
escaped = false;
|
|
32
|
+
else if (char === '\\')
|
|
33
|
+
escaped = true;
|
|
34
|
+
else if (char === '"')
|
|
35
|
+
inString = false;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (char === '"') {
|
|
39
|
+
inString = true;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (char === '{')
|
|
43
|
+
depth += 1;
|
|
44
|
+
else if (char === '}') {
|
|
45
|
+
depth -= 1;
|
|
46
|
+
if (depth === 0)
|
|
47
|
+
return text.slice(start, index + 1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Returns the leaked call when `text` is nothing but a tool call, else null.
|
|
54
|
+
*
|
|
55
|
+
* Deliberately strict: the JSON must be the whole message once code fences are
|
|
56
|
+
* stripped. A genuine answer that merely quotes some JSON keeps rendering as
|
|
57
|
+
* prose.
|
|
58
|
+
*/
|
|
59
|
+
export function parseLeakedToolCall(text) {
|
|
60
|
+
const stripped = text.trim().replace(FENCE, '').trim();
|
|
61
|
+
if (!stripped.startsWith('{'))
|
|
62
|
+
return null;
|
|
63
|
+
const raw = extractJsonObject(stripped);
|
|
64
|
+
if (raw === null || raw !== stripped)
|
|
65
|
+
return null;
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(raw);
|
|
68
|
+
if (typeof parsed.tool !== 'string')
|
|
69
|
+
return null;
|
|
70
|
+
const args = typeof parsed.args === 'object' && parsed.args !== null
|
|
71
|
+
? parsed.args
|
|
72
|
+
: {};
|
|
73
|
+
return { tool: parsed.tool, args };
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=prose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prose.js","sourceRoot":"","sources":["../../src/core/prose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,KAAK,GAAG,yBAAyB,CAAA;AAEvC;;;GAGG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;IAC/B,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAE7B,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,IAAI,OAAO,GAAG,KAAK,CAAA;IAEnB,KAAK,IAAI,KAAK,GAAG,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QACxB,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,OAAO;gBAAE,OAAO,GAAG,KAAK,CAAA;iBACvB,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAO,GAAG,IAAI,CAAA;iBACjC,IAAI,IAAI,KAAK,GAAG;gBAAE,QAAQ,GAAG,KAAK,CAAA;YACvC,SAAQ;QACV,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,QAAQ,GAAG,IAAI,CAAA;YACf,SAAQ;QACV,CAAC;QACD,IAAI,IAAI,KAAK,GAAG;YAAE,KAAK,IAAI,CAAC,CAAA;aACvB,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACtB,KAAK,IAAI,CAAC,CAAA;YACV,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAID;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IACtD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAE1C,MAAM,GAAG,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAA;IACvC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAEjD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuC,CAAA;QACpE,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QAChD,MAAM,IAAI,GACN,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;YACvD,CAAC,CAAE,MAAM,CAAC,IAAgC;YAC1C,CAAC,CAAC,EAAE,CAAA;QACR,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,CAAA;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Was an operator message conversation rather than a question about the fleet?
|
|
3
|
+
*
|
|
4
|
+
* This exists for one caller: whether an answer is worth marking as having used no tools. The
|
|
5
|
+
* marker is there to catch an answer that should have consulted the fleet and did not — and
|
|
6
|
+
* against a greeting, a thank-you, or "what can you do", calling nothing was the right move, so
|
|
7
|
+
* every one of those was carrying a warning about a failure that had not happened.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately asymmetric. All three gates below must agree before a message counts as small
|
|
10
|
+
* talk, and any fleet word vetoes it outright, because the two mistakes are not equal: missing a
|
|
11
|
+
* greeting costs one redundant marker, while reading a fleet question as small talk hides exactly
|
|
12
|
+
* the warning the marker exists to give.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isSmallTalk(text: string): boolean;
|
|
15
|
+
//# sourceMappingURL=small-talk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"small-talk.d.ts","sourceRoot":"","sources":["../../src/core/small-talk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAqBH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAWjD"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Was an operator message conversation rather than a question about the fleet?
|
|
3
|
+
*
|
|
4
|
+
* This exists for one caller: whether an answer is worth marking as having used no tools. The
|
|
5
|
+
* marker is there to catch an answer that should have consulted the fleet and did not — and
|
|
6
|
+
* against a greeting, a thank-you, or "what can you do", calling nothing was the right move, so
|
|
7
|
+
* every one of those was carrying a warning about a failure that had not happened.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately asymmetric. All three gates below must agree before a message counts as small
|
|
10
|
+
* talk, and any fleet word vetoes it outright, because the two mistakes are not equal: missing a
|
|
11
|
+
* greeting costs one redundant marker, while reading a fleet question as small talk hides exactly
|
|
12
|
+
* the warning the marker exists to give.
|
|
13
|
+
*/
|
|
14
|
+
/** Openers where the greeting *is* the message — nothing is being asked of the fleet. */
|
|
15
|
+
const CONVERSATIONAL = [
|
|
16
|
+
/^(?:hi|hey|hello|yo|hiya|howdy|greetings|good (?:morning|afternoon|evening|day))\b/,
|
|
17
|
+
/^(?:how are you|how's it going|what's up|sup)\b/,
|
|
18
|
+
/^(?:thanks|thank you|thx|ty|cheers|nice|great|cool|awesome|perfect|got it|ok|okay|sure|sorry|never mind|nvm)\b/,
|
|
19
|
+
/^(?:bye|goodbye|see you|see ya|later)\b/,
|
|
20
|
+
/^(?:who are you|what are you|what can you do|what do you do|can you help|help)\b/,
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Words that make a message about the fleet however it opens, so "hey, list the miners" is not
|
|
24
|
+
* small talk. Kept to unambiguous terms: "up" and "down" are fleet state but also "what's up".
|
|
25
|
+
*/
|
|
26
|
+
const FLEET_TERMS = /\b(?:device|devices|miner|miners|asic|asics|rig|rigs|worker|workers|site|container|containers|pool|pools|sensor|sensors|powermeter|powermeters|power meter|hashrate|hash rate|temperature|telemetry|fleet|rank|reboot|restart|offline|online|status|list|show|count)\b/;
|
|
27
|
+
/** Past this, a message is doing more than saying hello, whatever it opened with. */
|
|
28
|
+
const MAX_WORDS = 6;
|
|
29
|
+
export function isSmallTalk(text) {
|
|
30
|
+
const normalized = text
|
|
31
|
+
.toLowerCase()
|
|
32
|
+
.replaceAll('’', '\'') // a typographic apostrophe still spells "what's"
|
|
33
|
+
.trim();
|
|
34
|
+
if (normalized.length === 0)
|
|
35
|
+
return false;
|
|
36
|
+
if (FLEET_TERMS.test(normalized))
|
|
37
|
+
return false;
|
|
38
|
+
if (normalized.split(/\s+/).length > MAX_WORDS)
|
|
39
|
+
return false;
|
|
40
|
+
return CONVERSATIONAL.some((pattern) => pattern.test(normalized));
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=small-talk.js.map
|