@classic-homes/agent-chat 0.1.3 → 0.2.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.
- package/README.md +40 -0
- package/dist/components/AgentChat.svelte +65 -39
- package/dist/components/AgentChat.svelte.d.ts +2 -0
- package/dist/components/AgentChat.svelte.d.ts.map +1 -1
- package/dist/components/ArtifactPanel.svelte +79 -96
- package/dist/components/ArtifactPanel.svelte.d.ts +2 -0
- package/dist/components/ArtifactPanel.svelte.d.ts.map +1 -1
- package/dist/components/ChatHeader.svelte +122 -0
- package/dist/components/ChatHeader.svelte.d.ts +27 -0
- package/dist/components/ChatHeader.svelte.d.ts.map +1 -0
- package/dist/components/Composer.svelte +78 -121
- package/dist/components/Composer.svelte.d.ts.map +1 -1
- package/dist/components/ConversationDialogs.svelte +134 -0
- package/dist/components/ConversationDialogs.svelte.d.ts +25 -0
- package/dist/components/ConversationDialogs.svelte.d.ts.map +1 -0
- package/dist/components/ConversationSidebar.svelte +135 -114
- package/dist/components/ConversationSidebar.svelte.d.ts +8 -2
- package/dist/components/ConversationSidebar.svelte.d.ts.map +1 -1
- package/dist/components/Markdown.svelte +48 -0
- package/dist/components/Markdown.svelte.d.ts +16 -0
- package/dist/components/Markdown.svelte.d.ts.map +1 -0
- package/dist/components/MessageItem.svelte +27 -116
- package/dist/components/MessageItem.svelte.d.ts.map +1 -1
- package/dist/components/MessageList.svelte +23 -34
- package/dist/components/ModeBanner.svelte +7 -13
- package/dist/components/ModeBanner.svelte.d.ts.map +1 -1
- package/dist/components/SandboxFrame.svelte +43 -0
- package/dist/components/SandboxFrame.svelte.d.ts +13 -0
- package/dist/components/SandboxFrame.svelte.d.ts.map +1 -0
- package/dist/components/ToolTimeline.svelte +17 -68
- package/dist/components/ToolTimeline.svelte.d.ts.map +1 -1
- package/dist/components/UiFrame.svelte +9 -55
- package/dist/components/UiFrame.svelte.d.ts.map +1 -1
- package/dist/components/conversation-chrome.svelte.test.d.ts +2 -0
- package/dist/components/conversation-chrome.svelte.test.d.ts.map +1 -0
- package/dist/components/conversation-chrome.svelte.test.js +127 -0
- package/dist/components/markdown-prose.svelte.test.d.ts +2 -0
- package/dist/components/markdown-prose.svelte.test.d.ts.map +1 -0
- package/dist/components/markdown-prose.svelte.test.js +26 -0
- package/dist/composer-keys.d.ts +11 -0
- package/dist/composer-keys.d.ts.map +1 -0
- package/dist/composer-keys.js +10 -0
- package/dist/composer-keys.test.d.ts +2 -0
- package/dist/composer-keys.test.d.ts.map +1 -0
- package/dist/composer-keys.test.js +16 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/reducer.d.ts +10 -0
- package/dist/reducer.d.ts.map +1 -1
- package/dist/reducer.js +19 -1
- package/dist/reducer.test.js +29 -1
- package/dist/session.svelte.d.ts +6 -0
- package/dist/session.svelte.d.ts.map +1 -1
- package/dist/session.svelte.js +15 -1
- package/dist/ui-frame-protocol.d.ts +2 -2
- package/dist/ui-frame-protocol.js +2 -2
- package/dist/ui-frame-relay.d.ts +16 -0
- package/dist/ui-frame-relay.d.ts.map +1 -0
- package/dist/ui-frame-relay.js +22 -0
- package/dist/ui-frame-relay.test.d.ts +2 -0
- package/dist/ui-frame-relay.test.d.ts.map +1 -0
- package/dist/ui-frame-relay.test.js +36 -0
- package/package.json +8 -5
package/dist/reducer.d.ts
CHANGED
|
@@ -90,6 +90,16 @@ export declare function newUserMessage(requestId: string, text: string, options?
|
|
|
90
90
|
export declare function describeContext(context: DocumentContext): string;
|
|
91
91
|
/** Fold one stream event into the assistant message it belongs to. Returns a new object. */
|
|
92
92
|
export declare function applyEvent(message: AssistantMessage, event: ChatEvent): AssistantMessage;
|
|
93
|
+
/**
|
|
94
|
+
* Prepare a retry of the turn `requestId`: returns the user message to resend and the transcript
|
|
95
|
+
* without that turn (its user message and the failed assistant reply), so the retried turn
|
|
96
|
+
* replaces the failed one instead of appending a duplicate. Only the last turn can be retried,
|
|
97
|
+
* and only when it did not finish normally.
|
|
98
|
+
*/
|
|
99
|
+
export declare function turnForRetry(messages: ChatMessage[], requestId: string): {
|
|
100
|
+
user: UserMessage;
|
|
101
|
+
rest: ChatMessage[];
|
|
102
|
+
} | undefined;
|
|
93
103
|
/** Human titles for client error codes (mirrors the task pane's wording). */
|
|
94
104
|
export declare function errorTitle(code: ClientErrorCode): string;
|
|
95
105
|
export type { ClientKind };
|
package/dist/reducer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../src/lib/reducer.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../src/lib/reducer.ts"],"names":[],"mappings":"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"}
|
package/dist/reducer.js
CHANGED
|
@@ -165,6 +165,23 @@ export function applyEvent(message, event) {
|
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Prepare a retry of the turn `requestId`: returns the user message to resend and the transcript
|
|
170
|
+
* without that turn (its user message and the failed assistant reply), so the retried turn
|
|
171
|
+
* replaces the failed one instead of appending a duplicate. Only the last turn can be retried,
|
|
172
|
+
* and only when it did not finish normally.
|
|
173
|
+
*/
|
|
174
|
+
export function turnForRetry(messages, requestId) {
|
|
175
|
+
const last = messages[messages.length - 1];
|
|
176
|
+
if (last?.role !== 'assistant' || last.requestId !== requestId)
|
|
177
|
+
return undefined;
|
|
178
|
+
if (last.status === 'streaming' || last.status === 'done')
|
|
179
|
+
return undefined;
|
|
180
|
+
const user = messages[messages.length - 2];
|
|
181
|
+
if (user?.role !== 'user')
|
|
182
|
+
return undefined;
|
|
183
|
+
return { user, rest: messages.slice(0, -2) };
|
|
184
|
+
}
|
|
168
185
|
/** Human titles for client error codes (mirrors the task pane's wording). */
|
|
169
186
|
export function errorTitle(code) {
|
|
170
187
|
const titles = {
|
|
@@ -182,5 +199,6 @@ export function errorTitle(code) {
|
|
|
182
199
|
network: 'Cannot reach Classic Agent',
|
|
183
200
|
protocol: 'Unexpected response from Classic Agent',
|
|
184
201
|
};
|
|
185
|
-
|
|
202
|
+
// A newer runtime may send a code this client doesn't know yet.
|
|
203
|
+
return titles[code] ?? 'Something went wrong';
|
|
186
204
|
}
|
package/dist/reducer.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { applyEvent, errorTitle, newAssistantMessage, newUserMessage } from './reducer.js';
|
|
2
|
+
import { applyEvent, errorTitle, newAssistantMessage, newUserMessage, turnForRetry, } from './reducer.js';
|
|
3
3
|
const requestId = '11111111-1111-4111-8111-111111111111';
|
|
4
4
|
const scope = { requestId, agent: 'classic-agent', runId: 'r1' };
|
|
5
5
|
const ev = (e, seq) => ({ ...e, seq });
|
|
@@ -123,3 +123,31 @@ describe('applyEvent', () => {
|
|
|
123
123
|
expect(user.contextLabel).toBe('Lot 12');
|
|
124
124
|
});
|
|
125
125
|
});
|
|
126
|
+
describe('turnForRetry', () => {
|
|
127
|
+
const failed = (id, status) => ({
|
|
128
|
+
...newAssistantMessage(id, 2),
|
|
129
|
+
status,
|
|
130
|
+
});
|
|
131
|
+
const idA = '22222222-2222-4222-8222-222222222222';
|
|
132
|
+
it('drops the failed turn so the retry replaces it', () => {
|
|
133
|
+
const earlier = [newUserMessage(idA, 'first', { now: 1 }), failed(idA, 'done')];
|
|
134
|
+
const messages = [
|
|
135
|
+
...earlier,
|
|
136
|
+
newUserMessage(requestId, 'again', { now: 3 }),
|
|
137
|
+
failed(requestId, 'error'),
|
|
138
|
+
];
|
|
139
|
+
const turn = turnForRetry(messages, requestId);
|
|
140
|
+
expect(turn?.user.text).toBe('again');
|
|
141
|
+
expect(turn?.rest).toEqual(earlier);
|
|
142
|
+
});
|
|
143
|
+
it('retries stopped turns but not finished, running or earlier ones', () => {
|
|
144
|
+
const turn = (status) => [
|
|
145
|
+
newUserMessage(requestId, 'q', { now: 1 }),
|
|
146
|
+
failed(requestId, status),
|
|
147
|
+
];
|
|
148
|
+
expect(turnForRetry(turn('cancelled'), requestId)).toBeDefined();
|
|
149
|
+
expect(turnForRetry(turn('done'), requestId)).toBeUndefined();
|
|
150
|
+
expect(turnForRetry(turn('streaming'), requestId)).toBeUndefined();
|
|
151
|
+
expect(turnForRetry(turn('error'), idA)).toBeUndefined();
|
|
152
|
+
});
|
|
153
|
+
});
|
package/dist/session.svelte.d.ts
CHANGED
|
@@ -44,6 +44,12 @@ export declare class ChatSession {
|
|
|
44
44
|
send(text: string, options?: {
|
|
45
45
|
files?: FileRef[];
|
|
46
46
|
}): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Resend the last turn after it failed, was stopped or was cut short. The failed turn is
|
|
49
|
+
* replaced in the view rather than duplicated. (The runtime may still hold the first user
|
|
50
|
+
* message when it had accepted the turn; a server-side regenerate is a separate change.)
|
|
51
|
+
*/
|
|
52
|
+
retry(requestId: string): Promise<void>;
|
|
47
53
|
/** Re-attach to a turn after reload: replays events after the last seq we hold (none here). */
|
|
48
54
|
resume(requestId: string): Promise<void>;
|
|
49
55
|
cancel(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.svelte.d.ts","sourceRoot":"","sources":["../src/lib/session.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EACV,QAAQ,EACR,WAAW,EACX,SAAS,EAET,UAAU,EACV,eAAe,EACf,OAAO,EACR,MAAM,gCAAgC,CAAC;AACxC,OAAO,
|
|
1
|
+
{"version":3,"file":"session.svelte.d.ts","sourceRoot":"","sources":["../src/lib/session.svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EACV,QAAQ,EACR,WAAW,EACX,SAAS,EAET,UAAU,EACV,eAAe,EACf,OAAO,EACR,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,mGAAmG;IACnG,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,eAAe,GAAG,IAAI,GAAG,SAAS,CAAC;IACtD,qBAAqB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;AAE7D;;;GAGG;AACH,qBAAa,WAAW;IACtB,QAAQ,gBAA6B;IACrC,MAAM,gBAAiC;IACvC,cAAc,gBAA+B;IAC7C,gGAAgG;IAChG,oBAAoB,gBAA+B;IACnD,IAAI,8BAA8C;IAClD,SAAS,qBAAyC;IAClD,YAAY;;;;;;QAAyB;IAErC,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,OAAO,EAAE,kBAAkB;IAMvC,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,OAAO,IAAI,gBAAgB,GAAG,SAAS,CAG1C;IAED,yFAAyF;IACnF,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B3C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B5E;;;;OAIG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ7C,+FAA+F;IACzF,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9C,MAAM,IAAI,IAAI;YAOA,GAAG;IAmDjB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,MAAM;CAKf;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAE1E"}
|
package/dist/session.svelte.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiRequestError } from '@classic-homes/agent-client';
|
|
2
|
-
import { applyEvent, newAssistantMessage, newUserMessage, } from './reducer.js';
|
|
2
|
+
import { applyEvent, newAssistantMessage, newUserMessage, turnForRetry, } from './reducer.js';
|
|
3
3
|
/**
|
|
4
4
|
* Reactive conversation state (Svelte 5 runes class). Owns the stream lifecycle: one turn at a
|
|
5
5
|
* time, cancel, resume after reload, history load. Rendering is left to the components.
|
|
@@ -82,6 +82,20 @@ export class ChatSession {
|
|
|
82
82
|
];
|
|
83
83
|
await this.run(requestId, (signal, onEvent) => this.options.client.streamChat(request, { signal, onEvent }));
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Resend the last turn after it failed, was stopped or was cut short. The failed turn is
|
|
87
|
+
* replaced in the view rather than duplicated. (The runtime may still hold the first user
|
|
88
|
+
* message when it had accepted the turn; a server-side regenerate is a separate change.)
|
|
89
|
+
*/
|
|
90
|
+
async retry(requestId) {
|
|
91
|
+
if (this.status !== 'idle')
|
|
92
|
+
return;
|
|
93
|
+
const turn = turnForRetry(this.messages, requestId);
|
|
94
|
+
if (!turn)
|
|
95
|
+
return;
|
|
96
|
+
this.messages = turn.rest;
|
|
97
|
+
await this.send(turn.user.text, { files: turn.user.attachments });
|
|
98
|
+
}
|
|
85
99
|
/** Re-attach to a turn after reload: replays events after the last seq we hold (none here). */
|
|
86
100
|
async resume(requestId) {
|
|
87
101
|
if (!this.conversationId || this.status !== 'idle')
|
|
@@ -43,8 +43,8 @@ export declare function isFromFrame(event: MessageEvent, frame: HTMLIFrameElemen
|
|
|
43
43
|
/**
|
|
44
44
|
* Target origin for messages to the sandbox frame. An opaque origin matches nothing but "*",
|
|
45
45
|
* so a message addressed to the sandbox's https origin would be dropped silently. The window
|
|
46
|
-
* reference itself (`frame.contentWindow`) is what scopes the message; the HTML it carries
|
|
47
|
-
*
|
|
46
|
+
* reference itself (`frame.contentWindow`) is what scopes the message; the HTML it carries may contain private data. Keep the outer frame
|
|
47
|
+
* on the trusted sandbox page; never add navigation controlled by rendered content.
|
|
48
48
|
*/
|
|
49
49
|
export declare const FRAME_TARGET_ORIGIN = "*";
|
|
50
50
|
/** Text an intent or prompt action becomes in the composer. */
|
|
@@ -74,8 +74,8 @@ export function isFromFrame(event, frame, sandboxOrigin) {
|
|
|
74
74
|
/**
|
|
75
75
|
* Target origin for messages to the sandbox frame. An opaque origin matches nothing but "*",
|
|
76
76
|
* so a message addressed to the sandbox's https origin would be dropped silently. The window
|
|
77
|
-
* reference itself (`frame.contentWindow`) is what scopes the message; the HTML it carries
|
|
78
|
-
*
|
|
77
|
+
* reference itself (`frame.contentWindow`) is what scopes the message; the HTML it carries may contain private data. Keep the outer frame
|
|
78
|
+
* on the trusted sandbox page; never add navigation controlled by rendered content.
|
|
79
79
|
*/
|
|
80
80
|
export const FRAME_TARGET_ORIGIN = '*';
|
|
81
81
|
/** Text an intent or prompt action becomes in the composer. */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type FrameAction } from './ui-frame-protocol.js';
|
|
2
|
+
export interface FrameRelay {
|
|
3
|
+
send(): void;
|
|
4
|
+
destroy(): void;
|
|
5
|
+
}
|
|
6
|
+
/** Shared lifecycle for artifacts and MCP-UI; the outer frame URL is host-controlled. */
|
|
7
|
+
export declare function createFrameRelay(options: {
|
|
8
|
+
frame: () => HTMLIFrameElement | null;
|
|
9
|
+
origin: () => string;
|
|
10
|
+
payload: () => {
|
|
11
|
+
html: string;
|
|
12
|
+
uri: string;
|
|
13
|
+
};
|
|
14
|
+
onaction: (action: FrameAction) => void;
|
|
15
|
+
}): FrameRelay;
|
|
16
|
+
//# sourceMappingURL=ui-frame-relay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-frame-relay.d.ts","sourceRoot":"","sources":["../src/lib/ui-frame-relay.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,WAAW,EACjB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,UAAU;IACzB,IAAI,IAAI,IAAI,CAAC;IACb,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,yFAAyF;AACzF,wBAAgB,gBAAgB,CAAC,OAAO,EAAE;IACxC,KAAK,EAAE,MAAM,iBAAiB,GAAG,IAAI,CAAC;IACtC,MAAM,EAAE,MAAM,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;CACzC,GAAG,UAAU,CAkBb"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FRAME_TARGET_ORIGIN, UI_FRAME_RENDER, isFromFrame, parseFrameMessage, } from './ui-frame-protocol.js';
|
|
2
|
+
/** Shared lifecycle for artifacts and MCP-UI; the outer frame URL is host-controlled. */
|
|
3
|
+
export function createFrameRelay(options) {
|
|
4
|
+
const send = () => {
|
|
5
|
+
options
|
|
6
|
+
.frame()
|
|
7
|
+
?.contentWindow?.postMessage({ type: UI_FRAME_RENDER, ...options.payload() }, FRAME_TARGET_ORIGIN);
|
|
8
|
+
};
|
|
9
|
+
const listener = (event) => {
|
|
10
|
+
if (!isFromFrame(event, options.frame(), options.origin()))
|
|
11
|
+
return;
|
|
12
|
+
const action = parseFrameMessage(event.data);
|
|
13
|
+
if (!action)
|
|
14
|
+
return;
|
|
15
|
+
if (action.type === 'ready')
|
|
16
|
+
send();
|
|
17
|
+
else
|
|
18
|
+
options.onaction(action);
|
|
19
|
+
};
|
|
20
|
+
window.addEventListener('message', listener);
|
|
21
|
+
return { send, destroy: () => window.removeEventListener('message', listener) };
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-frame-relay.test.d.ts","sourceRoot":"","sources":["../src/lib/ui-frame-relay.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { expect, it, vi } from 'vitest';
|
|
2
|
+
import { createFrameRelay } from './ui-frame-relay.js';
|
|
3
|
+
it('scopes ready/actions to its own frame, sends current HTML, and removes the listener', () => {
|
|
4
|
+
const postMessage = vi.fn();
|
|
5
|
+
const source = { postMessage };
|
|
6
|
+
const frame = { contentWindow: source };
|
|
7
|
+
const onaction = vi.fn();
|
|
8
|
+
let html = '<p>first</p>';
|
|
9
|
+
const relay = createFrameRelay({
|
|
10
|
+
frame: () => frame,
|
|
11
|
+
origin: () => 'https://sandbox.example',
|
|
12
|
+
payload: () => ({ html, uri: 'ui://test' }),
|
|
13
|
+
onaction,
|
|
14
|
+
});
|
|
15
|
+
const dispatch = (sender, data, origin = 'null') => window.dispatchEvent(new MessageEvent('message', { source: sender, origin, data }));
|
|
16
|
+
try {
|
|
17
|
+
dispatch(window, { type: 'ui-lifecycle-iframe-ready' });
|
|
18
|
+
expect(postMessage).not.toHaveBeenCalled();
|
|
19
|
+
dispatch(source, { type: 'ui-lifecycle-iframe-ready' });
|
|
20
|
+
expect(postMessage).toHaveBeenLastCalledWith({ type: 'agent-ui:render', html, uri: 'ui://test' }, '*');
|
|
21
|
+
html = '<p>updated artifact</p>';
|
|
22
|
+
relay.send();
|
|
23
|
+
expect(postMessage).toHaveBeenLastCalledWith({ type: 'agent-ui:render', html, uri: 'ui://test' }, '*');
|
|
24
|
+
dispatch(source, { type: 'tool', payload: { toolName: 'test' } });
|
|
25
|
+
expect(onaction).toHaveBeenCalledTimes(1);
|
|
26
|
+
expect(onaction).toHaveBeenCalledWith({ type: 'tool', toolName: 'test', params: undefined });
|
|
27
|
+
dispatch(source, { type: 'link', payload: { url: 'javascript:alert(1)' } });
|
|
28
|
+
dispatch(source, { type: 'notify', payload: { message: 'spoof' } }, 'https://other.example');
|
|
29
|
+
expect(onaction).toHaveBeenCalledTimes(1);
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
relay.destroy();
|
|
33
|
+
}
|
|
34
|
+
dispatch(source, { type: 'ui-lifecycle-iframe-ready' });
|
|
35
|
+
expect(postMessage).toHaveBeenCalledTimes(2);
|
|
36
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classic-homes/agent-chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
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.",
|
|
@@ -17,18 +17,21 @@
|
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@classic-homes/agent-client": "0.
|
|
21
|
-
"@classic-homes/agent-contracts": "0.
|
|
20
|
+
"@classic-homes/agent-client": "0.2.0",
|
|
21
|
+
"@classic-homes/agent-contracts": "0.2.0",
|
|
22
22
|
"dompurify": "3.4.17"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@classic-homes/theme-svelte": "^1.
|
|
25
|
+
"@classic-homes/theme-svelte": "^1.39.0",
|
|
26
26
|
"svelte": "^5.0.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@classic-homes/theme-svelte": "1.
|
|
29
|
+
"@classic-homes/theme-svelte": "1.39.0",
|
|
30
30
|
"@sveltejs/package": "3.0.2",
|
|
31
31
|
"@sveltejs/vite-plugin-svelte": "7.3.1",
|
|
32
|
+
"@testing-library/svelte": "5.4.2",
|
|
33
|
+
"@testing-library/user-event": "14.6.1",
|
|
34
|
+
"jsdom": "30.1.3",
|
|
32
35
|
"svelte": "5.57.3",
|
|
33
36
|
"svelte-check": "4.7.7",
|
|
34
37
|
"vite": "8.3.5"
|