@kanzo-tech/ai 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/ai-mark.d.ts +1 -1
  2. package/dist/ai-mark.d.ts.map +1 -1
  3. package/dist/ai-mark.js +12 -12
  4. package/dist/ai-mark.js.map +1 -1
  5. package/dist/assist.d.ts +71 -0
  6. package/dist/assist.d.ts.map +1 -0
  7. package/dist/assist.js +394 -0
  8. package/dist/assist.js.map +1 -0
  9. package/dist/chat.d.ts +41 -0
  10. package/dist/chat.d.ts.map +1 -0
  11. package/dist/chat.js +77 -0
  12. package/dist/chat.js.map +1 -0
  13. package/dist/engine.d.ts +101 -0
  14. package/dist/engine.d.ts.map +1 -0
  15. package/dist/engine.js +197 -0
  16. package/dist/engine.js.map +1 -0
  17. package/dist/index.d.ts +7 -24
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +7 -56
  20. package/dist/index.js.map +1 -1
  21. package/dist/message.js +25 -66
  22. package/dist/message.js.map +1 -1
  23. package/dist/prompt-input.d.ts +2 -2
  24. package/dist/prompt-input.d.ts.map +1 -1
  25. package/dist/prompt-input.js +47 -49
  26. package/dist/prompt-input.js.map +1 -1
  27. package/dist/proposals.d.ts +28 -0
  28. package/dist/proposals.d.ts.map +1 -0
  29. package/dist/proposals.js +56 -0
  30. package/dist/proposals.js.map +1 -0
  31. package/dist/reasoning.d.ts +4 -3
  32. package/dist/reasoning.d.ts.map +1 -1
  33. package/dist/reasoning.js +60 -55
  34. package/dist/reasoning.js.map +1 -1
  35. package/dist/tool.d.ts +25 -50
  36. package/dist/tool.d.ts.map +1 -1
  37. package/dist/tool.js +83 -95
  38. package/dist/tool.js.map +1 -1
  39. package/package.json +18 -34
  40. package/dist/complete.d.ts +0 -87
  41. package/dist/complete.d.ts.map +0 -1
  42. package/dist/complete.js +0 -278
  43. package/dist/complete.js.map +0 -1
  44. package/dist/message-part.d.ts +0 -122
  45. package/dist/message-part.d.ts.map +0 -1
  46. package/dist/message-part.js +0 -8
  47. package/dist/message-part.js.map +0 -1
  48. package/dist/message-text.d.ts +0 -44
  49. package/dist/message-text.d.ts.map +0 -1
  50. package/dist/message-text.js +0 -49
  51. package/dist/message-text.js.map +0 -1
  52. package/dist/suggest.d.ts +0 -82
  53. package/dist/suggest.d.ts.map +0 -1
  54. package/dist/suggest.js +0 -123
  55. package/dist/suggest.js.map +0 -1
  56. package/dist/task.d.ts +0 -48
  57. package/dist/task.d.ts.map +0 -1
  58. package/dist/task.js +0 -91
  59. package/dist/task.js.map +0 -1
  60. package/dist/types.d.ts +0 -10
  61. package/dist/types.d.ts.map +0 -1
  62. package/dist/use-ai.d.ts +0 -170
  63. package/dist/use-ai.d.ts.map +0 -1
  64. package/dist/use-ai.js +0 -186
  65. package/dist/use-ai.js.map +0 -1
