@adminide-stack/yantra-mobile 12.0.51 → 12.0.52-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/lib/components/KeyboardComposerDock.js +4 -3
  2. package/lib/components/KeyboardComposerDock.js.map +1 -1
  3. package/lib/features/attachments/buildGatewayMedia.js +22 -15
  4. package/lib/features/attachments/buildGatewayMedia.js.map +1 -1
  5. package/lib/features/attachments/uploadChatAttachments.js +6 -4
  6. package/lib/features/attachments/uploadChatAttachments.js.map +1 -1
  7. package/lib/features/attachments/useImageAttachments.js +63 -17
  8. package/lib/features/attachments/useImageAttachments.js.map +1 -1
  9. package/lib/features/chat/AskUserCard.js +367 -0
  10. package/lib/features/chat/AskUserCard.js.map +1 -0
  11. package/lib/features/chat/ChatTranscript.js +203 -98
  12. package/lib/features/chat/ChatTranscript.js.map +1 -1
  13. package/lib/features/chat/ToolTimeline.js +118 -0
  14. package/lib/features/chat/ToolTimeline.js.map +1 -0
  15. package/lib/features/chat/askUser.js +136 -0
  16. package/lib/features/chat/askUser.js.map +1 -0
  17. package/lib/features/chat/streamingMarkdown.js +7 -0
  18. package/lib/features/chat/streamingMarkdown.js.map +1 -0
  19. package/lib/features/chat/toolActivity.js +77 -0
  20. package/lib/features/chat/toolActivity.js.map +1 -0
  21. package/lib/features/chat/transcriptGroups.js +29 -0
  22. package/lib/features/chat/transcriptGroups.js.map +1 -0
  23. package/lib/hooks/mintSurfaceSystemToken.js +28 -14
  24. package/lib/hooks/mintSurfaceSystemToken.js.map +1 -1
  25. package/lib/hooks/systemTokenJwt.js +75 -0
  26. package/lib/hooks/systemTokenJwt.js.map +1 -0
  27. package/lib/hooks/useCdecliAutoConnect.js +116 -138
  28. package/lib/hooks/useCdecliAutoConnect.js.map +1 -1
  29. package/lib/hooks/useChatApi.js +43 -17
  30. package/lib/hooks/useChatApi.js.map +1 -1
  31. package/lib/hooks/useChatStream.js +61 -13
  32. package/lib/hooks/useChatStream.js.map +1 -1
  33. package/lib/hooks/useObtainSystemToken.js +121 -36
  34. package/lib/hooks/useObtainSystemToken.js.map +1 -1
  35. package/lib/screens/Chat/index.js +29 -5
  36. package/lib/screens/Chat/index.js.map +1 -1
  37. package/lib/screens/Home/HomeScreen.js +21 -6
  38. package/lib/screens/Home/HomeScreen.js.map +1 -1
  39. package/lib/screens/Home/components/ChatHistoryLanding.js +46 -16
  40. package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
  41. package/package.json +4 -4
