@classic-homes/agent-chat 0.0.0-stage → 0.1.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 (53) hide show
  1. package/dist/components/AgentChat.svelte +174 -0
  2. package/dist/components/AgentChat.svelte.d.ts +26 -0
  3. package/dist/components/AgentChat.svelte.d.ts.map +1 -0
  4. package/dist/components/ArtifactPanel.svelte +161 -0
  5. package/dist/components/ArtifactPanel.svelte.d.ts +11 -0
  6. package/dist/components/ArtifactPanel.svelte.d.ts.map +1 -0
  7. package/dist/components/Composer.svelte +211 -0
  8. package/dist/components/Composer.svelte.d.ts +21 -0
  9. package/dist/components/Composer.svelte.d.ts.map +1 -0
  10. package/dist/components/ConversationSidebar.svelte +188 -0
  11. package/dist/components/ConversationSidebar.svelte.d.ts +18 -0
  12. package/dist/components/ConversationSidebar.svelte.d.ts.map +1 -0
  13. package/dist/components/MessageItem.svelte +253 -0
  14. package/dist/components/MessageItem.svelte.d.ts +17 -0
  15. package/dist/components/MessageItem.svelte.d.ts.map +1 -0
  16. package/dist/components/MessageList.svelte +104 -0
  17. package/dist/components/MessageList.svelte.d.ts +18 -0
  18. package/dist/components/MessageList.svelte.d.ts.map +1 -0
  19. package/dist/components/ModeBanner.svelte +23 -0
  20. package/dist/components/ModeBanner.svelte.d.ts +8 -0
  21. package/dist/components/ModeBanner.svelte.d.ts.map +1 -0
  22. package/dist/components/ToolTimeline.svelte +113 -0
  23. package/dist/components/ToolTimeline.svelte.d.ts +10 -0
  24. package/dist/components/ToolTimeline.svelte.d.ts.map +1 -0
  25. package/dist/components/UiFrame.svelte +95 -0
  26. package/dist/components/UiFrame.svelte.d.ts +13 -0
  27. package/dist/components/UiFrame.svelte.d.ts.map +1 -0
  28. package/dist/index.d.ts +14 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +13 -0
  31. package/dist/markdown.d.ts +13 -0
  32. package/dist/markdown.d.ts.map +1 -0
  33. package/dist/markdown.js +325 -0
  34. package/dist/markdown.test.d.ts +2 -0
  35. package/dist/markdown.test.d.ts.map +1 -0
  36. package/dist/markdown.test.js +35 -0
  37. package/dist/reducer.d.ts +96 -0
  38. package/dist/reducer.d.ts.map +1 -0
  39. package/dist/reducer.js +186 -0
  40. package/dist/reducer.test.d.ts +2 -0
  41. package/dist/reducer.test.d.ts.map +1 -0
  42. package/dist/reducer.test.js +125 -0
  43. package/dist/session.svelte.d.ts +51 -0
  44. package/dist/session.svelte.d.ts.map +1 -0
  45. package/dist/session.svelte.js +152 -0
  46. package/dist/ui-frame-protocol.d.ts +42 -0
  47. package/dist/ui-frame-protocol.d.ts.map +1 -0
  48. package/dist/ui-frame-protocol.js +73 -0
  49. package/dist/ui-frame-protocol.test.d.ts +2 -0
  50. package/dist/ui-frame-protocol.test.d.ts.map +1 -0
  51. package/dist/ui-frame-protocol.test.js +34 -0
  52. package/package.json +47 -3
  53. package/README.md +0 -3
