@taskyon/tyclient 0.4.6 → 0.5.1

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.
@@ -0,0 +1,735 @@
1
+ import z3, { z } from 'zod';
2
+
3
+ // ../taskyon/src/types/node.ts
4
+ var JSONSchemaEnum = z.enum(["string", "number", "integer", "boolean", "object", "array", "null"]);
5
+ var JSONSchema7 = z.lazy(() => JSONSchemaObjectRaw);
6
+ var JSONSchema7Definition = z.lazy(
7
+ () => z.union([z.boolean(), JSONSchema7])
8
+ );
9
+ var JSONSchemaObjectRaw = z.strictObject({
10
+ $id: z.string().optional(),
11
+ $ref: z.string().optional(),
12
+ $schema: z.string().optional(),
13
+ $comment: z.string().optional(),
14
+ type: z.union([JSONSchemaEnum, z.array(JSONSchemaEnum)]).optional(),
15
+ const: z.any().optional(),
16
+ enum: z.any().optional(),
17
+ multipleOf: z.number().optional(),
18
+ maximum: z.number().optional(),
19
+ exclusiveMaximum: z.number().optional(),
20
+ minimum: z.number().optional(),
21
+ exclusiveMinimum: z.number().optional(),
22
+ maxLength: z.number().optional(),
23
+ minLength: z.number().optional(),
24
+ pattern: z.string().optional(),
25
+ items: z.lazy(() => z.union([JSONSchema7Definition, z.array(JSONSchema7Definition)]).optional()),
26
+ additionalItems: z.lazy(() => JSONSchema7Definition.optional()),
27
+ contains: z.lazy(() => JSONSchema7Definition.optional()),
28
+ maxItems: z.number().optional(),
29
+ minItems: z.number().optional(),
30
+ uniqueItems: z.boolean().optional(),
31
+ maxProperties: z.number().optional(),
32
+ minProperties: z.number().optional(),
33
+ required: z.array(z.string()).optional(),
34
+ properties: z.lazy(() => z.record(z.string(), JSONSchema7Definition).optional()),
35
+ patternProperties: z.lazy(() => z.record(z.string(), JSONSchema7Definition).optional()),
36
+ additionalProperties: z.lazy(() => JSONSchema7Definition.optional()),
37
+ unevaluatedProperties: z.lazy(() => JSONSchema7Definition.optional()),
38
+ dependencies: z.lazy(
39
+ () => z.record(z.string(), z.union([JSONSchema7Definition, z.array(z.string())])).optional()
40
+ ),
41
+ propertyNames: z.lazy(() => JSONSchema7Definition.optional()),
42
+ if: z.lazy(() => JSONSchema7Definition.optional()),
43
+ then: z.lazy(() => JSONSchema7Definition.optional()),
44
+ else: z.lazy(() => JSONSchema7Definition.optional()),
45
+ allOf: z.lazy(() => z.array(z.lazy(() => JSONSchema7Definition)).optional()),
46
+ anyOf: z.lazy(() => z.array(z.lazy(() => JSONSchema7Definition)).optional()),
47
+ oneOf: z.lazy(() => z.array(z.lazy(() => JSONSchema7Definition)).optional()),
48
+ not: z.lazy(() => JSONSchema7Definition.optional()),
49
+ format: z.string().optional(),
50
+ contentMediaType: z.string().optional(),
51
+ contentEncoding: z.string().optional(),
52
+ definitions: z.record(
53
+ z.string(),
54
+ z.lazy(() => JSONSchema7Definition)
55
+ ).optional(),
56
+ // support Draft‑2019+ $defs
57
+ // this is required, because the "json-schema-to-ts" library uses $defs and
58
+ // we can not validate those schemas without it.
59
+ $defs: z.record(
60
+ z.string(),
61
+ z.lazy(() => JSONSchema7Definition)
62
+ ).optional(),
63
+ title: z.string().optional(),
64
+ description: z.string().optional(),
65
+ default: z.any().optional(),
66
+ readOnly: z.boolean().optional(),
67
+ writeOnly: z.boolean().optional(),
68
+ examples: z.array(z.any()).optional(),
69
+ nullable: z.boolean().optional()
70
+ });
71
+ JSONSchemaObjectRaw.extend({
72
+ $id: z.string()
73
+ });
74
+
75
+ // ../taskyon/src/types/tools.ts
76
+ var taskMarker = "*TY_TASKRESULT*";
77
+ var FunctionName = z3.string().refine((val) => /^[a-zA-Z0-9_-]+$/.test(val), {
78
+ error: ({ input }) => {
79
+ const msg = typeof input === "string" ? input : JSON.stringify(input);
80
+ return `The function/tool name ${msg} contains illegal characters. It has to fulfill '^[a-zA-Z0-9_-]+$'`;
81
+ }
82
+ });
83
+ var ParamType = z3.union([
84
+ z3.string(),
85
+ z3.number(),
86
+ z3.boolean(),
87
+ z3.record(z3.string(), z3.unknown()),
88
+ z3.array(z3.unknown()),
89
+ z3.null(),
90
+ // We are also allowing undefined calls to the functions, even though this is not allowed in jsonschema.
91
+ // But we are sometimes calling our functions manually and this way we can also call them without parameters.
92
+ z3.undefined()
93
+ ]);
94
+ var FunctionArguments = z3.record(z3.string(), ParamType).meta({
95
+ description: "arguments of the function"
96
+ });
97
+ var FunctionCall = z3.object({
98
+ name: FunctionName,
99
+ arguments: FunctionArguments
100
+ });
101
+ var ToolBase = z3.object({
102
+ description: z3.string().meta({
103
+ description: "A short description about the tool so that an LLM knows when to use it."
104
+ }),
105
+ longDescription: z3.string().optional().meta({
106
+ description: "An optional longer description for more complicated operations with this tool."
107
+ }),
108
+ name: FunctionName.meta({
109
+ description: "Name of the tool. Has to fulfill: /^[a-zA-Z0-9_-]+$/"
110
+ }),
111
+ renderOptions: z3.object({
112
+ hideChat: z3.boolean().describe(
113
+ "hide the tool in the UI chat. Useful if the function is used very often and we don't want it to clutter the chatWindow"
114
+ ),
115
+ hideLlm: z3.boolean(
116
+ `HideLlm will hide the tool from an LLM inside chatCompletion. This is mainly useful for tools like "chatCompletion" which the llm doesn't need to see in the chatCompletion.`
117
+ )
118
+ }).partial().optional(),
119
+ parameters: JSONSchema7.meta({
120
+ description: "A JSON schema object describing the parameters of the function."
121
+ }).readonly(),
122
+ code: z3.string().optional().describe(
123
+ `The functionality of the tool as javascript code. If a function description doesn't include any code,
124
+ Taskyon will automatically call a postMessage event with the parameters to the parent window
125
+ with the function name.`
126
+ )
127
+ });
128
+
129
+ // ../taskyon/src/types/node.ts
130
+ var Annotation = z3.union([
131
+ z3.object({
132
+ type: z3.literal("url_citation"),
133
+ url_citation: z3.object({
134
+ end_index: z3.number(),
135
+ start_index: z3.number(),
136
+ title: z3.string(),
137
+ url: z3.string(),
138
+ content: z3.string()
139
+ }).partial().optional()
140
+ }),
141
+ z3.object({
142
+ type: z3.literal("file"),
143
+ content: z3.object({
144
+ text: z3.string(),
145
+ type: z3.string()
146
+ }).array().optional()
147
+ })
148
+ ]);
149
+ var MessageContent = z3.object({
150
+ type: z3.literal("message"),
151
+ data: z3.string(),
152
+ ann: Annotation.array().optional()
153
+ });
154
+ var StructuredContent = z3.object({
155
+ type: z3.literal("structured"),
156
+ data: z3.unknown()
157
+ });
158
+ var ToolCallContent = z3.object({ type: z3.literal("functioncall"), data: FunctionCall });
159
+ var UploadedFilesContent = z3.object({
160
+ type: z3.literal("files"),
161
+ data: z3.array(z3.string())
162
+ });
163
+ var ToolResultContent = z3.object({ type: z3.literal("toolresult"), data: z3.unknown() });
164
+ var ToolDefinition = z3.object({ type: z3.literal("tooldefinition"), data: ToolBase });
165
+ var ErrorContent = z3.object({ type: z3.literal("error"), data: z3.unknown() }).meta({
166
+ description: "Gets created if any error occurs during task processing."
167
+ });
168
+ var Return = z3.object({ type: z3.literal("return"), data: z3.string() }).describe(
169
+ `A Termination task always indicates the end of an autonomous task chat execution.
170
+ Every Leaf task which is not a Termination task can potentially continue to be executed...
171
+
172
+ We can indicate the reason for termination here as well...`
173
+ );
174
+ z3.union([MessageContent, ToolResultContent, ErrorContent]);
175
+ var TaskContent = z3.union([
176
+ MessageContent.strict(),
177
+ ToolResultContent.strict(),
178
+ ToolDefinition.strict(),
179
+ ErrorContent.strict(),
180
+ StructuredContent.strict(),
181
+ ToolCallContent.strict(),
182
+ // TODO: replace with a "context" function which can also be a link to a URL for example or maybe a search string for other tasks...
183
+ // we can declare function for a lot of these things this way :)
184
+ UploadedFilesContent.strict(),
185
+ Return.strict()
186
+ ]);
187
+ var TaskNode = z3.object({
188
+ // TODO: get rid of "role" and put it into chatCompletion only...
189
+ // we don't need it in the rest of the app, I think.. we might be able to indicate that a task was
190
+ // "automatically" created by using a notation in "authorID" e.g. something like.
191
+ // "pubKey:gen" if the task was automatically generated && pubKey if it wasn't
192
+ // OR: we could simply check the parents & priors of tasks. if tasks have a parent, they were generated
193
+ // by a function. user-generated message should not have a parent...
194
+ role: z3.enum(["system", "user", "assistant", "function"]),
195
+ name: z3.string().optional().meta({
196
+ description: "An optional name for the task"
197
+ }),
198
+ content: TaskContent.describe(
199
+ `This is the actual content of the task. This is the actual content which is process at each step.
200
+ For example this is, what an LLM would actually get to see. There are only a few different ways
201
+ of how content can be structured. `
202
+ ),
203
+ label: z3.array(z3.string()).optional(),
204
+ parentID: z3.string().optional().meta({
205
+ description: "The ID of the parent task which created this subtask on a lower stack level"
206
+ }),
207
+ priorID: z3.string().optional().meta({
208
+ description: "The ID of the previous task in the same stack level."
209
+ }),
210
+ // TODO: validate this ID using our content address creation functions
211
+ id: z3.string(),
212
+ authorId: z3.string().optional(),
213
+ created_at: z3.number().optional(),
214
+ acl: z3.string().array().optional().describe(`A number of public keys which act as access control lists (ACL).
215
+ They are given certain as a list of public keys + type of ownership.
216
+ ["pubkey:owner", "pubkey:editor1", "pubkey:editor2"]
217
+
218
+ The value is optional. If no ACL is specified, the task is "public" and
219
+ can for example be freely exchange in p2p settings.
220
+
221
+ TODO: define onwership types..`),
222
+ sig: z3.string().optional().meta({
223
+ description: "A signature from the author of the Task. It is created from the entire content of the tasj except for the signature itself."
224
+ })
225
+ });
226
+ var partialTaskDraft = TaskNode.partial().required({ role: true, content: true }).meta({
227
+ description: "This is just a subset of the task properties which can be used to define new tasks in various places."
228
+ });
229
+ TaskContent.options.map((opt) => {
230
+ return opt.shape.type._zod.def.values[0];
231
+ });
232
+ z3.enum(["Open", "Queued", "In Progress", "Completed", "Cancelled", "Error"]).describe(`The task state indicates on what is happening with the task: for example
233
+ it shows whether a task flow is seen as "completed" or whether its waiting
234
+ to be further processed... E.g. there could be a task with no results, which stil counts as "completed"`);
235
+
236
+ // ../taskyon/src/utils/encoding.ts
237
+ function uint8ArrayToBase64UrlSafe(data) {
238
+ const u8 = data instanceof Uint8Array ? data : new Uint8Array(data);
239
+ let binary = "";
240
+ for (let i = 0; i < u8.length; i++) {
241
+ binary += String.fromCharCode(u8[i]);
242
+ }
243
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
244
+ }
245
+ async function sha256UrlSafeHash(obj) {
246
+ const json = JSON.stringify(obj);
247
+ const encoder = new TextEncoder();
248
+ const data = encoder.encode(json);
249
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
250
+ return uint8ArrayToBase64UrlSafe(hashBuffer);
251
+ }
252
+ async function sha256UrlSafeHashFromFile(file) {
253
+ const buffer = await file.arrayBuffer();
254
+ const hashBuffer = await crypto.subtle.digest("SHA-256", buffer);
255
+ return uint8ArrayToBase64UrlSafe(hashBuffer);
256
+ }
257
+
258
+ // ../taskyon/src/core/createTasks.ts
259
+ var TaskWithoutId = TaskNode.omit({ id: true }).strip();
260
+ function normalizeObj(task) {
261
+ const clone = JSON.parse(JSON.stringify(task));
262
+ const sorted = Object.keys(clone).sort().reduce((acc, key) => {
263
+ acc[key] = clone[key];
264
+ return acc;
265
+ }, {});
266
+ return sorted;
267
+ }
268
+ async function taskContentHash(task) {
269
+ if (typeof crypto === "undefined" || !crypto.subtle) {
270
+ throw new Error(
271
+ "crypto.subtle is not available in this environment, We can currently not generate task IDs!!"
272
+ );
273
+ }
274
+ console.log("generating new hash ID for task");
275
+ const taskWithoutId = TaskWithoutId.parse(task);
276
+ const normalized = normalizeObj(taskWithoutId);
277
+ const hash = await sha256UrlSafeHash(normalized);
278
+ return { hash, normalized };
279
+ }
280
+ async function ensureValidTaskId(task) {
281
+ const { hash, normalized } = await taskContentHash(task);
282
+ if (task.id && hash !== task.id) {
283
+ throw new Error(
284
+ `Not able to create new task as id doesn't match content. Expected: ${hash} got: ${task.id}.`
285
+ );
286
+ }
287
+ return { ...normalized, id: hash };
288
+ }
289
+ var forgeTaskChain = async (tasks) => {
290
+ const flattened = [];
291
+ for (const tl of tasks) {
292
+ let lastTaskId = void 0;
293
+ for (const t of tl) {
294
+ const task = await createTaskNode({ ...t, priorID: lastTaskId }, { createMeta: "missing" });
295
+ lastTaskId = task.id;
296
+ flattened.push(task);
297
+ }
298
+ }
299
+ return flattened;
300
+ };
301
+ function addTaskNodeMeta(options, task) {
302
+ const next = { ...task };
303
+ if (options.createMeta === "overwrite") {
304
+ next.created_at = Date.now();
305
+ }
306
+ if (options.createMeta !== void 0 && !next.created_at) {
307
+ next.created_at = Date.now();
308
+ }
309
+ return next;
310
+ }
311
+ var createTaskNode = async (task, options = { createMeta: "missing" }) => {
312
+ if (!task.id) {
313
+ const newTask = addTaskNodeMeta(options, task);
314
+ return ensureValidTaskId(newTask);
315
+ }
316
+ return ensureValidTaskId(task);
317
+ };
318
+ var internalToolFunctionSchema = z3.custom((val) => typeof val === "function", {
319
+ message: "Expected a function that accepts any arguments and returns unknown or Promise<unknown>"
320
+ });
321
+ ToolBase.extend({
322
+ // TODO: take away he "optional" from this type here...
323
+ function: internalToolFunctionSchema.optional()
324
+ }).meta({
325
+ description: "Internal tool definition, which has access to the taskyon system"
326
+ });
327
+ function createTool(tool) {
328
+ console.log("create tool", tool.name);
329
+ return tool;
330
+ }
331
+ function toolCall(f) {
332
+ return {
333
+ role: "function",
334
+ content: {
335
+ type: "functioncall",
336
+ data: f
337
+ }
338
+ };
339
+ }
340
+ z3.object({
341
+ taskResultMarker: z3.literal(taskMarker).default(taskMarker).meta({
342
+ description: "we use this marker in order to indicate that the result should be added as new tasks!"
343
+ }),
344
+ taskChainList: z3.array(z3.array(partialTaskDraft))
345
+ });
346
+ function makeTaskResult(tasks) {
347
+ let tasksArray;
348
+ if (Array.isArray(tasks)) {
349
+ if (Array.isArray(tasks[0])) {
350
+ tasksArray = tasks;
351
+ } else {
352
+ tasksArray = [tasks];
353
+ }
354
+ } else {
355
+ tasksArray = [[tasks]];
356
+ }
357
+ tasks = tasksArray;
358
+ return {
359
+ taskResultMarker: taskMarker,
360
+ taskChainList: tasks
361
+ };
362
+ }
363
+ var RemoteFunctionBase = z3.object({
364
+ functionName: z3.string().meta({
365
+ description: "the name of the function"
366
+ })
367
+ });
368
+ var RemoteFunctionCall = RemoteFunctionBase.extend({
369
+ type: z3.literal("functionCall").meta({
370
+ description: "Field to indicate what kind of a message we have here."
371
+ }),
372
+ arguments: FunctionArguments.optional().meta({
373
+ description: "the arguments for the function as a json object"
374
+ })
375
+ }).meta({
376
+ description: "This type is used for sending messages with function calls between windows. E.g. from iframe to parent"
377
+ });
378
+ var RemoteFunctionResponse = RemoteFunctionBase.extend({
379
+ type: z3.literal("functionResponse").meta({
380
+ description: "Field to indicate what kind of a message we have here."
381
+ }),
382
+ response: z3.unknown().optional().meta({
383
+ description: "response of a FunctionCall, e.g. through postMessage with iframes."
384
+ }),
385
+ error: z3.unknown().optional().meta({ description: "if an error occurs in the remote function, we can use this property" })
386
+ }).meta({
387
+ description: "This type is used for sending messages with the result of a remote function call between windows. E.g. from parent to taskyon iframe"
388
+ });
389
+
390
+ // ../taskyon/src/types/apiTypes.ts
391
+ var EncryptedTasks = z.object({
392
+ type: z.literal("addTasks"),
393
+ data: z.instanceof(Uint8Array),
394
+ info: z.string(),
395
+ ids: z.array(z.string())
396
+ });
397
+ var RequestTask = z.object({
398
+ type: z.literal("requestTask"),
399
+ id: z.string()
400
+ });
401
+ var TaskCreated = z.object({
402
+ type: z.literal("taskCreated"),
403
+ task: TaskNode.optional(),
404
+ // TODO: add optional encrypted task!
405
+ ids: z.array(z.string()).optional(),
406
+ info: z.string().optional()
407
+ });
408
+ var TaskMessage = z.object({
409
+ type: z.literal("task").meta({
410
+ description: "Field to indicate what kind of a message we have here."
411
+ }),
412
+ task: partialTaskDraft,
413
+ // TODO: we don't need this anymore.. any functioncall task is one that should be "executed"
414
+ execute: z.boolean().default(false).meta({
415
+ description: "should the task be queued for execution?"
416
+ }),
417
+ /* TODO: this needs to be captured in the frontend somehow!
418
+ // but we first add it to the tasks and get back a content ID in the backend.
419
+ // so not sure yet how to do this...The backend soehow would have to communicate this to the frontend..
420
+ // maybe we could do this through teh "back-channel?"*/
421
+ show: z.boolean().default(false).meta({
422
+ description: "select the task in the GUI"
423
+ })
424
+ }).meta({
425
+ description: "With this message type we can send tasks to taskyon from outside, e.g. a parent to a taskyon iframe"
426
+ });
427
+ var TaskChainMessage = z.object({
428
+ type: z.literal("tasks").meta({
429
+ description: "Field to indicate what kind of a message we have here."
430
+ }),
431
+ execute: z.boolean().default(false),
432
+ tasks: partialTaskDraft.array(),
433
+ show: z.boolean().default(false).meta({
434
+ description: "select the last task in the GUI"
435
+ })
436
+ });
437
+ var FunctionDescriptionMessage = ToolBase.extend({
438
+ type: z.literal("functionDescription").meta({
439
+ description: "Field to indicate that this is a function description message."
440
+ })
441
+ });
442
+ var TyReadyMessage = z.object({ type: z.literal("taskyonReady") }).meta({
443
+ description: "simple message which signals, that our API is ready!"
444
+ });
445
+ var BaseMessage = z.object({
446
+ origin: z.string().optional(),
447
+ peerId: z.string().optional()
448
+ });
449
+ var TyStatusMessage = z.object({
450
+ type: z.literal("status"),
451
+ data: z.object({
452
+ type: z.literal("newtool"),
453
+ id: z.string().meta({ description: "id of new tool" })
454
+ })
455
+ }).meta({ description: "A list of status message for taskyon" });
456
+ var TaskWorkerMessage = z.discriminatedUnion("type", [
457
+ z.object({ ...BaseMessage.shape, ...RemoteFunctionCall.shape }),
458
+ z.object({ ...BaseMessage.shape, ...RemoteFunctionResponse.shape })
459
+ ]);
460
+ var FileMessage = z.object({
461
+ type: z.literal("file"),
462
+ id: z.string(),
463
+ name: z.string(),
464
+ mime: z.string(),
465
+ size: z.number(),
466
+ store: z.enum(["memory", "opfs"]).optional(),
467
+ file: z.file()
468
+ // only supported over MessageChannel for now
469
+ });
470
+ var TyP2P = z.discriminatedUnion("type", [EncryptedTasks, RequestTask, TaskCreated]);
471
+ var TyBusMessage = z.discriminatedUnion("type", [
472
+ ...TyP2P.options,
473
+ z.object({ ...BaseMessage.shape, ...TyStatusMessage.shape }),
474
+ ...TaskWorkerMessage.options
475
+ ]);
476
+ z.discriminatedUnion("type", [
477
+ // TODO: can we unify the task message with the SyncApi?
478
+ ...TyBusMessage.options,
479
+ z.object({ ...BaseMessage.shape, ...TaskMessage.shape }),
480
+ z.object({ ...BaseMessage.shape, ...TaskChainMessage.shape }),
481
+ z.object({ ...BaseMessage.shape, ...FunctionDescriptionMessage.shape }),
482
+ z.object({ ...BaseMessage.shape, ...TyReadyMessage.shape }),
483
+ z.object({ ...BaseMessage.shape, ...FileMessage.shape })
484
+ ]);
485
+ var sendFile = (send) => async (file) => {
486
+ const id = await sha256UrlSafeHashFromFile(file);
487
+ send({
488
+ type: "file",
489
+ id,
490
+ name: file.name,
491
+ mime: file.type,
492
+ size: file.size,
493
+ file
494
+ });
495
+ return id;
496
+ };
497
+
498
+ // ../taskyon/src/utils/frpBus.ts
499
+ function createStream() {
500
+ const observers = [];
501
+ const stream = (observer) => {
502
+ observers.push(observer);
503
+ return () => {
504
+ const idx = observers.indexOf(observer);
505
+ if (idx >= 0) observers.splice(idx, 1);
506
+ };
507
+ };
508
+ stream.unsubscribeAll = () => {
509
+ observers.length = 0;
510
+ };
511
+ stream.narrow = (pred) => {
512
+ const newStream = createStream();
513
+ stream((v) => {
514
+ if (pred(v)) newStream.emit(v);
515
+ });
516
+ return newStream.stream;
517
+ };
518
+ stream.map = (fn) => {
519
+ const newStream = createStream();
520
+ stream((v) => newStream.emit(fn(v)));
521
+ return newStream.stream;
522
+ };
523
+ stream.filter = stream.narrow;
524
+ stream.wait = (opts) => new Promise((resolve, reject) => {
525
+ if (opts?.signal?.aborted) {
526
+ const err = new Error("Aborted");
527
+ err.name = "AbortError";
528
+ reject(err);
529
+ return;
530
+ }
531
+ let done = false;
532
+ let timeout;
533
+ let unsub = () => {
534
+ };
535
+ const finish = (err, value) => {
536
+ if (done) return;
537
+ done = true;
538
+ unsub();
539
+ clearTimeout(timeout);
540
+ opts?.signal?.removeEventListener("abort", onAbort);
541
+ if (err) reject(err);
542
+ else resolve(value);
543
+ };
544
+ const onAbort = () => {
545
+ const err = new Error("Aborted");
546
+ err.name = "AbortError";
547
+ finish(err);
548
+ };
549
+ opts?.signal?.addEventListener("abort", onAbort);
550
+ if (opts?.timeoutMs)
551
+ timeout = setTimeout(
552
+ () => finish(new Error(`Timeout after ${opts.timeoutMs}ms`)),
553
+ opts.timeoutMs
554
+ );
555
+ const observer = (value) => finish(void 0, value);
556
+ unsub = stream(observer);
557
+ });
558
+ return {
559
+ stream,
560
+ emit: (v) => {
561
+ [...observers].forEach((o) => void o(v));
562
+ }
563
+ };
564
+ }
565
+ var connectChannels = (x, y) => {
566
+ const unsubX = x.receive((msg) => y.send(msg));
567
+ const unsubY = y.receive((msg) => x.send(msg));
568
+ return () => {
569
+ unsubX();
570
+ unsubY();
571
+ };
572
+ };
573
+ var makePort = (send, receive) => {
574
+ const self = {
575
+ send,
576
+ receive,
577
+ connect: (other) => connectChannels(self, other)
578
+ };
579
+ return self;
580
+ };
581
+ var createChannelsFromStreams = (outS, inS) => ({
582
+ x: makePort(outS.emit, inS.stream),
583
+ y: makePort(inS.emit, outS.stream)
584
+ });
585
+ var createDuplexChannel = () => createChannelsFromStreams(createStream(), createStream());
586
+ function MessageChannelBridge(dport, mport) {
587
+ const unsub = dport.receive((msg) => mport.postMessage(msg));
588
+ mport.onmessage = (msg) => dport.send(msg.data);
589
+ const destroy = () => {
590
+ unsub();
591
+ mport.close();
592
+ };
593
+ return { destroy };
594
+ }
595
+
596
+ // ../taskyon/src/api/index.ts
597
+ var createChatCompletionTask = (args) => toolCall({ name: "chatCompletion", arguments: args });
598
+ var processTasks = (tyPort) => async (taskList, opts) => {
599
+ const tasks = await forgeTaskChain(taskList);
600
+ tyPort.send({
601
+ type: "tasks",
602
+ tasks,
603
+ execute: true,
604
+ show: true,
605
+ // we want to show this task in our GUI as a succesful test
606
+ origin: "Taskyon Diagnostics"
607
+ });
608
+ const initialIds = tasks.map((t) => t.id);
609
+ const subTasks = new Set(initialIds);
610
+ const subTasksCreated = tyPort.receive.narrow((m) => {
611
+ console.log("api received", m);
612
+ if (m.type === "taskCreated" && "task" in m && !!m.task?.id && !!m.task?.parentID && subTasks.has(m.task?.parentID)) {
613
+ subTasks.add(m.task?.id);
614
+ return true;
615
+ }
616
+ return false;
617
+ }).map((msg) => msg.task);
618
+ const condition = opts.quitCondition ? subTasksCreated.filter(opts.quitCondition) : subTasksCreated.filter((t) => t.content.type === "message");
619
+ const unsub = condition((m) => console.log("received matching message on port:", m));
620
+ const lastMsg = await condition.wait(opts);
621
+ console.log("finished processin all tasks!");
622
+ unsub();
623
+ return lastMsg;
624
+ };
625
+
626
+ // src/index.ts
627
+ function safeClone(data) {
628
+ try {
629
+ return structuredClone(data);
630
+ } catch {
631
+ return JSON.parse(JSON.stringify(data));
632
+ }
633
+ }
634
+ var waitForApiChannel = (iframe) => {
635
+ return new Promise((resolve) => {
636
+ let stopped = false;
637
+ const tryConnect = () => {
638
+ if (stopped) return;
639
+ const channel = new MessageChannel();
640
+ const targetOrigin = new URL(iframe.src, location.href).origin || "*";
641
+ const handleFirst = (ev) => {
642
+ console.log("tyclient received first message from taskyon!", ev);
643
+ stopped = true;
644
+ channel.port1.removeEventListener("message", handleFirst);
645
+ clearTimeout(retryTimer);
646
+ resolve(channel.port1);
647
+ };
648
+ channel.port1.addEventListener("message", handleFirst, { once: true });
649
+ channel.port1.start();
650
+ try {
651
+ iframe.contentWindow?.postMessage({ type: "initPort" }, targetOrigin, [channel.port2]);
652
+ } catch {
653
+ }
654
+ const retryTimer = setTimeout(() => {
655
+ if (!stopped) {
656
+ channel.port1.close();
657
+ channel.port2.close();
658
+ tryConnect();
659
+ }
660
+ }, 200);
661
+ };
662
+ tryConnect();
663
+ });
664
+ };
665
+ async function handleFunctionExecution(args, tool, stopSignal) {
666
+ const result = await tool.function(args, {
667
+ taskChain: [],
668
+ getSecret: (name) => {
669
+ console.log("tyclient get secret name", name);
670
+ return Promise.resolve("N/A");
671
+ },
672
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
673
+ setSecret: (name, _value) => {
674
+ console.log("tyclient set secret name", name);
675
+ return Promise.resolve();
676
+ },
677
+ stopSignal,
678
+ toolId: "N/A"
679
+ });
680
+ return result;
681
+ }
682
+ async function initializeTaskyon(options) {
683
+ console.log("initialize taskyon tyclient...");
684
+ const toolMap = options.tools.reduce((p, c) => {
685
+ p[c.name] = c;
686
+ return p;
687
+ }, {});
688
+ const taskyon = document.getElementById("taskyon");
689
+ const controller = new AbortController();
690
+ const { x: clientSidePort, y: towardsIframe } = createDuplexChannel();
691
+ if (taskyon !== null && taskyon.tagName === "IFRAME" && taskyon.contentWindow !== null) {
692
+ console.log("make sure, we can ");
693
+ const iframeMessagePort = await waitForApiChannel(taskyon);
694
+ MessageChannelBridge(towardsIframe, iframeMessagePort);
695
+ const send = (msg) => {
696
+ console.log("tyclient sending", msg);
697
+ clientSidePort.send(safeClone(msg));
698
+ };
699
+ console.log("tyclient send our configuration!");
700
+ send({
701
+ type: "configurationMessage",
702
+ conf: options.configuration,
703
+ persist: options.persist,
704
+ origin: window.location.origin,
705
+ peerId: options?.name
706
+ });
707
+ console.log("tyclient sending our functions!");
708
+ options.tools.forEach((t) => {
709
+ const { function: _toolfunc, ...fdescr } = t;
710
+ send({
711
+ type: "functionDescription",
712
+ ...fdescr
713
+ });
714
+ });
715
+ console.log("tyclient set up function listener!");
716
+ clientSidePort.receive((msg) => console.log("tyclient received message", msg));
717
+ clientSidePort.receive.narrow((msg) => msg.type === "functionCall")(async (msg) => {
718
+ const tool = toolMap[msg.functionName];
719
+ if (tool) {
720
+ const res = await handleFunctionExecution(msg.arguments ?? {}, tool, controller.signal);
721
+ send({ type: "functionResponse", functionName: tool.name, response: res });
722
+ console.log("tyclient tool send functionResponse to iframe", res, tool);
723
+ }
724
+ });
725
+ }
726
+ return {
727
+ processTasks: processTasks(clientSidePort),
728
+ port: clientSidePort,
729
+ sendFile: (file) => sendFile(clientSidePort.send)(file)
730
+ };
731
+ }
732
+
733
+ export { createChatCompletionTask, createTool, initializeTaskyon, makeTaskResult, processTasks, toolCall };
734
+ //# sourceMappingURL=tyclient.mjs.map
735
+ //# sourceMappingURL=tyclient.mjs.map