@ai-matrx/chat 0.7.17 → 0.7.19

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/agents/components/chat/ChatHistorySidebar.js +3 -0
  3. package/dist/agents/components/chat/ChatSidebarMenu.js +3 -0
  4. package/dist/agents/components/chat/PinnedAgentsSection.d.ts +3 -5
  5. package/dist/agents/components/chat/PinnedAgentsSection.js +7 -4
  6. package/dist/agents/components/conversation-history/ConversationHistorySidebar.d.ts +14 -3
  7. package/dist/agents/components/conversation-history/ConversationHistorySidebar.js +57 -37
  8. package/dist/agents/components/conversation-history/ConversationSourceFilterTree.js +3 -3
  9. package/dist/agents/components/inputs/smart-input/ConversationContextChip.js +18 -7
  10. package/dist/agents/components/inputs/smart-input/ConversationContextRail.js +4 -2
  11. package/dist/agents/components/inputs/smart-input/DesktopPresenceIndicator.js +6 -0
  12. package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPill.js +21 -7
  13. package/dist/agents/components/inputs/smart-input/composer/ComposerChipsRow.js +3 -3
  14. package/dist/agents/components/inputs/smart-input/composer/ComposerOutput.js +22 -15
  15. package/dist/agents/components/inputs/smart-input/composer/ComposerSplash.js +19 -3
  16. package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.d.ts +24 -3
  17. package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.js +32 -30
  18. package/dist/agents/components/inputs/smart-input/use-compute-target-actions.d.ts +20 -0
  19. package/dist/agents/components/inputs/smart-input/use-compute-target-actions.js +11 -0
  20. package/dist/agents/hooks/useConversationMaterialized.js +42 -9
  21. package/dist/agents/hooks/useCreatorOwnershipSync.js +7 -3
  22. package/dist/agents/hooks/useWorkingDocument.d.ts +12 -5
  23. package/dist/agents/hooks/useWorkingDocument.js +18 -6
  24. package/dist/agents/identity/agent-identity.d.ts +7 -1
  25. package/dist/agents/identity/agent-identity.js +11 -6
  26. package/dist/agents/identity/model-catalog.d.ts +7 -0
  27. package/dist/agents/identity/model-catalog.js +16 -1
  28. package/dist/agents/redux/conversation-history/slice.d.ts +2 -0
  29. package/dist/agents/redux/conversation-history/slice.js +2 -0
  30. package/dist/agents/redux/conversation-history/thunks.d.ts +2 -0
  31. package/dist/agents/redux/conversation-history/thunks.js +6 -3
  32. package/dist/agents/redux/conversation-history/types.d.ts +6 -0
  33. package/dist/agents/redux/conversation-history/types.js +1 -0
  34. package/dist/agents/redux/conversation-list/conversation-rows.d.ts +7 -0
  35. package/dist/agents/redux/conversation-list/conversation-rows.js +3 -1
  36. package/dist/agents/redux/execution-system/client-capabilities/desktop-native.provider.js +4 -1
  37. package/dist/agents/redux/execution-system/context-rules/mandate-kill-switch.d.ts +4 -0
  38. package/dist/agents/redux/execution-system/context-rules/mandate-kill-switch.js +26 -2
  39. package/dist/agents/redux/execution-system/conversations/conversations.selectors.d.ts +10 -0
  40. package/dist/agents/redux/execution-system/conversations/conversations.selectors.js +3 -0
  41. package/dist/agents/redux/execution-system/instance-user-input/useComposerDraftRestore.js +6 -1
  42. package/dist/agents/redux/execution-system/instance-working-document/scratchpad.thunks.js +9 -0
  43. package/dist/agents/redux/execution-system/thunks/launch-agent-execution.thunk.js +1 -8
  44. package/dist/agents/ui-first-tools/ui/lists/TaskPanelChip.js +4 -2
  45. package/dist/mandates/service.js +47 -32
  46. package/dist/next/navigation.js +5 -1
  47. package/package.json +6 -6
