@aomi-labs/react 0.2.0 → 0.2.2

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/index.cjs CHANGED
@@ -34,11 +34,12 @@ var __copyProps = (to, from, except, desc) => {
34
34
  };
35
35
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
36
 
37
- // src/index.ts
37
+ // packages/react/src/index.ts
38
38
  var index_exports = {};
39
39
  __export(index_exports, {
40
40
  AomiRuntimeProvider: () => AomiRuntimeProvider,
41
41
  BackendApi: () => BackendApi,
42
+ ControlContextProvider: () => ControlContextProvider,
42
43
  EventContextProvider: () => EventContextProvider,
43
44
  NotificationContextProvider: () => NotificationContextProvider,
44
45
  ThreadContextProvider: () => ThreadContextProvider,
@@ -46,7 +47,9 @@ __export(index_exports, {
46
47
  cn: () => cn,
47
48
  formatAddress: () => formatAddress,
48
49
  getNetworkName: () => getNetworkName,
50
+ initThreadControl: () => initThreadControl,
49
51
  useAomiRuntime: () => useAomiRuntime,
52
+ useControl: () => useControl,
50
53
  useCurrentThreadMessages: () => useCurrentThreadMessages,
51
54
  useCurrentThreadMetadata: () => useCurrentThreadMetadata,
52
55
  useEventContext: () => useEventContext,
@@ -58,7 +61,7 @@ __export(index_exports, {
58
61
  });
59
62
  module.exports = __toCommonJS(index_exports);
60
63
 
61
- // src/backend/sse.ts
64
+ // packages/react/src/backend/sse.ts
62
65
  function extractSseData(rawEvent) {
63
66
  const dataLines = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart());
64
67
  if (!dataLines.length) return null;
@@ -244,8 +247,9 @@ function createSseSubscriber({
244
247
  return { subscribe: subscribe2 };
245
248
  }
246
249
 
247
- // src/backend/client.ts
250
+ // packages/react/src/backend/client.ts
248
251
  var SESSION_ID_HEADER = "X-Session-Id";
252
+ var API_KEY_HEADER = "X-API-Key";
249
253
  function toQueryString(payload) {
250
254
  const params = new URLSearchParams();
251
255
  for (const [key, value] of Object.entries(payload)) {
@@ -260,12 +264,16 @@ function withSessionHeader(sessionId, init) {
260
264
  headers.set(SESSION_ID_HEADER, sessionId);
261
265
  return headers;
262
266
  }
263
- async function postState(backendUrl, path, payload, sessionId) {
267
+ async function postState(backendUrl, path, payload, sessionId, apiKey) {
264
268
  const query = toQueryString(payload);
265
269
  const url = `${backendUrl}${path}${query}`;
270
+ const headers = new Headers(withSessionHeader(sessionId));
271
+ if (apiKey) {
272
+ headers.set(API_KEY_HEADER, apiKey);
273
+ }
266
274
  const response = await fetch(url, {
267
275
  method: "POST",
268
- headers: withSessionHeader(sessionId)
276
+ headers
269
277
  });
270
278
  if (!response.ok) {
271
279
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
@@ -293,15 +301,17 @@ var BackendApi = class {
293
301
  }
294
302
  return await response.json();
295
303
  }
296
- async postChatMessage(sessionId, message, publicKey) {
304
+ async postChatMessage(sessionId, message, namespace, publicKey, apiKey) {
305
+ const payload = { message, namespace };
306
+ if (publicKey) {
307
+ payload.public_key = publicKey;
308
+ }
297
309
  return postState(
298
310
  this.backendUrl,
299
311
  "/api/chat",
300
- {
301
- message,
302
- public_key: publicKey
303
- },
304
- sessionId
312
+ payload,
313
+ sessionId,
314
+ apiKey
305
315
  );
306
316
  }
307
317
  async postSystemMessage(sessionId, message) {
@@ -421,16 +431,535 @@ var BackendApi = class {
421
431
  }
422
432
  return await response.json();
423
433
  }
424
- // fetchEventsAfter removed: /api/events only supports count now
434
+ // ===========================================================================
435
+ // Control API
436
+ // ===========================================================================
437
+ /**
438
+ * Get allowed namespaces for the current request context.
439
+ */
440
+ async getNamespaces(sessionId, publicKey, apiKey) {
441
+ const url = new URL("/api/control/namespaces", this.backendUrl);
442
+ if (publicKey) {
443
+ url.searchParams.set("public_key", publicKey);
444
+ }
445
+ console.log("[BackendApi.getNamespaces]", {
446
+ backendUrl: this.backendUrl,
447
+ fullUrl: url.toString(),
448
+ sessionId,
449
+ publicKey
450
+ });
451
+ const headers = new Headers(withSessionHeader(sessionId));
452
+ if (apiKey) {
453
+ headers.set(API_KEY_HEADER, apiKey);
454
+ }
455
+ const response = await fetch(url.toString(), { headers });
456
+ if (!response.ok) {
457
+ throw new Error(`Failed to get namespaces: HTTP ${response.status}`);
458
+ }
459
+ return await response.json();
460
+ }
461
+ /**
462
+ * Get available models.
463
+ */
464
+ async getModels(sessionId) {
465
+ const url = new URL("/api/control/models", this.backendUrl);
466
+ console.log("[BackendApi.getModels]", {
467
+ backendUrl: this.backendUrl,
468
+ fullUrl: url.toString(),
469
+ sessionId
470
+ });
471
+ const response = await fetch(url.toString(), {
472
+ headers: withSessionHeader(sessionId)
473
+ });
474
+ if (!response.ok) {
475
+ throw new Error(`Failed to get models: HTTP ${response.status}`);
476
+ }
477
+ return await response.json();
478
+ }
479
+ /**
480
+ * Set the model selection for a session.
481
+ */
482
+ async setModel(sessionId, rig, namespace, apiKey) {
483
+ const payload = { rig };
484
+ if (namespace) {
485
+ payload.namespace = namespace;
486
+ }
487
+ return postState(this.backendUrl, "/api/control/model", payload, sessionId, apiKey);
488
+ }
425
489
  };
426
490
 
427
- // src/runtime/aomi-runtime.tsx
428
- var import_react9 = require("react");
491
+ // packages/react/src/runtime/aomi-runtime.tsx
492
+ var import_react10 = require("react");
429
493
 
430
- // src/contexts/event-context.tsx
494
+ // packages/react/src/contexts/control-context.tsx
431
495
  var import_react = require("react");
432
496
 
433
- // src/backend/types.ts
497
+ // packages/react/src/utils/uuid.ts
498
+ function generateUUID() {
499
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
500
+ return crypto.randomUUID();
501
+ }
502
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
503
+ const r = Math.random() * 16 | 0;
504
+ const v = c === "x" ? r : r & 3 | 8;
505
+ return v.toString(16);
506
+ });
507
+ }
508
+
509
+ // packages/react/src/state/thread-store.ts
510
+ var shouldLogThreadUpdates = process.env.NODE_ENV !== "production";
511
+ var logThreadMetadataChange = (source, threadId, prev, next) => {
512
+ if (!shouldLogThreadUpdates) return;
513
+ if (!prev && !next) return;
514
+ if (!prev || !next) {
515
+ console.debug(`[aomi][thread:${source}]`, { threadId, prev, next });
516
+ return;
517
+ }
518
+ if (prev.title !== next.title || prev.status !== next.status || prev.lastActiveAt !== next.lastActiveAt) {
519
+ console.debug(`[aomi][thread:${source}]`, { threadId, prev, next });
520
+ }
521
+ };
522
+ function initThreadControl() {
523
+ return {
524
+ model: null,
525
+ namespace: null,
526
+ controlDirty: false,
527
+ isProcessing: false
528
+ };
529
+ }
530
+ var ThreadStore = class {
531
+ constructor(options) {
532
+ this.listeners = /* @__PURE__ */ new Set();
533
+ this.subscribe = (listener) => {
534
+ this.listeners.add(listener);
535
+ return () => {
536
+ this.listeners.delete(listener);
537
+ };
538
+ };
539
+ this.getSnapshot = () => this.snapshot;
540
+ this.setCurrentThreadId = (threadId) => {
541
+ this.ensureThreadExists(threadId);
542
+ this.updateState({ currentThreadId: threadId });
543
+ };
544
+ this.bumpThreadViewKey = () => {
545
+ this.updateState({ threadViewKey: this.state.threadViewKey + 1 });
546
+ };
547
+ this.setThreadCnt = (updater) => {
548
+ const nextCnt = this.resolveStateAction(updater, this.state.threadCnt);
549
+ this.updateState({ threadCnt: nextCnt });
550
+ };
551
+ this.setThreads = (updater) => {
552
+ const nextThreads = this.resolveStateAction(updater, this.state.threads);
553
+ this.updateState({ threads: new Map(nextThreads) });
554
+ };
555
+ this.setThreadMetadata = (updater) => {
556
+ const prevMetadata = this.state.threadMetadata;
557
+ const nextMetadata = this.resolveStateAction(updater, prevMetadata);
558
+ for (const [threadId, next] of nextMetadata.entries()) {
559
+ logThreadMetadataChange(
560
+ "setThreadMetadata",
561
+ threadId,
562
+ prevMetadata.get(threadId),
563
+ next
564
+ );
565
+ }
566
+ for (const [threadId, prev] of prevMetadata.entries()) {
567
+ if (!nextMetadata.has(threadId)) {
568
+ logThreadMetadataChange("setThreadMetadata", threadId, prev, void 0);
569
+ }
570
+ }
571
+ this.updateState({ threadMetadata: new Map(nextMetadata) });
572
+ };
573
+ this.setThreadMessages = (threadId, messages) => {
574
+ this.ensureThreadExists(threadId);
575
+ const nextThreads = new Map(this.state.threads);
576
+ nextThreads.set(threadId, messages);
577
+ this.updateState({ threads: nextThreads });
578
+ };
579
+ this.getThreadMessages = (threadId) => {
580
+ var _a;
581
+ return (_a = this.state.threads.get(threadId)) != null ? _a : [];
582
+ };
583
+ this.getThreadMetadata = (threadId) => {
584
+ return this.state.threadMetadata.get(threadId);
585
+ };
586
+ this.updateThreadMetadata = (threadId, updates) => {
587
+ const existing = this.state.threadMetadata.get(threadId);
588
+ if (!existing) {
589
+ return;
590
+ }
591
+ const next = __spreadValues(__spreadValues({}, existing), updates);
592
+ const nextMetadata = new Map(this.state.threadMetadata);
593
+ nextMetadata.set(threadId, next);
594
+ logThreadMetadataChange("updateThreadMetadata", threadId, existing, next);
595
+ this.updateState({ threadMetadata: nextMetadata });
596
+ };
597
+ var _a;
598
+ const initialThreadId = (_a = options == null ? void 0 : options.initialThreadId) != null ? _a : generateUUID();
599
+ this.state = {
600
+ currentThreadId: initialThreadId,
601
+ threadViewKey: 0,
602
+ threadCnt: 1,
603
+ threads: /* @__PURE__ */ new Map([[initialThreadId, []]]),
604
+ threadMetadata: /* @__PURE__ */ new Map([
605
+ [
606
+ initialThreadId,
607
+ {
608
+ title: "New Chat",
609
+ status: "pending",
610
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
611
+ control: initThreadControl()
612
+ }
613
+ ]
614
+ ])
615
+ };
616
+ this.snapshot = this.buildSnapshot();
617
+ }
618
+ emit() {
619
+ for (const listener of this.listeners) {
620
+ listener();
621
+ }
622
+ }
623
+ resolveStateAction(updater, current) {
624
+ return typeof updater === "function" ? updater(current) : updater;
625
+ }
626
+ ensureThreadExists(threadId) {
627
+ if (!this.state.threadMetadata.has(threadId)) {
628
+ const nextMetadata = new Map(this.state.threadMetadata);
629
+ nextMetadata.set(threadId, {
630
+ title: "New Chat",
631
+ status: "regular",
632
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
633
+ control: initThreadControl()
634
+ });
635
+ this.state = __spreadProps(__spreadValues({}, this.state), { threadMetadata: nextMetadata });
636
+ }
637
+ if (!this.state.threads.has(threadId)) {
638
+ const nextThreads = new Map(this.state.threads);
639
+ nextThreads.set(threadId, []);
640
+ this.state = __spreadProps(__spreadValues({}, this.state), { threads: nextThreads });
641
+ }
642
+ }
643
+ updateState(partial) {
644
+ this.state = __spreadValues(__spreadValues({}, this.state), partial);
645
+ this.snapshot = this.buildSnapshot();
646
+ this.emit();
647
+ }
648
+ buildSnapshot() {
649
+ return {
650
+ currentThreadId: this.state.currentThreadId,
651
+ setCurrentThreadId: this.setCurrentThreadId,
652
+ threadViewKey: this.state.threadViewKey,
653
+ bumpThreadViewKey: this.bumpThreadViewKey,
654
+ allThreads: this.state.threads,
655
+ setThreads: this.setThreads,
656
+ allThreadsMetadata: this.state.threadMetadata,
657
+ setThreadMetadata: this.setThreadMetadata,
658
+ threadCnt: this.state.threadCnt,
659
+ setThreadCnt: this.setThreadCnt,
660
+ getThreadMessages: this.getThreadMessages,
661
+ setThreadMessages: this.setThreadMessages,
662
+ getThreadMetadata: this.getThreadMetadata,
663
+ updateThreadMetadata: this.updateThreadMetadata
664
+ };
665
+ }
666
+ };
667
+
668
+ // packages/react/src/contexts/control-context.tsx
669
+ var import_jsx_runtime = require("react/jsx-runtime");
670
+ var API_KEY_STORAGE_KEY = "aomi_api_key";
671
+ var ControlContext = (0, import_react.createContext)(null);
672
+ function useControl() {
673
+ const ctx = (0, import_react.useContext)(ControlContext);
674
+ if (!ctx) {
675
+ throw new Error("useControl must be used within ControlContextProvider");
676
+ }
677
+ return ctx;
678
+ }
679
+ function ControlContextProvider({
680
+ children,
681
+ backendApi,
682
+ sessionId,
683
+ publicKey,
684
+ getThreadMetadata,
685
+ updateThreadMetadata
686
+ }) {
687
+ var _a, _b;
688
+ const [state, setStateInternal] = (0, import_react.useState)(() => ({
689
+ apiKey: null,
690
+ availableModels: [],
691
+ authorizedNamespaces: [],
692
+ defaultModel: null,
693
+ defaultNamespace: null
694
+ }));
695
+ const stateRef = (0, import_react.useRef)(state);
696
+ stateRef.current = state;
697
+ const backendApiRef = (0, import_react.useRef)(backendApi);
698
+ backendApiRef.current = backendApi;
699
+ const sessionIdRef = (0, import_react.useRef)(sessionId);
700
+ sessionIdRef.current = sessionId;
701
+ const publicKeyRef = (0, import_react.useRef)(publicKey);
702
+ publicKeyRef.current = publicKey;
703
+ const getThreadMetadataRef = (0, import_react.useRef)(getThreadMetadata);
704
+ getThreadMetadataRef.current = getThreadMetadata;
705
+ const updateThreadMetadataRef = (0, import_react.useRef)(updateThreadMetadata);
706
+ updateThreadMetadataRef.current = updateThreadMetadata;
707
+ const callbacks = (0, import_react.useRef)(/* @__PURE__ */ new Set());
708
+ const currentThreadMetadata = getThreadMetadata(sessionId);
709
+ const isProcessing = (_b = (_a = currentThreadMetadata == null ? void 0 : currentThreadMetadata.control) == null ? void 0 : _a.isProcessing) != null ? _b : false;
710
+ (0, import_react.useEffect)(() => {
711
+ var _a2, _b2;
712
+ try {
713
+ const storedApiKey = (_b2 = (_a2 = globalThis.localStorage) == null ? void 0 : _a2.getItem(API_KEY_STORAGE_KEY)) != null ? _b2 : null;
714
+ if (storedApiKey) {
715
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), { apiKey: storedApiKey }));
716
+ }
717
+ } catch (e) {
718
+ }
719
+ }, []);
720
+ (0, import_react.useEffect)(() => {
721
+ var _a2, _b2;
722
+ try {
723
+ if (state.apiKey) {
724
+ (_a2 = globalThis.localStorage) == null ? void 0 : _a2.setItem(API_KEY_STORAGE_KEY, state.apiKey);
725
+ } else {
726
+ (_b2 = globalThis.localStorage) == null ? void 0 : _b2.removeItem(API_KEY_STORAGE_KEY);
727
+ }
728
+ } catch (e) {
729
+ }
730
+ }, [state.apiKey]);
731
+ (0, import_react.useEffect)(() => {
732
+ const fetchNamespaces = async () => {
733
+ var _a2, _b2;
734
+ try {
735
+ const namespaces = await backendApiRef.current.getNamespaces(
736
+ sessionIdRef.current,
737
+ publicKeyRef.current,
738
+ (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
739
+ );
740
+ const defaultNs = namespaces.includes("default") ? "default" : (_b2 = namespaces[0]) != null ? _b2 : null;
741
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
742
+ authorizedNamespaces: namespaces,
743
+ defaultNamespace: defaultNs
744
+ }));
745
+ } catch (error) {
746
+ console.error("Failed to fetch namespaces:", error);
747
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
748
+ authorizedNamespaces: ["default"],
749
+ defaultNamespace: "default"
750
+ }));
751
+ }
752
+ };
753
+ void fetchNamespaces();
754
+ }, [state.apiKey]);
755
+ (0, import_react.useEffect)(() => {
756
+ const fetchModels = async () => {
757
+ try {
758
+ const models = await backendApiRef.current.getModels(
759
+ sessionIdRef.current
760
+ );
761
+ setStateInternal((prev) => {
762
+ var _a2;
763
+ return __spreadProps(__spreadValues({}, prev), {
764
+ availableModels: models,
765
+ defaultModel: (_a2 = models[0]) != null ? _a2 : null
766
+ });
767
+ });
768
+ } catch (error) {
769
+ console.error("Failed to fetch models:", error);
770
+ }
771
+ };
772
+ void fetchModels();
773
+ }, []);
774
+ const setApiKey = (0, import_react.useCallback)((apiKey) => {
775
+ setStateInternal((prev) => {
776
+ const next = __spreadProps(__spreadValues({}, prev), { apiKey: apiKey === "" ? null : apiKey });
777
+ callbacks.current.forEach((cb) => cb(next));
778
+ return next;
779
+ });
780
+ }, []);
781
+ const getAvailableModels = (0, import_react.useCallback)(async () => {
782
+ try {
783
+ const models = await backendApiRef.current.getModels(
784
+ sessionIdRef.current
785
+ );
786
+ setStateInternal((prev) => {
787
+ var _a2, _b2;
788
+ return __spreadProps(__spreadValues({}, prev), {
789
+ availableModels: models,
790
+ defaultModel: (_b2 = (_a2 = prev.defaultModel) != null ? _a2 : models[0]) != null ? _b2 : null
791
+ });
792
+ });
793
+ return models;
794
+ } catch (error) {
795
+ console.error("Failed to fetch models:", error);
796
+ return [];
797
+ }
798
+ }, []);
799
+ const getAuthorizedNamespaces = (0, import_react.useCallback)(async () => {
800
+ var _a2, _b2;
801
+ try {
802
+ const namespaces = await backendApiRef.current.getNamespaces(
803
+ sessionIdRef.current,
804
+ publicKeyRef.current,
805
+ (_a2 = stateRef.current.apiKey) != null ? _a2 : void 0
806
+ );
807
+ const defaultNs = namespaces.includes("default") ? "default" : (_b2 = namespaces[0]) != null ? _b2 : null;
808
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
809
+ authorizedNamespaces: namespaces,
810
+ defaultNamespace: defaultNs
811
+ }));
812
+ return namespaces;
813
+ } catch (error) {
814
+ console.error("Failed to fetch namespaces:", error);
815
+ setStateInternal((prev) => __spreadProps(__spreadValues({}, prev), {
816
+ authorizedNamespaces: ["default"],
817
+ defaultNamespace: "default"
818
+ }));
819
+ return ["default"];
820
+ }
821
+ }, []);
822
+ const getCurrentThreadControl = (0, import_react.useCallback)(() => {
823
+ var _a2;
824
+ const metadata = getThreadMetadataRef.current(sessionIdRef.current);
825
+ return (_a2 = metadata == null ? void 0 : metadata.control) != null ? _a2 : initThreadControl();
826
+ }, []);
827
+ const onModelSelect = (0, import_react.useCallback)(async (model) => {
828
+ var _a2, _b2, _c, _d, _e;
829
+ const threadId = sessionIdRef.current;
830
+ const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
831
+ const isProcessing2 = currentControl.isProcessing;
832
+ console.log("[control-context] onModelSelect called", {
833
+ model,
834
+ isProcessing: isProcessing2,
835
+ threadId
836
+ });
837
+ if (isProcessing2) {
838
+ console.warn("[control-context] Cannot switch model while processing");
839
+ return;
840
+ }
841
+ const namespace = (_d = (_c = currentControl.namespace) != null ? _c : stateRef.current.defaultNamespace) != null ? _d : "default";
842
+ console.log("[control-context] onModelSelect updating metadata", {
843
+ threadId,
844
+ model,
845
+ namespace,
846
+ currentControl
847
+ });
848
+ updateThreadMetadataRef.current(threadId, {
849
+ control: __spreadProps(__spreadValues({}, currentControl), {
850
+ model,
851
+ namespace,
852
+ controlDirty: true
853
+ })
854
+ });
855
+ console.log("[control-context] onModelSelect calling backend setModel", {
856
+ threadId,
857
+ model,
858
+ namespace,
859
+ backendUrl: backendApiRef.current
860
+ });
861
+ try {
862
+ const result = await backendApiRef.current.setModel(
863
+ threadId,
864
+ model,
865
+ namespace,
866
+ (_e = stateRef.current.apiKey) != null ? _e : void 0
867
+ );
868
+ console.log("[control-context] onModelSelect backend result", result);
869
+ } catch (err) {
870
+ console.error("[control-context] setModel failed:", err);
871
+ throw err;
872
+ }
873
+ }, []);
874
+ const onNamespaceSelect = (0, import_react.useCallback)((namespace) => {
875
+ var _a2, _b2;
876
+ const threadId = sessionIdRef.current;
877
+ const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
878
+ const isProcessing2 = currentControl.isProcessing;
879
+ console.log("[control-context] onNamespaceSelect called", {
880
+ namespace,
881
+ isProcessing: isProcessing2,
882
+ threadId
883
+ });
884
+ if (isProcessing2) {
885
+ console.warn(
886
+ "[control-context] Cannot switch namespace while processing"
887
+ );
888
+ return;
889
+ }
890
+ console.log("[control-context] onNamespaceSelect updating metadata", {
891
+ threadId,
892
+ namespace,
893
+ currentControl
894
+ });
895
+ updateThreadMetadataRef.current(threadId, {
896
+ control: __spreadProps(__spreadValues({}, currentControl), {
897
+ namespace,
898
+ controlDirty: true
899
+ })
900
+ });
901
+ console.log("[control-context] onNamespaceSelect metadata updated");
902
+ }, []);
903
+ const markControlSynced = (0, import_react.useCallback)(() => {
904
+ var _a2, _b2;
905
+ const threadId = sessionIdRef.current;
906
+ const currentControl = (_b2 = (_a2 = getThreadMetadataRef.current(threadId)) == null ? void 0 : _a2.control) != null ? _b2 : initThreadControl();
907
+ if (currentControl.controlDirty) {
908
+ updateThreadMetadataRef.current(threadId, {
909
+ control: __spreadProps(__spreadValues({}, currentControl), {
910
+ controlDirty: false
911
+ })
912
+ });
913
+ }
914
+ }, []);
915
+ const getControlState = (0, import_react.useCallback)(() => stateRef.current, []);
916
+ const onControlStateChange = (0, import_react.useCallback)(
917
+ (callback) => {
918
+ callbacks.current.add(callback);
919
+ return () => {
920
+ callbacks.current.delete(callback);
921
+ };
922
+ },
923
+ []
924
+ );
925
+ const setState = (0, import_react.useCallback)(
926
+ (updates) => {
927
+ var _a2;
928
+ if ("apiKey" in updates) {
929
+ setApiKey((_a2 = updates.apiKey) != null ? _a2 : null);
930
+ }
931
+ if ("namespace" in updates && updates.namespace !== void 0 && updates.namespace !== null) {
932
+ onNamespaceSelect(updates.namespace);
933
+ }
934
+ },
935
+ [setApiKey, onNamespaceSelect]
936
+ );
937
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
938
+ ControlContext.Provider,
939
+ {
940
+ value: {
941
+ state,
942
+ setApiKey,
943
+ getAvailableModels,
944
+ getAuthorizedNamespaces,
945
+ getCurrentThreadControl,
946
+ onModelSelect,
947
+ onNamespaceSelect,
948
+ isProcessing,
949
+ markControlSynced,
950
+ getControlState,
951
+ onControlStateChange,
952
+ setState
953
+ },
954
+ children
955
+ }
956
+ );
957
+ }
958
+
959
+ // packages/react/src/contexts/event-context.tsx
960
+ var import_react2 = require("react");
961
+
962
+ // packages/react/src/backend/types.ts
434
963
  function isInlineCall(event) {
435
964
  return "InlineCall" in event;
436
965
  }