@@ -0,0 +1,75 @@
1
+ function normalizeGraphqlUrl(url) {
2
+ return url.trim().replace(/\/$/, "");
3
+ }
4
+ function hostnameOf(url) {
5
+ try {
6
+ return new URL(url).hostname.toLowerCase();
7
+ } catch (e) {
8
+ return "";
9
+ }
10
+ }
11
+ function isLoopbackOrPrivateHost(hostname) {
12
+ const host = hostname.toLowerCase();
13
+ if (!host) return false;
14
+ if (host === "localhost" || host === "127.0.0.1" || host === "::1") return true;
15
+ if (host.endsWith(".local")) return true;
16
+ if (/^10\./.test(host)) return true;
17
+ if (/^192\.168\./.test(host)) return true;
18
+ if (/^172\.(1[6-9]|2\d|3[0-1])\./.test(host)) return true;
19
+ return false;
20
+ }
21
+ function isLoopbackOrPrivateUrl(url) {
22
+ return isLoopbackOrPrivateHost(hostnameOf(url));
23
+ }
24
+ function resolveCdecliAgentTokenGraphqlUrl(appGraphqlUrl, agentEndpoint, clusterGraphqlUrl) {
25
+ const app = normalizeGraphqlUrl(appGraphqlUrl);
26
+ const cluster = normalizeGraphqlUrl(clusterGraphqlUrl);
27
+ if (!isLoopbackOrPrivateUrl(app)) return app;
28
+ if (isLoopbackOrPrivateUrl(agentEndpoint)) return app;
29
+ return cluster || app;
30
+ }
31
+ function shouldInlineGatewayMediaForHostedAgent(appGraphqlUrl, agentEndpoint) {
32
+ return isLoopbackOrPrivateUrl(appGraphqlUrl) && !isLoopbackOrPrivateUrl(agentEndpoint);
33
+ }
34
+ function decodeJwtJson(part) {
35
+ if (!part) return null;
36
+ try {
37
+ const b64 = part.replace(/-/g, "+").replace(/_/g, "/");
38
+ const pad = b64 + "=".repeat((4 - b64.length % 4) % 4);
39
+ const json = typeof atob === "function" ? atob(pad) : Buffer.from(pad, "base64").toString("utf8");
40
+ return JSON.parse(json);
41
+ } catch (e) {
42
+ return null;
43
+ }
44
+ }
45
+ function getJwtExpirySec(token) {
46
+ const payload = decodeJwtJson(token.split(".")[1]);
47
+ return typeof (payload == null ? void 0 : payload.exp) === "number" ? payload.exp : null;
48
+ }
49
+ function isJwtExpired(token, skewSec = 60) {
50
+ const exp = getJwtExpirySec(token);
51
+ if (exp == null) return false;
52
+ return exp < Math.floor(Date.now() / 1e3) + skewSec;
53
+ }
54
+ function isSystemTokenRowStale(expiresAt) {
55
+ if (!expiresAt) return false;
56
+ const expiryMs = new Date(expiresAt).getTime();
57
+ if (!Number.isFinite(expiryMs)) return false;
58
+ return expiryMs - Date.now() < 36e5;
59
+ }
60
+ function isAgentJwtRejected(text) {
61
+ const t = text.trim();
62
+ if (!t) return false;
63
+ return /invalid HS256 signature/i.test(t) || /invalid RS256 signature/i.test(t) || /invalid token/i.test(t) || /Session creation failed:\s*HTTP 401/i.test(t) || /Failed to create session[\s\S]*HTTP 401/i.test(t);
64
+ }
65
+ function isMissingRs256KeyError(err) {
66
+ const message = err instanceof Error ? err.message : String(err != null ? err : "");
67
+ return /private key|SECRET_API_TOKEN_PRIVATE_KEY|RS256/i.test(message);
68
+ }
69
+ function isAlreadyHasSystemTokenError(err) {
70
+ const message = err instanceof Error ? err.message : String(err != null ? err : "");
71
+ return /already have a system token/i.test(message);
72
+ }
73
+ function isMissingVaultSecretError(message) {
74
+ return /secret not found|not found in vault/i.test(message);
75
+ }export{getJwtExpirySec,isAgentJwtRejected,isAlreadyHasSystemTokenError,isJwtExpired,isLoopbackOrPrivateHost,isLoopbackOrPrivateUrl,isMissingRs256KeyError,isMissingVaultSecretError,isSystemTokenRowStale,resolveCdecliAgentTokenGraphqlUrl,shouldInlineGatewayMediaForHostedAgent};//# sourceMappingURL=systemTokenJwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systemTokenJwt.js","sources":["../../src/hooks/systemTokenJwt.ts"],"sourcesContent":["/**\n * JWT helpers for the org-scoped system token (CDeCLI / surfaces).\n *\n * The gateway pill can show connected while cdecli-agent still 401s session\n * create: connect talks to Yantra GraphQL; the agent verifies the JWT itself.\n * A locally minted HS256 token will not verify on hosted cdecli-agent.\n */\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\nfunction hostnameOf(url: string): string {\n try {\n return new URL(url).hostname.toLowerCase();\n } catch {\n return '';\n }\n}\n\n/** Loopback, RFC1918, or Bonjour `.local` — a laptop/LAN GraphQL, not a cluster. */\nexport function isLoopbackOrPrivateHost(hostname: string): boolean {\n const host = hostname.toLowerCase();\n if (!host) return false;\n if (host === 'localhost' || host === '127.0.0.1' || host === '::1') return true;\n if (host.endsWith('.local')) return true;\n if (/^10\\./.test(host)) return true;\n if (/^192\\.168\\./.test(host)) return true;\n if (/^172\\.(1[6-9]|2\\d|3[0-1])\\./.test(host)) return true;\n return false;\n}\n\nexport function isLoopbackOrPrivateUrl(url: string): boolean {\n return isLoopbackOrPrivateHost(hostnameOf(url));\n}\n\n/**\n * GraphQL backend whose SECRET_API_TOKEN_* keys hosted cdecli-agent will use.\n *\n * Local GRAPHQL_URL signs with the laptop JWT secret; `cdecli-agent.cdebase.dev`\n * verifies with the cluster keys. Minting against CANVAS_BACKEND_GRAPHQL_URL\n * (ideback) is the same pairing canvas already uses for its index.\n */\nexport function resolveCdecliAgentTokenGraphqlUrl(\n appGraphqlUrl: string,\n agentEndpoint: string,\n clusterGraphqlUrl: string,\n): string {\n const app = normalizeGraphqlUrl(appGraphqlUrl);\n const cluster = normalizeGraphqlUrl(clusterGraphqlUrl);\n if (!isLoopbackOrPrivateUrl(app)) return app;\n if (isLoopbackOrPrivateUrl(agentEndpoint)) return app;\n return cluster || app;\n}\n\n/**\n * Local GraphQL mints S3/MinIO URLs the laptop can fetch; hosted cdecli-agent\n * cannot. Inline the bytes so the gateway re-hosts into the agent workspace\n * (web: \"saved the actual image locally — let me view it now\").\n */\nexport function shouldInlineGatewayMediaForHostedAgent(appGraphqlUrl: string, agentEndpoint: string): boolean {\n return isLoopbackOrPrivateUrl(appGraphqlUrl) && !isLoopbackOrPrivateUrl(agentEndpoint);\n}\n\nfunction decodeJwtJson(part: string | undefined): Record<string, unknown> | null {\n if (!part) return null;\n try {\n const b64 = part.replace(/-/g, '+').replace(/_/g, '/');\n const pad = b64 + '='.repeat((4 - (b64.length % 4)) % 4);\n const json = typeof atob === 'function' ? atob(pad) : Buffer.from(pad, 'base64').toString('utf8');\n return JSON.parse(json) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nexport function getJwtAlg(token: string): string | null {\n const header = decodeJwtJson(token.split('.')[0]);\n return typeof header?.alg === 'string' ? header.alg : null;\n}\n\nexport function getJwtExpirySec(token: string): number | null {\n const payload = decodeJwtJson(token.split('.')[1]);\n return typeof payload?.exp === 'number' ? payload.exp : null;\n}\n\nexport function isJwtExpired(token: string, skewSec = 60): boolean {\n const exp = getJwtExpirySec(token);\n if (exp == null) return false;\n return exp < Math.floor(Date.now() / 1000) + skewSec;\n}\n\n/** True when a GetUserSystemToken row is expired or within an hour of expiry. */\nexport function isSystemTokenRowStale(expiresAt: string | null | undefined): boolean {\n if (!expiresAt) return false;\n const expiryMs = new Date(expiresAt).getTime();\n if (!Number.isFinite(expiryMs)) return false;\n return expiryMs - Date.now() < 3600_000;\n}\n\n/** cdecli-agent rejected the system JWT (signature / 401), not a generic LLM error. */\nexport function isAgentJwtRejected(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n return (\n /invalid HS256 signature/i.test(t) ||\n /invalid RS256 signature/i.test(t) ||\n /invalid token/i.test(t) ||\n /Session creation failed:\\s*HTTP 401/i.test(t) ||\n /Failed to create session[\\s\\S]*HTTP 401/i.test(t)\n );\n}\n\nexport function isMissingRs256KeyError(err: unknown): boolean {\n const message = err instanceof Error ? err.message : String(err ?? '');\n return /private key|SECRET_API_TOKEN_PRIVATE_KEY|RS256/i.test(message);\n}\n\nexport function isAlreadyHasSystemTokenError(err: unknown): boolean {\n const message = err instanceof Error ? err.message : String(err ?? '');\n return /already have a system token/i.test(message);\n}\n\nexport function isMissingVaultSecretError(message: string): boolean {\n return /secret not found|not found in vault/i.test(message);\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,4 @@
1
- import {useState,useRef,useCallback,useEffect}from'react';import {useGatewayConnectMutation,useGatewayDisconnectMutation,useCreateSecretApiTokenMutation,useRevealSecretApiTokenMutation,useGetUserSystemTokenQuery}from'common/graphql';import {SecretApiTokenAlgorithm}from'common';import {config}from'../config/env-config.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';var __defProp = Object.defineProperty;
2
- var __defProps = Object.defineProperties;
3
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
1
+ import {useState,useRef,useMemo,useCallback,useEffect}from'react';import {useGatewayConnectMutation}from'common/graphql';import {config}from'../config/env-config.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {mintSurfaceSystemToken}from'./mintSurfaceSystemToken.js';import {useObtainSystemToken}from'./useObtainSystemToken.js';import {resolveCdecliAgentTokenGraphqlUrl}from'./systemTokenJwt.js';var __defProp = Object.defineProperty;
4
2
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
3
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
4
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -16,9 +14,10 @@ var __spreadValues = (a, b) => {
16
14
  }
17
15
  return a;
18
16
  };
19
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
- const SYSTEM_TOKEN_ALGORITHM = SecretApiTokenAlgorithm.Hs256;
21
17
  const CHANNEL_TYPE = "cdecli-serve";