package/dist/chat.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ import { UIMessage } from '@kanzo-tech/llm';
2
+ import { UseChatHelpers } from '@ai-sdk/react';
3
+ import { ToolPart } from './tool.js';
4
+ import * as React from "react";
5
+ /** Every word `Chat` draws. English by default. */
6
+ export interface ChatTranslations {
7
+ /** The composer's placeholder. */
8
+ placeholder: string;
9
+ /** Announced to a screen reader with the error that stopped an answer. */
10
+ failed: string;
11
+ }
12
+ /**
13
+ * How a host draws its own tools' results, by tool name. Each gets the call's part — typed, when the
14
+ * host's messages are (`InferAgentUIMessage<typeof agent>`) — and draws inside the tool's frame,
15
+ * under its input: a result table, a chart, an action that takes the result somewhere.
16
+ */
17
+ export type ChatToolRenderers = Record<string, (part: ToolPart) => React.ReactNode>;
18
+ /** What `Chat` reads from `useChat` — the helpers, not the hook, so any chat state can drive it. */
19
+ type ChatState<M extends UIMessage> = Pick<UseChatHelpers<M>, "messages" | "status" | "error" | "sendMessage" | "stop" | "regenerate">;
20
+ export interface ChatProps<M extends UIMessage> {
21
+ /** `useChat(...)`'s return. */
22
+ chat: ChatState<M>;
23
+ tools?: ChatToolRenderers;
24
+ /** What the panel says before the first question. */
25
+ empty?: React.ReactNode;
26
+ /** Questions to start from, while the conversation is empty. Pressing one asks it. */
27
+ suggestions?: string[];
28
+ translations?: Partial<ChatTranslations>;
29
+ className?: string;
30
+ }
31
+ /**
32
+ * A conversation with a model, whole: the transcript, the model's reasoning and calls as they
33
+ * happen, markdown that streams, and a composer that sends, stops and retries. A host brings the
34
+ * chat (`useChat`) and draws its own tools' results; everything else is here.
35
+ *
36
+ * Each part is drawn from the AI SDK's own message parts, so nothing is translated on the way in:
37
+ * text as markdown, `reasoning` folded away, every tool call in its frame with the SDK's state.
38
+ */
39
+ export declare function Chat<M extends UIMessage>(props: ChatProps<M>): React.JSX.Element;
40
+ export {};
41
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../src/chat.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAYpD,OAAO,EAAwD,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAEhG,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAC/B,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;CAChB;AAOD;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;AAEpF,oGAAoG;AACpG,KAAK,SAAS,CAAC,CAAC,SAAS,SAAS,IAAI,IAAI,CACxC,cAAc,CAAC,CAAC,CAAC,EACjB,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,aAAa,GAAG,MAAM,GAAG,YAAY,CACxE,CAAC;AAEF,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,SAAS;IAC5C,+BAA+B;IAC/B,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,qDAAqD;IACrD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,sFAAsF;IACtF,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,qBAiG5D"}
package/dist/chat.js ADDED
@@ -0,0 +1,77 @@
1
+ "use client";
2
+ import { jsxs as o, jsx as t } from "react/jsx-runtime";
3
+ import * as N from "react";
4
+ import { isToolUIPart as C, getToolName as S } from "@kanzo-tech/llm";
5
+ import { Suggestions as b, Suggestion as I, cn as w } from "@kanzo-tech/ui";
6
+ import { Conversation as M, ConversationContent as k, ConversationScrollButton as P } from "./conversation.js";
7
+ import { MessageMarkdown as j } from "./markdown.js";
8
+ import { MessageList as D, Message as H, MessageContent as L } from "./message.js";
9
+ import { PromptInput as R, PromptInputTextarea as $, PromptInputToolbar as A, PromptInputSubmit as B } from "./prompt-input.js";
10
+ import { Reasoning as E } from "./reasoning.js";
11
+ import { Tool as G, ToolHeader as O, ToolContent as U, ToolInput as q, ToolOutput as z } from "./tool.js";
12
+ const F = {
13
+ placeholder: "Ask anything…",
14
+ failed: "The answer stopped:"
15
+ };
16
+ function ee(p) {
17
+ const { chat: s, tools: h = {}, empty: f, suggestions: a, translations: g, className: x } = p, i = { ...F, ...g }, [l, c] = N.useState(""), m = s.status === "submitted" || s.status === "streaming", v = s.messages.at(-1), d = (e) => {
18
+ e.trim() && (s.sendMessage({ text: e }), c(""));
19
+ };
20
+ return /* @__PURE__ */ o("div", { className: w("flex min-h-0 flex-1 flex-col gap-3", x), "data-slot": "chat", children: [
21
+ /* @__PURE__ */ o(M, { children: [
22
+ /* @__PURE__ */ o(k, { children: [
23
+ s.messages.length === 0 ? /* @__PURE__ */ o("div", { className: "flex flex-1 flex-col items-center justify-center gap-4 text-center", "data-slot": "chat-empty", children: [
24
+ f,
25
+ a && a.length > 0 && /* @__PURE__ */ t(b, { className: "justify-center", children: a.map((e) => /* @__PURE__ */ t(I, { onSelect: d, value: e, children: e }, e)) })
26
+ ] }) : /* @__PURE__ */ t(D, { children: s.messages.map((e) => /* @__PURE__ */ t(H, { role: e.role, children: /* @__PURE__ */ t(L, { children: e.parts.map((r, y) => {
27
+ const n = `${e.id}-${y}`, T = m && e === v;
28
+ if (r.type === "text")
29
+ return e.role === "user" ? /* @__PURE__ */ t("p", { className: "whitespace-pre-wrap break-words", children: r.text }, n) : /* @__PURE__ */ t(j, { streaming: T && r.state === "streaming", children: r.text }, n);
30
+ if (r.type === "reasoning") return /* @__PURE__ */ t(E, { part: r }, n);
31
+ if (C(r)) {
32
+ const u = h[S(r)];
33
+ return /* @__PURE__ */ o(G, { part: r, children: [
34
+ /* @__PURE__ */ t(O, {}),
35
+ /* @__PURE__ */ o(U, { children: [
36
+ /* @__PURE__ */ t(q, {}),
37
+ u && r.state === "output-available" ? u(r) : /* @__PURE__ */ t(z, {})
38
+ ] })
39
+ ] }, n);
40
+ }
41
+ return null;
42
+ }) }) }, e.id)) }),
43
+ s.error && /* @__PURE__ */ o("p", { className: "text-destructive-foreground text-sm", "data-slot": "chat-error", role: "alert", children: [
44
+ /* @__PURE__ */ o("span", { className: "sr-only", children: [
45
+ i.failed,
46
+ " "
47
+ ] }),
48
+ s.error.message
49
+ ] })
50
+ ] }),
51
+ /* @__PURE__ */ t(P, {})
52
+ ] }),
53
+ /* @__PURE__ */ o(
54
+ R,
55
+ {
56
+ onSubmit: (e) => {
57
+ e.preventDefault(), m ? s.stop() : s.status === "error" && !l.trim() ? s.regenerate() : d(l);
58
+ },
59
+ children: [
60
+ /* @__PURE__ */ t(
61
+ $,
62
+ {
63
+ onChange: (e) => c(e.target.value),
64
+ placeholder: i.placeholder,
65
+ value: l
66
+ }
67
+ ),
68
+ /* @__PURE__ */ t(A, { children: /* @__PURE__ */ t(B, { status: s.status }) })
69
+ ]
70
+ }
71
+ )
72
+ ] });
73
+ }
74
+ export {
75
+ ee as Chat
76
+ };
77
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sources":["../src/chat.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { getToolName, isToolUIPart, type UIMessage } from \"@kanzo-tech/llm\";\nimport type { UseChatHelpers } from \"@ai-sdk/react\";\nimport { cn, Suggestion, Suggestions } from \"@kanzo-tech/ui\";\nimport { Conversation, ConversationContent, ConversationScrollButton } from \"./conversation.js\";\nimport { MessageMarkdown } from \"./markdown.js\";\nimport { Message, MessageContent, MessageList } from \"./message.js\";\nimport {\n PromptInput,\n PromptInputSubmit,\n PromptInputTextarea,\n PromptInputToolbar,\n} from \"./prompt-input.js\";\nimport { Reasoning } from \"./reasoning.js\";\nimport { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput, type ToolPart } from \"./tool.js\";\n\n/** Every word `Chat` draws. English by default. */\nexport interface ChatTranslations {\n /** The composer's placeholder. */\n placeholder: string;\n /** Announced to a screen reader with the error that stopped an answer. */\n failed: string;\n}\n\nconst ENGLISH: ChatTranslations = {\n placeholder: \"Ask anything…\",\n failed: \"The answer stopped:\",\n};\n\n/**\n * How a host draws its own tools' results, by tool name. Each gets the call's part — typed, when the\n * host's messages are (`InferAgentUIMessage<typeof agent>`) — and draws inside the tool's frame,\n * under its input: a result table, a chart, an action that takes the result somewhere.\n */\nexport type ChatToolRenderers = Record<string, (part: ToolPart) => React.ReactNode>;\n\n/** What `Chat` reads from `useChat` — the helpers, not the hook, so any chat state can drive it. */\ntype ChatState<M extends UIMessage> = Pick<\n UseChatHelpers<M>,\n \"messages\" | \"status\" | \"error\" | \"sendMessage\" | \"stop\" | \"regenerate\"\n>;\n\nexport interface ChatProps<M extends UIMessage> {\n /** `useChat(...)`'s return. */\n chat: ChatState<M>;\n tools?: ChatToolRenderers;\n /** What the panel says before the first question. */\n empty?: React.ReactNode;\n /** Questions to start from, while the conversation is empty. Pressing one asks it. */\n suggestions?: string[];\n translations?: Partial<ChatTranslations>;\n className?: string;\n}\n\n/**\n * A conversation with a model, whole: the transcript, the model's reasoning and calls as they\n * happen, markdown that streams, and a composer that sends, stops and retries. A host brings the\n * chat (`useChat`) and draws its own tools' results; everything else is here.\n *\n * Each part is drawn from the AI SDK's own message parts, so nothing is translated on the way in:\n * text as markdown, `reasoning` folded away, every tool call in its frame with the SDK's state.\n */\nexport function Chat<M extends UIMessage>(props: ChatProps<M>) {\n const { chat, tools = {}, empty, suggestions, translations, className } = props;\n const t = { ...ENGLISH, ...translations };\n const [draft, setDraft] = React.useState(\"\");\n const busy = chat.status === \"submitted\" || chat.status === \"streaming\";\n const last = chat.messages.at(-1);\n\n const ask = (text: string) => {\n if (!text.trim()) return;\n void chat.sendMessage({ text });\n setDraft(\"\");\n };\n\n return (\n <div className={cn(\"flex min-h-0 flex-1 flex-col gap-3\", className)} data-slot=\"chat\">\n <Conversation>\n <ConversationContent>\n {chat.messages.length === 0 ? (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-4 text-center\" data-slot=\"chat-empty\">\n {empty}\n {suggestions && suggestions.length > 0 && (\n <Suggestions className=\"justify-center\">\n {suggestions.map((s) => (\n <Suggestion key={s} onSelect={ask} value={s}>\n {s}\n </Suggestion>\n ))}\n </Suggestions>\n )}\n </div>\n ) : (\n <MessageList>\n {chat.messages.map((m) => (\n <Message key={m.id} role={m.role}>\n <MessageContent>\n {m.parts.map((part, i) => {\n const key = `${m.id}-${i}`;\n const streaming = busy && m === last;\n if (part.type === \"text\") {\n return m.role === \"user\" ? (\n <p className=\"whitespace-pre-wrap break-words\" key={key}>\n {part.text}\n </p>\n ) : (\n <MessageMarkdown key={key} streaming={streaming && part.state === \"streaming\"}>\n {part.text}\n </MessageMarkdown>\n );\n }\n if (part.type === \"reasoning\") return <Reasoning key={key} part={part} />;\n if (isToolUIPart(part)) {\n const draw = tools[getToolName(part)];\n return (\n <Tool key={key} part={part}>\n <ToolHeader />\n <ToolContent>\n <ToolInput />\n {draw && part.state === \"output-available\" ? draw(part) : <ToolOutput />}\n </ToolContent>\n </Tool>\n );\n }\n return null;\n })}\n </MessageContent>\n </Message>\n ))}\n </MessageList>\n )}\n {chat.error && (\n <p className=\"text-destructive-foreground text-sm\" data-slot=\"chat-error\" role=\"alert\">\n <span className=\"sr-only\">{t.failed} </span>\n {chat.error.message}\n </p>\n )}\n </ConversationContent>\n <ConversationScrollButton />\n </Conversation>\n <PromptInput\n onSubmit={(event) => {\n event.preventDefault();\n if (busy) void chat.stop();\n else if (chat.status === \"error\" && !draft.trim()) void chat.regenerate();\n else ask(draft);\n }}\n >\n <PromptInputTextarea\n onChange={(event) => setDraft(event.target.value)}\n placeholder={t.placeholder}\n value={draft}\n />\n <PromptInputToolbar>\n <PromptInputSubmit status={chat.status} />\n </PromptInputToolbar>\n </PromptInput>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AA0BA;AAAkC;AACnB;AAEf;AAmCO;AACL;AAOE;AAEW;AAGb;AAEI;AACE;AACG;AAEI;AAAA;AAQC;AASM;AAEA;AACE;AAUF;AACA;AACE;AACA;AAEI;AAAY;AAEV;AAAW;AAC2D;AACxE;AACF;AAGJ;AAAO;AAKjB;AAIE;AAA2B;AAAE;AAAO;AAAC;AACzB;AACd;AAEJ;AAC0B;AAC5B;AACA;AAAC;AAAA;AAEG;AAGc;AAChB;AAEA;AAAA;AAAC;AAAA;AACiD;AACjC;AACR;AAAA;AAAA;AAIT;AAAA;AAAA;AAAA;AAIR;;;;"}
@@ -0,0 +1,101 @@
1
+ /** A value the model offers a field. LSP 3.18's `InlineCompletionItem`, as a field needs it. */
2
+ export interface Proposal {
3
+ text: string;
4
+ /**
5
+ * What taking it replaces, as `[from, to)` in the field's value: an empty range at the caret is a
6
+ * continuation, the whole value is a replacement. Absent for a new item in a list field (tags),
7
+ * where taking it adds rather than edits.
8
+ */
9
+ range?: [from: number, to: number];
10
+ /** Why the model offered it. Shown as the candidate's tooltip. */
11
+ rationale?: string;
12
+ }
13
+ /**
14
+ * What happened to a proposal, for a host that learns from it (HAX G13). The lifecycle VS Code's
15
+ * inline completions report, in one callback: it was **shown**, taken whole (**accepted**) or a word
16
+ * at a time (**partial**), turned down by the reader (**rejected**: Esc, or a candidate's ✕), or
17
+ * left to die because they typed something else (**ignored**).
18
+ */
19
+ export interface AssistEvent {
20
+ kind: "shown" | "accepted" | "partial" | "rejected" | "ignored";
21
+ proposal: Proposal;
22
+ /** The field's accessible name — which field it was. */
23
+ field: string;
24
+ }
25
+ /** Where a request has got to. Internal: no surface exposes it, they draw from it. */
26
+ export type Status = "idle" | "loading" | "ready" | "error";
27
+ /**
28
+ * Open a source and pull it to the end, handing each value to `each`; `false` from `each` stops it.
29
+ * A new run supersedes the one in flight, and resolves to what happened to **this** run, so a loop
30
+ * that outlives its render never reads a stale status.
31
+ */
32
+ export declare function useStream<T>(errorText: string): {
33
+ run: (src: (signal: AbortSignal) => AsyncIterable<T>, each: (value: T) => boolean | void) => Promise<Status>;
34
+ cancel: () => void;
35
+ reset: () => void;
36
+ status: Status;
37
+ error: string | null;
38
+ };
39
+ /**
40
+ * The continuation is appended verbatim, with one safety net: a model that restated the value
41
+ * before continuing gets that prefix dropped, and an echo still arriving renders as nothing until it
42
+ * diverges — painting the value twice is worse than a few frames of nothing.
43
+ */
44
+ export declare function cleanGhost(base: string, cont: string): string;
45
+ /** Below this there is not enough text to continue, so nothing is asked. */
46
+ export declare const MIN_CONTINUE_LENGTH = 4;
47
+ export type Trigger = "invoked" | "automatic";
48
+ export interface ContinuationRequest {
49
+ value: string;
50
+ position: number;
51
+ trigger: Trigger;
52
+ /** Continuations already offered at this point; an alternative must differ from them. */
53
+ avoid: string[];
54
+ signal: AbortSignal;
55
+ }
56
+ /**
57
+ * Ghost text that does not own the value: the field owns it and performs the insertion.
58
+ *
59
+ * **The offer survives typing that agrees with it.** An offer made at `from` stays on the table
60
+ * while what was typed since is a prefix of it, and the ghost is what is left — no request at all.
61
+ * That is LSP's `filterText` rule and the only way to Smart Compose's 60 ms budget: not asking.
62
+ *
63
+ * **Alternatives** are LSP's `Invoked` case: asking again at the same point appends a different
64
+ * offer rather than replacing the one on the table, and `cycle` moves between them (Alt+] / Alt+[).
65
+ */
66
+ export declare function useContinuation(options: {
67
+ source: (request: ContinuationRequest) => AsyncIterable<string>;
68
+ onEvent: (kind: AssistEvent["kind"], proposal: Proposal) => void;
69
+ debounceMs?: number;
70
+ }): {
71
+ ghost: string;
72
+ status: Status;
73
+ error: string | null;
74
+ setValue: (value: string, position?: number) => void;
75
+ ask: (value: string, position?: number) => void;
76
+ cycle: (step: 1 | -1) => void;
77
+ settle: (kind: "accepted" | "partial", text: string) => void;
78
+ dismiss: (why?: "rejected" | "ignored") => void;
79
+ };
80
+ /**
81
+ * A streamed list of whole values, deduped against what the field already holds. A budget rather
82
+ * than a window: nothing refills when one is taken or dismissed.
83
+ */
84
+ export declare function useCandidates(options: {
85
+ source: (request: {
86
+ existing: string[];
87
+ signal: AbortSignal;
88
+ }) => AsyncIterable<Proposal>;
89
+ existing: string[];
90
+ onEvent: (kind: AssistEvent["kind"], proposal: Proposal) => void;
91
+ limit?: number;
92
+ }): {
93
+ items: Proposal[];
94
+ status: Status;
95
+ error: string | null;
96
+ press: () => void;
97
+ take: (t: string) => void;
98
+ dismiss: (t: string) => void;
99
+ cancel: () => void;
100
+ };
101
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAQA,gGAAgG;AAChG,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;IACnC,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAChE,QAAQ,EAAE,QAAQ,CAAC;IACnB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,sFAAsF;AACtF,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAO5D;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM;eAepC,CAAC,MAAM,EAAE,WAAW,KAAK,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,GAAG,IAAI;;;;;EAqCtF;AA+BD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAO7D;AAID,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;AAE9C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,yFAAyF;IACzF,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;CACrB;AAQD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,MAAM,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,aAAa,CAAC,MAAM,CAAC,CAAC;IAChE,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;;;;sBA4FW,MAAM;iBAkBN,MAAM;kBAaP,CAAC,GAAG,CAAC,CAAC;mBAgBN,UAAU,GAAG,SAAS,QAAQ,MAAM;oBA3FpC,UAAU,GAAG,SAAS;EAoHhC;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,MAAM,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,KAAK,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC1F,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;;;;cAuDiD,MAAM;iBAAyC,MAAM;;EACtG"}
package/dist/engine.js ADDED
@@ -0,0 +1,197 @@
1
+ "use client";
2
+ import { useState as I, useRef as y, useCallback as f, useEffect as F, useMemo as H } from "react";
3
+ const U = (c, r) => c instanceof Error && c.message ? c.message : r;
4
+ function O(c) {
5
+ const [r, u] = I("idle"), [g, k] = I(null), W = y("idle"), m = y(void 0), a = f((s, w = null) => {
6
+ W.current = s, u(s), k(w);
7
+ }, []);
8
+ F(() => () => {
9
+ var s;
10
+ return (s = m.current) == null ? void 0 : s.abort();
11
+ }, []);
12
+ const h = f(
13
+ (s, w) => {
14
+ var T;
15
+ (T = m.current) == null || T.abort();
16
+ const l = new AbortController();
17
+ return m.current = l, a("loading"), (async () => {
18
+ try {
19
+ for await (const i of s(l.signal)) {
20
+ if (l.signal.aborted) return "idle";
21
+ if (w(i) === !1) {
22
+ l.abort();
23
+ break;
24
+ }
25
+ }
26
+ } catch (i) {
27
+ return l.signal.aborted ? "idle" : (a("error", U(i, c)), "error");
28
+ }
29
+ return m.current === l && a("ready"), "ready";
30
+ })();
31
+ },
32
+ [c, a]
33
+ ), b = f(() => {
34
+ var s;
35
+ (s = m.current) == null || s.abort(), a("idle");
36
+ }, [a]), n = f(() => {
37
+ W.current !== "loading" && a("idle");
38
+ }, [a]);
39
+ return H(() => ({ run: h, cancel: b, reset: n, status: r, error: g }), [b, g, n, h, r]);
40
+ }
41
+ function V(c) {
42
+ let r, u = !1;
43
+ const g = () => {
44
+ u = !0, r === void 0 && (r = requestAnimationFrame(() => {
45
+ r = void 0, u = !1, c();
46
+ }));
47
+ }, k = () => {
48
+ r !== void 0 && cancelAnimationFrame(r), r = void 0, u = !1;
49
+ };
50
+ return Object.assign(g, { flush: () => {
51
+ const m = u;
52
+ k(), m && c();
53
+ }, cancel: k });
54
+ }
55
+ function z(c, r) {
56
+ const u = c.trimEnd();
57
+ if (!u || !r) return r;
58
+ const g = { base: u.toLowerCase(), cont: r.toLowerCase() };
59
+ return g.cont.startsWith(g.base) ? r.slice(u.length) : g.base.startsWith(g.cont) ? "" : r;
60
+ }
61
+ const D = 4;
62
+ function K(c) {
63
+ const { run: r, cancel: u, status: g, error: k } = O("Couldn’t complete"), [W, m] = I(""), a = y(c);
64
+ a.current = c;
65
+ const h = y([]), b = y(0), n = y({ value: "", position: 0 }), s = y(void 0), w = y(null), l = () => h.current[b.current] ?? null, T = f(() => {
66
+ const t = l();
67
+ if (!t) return "";
68
+ const { value: e, position: d } = n.current;
69
+ if (d < t.from) return "";
70
+ const x = e.slice(t.from, d);
71
+ return t.text.startsWith(x) ? t.text.slice(x.length) : "";
72
+ }, []), i = f(() => {
73
+ const t = l();
74
+ if (!t) return !1;
75
+ const { value: e, position: d } = n.current;
76
+ if (d < t.from) return !1;
77
+ const x = e.slice(t.from, d);
78
+ return t.text.startsWith(x) || !t.done && x.startsWith(t.text);
79
+ }, []), N = (t) => ({ text: t.text, range: [t.from, t.from] }), o = y(void 0);
80
+ o.current ?? (o.current = V(() => {
81
+ const t = T();
82
+ m(t);
83
+ const e = l();
84
+ t && e && w.current !== e && (w.current = e, a.current.onEvent("shown", N(e)));
85
+ }));
86
+ const E = f(
87
+ (t) => {
88
+ var d;
89
+ clearTimeout(s.current), (d = o.current) == null || d.cancel();
90
+ const e = l();
91
+ t && e && w.current === e && a.current.onEvent(t, N(e)), h.current = [], b.current = 0, w.current = null, u(), m("");
92
+ },
93
+ [u]
94
+ ), p = f(
95
+ (t, e, d) => {
96
+ const x = h.current.filter((C) => C.from === e).map((C) => C.text), L = { from: e, text: "", done: !1 };
97
+ h.current = [...h.current.filter((C) => C.from === e), L], b.current = h.current.length - 1;
98
+ const B = t.slice(0, e);
99
+ let j = "";
100
+ r(
101
+ (C) => a.current.source({ value: t, position: e, trigger: d, avoid: x, signal: C }),
102
+ (C) => {
103
+ var A;
104
+ if (l() !== L) return !1;
105
+ j += C, L.text = z(B, j), (A = o.current) == null || A.call(o);
106
+ }
107
+ ).then((C) => {
108
+ var A, q;
109
+ L.done = !0, l() === L && (C === "ready" ? (A = o.current) == null || A.flush() : (q = o.current) == null || q.cancel());
110
+ });
111
+ },
112
+ [r]
113
+ ), v = (t) => t.trim().length < D, S = f(
114
+ (t, e = t.length) => {
115
+ if (n.current = { value: t, position: e }, i()) {
116
+ m(T());
117
+ return;
118
+ }
119
+ E("ignored"), !v(t) && (s.current = setTimeout(
120
+ () => p(t, e, "automatic"),
121
+ a.current.debounceMs ?? 350
122
+ ));
123
+ },
124
+ [i, E, T, p]
125
+ ), M = f(
126
+ (t, e = t.length) => {
127
+ const d = n.current.value === t && n.current.position === e;
128
+ n.current = { value: t, position: e }, d || E("ignored"), clearTimeout(s.current), !v(t) && p(t, e, "invoked");
129
+ },
130
+ [E, p]
131
+ ), R = f(
132
+ (t) => {
133
+ var d;
134
+ const e = b.current + t;
135
+ if (!(e < 0)) {
136
+ if (e >= h.current.length) {
137
+ const { value: x, position: L } = n.current;
138
+ M(x, L);
139
+ return;
140
+ }
141
+ b.current = e, m(T()), (d = o.current) == null || d.call(o);
142
+ }
143
+ },
144
+ [M, T]
145
+ ), _ = f(
146
+ (t, e) => {
147
+ var x;
148
+ l() && a.current.onEvent(t, { text: e, range: [n.current.position, n.current.position] }), t === "accepted" && (h.current = [], b.current = 0, w.current = null, clearTimeout(s.current), (x = o.current) == null || x.cancel(), u(), m(""));
149
+ },
150
+ [u]
151
+ );
152
+ return F(
153
+ () => () => {
154
+ var t;
155
+ clearTimeout(s.current), (t = o.current) == null || t.cancel();
156
+ },
157
+ []
158
+ ), { ghost: W, status: g, error: k, setValue: S, ask: M, cycle: R, settle: _, dismiss: E };
159
+ }
160
+ const G = (c) => c.trim().toLowerCase();
161
+ function P(c) {
162
+ const { run: r, cancel: u, reset: g, status: k, error: W } = O("Couldn’t load suggestions"), [m, a] = I([]), h = y([]), b = y(c);
163
+ b.current = c;
164
+ const n = f((i) => {
165
+ h.current = i, a(i);
166
+ }, []), s = f(() => {
167
+ const { existing: i, limit: N = 6, source: o } = b.current, E = new Set(i.map(G).filter(Boolean)), p = [];
168
+ n([]), r(
169
+ (v) => o({ existing: i, signal: v }),
170
+ (v) => {
171
+ const S = G(v.text);
172
+ return !S || E.has(S) ? !0 : (E.add(S), p.push(v), n([...p]), b.current.onEvent("shown", v), p.length < N);
173
+ }
174
+ );
175
+ }, [n, r]), w = f(() => {
176
+ k !== "loading" && s();
177
+ }, [s, k]), l = f(
178
+ (i, N) => {
179
+ const o = G(i), E = h.current.find((v) => G(v.text) === o);
180
+ E && b.current.onEvent(N, E);
181
+ const p = h.current.filter((v) => G(v.text) !== o);
182
+ n(p), p.length === 0 && g();
183
+ },
184
+ [n, g]
185
+ ), T = f(() => {
186
+ u(), n([]);
187
+ }, [u, n]);
188
+ return { items: m, status: k, error: W, press: w, take: (i) => l(i, "accepted"), dismiss: (i) => l(i, "rejected"), cancel: T };
189
+ }
190
+ export {
191
+ D as MIN_CONTINUE_LENGTH,
192
+ z as cleanGhost,
193
+ P as useCandidates,
194
+ K as useContinuation,
195
+ O as useStream
196
+ };
197
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sources":["../src/engine.ts"],"sourcesContent":["\"use client\";\n\n// The machinery behind `Assist`, and none of it is public. A field offers a **proposal** — text the\n// reader may take — and this file is everything about getting proposals out of a stream and keeping\n// them honest while the reader types: one engine, the inline continuation, and the candidate list.\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\n/** A value the model offers a field. LSP 3.18's `InlineCompletionItem`, as a field needs it. */\nexport interface Proposal {\n text: string;\n /**\n * What taking it replaces, as `[from, to)` in the field's value: an empty range at the caret is a\n * continuation, the whole value is a replacement. Absent for a new item in a list field (tags),\n * where taking it adds rather than edits.\n */\n range?: [from: number, to: number];\n /** Why the model offered it. Shown as the candidate's tooltip. */\n rationale?: string;\n}\n\n/**\n * What happened to a proposal, for a host that learns from it (HAX G13). The lifecycle VS Code's\n * inline completions report, in one callback: it was **shown**, taken whole (**accepted**) or a word\n * at a time (**partial**), turned down by the reader (**rejected**: Esc, or a candidate's ✕), or\n * left to die because they typed something else (**ignored**).\n */\nexport interface AssistEvent {\n kind: \"shown\" | \"accepted\" | \"partial\" | \"rejected\" | \"ignored\";\n proposal: Proposal;\n /** The field's accessible name — which field it was. */\n field: string;\n}\n\n/** Where a request has got to. Internal: no surface exposes it, they draw from it. */\nexport type Status = \"idle\" | \"loading\" | \"ready\" | \"error\";\n\nconst message = (e: unknown, fallback: string) =>\n e instanceof Error && e.message ? e.message : fallback;\n\n// ── The engine ───────────────────────────────────────────────────────────────\n\n/**\n * Open a source and pull it to the end, handing each value to `each`; `false` from `each` stops it.\n * A new run supersedes the one in flight, and resolves to what happened to **this** run, so a loop\n * that outlives its render never reads a stale status.\n */\nexport function useStream<T>(errorText: string) {\n const [status, setStatus] = useState<Status>(\"idle\");\n const [error, setError] = useState<string | null>(null);\n const statusRef = useRef<Status>(\"idle\");\n const ctrl = useRef<AbortController>(undefined);\n\n const set = useCallback((s: Status, e: string | null = null) => {\n statusRef.current = s;\n setStatus(s);\n setError(e);\n }, []);\n\n useEffect(() => () => ctrl.current?.abort(), []);\n\n const run = useCallback(\n (src: (signal: AbortSignal) => AsyncIterable<T>, each: (value: T) => boolean | void) => {\n ctrl.current?.abort();\n const c = new AbortController();\n ctrl.current = c;\n set(\"loading\");\n\n return (async (): Promise<Status> => {\n try {\n for await (const value of src(c.signal)) {\n if (c.signal.aborted) return \"idle\";\n if (each(value) === false) {\n c.abort();\n break;\n }\n }\n } catch (e) {\n if (c.signal.aborted) return \"idle\";\n set(\"error\", message(e, errorText));\n return \"error\";\n }\n if (ctrl.current === c) set(\"ready\");\n return \"ready\";\n })();\n },\n [errorText, set],\n );\n\n const cancel = useCallback(() => {\n ctrl.current?.abort();\n set(\"idle\");\n }, [set]);\n\n const reset = useCallback(() => {\n if (statusRef.current !== \"loading\") set(\"idle\");\n }, [set]);\n\n return useMemo(() => ({ run, cancel, reset, status, error }), [cancel, error, reset, run, status]);\n}\n\n/**\n * Run `paint` at most once a frame, and once more on `flush()`. A token stream arrives faster than\n * the display refreshes, so a `setState` per chunk renders work nobody sees.\n */\nfunction coalesce(paint: () => void) {\n let frame: number | undefined;\n let queued = false;\n const run = () => {\n queued = true;\n if (frame !== undefined) return;\n frame = requestAnimationFrame(() => {\n frame = undefined;\n queued = false;\n paint();\n });\n };\n const cancel = () => {\n if (frame !== undefined) cancelAnimationFrame(frame);\n frame = undefined;\n queued = false;\n };\n const flush = () => {\n const pending = queued;\n cancel();\n if (pending) paint();\n };\n return Object.assign(run, { flush, cancel });\n}\n\n/**\n * The continuation is appended verbatim, with one safety net: a model that restated the value\n * before continuing gets that prefix dropped, and an echo still arriving renders as nothing until it\n * diverges — painting the value twice is worse than a few frames of nothing.\n */\nexport function cleanGhost(base: string, cont: string): string {\n const b = base.trimEnd();\n if (!b || !cont) return cont;\n const lower = { base: b.toLowerCase(), cont: cont.toLowerCase() };\n if (lower.cont.startsWith(lower.base)) return cont.slice(b.length);\n if (lower.base.startsWith(lower.cont)) return \"\";\n return cont;\n}\n\n// ── The continuation: one offer at the caret, with alternatives ──────────────\n\n/** Below this there is not enough text to continue, so nothing is asked. */\nexport const MIN_CONTINUE_LENGTH = 4;\n\nexport type Trigger = \"invoked\" | \"automatic\";\n\nexport interface ContinuationRequest {\n value: string;\n position: number;\n trigger: Trigger;\n /** Continuations already offered at this point; an alternative must differ from them. */\n avoid: string[];\n signal: AbortSignal;\n}\n\ninterface Offer {\n from: number;\n text: string;\n done: boolean;\n}\n\n/**\n * Ghost text that does not own the value: the field owns it and performs the insertion.\n *\n * **The offer survives typing that agrees with it.** An offer made at `from` stays on the table\n * while what was typed since is a prefix of it, and the ghost is what is left — no request at all.\n * That is LSP's `filterText` rule and the only way to Smart Compose's 60 ms budget: not asking.\n *\n * **Alternatives** are LSP's `Invoked` case: asking again at the same point appends a different\n * offer rather than replacing the one on the table, and `cycle` moves between them (Alt+] / Alt+[).\n */\nexport function useContinuation(options: {\n source: (request: ContinuationRequest) => AsyncIterable<string>;\n onEvent: (kind: AssistEvent[\"kind\"], proposal: Proposal) => void;\n debounceMs?: number;\n}) {\n const { run, cancel, status, error } = useStream<string>(\"Couldn’t complete\");\n const [ghost, setGhost] = useState(\"\");\n const opts = useRef(options);\n opts.current = options;\n\n const offers = useRef<Offer[]>([]);\n const index = useRef(0);\n const field = useRef({ value: \"\", position: 0 });\n const timer = useRef<ReturnType<typeof setTimeout>>(undefined);\n const shown = useRef<Offer | null>(null);\n\n const current = () => offers.current[index.current] ?? null;\n\n const rendered = useCallback((): string => {\n const o = current();\n if (!o) return \"\";\n const { value, position } = field.current;\n if (position < o.from) return \"\";\n const typed = value.slice(o.from, position);\n return o.text.startsWith(typed) ? o.text.slice(typed.length) : \"\";\n }, []);\n\n const alive = useCallback((): boolean => {\n const o = current();\n if (!o) return false;\n const { value, position } = field.current;\n if (position < o.from) return false;\n const typed = value.slice(o.from, position);\n // Typing ahead of a stream still arriving is not a mismatch yet: the next chunk may agree.\n return o.text.startsWith(typed) || (!o.done && typed.startsWith(o.text));\n }, []);\n\n const proposal = (o: Offer): Proposal => ({ text: o.text, range: [o.from, o.from] });\n\n const painter = useRef<ReturnType<typeof coalesce>>(undefined);\n painter.current ??= coalesce(() => {\n const next = rendered();\n setGhost(next);\n const o = current();\n if (next && o && shown.current !== o) {\n shown.current = o;\n opts.current.onEvent(\"shown\", proposal(o));\n }\n });\n\n /** Forget every offer. `why` is reported once, for the offer on screen. */\n const drop = useCallback(\n (why?: \"rejected\" | \"ignored\") => {\n clearTimeout(timer.current);\n painter.current?.cancel();\n const o = current();\n if (why && o && shown.current === o) opts.current.onEvent(why, proposal(o));\n offers.current = [];\n index.current = 0;\n shown.current = null;\n cancel();\n setGhost(\"\");\n },\n [cancel],\n );\n\n const start = useCallback(\n (value: string, position: number, trigger: Trigger) => {\n const avoid = offers.current.filter((o) => o.from === position).map((o) => o.text);\n const mine: Offer = { from: position, text: \"\", done: false };\n offers.current = [...offers.current.filter((o) => o.from === position), mine];\n index.current = offers.current.length - 1;\n const base = value.slice(0, position);\n let raw = \"\";\n void run(\n (signal) => opts.current.source({ value, position, trigger, avoid, signal }),\n (chunk) => {\n if (current() !== mine) return false;\n raw += chunk;\n mine.text = cleanGhost(base, raw);\n painter.current?.();\n },\n ).then((outcome) => {\n mine.done = true;\n if (current() !== mine) return;\n if (outcome === \"ready\") painter.current?.flush();\n else painter.current?.cancel();\n });\n },\n [run],\n );\n\n const short = (value: string) => value.trim().length < MIN_CONTINUE_LENGTH;\n\n /** The field changed. Ask after a debounce — unless the offer on the table survives it. */\n const setValue = useCallback(\n (value: string, position = value.length) => {\n field.current = { value, position };\n if (alive()) {\n setGhost(rendered());\n return;\n }\n drop(\"ignored\");\n if (short(value)) return;\n timer.current = setTimeout(\n () => start(value, position, \"automatic\"),\n opts.current.debounceMs ?? 350,\n );\n },\n [alive, drop, rendered, start],\n );\n\n /** Ask now — the ✨, and Alt+] past the last alternative. */\n const ask = useCallback(\n (value: string, position = value.length) => {\n const same = field.current.value === value && field.current.position === position;\n field.current = { value, position };\n if (!same) drop(\"ignored\");\n clearTimeout(timer.current);\n if (short(value)) return;\n start(value, position, \"invoked\");\n },\n [drop, start],\n );\n\n /** Move between alternatives; past the last one, ask for another. */\n const cycle = useCallback(\n (step: 1 | -1) => {\n const next = index.current + step;\n if (next < 0) return;\n if (next >= offers.current.length) {\n const { value, position } = field.current;\n ask(value, position);\n return;\n }\n index.current = next;\n setGhost(rendered());\n painter.current?.();\n },\n [ask, rendered],\n );\n\n const settle = useCallback(\n (kind: \"accepted\" | \"partial\", text: string) => {\n const o = current();\n if (o) opts.current.onEvent(kind, { text, range: [field.current.position, field.current.position] });\n if (kind === \"accepted\") {\n offers.current = [];\n index.current = 0;\n shown.current = null;\n clearTimeout(timer.current);\n painter.current?.cancel();\n cancel();\n setGhost(\"\");\n }\n },\n [cancel],\n );\n\n useEffect(\n () => () => {\n clearTimeout(timer.current);\n painter.current?.cancel();\n },\n [],\n );\n\n return { ghost, status, error, setValue, ask, cycle, settle, dismiss: drop };\n}\n\n// ── The candidates: N whole values, deduped ──────────────────────────────────\n\nconst norm = (v: string) => v.trim().toLowerCase();\n\n/**\n * A streamed list of whole values, deduped against what the field already holds. A budget rather\n * than a window: nothing refills when one is taken or dismissed.\n */\nexport function useCandidates(options: {\n source: (request: { existing: string[]; signal: AbortSignal }) => AsyncIterable<Proposal>;\n existing: string[];\n onEvent: (kind: AssistEvent[\"kind\"], proposal: Proposal) => void;\n limit?: number;\n}) {\n const { run, cancel: abort, reset, status, error } = useStream<Proposal>(\"Couldn’t load suggestions\");\n const [items, setItems] = useState<Proposal[]>([]);\n const shown = useRef<Proposal[]>([]);\n const opts = useRef(options);\n opts.current = options;\n\n const put = useCallback((next: Proposal[]) => {\n shown.current = next;\n setItems(next);\n }, []);\n\n const refresh = useCallback(() => {\n const { existing, limit = 6, source } = opts.current;\n const seen = new Set(existing.map(norm).filter(Boolean));\n const taken: Proposal[] = [];\n put([]);\n void run(\n (signal) => source({ existing, signal }),\n (item) => {\n const key = norm(item.text);\n if (!key || seen.has(key)) return true;\n seen.add(key);\n taken.push(item);\n put([...taken]);\n opts.current.onEvent(\"shown\", item);\n return taken.length < limit;\n },\n );\n }, [put, run]);\n\n /** The ✨: a first set, a different set, or a retry — never inert, except while loading. */\n const press = useCallback(() => {\n if (status === \"loading\") return;\n refresh();\n }, [refresh, status]);\n\n const remove = useCallback(\n (text: string, kind: \"accepted\" | \"rejected\") => {\n const key = norm(text);\n const item = shown.current.find((p) => norm(p.text) === key);\n if (item) opts.current.onEvent(kind, item);\n const next = shown.current.filter((p) => norm(p.text) !== key);\n put(next);\n // Nothing left of an answer is not an answer with nothing in it.\n if (next.length === 0) reset();\n },\n [put, reset],\n );\n\n const cancel = useCallback(() => {\n abort();\n put([]);\n }, [abort, put]);\n\n return { items, status, error, press, take: (t: string) => remove(t, \"accepted\"), dismiss: (t: string) => remove(t, \"rejected\"), cancel };\n}\n"],"names":[],"mappings":";;AAqCA;AAUO;AACL;AAME;AAEU;AAGZ;;AAAsB;AAAc;AAEpC;AAAY;;AAER;AACA;AACA;AAIE;AACE;AACE;AACA;AACE;AACA;AAAA;AACF;AACF;AAEA;AAEO;AAET;AACO;AACT;AACF;AACe;;AAIf;AACU;AAIV;AAA+C;AAGjD;AACF;AAMA;AACE;AAEA;AAEE;AAEE;AAEA;AACD;AAGD;AAES;AAOX;AAJE;AACA;AACa;AAGjB;AAOO;AACL;AACA;AACA;AACA;AAGF;AAKO;AA6BA;AAKL;AAGA;AAEA;AASE;AACA;AACA;AACA;AACA;AACA;AAA+D;AAI/D;AACA;AACA;AACA;AACA;AAEA;AAAsE;AAMxE;AACE;AACA;AACA;AACA;AAE2C;AAK7C;AAAa;;AAET;AAEA;AACA;AAKW;AACb;AACO;AAGK;AAEV;AAEA;AAEA;AACA;AACA;AAAK;AACwE;;AAEzE;AACA;AAEA;AACF;;AAGA;AAEsB;AACvB;AACH;AACI;AAMW;AAGb;AACE;AACA;AAAA;AAGF;AACgB;AAC0B;AACb;AAAA;AAE/B;AAC6B;AAInB;AAER;AAIA;AACgC;AAClC;AACY;AAIA;;AAEV;AACA;AACA;AACE;AACA;AACA;AAAA;AAEF;AAEA;AAAA;AACF;AACc;AAGD;;AAGX;AAQa;AAEf;AACO;AAGT;AAAA;;AAEI;AACiB;AACnB;AACA;AAIJ;AAIA;AAMO;AAML;AAIA;AAEA;AACE;AACa;AAIb;AAGA;AACK;AACoC;AAErC;AACA;AAKsB;AACxB;AAAA;AAMF;AACA;AAGa;AAEX;AAEA;AACA;AACA;AAEuB;AACzB;AACW;AAIX;AACM;AAGR;AACF;;;;;;;;"}
package/dist/index.d.ts CHANGED
@@ -1,25 +1,8 @@
1
- export { Conversation, ConversationContent, ConversationScrollButton, } from './conversation.js';
2
- export { Message, MessageActions, MessageAvatar, MessageContent, MessageList, } from './message.js';
3
- export { MessageText } from './message-text.js';
4
- export type { MessageProps, MessageAvatarProps, MessageRole } from './message.js';
5
- export type { MessageTextProps } from './message-text.js';
6
- export { PromptInput, PromptInputSubmit, PromptInputTextarea, PromptInputToolbar, } from './prompt-input.js';
7
- export type { PromptInputSubmitProps } from './prompt-input.js';
8
- export { Reasoning, ReasoningContent, ReasoningTrigger } from './reasoning.js';
9
- export type { ReasoningProps } from './reasoning.js';
10
- export { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput } from './tool.js';
11
- export type { ToolProps, ToolIoProps } from './tool.js';
12
- export { Task, TaskList, TaskStatus, TaskTitle } from './task.js';
13
- export type { TaskProps, RunState } from './task.js';
14
- export { CompleteRoot, CompleteTextarea, CompleteGhost, CompleteHint, CompleteKeys, CompleteError, CompleteMark, } from './complete.js';
15
- export { AiMark } from './ai-mark.js';
16
- export type { AiMarkProps } from './ai-mark.js';
17
- export { SuggestRoot, SuggestMark, SuggestList } from './suggest.js';
18
- export type { SuggestRootProps, SuggestListProps, SuggestTrigger } from './suggest.js';
19
- export type { CompleteRootProps } from './complete.js';
20
- export { useAiStream, useInlineCompletion, useSuggestions, cleanGhost } from './use-ai.js';
21
- export type { AiStatus, AiStream, InlineCompletion, InlineCompletionRequest, InlineCompletionTrigger, SuggestionsController, UseInlineCompletionOptions, UseSuggestionsOptions, } from './use-ai.js';
22
- export type { Candidate } from './types.js';
23
- export { isTextPart, isReasoningPart, isToolPart, isTaskPart } from './message-part.js';
24
- export type { AiMessage, AiMessagePart, AiReasoningPart, AiTaskPart, AiTextPart, AiToolPart, StreamState, } from './message-part.js';
1
+ export { AssistProvider, Assist } from './assist.js';
2
+ export type { AssistProviderProps, AssistProps, AssistTranslations } from './assist.js';
3
+ export type { AssistEvent, Proposal } from './engine.js';
4
+ export { Chat } from './chat.js';
5
+ export type { ChatProps, ChatToolRenderers, ChatTranslations } from './chat.js';
6
+ export type { ToolPart } from './tool.js';
7
+ export { useChat } from '@ai-sdk/react';
25
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,OAAO,EACP,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAClF,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAG1D,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAGhE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjF,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAClE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,GACb,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACrE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACvF,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC3F,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAI5C,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxF,YAAY,EACV,SAAS,EACT,aAAa,EACb,eAAe,EACf,UAAU,EACV,UAAU,EACV,UAAU,EACV,WAAW,GACZ,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrD,YAAY,EAAE,mBAAmB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACxF,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAChF,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,59 +1,10 @@
1
- import { Conversation as t, ConversationContent as r, ConversationScrollButton as s } from "./conversation.js";
2
- import { Message as n, MessageActions as p, MessageAvatar as m, MessageContent as i, MessageList as g } from "./message.js";
3
- import { MessageText as T } from "./message-text.js";
4
- import { PromptInput as x, PromptInputSubmit as C, PromptInputTextarea as f, PromptInputToolbar as M } from "./prompt-input.js";
5
- import { Reasoning as P, ReasoningContent as S, ReasoningTrigger as I } from "./reasoning.js";
6
- import { Tool as v, ToolContent as A, ToolHeader as c, ToolInput as L, ToolOutput as b } from "./tool.js";
7
- import { Task as G, TaskList as H, TaskStatus as d, TaskTitle as y } from "./task.js";
8
- import { CompleteError as E, CompleteGhost as K, CompleteHint as O, CompleteKeys as j, CompleteMark as q, CompleteRoot as w, CompleteTextarea as z } from "./complete.js";
9
- import { AiMark as F } from "./ai-mark.js";
10
- import { SuggestList as N, SuggestMark as Q, SuggestRoot as U } from "./suggest.js";
11
- import { cleanGhost as W, useAiStream as X, useInlineCompletion as Y, useSuggestions as Z } from "./use-ai.js";
12
- import { isReasoningPart as $, isTaskPart as oo, isTextPart as eo, isToolPart as to } from "./message-part.js";
1
+ import { Assist as s, AssistProvider as t } from "./assist.js";
2
+ import { Chat as f } from "./chat.js";
3
+ import { useChat as m } from "@ai-sdk/react";
13
4
  export {
14
- F as AiMark,
15
- E as CompleteError,
16
- K as CompleteGhost,
17
- O as CompleteHint,
18
- j as CompleteKeys,
19
- q as CompleteMark,
20
- w as CompleteRoot,
21
- z as CompleteTextarea,
22
- t as Conversation,
23
- r as ConversationContent,
24
- s as ConversationScrollButton,
25
- n as Message,
26
- p as MessageActions,
27
- m as MessageAvatar,
28
- i as MessageContent,
29
- g as MessageList,
30
- T as MessageText,
31
- x as PromptInput,
32
- C as PromptInputSubmit,
33
- f as PromptInputTextarea,
34
- M as PromptInputToolbar,
35
- P as Reasoning,
36
- S as ReasoningContent,
37
- I as ReasoningTrigger,
38
- N as SuggestList,
39
- Q as SuggestMark,
40
- U as SuggestRoot,
41
- G as Task,
42
- H as TaskList,
43
- d as TaskStatus,
44
- y as TaskTitle,
45
- v as Tool,
46
- A as ToolContent,
47
- c as ToolHeader,
48
- L as ToolInput,
49
- b as ToolOutput,
50
- W as cleanGhost,
51
- $ as isReasoningPart,
52
- oo as isTaskPart,
53
- eo as isTextPart,
54
- to as isToolPart,
55
- X as useAiStream,
56
- Y as useInlineCompletion,
57
- Z as useSuggestions
5
+ s as Assist,
6
+ t as AssistProvider,
7
+ f as Chat,
8
+ m as useChat
58
9
  };
59
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}