@ai-matrx/chat 0.7.18 → 0.7.20
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/CHANGELOG.md +17 -0
- package/dist/agents/components/chat/ChatHistorySidebar.js +3 -0
- package/dist/agents/components/chat/ChatSidebarMenu.js +3 -0
- package/dist/agents/components/chat/PinnedAgentsSection.d.ts +3 -5
- package/dist/agents/components/chat/PinnedAgentsSection.js +7 -4
- package/dist/agents/components/conversation-history/ConversationHistorySidebar.d.ts +14 -3
- package/dist/agents/components/conversation-history/ConversationHistorySidebar.js +57 -37
- package/dist/agents/components/conversation-history/ConversationSourceFilterTree.js +3 -3
- package/dist/agents/components/inputs/smart-input/ConversationContextChip.js +18 -7
- package/dist/agents/components/inputs/smart-input/ConversationContextRail.js +4 -2
- package/dist/agents/components/inputs/smart-input/DesktopPresenceIndicator.js +6 -0
- package/dist/agents/components/inputs/smart-input/composer/ComposerAgentPill.js +21 -7
- package/dist/agents/components/inputs/smart-input/composer/ComposerChipsRow.js +3 -3
- package/dist/agents/components/inputs/smart-input/composer/ComposerOutput.js +22 -15
- package/dist/agents/components/inputs/smart-input/composer/ComposerSplash.js +19 -3
- package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.d.ts +24 -3
- package/dist/agents/components/inputs/smart-input/composer/useComposerAgent.js +32 -30
- package/dist/agents/components/inputs/smart-input/use-compute-target-actions.d.ts +20 -0
- package/dist/agents/components/inputs/smart-input/use-compute-target-actions.js +11 -0
- package/dist/agents/hooks/useConversationMaterialized.js +42 -9
- package/dist/agents/hooks/useCreatorOwnershipSync.js +7 -3
- package/dist/agents/hooks/useWorkingDocument.d.ts +12 -5
- package/dist/agents/hooks/useWorkingDocument.js +18 -6
- package/dist/agents/identity/agent-identity.d.ts +7 -1
- package/dist/agents/identity/agent-identity.js +11 -6
- package/dist/agents/identity/model-catalog.d.ts +7 -0
- package/dist/agents/identity/model-catalog.js +16 -1
- package/dist/agents/redux/conversation-history/slice.d.ts +2 -0
- package/dist/agents/redux/conversation-history/slice.js +2 -0
- package/dist/agents/redux/conversation-history/thunks.d.ts +2 -0
- package/dist/agents/redux/conversation-history/thunks.js +6 -3
- package/dist/agents/redux/conversation-history/types.d.ts +6 -0
- package/dist/agents/redux/conversation-history/types.js +1 -0
- package/dist/agents/redux/conversation-list/conversation-rows.d.ts +7 -0
- package/dist/agents/redux/conversation-list/conversation-rows.js +3 -1
- package/dist/agents/redux/execution-system/client-capabilities/desktop-native.provider.js +4 -1
- package/dist/agents/redux/execution-system/context-rules/mandate-kill-switch.d.ts +4 -0
- package/dist/agents/redux/execution-system/context-rules/mandate-kill-switch.js +26 -2
- package/dist/agents/redux/execution-system/conversations/conversations.selectors.d.ts +10 -0
- package/dist/agents/redux/execution-system/conversations/conversations.selectors.js +3 -0
- package/dist/agents/redux/execution-system/instance-user-input/useComposerDraftRestore.js +6 -1
- package/dist/agents/redux/execution-system/instance-working-document/scratchpad.thunks.js +9 -0
- package/dist/agents/redux/execution-system/thunks/launch-agent-execution.thunk.js +1 -8
- package/dist/agents/ui-first-tools/ui/lists/TaskPanelChip.js +4 -2
- package/dist/mandates/service.js +47 -32
- package/dist/next/navigation.js +5 -1
- 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 = () =>
|
|
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)
|
|
9
|
-
|
|
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
|
|
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,
|
package/dist/mandates/service.js
CHANGED
|
@@ -84,9 +84,24 @@ function mandateCacheKey(userId, organizationId, mandateKey) {
|
|
|
84
84
|
return `${userId}:${organizationId}:${mandateKey}`;
|
|
85
85
|
}
|
|
86
86
|
function dropMandateCacheForOrgSwitch() {
|
|
87
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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,
|
package/dist/next/navigation.js
CHANGED
|
@@ -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:
|
|
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.
|
|
3
|
+
"version": "0.7.20",
|
|
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",
|
|
@@ -4684,9 +4684,9 @@
|
|
|
4684
4684
|
"@ai-matrx/alchemy": "latest",
|
|
4685
4685
|
"@ai-matrx/associations": "latest",
|
|
4686
4686
|
"@ai-matrx/browser-audio": "latest",
|
|
4687
|
-
"@ai-matrx/agents": "latest",
|
|
4688
4687
|
"@ai-matrx/canvas": "latest",
|
|
4689
4688
|
"@ai-matrx/content-ir": "latest",
|
|
4689
|
+
"@ai-matrx/agents": "latest",
|
|
4690
4690
|
"@ai-matrx/content-ir-react": "latest",
|
|
4691
4691
|
"@ai-matrx/data": "latest",
|
|
4692
4692
|
"@ai-matrx/design-system": "latest",
|
|
@@ -4694,12 +4694,12 @@
|
|
|
4694
4694
|
"@ai-matrx/icons": "latest",
|
|
4695
4695
|
"@ai-matrx/kit": "latest",
|
|
4696
4696
|
"@ai-matrx/media": "latest",
|
|
4697
|
-
"@ai-matrx/print": "latest",
|
|
4698
4697
|
"@ai-matrx/realtime": "latest",
|
|
4699
4698
|
"@ai-matrx/records": "latest",
|
|
4699
|
+
"@ai-matrx/records-ui": "latest",
|
|
4700
|
+
"@ai-matrx/print": "latest",
|
|
4700
4701
|
"@ai-matrx/rich-content": "latest",
|
|
4701
|
-
"@ai-matrx/rich-editor": "latest"
|
|
4702
|
-
"@ai-matrx/records-ui": "latest"
|
|
4702
|
+
"@ai-matrx/rich-editor": "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": "
|
|
4821
|
+
"gitHead": "204f87ddd70605c036eafd7a28c0e4566f50f2a3"
|
|
4822
4822
|
}
|