@@ -0,0 +1,73 @@
1
+ /**
2
+ * MCP-UI sandbox protocol. The host page embeds an iframe on a separate origin (the agent-api
3
+ * sandbox, `/frame`), hands it HTML over postMessage (never in the URL) and receives a small
4
+ * set of actions back. Everything from the frame is untrusted: origins and sources are
5
+ * verified, URLs restricted to https, and tool requests are never run without the user.
6
+ */
7
+ export const UI_FRAME_READY = 'ui-lifecycle-iframe-ready';
8
+ export const UI_FRAME_RENDER = 'agent-ui:render';
9
+ export const MIN_FRAME_HEIGHT = 120;
10
+ export const MAX_FRAME_HEIGHT = 1200;
11
+ /** Parse a message from the sandbox into a FrameAction, or null when it is not one. */
12
+ export function parseFrameMessage(data) {
13
+ if (!data || typeof data !== 'object')
14
+ return null;
15
+ const m = data;
16
+ const type = m.type;
17
+ if (type === UI_FRAME_READY)
18
+ return { type: 'ready' };
19
+ if (type === 'ui-size-change') {
20
+ const payload = (m.payload ?? m);
21
+ const height = Number(payload.height);
22
+ if (!Number.isFinite(height))
23
+ return null;
24
+ return {
25
+ type: 'size',
26
+ height: Math.min(MAX_FRAME_HEIGHT, Math.max(MIN_FRAME_HEIGHT, Math.round(height))),
27
+ };
28
+ }
29
+ const payload = (m.payload ?? {});
30
+ switch (type) {
31
+ case 'prompt':
32
+ return typeof payload.prompt === 'string'
33
+ ? { type: 'prompt', prompt: payload.prompt.slice(0, 8_000) }
34
+ : null;
35
+ case 'intent':
36
+ return typeof payload.intent === 'string'
37
+ ? { type: 'intent', intent: payload.intent.slice(0, 200), params: asRecord(payload.params) }
38
+ : null;
39
+ case 'tool':
40
+ return typeof payload.toolName === 'string'
41
+ ? {
42
+ type: 'tool',
43
+ toolName: payload.toolName.slice(0, 128),
44
+ params: asRecord(payload.params),
45
+ }
46
+ : null;
47
+ case 'link':
48
+ return typeof payload.url === 'string' && /^https:\/\//i.test(payload.url)
49
+ ? { type: 'link', url: payload.url.slice(0, 2_000) }
50
+ : null;
51
+ case 'notify':
52
+ return typeof payload.message === 'string'
53
+ ? { type: 'notify', message: payload.message.slice(0, 500) }
54
+ : null;
55
+ }
56
+ return null;
57
+ }
58
+ function asRecord(value) {
59
+ return value && typeof value === 'object' && !Array.isArray(value)
60
+ ? value
61
+ : undefined;
62
+ }
63
+ /** True when a message event came from our sandbox frame and nowhere else. */
64
+ export function isFromFrame(event, frame, sandboxOrigin) {
65
+ return !!frame && event.source === frame.contentWindow && event.origin === sandboxOrigin;
66
+ }
67
+ /** Text an intent or prompt action becomes in the composer. */
68
+ export function composerTextFor(action) {
69
+ if (action.type === 'prompt')
70
+ return action.prompt;
71
+ const params = action.params ? ` ${JSON.stringify(action.params)}` : '';
72
+ return `${action.intent}${params}`;
73
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ui-frame-protocol.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-frame-protocol.test.d.ts","sourceRoot":"","sources":["../src/lib/ui-frame-protocol.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,34 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { composerTextFor, parseFrameMessage } from './ui-frame-protocol.js';
3
+ describe('parseFrameMessage', () => {
4
+ it('accepts the MCP-UI action set and clamps sizes', () => {
5
+ expect(parseFrameMessage({ type: 'ui-lifecycle-iframe-ready' })).toEqual({ type: 'ready' });
6
+ expect(parseFrameMessage({ type: 'ui-size-change', payload: { height: 5000 } })).toEqual({
7
+ type: 'size',
8
+ height: 1200,
9
+ });
10
+ expect(parseFrameMessage({ type: 'ui-size-change', payload: { height: 10 } })).toEqual({
11
+ type: 'size',
12
+ height: 120,
13
+ });
14
+ expect(parseFrameMessage({ type: 'prompt', payload: { prompt: 'Show lot 12' } })).toEqual({
15
+ type: 'prompt',
16
+ prompt: 'Show lot 12',
17
+ });
18
+ expect(parseFrameMessage({
19
+ type: 'tool',
20
+ payload: { toolName: 'search_homes', params: { q: 'x' } },
21
+ })).toEqual({ type: 'tool', toolName: 'search_homes', params: { q: 'x' } });
22
+ expect(composerTextFor({ type: 'intent', intent: 'open_home', params: { id: 1 } })).toBe('open_home {"id":1}');
23
+ });
24
+ it('rejects non-https links and unknown shapes', () => {
25
+ expect(parseFrameMessage({ type: 'link', payload: { url: 'javascript:alert(1)' } })).toBeNull();
26
+ expect(parseFrameMessage({ type: 'link', payload: { url: 'http://x.example' } })).toBeNull();
27
+ expect(parseFrameMessage({
28
+ type: 'link',
29
+ payload: { url: 'https://hotsheet.classichomes.com/l/1' },
30
+ })).toEqual({ type: 'link', url: 'https://hotsheet.classichomes.com/l/1' });
31
+ expect(parseFrameMessage('nope')).toBeNull();
32
+ expect(parseFrameMessage({ type: 'eval', payload: {} })).toBeNull();
33
+ });
34
+ });
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "@classic-homes/agent-chat",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Svelte 5 chat UI for Classic Agent: session state, message list, composer, tool timeline, artifacts and MCP-UI sandbox frames. Consumed by the Classic App.",
7
+ "svelte": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "svelte": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "dependencies": {
20
+ "@classic-homes/agent-client": "0.1.0",
21
+ "@classic-homes/agent-contracts": "0.1.0",
22
+ "dompurify": "3.4.17"
23
+ },
24
+ "peerDependencies": {
25
+ "@classic-homes/theme-svelte": "^1.34.0",
26
+ "svelte": "^5.0.0"
27
+ },
28
+ "devDependencies": {
29
+ "@classic-homes/theme-svelte": "1.34.0",
30
+ "@sveltejs/package": "3.0.2",
31
+ "@sveltejs/vite-plugin-svelte": "7.3.1",
32
+ "svelte": "5.57.3",
33
+ "svelte-check": "4.7.7",
34
+ "vite": "8.3.5"
35
+ },
36
+ "license": "UNLICENSED",
37
+ "publishConfig": {
38
+ "access": "public",
39
+ "provenance": true
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/Classic-Homes/classic-agent.git",
44
+ "directory": "packages/agent-chat"
45
+ },
46
+ "scripts": {
47
+ "build": "svelte-package -i src/lib -o dist",
48
+ "typecheck": "svelte-check --tsconfig ./tsconfig.json --threshold error"
49
+ }
6
50
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.