@windowkit/agent-chat 0.0.0-stage → 0.3.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 (164) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +147 -2
  3. package/dist/core/driver.d.ts +183 -0
  4. package/dist/core/driver.d.ts.map +1 -0
  5. package/dist/core/driver.js +12 -0
  6. package/dist/core/driver.js.map +1 -0
  7. package/dist/core/index.d.ts +8 -0
  8. package/dist/core/index.d.ts.map +1 -0
  9. package/dist/core/index.js +11 -0
  10. package/dist/core/index.js.map +1 -0
  11. package/dist/core/markdown.d.ts +7 -0
  12. package/dist/core/markdown.d.ts.map +1 -0
  13. package/dist/core/markdown.js +53 -0
  14. package/dist/core/markdown.js.map +1 -0
  15. package/dist/core/policy.d.ts +18 -0
  16. package/dist/core/policy.d.ts.map +1 -0
  17. package/dist/core/policy.js +67 -0
  18. package/dist/core/policy.js.map +1 -0
  19. package/dist/core/reducer.d.ts +61 -0
  20. package/dist/core/reducer.d.ts.map +1 -0
  21. package/dist/core/reducer.js +391 -0
  22. package/dist/core/reducer.js.map +1 -0
  23. package/dist/core/session.d.ts +105 -0
  24. package/dist/core/session.d.ts.map +1 -0
  25. package/dist/core/session.js +648 -0
  26. package/dist/core/session.js.map +1 -0
  27. package/dist/core/tools.d.ts +120 -0
  28. package/dist/core/tools.d.ts.map +1 -0
  29. package/dist/core/tools.js +120 -0
  30. package/dist/core/tools.js.map +1 -0
  31. package/dist/core/types.d.ts +295 -0
  32. package/dist/core/types.d.ts.map +1 -0
  33. package/dist/core/types.js +21 -0
  34. package/dist/core/types.js.map +1 -0
  35. package/dist/drivers/claude/index.d.ts +20 -0
  36. package/dist/drivers/claude/index.d.ts.map +1 -0
  37. package/dist/drivers/claude/index.js +727 -0
  38. package/dist/drivers/claude/index.js.map +1 -0
  39. package/dist/drivers/claude/input.d.ts +7 -0
  40. package/dist/drivers/claude/input.d.ts.map +1 -0
  41. package/dist/drivers/claude/input.js +46 -0
  42. package/dist/drivers/claude/input.js.map +1 -0
  43. package/dist/drivers/claude/peers.d.ts +16 -0
  44. package/dist/drivers/claude/peers.d.ts.map +1 -0
  45. package/dist/drivers/claude/peers.js +137 -0
  46. package/dist/drivers/claude/peers.js.map +1 -0
  47. package/dist/drivers/claude/schema.d.ts +5 -0
  48. package/dist/drivers/claude/schema.d.ts.map +1 -0
  49. package/dist/drivers/claude/schema.js +77 -0
  50. package/dist/drivers/claude/schema.js.map +1 -0
  51. package/dist/drivers/claude/sdk.d.ts +86 -0
  52. package/dist/drivers/claude/sdk.d.ts.map +1 -0
  53. package/dist/drivers/claude/sdk.js +47 -0
  54. package/dist/drivers/claude/sdk.js.map +1 -0
  55. package/dist/drivers/claude/tools.d.ts +18 -0
  56. package/dist/drivers/claude/tools.d.ts.map +1 -0
  57. package/dist/drivers/claude/tools.js +119 -0
  58. package/dist/drivers/claude/tools.js.map +1 -0
  59. package/dist/index.d.ts +8 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +12 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/testing/fake.d.ts +63 -0
  64. package/dist/testing/fake.d.ts.map +1 -0
  65. package/dist/testing/fake.js +270 -0
  66. package/dist/testing/fake.js.map +1 -0
  67. package/dist/testing/index.d.ts +2 -0
  68. package/dist/testing/index.d.ts.map +1 -0
  69. package/dist/testing/index.js +4 -0
  70. package/dist/testing/index.js.map +1 -0
  71. package/dist/view/AgentChat.d.ts +53 -0
  72. package/dist/view/AgentChat.d.ts.map +1 -0
  73. package/dist/view/AgentChat.js +87 -0
  74. package/dist/view/AgentChat.js.map +1 -0
  75. package/dist/view/CodeBlock.d.ts +9 -0
  76. package/dist/view/CodeBlock.d.ts.map +1 -0
  77. package/dist/view/CodeBlock.js +67 -0
  78. package/dist/view/CodeBlock.js.map +1 -0
  79. package/dist/view/Composer.d.ts +24 -0
  80. package/dist/view/Composer.d.ts.map +1 -0
  81. package/dist/view/Composer.js +144 -0
  82. package/dist/view/Composer.js.map +1 -0
  83. package/dist/view/Header.d.ts +3 -0
  84. package/dist/view/Header.d.ts.map +1 -0
  85. package/dist/view/Header.js +30 -0
  86. package/dist/view/Header.js.map +1 -0
  87. package/dist/view/MessageList.d.ts +9 -0
  88. package/dist/view/MessageList.d.ts.map +1 -0
  89. package/dist/view/MessageList.js +111 -0
  90. package/dist/view/MessageList.js.map +1 -0
  91. package/dist/view/QuestionCard.d.ts +6 -0
  92. package/dist/view/QuestionCard.d.ts.map +1 -0
  93. package/dist/view/QuestionCard.js +39 -0
  94. package/dist/view/QuestionCard.js.map +1 -0
  95. package/dist/view/TextBlock.d.ts +9 -0
  96. package/dist/view/TextBlock.d.ts.map +1 -0
  97. package/dist/view/TextBlock.js +25 -0
  98. package/dist/view/TextBlock.js.map +1 -0
  99. package/dist/view/ToolCard.d.ts +7 -0
  100. package/dist/view/ToolCard.d.ts.map +1 -0
  101. package/dist/view/ToolCard.js +155 -0
  102. package/dist/view/ToolCard.js.map +1 -0
  103. package/dist/view/Transcript.d.ts +5 -0
  104. package/dist/view/Transcript.d.ts.map +1 -0
  105. package/dist/view/Transcript.js +56 -0
  106. package/dist/view/Transcript.js.map +1 -0
  107. package/dist/view/context.d.ts +21 -0
  108. package/dist/view/context.d.ts.map +1 -0
  109. package/dist/view/context.js +37 -0
  110. package/dist/view/context.js.map +1 -0
  111. package/dist/view/fences.d.ts +13 -0
  112. package/dist/view/fences.d.ts.map +1 -0
  113. package/dist/view/fences.js +59 -0
  114. package/dist/view/fences.js.map +1 -0
  115. package/dist/view/files.d.ts +11 -0
  116. package/dist/view/files.d.ts.map +1 -0
  117. package/dist/view/files.js +109 -0
  118. package/dist/view/files.js.map +1 -0
  119. package/dist/view/look.d.ts +25 -0
  120. package/dist/view/look.d.ts.map +1 -0
  121. package/dist/view/look.js +47 -0
  122. package/dist/view/look.js.map +1 -0
  123. package/dist/view/types.d.ts +50 -0
  124. package/dist/view/types.d.ts.map +1 -0
  125. package/dist/view/types.js +14 -0
  126. package/dist/view/types.js.map +1 -0
  127. package/dist/view/use-agent.d.ts +11 -0
  128. package/dist/view/use-agent.d.ts.map +1 -0
  129. package/dist/view/use-agent.js +33 -0
  130. package/dist/view/use-agent.js.map +1 -0
  131. package/package.json +92 -4
  132. package/src/core/driver.ts +191 -0
  133. package/src/core/index.ts +54 -0
  134. package/src/core/markdown.ts +56 -0
  135. package/src/core/policy.ts +77 -0
  136. package/src/core/reducer.ts +490 -0
  137. package/src/core/session.ts +824 -0
  138. package/src/core/tools.ts +233 -0
  139. package/src/core/types.ts +321 -0
  140. package/src/drivers/claude/index.ts +825 -0
  141. package/src/drivers/claude/input.ts +47 -0
  142. package/src/drivers/claude/peers.ts +153 -0
  143. package/src/drivers/claude/schema.ts +99 -0
  144. package/src/drivers/claude/sdk.ts +138 -0
  145. package/src/drivers/claude/tools.ts +141 -0
  146. package/src/index.ts +51 -0
  147. package/src/testing/fake.ts +374 -0
  148. package/src/testing/index.ts +11 -0
  149. package/src/view/AgentChat.tsx +204 -0
  150. package/src/view/CodeBlock.tsx +115 -0
  151. package/src/view/Composer.tsx +230 -0
  152. package/src/view/Header.tsx +73 -0
  153. package/src/view/MessageList.tsx +290 -0
  154. package/src/view/QuestionCard.tsx +107 -0
  155. package/src/view/TextBlock.tsx +61 -0
  156. package/src/view/ToolCard.tsx +283 -0
  157. package/src/view/Transcript.tsx +91 -0
  158. package/src/view/context.ts +70 -0
  159. package/src/view/dom-globals.d.ts +10 -0
  160. package/src/view/fences.ts +73 -0
  161. package/src/view/files.ts +111 -0
  162. package/src/view/look.ts +70 -0
  163. package/src/view/types.ts +71 -0
  164. package/src/view/use-agent.ts +40 -0