@@ -444,7 +973,7 @@ function isAsyncCallback(event) {
444
973
  return "AsyncCallback" in event;
445
974
  }
446
975
 
447
- // src/state/event-buffer.ts
976
+ // packages/react/src/state/event-buffer.ts
448
977
  function createEventBuffer() {
449
978
  return {
450
979
  inboundQueue: [],
@@ -488,11 +1017,11 @@ function setSSEStatus(state, status) {
488
1017
  state.sseStatus = status;
489
1018
  }
490
1019
 
491
- // src/contexts/event-context.tsx
492
- var import_jsx_runtime = require("react/jsx-runtime");
493
- var EventContextState = (0, import_react.createContext)(null);
1020
+ // packages/react/src/contexts/event-context.tsx
1021
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1022
+ var EventContextState = (0, import_react2.createContext)(null);
494
1023
  function useEventContext() {
495
- const context = (0, import_react.useContext)(EventContextState);
1024
+ const context = (0, import_react2.useContext)(EventContextState);
496
1025
  if (!context) {
497
1026
  throw new Error(
498
1027
  "useEventContext must be used within EventContextProvider. Wrap your app with <EventContextProvider>...</EventContextProvider>"
@@ -505,13 +1034,13 @@ function EventContextProvider({
505
1034
  backendApi,
506
1035
  sessionId
507
1036
  }) {
508
- const bufferRef = (0, import_react.useRef)(null);
1037
+ const bufferRef = (0, import_react2.useRef)(null);
509
1038
  if (!bufferRef.current) {
510
1039
  bufferRef.current = createEventBuffer();
511
1040
  }
512
1041
  const buffer = bufferRef.current;
513
- const [sseStatus, setSseStatus] = (0, import_react.useState)("disconnected");
514
- (0, import_react.useEffect)(() => {
1042
+ const [sseStatus, setSseStatus] = (0, import_react2.useState)("disconnected");
1043
+ (0, import_react2.useEffect)(() => {
515
1044
  setSSEStatus(buffer, "connecting");
516
1045
  setSseStatus("connecting");
517
1046
  const unsubscribe = backendApi.subscribeSSE(
@@ -545,13 +1074,13 @@ function EventContextProvider({
545
1074
  setSseStatus("disconnected");
546
1075
  };
547
1076
  }, [backendApi, sessionId, buffer]);
548
- const subscribeCallback = (0, import_react.useCallback)(
1077
+ const subscribeCallback = (0, import_react2.useCallback)(
549
1078
  (type, callback) => {
550
1079
  return subscribe(buffer, type, callback);
551
1080
  },
552
1081
  [buffer]
553
1082
  );
554
- const sendOutbound = (0, import_react.useCallback)(
1083
+ const sendOutbound = (0, import_react2.useCallback)(
555
1084
  async (event) => {
556
1085
  try {
557
1086
  const message = JSON.stringify({
@@ -565,7 +1094,7 @@ function EventContextProvider({
565
1094
  },
566
1095
  [backendApi]
567
1096
  );
568
- const dispatchSystemEvents = (0, import_react.useCallback)(
1097
+ const dispatchSystemEvents = (0, import_react2.useCallback)(
569
1098
  (sessionId2, events) => {
570
1099
  var _a;
571
1100
  for (const event of events) {
@@ -610,15 +1139,15 @@ function EventContextProvider({
610
1139
  dispatchInboundSystem: dispatchSystemEvents,
611
1140
  sseStatus
612
1141
  };
613
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventContextState.Provider, { value: contextValue, children });
1142
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EventContextState.Provider, { value: contextValue, children });
614
1143
  }
615
1144
 
616
- // src/contexts/notification-context.tsx
617
- var import_react2 = require("react");
618
- var import_jsx_runtime2 = require("react/jsx-runtime");
619
- var NotificationContext = (0, import_react2.createContext)(null);
1145
+ // packages/react/src/contexts/notification-context.tsx
1146
+ var import_react3 = require("react");
1147
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1148
+ var NotificationContext = (0, import_react3.createContext)(null);
620
1149
  function useNotification() {
621
- const context = (0, import_react2.useContext)(NotificationContext);
1150
+ const context = (0, import_react3.useContext)(NotificationContext);
622
1151
  if (!context) {
623
1152
  throw new Error(
624
1153
  "useNotification must be used within NotificationContextProvider"
@@ -633,8 +1162,8 @@ function generateId() {
633
1162
  function NotificationContextProvider({
634
1163
  children
635
1164
  }) {
636
- const [notifications, setNotifications] = (0, import_react2.useState)([]);
637
- const showNotification = (0, import_react2.useCallback)((params) => {
1165
+ const [notifications, setNotifications] = (0, import_react3.useState)([]);
1166
+ const showNotification = (0, import_react3.useCallback)((params) => {
638
1167
  const id = generateId();
639
1168
  const notification = __spreadProps(__spreadValues({}, params), {
640
1169
  id,
@@ -643,10 +1172,10 @@ function NotificationContextProvider({
643
1172
  setNotifications((prev) => [notification, ...prev]);
644
1173
  return id;
645
1174
  }, []);
646
- const dismissNotification = (0, import_react2.useCallback)((id) => {
1175
+ const dismissNotification = (0, import_react3.useCallback)((id) => {
647
1176
  setNotifications((prev) => prev.filter((n) => n.id !== id));
648
1177
  }, []);
649
- const clearAll = (0, import_react2.useCallback)(() => {
1178
+ const clearAll = (0, import_react3.useCallback)(() => {
650
1179
  setNotifications([]);
651
1180
  }, []);
652
1181
  const value = {
@@ -655,166 +1184,15 @@ function NotificationContextProvider({
655
1184
  dismissNotification,
656
1185
  clearAll
657
1186
  };
658
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(NotificationContext.Provider, { value, children });
1187
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(NotificationContext.Provider, { value, children });
659
1188
  }
660
1189
 
661
- // src/contexts/thread-context.tsx
662
- var import_react3 = require("react");
663
-
664
- // src/state/thread-store.ts
665
- var shouldLogThreadUpdates = process.env.NODE_ENV !== "production";
666
- var logThreadMetadataChange = (source, threadId, prev, next) => {
667
- if (!shouldLogThreadUpdates) return;
668
- if (!prev && !next) return;
669
- if (!prev || !next) {
670
- console.debug(`[aomi][thread:${source}]`, { threadId, prev, next });
671
- return;
672
- }
673
- if (prev.title !== next.title || prev.status !== next.status || prev.lastActiveAt !== next.lastActiveAt) {
674
- console.debug(`[aomi][thread:${source}]`, { threadId, prev, next });
675
- }
676
- };
677
- var ThreadStore = class {
678
- constructor(options) {
679
- this.listeners = /* @__PURE__ */ new Set();
680
- this.subscribe = (listener) => {
681
- this.listeners.add(listener);
682
- return () => {
683
- this.listeners.delete(listener);
684
- };
685
- };
686
- this.getSnapshot = () => this.snapshot;
687
- this.setCurrentThreadId = (threadId) => {
688
- this.ensureThreadExists(threadId);
689
- this.updateState({ currentThreadId: threadId });
690
- };
691
- this.bumpThreadViewKey = () => {
692
- this.updateState({ threadViewKey: this.state.threadViewKey + 1 });
693
- };
694
- this.setThreadCnt = (updater) => {
695
- const nextCnt = this.resolveStateAction(updater, this.state.threadCnt);
696
- this.updateState({ threadCnt: nextCnt });
697
- };
698
- this.setThreads = (updater) => {
699
- const nextThreads = this.resolveStateAction(updater, this.state.threads);
700
- this.updateState({ threads: new Map(nextThreads) });
701
- };
702
- this.setThreadMetadata = (updater) => {
703
- const prevMetadata = this.state.threadMetadata;
704
- const nextMetadata = this.resolveStateAction(updater, prevMetadata);
705
- for (const [threadId, next] of nextMetadata.entries()) {
706
- logThreadMetadataChange(
707
- "setThreadMetadata",
708
- threadId,
709
- prevMetadata.get(threadId),
710
- next
711
- );
712
- }
713
- for (const [threadId, prev] of prevMetadata.entries()) {
714
- if (!nextMetadata.has(threadId)) {
715
- logThreadMetadataChange("setThreadMetadata", threadId, prev, void 0);
716
- }
717
- }
718
- this.updateState({ threadMetadata: new Map(nextMetadata) });
719
- };
720
- this.setThreadMessages = (threadId, messages) => {
721
- this.ensureThreadExists(threadId);
722
- const nextThreads = new Map(this.state.threads);
723
- nextThreads.set(threadId, messages);
724
- this.updateState({ threads: nextThreads });
725
- };
726
- this.getThreadMessages = (threadId) => {
727
- var _a;
728
- return (_a = this.state.threads.get(threadId)) != null ? _a : [];
729
- };
730
- this.getThreadMetadata = (threadId) => {
731
- return this.state.threadMetadata.get(threadId);
732
- };
733
- this.updateThreadMetadata = (threadId, updates) => {
734
- const existing = this.state.threadMetadata.get(threadId);
735
- if (!existing) {
736
- return;
737
- }
738
- const next = __spreadValues(__spreadValues({}, existing), updates);
739
- const nextMetadata = new Map(this.state.threadMetadata);
740
- nextMetadata.set(threadId, next);
741
- logThreadMetadataChange("updateThreadMetadata", threadId, existing, next);
742
- this.updateState({ threadMetadata: nextMetadata });
743
- };
744
- var _a;
745
- const initialThreadId = (_a = options == null ? void 0 : options.initialThreadId) != null ? _a : crypto.randomUUID();
746
- this.state = {
747
- currentThreadId: initialThreadId,
748
- threadViewKey: 0,
749
- threadCnt: 1,
750
- threads: /* @__PURE__ */ new Map([[initialThreadId, []]]),
751
- threadMetadata: /* @__PURE__ */ new Map([
752
- [
753
- initialThreadId,
754
- {
755
- title: "New Chat",
756
- status: "pending",
757
- lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
758
- }
759
- ]
760
- ])
761
- };
762
- this.snapshot = this.buildSnapshot();
763
- }
764
- emit() {
765
- for (const listener of this.listeners) {
766
- listener();
767
- }
768
- }
769
- resolveStateAction(updater, current) {
770
- return typeof updater === "function" ? updater(current) : updater;
771
- }
772
- ensureThreadExists(threadId) {
773
- if (!this.state.threadMetadata.has(threadId)) {
774
- const nextMetadata = new Map(this.state.threadMetadata);
775
- nextMetadata.set(threadId, {
776
- title: "New Chat",
777
- status: "regular",
778
- lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
779
- });
780
- this.state = __spreadProps(__spreadValues({}, this.state), { threadMetadata: nextMetadata });
781
- }
782
- if (!this.state.threads.has(threadId)) {
783
- const nextThreads = new Map(this.state.threads);
784
- nextThreads.set(threadId, []);
785
- this.state = __spreadProps(__spreadValues({}, this.state), { threads: nextThreads });
786
- }
787
- }
788
- updateState(partial) {
789
- this.state = __spreadValues(__spreadValues({}, this.state), partial);
790
- this.snapshot = this.buildSnapshot();
791
- this.emit();
792
- }
793
- buildSnapshot() {
794
- return {
795
- currentThreadId: this.state.currentThreadId,
796
- setCurrentThreadId: this.setCurrentThreadId,
797
- threadViewKey: this.state.threadViewKey,
798
- bumpThreadViewKey: this.bumpThreadViewKey,
799
- allThreads: this.state.threads,
800
- setThreads: this.setThreads,
801
- allThreadsMetadata: this.state.threadMetadata,
802
- setThreadMetadata: this.setThreadMetadata,
803
- threadCnt: this.state.threadCnt,
804
- setThreadCnt: this.setThreadCnt,
805
- getThreadMessages: this.getThreadMessages,
806
- setThreadMessages: this.setThreadMessages,
807
- getThreadMetadata: this.getThreadMetadata,
808
- updateThreadMetadata: this.updateThreadMetadata
809
- };
810
- }
811
- };
812
-
813
- // src/contexts/thread-context.tsx
814
- var import_jsx_runtime3 = require("react/jsx-runtime");
815
- var ThreadContextState = (0, import_react3.createContext)(null);
1190
+ // packages/react/src/contexts/thread-context.tsx
1191
+ var import_react4 = require("react");
1192
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1193
+ var ThreadContextState = (0, import_react4.createContext)(null);
816
1194
  function useThreadContext() {
817
- const context = (0, import_react3.useContext)(ThreadContextState);
1195
+ const context = (0, import_react4.useContext)(ThreadContextState);
818
1196
  if (!context) {
819
1197
  throw new Error(
820
1198
  "useThreadContext must be used within ThreadContextProvider. Wrap your app with <ThreadContextProvider>...</ThreadContextProvider>"
@@ -826,39 +1204,39 @@ function ThreadContextProvider({
826
1204
  children,
827
1205
  initialThreadId
828
1206
  }) {
829
- const storeRef = (0, import_react3.useRef)(null);
1207
+ const storeRef = (0, import_react4.useRef)(null);
830
1208
  if (!storeRef.current) {
831
1209
  storeRef.current = new ThreadStore({ initialThreadId });
832
1210
  }
833
1211
  const store = storeRef.current;
834
- const value = (0, import_react3.useSyncExternalStore)(
1212
+ const value = (0, import_react4.useSyncExternalStore)(
835
1213
  store.subscribe,
836
1214
  store.getSnapshot,
837
1215
  store.getSnapshot
838
1216
  );
839
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ThreadContextState.Provider, { value, children });
1217
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThreadContextState.Provider, { value, children });
840
1218
  }
841
1219
  function useCurrentThreadMessages() {
842
1220
  const { currentThreadId, getThreadMessages } = useThreadContext();
843
- return (0, import_react3.useMemo)(
1221
+ return (0, import_react4.useMemo)(
844
1222
  () => getThreadMessages(currentThreadId),
845
1223
  [currentThreadId, getThreadMessages]
846
1224
  );
847
1225
  }
848
1226
  function useCurrentThreadMetadata() {
849
1227
  const { currentThreadId, getThreadMetadata } = useThreadContext();
850
- return (0, import_react3.useMemo)(
1228
+ return (0, import_react4.useMemo)(
851
1229
  () => getThreadMetadata(currentThreadId),
852
1230
  [currentThreadId, getThreadMetadata]
853
1231
  );
854
1232
  }
855
1233
 
856
- // src/contexts/user-context.tsx
857
- var import_react4 = require("react");
858
- var import_jsx_runtime4 = require("react/jsx-runtime");
859
- var UserContext = (0, import_react4.createContext)(void 0);
1234
+ // packages/react/src/contexts/user-context.tsx
1235
+ var import_react5 = require("react");
1236
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1237
+ var UserContext = (0, import_react5.createContext)(void 0);
860
1238
  function useUser() {
861
- const context = (0, import_react4.useContext)(UserContext);
1239
+ const context = (0, import_react5.useContext)(UserContext);
862
1240
  if (!context) {
863
1241
  throw new Error("useUser must be used within UserContextProvider");
864
1242
  }
@@ -870,18 +1248,18 @@ function useUser() {
870
1248
  };
871
1249
  }
872
1250
  function UserContextProvider({ children }) {
873
- const [user, setUserState] = (0, import_react4.useState)({
1251
+ const [user, setUserState] = (0, import_react5.useState)({
874
1252
  isConnected: false,
875
1253
  address: void 0,
876
1254
  chainId: void 0,
877
1255
  ensName: void 0
878
1256
  });
879
- const userRef = (0, import_react4.useRef)(user);
1257
+ const userRef = (0, import_react5.useRef)(user);
880
1258
  userRef.current = user;
881
- const StateChangeCallbacks = (0, import_react4.useRef)(
1259
+ const StateChangeCallbacks = (0, import_react5.useRef)(
882
1260
  /* @__PURE__ */ new Set()
883
1261
  );
884
- const setUser = (0, import_react4.useCallback)((data) => {
1262
+ const setUser = (0, import_react5.useCallback)((data) => {
885
1263
  setUserState((prev) => {
886
1264
  const next = __spreadValues(__spreadValues({}, prev), data);
887
1265
  StateChangeCallbacks.current.forEach((callback) => {
@@ -890,8 +1268,8 @@ function UserContextProvider({ children }) {
890
1268
  return next;
891
1269
  });
892
1270
  }, []);
893
- const getUserState = (0, import_react4.useCallback)(() => userRef.current, []);
894
- const onUserStateChange = (0, import_react4.useCallback)(
1271
+ const getUserState = (0, import_react5.useCallback)(() => userRef.current, []);
1272
+ const onUserStateChange = (0, import_react5.useCallback)(
895
1273
  (callback) => {
896
1274
  StateChangeCallbacks.current.add(callback);
897
1275
  return () => {
@@ -900,7 +1278,7 @@ function UserContextProvider({ children }) {
900
1278
  },
901
1279
  []
902
1280
  );
903
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1281
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
904
1282
  UserContext.Provider,
905
1283
  {
906
1284
  value: {
@@ -914,14 +1292,14 @@ function UserContextProvider({ children }) {
914
1292
  );
915
1293
  }
916
1294
 
917
- // src/runtime/core.tsx
918
- var import_react7 = require("react");
919
- var import_react8 = require("@assistant-ui/react");
1295
+ // packages/react/src/runtime/core.tsx
1296
+ var import_react8 = require("react");
1297
+ var import_react9 = require("@assistant-ui/react");
920
1298
 
921
- // src/runtime/orchestrator.ts
922
- var import_react5 = require("react");
1299
+ // packages/react/src/runtime/orchestrator.ts
1300
+ var import_react6 = require("react");
923
1301
 
924
- // src/runtime/utils.ts
1302
+ // packages/react/src/runtime/utils.ts
925
1303
  var import_clsx = require("clsx");
926
1304
  var import_tailwind_merge = require("tailwind-merge");
927
1305
  function cn(...inputs) {
@@ -1014,7 +1392,7 @@ var getNetworkName = (chainId) => {
1014
1392
  };
1015
1393
  var formatAddress = (addr) => addr ? `${addr.slice(0, 6)}...${addr.slice(-4)}` : "Connect Wallet";
1016
1394
 
1017
- // src/state/backend-state.ts
1395
+ // packages/react/src/state/backend-state.ts
1018
1396
  function createBackendState() {
1019
1397
  return {
1020
1398
  skipInitialFetch: /* @__PURE__ */ new Set(),
@@ -1065,7 +1443,7 @@ function hasPendingChat(state, threadId) {
1065
1443
  return ((_b = (_a = state.pendingChat.get(threadId)) == null ? void 0 : _a.length) != null ? _b : 0) > 0;
1066
1444
  }
1067
1445
 
1068
- // src/runtime/message-controller.ts
1446
+ // packages/react/src/runtime/message-controller.ts
1069
1447
  var MessageController = class {
1070
1448
  constructor(config) {
1071
1449
  this.config = config;
@@ -1086,7 +1464,7 @@ var MessageController = class {
1086
1464
  this.getThreadContextApi().setThreadMessages(threadId, threadMessages);
1087
1465
  }
1088
1466
  async outbound(message, threadId) {
1089
- var _a, _b, _c;
1467
+ var _a, _b, _c, _d, _e, _f;
1090
1468
  const backendState = this.config.backendStateRef.current;
1091
1469
  const text = message.content.filter(
1092
1470
  (part) => part.type === "text"
@@ -1111,21 +1489,22 @@ var MessageController = class {
1111
1489
  return;
1112
1490
  }
1113
1491
  const backendThreadId = resolveThreadId(backendState, threadId);
1492
+ const namespace = this.config.getNamespace();
1114
1493
  const publicKey = (_b = (_a = this.config).getPublicKey) == null ? void 0 : _b.call(_a);
1494
+ const apiKey = (_e = (_d = (_c = this.config).getApiKey) == null ? void 0 : _d.call(_c)) != null ? _e : void 0;
1115
1495
  try {
1116
1496
  this.markRunning(threadId, true);
1117
- const response = publicKey ? await this.config.backendApiRef.current.postChatMessage(
1497
+ const response = await this.config.backendApiRef.current.postChatMessage(
1118
1498
  backendThreadId,
1119
1499
  text,
1120
- publicKey
1121
- ) : await this.config.backendApiRef.current.postChatMessage(
1122
- backendThreadId,
1123
- text
1500
+ namespace,
1501
+ publicKey,
1502
+ apiKey
1124
1503
  );
1125
1504
  if (response == null ? void 0 : response.messages) {
1126
1505
  this.inbound(threadId, response.messages);
1127
1506
  }
1128
- if (((_c = response == null ? void 0 : response.system_events) == null ? void 0 : _c.length) && this.config.onSyncEvents) {
1507
+ if (((_f = response == null ? void 0 : response.system_events) == null ? void 0 : _f.length) && this.config.onSyncEvents) {
1129
1508
  this.config.onSyncEvents(backendThreadId, response.system_events);
1130
1509
  }
1131
1510
  if (response == null ? void 0 : response.is_processing) {
@@ -1139,26 +1518,23 @@ var MessageController = class {
1139
1518
  }
1140
1519
  }
1141
1520
  async flushPendingChat(threadId) {
1142
- var _a, _b;
1521
+ var _a, _b, _c, _d, _e;
1143
1522
  const backendState = this.config.backendStateRef.current;
1144
1523
  const pending = dequeuePendingChat(backendState, threadId);
1145
1524
  if (!pending.length) return;
1146
1525
  const backendThreadId = resolveThreadId(backendState, threadId);
1526
+ const namespace = this.config.getNamespace();
1147
1527
  const publicKey = (_b = (_a = this.config).getPublicKey) == null ? void 0 : _b.call(_a);
1528
+ const apiKey = (_e = (_d = (_c = this.config).getApiKey) == null ? void 0 : _d.call(_c)) != null ? _e : void 0;
1148
1529
  for (const text of pending) {
1149
1530
  try {
1150
- if (publicKey) {
1151
- await this.config.backendApiRef.current.postChatMessage(
1152
- backendThreadId,
1153
- text,
1154
- publicKey
1155
- );
1156
- } else {
1157
- await this.config.backendApiRef.current.postChatMessage(
1158
- backendThreadId,
1159
- text
1160
- );
1161
- }
1531
+ await this.config.backendApiRef.current.postChatMessage(
1532
+ backendThreadId,
1533
+ text,
1534
+ namespace,
1535
+ publicKey,
1536
+ apiKey
1537
+ );
1162
1538
  } catch (error) {
1163
1539
  console.error("Failed to send queued message:", error);
1164
1540
  }
@@ -1197,7 +1573,7 @@ var MessageController = class {
1197
1573
  }
1198
1574
  };
1199
1575
 
1200
- // src/runtime/polling-controller.ts
1576
+ // packages/react/src/runtime/polling-controller.ts
1201
1577
  var PollingController = class {
1202
1578
  constructor(config) {
1203
1579
  this.config = config;
@@ -1268,18 +1644,18 @@ var PollingController = class {
1268
1644
  }
1269
1645
  };
1270
1646
 
1271
- // src/runtime/orchestrator.ts
1647
+ // packages/react/src/runtime/orchestrator.ts
1272
1648
  function useRuntimeOrchestrator(backendApi, options) {
1273
1649
  const threadContext = useThreadContext();
1274
- const threadContextRef = (0, import_react5.useRef)(threadContext);
1650
+ const threadContextRef = (0, import_react6.useRef)(threadContext);
1275
1651
  threadContextRef.current = threadContext;
1276
- const backendApiRef = (0, import_react5.useRef)(backendApi);
1652
+ const backendApiRef = (0, import_react6.useRef)(backendApi);
1277
1653
  backendApiRef.current = backendApi;
1278
- const backendStateRef = (0, import_react5.useRef)(createBackendState());
1279
- const [isRunning, setIsRunning] = (0, import_react5.useState)(false);
1280
- const messageControllerRef = (0, import_react5.useRef)(null);
1281
- const pollingRef = (0, import_react5.useRef)(null);
1282
- const pendingFetches = (0, import_react5.useRef)(/* @__PURE__ */ new Set());
1654
+ const backendStateRef = (0, import_react6.useRef)(createBackendState());
1655
+ const [isRunning, setIsRunning] = (0, import_react6.useState)(false);
1656
+ const messageControllerRef = (0, import_react6.useRef)(null);
1657
+ const pollingRef = (0, import_react6.useRef)(null);
1658
+ const pendingFetches = (0, import_react6.useRef)(/* @__PURE__ */ new Set());
1283
1659
  if (!pollingRef.current) {
1284
1660
  pollingRef.current = new PollingController({
1285
1661
  backendApiRef,
@@ -1288,8 +1664,8 @@ function useRuntimeOrchestrator(backendApi, options) {
1288
1664
  var _a;
1289
1665
  (_a = messageControllerRef.current) == null ? void 0 : _a.inbound(threadId, msgs);
1290
1666
  },
1291
- onSyncEvents: options == null ? void 0 : options.onSyncEvents,
1292
- getUserState: options == null ? void 0 : options.getUserState,
1667
+ onSyncEvents: options.onSyncEvents,
1668
+ getUserState: options.getUserState,
1293
1669
  onStart: (threadId) => {
1294
1670
  if (threadContextRef.current.currentThreadId === threadId) {
1295
1671
  setIsRunning(true);
@@ -1309,11 +1685,13 @@ function useRuntimeOrchestrator(backendApi, options) {
1309
1685
  threadContextRef,
1310
1686
  polling: pollingRef.current,
1311
1687
  setGlobalIsRunning: setIsRunning,
1312
- getPublicKey: options == null ? void 0 : options.getPublicKey,
1313
- onSyncEvents: options == null ? void 0 : options.onSyncEvents
1688
+ getPublicKey: options.getPublicKey,
1689
+ getNamespace: options.getNamespace,
1690
+ getApiKey: options.getApiKey,
1691
+ onSyncEvents: options.onSyncEvents
1314
1692
  });
1315
1693
  }
1316
- const ensureInitialState = (0, import_react5.useCallback)(async (threadId) => {
1694
+ const ensureInitialState = (0, import_react6.useCallback)(async (threadId) => {
1317
1695
  var _a, _b, _c, _d;
1318
1696
  const backendState = backendStateRef.current;
1319
1697
  if (shouldSkipInitialFetch(backendState, threadId)) {
@@ -1333,13 +1711,13 @@ function useRuntimeOrchestrator(backendApi, options) {
1333
1711
  const backendThreadId = resolveThreadId(backendState, threadId);
1334
1712
  pendingFetches.current.add(threadId);
1335
1713
  try {
1336
- const userState = (_a = options == null ? void 0 : options.getUserState) == null ? void 0 : _a.call(options);
1714
+ const userState = (_a = options.getUserState) == null ? void 0 : _a.call(options);
1337
1715
  const state = await backendApiRef.current.fetchState(
1338
1716
  backendThreadId,
1339
1717
  userState
1340
1718
  );
1341
1719
  (_b = messageControllerRef.current) == null ? void 0 : _b.inbound(threadId, state.messages);
1342
- if (((_c = state.system_events) == null ? void 0 : _c.length) && (options == null ? void 0 : options.onSyncEvents)) {
1720
+ if (((_c = state.system_events) == null ? void 0 : _c.length) && options.onSyncEvents) {
1343
1721
  options.onSyncEvents(backendThreadId, state.system_events);
1344
1722
  }
1345
1723
  if (threadContextRef.current.currentThreadId === threadId) {
@@ -1370,7 +1748,7 @@ function useRuntimeOrchestrator(backendApi, options) {
1370
1748
  };
1371
1749
  }
1372
1750
 
1373
- // src/runtime/threadlist-adapter.ts
1751
+ // packages/react/src/runtime/threadlist-adapter.ts
1374
1752
  var sortByLastActiveDesc = ([, metaA], [, metaB]) => {
1375
1753
  const tsA = parseTimestamp(metaA.lastActiveAt);
1376
1754
  const tsB = parseTimestamp(metaB.lastActiveAt);
@@ -1403,7 +1781,9 @@ function buildThreadListAdapter({
1403
1781
  currentThreadIdRef,
1404
1782
  polling,
1405
1783
  userAddress,
1406
- setIsRunning
1784
+ setIsRunning,
1785
+ getNamespace,
1786
+ getApiKey
1407
1787
  }) {
1408
1788
  const backendState = backendStateRef.current;
1409
1789
  const { regularThreads, archivedThreads } = buildThreadLists(
@@ -1431,7 +1811,8 @@ function buildThreadListAdapter({
1431
1811
  (prev) => new Map(prev).set(threadId, {
1432
1812
  title: "New Chat",
1433
1813
  status: "pending",
1434
- lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
1814
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
1815
+ control: initThreadControl()
1435
1816
  })
1436
1817
  );
1437
1818
  threadContext.setThreadMessages(threadId, []);
@@ -1458,15 +1839,13 @@ function buildThreadListAdapter({
1458
1839
  return;
1459
1840
  }
1460
1841
  if (backendState.createThreadPromise) {
1461
- preparePendingThread(
1462
- (_a = backendState.creatingThreadId) != null ? _a : crypto.randomUUID()
1463
- );
1842
+ preparePendingThread((_a = backendState.creatingThreadId) != null ? _a : generateUUID());
1464
1843
  return;
1465
1844
  }
1466
- const threadId = crypto.randomUUID();
1845
+ const threadId = generateUUID();
1467
1846
  preparePendingThread(threadId);
1468
1847
  const createPromise = backendApiRef.current.createThread(threadId, userAddress).then(async (newThread) => {
1469
- var _a2;
1848
+ var _a2, _b;
1470
1849
  const uiThreadId = (_a2 = backendState.creatingThreadId) != null ? _a2 : threadId;
1471
1850
  const backendId = newThread.session_id;
1472
1851
  if (uiThreadId !== backendId) {
@@ -1477,14 +1856,15 @@ function buildThreadListAdapter({
1477
1856
  }
1478
1857
  markSkipInitialFetch(backendState, uiThreadId);
1479
1858
  threadContext.setThreadMetadata((prev) => {
1480
- var _a3, _b;
1859
+ var _a3, _b2, _c;
1481
1860
  const next = new Map(prev);
1482
1861
  const existing = next.get(uiThreadId);
1483
1862
  const nextStatus = (existing == null ? void 0 : existing.status) === "archived" ? "archived" : "regular";
1484
1863
  next.set(uiThreadId, {
1485
1864
  title: (_a3 = existing == null ? void 0 : existing.title) != null ? _a3 : "New Chat",
1486
1865
  status: nextStatus,
1487
- lastActiveAt: (_b = existing == null ? void 0 : existing.lastActiveAt) != null ? _b : (/* @__PURE__ */ new Date()).toISOString()
1866
+ lastActiveAt: (_b2 = existing == null ? void 0 : existing.lastActiveAt) != null ? _b2 : (/* @__PURE__ */ new Date()).toISOString(),
1867
+ control: (_c = existing == null ? void 0 : existing.control) != null ? _c : initThreadControl()
1488
1868
  });
1489
1869
  return next;
1490
1870
  });
@@ -1494,9 +1874,17 @@ function buildThreadListAdapter({
1494
1874
  const pendingMessages = backendState.pendingChat.get(uiThreadId);
1495
1875
  if (pendingMessages == null ? void 0 : pendingMessages.length) {
1496
1876
  backendState.pendingChat.delete(uiThreadId);
1877
+ const namespace = getNamespace();
1878
+ const apiKey = (_b = getApiKey == null ? void 0 : getApiKey()) != null ? _b : void 0;
1497
1879
  for (const text of pendingMessages) {
1498
1880
  try {
1499
- await backendApiRef.current.postChatMessage(backendId, text);
1881
+ await backendApiRef.current.postChatMessage(
1882
+ backendId,
1883
+ text,
1884
+ namespace,
1885
+ userAddress,
1886
+ apiKey
1887
+ );
1500
1888
  } catch (error) {
1501
1889
  console.error("Failed to send queued message:", error);
1502
1890
  }
@@ -1595,7 +1983,8 @@ function buildThreadListAdapter({
1595
1983
  (prev) => new Map(prev).set(defaultId, {
1596
1984
  title: "New Chat",
1597
1985
  status: "regular",
1598
- lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
1986
+ lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
1987
+ control: initThreadControl()
1599
1988
  })
1600
1989
  );
1601
1990
  threadContext.setThreadMessages(defaultId, []);
@@ -1610,12 +1999,12 @@ function buildThreadListAdapter({
1610
1999
  };
1611
2000
  }
1612
2001
 
1613
- // src/interface.tsx
1614
- var import_react6 = require("react");
1615
- var AomiRuntimeContext = (0, import_react6.createContext)(null);
2002
+ // packages/react/src/interface.tsx
2003
+ var import_react7 = require("react");
2004
+ var AomiRuntimeContext = (0, import_react7.createContext)(null);
1616
2005
  var AomiRuntimeApiProvider = AomiRuntimeContext.Provider;
1617
2006
  function useAomiRuntime() {
1618
- const context = (0, import_react6.useContext)(AomiRuntimeContext);
2007
+ const context = (0, import_react7.useContext)(AomiRuntimeContext);
1619
2008
  if (!context) {
1620
2009
  throw new Error(
1621
2010
  "useAomiRuntime must be used within AomiRuntimeProvider. Wrap your app with <AomiRuntimeProvider>...</AomiRuntimeProvider>"
@@ -1624,8 +2013,8 @@ function useAomiRuntime() {
1624
2013
  return context;
1625
2014
  }
1626
2015
 
1627
- // src/runtime/core.tsx
1628
- var import_jsx_runtime5 = require("react/jsx-runtime");
2016
+ // packages/react/src/runtime/core.tsx
2017
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1629
2018
  function AomiRuntimeCore({
1630
2019
  children,
1631
2020
  backendApi
@@ -1635,6 +2024,7 @@ function AomiRuntimeCore({
1635
2024
  const notificationContext = useNotification();
1636
2025
  const { dispatchInboundSystem: dispatchSystemEvents } = eventContext;
1637
2026
  const { user, onUserStateChange, getUserState } = useUser();
2027
+ const { getControlState, getCurrentThreadControl } = useControl();
1638
2028
  const {
1639
2029
  backendStateRef,
1640
2030
  polling,
@@ -1646,9 +2036,14 @@ function AomiRuntimeCore({
1646
2036
  } = useRuntimeOrchestrator(backendApi, {
1647
2037
  onSyncEvents: dispatchSystemEvents,
1648
2038
  getPublicKey: () => getUserState().address,
1649
- getUserState
2039
+ getUserState,
2040
+ getNamespace: () => {
2041
+ var _a, _b;
2042
+ return (_b = (_a = getCurrentThreadControl().namespace) != null ? _a : getControlState().defaultNamespace) != null ? _b : "default";
2043
+ },
2044
+ getApiKey: () => getControlState().apiKey
1650
2045
  });
1651
- (0, import_react7.useEffect)(() => {
2046
+ (0, import_react8.useEffect)(() => {
1652
2047
  const unsubscribe = onUserStateChange(async (newUser) => {
1653
2048
  const sessionId = threadContext.currentThreadId;
1654
2049
  const message = JSON.stringify({
@@ -1664,23 +2059,34 @@ function AomiRuntimeCore({
1664
2059
  });
1665
2060
  return unsubscribe;
1666
2061
  }, [onUserStateChange, backendApiRef, threadContext.currentThreadId]);
1667
- const threadContextRef = (0, import_react7.useRef)(threadContext);
2062
+ const threadContextRef = (0, import_react8.useRef)(threadContext);
1668
2063
  threadContextRef.current = threadContext;
1669
- const currentThreadIdRef = (0, import_react7.useRef)(threadContext.currentThreadId);
1670
- (0, import_react7.useEffect)(() => {
2064
+ const currentThreadIdRef = (0, import_react8.useRef)(threadContext.currentThreadId);
2065
+ (0, import_react8.useEffect)(() => {
1671
2066
  currentThreadIdRef.current = threadContext.currentThreadId;
1672
2067
  }, [threadContext.currentThreadId]);
1673
- (0, import_react7.useEffect)(() => {
2068
+ (0, import_react8.useEffect)(() => {
1674
2069
  void ensureInitialState(threadContext.currentThreadId);
1675
2070
  }, [ensureInitialState, threadContext.currentThreadId]);
1676
- (0, import_react7.useEffect)(() => {
2071
+ (0, import_react8.useEffect)(() => {
1677
2072
  const threadId = threadContext.currentThreadId;
1678
2073
  setIsRunning(isThreadRunning(backendStateRef.current, threadId));
1679
2074
  }, [backendStateRef, setIsRunning, threadContext.currentThreadId]);
2075
+ (0, import_react8.useEffect)(() => {
2076
+ const threadId = threadContext.currentThreadId;
2077
+ const currentMeta = threadContext.getThreadMetadata(threadId);
2078
+ if (currentMeta && currentMeta.control.isProcessing !== isRunning) {
2079
+ threadContext.updateThreadMetadata(threadId, {
2080
+ control: __spreadProps(__spreadValues({}, currentMeta.control), {
2081
+ isProcessing: isRunning
2082
+ })
2083
+ });
2084
+ }
2085
+ }, [isRunning, threadContext]);
1680
2086
  const currentMessages = threadContext.getThreadMessages(
1681
2087
  threadContext.currentThreadId
1682
2088
  );
1683
- const resolvedSessionId = (0, import_react7.useMemo)(
2089
+ const resolvedSessionId = (0, import_react8.useMemo)(
1684
2090
  () => resolveThreadId(backendStateRef.current, threadContext.currentThreadId),
1685
2091
  [
1686
2092
  backendStateRef,
@@ -1688,11 +2094,11 @@ function AomiRuntimeCore({
1688
2094
  threadContext.allThreadsMetadata
1689
2095
  ]
1690
2096
  );
1691
- (0, import_react7.useEffect)(() => {
2097
+ (0, import_react8.useEffect)(() => {
1692
2098
  const userAddress = user.address;
1693
2099
  if (!userAddress) return;
1694
2100
  const fetchThreadList = async () => {
1695
- var _a, _b;
2101
+ var _a, _b, _c;
1696
2102
  try {
1697
2103
  const threadList = await backendApiRef.current.fetchThreads(userAddress);
1698
2104
  const currentContext = threadContextRef.current;
@@ -1702,10 +2108,12 @@ function AomiRuntimeCore({
1702
2108
  const rawTitle = (_a = thread.title) != null ? _a : "";
1703
2109
  const title = isPlaceholderTitle(rawTitle) ? "" : rawTitle;
1704
2110
  const lastActive = ((_b = newMetadata.get(thread.session_id)) == null ? void 0 : _b.lastActiveAt) || (/* @__PURE__ */ new Date()).toISOString();
2111
+ const existingControl = (_c = newMetadata.get(thread.session_id)) == null ? void 0 : _c.control;
1705
2112
  newMetadata.set(thread.session_id, {
1706
2113
  title,
1707
2114
  status: thread.is_archived ? "archived" : "regular",
1708
- lastActiveAt: lastActive
2115
+ lastActiveAt: lastActive,
2116
+ control: existingControl != null ? existingControl : initThreadControl()
1709
2117
  });
1710
2118
  const match = title.match(/^Chat (\d+)$/);
1711
2119
  if (match) {
@@ -1725,7 +2133,7 @@ function AomiRuntimeCore({
1725
2133
  };
1726
2134
  void fetchThreadList();
1727
2135
  }, [user.address, backendApiRef]);
1728
- const threadListAdapter = (0, import_react7.useMemo)(
2136
+ const threadListAdapter = (0, import_react8.useMemo)(
1729
2137
  () => buildThreadListAdapter({
1730
2138
  backendStateRef,
1731
2139
  backendApiRef,
@@ -1733,7 +2141,12 @@ function AomiRuntimeCore({
1733
2141
  currentThreadIdRef,
1734
2142
  polling,
1735
2143
  userAddress: user.address,
1736
- setIsRunning
2144
+ setIsRunning,
2145
+ getNamespace: () => {
2146
+ var _a, _b;
2147
+ return (_b = (_a = getCurrentThreadControl().namespace) != null ? _a : getControlState().defaultNamespace) != null ? _b : "default";
2148
+ },
2149
+ getApiKey: () => getControlState().apiKey
1737
2150
  }),
1738
2151
  [
1739
2152
  backendApiRef,
@@ -1743,10 +2156,11 @@ function AomiRuntimeCore({
1743
2156
  setIsRunning,
1744
2157
  threadContext,
1745
2158
  threadContext.currentThreadId,
1746
- threadContext.allThreadsMetadata
2159
+ threadContext.allThreadsMetadata,
2160
+ getControlState
1747
2161
  ]
1748
2162
  );
1749
- (0, import_react7.useEffect)(() => {
2163
+ (0, import_react8.useEffect)(() => {
1750
2164
  const backendState = backendStateRef.current;
1751
2165
  const currentSessionId = threadContext.currentThreadId;
1752
2166
  if (process.env.NODE_ENV !== "production") {
@@ -1777,14 +2191,15 @@ function AomiRuntimeCore({
1777
2191
  });
1778
2192
  }
1779
2193
  threadContextRef.current.setThreadMetadata((prev) => {
1780
- var _a;
2194
+ var _a, _b;
1781
2195
  const next = new Map(prev);
1782
2196
  const existing = next.get(targetThreadId);
1783
2197
  const nextStatus = (existing == null ? void 0 : existing.status) === "archived" ? "archived" : "regular";
1784
2198
  next.set(targetThreadId, {
1785
2199
  title: normalizedTitle,
1786
2200
  status: nextStatus,
1787
- lastActiveAt: (_a = existing == null ? void 0 : existing.lastActiveAt) != null ? _a : (/* @__PURE__ */ new Date()).toISOString()
2201
+ lastActiveAt: (_a = existing == null ? void 0 : existing.lastActiveAt) != null ? _a : (/* @__PURE__ */ new Date()).toISOString(),
2202
+ control: (_b = existing == null ? void 0 : existing.control) != null ? _b : initThreadControl()
1788
2203
  });
1789
2204
  return next;
1790
2205
  });
@@ -1803,12 +2218,12 @@ function AomiRuntimeCore({
1803
2218
  threadContext.currentThreadId,
1804
2219
  resolvedSessionId
1805
2220
  ]);
1806
- (0, import_react7.useEffect)(() => {
2221
+ (0, import_react8.useEffect)(() => {
1807
2222
  const threadId = threadContext.currentThreadId;
1808
2223
  if (!isThreadReady(backendStateRef.current, threadId)) return;
1809
2224
  void messageController.flushPendingChat(threadId);
1810
2225
  }, [messageController, backendStateRef, threadContext.currentThreadId]);
1811
- (0, import_react7.useEffect)(() => {
2226
+ (0, import_react8.useEffect)(() => {
1812
2227
  const showToolNotification = (eventType) => (event) => {
1813
2228
  const payload = event.payload;
1814
2229
  const toolName = typeof (payload == null ? void 0 : payload.tool_name) === "string" ? payload.tool_name : void 0;
@@ -1833,19 +2248,14 @@ function AomiRuntimeCore({
1833
2248
  unsubscribeComplete();
1834
2249
  };
1835
2250
  }, [eventContext, notificationContext]);
1836
- (0, import_react7.useEffect)(() => {
2251
+ (0, import_react8.useEffect)(() => {
1837
2252
  const unsubscribe = eventContext.subscribe("system_notice", (event) => {
1838
2253
  const payload = event.payload;
1839
2254
  const message = payload == null ? void 0 : payload.message;
1840
- notificationContext.showNotification({
1841
- type: "notice",
1842
- title: "System notice",
1843
- message
1844
- });
1845
2255
  });
1846
2256
  return unsubscribe;
1847
2257
  }, [eventContext, notificationContext]);
1848
- const runtime = (0, import_react8.useExternalStoreRuntime)({
2258
+ const runtime = (0, import_react9.useExternalStoreRuntime)({
1849
2259
  messages: currentMessages,
1850
2260
  setMessages: (msgs) => threadContext.setThreadMessages(threadContext.currentThreadId, [...msgs]),
1851
2261
  isRunning,
@@ -1854,13 +2264,13 @@ function AomiRuntimeCore({
1854
2264
  convertMessage: (msg) => msg,
1855
2265
  adapters: { threadList: threadListAdapter }
1856
2266
  });
1857
- (0, import_react7.useEffect)(() => {
2267
+ (0, import_react8.useEffect)(() => {
1858
2268
  return () => {
1859
2269
  polling.stopAll();
1860
2270
  };
1861
2271
  }, [polling]);
1862
2272
  const userContext = useUser();
1863
- const sendMessage = (0, import_react7.useCallback)(
2273
+ const sendMessage = (0, import_react8.useCallback)(
1864
2274
  async (text) => {
1865
2275
  const appendMessage = {
1866
2276
  role: "user",
@@ -1873,39 +2283,39 @@ function AomiRuntimeCore({
1873
2283
  },
1874
2284
  [messageController, threadContext.currentThreadId]
1875
2285
  );
1876
- const cancelGeneration = (0, import_react7.useCallback)(() => {
2286
+ const cancelGeneration = (0, import_react8.useCallback)(() => {
1877
2287
  messageController.cancel(threadContext.currentThreadId);
1878
2288
  }, [messageController, threadContext.currentThreadId]);
1879
- const getMessages = (0, import_react7.useCallback)(
2289
+ const getMessages = (0, import_react8.useCallback)(
1880
2290
  (threadId) => {
1881
2291
  const id = threadId != null ? threadId : threadContext.currentThreadId;
1882
2292
  return threadContext.getThreadMessages(id);
1883
2293
  },
1884
2294
  [threadContext]
1885
2295
  );
1886
- const createThread = (0, import_react7.useCallback)(async () => {
2296
+ const createThread = (0, import_react8.useCallback)(async () => {
1887
2297
  await threadListAdapter.onSwitchToNewThread();
1888
2298
  return threadContextRef.current.currentThreadId;
1889
2299
  }, [threadListAdapter]);
1890
- const deleteThread = (0, import_react7.useCallback)(
2300
+ const deleteThread = (0, import_react8.useCallback)(
1891
2301
  async (threadId) => {
1892
2302
  await threadListAdapter.onDelete(threadId);
1893
2303
  },
1894
2304
  [threadListAdapter]
1895
2305
  );
1896
- const renameThread = (0, import_react7.useCallback)(
2306
+ const renameThread = (0, import_react8.useCallback)(
1897
2307
  async (threadId, title) => {
1898
2308
  await threadListAdapter.onRename(threadId, title);
1899
2309
  },
1900
2310
  [threadListAdapter]
1901
2311
  );
1902
- const archiveThread = (0, import_react7.useCallback)(
2312
+ const archiveThread = (0, import_react8.useCallback)(
1903
2313
  async (threadId) => {
1904
2314
  await threadListAdapter.onArchive(threadId);
1905
2315
  },
1906
2316
  [threadListAdapter]
1907
2317
  );
1908
- const selectThread = (0, import_react7.useCallback)(
2318
+ const selectThread = (0, import_react8.useCallback)(
1909
2319
  (threadId) => {
1910
2320
  if (threadContext.allThreadsMetadata.has(threadId)) {
1911
2321
  threadListAdapter.onSwitchToThread(threadId);
@@ -1915,7 +2325,7 @@ function AomiRuntimeCore({
1915
2325
  },
1916
2326
  [threadContext.allThreadsMetadata, threadListAdapter]
1917
2327
  );
1918
- const aomiRuntimeApi = (0, import_react7.useMemo)(
2328
+ const aomiRuntimeApi = (0, import_react8.useMemo)(
1919
2329
  () => ({
1920
2330
  // User API
1921
2331
  user: userContext.user,
@@ -1966,45 +2376,57 @@ function AomiRuntimeCore({
1966
2376
  eventContext
1967
2377
  ]
1968
2378
  );
1969
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AomiRuntimeApiProvider, { value: aomiRuntimeApi, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react8.AssistantRuntimeProvider, { runtime, children }) });
2379
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AomiRuntimeApiProvider, { value: aomiRuntimeApi, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react9.AssistantRuntimeProvider, { runtime, children }) });
1970
2380
  }
1971
2381
 
1972
- // src/runtime/aomi-runtime.tsx
1973
- var import_jsx_runtime6 = require("react/jsx-runtime");
2382
+ // packages/react/src/runtime/aomi-runtime.tsx
2383
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1974
2384
  function AomiRuntimeProvider({
1975
2385
  children,
1976
2386
  backendUrl = "http://localhost:8080"
1977
2387
  }) {
1978
- const backendApi = (0, import_react9.useMemo)(() => new BackendApi(backendUrl), [backendUrl]);
1979
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ThreadContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(NotificationContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(UserContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AomiRuntimeInner, { backendApi, children }) }) }) });
2388
+ const backendApi = (0, import_react10.useMemo)(() => new BackendApi(backendUrl), [backendUrl]);
2389
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ThreadContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NotificationContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(UserContextProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AomiRuntimeInner, { backendApi, children }) }) }) });
1980
2390
  }
1981
2391
  function AomiRuntimeInner({
1982
2392
  children,
1983
2393
  backendApi
1984
2394
  }) {
2395
+ var _a;
1985
2396
  const threadContext = useThreadContext();
1986
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1987
- EventContextProvider,
2397
+ const { user } = useUser();
2398
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2399
+ ControlContextProvider,
1988
2400
  {
1989
2401
  backendApi,
1990
2402
  sessionId: threadContext.currentThreadId,
1991
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AomiRuntimeCore, { backendApi, children })
2403
+ publicKey: (_a = user.address) != null ? _a : void 0,
2404
+ getThreadMetadata: threadContext.getThreadMetadata,
2405
+ updateThreadMetadata: threadContext.updateThreadMetadata,
2406
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2407
+ EventContextProvider,
2408
+ {
2409
+ backendApi,
2410
+ sessionId: threadContext.currentThreadId,
2411
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AomiRuntimeCore, { backendApi, children })
2412
+ }
2413
+ )
1992
2414
  }
1993
2415
  );
1994
2416
  }
1995
2417
 
1996
- // src/handlers/wallet-handler.ts
1997
- var import_react10 = require("react");
2418
+ // packages/react/src/handlers/wallet-handler.ts
2419
+ var import_react11 = require("react");
1998
2420
  function useWalletHandler({
1999
2421
  sessionId,
2000
2422
  onTxRequest
2001
2423
  }) {
2002
2424
  const { subscribe: subscribe2, sendOutboundSystem: sendOutbound } = useEventContext();
2003
2425
  const { setUser, getUserState } = useUser();
2004
- const [pendingTxRequests, setPendingTxRequests] = (0, import_react10.useState)(
2426
+ const [pendingTxRequests, setPendingTxRequests] = (0, import_react11.useState)(
2005
2427
  []
2006
2428
  );
2007
- (0, import_react10.useEffect)(() => {
2429
+ (0, import_react11.useEffect)(() => {
2008
2430
  const unsubscribe = subscribe2(
2009
2431
  "wallet_tx_request",
2010
2432
  (event) => {
@@ -2015,7 +2437,7 @@ function useWalletHandler({
2015
2437
  );
2016
2438
  return unsubscribe;
2017
2439
  }, [subscribe2, onTxRequest]);
2018
- (0, import_react10.useEffect)(() => {
2440
+ (0, import_react11.useEffect)(() => {
2019
2441
  const unsubscribe = subscribe2(
2020
2442
  "user_state_request",
2021
2443
  (event) => {
@@ -2028,7 +2450,7 @@ function useWalletHandler({
2028
2450
  );
2029
2451
  return unsubscribe;
2030
2452
  }, [subscribe2, onTxRequest]);
2031
- const sendTxComplete = (0, import_react10.useCallback)(
2453
+ const sendTxComplete = (0, import_react11.useCallback)(
2032
2454
  (tx) => {
2033
2455
  sendOutbound({
2034
2456
  type: "wallet:tx_complete",
@@ -2038,7 +2460,7 @@ function useWalletHandler({
2038
2460
  },
2039
2461
  [sendOutbound, sessionId]
2040
2462
  );
2041
- const sendConnectionChange = (0, import_react10.useCallback)(
2463
+ const sendConnectionChange = (0, import_react11.useCallback)(
2042
2464
  (status, address, chainId) => {
2043
2465
  if (status === "connected") {
2044
2466
  setUser({
@@ -2061,7 +2483,7 @@ function useWalletHandler({
2061
2483
  },
2062
2484
  [setUser, sendOutbound, sessionId]
2063
2485
  );
2064
- const clearTxRequest = (0, import_react10.useCallback)((index) => {
2486
+ const clearTxRequest = (0, import_react11.useCallback)((index) => {
2065
2487
  setPendingTxRequests((prev) => prev.filter((_, i) => i !== index));
2066
2488
  }, []);
2067
2489
  return {
@@ -2072,8 +2494,8 @@ function useWalletHandler({
2072
2494
  };
2073
2495
  }
2074
2496
 
2075
- // src/handlers/notification-handler.ts
2076
- var import_react11 = require("react");
2497
+ // packages/react/src/handlers/notification-handler.ts
2498
+ var import_react12 = require("react");
2077
2499
  var notificationIdCounter2 = 0;
2078
2500
  function generateNotificationId() {
2079
2501
  return `notif-${Date.now()}-${++notificationIdCounter2}`;
@@ -2082,8 +2504,8 @@ function useNotificationHandler({
2082
2504
  onNotification
2083
2505
  } = {}) {
2084
2506
  const { subscribe: subscribe2 } = useEventContext();
2085
- const [notifications, setNotifications] = (0, import_react11.useState)([]);
2086
- (0, import_react11.useEffect)(() => {
2507
+ const [notifications, setNotifications] = (0, import_react12.useState)([]);
2508
+ (0, import_react12.useEffect)(() => {
2087
2509
  const unsubscribe = subscribe2("notification", (event) => {
2088
2510
  var _a, _b;
2089
2511
  const payload = event.payload;
@@ -2102,7 +2524,7 @@ function useNotificationHandler({
2102
2524
  return unsubscribe;
2103
2525
  }, [subscribe2, onNotification]);
2104
2526
  const unhandledCount = notifications.filter((n) => !n.handled).length;
2105
- const markHandled = (0, import_react11.useCallback)((id) => {
2527
+ const markHandled = (0, import_react12.useCallback)((id) => {
2106
2528
  setNotifications(
2107
2529
  (prev) => prev.map((n) => n.id === id ? __spreadProps(__spreadValues({}, n), { handled: true }) : n)
2108
2530
  );
@@ -2117,6 +2539,7 @@ function useNotificationHandler({
2117
2539
  0 && (module.exports = {
2118
2540
  AomiRuntimeProvider,
2119
2541
  BackendApi,
2542
+ ControlContextProvider,
2120
2543
  EventContextProvider,
2121
2544
  NotificationContextProvider,
2122
2545
  ThreadContextProvider,
@@ -2124,7 +2547,9 @@ function useNotificationHandler({
2124
2547
  cn,
2125
2548
  formatAddress,
2126
2549
  getNetworkName,
2550
+ initThreadControl,
2127
2551
  useAomiRuntime,
2552
+ useControl,
2128
2553
  useCurrentThreadMessages,
2129
2554
  useCurrentThreadMetadata,
2130
2555
  useEventContext,