@@ -11,6 +11,13 @@
11
11
  import type { ConversationListItem } from "./conversation-list.types.js";
12
12
  /** Columns projected from `chat.conversation` — enough for any list row. */
13
13
  export declare const CONVERSATION_ROW_COLUMNS = "id, title, description, status, message_count, initial_agent_id, last_model_id, source_app, source_feature, origin_class, created_at, updated_at, exclude_from_kg";
14
+ /**
15
+ * The columns a history ROW shows — its name, its time, and the provenance its
16
+ * menu offers (agent, source, archive, knowledge-graph opt-out). The /chat
17
+ * sidebar reads only these (owner, 2026-10-09: "little to no additional data
18
+ * than just the names"); `mapConversationDbRow` leaves the rest unset.
19
+ */
20
+ export declare const CONVERSATION_NAME_ROW_COLUMNS = "id, title, status, initial_agent_id, source_app, source_feature, origin_class, updated_at, exclude_from_kg";
14
21
  /** Structural row type — the columns above, loosely typed like the query result. */
15
22
  export type ConversationDbRow = Record<string, unknown>;
16
23
  export declare function mapConversationDbRow(row: ConversationDbRow): ConversationListItem;
@@ -1,12 +1,13 @@
1
1
  import { conversationTitleText } from "@ai-matrx/content-ir/surfaces";
2
2
  const CONVERSATION_ROW_COLUMNS = "id, title, description, status, message_count, initial_agent_id, last_model_id, source_app, source_feature, origin_class, created_at, updated_at, exclude_from_kg";