@@ -0,0 +1,70 @@
1
+ // The palette the chat draws with, read from the theme in force, so the chat
2
+ // takes the app's look and the desktop's light or dark scheme.
3
+
4
+ import { useMemo } from 'react';
5
+ import { useTheme } from 'react-x11';
6
+ import { tint } from 'react-x11/style';
7
+ import {
8
+ codeBlockLook,
9
+ type CodeBlockLook,
10
+ } from '@react-x11/components/codeblock';
11
+
12
+ export interface ChatLook {
13
+ text: string;
14
+ muted: string;
15
+ border: string;
16
+ surface: string;
17
+ background: string;
18
+ accent: string;
19
+ danger: string;
20
+ success: string;
21
+ warning: string;
22
+ mono: string;
23
+ /** The user's bubble: the text colour, faintly, so it shows on any ground. */
24
+ bubble: string;
25
+ }
26
+
27
+ export function useLook(): ChatLook {
28
+ const theme = useTheme();
29
+ return useMemo(() => {
30
+ const text = String(theme.text ?? '#2d3436');
31
+ return {
32
+ text,
33
+ muted: String(theme.textMuted ?? '#7f8c8d'),
34
+ border: String(theme.border ?? '#b2bec3'),
35
+ surface: String(theme.surface ?? '#f1f2f6'),
36
+ background: String(theme.background ?? 'white'),
37
+ accent: String(theme.accent ?? '#2980b9'),
38
+ danger: String(theme.danger ?? '#c0392b'),
39
+ success: String(theme.success ?? '#27ae60'),
40
+ warning: String(theme.warning ?? '#e67e22'),
41
+ mono: String(theme.monoFamily ?? 'monospace'),
42
+ bubble: tint(text, 0.07),
43
+ };
44
+ }, [theme]);
45
+ }
46
+
47
+ /**
48
+ * What a block of code looks like here: what `<Markdown>` draws a fence
49
+ * with, at the chat's text size, so a fence the chat draws itself (to carry
50
+ * its actions) has the fill, radius, inset and spacing of one it does not.
51
+ */
52
+ export function useCodeLook(fontSize: number | undefined): CodeBlockLook {
53
+ const theme = useTheme() as unknown as Record<string, unknown>;
54
+ return useMemo(
55
+ () => codeBlockLook(theme, fontSize ? { baseSize: fontSize } : {}),
56
+ [theme, fontSize],
57
+ );
58
+ }
59
+
60
+ /** At most `max` lines: the first and last halves, and how many were cut. */
61
+ export function clipLines(text: string, max = 200): string {
62
+ const lines = text.split('\n');
63
+ if (lines.length <= max) return text;
64
+ const half = Math.floor(max / 2);
65
+ return [
66
+ ...lines.slice(0, half),
67
+ `… ${lines.length - max} more lines …`,
68
+ ...lines.slice(-half),
69
+ ].join('\n');
70
+ }
@@ -0,0 +1,71 @@
1
+ // The view's own props: renderers and actions. Nothing here reaches the
2
+ // session, but `describe` does reach the agent, as instructions.
3
+
4
+ import type { ComponentType, ReactNode } from 'react';
5
+ import type { IconName } from 'react-x11';
6
+ import type { FenceInfo } from '@react-x11/components/markdown';
7
+
8
+ import type { AgentSession } from '../core/session.js';
9
+ import type {
10
+ ToolDef,
11
+ ToolParameters,
12
+ InferInput,
13
+ ToolRenderInfo,
14
+ } from '../core/tools.js';
15
+ import type { AgentMessage } from '../core/types.js';
16
+
17
+ export type { FenceInfo };
18
+
19
+ /**
20
+ * How a fenced block with this language is drawn: a function, or the
21
+ * function and a line that tells the agent the format exists.
22
+ */
23
+ export type FenceEntry =
24
+ | ((fence: FenceInfo) => ReactNode)
25
+ | { render: (fence: FenceInfo) => ReactNode; describe?: string };
26
+
27
+ /**
28
+ * A component a reply may name as a tag on its own line: `<Ticket id="12"/>`.
29
+ * A tag is a component iff its name is a key here; nothing is evaluated.
30
+ */
31
+ export type ComponentEntry =
32
+ | ComponentType<Record<string, unknown>>
33
+ | { component: ComponentType<Record<string, unknown>>; describe?: string };
34
+
35
+ /** What a code action is handed besides the fence. */
36
+ export interface CodeActionContext {
37
+ session: AgentSession;
38
+ message: AgentMessage;
39
+ /** Put text on the clipboard. */
40
+ copy(text: string): Promise<void>;
41
+ }
42
+
43
+ /** A button on a fenced code block's header. */
44
+ export interface CodeAction {
45
+ id: string;
46
+ label: string;
47
+ icon?: IconName;
48
+ /** Shown for a moment after `run` resolves: `'Copied'`. */
49
+ doneLabel?: string;
50
+ /** Default: every block. */
51
+ when?: (fence: FenceInfo) => boolean;
52
+ run(fence: FenceInfo, ctx: CodeActionContext): void | Promise<void>;
53
+ }
54
+
55
+ /** The built-in Copy. The default `codeActions` is `[copyCode]`. */
56
+ export const copyCode: CodeAction = Object.freeze({
57
+ id: 'copy',
58
+ label: 'Copy',
59
+ doneLabel: 'Copied',
60
+ run: (fence: FenceInfo, ctx: CodeActionContext) => ctx.copy(fence.text),
61
+ });
62
+
63
+ /** Draws a tool call: the part, its parsed input, and what `run` returned. */
64
+ export type ToolRenderer = (info: ToolRenderInfo) => ReactNode;
65
+
66
+ /** `defineTool`, with `render` typed for this view. */
67
+ export function defineTool<P extends ToolParameters, O>(
68
+ def: ToolDef<InferInput<P>, O, ReactNode> & { parameters: P },
69
+ ): ToolDef<InferInput<P>, O, ReactNode> {
70
+ return def;
71
+ }
@@ -0,0 +1,40 @@
1
+ // The session as React state.
2
+
3
+ import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
4
+
5
+ import {
6
+ createAgentSession,
7
+ type AgentOptions,
8
+ type AgentSession,
9
+ type SessionSnapshot,
10
+ } from '../core/session.js';
11
+
12
+ /**
13
+ * A session for this component's life: made at the first render from
14
+ * `options`, which are read once (give the component a new `key` to start
15
+ * over), and closed when the component unmounts. The component re-renders
16
+ * whenever the session changes.
17
+ */
18
+ export function useAgent(options: AgentOptions): AgentSession {
19
+ const [session] = useState(() => createAgentSession(options));
20
+ useSessionSnapshot(session);
21
+ // Closed a turn after the unmount, not at it: StrictMode's development
22
+ // double mount unmounts and mounts again at once, and the session should
23
+ // survive that.
24
+ const mounted = useRef(0);
25
+ useEffect(() => {
26
+ mounted.current++;
27
+ return () => {
28
+ const at = --mounted.current;
29
+ setTimeout(() => {
30
+ if (mounted.current === at && at === 0) void session.close();
31
+ }, 0);
32
+ };
33
+ }, [session]);
34
+ return session;
35
+ }
36
+
37
+ /** The session's current snapshot, re-rendering as it changes. */
38
+ export function useSessionSnapshot(session: AgentSession): SessionSnapshot {
39
+ return useSyncExternalStore(session.subscribe, () => session.snapshot);
40
+ }