18
+ function normalizeGraphqlUrl(url) {
19
+ return url.trim().replace(/\/$/, "");
20
+ }
22
21
  function useCdecliAutoConnect(isSelected, channelId) {
23
22
  const [status, setStatus] = useState("idle");
24
23
  const [error, setError] = useState(null);
@@ -27,90 +26,88 @@ function useCdecliAutoConnect(isSelected, channelId) {
27
26
  const connectingRef = useRef(false);
28
27
  const connectedRef = useRef(false);
29
28
  const {
30
- orgName,
31
- projectId,
32
- tagId,
33
29
  accountUserId,
34
30
  loading: prerequisitesLoading
35
31
  } = usePrerequisiteIds();
36
32
  const accountId = accountUserId != null ? accountUserId : "default";
37
33
  const prevChannelIdRef = useRef(channelId);
38
34
  const [connectMutation] = useGatewayConnectMutation();
39
- const [disconnectMutation] = useGatewayDisconnectMutation();
40
- const [createTokenMutation] = useCreateSecretApiTokenMutation();
41
- const [revealTokenMutation] = useRevealSecretApiTokenMutation();
35
+ const endpoint = (config.CDECLI_AGENT_ENDPOINT || "https://cdecli-agent.cdebase.dev").trim();
36
+ const tokenGraphqlUrl = useMemo(() => resolveCdecliAgentTokenGraphqlUrl(config.GRAPHQL_URL, endpoint, config.CANVAS_BACKEND_GRAPHQL_URL), [endpoint]);
37
+ const mintOnCluster = normalizeGraphqlUrl(tokenGraphqlUrl) !== normalizeGraphqlUrl(config.GRAPHQL_URL);
42
38
  const {
43
- data: systemTokenData,
44
- loading: systemTokenLoading,
45
- refetch: refetchSystemToken
46
- } = useGetUserSystemTokenQuery({
47
- skip: !isSelected,
48
- fetchPolicy: "network-only"
49
- });
50
- const isAlreadyHasSystemTokenError = (err) => {
51
- const message = err instanceof Error ? err.message : String(err != null ? err : "");
52
- return /already have a system token/i.test(message);
53
- };
54
- const obtainSystemToken = useCallback(async () => {
55
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
56
- try {
57
- let tokenId = (_b = (_a = systemTokenData == null ? void 0 : systemTokenData.getUserSystemToken) == null ? void 0 : _a.id) != null ? _b : null;
58
- if (!tokenId) {
59
- if (!orgName || !projectId) {
60
- const refetchResult = await refetchSystemToken().catch(() => null);
61
- tokenId = (_e = (_d = (_c = refetchResult == null ? void 0 : refetchResult.data) == null ? void 0 : _c.getUserSystemToken) == null ? void 0 : _d.id) != null ? _e : null;
62
- if (!tokenId) return null;
63
- } else {
64
- try {
65
- const {
66
- data: createData
67
- } = await createTokenMutation({
68
- variables: {
69
- input: {
70
- name: "System Token",
71
- expiryInSeconds: 31536e3,
72
- isSystemToken: true,
73
- environmentTagId: tagId,
74
- orgName: orgName || "default",
75
- projectId: projectId || "default",
76
- algorithm: SYSTEM_TOKEN_ALGORITHM
77
- }
78
- }
79
- });
80
- tokenId = (_g = (_f = createData == null ? void 0 : createData.createSecretApiToken) == null ? void 0 : _f.id) != null ? _g : null;
81
- await refetchSystemToken();
82
- } catch (createErr) {
83
- if (!isAlreadyHasSystemTokenError(createErr)) throw createErr;
84
- const refetchResult = await refetchSystemToken().catch(() => null);
85
- tokenId = (_j = (_i = (_h = refetchResult == null ? void 0 : refetchResult.data) == null ? void 0 : _h.getUserSystemToken) == null ? void 0 : _i.id) != null ? _j : null;
86
- if (!tokenId) {
87
- console.warn("[useCdecliAutoConnect] System token already exists but refetch returned no id; retrying");
88
- return null;
89
- }
90
- }
91
- }
39
+ loading: localTokenLoading,
40
+ obtainSystemToken,
41
+ regenerateSystemToken
42
+ } = useObtainSystemToken(isSelected && !mintOnCluster);
43
+ const obtainTokenForAgent = useCallback(async (discardExistingRow = false) => {
44
+ var _a;
45
+ if (mintOnCluster) {
46
+ try {
47
+ const minted = await mintSurfaceSystemToken({
48
+ graphqlUrl: tokenGraphqlUrl,
49
+ discoverScope: true,
50
+ forceRemint: discardExistingRow
51
+ });
52
+ return ((_a = minted.token) == null ? void 0 : _a.trim()) || null;
53
+ } catch (err) {
54
+ console.error("[useCdecliAutoConnect] cluster system-token mint failed:", err);
55
+ return null;
92
56
  }
93
- if (!tokenId) return null;
94
- const {
95
- data: revealData
96
- } = await revealTokenMutation({
97
- variables: {
98
- id: tokenId
99
- }
100
- });
101
- return (revealData == null ? void 0 : revealData.revealSecretApiToken) || null;
102
- } catch (err) {
103
- console.error("[useCdecliAutoConnect] obtainSystemToken error:", err);
104
- return null;
105
57
  }
106
- }, [systemTokenData, createTokenMutation, revealTokenMutation, refetchSystemToken, orgName, projectId, tagId]);
58
+ return discardExistingRow ? regenerateSystemToken() : obtainSystemToken();
59
+ }, [mintOnCluster, tokenGraphqlUrl, obtainSystemToken, regenerateSystemToken]);
60
+ const applyConnectResult = useCallback((data) => {
61
+ var _a;
62
+ const result = data == null ? void 0 : data.gatewayConnect;
63
+ if (result == null ? void 0 : result.persistenceMode) {
64
+ setPersistenceMode(result.persistenceMode);
65
+ }
66
+ if ((result == null ? void 0 : result.state) === "ERROR") {
67
+ setStatus("error");
68
+ setError((_a = result.lastError) != null ? _a : "Failed to connect to CDeCLI agent");
69
+ connectedRef.current = false;
70
+ return false;
71
+ }
72
+ setStatus("connected");
73
+ connectedRef.current = true;
74
+ return true;
75
+ }, []);
76
+ const runConnect = useCallback(async (opts) => {
77
+ const {
78
+ data
79
+ } = await connectMutation({
80
+ variables: {
81
+ input: {
82
+ channelType: CHANNEL_TYPE,
83
+ accountId,
84
+ options: __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
85
+ endpoint
86
+ }, opts.token ? {
87
+ token: opts.token
88
+ } : {}), channelId ? {
89
+ chatId: channelId
90
+ } : {}), opts.skill ? {
91
+ skill: opts.skill
92
+ } : {}), opts.skillId ? {
93
+ skillId: opts.skillId
94
+ } : {}), opts.model ? {
95
+ model: opts.model
96
+ } : {}), opts.systemPrompt ? {
97
+ systemPrompt: opts.systemPrompt
98
+ } : {})
99
+ }
100
+ }
101
+ });
102
+ return applyConnectResult(data);
103
+ }, [connectMutation, accountId, endpoint, channelId, applyConnectResult]);
107
104
  useEffect(() => {
108
105
  if (!isSelected) {
109
106
  connectingRef.current = false;
110
107
  return;
111
108
  }
112
109
  if (prerequisitesLoading || !accountUserId) return;
113
- if (systemTokenLoading) return;
110
+ if (!mintOnCluster && localTokenLoading) return;
114
111
  let isSilentReconnect = false;
115
112
  if (connectedRef.current || connectingRef.current) {
116
113
  if (channelId && prevChannelIdRef.current !== channelId) {
@@ -122,14 +119,18 @@ function useCdecliAutoConnect(isSelected, channelId) {
122
119
  }
123
120
  }
124
121
  const doConnect = async () => {
125
- var _a;
126
122
  connectingRef.current = true;
127
123
  if (!isSilentReconnect) setStatus("connecting");
128
124
  setError(null);
129
125
  try {
130
- const endpoint = config.CDECLI_AGENT_ENDPOINT || "https://cdecli-agent.cdebase.dev";
131
- const token = await obtainSystemToken();
126
+ const token = await obtainTokenForAgent(false);
132
127
  if (!token) {
128
+ if (mintOnCluster) {
129
+ setStatus("error");
130
+ setError("Could not mint a system token for the hosted CDeCLI agent.");
131
+ connectedRef.current = false;
132
+ return;
133
+ }
133
134
  console.warn("[useCdecliAutoConnect] No system token, connecting without auth");
134
135
  }
135
136
  const {
@@ -140,7 +141,7 @@ function useCdecliAutoConnect(isSelected, channelId) {
140
141
  channelType: CHANNEL_TYPE,
141
142
  accountId,
142
143
  options: __spreadValues(__spreadValues({
143
- endpoint: endpoint.trim()
144
+ endpoint
144
145
  }, token ? {
145
146
  token
146
147
  } : {}), channelId ? {
@@ -149,22 +150,8 @@ function useCdecliAutoConnect(isSelected, channelId) {
149
150
  }
150
151
  }
151
152
  });
152
- const result = data == null ? void 0 : data.gatewayConnect;
153
- console.log("[useCdecliAutoConnect] connectMutation result (account=%s):", accountId, JSON.stringify(result));
154
- if (result == null ? void 0 : result.persistenceMode) {
155
- setPersistenceMode(result.persistenceMode);
156
- }
157
- if ((result == null ? void 0 : result.state) === "CONNECTED" || (result == null ? void 0 : result.connected)) {
158
- setStatus("connected");
159
- connectedRef.current = true;
160
- } else if ((result == null ? void 0 : result.state) === "ERROR") {
161
- setStatus("error");
162
- setError((_a = result.lastError) != null ? _a : "Failed to connect to CDeCLI agent");
163
- connectedRef.current = false;
164
- } else {
165
- setStatus("connected");
166
- connectedRef.current = true;
167
- }
153
+ console.log("[useCdecliAutoConnect] connectMutation result (account=%s mintOnCluster=%s):", accountId, mintOnCluster, JSON.stringify(data));
154
+ applyConnectResult(data);
168
155
  } catch (err) {
169
156
  const msg = err instanceof Error ? err.message : String(err);
170
157
  console.error("[useCdecliAutoConnect] connect error:", msg);
@@ -176,62 +163,52 @@ function useCdecliAutoConnect(isSelected, channelId) {
176
163
  }
177
164
  };
178
165
  doConnect();
179
- }, [isSelected, obtainSystemToken, connectMutation, accountId, prerequisitesLoading, accountUserId, channelId, systemTokenLoading]);
166
+ }, [isSelected, obtainTokenForAgent, connectMutation, applyConnectResult, accountId, prerequisitesLoading, accountUserId, channelId, localTokenLoading, mintOnCluster, endpoint]);
180
167
  const connectWithSkill = useCallback(async (skill, model, systemPrompt, skillId) => {
181
168
  var _a;
182
169
  console.log("[useCdecliAutoConnect] connectWithSkill called with skill=%s skillId=%s model=%s systemPrompt=%d chars", skill, skillId != null ? skillId : "", model, (_a = systemPrompt == null ? void 0 : systemPrompt.length) != null ? _a : 0);
183
170
  setStatus("connecting");
184
171
  setError(null);
185
172
  try {
186
- const endpoint = config.CDECLI_AGENT_ENDPOINT || "https://cdecli-agent.cdebase.dev";
187
- const token = await obtainSystemToken();
188
- console.log("[useCdecliAutoConnect] connectWithSkill sending mutation with skill=%s", skill);
189
- const {
190
- data
191
- } = await connectMutation({
192
- variables: {
193
- input: {
194
- channelType: CHANNEL_TYPE,
195
- accountId,
196
- options: __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadProps(__spreadValues({
197
- endpoint: endpoint.trim()
198
- }, token ? {
199
- token
200
- } : {}), {
201
- skill
202
- }), skillId ? {
203
- skillId
204
- } : {}), model ? {
205
- model
206
- } : {}), systemPrompt ? {
207
- systemPrompt
208
- } : {}), channelId ? {
209
- chatId: channelId
210
- } : {})
211
- }
212
- }
173
+ const token = await obtainTokenForAgent(false);
174
+ await runConnect({
175
+ token,
176
+ skill,
177
+ skillId,
178
+ model,
179
+ systemPrompt
213
180
  });
214
- console.log("[useCdecliAutoConnect] connectWithSkill result:", JSON.stringify(data == null ? void 0 : data.gatewayConnect));
215
- const result = data == null ? void 0 : data.gatewayConnect;
216
- if (result == null ? void 0 : result.persistenceMode) {
217
- setPersistenceMode(result.persistenceMode);
218
- }
219
- if ((result == null ? void 0 : result.state) === "CONNECTED" || (result == null ? void 0 : result.connected)) {
220
- setStatus("connected");
221
- connectedRef.current = true;
222
- setActiveSkill(skill);
223
- } else {
224
- setStatus("connected");
225
- connectedRef.current = true;
226
- setActiveSkill(skill);
227
- }
181
+ setActiveSkill(skill);
228
182
  } catch (err) {
229
183
  const msg = err instanceof Error ? err.message : String(err);
230
184
  console.error("[useCdecliAutoConnect] connectWithSkill error:", msg);
231
185
  setStatus("error");
232
186
  setError(msg);
233
187
  }
234
- }, [connectMutation, obtainSystemToken, accountId, channelId]);
188
+ }, [obtainTokenForAgent, runConnect]);
189
+ const recoverAgentAuth = useCallback(async () => {
190
+ try {
191
+ const token = await obtainTokenForAgent(true);
192
+ if (!token) {
193
+ setStatus("error");
194
+ setError("Could not remint the CDeCLI system token. Sign in again, then retry.");
195
+ connectedRef.current = false;
196
+ return false;
197
+ }
198
+ const ok = await runConnect({
199
+ token
200
+ });
201
+ if (ok) setError(null);
202
+ return ok;
203
+ } catch (err) {
204
+ const msg = err instanceof Error ? err.message : String(err);
205
+ console.error("[useCdecliAutoConnect] recoverAgentAuth error:", msg);
206
+ setStatus("error");
207
+ setError(msg);
208
+ connectedRef.current = false;
209
+ return false;
210
+ }
211
+ }, [obtainTokenForAgent, runConnect]);
235
212
  return {
236
213
  channelConnected: status === "connected",
237
214
  status,
@@ -239,6 +216,7 @@ function useCdecliAutoConnect(isSelected, channelId) {
239
216
  persistenceMode,
240
217
  activeSkill,
241
218
  accountId,
242
- connectWithSkill
219
+ connectWithSkill,
220
+ recoverAgentAuth
243
221
  };
244
222
  }export{useCdecliAutoConnect};//# sourceMappingURL=useCdecliAutoConnect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCdecliAutoConnect.js","sources":["../../src/hooks/useCdecliAutoConnect.ts"],"sourcesContent":["/**\n * Auto-connects the cdecli-serve messenger-gateway channel when the CDeCLI\n * gateway is selected from the dropdown. Obtains/creates a system token and\n * calls the `gatewayConnect` mutation so the backend provider has a live\n * session with the cdecli-agent endpoint.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n useGatewayConnectMutation,\n useGatewayDisconnectMutation,\n useGetUserSystemTokenQuery,\n useCreateSecretApiTokenMutation,\n useRevealSecretApiTokenMutation,\n} from 'common/graphql';\nimport { SecretApiTokenAlgorithm } from 'common';\nimport { config } from '../config/env-config';\nimport { usePrerequisiteIds } from './usePrerequisiteIds';\n\n/** Local backend ships RS256 public key but often no private key; HS256 uses dev JWT secret. */\nconst SYSTEM_TOKEN_ALGORITHM = SecretApiTokenAlgorithm.Hs256;\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport type CdecliAutoConnectStatus = 'idle' | 'connecting' | 'connected' | 'error';\n\nexport interface UseCdecliAutoConnectResult {\n /** Whether the backend cdecli-serve channel is connected. */\n channelConnected: boolean;\n /** Current connection status. */\n status: CdecliAutoConnectStatus;\n /** Error message if status is 'error'. */\n error: string | null;\n /** Persistence mode reported by the backend provider ('backend' | 'frontend'). */\n persistenceMode: 'backend' | 'frontend' | undefined;\n /** Active skill name on the current CDeCLI session (null if none). */\n activeSkill: string | null;\n /** Per-user accountId used for this gateway connection. */\n accountId: string;\n /** Reconnect the CDeCLI session with a specific skill pre-activated. */\n connectWithSkill: (skill: string, model?: string, systemPrompt?: string, skillId?: string) => Promise<void>;\n}\n\n// ─── Constants ───────────────────────────────────────────────────\n\nconst CHANNEL_TYPE = 'cdecli-serve';\n\n// ─── Hook ────────────────────────────────────────────────────────\n\n/**\n * When `isSelected` is true, auto-connects the cdecli-serve channel on the\n * messenger-gateway backend using the hosted endpoint and a system token.\n */\nexport function useCdecliAutoConnect(isSelected: boolean, channelId?: string): UseCdecliAutoConnectResult {\n const [status, setStatus] = useState<CdecliAutoConnectStatus>('idle');\n const [error, setError] = useState<string | null>(null);\n const [persistenceMode, setPersistenceMode] = useState<'backend' | 'frontend' | undefined>(undefined);\n const [activeSkill, setActiveSkill] = useState<string | null>(null);\n const connectingRef = useRef(false);\n const connectedRef = useRef(false);\n\n const { orgName, projectId, tagId, accountUserId, loading: prerequisitesLoading } = usePrerequisiteIds();\n\n // Per-user accountId so each user gets their own cdecli-agent session.\n // Wait for the profile query to load before connecting — otherwise all\n // users fall back to 'default' and share a single cdecli-agent session.\n const accountId = accountUserId ?? 'default';\n\n // Track the previous channelId so we can reconnect when the user opens a new chat.\n const prevChannelIdRef = useRef<string | undefined>(channelId);\n\n const [connectMutation] = useGatewayConnectMutation();\n const [disconnectMutation] = useGatewayDisconnectMutation();\n const [createTokenMutation] = useCreateSecretApiTokenMutation();\n const [revealTokenMutation] = useRevealSecretApiTokenMutation();\n const {\n data: systemTokenData,\n loading: systemTokenLoading,\n refetch: refetchSystemToken,\n } = useGetUserSystemTokenQuery({\n skip: !isSelected,\n fetchPolicy: 'network-only',\n });\n\n /** True when the backend rejects createSecretApiToken because the user already has one. */\n const isAlreadyHasSystemTokenError = (err: unknown): boolean => {\n const message = err instanceof Error ? err.message : String(err ?? '');\n return /already have a system token/i.test(message);\n };\n\n const obtainSystemToken = useCallback(async (): Promise<string | null> => {\n try {\n let tokenId: string | null = systemTokenData?.getUserSystemToken?.id ?? null;\n\n /**\n * Race recovery: if the GetUserSystemToken query hasn't returned yet (or returned\n * stale data) we'll fall through to createSecretApiToken. The backend rejects that\n * with \"you already have a system token\" — catch it, refetch, and use the existing\n * token id instead of falling back to an unauthenticated connection.\n */\n if (!tokenId) {\n if (!orgName || !projectId) {\n const refetchResult = await refetchSystemToken().catch(() => null);\n tokenId = refetchResult?.data?.getUserSystemToken?.id ?? null;\n if (!tokenId) return null;\n } else {\n try {\n const { data: createData } = await createTokenMutation({\n variables: {\n input: {\n name: 'System Token',\n expiryInSeconds: 31536000,\n isSystemToken: true,\n environmentTagId: tagId,\n orgName: orgName || 'default',\n projectId: projectId || 'default',\n algorithm: SYSTEM_TOKEN_ALGORITHM,\n },\n },\n });\n tokenId = createData?.createSecretApiToken?.id ?? null;\n await refetchSystemToken();\n } catch (createErr) {\n if (!isAlreadyHasSystemTokenError(createErr)) throw createErr;\n const refetchResult = await refetchSystemToken().catch(() => null);\n tokenId = refetchResult?.data?.getUserSystemToken?.id ?? null;\n if (!tokenId) {\n console.warn(\n '[useCdecliAutoConnect] System token already exists but refetch returned no id; retrying',\n );\n return null;\n }\n }\n }\n }\n\n if (!tokenId) return null;\n\n const { data: revealData } = await revealTokenMutation({\n variables: { id: tokenId },\n });\n return (revealData?.revealSecretApiToken as string) || null;\n } catch (err) {\n console.error('[useCdecliAutoConnect] obtainSystemToken error:', err);\n return null;\n }\n }, [systemTokenData, createTokenMutation, revealTokenMutation, refetchSystemToken, orgName, projectId, tagId]);\n\n // Auto-connect when CDeCLI gateway is selected\n useEffect(() => {\n if (!isSelected) {\n // Reset when deselected, but don't disconnect — keep the session alive\n // so switching back is instant.\n connectingRef.current = false;\n return;\n }\n\n // Wait until the user profile has loaded so accountId is the real\n // user id instead of the 'default' fallback.\n if (prerequisitesLoading || !accountUserId) return;\n\n /**\n * Wait for the GetUserSystemToken query to resolve before we connect — otherwise we\n * race the network and try to create a duplicate token, which the backend rejects with\n * \"you already have a system token\" and we fall back to an unauthenticated session.\n */\n if (systemTokenLoading) return;\n\n /**\n * If we're already connected and just switching channels, the gateway needs a reconnect\n * (the session is bound server-side to chatId), but the user already had a working\n * connection. Skip flipping the visible status to 'connecting' so the toolbar pill\n * doesn't briefly show \"Checking...\" every time someone taps a history row.\n */\n let isSilentReconnect = false;\n if (connectedRef.current || connectingRef.current) {\n if (channelId && prevChannelIdRef.current !== channelId) {\n isSilentReconnect = connectedRef.current;\n prevChannelIdRef.current = channelId;\n connectedRef.current = false;\n } else {\n return;\n }\n }\n\n const doConnect = async () => {\n connectingRef.current = true;\n if (!isSilentReconnect) setStatus('connecting');\n setError(null);\n\n try {\n const endpoint = config.CDECLI_AGENT_ENDPOINT || 'https://cdecli-agent.cdebase.dev';\n const token = await obtainSystemToken();\n if (!token) {\n console.warn('[useCdecliAutoConnect] No system token, connecting without auth');\n }\n\n const { data } = await connectMutation({\n variables: {\n input: {\n channelType: CHANNEL_TYPE,\n accountId,\n options: {\n endpoint: endpoint.trim(),\n ...(token ? { token } : {}),\n ...(channelId ? { chatId: channelId } : {}),\n },\n },\n },\n });\n\n const result = data?.gatewayConnect as\n | ((typeof data)['gatewayConnect'] & { persistenceMode?: string })\n | undefined;\n console.log(\n '[useCdecliAutoConnect] connectMutation result (account=%s):',\n accountId,\n JSON.stringify(result),\n );\n if (result?.persistenceMode) {\n setPersistenceMode(result.persistenceMode as 'backend' | 'frontend');\n }\n if (result?.state === 'CONNECTED' || result?.connected) {\n setStatus('connected');\n connectedRef.current = true;\n } else if (result?.state === 'ERROR') {\n setStatus('error');\n setError(result.lastError ?? 'Failed to connect to CDeCLI agent');\n connectedRef.current = false;\n } else {\n // Assume connecting — the subscription will push the final state\n setStatus('connected');\n connectedRef.current = true;\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] connect error:', msg);\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n } finally {\n connectingRef.current = false;\n }\n };\n\n doConnect();\n }, [\n isSelected,\n obtainSystemToken,\n connectMutation,\n accountId,\n prerequisitesLoading,\n accountUserId,\n channelId,\n systemTokenLoading,\n ]);\n\n // Keep the backend session alive across page refreshes and SPA navigations.\n // The backend CdecliServeProvider.connect() will reuse the existing session\n // if the endpoint hasn't changed, preserving conversation context.\n // Only disconnect when the user explicitly switches to a different gateway.\n\n /** Reconnect the CDeCLI session with a specific skill pre-activated. */\n const connectWithSkill = useCallback(\n async (skill: string, model?: string, systemPrompt?: string, skillId?: string) => {\n console.log(\n '[useCdecliAutoConnect] connectWithSkill called with skill=%s skillId=%s model=%s systemPrompt=%d chars',\n skill,\n skillId ?? '',\n model,\n systemPrompt?.length ?? 0,\n );\n setStatus('connecting');\n setError(null);\n\n try {\n const endpoint = config.CDECLI_AGENT_ENDPOINT || 'https://cdecli-agent.cdebase.dev';\n const token = await obtainSystemToken();\n\n console.log('[useCdecliAutoConnect] connectWithSkill sending mutation with skill=%s', skill);\n const { data } = await connectMutation({\n variables: {\n input: {\n channelType: CHANNEL_TYPE,\n accountId,\n options: {\n endpoint: endpoint.trim(),\n ...(token ? { token } : {}),\n skill,\n ...(skillId ? { skillId } : {}),\n ...(model ? { model } : {}),\n ...(systemPrompt ? { systemPrompt } : {}),\n ...(channelId ? { chatId: channelId } : {}),\n },\n },\n },\n });\n\n console.log('[useCdecliAutoConnect] connectWithSkill result:', JSON.stringify(data?.gatewayConnect));\n const result = data?.gatewayConnect as\n | ((typeof data)['gatewayConnect'] & { persistenceMode?: string })\n | undefined;\n if (result?.persistenceMode) {\n setPersistenceMode(result.persistenceMode as 'backend' | 'frontend');\n }\n if (result?.state === 'CONNECTED' || result?.connected) {\n setStatus('connected');\n connectedRef.current = true;\n setActiveSkill(skill);\n } else {\n setStatus('connected');\n connectedRef.current = true;\n setActiveSkill(skill);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] connectWithSkill error:', msg);\n setStatus('error');\n setError(msg);\n }\n },\n [connectMutation, obtainSystemToken, accountId, channelId],\n );\n\n return {\n channelConnected: status === 'connected',\n status,\n error,\n persistenceMode,\n activeSkill,\n accountId,\n connectWithSkill,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useCdecliAutoConnect.js","sources":["../../src/hooks/useCdecliAutoConnect.ts"],"sourcesContent":["/**\n * Auto-connects the cdecli-serve messenger-gateway channel when the CDeCLI\n * gateway is selected from the dropdown. Obtains/creates a system token and\n * calls the `gatewayConnect` mutation so the backend provider has a live\n * session with the cdecli-agent endpoint.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useGatewayConnectMutation } from 'common/graphql';\nimport { config } from '../config/env-config';\nimport { usePrerequisiteIds } from './usePrerequisiteIds';\nimport { mintSurfaceSystemToken } from './mintSurfaceSystemToken';\nimport { useObtainSystemToken } from './useObtainSystemToken';\nimport { resolveCdecliAgentTokenGraphqlUrl } from './systemTokenJwt';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport type CdecliAutoConnectStatus = 'idle' | 'connecting' | 'connected' | 'error';\n\nexport interface UseCdecliAutoConnectResult {\n /** Whether the backend cdecli-serve channel is connected. */\n channelConnected: boolean;\n /** Current connection status. */\n status: CdecliAutoConnectStatus;\n /** Error message if status is 'error'. */\n error: string | null;\n /** Persistence mode reported by the backend provider ('backend' | 'frontend'). */\n persistenceMode: 'backend' | 'frontend' | undefined;\n /** Active skill name on the current CDeCLI session (null if none). */\n activeSkill: string | null;\n /** Per-user accountId used for this gateway connection. */\n accountId: string;\n /** Reconnect the CDeCLI session with a specific skill pre-activated. */\n connectWithSkill: (skill: string, model?: string, systemPrompt?: string, skillId?: string) => Promise<void>;\n /**\n * Revoke the dead system JWT, remint against the agent-trusted backend, and\n * reconnect. Used when cdecli-agent 401s session create (invalid HS256 signature).\n */\n recoverAgentAuth: () => Promise<boolean>;\n}\n\n// ─── Constants ───────────────────────────────────────────────────\n\nconst CHANNEL_TYPE = 'cdecli-serve';\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\n// ─── Hook ────────────────────────────────────────────────────────\n\n/**\n * When `isSelected` is true, auto-connects the cdecli-serve channel on the\n * messenger-gateway backend using the hosted endpoint and a system token.\n */\nexport function useCdecliAutoConnect(isSelected: boolean, channelId?: string): UseCdecliAutoConnectResult {\n const [status, setStatus] = useState<CdecliAutoConnectStatus>('idle');\n const [error, setError] = useState<string | null>(null);\n const [persistenceMode, setPersistenceMode] = useState<'backend' | 'frontend' | undefined>(undefined);\n const [activeSkill, setActiveSkill] = useState<string | null>(null);\n const connectingRef = useRef(false);\n const connectedRef = useRef(false);\n\n const { accountUserId, loading: prerequisitesLoading } = usePrerequisiteIds();\n\n // Per-user accountId so each user gets their own cdecli-agent session.\n // Wait for the profile query to load before connecting — otherwise all\n // users fall back to 'default' and share a single cdecli-agent session.\n const accountId = accountUserId ?? 'default';\n\n const prevChannelIdRef = useRef<string | undefined>(channelId);\n\n const [connectMutation] = useGatewayConnectMutation();\n\n const endpoint = (config.CDECLI_AGENT_ENDPOINT || 'https://cdecli-agent.cdebase.dev').trim();\n const tokenGraphqlUrl = useMemo(\n () => resolveCdecliAgentTokenGraphqlUrl(config.GRAPHQL_URL, endpoint, config.CANVAS_BACKEND_GRAPHQL_URL),\n [endpoint],\n );\n const mintOnCluster = normalizeGraphqlUrl(tokenGraphqlUrl) !== normalizeGraphqlUrl(config.GRAPHQL_URL);\n\n const {\n loading: localTokenLoading,\n obtainSystemToken,\n regenerateSystemToken,\n } = useObtainSystemToken(isSelected && !mintOnCluster);\n\n const obtainTokenForAgent = useCallback(\n async (discardExistingRow = false): Promise<string | null> => {\n if (mintOnCluster) {\n try {\n const minted = await mintSurfaceSystemToken({\n graphqlUrl: tokenGraphqlUrl,\n discoverScope: true,\n forceRemint: discardExistingRow,\n });\n return minted.token?.trim() || null;\n } catch (err) {\n console.error('[useCdecliAutoConnect] cluster system-token mint failed:', err);\n return null;\n }\n }\n return discardExistingRow ? regenerateSystemToken() : obtainSystemToken();\n },\n [mintOnCluster, tokenGraphqlUrl, obtainSystemToken, regenerateSystemToken],\n );\n\n const applyConnectResult = useCallback((data: { gatewayConnect?: unknown } | null | undefined) => {\n const result = data?.gatewayConnect as\n | {\n persistenceMode?: string | null;\n state?: string | null;\n connected?: boolean | null;\n lastError?: string | null;\n }\n | undefined;\n if (result?.persistenceMode) {\n setPersistenceMode(result.persistenceMode as 'backend' | 'frontend');\n }\n if (result?.state === 'ERROR') {\n setStatus('error');\n setError(result.lastError ?? 'Failed to connect to CDeCLI agent');\n connectedRef.current = false;\n return false;\n }\n setStatus('connected');\n connectedRef.current = true;\n return true;\n }, []);\n\n const runConnect = useCallback(\n async (opts: {\n token: string | null;\n skill?: string;\n skillId?: string;\n model?: string;\n systemPrompt?: string;\n }) => {\n const { data } = await connectMutation({\n variables: {\n input: {\n channelType: CHANNEL_TYPE,\n accountId,\n options: {\n endpoint,\n ...(opts.token ? { token: opts.token } : {}),\n ...(channelId ? { chatId: channelId } : {}),\n ...(opts.skill ? { skill: opts.skill } : {}),\n ...(opts.skillId ? { skillId: opts.skillId } : {}),\n ...(opts.model ? { model: opts.model } : {}),\n ...(opts.systemPrompt ? { systemPrompt: opts.systemPrompt } : {}),\n },\n },\n },\n });\n return applyConnectResult(data);\n },\n [connectMutation, accountId, endpoint, channelId, applyConnectResult],\n );\n\n // Auto-connect when CDeCLI gateway is selected\n useEffect(() => {\n if (!isSelected) {\n connectingRef.current = false;\n return;\n }\n\n if (prerequisitesLoading || !accountUserId) return;\n\n if (!mintOnCluster && localTokenLoading) return;\n\n let isSilentReconnect = false;\n if (connectedRef.current || connectingRef.current) {\n if (channelId && prevChannelIdRef.current !== channelId) {\n isSilentReconnect = connectedRef.current;\n prevChannelIdRef.current = channelId;\n connectedRef.current = false;\n } else {\n return;\n }\n }\n\n const doConnect = async () => {\n connectingRef.current = true;\n if (!isSilentReconnect) setStatus('connecting');\n setError(null);\n\n try {\n const token = await obtainTokenForAgent(false);\n if (!token) {\n if (mintOnCluster) {\n setStatus('error');\n setError('Could not mint a system token for the hosted CDeCLI agent.');\n connectedRef.current = false;\n return;\n }\n console.warn('[useCdecliAutoConnect] No system token, connecting without auth');\n }\n\n const { data } = await connectMutation({\n variables: {\n input: {\n channelType: CHANNEL_TYPE,\n accountId,\n options: {\n endpoint,\n ...(token ? { token } : {}),\n ...(channelId ? { chatId: channelId } : {}),\n },\n },\n },\n });\n\n console.log(\n '[useCdecliAutoConnect] connectMutation result (account=%s mintOnCluster=%s):',\n accountId,\n mintOnCluster,\n JSON.stringify(data),\n );\n applyConnectResult(data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] connect error:', msg);\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n } finally {\n connectingRef.current = false;\n }\n };\n\n doConnect();\n }, [\n isSelected,\n obtainTokenForAgent,\n connectMutation,\n applyConnectResult,\n accountId,\n prerequisitesLoading,\n accountUserId,\n channelId,\n localTokenLoading,\n mintOnCluster,\n endpoint,\n ]);\n\n const connectWithSkill = useCallback(\n async (skill: string, model?: string, systemPrompt?: string, skillId?: string) => {\n console.log(\n '[useCdecliAutoConnect] connectWithSkill called with skill=%s skillId=%s model=%s systemPrompt=%d chars',\n skill,\n skillId ?? '',\n model,\n systemPrompt?.length ?? 0,\n );\n setStatus('connecting');\n setError(null);\n\n try {\n const token = await obtainTokenForAgent(false);\n await runConnect({ token, skill, skillId, model, systemPrompt });\n setActiveSkill(skill);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] connectWithSkill error:', msg);\n setStatus('error');\n setError(msg);\n }\n },\n [obtainTokenForAgent, runConnect],\n );\n\n const recoverAgentAuth = useCallback(async (): Promise<boolean> => {\n try {\n const token = await obtainTokenForAgent(true);\n if (!token) {\n setStatus('error');\n setError('Could not remint the CDeCLI system token. Sign in again, then retry.');\n connectedRef.current = false;\n return false;\n }\n const ok = await runConnect({ token });\n if (ok) setError(null);\n return ok;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] recoverAgentAuth error:', msg);\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n return false;\n }\n }, [obtainTokenForAgent, runConnect]);\n\n return {\n channelConnected: status === 'connected',\n status,\n error,\n persistenceMode,\n activeSkill,\n accountId,\n connectWithSkill,\n recoverAgentAuth,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -41,7 +41,8 @@ const AI_ASSISTANT_CHANNELS_QUERY_VARS = {