3
+ const CONVERSATION_NAME_ROW_COLUMNS = "id, title, status, initial_agent_id, source_app, source_feature, origin_class, updated_at, exclude_from_kg";
3
4
  function mapConversationDbRow(row) {
4
5
  return {
5
6
  conversationId: row.id,
6
7
  title: conversationTitleText(row.title ?? null),
7
8
  description: row.description ?? null,
8
9
  updatedAt: row.updated_at,
9
- createdAt: row.created_at,
10
+ ...typeof row.created_at === "string" ? { createdAt: row.created_at } : {},
10
11
  status: row.status,
11
12
  messageCount: row.message_count ?? 0,
12
13
  isFavorite: false,
@@ -19,6 +20,7 @@ function mapConversationDbRow(row) {
19
20
  };
20
21
  }
21
22
  export {
23
+ CONVERSATION_NAME_ROW_COLUMNS,
22
24
  CONVERSATION_ROW_COLUMNS,
23
25
  mapConversationDbRow
24
26
  };
@@ -1,3 +1,4 @@
1
+ import { whenPageIdle } from "@ai-matrx/kit/idle-scheduler";
1
2
  import { getLiveDesktopInstance } from "./desktop-presence.js";
2
3
  import { registerClientCapability } from "./registry.js";
3
4
  import { isChatHostConfigured, onChatHostConfigured } from "../../../../host/configure.js";
@@ -6,7 +7,9 @@ function toSysPlatform(platform) {
6
7
  }
7
8
  const PRESENCE_BUDGET_MS = 150;
8
9
  if (typeof window !== "undefined") {
9
- const warm = () => setTimeout(() => void getLiveDesktopInstance(), 0);
10
+ const warm = () => void whenPageIdle().then((idle) => {
11
+ if (idle) void getLiveDesktopInstance();
12
+ });
10
13
  if (isChatHostConfigured()) {
11
14
  warm();
12
15
  } else {
@@ -13,6 +13,10 @@
13
13
  * `false` would send page values the server then withholds (R2-1). Every
14
14
  * resolution invalidation (an organization switch, a binding write) forgets
15
15
  * the answers, since a verdict is true for one person in one organization.
16
+ * An answer younger than the resolution cache's lifetime is reused without a
17
+ * re-ask, and concurrent askers (the rail and the meta row) share one refresh.
16
18
  */
17
19
  import type { AnyMandateKey } from "@ai-matrx/agents/mandates";
18
20
  export declare function resolveMandateKillSwitch(mandateKey: AnyMandateKey | null | undefined): Promise<boolean>;
21
+ /** The last answer, synchronously (undefined until one was read) — a mount's first paint. */
22
+ export declare function peekMandateKillSwitch(mandateKey: AnyMandateKey | null | undefined): boolean | undefined;
@@ -1,19 +1,37 @@
1
1
  import { onMandateCacheInvalidated, resolveMandate } from "../../../../mandates/service.js";
2
2
  const lastKnown = /* @__PURE__ */ new Map();
3
+ const knownAt = /* @__PURE__ */ new Map();
4
+ const refreshing = /* @__PURE__ */ new Map();
5
+ const FRESH_MS = 5 * 60 * 1e3;
3
6
  let forgetsOnInvalidation = false;
4
7
  function forgetOnInvalidation() {
5
8
  if (forgetsOnInvalidation) return;
6
9
  forgetsOnInvalidation = true;
7
10
  onMandateCacheInvalidated((mandateKey) => {
8
- if (mandateKey === void 0) lastKnown.clear();
9
- else lastKnown.delete(mandateKey);
11
+ if (mandateKey === void 0) {
12
+ lastKnown.clear();
13
+ knownAt.clear();
14
+ } else {
15
+ lastKnown.delete(mandateKey);
16
+ knownAt.delete(mandateKey);
17
+ }
10
18
  });
11
19
  }
12
20
  function refreshKillSwitch(mandateKey) {
21
+ const pending = refreshing.get(mandateKey);
22
+ if (pending) return pending;
23
+ const run = askKillSwitch(mandateKey).finally(() => {
24
+ if (refreshing.get(mandateKey) === run) refreshing.delete(mandateKey);
25
+ });
26
+ refreshing.set(mandateKey, run);
27
+ return run;
28
+ }
29
+ function askKillSwitch(mandateKey) {
13
30
  return resolveMandate(mandateKey, { optional: true }).then(
14
31
  (mandate) => {
15
32
  const closed = mandate?.contract.autoContextDisabled === true;
16
33
  lastKnown.set(mandateKey, closed);
34
+ knownAt.set(mandateKey, Date.now());
17
35
  return closed;
18
36
  },
19
37
  (error) => {
@@ -26,9 +44,15 @@ async function resolveMandateKillSwitch(mandateKey) {
26
44
  if (!mandateKey) return false;
27
45
  forgetOnInvalidation();
28
46
  const known = lastKnown.get(mandateKey);
47
+ const at = knownAt.get(mandateKey);
48
+ if (known !== void 0 && at !== void 0 && Date.now() - at < FRESH_MS) return known;
29
49
  const fresh = refreshKillSwitch(mandateKey);
30
50
  return known ?? fresh;
31
51
  }
52
+ function peekMandateKillSwitch(mandateKey) {
53
+ return mandateKey ? lastKnown.get(mandateKey) : void 0;
54
+ }
32
55
  export {
56
+ peekMandateKillSwitch,
33
57
  resolveMandateKillSwitch
34
58
  };
@@ -266,6 +266,16 @@ export declare const selectRunningInstances: ((state: {
266
266
  };
267
267
  export declare const selectAgentIdFromInstance: (conversationId: string) => (state: ChatRootState) => string | undefined;
268
268
  export declare const selectIsCacheOnly: (conversationId: string) => (state: ChatRootState) => boolean;
269
+ /**
270
+ * Is this a DRAFT — a conversation minted in this tab that has no server row
271
+ * yet (`cacheOnly`, until the first send's confirmation or a DB hydrate flips
272
+ * it)? Reads keyed by a draft's id can only answer "nothing": gate them on
273
+ * this and they run the moment the first send makes it real. A conversation
274
+ * Redux does not hold is NOT a draft (it was opened from elsewhere, so it has a
275
+ * row) — unlike `selectIsCacheOnly`, which answers true for an unknown id
276
+ * because it gates WRITES.
277
+ */
278
+ export declare const selectIsDraftConversation: (conversationId: string | null | undefined) => (state: ChatRootState) => boolean;
269
279
  export declare const selectConversationTitle: (conversationId: string) => (state: ChatRootState) => string | null;
270
280
  export declare const selectConversationDescription: (conversationId: string) => (state: ChatRootState) => string | null;
271
281
  export declare const selectConversationKeywords: (conversationId: string) => (state: ChatRootState) => string[] | null;
@@ -44,6 +44,8 @@ const selectRunningInstances = createSelector(
44
44
  );
45
45
  const selectAgentIdFromInstance = (conversationId) => (state) => state.conversations.byConversationId[conversationId]?.agentId;
46
46
  const selectIsCacheOnly = (conversationId) => (state) => state.conversations.byConversationId[conversationId]?.cacheOnly ?? true;
47
+ const selectIsDraftConversation = (conversationId) => (state) => !!conversationId && // A store without the conversations slice holds no drafts.
48
+ state.conversations?.byConversationId[conversationId]?.cacheOnly === true;
47
49
  const selectConversationTitle = (conversationId) => (state) => state.conversations.byConversationId[conversationId]?.title ?? null;
48
50
  const selectConversationDescription = (conversationId) => (state) => state.conversations.byConversationId[conversationId]?.description ?? null;
49
51
  const selectConversationKeywords = (conversationId) => (state) => state.conversations.byConversationId[conversationId]?.keywords ?? null;
@@ -82,6 +84,7 @@ export {
82
84
  selectInstanceStatus,
83
85
  selectInstancesByAgent,
84
86
  selectIsCacheOnly,
87
+ selectIsDraftConversation,
85
88
  selectRunningInstances,
86
89
  selectViewerCanReply
87
90
  };
@@ -10,6 +10,7 @@ import {
10
10
  } from "./composer-draft-store.js";
11
11
  import { applyComposerDraft } from "./restore-composer-draft.thunk.js";
12
12
  import { restoreComposerRemarks } from "../instance-resources/remarks.js";
13
+ import { selectIsDraftConversation } from "../conversations/conversations.selectors.js";
13
14
  import {
14
15
  flushComposerDraftWrite,
15
16
  isDraftRestoreEnabled
@@ -50,7 +51,6 @@ function useComposerDraftRestore(conversationId, alias) {
50
51
  setRestoredValue(null);
51
52
  setStorageAvailable(isComposerDraftStorageAvailable());
52
53
  if (!enabled) return;
53
- if (chatHostHasDatabase()) restoreComposerRemarks(conversationId);
54
54
  restoreRenderedArtifactChips(dispatch, store.getState, conversationId, ownerId ?? null);
55
55
  const token = peekComposerDraft(conversationId, liveAlias, ownerId);
56
56
  if (!token) return;
@@ -58,6 +58,11 @@ function useComposerDraftRestore(conversationId, alias) {
58
58
  setRestoredValue(token.value);
59
59
  }
60
60
  }, [conversationId, liveAlias, dispatch, store, enabled, entryReady, ownerId]);
61
+ const isDraft = useAppSelector(selectIsDraftConversation(conversationId));
62
+ useEffect(() => {
63
+ if (!enabled || !entryReady || isDraft || !ownerId) return;
64
+ if (chatHostHasDatabase()) restoreComposerRemarks(conversationId);
65
+ }, [conversationId, enabled, entryReady, isDraft, ownerId]);
61
66
  useEffect(() => {
62
67
  if (restoredValue !== null && text !== restoredValue) setRestoredValue(null);
63
68
  }, [text, restoredValue]);
@@ -27,6 +27,7 @@ import {
27
27
  unlinkDocumentFromConversation
28
28
  } from "./cx-working-document.service.js";
29
29
  import { getActiveOrgId } from "../../../../host/org.js";
30
+ import { toast } from "../../../../host/notify.js";
30
31
  function setActivePreference(dispatch, id) {
31
32
  dispatch(
32
33
  setPreference({ module: "scratchpad", preference: "activeId", value: id })
@@ -131,6 +132,14 @@ const createScratchpadThunk = createAsyncThunk("scratchpad/create", async (args,
131
132
  const setScratchpadGateThunk = createAsyncThunk(
132
133
  "scratchpad/setGate",
133
134
  async ({ conversationId, enabled }, { dispatch, getState }) => {
135
+ if (enabled) {
136
+ try {
137
+ await dispatch(hydrateActiveScratchpadThunk()).unwrap();
138
+ } catch (err) {
139
+ toast.error("Couldn't load your scratchpad. Try turning it on again.");
140
+ throw err;
141
+ }
142
+ }
134
143
  if (enabled && !selectActiveScratchpadId(getState())) {
135
144
  await dispatch(createScratchpadThunk());
136
145
  }
@@ -26,7 +26,7 @@ import { alwaysOnSurfaceKeys } from "../../../../surfaces/utils/always-on-contex
26
26
  import {
27
27
  fetchAgentRunTier
28
28
  } from "../../agent-definition/thunks.js";
29
- import { isSignedOutVisitor, NotAuthenticatedError, selectAccessToken } from "../../../../host/identity.js";
29
+ import { isSignedOutVisitor, NotAuthenticatedError } from "../../../../host/identity.js";
30
30
  import { applyLaunchModelOverrides } from "../instance-model-overrides/launch-model-overrides.js";
31
31
  import { selectAgentRunTier, selectAgentById } from "../../agent-definition/selectors.js";
32
32
  import { getShortcutRecordFromState } from "../../agent-shortcuts/selectors.js";
@@ -80,11 +80,7 @@ import {
80
80
  resolveLaunchMappingLayers,
81
81
  resolvePerLaunchMappings
82
82
  } from "./surface-scope-mapping.js";
83
- import { ensureAgentCatalog } from "../../../identity/agent-identity.js";
84
83
  import { selectOrganizationId } from "../../../../host/org.js";
85
- function hasSignedInSession(state) {
86
- return Boolean(selectAccessToken(state));
87
- }
88
84
  const INTERACTIVE_MODES = /* @__PURE__ */ new Set([
89
85
  "modal-full",
90
86
  "modal-compact",
@@ -217,9 +213,6 @@ const launchAgentExecution = createAsyncThunk("instances/launch", async (options
217
213
  let bindingAutoRun = resolvedMandate?.autoRun ?? null;
218
214
  let bindingAutoRunGap = [];
219
215
  if (agentId && !shortcutId) {
220
- if (hasSignedInSession(getState())) {
221
- void ensureAgentCatalog();
222
- }
223
216
  const preState = getState();
224
217
  const payload = selectAgentRunTier(preState, agentId);
225
218
  const debugProjectCreate = isProjectCreateFlow(sourceFeature, agentId);
@@ -15,6 +15,7 @@ import {
15
15
  unsubscribeAgentLists
16
16
  } from "../../redux/agent-lists.thunks.js";
17
17
  import { useConversationListsTab } from "./TaskPanel.js";
18
+ import { selectIsDraftConversation } from "../../../redux/execution-system/conversations/conversations.selectors.js";
18
19
  function TaskPanelChip({
19
20
  conversationId,
20
21
  className
@@ -24,14 +25,15 @@ function TaskPanelChip({
24
25
  const hasContent = useAppSelector(selectHasAgentListsContent(conversationId));
25
26
  const taskCounts = useAppSelector(selectAgentTaskCounts(conversationId));
26
27
  const todoCounts = useAppSelector(selectUserTodoCounts(conversationId));
28
+ const isDraft = useAppSelector(selectIsDraftConversation(conversationId));
27
29
  useEffect(() => {
28
- if (!conversationId) return void 0;
30
+ if (!conversationId || isDraft) return void 0;
29
31
  void dispatch(ensureAgentLists(conversationId));
30
32
  dispatch(subscribeAgentLists(conversationId));
31
33
  return () => {
32
34
  dispatch(unsubscribeAgentLists(conversationId));
33
35
  };
34
- }, [conversationId, dispatch]);
36
+ }, [conversationId, isDraft, dispatch]);
35
37
  if (!hasContent) return null;
36
38
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
37
39
  Chip,
@@ -84,9 +84,24 @@ function mandateCacheKey(userId, organizationId, mandateKey) {
84
84
  return `${userId}:${organizationId}:${mandateKey}`;
85
85
  }
86
86
  function dropMandateCacheForOrgSwitch() {
87
- cache.clear();
87
+ let current = null;
88
+ try {
89
+ current = peekSelectedOrganizationId() ?? null;
90
+ } catch {
91
+ current = null;
92
+ }
93
+ let dropped = false;
94
+ for (const [key, entry] of cache) {
95
+ if (current !== null && entry.value.organizationId === current) continue;
96
+ cache.delete(key);
97
+ dropped = true;
98
+ }
99
+ resolutionsInFlight.clear();
100
+ cacheGeneration += 1;
101
+ if (!dropped) return;
88
102
  for (const listener of invalidationListeners) listener(void 0);
89
103
  }
104
+ let cacheGeneration = 0;
90
105
  const invalidationListeners = /* @__PURE__ */ new Set();
91
106
  function onMandateCacheInvalidated(listener) {
92
107
  invalidationListeners.add(listener);
@@ -94,6 +109,8 @@ function onMandateCacheInvalidated(listener) {
94
109
  }
95
110
  function invalidateMandateCache(mandateKey) {
96
111
  invalidateMandateCatalogueCache();
112
+ cacheGeneration += 1;
113
+ resolutionsInFlight.clear();
97
114
  if (mandateKey) {
98
115
  for (const key of cache.keys()) {
99
116
  if (key.endsWith(`:${mandateKey}`)) cache.delete(key);
@@ -153,7 +170,13 @@ async function selectedOrganizationFor(mandateKey) {
153
170
  }
154
171
  return organizationId;
155
172
  }
156
- async function fetchResolutionVerdict(mandateKey, options, cacheLookup) {
173
+ async function fetchResolutionVerdict(mandateKey, options) {
174
+ const question = await resolutionQuestion(mandateKey, options);
175
+ const verdict = await askResolutionDoor(mandateKey, options, question.organizationId);
176
+ if (verdict === null) return null;
177
+ return { kind: "verdict", verdict, ...question };
178
+ }
179
+ async function resolutionQuestion(mandateKey, options) {
157
180
  const supabase = createClient();
158
181
  const { data: auth, error: authError } = await getClaimsUser(supabase);
159
182
  const userId = auth.user?.id;
@@ -161,26 +184,10 @@ async function fetchResolutionVerdict(mandateKey, options, cacheLookup) {
161
184
  throw new Error(`mandate resolution could not verify the session: ${authError.message}`);
162
185
  }
163
186
  if (!userId) {
164
- const guestKey = `guest::${mandateKey}`;
165
- const cachedGuest = cacheLookup?.(guestKey);
166
- if (cachedGuest) return { kind: "cached", value: cachedGuest };
167
- const guestVerdict = await askResolutionDoor(mandateKey, options, null);
168
- if (guestVerdict === null) return null;
169
- return {
170
- kind: "verdict",
171
- verdict: guestVerdict,
172
- organizationId: null,
173
- cacheKey: guestKey,
174
- guest: true
175
- };
187
+ return { organizationId: null, cacheKey: `guest::${mandateKey}`, guest: true };
176
188
  }
177
189
  const organizationId = options.organizationId ?? await selectedOrganizationFor(mandateKey);
178
- const cacheKey = mandateCacheKey(userId, organizationId, mandateKey);
179
- const cachedValue = cacheLookup?.(cacheKey);
180
- if (cachedValue) return { kind: "cached", value: cachedValue };
181
- const verdict = await askResolutionDoor(mandateKey, options, organizationId);
182
- if (verdict === null) return null;
183
- return { kind: "verdict", verdict, organizationId, cacheKey, guest: false };
190
+ return { organizationId, cacheKey: mandateCacheKey(userId, organizationId, mandateKey), guest: false };
184
191
  }
185
192
  async function askResolutionDoor(mandateKey, options, organizationId) {
186
193
  let verdict;
@@ -222,14 +229,25 @@ async function askResolutionDoor(mandateKey, options, organizationId) {
222
229
  return verdict;
223
230
  }
224
231
  async function resolveMandate(mandateKey, options = {}) {
225
- const supabase = createClient();
226
- const fetched = await fetchResolutionVerdict(mandateKey, options, (cacheKey2) => {
227
- const cached = cache.get(cacheKey2);
228
- return cached && Date.now() - cached.at < CACHE_TTL_MS ? cached.value : void 0;
232
+ const question = await resolutionQuestion(mandateKey, options);
233
+ const cached = cache.get(question.cacheKey);
234
+ if (cached && Date.now() - cached.at < CACHE_TTL_MS) return cached.value;
235
+ const flightKey = `${question.cacheKey}|${options.optional ? "optional" : "required"}`;
236
+ const pending = resolutionsInFlight.get(flightKey);
237
+ if (pending) return pending;
238
+ const run = resolveFromDoor(mandateKey, options, question).finally(() => {
239
+ if (resolutionsInFlight.get(flightKey) === run) resolutionsInFlight.delete(flightKey);
229
240
  });
230
- if (fetched === null) return null;
231
- if (fetched.kind === "cached") return fetched.value;
232
- const { verdict, organizationId, cacheKey, guest } = fetched;
241
+ resolutionsInFlight.set(flightKey, run);
242
+ return run;
243
+ }
244
+ const resolutionsInFlight = /* @__PURE__ */ new Map();
245
+ async function resolveFromDoor(mandateKey, options, question) {
246
+ const supabase = createClient();
247
+ const generation = cacheGeneration;
248
+ const verdict = await askResolutionDoor(mandateKey, options, question.organizationId);
249
+ if (verdict === null) return null;
250
+ const { organizationId, cacheKey, guest } = question;
233
251
  const holder = assertRunnableVerdict(mandateKey, verdict);
234
252
  const provenance = verdict.provenance;
235
253
  const holderType = verdict.holder_type ?? "agent";
@@ -260,7 +278,7 @@ async function resolveMandate(mandateKey, options = {}) {
260
278
  pinnedContext: [],
261
279
  presentation: null
262
280
  };
263
- cache.set(cacheKey, { at: Date.now(), value: guestValue });
281
+ if (generation === cacheGeneration) cache.set(cacheKey, { at: Date.now(), value: guestValue });
264
282
  return guestValue;
265
283
  }
266
284
  const { data: mandate, error } = await mandateDefinitions(supabase).select("*").eq("mandate_key", mandateKey).is("deleted_at", null).maybeSingle();
@@ -319,15 +337,12 @@ async function resolveMandate(mandateKey, options = {}) {
319
337
  pinnedContext: wave1.pinnedContext,
320
338
  presentation
321
339
  };
322
- cache.set(cacheKey, { at: Date.now(), value });
340
+ if (generation === cacheGeneration) cache.set(cacheKey, { at: Date.now(), value });
323
341
  return value;
324
342
  }
325
343
  async function resolveMandateHolder(mandateKey, options = {}) {
326
344
  const fetched = await fetchResolutionVerdict(mandateKey, options);
327
345
  if (fetched === null) return null;
328
- if (fetched.kind === "cached") {
329
- return null;
330
- }
331
346
  const { verdict, organizationId } = fetched;
332
347
  const base = {
333
348
  mandateKey,
@@ -1,6 +1,10 @@
1
1
  "use client";
2
+ import { createElement } from "react";
2
3
  import NextLink from "next/link";
3
4
  import { usePathname, useRouter, useSearchParams } from "next/navigation";
5
+ function ChatNextLink({ prefetch, ...props }) {
6
+ return createElement(NextLink, { ...props, prefetch: prefetch ?? false });
7
+ }
4
8
  function useNextNavigation() {
5
9
  const router = useRouter();
6
10
  return {
@@ -10,7 +14,7 @@ function useNextNavigation() {
10
14
  useRouter,
11
15
  usePathname,
12
16
  useSearchParams,
13
- Link: NextLink
17
+ Link: ChatNextLink
14
18
  };
15
19
  }
16
20
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/chat",
3
- "version": "0.7.17",
3
+ "version": "0.7.19",
4
4
  "description": "THE one AI Matrx person-to-agent chat system: the full chat (running agents, conversations, tool calls, streaming, context, surfaces, voice) as explicit per-module subpaths @ai-matrx/chat/<domain>/<module> (no wildcards: scripts/public-modules.json) over a host contract (/host, /host/react) whose only required value is a db. One chat, one renderer: the old kernel entries (., /external-store, /matrx, /react, /tools, styles.css, tokens.css) are retired.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -4681,25 +4681,25 @@
4681
4681
  "server-only": "^0.0.1",
4682
4682
  "uuid": "^14.0.2",
4683
4683
  "zod": "^4.6.5",
4684
+ "@ai-matrx/alchemy": "latest",
4684
4685
  "@ai-matrx/agents": "latest",
4685
4686
  "@ai-matrx/associations": "latest",
4686
- "@ai-matrx/browser-audio": "latest",
4687
- "@ai-matrx/alchemy": "latest",
4688
4687
  "@ai-matrx/content-ir": "latest",
4689
4688
  "@ai-matrx/canvas": "latest",
4689
+ "@ai-matrx/browser-audio": "latest",
4690
+ "@ai-matrx/content-ir-react": "latest",
4690
4691
  "@ai-matrx/data": "latest",
4691
4692
  "@ai-matrx/design-system": "latest",
4692
4693
  "@ai-matrx/diff": "latest",
4693
4694
  "@ai-matrx/icons": "latest",
4694
4695
  "@ai-matrx/kit": "latest",
4695
- "@ai-matrx/media": "latest",
4696
4696
  "@ai-matrx/print": "latest",
4697
4697
  "@ai-matrx/realtime": "latest",
4698
4698
  "@ai-matrx/records": "latest",
4699
4699
  "@ai-matrx/records-ui": "latest",
4700
4700
  "@ai-matrx/rich-content": "latest",
4701
4701
  "@ai-matrx/rich-editor": "latest",
4702
- "@ai-matrx/content-ir-react": "latest"
4702
+ "@ai-matrx/media": "latest"
4703
4703
  },
4704
4704
  "devDependencies": {
4705
4705
  "@arethetypeswrong/cli": "^0.18.5",
@@ -4818,5 +4818,5 @@
4818
4818
  "check:side-effects": "node ../scripts/check-side-effects-listed.mjs . && node ../scripts/check-side-effects-listed.mjs --self-test",
4819
4819
  "check:public-surface": "node ./scripts/sync-public-surface.mjs --check && node ./scripts/sync-public-surface.mjs --self-test"
4820
4820
  },
4821
- "gitHead": "d9101c14c9a0338c4468cfd4c7d18aa5400a5762"
4821
+ "gitHead": "e41d5e343fbfd45a4bac979f5b353b34d63d348b"
4822
4822
  }