theokit 0.25.0 → 0.27.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/dist/{agent-GXJNFW6N.js → agent-I5QEOMCN.js} +2 -2
- package/dist/{agents-typed-client-RIJVHSX4.js → agents-typed-client-5DCGDWFZ.js} +8 -2
- package/dist/agents-typed-client-5DCGDWFZ.js.map +1 -0
- package/dist/{agents-typed-client-DCPSXUU5.js → agents-typed-client-MAAILRI6.js} +8 -2
- package/dist/agents-typed-client-MAAILRI6.js.map +1 -0
- package/dist/{build-WD2PT5TA.js → build-V2QEBE7I.js} +2 -2
- package/dist/{chunk-2JNG3P4Y.js → chunk-R4VVXLKZ.js} +2 -2
- package/dist/{chunk-GG6H76GC.js → chunk-VRVNM3QL.js} +2 -2
- package/dist/{chunk-LVGDYTU2.js → chunk-Y4TOVMNC.js} +2 -2
- package/dist/cli/index.js +4 -4
- package/dist/client/index.d.ts +177 -19
- package/dist/client/index.js +314 -62
- package/dist/client/index.js.map +1 -1
- package/dist/{dev-63UNEUF6.js → dev-5ZQMWVFC.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/{mcp-MIZFVNCL.js → mcp-VMKWB2BY.js} +2 -2
- package/dist/server/index.d.ts +10 -5
- package/dist/server/index.js +31 -7
- package/dist/server/index.js.map +1 -1
- package/dist/vite-plugin/index.js +1 -1
- package/dist/{vite-plugin-ST4JZQRJ.js → vite-plugin-BNI7YLCD.js} +2 -2
- package/package.json +1 -1
- package/dist/agents-typed-client-DCPSXUU5.js.map +0 -1
- package/dist/agents-typed-client-RIJVHSX4.js.map +0 -1
- /package/dist/{agent-GXJNFW6N.js.map → agent-I5QEOMCN.js.map} +0 -0
- /package/dist/{build-WD2PT5TA.js.map → build-V2QEBE7I.js.map} +0 -0
- /package/dist/{chunk-2JNG3P4Y.js.map → chunk-R4VVXLKZ.js.map} +0 -0
- /package/dist/{chunk-GG6H76GC.js.map → chunk-VRVNM3QL.js.map} +0 -0
- /package/dist/{chunk-LVGDYTU2.js.map → chunk-Y4TOVMNC.js.map} +0 -0
- /package/dist/{dev-63UNEUF6.js.map → dev-5ZQMWVFC.js.map} +0 -0
- /package/dist/{mcp-MIZFVNCL.js.map → mcp-VMKWB2BY.js.map} +0 -0
- /package/dist/{vite-plugin-ST4JZQRJ.js.map → vite-plugin-BNI7YLCD.js.map} +0 -0
package/dist/client/index.js
CHANGED
|
@@ -386,10 +386,20 @@ function createAppClient(baseUrlOrOptions, legacyFetchImpl) {
|
|
|
386
386
|
|
|
387
387
|
// src/client/consume-ui-message-stream.ts
|
|
388
388
|
async function consumeUIMessageStream(response, onMessage) {
|
|
389
|
-
|
|
390
|
-
|
|
389
|
+
const chunkStream = await responseToChunkStream(response);
|
|
390
|
+
await consumeChunkStream(chunkStream, onMessage);
|
|
391
|
+
}
|
|
392
|
+
async function responseToChunkStream(response) {
|
|
393
|
+
if (response.body === null) {
|
|
394
|
+
return new ReadableStream({
|
|
395
|
+
start(controller) {
|
|
396
|
+
controller.close();
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
const { parseJsonEventStream, uiMessageChunkSchema } = await import("ai");
|
|
391
401
|
const parsed = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema });
|
|
392
|
-
|
|
402
|
+
return new ReadableStream({
|
|
393
403
|
async start(controller) {
|
|
394
404
|
for await (const result of parsed) {
|
|
395
405
|
if (result.success) controller.enqueue(result.value);
|
|
@@ -397,77 +407,314 @@ async function consumeUIMessageStream(response, onMessage) {
|
|
|
397
407
|
controller.close();
|
|
398
408
|
}
|
|
399
409
|
});
|
|
400
|
-
|
|
410
|
+
}
|
|
411
|
+
async function consumeChunkStream(stream, onMessage) {
|
|
412
|
+
const { readUIMessageStream } = await import("ai");
|
|
413
|
+
for await (const message of readUIMessageStream({ stream })) {
|
|
401
414
|
onMessage(message);
|
|
402
415
|
}
|
|
403
416
|
}
|
|
404
417
|
|
|
405
418
|
// src/client/use-agent.ts
|
|
406
|
-
import {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
419
|
+
import { useMemo, useRef, useSyncExternalStore } from "react";
|
|
420
|
+
|
|
421
|
+
// src/client/agent-client.ts
|
|
422
|
+
function inputToText(input) {
|
|
423
|
+
if (typeof input === "object" && input !== null && typeof input.message === "string") {
|
|
424
|
+
return input.message;
|
|
425
|
+
}
|
|
426
|
+
if (typeof input === "string") return input;
|
|
427
|
+
return JSON.stringify(input);
|
|
428
|
+
}
|
|
429
|
+
function buildUserMessage(input) {
|
|
430
|
+
return {
|
|
431
|
+
id: crypto.randomUUID(),
|
|
432
|
+
role: "user",
|
|
433
|
+
parts: [{ type: "text", text: inputToText(input) }]
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
var AgentClient = class {
|
|
437
|
+
#transport;
|
|
438
|
+
#chatId = crypto.randomUUID();
|
|
439
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
440
|
+
#messages = [];
|
|
441
|
+
#status = "idle";
|
|
442
|
+
#error;
|
|
443
|
+
#controller = null;
|
|
444
|
+
#snapshot = { messages: [], status: "idle", error: void 0 };
|
|
445
|
+
constructor(transport) {
|
|
446
|
+
this.#transport = transport;
|
|
447
|
+
}
|
|
448
|
+
/** Subscribe to state changes; returns an unsubscribe fn. */
|
|
449
|
+
subscribe = (listener) => {
|
|
450
|
+
this.#listeners.add(listener);
|
|
451
|
+
return () => {
|
|
452
|
+
this.#listeners.delete(listener);
|
|
453
|
+
};
|
|
454
|
+
};
|
|
455
|
+
/** The current immutable snapshot (stable reference until the next emit). */
|
|
456
|
+
getSnapshot = () => this.#snapshot;
|
|
457
|
+
#emit() {
|
|
458
|
+
this.#snapshot = { messages: this.#messages, status: this.#status, error: this.#error };
|
|
459
|
+
for (const listener of this.#listeners) listener();
|
|
460
|
+
}
|
|
461
|
+
#upsert(message) {
|
|
462
|
+
const next = [...this.#messages];
|
|
463
|
+
const idx = next.findIndex((existing) => existing.id === message.id);
|
|
464
|
+
if (idx >= 0) next[idx] = message;
|
|
465
|
+
else next.push(message);
|
|
466
|
+
this.#messages = next;
|
|
467
|
+
}
|
|
468
|
+
async #drive(open, controller) {
|
|
469
|
+
const aborted = () => controller.signal.aborted;
|
|
470
|
+
try {
|
|
471
|
+
const stream = await open();
|
|
472
|
+
if (aborted()) return;
|
|
473
|
+
if (stream === null) {
|
|
474
|
+
this.#status = this.#messages.length > 0 ? "done" : "idle";
|
|
475
|
+
this.#emit();
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
await consumeChunkStream(stream, (message) => {
|
|
479
|
+
if (aborted()) return;
|
|
480
|
+
this.#upsert(message);
|
|
481
|
+
this.#emit();
|
|
482
|
+
});
|
|
483
|
+
if (aborted()) return;
|
|
484
|
+
this.#status = "done";
|
|
485
|
+
this.#emit();
|
|
486
|
+
} catch (err) {
|
|
487
|
+
if (aborted()) return;
|
|
488
|
+
this.#error = err instanceof Error ? err : new Error(String(err));
|
|
489
|
+
this.#status = "error";
|
|
490
|
+
this.#emit();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/** Send a typed input; opens a fresh stream (replaces prior messages). */
|
|
494
|
+
send = (input) => {
|
|
495
|
+
this.abort();
|
|
496
|
+
const controller = new AbortController();
|
|
497
|
+
this.#controller = controller;
|
|
498
|
+
this.#messages = [];
|
|
499
|
+
this.#error = void 0;
|
|
500
|
+
this.#status = "streaming";
|
|
501
|
+
this.#emit();
|
|
502
|
+
void this.#drive(
|
|
503
|
+
() => this.#transport.sendMessages({
|
|
504
|
+
trigger: "submit-message",
|
|
505
|
+
chatId: this.#chatId,
|
|
506
|
+
messageId: void 0,
|
|
507
|
+
messages: [buildUserMessage(input)],
|
|
508
|
+
abortSignal: controller.signal,
|
|
509
|
+
// Only object inputs flow as the request `body` (the turn text is always in `messages`);
|
|
510
|
+
// a primitive input is carried by the user message, never spread into the body.
|
|
511
|
+
body: typeof input === "object" && input !== null ? input : void 0
|
|
512
|
+
}),
|
|
513
|
+
controller
|
|
514
|
+
);
|
|
515
|
+
};
|
|
516
|
+
/** Resume an interrupted stream via the transport's `reconnectToStream` (no-op when unavailable). */
|
|
517
|
+
reconnect = () => {
|
|
518
|
+
const controller = new AbortController();
|
|
519
|
+
this.#controller = controller;
|
|
520
|
+
this.#status = "streaming";
|
|
521
|
+
this.#emit();
|
|
522
|
+
void this.#drive(() => this.#transport.reconnectToStream({ chatId: this.#chatId }), controller);
|
|
523
|
+
};
|
|
524
|
+
/** Abort an in-flight stream (not an error — leaves messages as-is). */
|
|
525
|
+
abort = () => {
|
|
526
|
+
this.#controller?.abort();
|
|
527
|
+
this.#controller = null;
|
|
528
|
+
};
|
|
529
|
+
/** Clear messages + error, back to idle. */
|
|
530
|
+
reset = () => {
|
|
531
|
+
this.abort();
|
|
532
|
+
this.#messages = [];
|
|
533
|
+
this.#error = void 0;
|
|
534
|
+
this.#status = "idle";
|
|
535
|
+
this.#emit();
|
|
536
|
+
};
|
|
537
|
+
/** Settle a paused HITL approval via the transport's HITL path (HTTP POST or inline callback). */
|
|
538
|
+
approve = async (approvalId, decision) => {
|
|
539
|
+
await this.#transport.approve?.(approvalId, decision);
|
|
540
|
+
};
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
// src/client/http-transport.ts
|
|
544
|
+
function toRecord(headers) {
|
|
545
|
+
if (headers === void 0) return {};
|
|
546
|
+
if (headers instanceof Headers) return Object.fromEntries(headers.entries());
|
|
547
|
+
return headers;
|
|
548
|
+
}
|
|
549
|
+
var HttpTransport = class {
|
|
550
|
+
#api;
|
|
551
|
+
#headers;
|
|
552
|
+
#fetch;
|
|
553
|
+
/** Server-minted id of the last run (from `x-theokit-run-id`) — the reconnect key. */
|
|
554
|
+
#lastRunId;
|
|
555
|
+
constructor(options) {
|
|
556
|
+
this.#api = options.api;
|
|
557
|
+
this.#headers = options.headers ?? {};
|
|
558
|
+
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
559
|
+
}
|
|
560
|
+
/** Resolve the configured headers per request (a resolver evaluates now — dynamic auth is never stale). */
|
|
561
|
+
#resolveHeaders() {
|
|
562
|
+
return (typeof this.#headers === "function" ? this.#headers() : this.#headers) ?? {};
|
|
563
|
+
}
|
|
564
|
+
async sendMessages(options) {
|
|
565
|
+
const { messages, abortSignal, headers, body } = options;
|
|
566
|
+
const extra = typeof body === "object" ? body : void 0;
|
|
567
|
+
const response = await this.#fetch(this.#api, {
|
|
568
|
+
method: "POST",
|
|
569
|
+
headers: {
|
|
570
|
+
"content-type": "application/json",
|
|
571
|
+
accept: "text/event-stream",
|
|
572
|
+
"X-Theo-Action": "1",
|
|
573
|
+
...this.#resolveHeaders(),
|
|
574
|
+
...toRecord(headers)
|
|
575
|
+
},
|
|
576
|
+
body: JSON.stringify({ ...extra, messages }),
|
|
577
|
+
signal: abortSignal
|
|
578
|
+
});
|
|
579
|
+
if (!response.ok) {
|
|
580
|
+
throw new Error(
|
|
581
|
+
`Agent request to ${this.#api} failed: ${response.status} ${response.statusText}`
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
this.#lastRunId = response.headers.get("x-theokit-run-id") ?? void 0;
|
|
585
|
+
return responseToChunkStream(response);
|
|
586
|
+
}
|
|
587
|
+
async reconnectToStream(options) {
|
|
588
|
+
if (this.#lastRunId === void 0) return null;
|
|
589
|
+
const response = await this.#fetch(`${this.#api}/runs/${this.#lastRunId}/stream`, {
|
|
590
|
+
method: "GET",
|
|
591
|
+
headers: { ...this.#resolveHeaders(), ...toRecord(options.headers) }
|
|
592
|
+
});
|
|
593
|
+
if (response.status === 404) return null;
|
|
594
|
+
if (!response.ok) {
|
|
595
|
+
throw new Error(
|
|
596
|
+
`Agent reconnect to run ${this.#lastRunId} failed: ${response.status} ${response.statusText}`
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
return responseToChunkStream(response);
|
|
600
|
+
}
|
|
601
|
+
async approve(approvalId, decision) {
|
|
602
|
+
const response = await this.#fetch(`${this.#api}/approve/${approvalId}`, {
|
|
603
|
+
method: "POST",
|
|
604
|
+
headers: {
|
|
605
|
+
"content-type": "application/json",
|
|
606
|
+
"X-Theo-Action": "1",
|
|
607
|
+
...this.#resolveHeaders()
|
|
608
|
+
},
|
|
609
|
+
body: JSON.stringify(decision)
|
|
610
|
+
});
|
|
611
|
+
if (!response.ok) {
|
|
612
|
+
throw new Error(`Approve ${approvalId} failed: ${response.status} ${response.statusText}`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
// src/client/use-agent.ts
|
|
618
|
+
function useAgent(pathOrTransport, options = {}) {
|
|
619
|
+
const optionsRef = useRef(options);
|
|
620
|
+
optionsRef.current = options;
|
|
621
|
+
const client = useMemo(
|
|
622
|
+
() => new AgentClient(
|
|
623
|
+
typeof pathOrTransport === "string" ? new HttpTransport({
|
|
624
|
+
api: pathOrTransport,
|
|
625
|
+
headers: () => optionsRef.current.headers,
|
|
626
|
+
fetch: optionsRef.current.fetch
|
|
627
|
+
}) : pathOrTransport
|
|
628
|
+
),
|
|
629
|
+
// Identity is the binding; headers are resolved live via optionsRef, fetch captured at creation.
|
|
630
|
+
[pathOrTransport]
|
|
631
|
+
);
|
|
632
|
+
const state = useSyncExternalStore(client.subscribe, client.getSnapshot, client.getSnapshot);
|
|
633
|
+
return {
|
|
634
|
+
messages: state.messages,
|
|
635
|
+
status: state.status,
|
|
636
|
+
error: state.error,
|
|
637
|
+
send: client.send,
|
|
638
|
+
abort: client.abort,
|
|
639
|
+
reset: client.reset,
|
|
640
|
+
approve: client.approve,
|
|
641
|
+
reconnect: client.reconnect
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/client/in-process-transport.ts
|
|
646
|
+
function generatorToStream(gen) {
|
|
647
|
+
return new ReadableStream({
|
|
648
|
+
async pull(controller) {
|
|
649
|
+
try {
|
|
650
|
+
const result = await gen.next();
|
|
651
|
+
if (result.done === true) {
|
|
652
|
+
controller.close();
|
|
653
|
+
return;
|
|
458
654
|
}
|
|
459
|
-
|
|
655
|
+
controller.enqueue(result.value);
|
|
656
|
+
} catch (err) {
|
|
657
|
+
controller.error(err);
|
|
658
|
+
}
|
|
460
659
|
},
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
660
|
+
async cancel() {
|
|
661
|
+
await gen.return(void 0);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
464
664
|
}
|
|
665
|
+
function extractLastUserText(messages) {
|
|
666
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
667
|
+
const message = messages[i];
|
|
668
|
+
if (message.role !== "user") continue;
|
|
669
|
+
const text = message.parts.filter((part) => part.type === "text").map((part) => part.text).join("");
|
|
670
|
+
if (text.length > 0) return text;
|
|
671
|
+
}
|
|
672
|
+
return "";
|
|
673
|
+
}
|
|
674
|
+
var InProcessTransport = class {
|
|
675
|
+
#run;
|
|
676
|
+
/** Pending inline approvals: approvalId → resolver of the parked `awaitApproval` promise. */
|
|
677
|
+
#pending = /* @__PURE__ */ new Map();
|
|
678
|
+
constructor(options) {
|
|
679
|
+
this.#run = options.run;
|
|
680
|
+
}
|
|
681
|
+
#awaitApproval = (req) => new Promise((resolve, reject) => {
|
|
682
|
+
if (this.#pending.has(req.approvalId)) {
|
|
683
|
+
reject(new Error(`Duplicate pending approval id '${req.approvalId}' \u2014 ids must be unique.`));
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
this.#pending.set(req.approvalId, resolve);
|
|
687
|
+
});
|
|
688
|
+
sendMessages(options) {
|
|
689
|
+
const { messages, abortSignal } = options;
|
|
690
|
+
const generator = this.#run({
|
|
691
|
+
message: extractLastUserText(messages),
|
|
692
|
+
signal: abortSignal ?? void 0,
|
|
693
|
+
awaitApproval: this.#awaitApproval
|
|
694
|
+
});
|
|
695
|
+
return Promise.resolve(generatorToStream(generator));
|
|
696
|
+
}
|
|
697
|
+
reconnectToStream() {
|
|
698
|
+
return Promise.resolve(null);
|
|
699
|
+
}
|
|
700
|
+
approve(approvalId, decision) {
|
|
701
|
+
const resolve = this.#pending.get(approvalId);
|
|
702
|
+
if (resolve === void 0) {
|
|
703
|
+
return Promise.reject(
|
|
704
|
+
new Error(`No pending approval '${approvalId}' (unknown or already settled).`)
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
this.#pending.delete(approvalId);
|
|
708
|
+
resolve(decision);
|
|
709
|
+
return Promise.resolve();
|
|
710
|
+
}
|
|
711
|
+
};
|
|
465
712
|
|
|
466
713
|
// src/client/link.tsx
|
|
467
714
|
import {
|
|
468
715
|
Link as RouterLink
|
|
469
716
|
} from "react-router";
|
|
470
|
-
import { useRef as useRef2, useCallback
|
|
717
|
+
import { useRef as useRef2, useCallback, useEffect } from "react";
|
|
471
718
|
import { jsx } from "react/jsx-runtime";
|
|
472
719
|
var prefetched = /* @__PURE__ */ new Set();
|
|
473
720
|
function injectPrefetch(href) {
|
|
@@ -485,7 +732,7 @@ function resolveTo(to) {
|
|
|
485
732
|
}
|
|
486
733
|
function Link({ prefetch = "intent", to, onMouseEnter, onFocus, ...rest }) {
|
|
487
734
|
const ref = useRef2(null);
|
|
488
|
-
const handleIntent =
|
|
735
|
+
const handleIntent = useCallback(
|
|
489
736
|
(event) => {
|
|
490
737
|
if (prefetch === "intent") {
|
|
491
738
|
injectPrefetch(resolveTo(to));
|
|
@@ -601,17 +848,22 @@ function mountMcpApp(container, resource, opts) {
|
|
|
601
848
|
};
|
|
602
849
|
}
|
|
603
850
|
export {
|
|
851
|
+
AgentClient,
|
|
852
|
+
HttpTransport,
|
|
604
853
|
Image,
|
|
854
|
+
InProcessTransport,
|
|
605
855
|
Link,
|
|
606
856
|
MCP_APP_SANDBOX,
|
|
607
857
|
Metadata,
|
|
608
858
|
TheoFetchError,
|
|
609
859
|
buildUseTheoQueryConfig,
|
|
860
|
+
consumeChunkStream,
|
|
610
861
|
consumeUIMessageStream,
|
|
611
862
|
createAppClient,
|
|
612
863
|
createBatcher,
|
|
613
864
|
createGuestMessageHandler,
|
|
614
865
|
mountMcpApp,
|
|
866
|
+
responseToChunkStream,
|
|
615
867
|
stableQueryKey,
|
|
616
868
|
theoFetch,
|
|
617
869
|
useAgent
|