41
41
  value: SortEnum.Desc
42
42
  }
43
43
  };
44
- const HISTORY_PAGE_SIZE = 20;
44
+ const HISTORY_PAGE_SIZE = 10;
45
+ const HISTORY_TITLE_OVERLAY_LIMIT = 20;
45
46
  function getHistoryChannelsQueryVariables(orgName, options) {
46
47
  var _a, _b;
47
48
  const trimmedOrg = typeof orgName === "string" && orgName.trim().length > 0 ? orgName.trim() : void 0;
@@ -97,23 +98,21 @@ function getChatHistoryMessagesQueryVariables(accountUserId, options) {
97
98
  value: SortEnum.Desc
98
99
  },
99
100
  type: PostTypeEnum.Aiassistant,
100
- editedBy: accountUserId,
101
- props: {
102
- role: "user"
103
- }
101
+ editedBy: accountUserId
104
102
  };
105
103
  }
106
104
  function chatHistorySessionsFromMessages(data) {
107
- var _a, _b, _c;
105
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
108
106
  const rows = (_a = data == null ? void 0 : data.messages) == null ? void 0 : _a.data;
109
107
  if (!(rows == null ? void 0 : rows.length)) return [];
110
108
  const seen = /* @__PURE__ */ new Set();
111
109
  const sessions = [];
112
110
  for (const post of rows) {
113
- const channelId = ((_b = post == null ? void 0 : post.channel) == null ? void 0 : _b.id) ? String(post.channel.id) : "";
111
+ const channelId = ((_b = post == null ? void 0 : post.channel) == null ? void 0 : _b.id) != null ? String(post.channel.id) : typeof (post == null ? void 0 : post.channel) === "string" ? post.channel : "";
114
112
  if (!channelId || seen.has(channelId)) continue;
115
113
  const rawTitle = String((_c = post == null ? void 0 : post.message) != null ? _c : "").replace(/\s+/g, " ").trim();
116
- if (/^thinking[.…]*$/i.test(rawTitle)) continue;
114
+ const role = String((_k = (_j = (_g = (_d = post == null ? void 0 : post.props) == null ? void 0 : _d.role) != null ? _g : (_f = (_e = post == null ? void 0 : post.propsConfiguration) == null ? void 0 : _e.contents) == null ? void 0 : _f.role) != null ? _j : (_i = (_h = post == null ? void 0 : post.propsConfiguration) == null ? void 0 : _h.content) == null ? void 0 : _i.role) != null ? _k : "").toUpperCase();
115
+ if (isTransientHistoryText(rawTitle) || looksLikeAssistantReply(rawTitle, role)) continue;
117
116
  seen.add(channelId);
118
117
  const updatedAt = (post == null ? void 0 : post.updatedAt) ? new Date(post.updatedAt) : /* @__PURE__ */ new Date();
119
118
  const createdAt = (post == null ? void 0 : post.createdAt) ? new Date(post.createdAt) : updatedAt;
@@ -142,7 +141,7 @@ function useChatHistorySessionsFromMessages(accountUserId, options) {
142
141
  refetch
143
142
  } = useMessagesQuery({
144
143
  variables: accountUserId ? getChatHistoryMessagesQueryVariables(accountUserId, {
145
- limit: 100
144
+ limit: HISTORY_TITLE_OVERLAY_LIMIT
146
145
  }) : void 0,
147
146
  skip,
148
147
  // cache-first + `messages` typePolicy (keyed by editedBy/type/props).
@@ -295,17 +294,36 @@ function buildSessionFromChannel(channel) {
295
294
  function chatHistorySessionsFromChannels(data) {
296
295
  var _a;
297
296
  const channels = (_a = data == null ? void 0 : data.channelsByUser) != null ? _a : [];
298
- const now = Date.now();
299
- return channels.filter((c) => Boolean(c == null ? void 0 : c.id) && (!(c == null ? void 0 : c.type) || c.type === RoomType.Aiassistant)).map((c) => buildSessionFromChannel(c)).filter((row) => row !== null).filter((row) => {
300
- if (!row.isPlaceholder) return true;
301
- if (row.preview) return true;
302
- return now - row.updatedAt.getTime() < 2 * 60 * 1e3;
303
- }).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
297
+ return channels.filter((c) => Boolean(c == null ? void 0 : c.id) && (!(c == null ? void 0 : c.type) || c.type === RoomType.Aiassistant)).map((c) => buildSessionFromChannel(c)).filter((row) => row !== null).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
298
+ }
299
+ const rememberedHistoryTitles = /* @__PURE__ */ new Map();
300
+ function rememberHistoryTitle(session) {
301
+ var _a;
302
+ if (session.isPlaceholder) return;
303
+ const title = (_a = session.title) == null ? void 0 : _a.trim();
304
+ if (!title || /^new chat$/i.test(title) || isTransientHistoryText(title)) return;
305
+ rememberedHistoryTitles.set(session.channelId, {
306
+ title,
307
+ isAttachment: session.isAttachment
308
+ });
309
+ }
310
+ function applyRememberedHistoryTitles(rows) {
311
+ return rows.map((row) => {
312
+ rememberHistoryTitle(row);
313
+ if (!row.isPlaceholder && !/^new chat$/i.test(row.title)) return row;
314
+ const mem = rememberedHistoryTitles.get(row.channelId);
315
+ if (!mem) return row;
316
+ return __spreadProps(__spreadValues({}, row), {
317
+ title: mem.title,
318
+ isPlaceholder: false,
319
+ isAttachment: mem.isAttachment || row.isAttachment
320
+ });
321
+ });
304
322
  }
305
323
  function enrichHistorySessionsWithUserPrompts(fromChannels, fromMessages) {
306
- if (!fromMessages.length) return fromChannels;
324
+ if (!fromMessages.length) return applyRememberedHistoryTitles(fromChannels);
307
325
  const byId = new Map(fromMessages.map((session) => [session.channelId, session]));
308
- return fromChannels.map((row) => {
326
+ const merged = fromChannels.map((row) => {
309
327
  const fromUser = byId.get(row.channelId);
310
328
  if (!(fromUser == null ? void 0 : fromUser.title) || isTransientHistoryText(fromUser.title)) return row;
311
329
  const titleMissing = row.isPlaceholder || isTransientHistoryText(row.title) || /^new chat$/i.test(row.title);
@@ -321,6 +339,14 @@ function enrichHistorySessionsWithUserPrompts(fromChannels, fromMessages) {
321
339
  isAttachment: fromUser.isAttachment || row.isAttachment
322
340
  });
323
341
  });
342
+ const seen = new Set(merged.map((row) => row.channelId));
343
+ for (const fromUser of fromMessages) {
344
+ if (seen.has(fromUser.channelId)) continue;
345
+ if (!fromUser.title || isTransientHistoryText(fromUser.title)) continue;
346
+ seen.add(fromUser.channelId);
347
+ merged.push(fromUser);
348
+ }
349
+ return applyRememberedHistoryTitles(merged);
324
350
  }
325
351
  function useChatHistorySessionsFromChannels(orgName, options) {
326
352
  var _a, _b;