@temporalio/agent-harness-svelte 0.1.0 → 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 +296 -4
- package/dist/state.svelte.d.ts +4 -1
- package/dist/state.svelte.js +13 -2
- package/package.json +19 -14
package/README.md
CHANGED
|
@@ -28,13 +28,305 @@ harness agent session.
|
|
|
28
28
|
- **The whole tree.** `messages`, `states` and `agentStatus` are the root agent's; `agents` and
|
|
29
29
|
`agent(id)` reach every subagent, and a `subagent` part's `messageId` names the child message
|
|
30
30
|
it ran. `pendingApprovals` and `pendingCallbacks` collect waiting tool calls from all of them.
|
|
31
|
+
- **Key message rows on `message.key`** (`{#each messages as m (m.key)}`), not `m.id`. A send's
|
|
32
|
+
`id` changes from its local id to the server's when it is admitted; its `key` does not, so the
|
|
33
|
+
row keeps its DOM, focus and measured height. (From the client release after 0.1.0.)
|
|
34
|
+
|
|
35
|
+
## Rendering replies as Markdown
|
|
36
|
+
|
|
37
|
+
A reply is model output, so treat its Markdown as untrusted. With `@tanstack/markdown`, which
|
|
38
|
+
escapes raw HTML by default, use `urlTransform` for two more rules:
|
|
39
|
+
- keep links only to `http:`, `https:` and `mailto:`;
|
|
40
|
+
- render images as their alt text. The renderer loads remote images by default, so a reply could
|
|
41
|
+
plant a tracking pixel or an image URL that carries data out.
|
|
42
|
+
|
|
43
|
+
```svelte
|
|
44
|
+
<script lang="ts">
|
|
45
|
+
import type { UrlTransform } from "@tanstack/markdown";
|
|
46
|
+
import { renderHtml } from "@tanstack/markdown/html";
|
|
47
|
+
import { parseMarkdown } from "@tanstack/markdown/parser";
|
|
48
|
+
|
|
49
|
+
let { text }: { text: string } = $props();
|
|
50
|
+
|
|
51
|
+
const urlTransform: UrlTransform = (url, kind, defaultUrl) =>
|
|
52
|
+
kind === "link" && defaultUrl && /^(https?:|mailto:)/i.test(defaultUrl) ? defaultUrl : null;
|
|
53
|
+
// Once per change of `text`, which the session batches per flush.
|
|
54
|
+
const html = $derived(renderHtml(parseMarkdown(text, { urlTransform })));
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div class="reply">{@html html}</div>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`null` from `urlTransform` drops the link or image but keeps its text. Relative links are
|
|
61
|
+
dropped too, since a reply has no base to be relative to.
|
|
62
|
+
|
|
63
|
+
To show some images, allow only sources that cannot phone home, as the console does: a raster
|
|
64
|
+
data URL (`/^data:image\/(png|jpeg|gif|webp);base64,[A-Za-z0-9+/]*={0,2}$/i`, never SVG, which
|
|
65
|
+
can carry script), capped in size, or a content-addressed path your own API serves. Return `url`
|
|
66
|
+
for `kind === "image"` when it passes, and keep `null` for everything else.
|
|
67
|
+
|
|
68
|
+
## Rendering replies per handler
|
|
69
|
+
|
|
70
|
+
With generated types, `message.handler` narrows `message.output` to that handler's reply, so a
|
|
71
|
+
map from handler to component can be typed end to end. Handlers without a card fall back to
|
|
72
|
+
JSON:
|
|
73
|
+
|
|
74
|
+
```svelte
|
|
75
|
+
<!-- Reply.svelte -->
|
|
76
|
+
<script lang="ts">
|
|
77
|
+
import type { Component } from "svelte";
|
|
78
|
+
import {
|
|
79
|
+
parsePartialJson,
|
|
80
|
+
type AgentSchema,
|
|
81
|
+
type HandlerName,
|
|
82
|
+
type HandlerOutput,
|
|
83
|
+
type HarnessMessage
|
|
84
|
+
} from "@temporalio/agent-harness-client";
|
|
85
|
+
import type { TicTacToeAgent } from "./client_sdk/TicTacToeAgent"; // from harness-codegen
|
|
86
|
+
import MoveCard from "./MoveCard.svelte"; // props: { output: Partial<TextReply> }
|
|
87
|
+
|
|
88
|
+
type Cards<A extends AgentSchema> = {
|
|
89
|
+
[H in HandlerName<A>]?: Component<{ output: Partial<HandlerOutput<A, H>> }>;
|
|
90
|
+
};
|
|
91
|
+
const cards: Cards<TicTacToeAgent> = { play: MoveCard };
|
|
92
|
+
|
|
93
|
+
let { message }: { message: HarnessMessage<TicTacToeAgent> } = $props();
|
|
94
|
+
|
|
95
|
+
// TypeScript can't tie the lookup to the narrowed message, so it is cast once here.
|
|
96
|
+
const Card = $derived(cards[message.handler] as Component<{ output: unknown }> | undefined);
|
|
97
|
+
// The reply text streamed so far: a data handler streams its output as JSON.
|
|
98
|
+
const text = $derived(
|
|
99
|
+
message.output == null && Card ? message.parts.map((part) => (part.type === "reply_delta" ? part.text : "")).join("") : ""
|
|
100
|
+
);
|
|
101
|
+
// Once per flush; strip a ```json fence a model may open with.
|
|
102
|
+
const draft = $derived(text ? parsePartialJson(text.replace(/^\s*```(?:json)?\s*/, "")) : undefined);
|
|
103
|
+
const output = $derived(message.output ?? draft);
|
|
104
|
+
</script>
|
|
105
|
+
|
|
106
|
+
{#if output != null}
|
|
107
|
+
{#if Card}<Card {output} />{:else}<pre>{JSON.stringify(output, null, 2)}</pre>{/if}
|
|
108
|
+
{/if}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
While the reply streams, `parsePartialJson` reads the JSON so far as a document: it closes open
|
|
112
|
+
strings and brackets and drops a half-written key or value. A card therefore gets a `Partial`
|
|
113
|
+
of its output and should draw a missing field as a placeholder. The parser is best effort, and
|
|
114
|
+
quadratic on text that never parses, so run it once per flush and only for handlers with a
|
|
115
|
+
card. (`parsePartialJson` is in the client release after 0.1.0.)
|
|
116
|
+
|
|
117
|
+
## Forms from a handler's schema
|
|
118
|
+
|
|
119
|
+
`GET /api/agent-interface/{sessionId}` lists each handler with `parameters`, the JSON Schema
|
|
120
|
+
of its input. The client turns that schema into fields (`describeSchema`), checks values
|
|
121
|
+
against it (`validate`, keyed by field path such as `address.city`) and builds the payload
|
|
122
|
+
(`buildPayload`). The server validates again and is the authority. (These helpers are in the
|
|
123
|
+
client release after 0.1.0.)
|
|
124
|
+
|
|
125
|
+
With native inputs, the schema's constraints become attributes the browser checks too:
|
|
126
|
+
|
|
127
|
+
```svelte
|
|
128
|
+
<!-- SchemaInput.svelte -->
|
|
129
|
+
<script lang="ts">
|
|
130
|
+
import type { SchemaField } from "@temporalio/agent-harness-client";
|
|
131
|
+
|
|
132
|
+
let { field, value, onchange }: { field: SchemaField; value: unknown; onchange: (value: unknown) => void } = $props();
|
|
133
|
+
const required = $derived(field.required && field.kind !== "boolean");
|
|
134
|
+
</script>
|
|
135
|
+
|
|
136
|
+
{#if field.kind === "boolean"}
|
|
137
|
+
<input id={field.name} type="checkbox" checked={value === true} onchange={(e) => onchange(e.currentTarget.checked)} />
|
|
138
|
+
{:else if field.kind === "enum"}
|
|
139
|
+
<select id={field.name} {required} value={String(value)} onchange={(e) => onchange(e.currentTarget.value)}>
|
|
140
|
+
{#each field.choices ?? [] as choice (choice)}<option>{choice}</option>{/each}
|
|
141
|
+
</select>
|
|
142
|
+
{:else if field.kind === "integer" || field.kind === "number"}
|
|
143
|
+
<input id={field.name} {required} type="number" step={field.kind === "integer" ? 1 : "any"} min={field.minimum}
|
|
144
|
+
max={field.maximum} value={String(value)} oninput={(e) => onchange(e.currentTarget.value)} />
|
|
145
|
+
{:else}
|
|
146
|
+
<!-- Text. `pattern` is anchored by the browser but not by JSON Schema, hence the `.*`s. Give
|
|
147
|
+
"json", "array" and "object" fields a textarea or recurse into `field.fields`/`field.item`. -->
|
|
148
|
+
<input id={field.name} {required} minlength={field.minLength} maxlength={field.maxLength}
|
|
149
|
+
pattern={field.pattern && `.*(?:${field.pattern}).*`} value={String(value)} oninput={(e) => onchange(e.currentTarget.value)} />
|
|
150
|
+
{/if}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```svelte
|
|
154
|
+
<!-- HandlerForm.svelte -->
|
|
155
|
+
<script lang="ts">
|
|
156
|
+
import { buildPayload, describeSchema, emptyValues, validate } from "@temporalio/agent-harness-client";
|
|
157
|
+
import { AgentSession } from "@temporalio/agent-harness-svelte";
|
|
158
|
+
import SchemaInput from "./SchemaInput.svelte";
|
|
159
|
+
|
|
160
|
+
/** `handler` is one entry of `GET /api/agent-interface/{sessionId}`. */
|
|
161
|
+
let { sessionId, handler }: { sessionId: string; handler: { name: string; parameters: Record<string, unknown> } } = $props();
|
|
162
|
+
const agent = new AgentSession({ get sessionId() { return sessionId; } });
|
|
163
|
+
const fields = $derived(describeSchema(handler.parameters));
|
|
164
|
+
// Reassigned on each edit (Svelte 5.25 or later), and blank again when `handler` changes.
|
|
165
|
+
let values = $derived(emptyValues(fields));
|
|
166
|
+
let problems = $state<Record<string, string>>({});
|
|
167
|
+
|
|
168
|
+
function submit(event: SubmitEvent) {
|
|
169
|
+
event.preventDefault();
|
|
170
|
+
problems = validate(fields, values);
|
|
171
|
+
if (Object.keys(problems).length === 0) void agent.sendMessage(handler.name, buildPayload(fields, values));
|
|
172
|
+
}
|
|
173
|
+
</script>
|
|
174
|
+
|
|
175
|
+
<form onsubmit={submit}>
|
|
176
|
+
{#each fields as field (field.name)}
|
|
177
|
+
<p>
|
|
178
|
+
<label for={field.name}>{field.title}</label>
|
|
179
|
+
<SchemaInput {field} value={values[field.name]} onchange={(value) => (values = { ...values, [field.name]: value })} />
|
|
180
|
+
{#if problems[field.name]}<span role="alert">{problems[field.name]}</span>{/if}
|
|
181
|
+
</p>
|
|
182
|
+
{/each}
|
|
183
|
+
<button>Send</button>
|
|
184
|
+
</form>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
With [TanStack Form](https://tanstack.com/form), wrap `validate` as a
|
|
188
|
+
[Standard Schema](https://standardschema.dev), which TanStack Form accepts as a validator and
|
|
189
|
+
whose issue paths it maps onto fields. The same `SchemaInput` renders each field:
|
|
190
|
+
|
|
191
|
+
```svelte
|
|
192
|
+
<!-- HandlerTanStackForm.svelte: wrap it in {#key handler.name} so another handler starts blank. -->
|
|
193
|
+
<script lang="ts">
|
|
194
|
+
import { createForm, type StandardSchemaV1 } from "@tanstack/svelte-form";
|
|
195
|
+
import { buildPayload, describeSchema, emptyValues, validate, type SchemaField } from "@temporalio/agent-harness-client";
|
|
196
|
+
import { AgentSession } from "@temporalio/agent-harness-svelte";
|
|
197
|
+
import SchemaInput from "./SchemaInput.svelte";
|
|
198
|
+
|
|
199
|
+
/** The client's `validate` as a Standard Schema: each problem is an issue at its field's path. */
|
|
200
|
+
const standardSchema = (fields: SchemaField[]): StandardSchemaV1<Record<string, unknown>> => ({
|
|
201
|
+
"~standard": {
|
|
202
|
+
version: 1,
|
|
203
|
+
vendor: "agent-harness",
|
|
204
|
+
validate: (input) => {
|
|
205
|
+
const value = input as Record<string, unknown>;
|
|
206
|
+
const problems = Object.entries(validate(fields, value));
|
|
207
|
+
return problems.length === 0
|
|
208
|
+
? { value }
|
|
209
|
+
: { issues: problems.map(([path, message]) => ({ message, path: path.split(".") })) };
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
let { sessionId, handler }: { sessionId: string; handler: { name: string; parameters: Record<string, unknown> } } = $props();
|
|
215
|
+
const agent = new AgentSession({ get sessionId() { return sessionId; } });
|
|
216
|
+
const fields = $derived(describeSchema(handler.parameters));
|
|
217
|
+
const form = createForm(() => ({
|
|
218
|
+
defaultValues: emptyValues(fields),
|
|
219
|
+
validators: { onSubmit: standardSchema(fields) },
|
|
220
|
+
onSubmit: ({ value }) => agent.sendMessage(handler.name, buildPayload(fields, value))
|
|
221
|
+
}));
|
|
222
|
+
</script>
|
|
223
|
+
|
|
224
|
+
<form onsubmit={(event) => { event.preventDefault(); void form.handleSubmit(); }}>
|
|
225
|
+
{#each fields as schemaField (schemaField.name)}
|
|
226
|
+
<form.Field name={schemaField.name}>
|
|
227
|
+
{#snippet children(field)}
|
|
228
|
+
<p>
|
|
229
|
+
<label for={schemaField.name}>{schemaField.title}</label>
|
|
230
|
+
<SchemaInput field={schemaField} value={field.state.value} onchange={field.handleChange} />
|
|
231
|
+
{#each field.state.meta.errors as issue (issue?.message)}<span role="alert">{issue?.message}</span>{/each}
|
|
232
|
+
</p>
|
|
233
|
+
{/snippet}
|
|
234
|
+
</form.Field>
|
|
235
|
+
{/each}
|
|
236
|
+
<button>Send</button>
|
|
237
|
+
</form>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## A virtual transcript
|
|
241
|
+
|
|
242
|
+
For long sessions, render only the visible messages with `@tanstack/svelte-virtual`:
|
|
243
|
+
- `anchorTo: "end"` keeps the view on the latest message as replies grow.
|
|
244
|
+
- `followOnAppend` follows new messages only while the reader is already at the end.
|
|
245
|
+
- `getItemKey` uses `message.key`, so a send keeps its measured height when the server admits it.
|
|
246
|
+
|
|
247
|
+
```svelte
|
|
248
|
+
<script lang="ts">
|
|
249
|
+
import { createVirtualizer } from "@tanstack/svelte-virtual";
|
|
250
|
+
import { get } from "svelte/store";
|
|
251
|
+
|
|
252
|
+
let { sessionId }: { sessionId: string } = $props();
|
|
253
|
+
const agent = new AgentSession({ get sessionId() { return sessionId; } });
|
|
254
|
+
const messages = $derived([...agent.messages, ...agent.pending]);
|
|
255
|
+
|
|
256
|
+
let scroller = $state<HTMLDivElement | null>(null);
|
|
257
|
+
const virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({
|
|
258
|
+
count: 0,
|
|
259
|
+
getScrollElement: () => scroller,
|
|
260
|
+
estimateSize: () => 72,
|
|
261
|
+
anchorTo: "end",
|
|
262
|
+
followOnAppend: true,
|
|
263
|
+
overscan: 6
|
|
264
|
+
});
|
|
265
|
+
$effect(() => {
|
|
266
|
+
const list = messages;
|
|
267
|
+
// `get`, not `$virtualizer`: setOptions updates the store, which would re-run this effect.
|
|
268
|
+
get(virtualizer).setOptions({ count: list.length, getItemKey: (index) => list[index]!.key });
|
|
269
|
+
});
|
|
270
|
+
const measure = (row: HTMLDivElement) => get(virtualizer).measureElement(row);
|
|
271
|
+
</script>
|
|
272
|
+
|
|
273
|
+
<div bind:this={scroller} tabindex="-1" style="height: 600px; overflow: auto">
|
|
274
|
+
<div style="position: relative; height: {$virtualizer.getTotalSize()}px">
|
|
275
|
+
{#each $virtualizer.getVirtualItems() as item (item.key)}
|
|
276
|
+
<div {@attach measure} data-index={item.index} style="position: absolute; width: 100%; transform: translateY({item.start}px)">
|
|
277
|
+
<Message message={messages[item.index]} />
|
|
278
|
+
</div>
|
|
279
|
+
{/each}
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
A row scrolled out of the window unmounts, taking focus with it. When that happens, move focus to
|
|
285
|
+
the row now at the same index, or to the list itself (hence its `tabindex="-1"`).
|
|
286
|
+
|
|
287
|
+
## With TanStack Query
|
|
288
|
+
|
|
289
|
+
The session list and session creation are plain request/response calls, so they suit TanStack
|
|
290
|
+
Query. Sends, approvals, callbacks and status stay in the session: they go through
|
|
291
|
+
`AgentSession` and arrive on its stream, not through queries or mutations.
|
|
292
|
+
|
|
293
|
+
```svelte
|
|
294
|
+
<script lang="ts">
|
|
295
|
+
import { createMutation, createQuery, queryOptions, useQueryClient } from "@tanstack/svelte-query";
|
|
296
|
+
import { HttpTransport, startSession } from "@temporalio/agent-harness-svelte";
|
|
297
|
+
import { TicTacToeAgent } from "./client_sdk/TicTacToeAgent"; // from harness-codegen
|
|
298
|
+
|
|
299
|
+
const transport = new HttpTransport({ baseUrl: "/api/" });
|
|
300
|
+
const sessionsQuery = () =>
|
|
301
|
+
queryOptions({
|
|
302
|
+
queryKey: ["agent-harness", "sessions"],
|
|
303
|
+
queryFn: ({ signal }) => transport.listSessions(signal)
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const queryClient = useQueryClient();
|
|
307
|
+
const sessions = createQuery(sessionsQuery);
|
|
308
|
+
const start = createMutation(() => ({
|
|
309
|
+
mutationFn: () => startSession(transport, TicTacToeAgent),
|
|
310
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: sessionsQuery().queryKey })
|
|
311
|
+
}));
|
|
312
|
+
</script>
|
|
313
|
+
|
|
314
|
+
<button onclick={() => start.mutate()} disabled={start.isPending}>New session</button>
|
|
315
|
+
{#each sessions.data ?? [] as s (s.workflow_id)}
|
|
316
|
+
<a href="?session={s.workflow_id}">{s.label}</a>
|
|
317
|
+
{/each}
|
|
318
|
+
```
|
|
31
319
|
|
|
32
320
|
## Development
|
|
33
321
|
|
|
34
|
-
This package
|
|
322
|
+
This package is part of the pnpm workspace in `typescript/`, and builds and tests against the
|
|
323
|
+
`@temporalio/agent-harness-client` in the working tree; its published dependency is the
|
|
324
|
+
`^<version>` range in `package.json`. `bazel test //typescript/packages/svelte/...` builds and
|
|
325
|
+
tests it. Without Bazel, build the client first, then run these commands from
|
|
326
|
+
`typescript/packages/svelte` (see
|
|
327
|
+
[Testing local changes](../README.md#testing-local-changes-without-publishing)):
|
|
35
328
|
|
|
36
329
|
```sh
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
npm run check && npm test && npm run build
|
|
330
|
+
pnpm install
|
|
331
|
+
pnpm run check && pnpm test && pnpm run build
|
|
40
332
|
```
|
package/dist/state.svelte.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { type AgentSchema, type AgentSseFrame, type AgentUpdate, type AgentView, type ConnectionStatus, type HarnessMessage, type SessionState, type UntypedAgent } from "@temporalio/agent-harness-client";
|
|
2
2
|
export declare class SvelteSessionState<A extends AgentSchema = UntypedAgent> implements SessionState<A> {
|
|
3
|
+
#private;
|
|
3
4
|
connection: ConnectionStatus;
|
|
4
5
|
error: Error | undefined;
|
|
5
6
|
rootAgentId: string | null;
|
|
6
7
|
agents: Readonly<Record<string, AgentView<UntypedAgent>>>;
|
|
7
8
|
pending: readonly HarnessMessage<A>[];
|
|
8
|
-
frames
|
|
9
|
+
/** Every frame received. Copied when read after new frames arrive, not on every flush, so a
|
|
10
|
+
* long session costs nothing extra per flush unless something reads its frames. */
|
|
11
|
+
get frames(): readonly AgentSseFrame[];
|
|
9
12
|
updateAgents(updates: ReadonlyArray<readonly [string, AgentUpdate]>): void;
|
|
10
13
|
appendFrames(frames: readonly AgentSseFrame[]): void;
|
|
11
14
|
}
|
package/dist/state.svelte.js
CHANGED
|
@@ -8,7 +8,16 @@ export class SvelteSessionState {
|
|
|
8
8
|
rootAgentId = $state(null);
|
|
9
9
|
agents = $state.raw({});
|
|
10
10
|
pending = $state.raw([]);
|
|
11
|
-
|
|
11
|
+
#frameCount = $state(0);
|
|
12
|
+
#frames = [];
|
|
13
|
+
#framesCopy = [];
|
|
14
|
+
/** Every frame received. Copied when read after new frames arrive, not on every flush, so a
|
|
15
|
+
* long session costs nothing extra per flush unless something reads its frames. */
|
|
16
|
+
get frames() {
|
|
17
|
+
if (this.#framesCopy.length !== this.#frameCount)
|
|
18
|
+
this.#framesCopy = this.#frames.slice();
|
|
19
|
+
return this.#framesCopy;
|
|
20
|
+
}
|
|
12
21
|
updateAgents(updates) {
|
|
13
22
|
const next = { ...this.agents };
|
|
14
23
|
for (const [id, update] of updates)
|
|
@@ -16,6 +25,8 @@ export class SvelteSessionState {
|
|
|
16
25
|
this.agents = next;
|
|
17
26
|
}
|
|
18
27
|
appendFrames(frames) {
|
|
19
|
-
|
|
28
|
+
for (const frame of frames)
|
|
29
|
+
this.#frames.push(frame);
|
|
30
|
+
this.#frameCount = this.#frames.length;
|
|
20
31
|
}
|
|
21
32
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@temporalio/agent-harness-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Svelte 5 binding for @temporalio/agent-harness-client: a reactive AgentSession over a temporal-agent-harness agent's event stream.",
|
|
@@ -18,26 +18,31 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "svelte-package",
|
|
23
|
-
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
24
|
-
"test": "vitest run"
|
|
25
|
-
},
|
|
26
21
|
"peerDependencies": {
|
|
27
22
|
"svelte": "^5.7.0"
|
|
28
23
|
},
|
|
29
24
|
"dependencies": {
|
|
30
|
-
"@temporalio/agent-harness-client": "^0.
|
|
25
|
+
"@temporalio/agent-harness-client": "^0.2.0"
|
|
31
26
|
},
|
|
32
27
|
"devDependencies": {
|
|
33
|
-
"@sveltejs/package": "^
|
|
34
|
-
"@sveltejs/vite-plugin-svelte": "^
|
|
28
|
+
"@sveltejs/package": "^3.0.1",
|
|
29
|
+
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
35
30
|
"@types/node": "^22.20.4",
|
|
36
|
-
"happy-dom": "^
|
|
37
|
-
"svelte": "^5.57.
|
|
38
|
-
"svelte-check": "^4.7.
|
|
31
|
+
"happy-dom": "^21.0.4",
|
|
32
|
+
"svelte": "^5.57.2",
|
|
33
|
+
"svelte-check": "^4.7.7",
|
|
39
34
|
"typescript": "^6.0.3",
|
|
40
|
-
"vite": "^
|
|
35
|
+
"vite": "^7.3.7",
|
|
41
36
|
"vitest": "^5.0.1"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/temporal-community/temporal-agent-harness.git",
|
|
41
|
+
"directory": "typescript/packages/svelte"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "svelte-package",
|
|
45
|
+
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
46
|
+
"test": "vitest run"
|
|
42
47
|
}
|
|
43
|
-
}
|
|
48
|
+
}
|