@adminide-stack/yantra-mobile 12.0.123 → 12.0.124-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.
- package/lib/api/stt.js +76 -25
- package/lib/api/stt.js.map +1 -1
- package/lib/contexts/CdecliConnectionContext.js +4 -1
- package/lib/contexts/CdecliConnectionContext.js.map +1 -1
- package/lib/features/audio-input/AudioRecorderPanel.js +21 -8
- package/lib/features/audio-input/AudioRecorderPanel.js.map +1 -1
- package/lib/features/chat/ChatWidgetCard.js +1 -1
- package/lib/features/chat/ChatWidgetCard.js.map +1 -1
- package/lib/features/chat/TemplatesChatCards.js +24 -7
- package/lib/features/chat/TemplatesChatCards.js.map +1 -1
- package/lib/features/chat/chatWidgets.js +17 -1
- package/lib/features/chat/chatWidgets.js.map +1 -1
- package/lib/features/webbuilder/buildJob.js +2 -2
- package/lib/features/webbuilder/buildJob.js.map +1 -1
- package/lib/hooks/useCdecliAutoConnect.js +22 -2
- package/lib/hooks/useCdecliAutoConnect.js.map +1 -1
- package/lib/hooks/useChatStream.js +21 -4
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/hooks/useSurfaceJobs.js +5 -2
- package/lib/hooks/useSurfaceJobs.js.map +1 -1
- package/lib/services/brainQuickReply.js +50 -11
- package/lib/services/brainQuickReply.js.map +1 -1
- package/lib/services/queuedGatewayMessages.js +12 -1
- package/lib/services/queuedGatewayMessages.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chatWidgets.js","sources":["../../../src/features/chat/chatWidgets.ts"],"sourcesContent":["/**\n * Widget envelopes in an assistant reply, for the mobile main chat.\n *\n * Skills end a reply with a card envelope in a fenced block:\n *\n * ```yantra:widget (or ```json)\n * {\"propsComponent\":\"travel\",\"propsConfiguration\":{\"contents\":{\"attachment\":{…}}}}\n * ```\n *\n * The web chat turns that into a card (parseChatWidget). The mobile chat had no\n * parser, so the envelope rendered as a raw JSON code block, and while\n * streaming it grew in place character by character. This pulls every envelope\n * out of the text and returns what the thread should show instead.\n
|
|
1
|
+
{"version":3,"file":"chatWidgets.js","sources":["../../../src/features/chat/chatWidgets.ts"],"sourcesContent":["/**\n * Widget envelopes in an assistant reply, for the mobile main chat.\n *\n * Skills end a reply with a card envelope in a fenced block:\n *\n * ```yantra:widget (or ```json)\n * {\"propsComponent\":\"travel\",\"propsConfiguration\":{\"contents\":{\"attachment\":{…}}}}\n * ```\n *\n * The web chat turns that into a card (parseChatWidget). The mobile chat had no\n * parser, so the envelope rendered as a raw JSON code block, and while\n * streaming it grew in place character by character. This pulls every envelope\n * out of the text and returns what the thread should show instead.\n *\n * A `templates` widget also takes over the Templates surface link the messenger\n * gateway ends the reply with when the agent asked for the surface\n * (`?ys=<snapshot>`, splitTemplatesSurfaceLink): its row opens that link, the\n * one that lets the Templates page send a pick back to this chat, and the line\n * leaves the text - one way into the same templates, not two.\n */\n\nimport {\n CANVAS_DESIGN_WIDGET,\n canvasDesignFromWidget,\n parseTemplatesOpening,\n splitTemplatesSurfaceLink,\n TEMPLATES_WIDGET,\n} from 'common';\n\nexport interface ChatWidget {\n component: string;\n attachment: Record<string, unknown>;\n /**\n * The surface link the reply carried for this widget (a `templates` widget's\n * Templates surface page, with the reply's snapshot). Absent on a reply that\n * had none - an older reply, or one where the agent asked for no surface.\n */\n link?: string;\n}\n\nexport interface ChatWidgetSplit {\n /** The reply with every widget envelope removed. */\n text: string;\n widgets: ChatWidget[];\n}\n\nconst WIDGET_FENCE = /```(?:yantra:widget|json)[^\\S\\n]*\\n?([\\s\\S]*?)```/gi;\n/** An envelope still streaming in: an opened widget fence with no closing fence yet. */\nconst OPEN_WIDGET_TAIL = /```(?:yantra:widget|json)[^\\S\\n]*\\n?\\s*\\{\\s*\"propsComponent\"[\\s\\S]*$/i;\n/** The same, before the JSON reaches the component key. */\nconst OPEN_YANTRA_WIDGET_TAIL = /```yantra:widget[\\s\\S]*$/i;\n\nfunction toWidget(body: string): ChatWidget | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body.trim());\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== 'object') return null;\n const obj = parsed as {\n propsComponent?: unknown;\n propsConfiguration?: { contents?: { attachment?: unknown } };\n attachment?: unknown;\n };\n if (typeof obj.propsComponent !== 'string' || !obj.propsComponent) return null;\n // Templates to browse: every field optional, the opening sometimes flat in `contents`.\n if (obj.propsComponent === TEMPLATES_WIDGET) {\n const contents = obj.propsConfiguration?.contents as Record<string, unknown> | undefined;\n const opening = parseTemplatesOpening(contents?.attachment ?? obj.attachment ?? contents ?? {});\n return opening ? { component: TEMPLATES_WIDGET, attachment: { ...opening } } : null;\n }\n // A design made from a template, only with the canvas artifact it is.\n if (obj.propsComponent === CANVAS_DESIGN_WIDGET) {\n const design = canvasDesignFromWidget(obj as Record<string, unknown>);\n return design ? { component: CANVAS_DESIGN_WIDGET, attachment: { ...design } } : null;\n }\n // Canonical path, tolerating the flatter `attachment` the web parser also accepts.\n const attachment = obj.propsConfiguration?.contents?.attachment ?? obj.attachment;\n if (!attachment || typeof attachment !== 'object') return null;\n return { component: obj.propsComponent, attachment: attachment as Record<string, unknown> };\n}\n\nexport function splitChatWidgets(content: string, streaming = false): ChatWidgetSplit {\n const widgets: ChatWidget[] = [];\n let text = content.replace(WIDGET_FENCE, (match, body: string) => {\n const widget = toWidget(body);\n if (!widget) return match; // an ordinary ```json example stays in the answer\n widgets.push(widget);\n return '';\n });\n if (streaming) {\n // Hide an envelope that is still arriving rather than paint half of it.\n text = text.replace(OPEN_WIDGET_TAIL, '').replace(OPEN_YANTRA_WIDGET_TAIL, '');\n }\n // The gateway keeps the reply's LAST templates widget as the link's snapshot, so that one owns it.\n const templates = widgets.map((w) => w.component).lastIndexOf(TEMPLATES_WIDGET);\n if (templates >= 0) {\n const { text: rest, url } = splitTemplatesSurfaceLink(text);\n if (url) {\n text = rest;\n widgets[templates] = { ...widgets[templates], link: url };\n }\n }\n return { text: text.replace(/\\n{3,}/g, '\\n\\n').trim(), widgets };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {titleFromPrompt,cleanPrompt
|
|
1
|
+
import {titleFromPrompt,stripAgentNote,cleanPrompt}from'./buildFragments.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -32,7 +32,7 @@ function webBuilderPageUrl(webOrigin, channelId) {
|
|
|
32
32
|
}
|
|
33
33
|
function webBuilderJobRecord(start) {
|
|
34
34
|
const brief = briefFrom(start.ask);
|
|
35
|
-
const name = start.siteTitle || titleFromPrompt(
|
|
35
|
+
const name = start.siteTitle || titleFromPrompt(stripAgentNote(start.ask)) || "New site";
|
|
36
36
|
const pageUrl = webBuilderPageUrl(start.webOrigin, start.channelId);
|
|
37
37
|
return __spreadProps(__spreadValues({
|
|
38
38
|
// One job per turn; the server keys it on `<surface>:<id>`, so a retry records nothing new.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildJob.js","sources":["../../../src/features/webbuilder/buildJob.ts"],"sourcesContent":["/**\n * A Web Builder turn (a new site, a remix or an edit) is a long-running job:\n * it is recorded as a server-owned surface job so it shows in the phone's Tasks\n * sheet and the web chat's Tasks panel while it runs, and after the app closes.\n *\n * Contract: adminIde project-mgmt `recordSurfaceJob(job)` / `updateSurfaceJob(id,\n * patch)` (surface-job-rules.ts); read back by common CENTRAL_TASKS_MODEL. The\n * agent settles a job whose ref rode on its chat turn (`job {task_id,\n * report_token}`); the app settles it too when it sees the turn end, and the\n * server's deadline sweep ends one nobody reported on.\n *\n * Pure (no React, no fetch): the host hook records and settles.\n */\nimport { cleanPrompt, stripAgentNote, titleFromPrompt } from './buildFragments';\nimport type { WebBuilderBuild } from './types';\n\n/** The slug every Web Builder job is recorded under. */\nexport const WEBBUILDER_JOB_SURFACE = 'webbuilder';\n/** A build or an edit finishes well inside this. */\nexport const WEBBUILDER_JOB_BUDGET_MS = 30 * 60 * 1000;\n/** Slack past the budget before a running job reads as stopped (surface-kit's grace). */\nexport const WEBBUILDER_JOB_GRACE_MS = 10 * 60 * 1000;\n/** The brief is a line under the title, not the whole ask. */\nconst BRIEF_MAX = 280;\n\n/** The ref a chat turn carries so the agent reports the job's verdict itself. */\nexport interface SurfaceJobRef {\n task_id: string;\n report_token: string;\n}\n\nexport type WebBuilderJobMode = 'new' | 'remix' | 'edit';\n\nexport interface WebBuilderJobStart {\n channelId: string;\n projectId?: string | null;\n /** The person's words for this turn, as sent (notes and prefixes are stripped). */\n ask: string;\n /** The site's name when it has one (an edit); else it is named from the ask. */\n siteTitle?: string | null;\n mode: WebBuilderJobMode;\n /** Who runs it: the agent chat, or the build workflow. */\n lane: 'agent' | 'workflow';\n startedAt: number;\n /** The web app's origin; the job opens this site's Web Builder there. */\n webOrigin?: string | null;\n}\n\nfunction briefFrom(ask: string): string {\n const text = cleanPrompt(stripAgentNote(ask));\n return text.length > BRIEF_MAX ? `${text.slice(0, BRIEF_MAX - 1).trimEnd()}…` : text;\n}\n\n/** `<origin>/c/<channelId>/webbuilder`, the web's Web Builder rail for this chat; http(s) only. */\nexport function webBuilderPageUrl(webOrigin: string | null | undefined, channelId: string): string | undefined {\n const origin = (webOrigin || '').trim().replace(/\\/+$/, '');\n if (!/^https?:\\/\\/[^/]+$/i.test(origin)) return undefined;\n return `${origin}/c/${encodeURIComponent(channelId)}/webbuilder`;\n}\n\n/** The `recordSurfaceJob` input for a turn that is starting. */\nexport function webBuilderJobRecord(start: WebBuilderJobStart): Record<string, unknown> {\n const brief = briefFrom(start.ask);\n const name = start.siteTitle || titleFromPrompt(
|
|
1
|
+
{"version":3,"file":"buildJob.js","sources":["../../../src/features/webbuilder/buildJob.ts"],"sourcesContent":["/**\n * A Web Builder turn (a new site, a remix or an edit) is a long-running job:\n * it is recorded as a server-owned surface job so it shows in the phone's Tasks\n * sheet and the web chat's Tasks panel while it runs, and after the app closes.\n *\n * Contract: adminIde project-mgmt `recordSurfaceJob(job)` / `updateSurfaceJob(id,\n * patch)` (surface-job-rules.ts); read back by common CENTRAL_TASKS_MODEL. The\n * agent settles a job whose ref rode on its chat turn (`job {task_id,\n * report_token}`); the app settles it too when it sees the turn end, and the\n * server's deadline sweep ends one nobody reported on.\n *\n * Pure (no React, no fetch): the host hook records and settles.\n */\nimport { cleanPrompt, stripAgentNote, titleFromPrompt } from './buildFragments';\nimport type { WebBuilderBuild } from './types';\n\n/** The slug every Web Builder job is recorded under. */\nexport const WEBBUILDER_JOB_SURFACE = 'webbuilder';\n/** A build or an edit finishes well inside this. */\nexport const WEBBUILDER_JOB_BUDGET_MS = 30 * 60 * 1000;\n/** Slack past the budget before a running job reads as stopped (surface-kit's grace). */\nexport const WEBBUILDER_JOB_GRACE_MS = 10 * 60 * 1000;\n/** The brief is a line under the title, not the whole ask. */\nconst BRIEF_MAX = 280;\n\n/** The ref a chat turn carries so the agent reports the job's verdict itself. */\nexport interface SurfaceJobRef {\n task_id: string;\n report_token: string;\n}\n\nexport type WebBuilderJobMode = 'new' | 'remix' | 'edit';\n\nexport interface WebBuilderJobStart {\n channelId: string;\n projectId?: string | null;\n /** The person's words for this turn, as sent (notes and prefixes are stripped). */\n ask: string;\n /** The site's name when it has one (an edit); else it is named from the ask. */\n siteTitle?: string | null;\n mode: WebBuilderJobMode;\n /** Who runs it: the agent chat, or the build workflow. */\n lane: 'agent' | 'workflow';\n startedAt: number;\n /** The web app's origin; the job opens this site's Web Builder there. */\n webOrigin?: string | null;\n}\n\nfunction briefFrom(ask: string): string {\n const text = cleanPrompt(stripAgentNote(ask));\n return text.length > BRIEF_MAX ? `${text.slice(0, BRIEF_MAX - 1).trimEnd()}…` : text;\n}\n\n/** `<origin>/c/<channelId>/webbuilder`, the web's Web Builder rail for this chat; http(s) only. */\nexport function webBuilderPageUrl(webOrigin: string | null | undefined, channelId: string): string | undefined {\n const origin = (webOrigin || '').trim().replace(/\\/+$/, '');\n if (!/^https?:\\/\\/[^/]+$/i.test(origin)) return undefined;\n return `${origin}/c/${encodeURIComponent(channelId)}/webbuilder`;\n}\n\n/** The `recordSurfaceJob` input for a turn that is starting. */\nexport function webBuilderJobRecord(start: WebBuilderJobStart): Record<string, unknown> {\n const brief = briefFrom(start.ask);\n // Named from the ask as sent: its \"Build me:\" marks the brief, so \"Build me: Make me a pin\n // board\" is \"Pin Board\" - without it \"Make me ...\" reads as an edit and gets no name.\n const name = start.siteTitle || titleFromPrompt(stripAgentNote(start.ask)) || 'New site';\n const pageUrl = webBuilderPageUrl(start.webOrigin, start.channelId);\n return {\n // One job per turn; the server keys it on `<surface>:<id>`, so a retry records nothing new.\n id: `${start.channelId}:${start.startedAt}`,\n surface: WEBBUILDER_JOB_SURFACE,\n lane: start.lane === 'agent' ? 'channel-turn' : 'workflow',\n title: start.mode === 'edit' ? `Update ${name}` : name,\n brief,\n state: 'running',\n createdAt: start.startedAt,\n startedAt: start.startedAt,\n deadlineAt: start.startedAt + WEBBUILDER_JOB_BUDGET_MS + WEBBUILDER_JOB_GRACE_MS,\n progress: start.mode === 'edit' ? 'Updating your app' : 'Starting your build',\n ...(pageUrl ? { pageUrl } : {}),\n handle: { channelId: start.channelId, ...(start.projectId ? { projectId: start.projectId } : {}) },\n };\n}\n\nexport type WebBuilderJobOutcome =\n | {\n ok: true;\n endedAt: number;\n build?: Pick<WebBuilderBuild, 'previewUrl' | 'liveUrl' | 'coverUrl' | 'title'> | null;\n }\n | { ok: false; endedAt: number; error: string };\n\n/** The `updateSurfaceJob` patch that ends a turn's job. */\nexport function webBuilderJobFinish(outcome: WebBuilderJobOutcome): Record<string, unknown> {\n if (!outcome.ok) {\n return { state: 'failed', endedAt: outcome.endedAt, progress: null, error: outcome.error };\n }\n const cover = outcome.build?.coverUrl;\n const results =\n cover && /^https?:\\/\\//i.test(cover)\n ? [{ url: cover, kind: 'image', title: outcome.build?.title || 'Site' }]\n : [];\n const site = outcome.build?.liveUrl || outcome.build?.previewUrl;\n return {\n state: 'succeeded',\n endedAt: outcome.endedAt,\n progress: null,\n ...(results.length ? { results } : {}),\n ...(site && /^https?:\\/\\//i.test(site) ? { resultUrl: site } : {}),\n };\n}\n\n/** The job's ref for a chat turn, when the server gave it a report token. */\nexport function surfaceJobRef(\n record: { id?: string | null; reportToken?: string | null } | null | undefined,\n): SurfaceJobRef | null {\n if (!record?.id || !record.reportToken) return null;\n return { task_id: record.id, report_token: record.reportToken };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -25,6 +25,7 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
25
25
|
const [activeSkill, setActiveSkill] = useState(null);
|
|
26
26
|
const connectingRef = useRef(false);
|
|
27
27
|
const connectedRef = useRef(false);
|
|
28
|
+
const attemptRef = useRef(0);
|
|
28
29
|
const {
|
|
29
30
|
accountUserId,
|
|
30
31
|
orgName,
|
|
@@ -119,6 +120,7 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
});
|
|
123
|
+
if (opts.attempt !== attemptRef.current) return true;
|
|
122
124
|
return applyConnectResult(data);
|
|
123
125
|
}, [connectMutation, accountId, endpoint, channelId, applyConnectResult]);
|
|
124
126
|
useEffect(() => {
|
|
@@ -140,11 +142,15 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
140
142
|
}
|
|
141
143
|
const doConnect = async () => {
|
|
142
144
|
var _a;
|
|
145
|
+
attemptRef.current += 1;
|
|
146
|
+
const attempt = attemptRef.current;
|
|
147
|
+
const superseded = () => attempt !== attemptRef.current;
|
|
143
148
|
connectingRef.current = true;
|
|
144
149
|
if (!isSilentReconnect) setStatus("connecting");
|
|
145
150
|
setError(null);
|
|
146
151
|
try {
|
|
147
152
|
const token = await obtainTokenForAgent(false);
|
|
153
|
+
if (superseded()) return;
|
|
148
154
|
if (!token) {
|
|
149
155
|
if (mintOnCluster) {
|
|
150
156
|
setStatus("error");
|
|
@@ -171,16 +177,21 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
171
177
|
}
|
|
172
178
|
}
|
|
173
179
|
});
|
|
180
|
+
if (superseded()) return;
|
|
174
181
|
console.log("[useCdecliAutoConnect] connectMutation result (account=%s mintOnCluster=%s):", accountId, mintOnCluster, JSON.stringify(data));
|
|
175
182
|
applyConnectResult(data);
|
|
176
183
|
} catch (err) {
|
|
177
184
|
const msg = err instanceof Error ? err.message : String(err);
|
|
185
|
+
if (superseded()) {
|
|
186
|
+
console.warn("[useCdecliAutoConnect] superseded connect failed (ignored):", msg);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
178
189
|
console.error("[useCdecliAutoConnect] connect error:", msg);
|
|
179
190
|
setStatus("error");
|
|
180
191
|
setError(msg);
|
|
181
192
|
connectedRef.current = false;
|
|
182
193
|
} finally {
|
|
183
|
-
connectingRef.current = false;
|
|
194
|
+
if (!superseded()) connectingRef.current = false;
|
|
184
195
|
}
|
|
185
196
|
};
|
|
186
197
|
doConnect();
|
|
@@ -188,11 +199,14 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
188
199
|
const connectWithSkill = useCallback(async (skill, model, systemPrompt, skillId) => {
|
|
189
200
|
var _a;
|
|
190
201
|
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);
|
|
202
|
+
attemptRef.current += 1;
|
|
203
|
+
const attempt = attemptRef.current;
|
|
191
204
|
setStatus("connecting");
|
|
192
205
|
setError(null);
|
|
193
206
|
try {
|
|
194
207
|
const token = await obtainTokenForAgent(false);
|
|
195
208
|
await runConnect({
|
|
209
|
+
attempt,
|
|
196
210
|
token,
|
|
197
211
|
skill,
|
|
198
212
|
skillId,
|
|
@@ -203,14 +217,18 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
203
217
|
} catch (err) {
|
|
204
218
|
const msg = err instanceof Error ? err.message : String(err);
|
|
205
219
|
console.error("[useCdecliAutoConnect] connectWithSkill error:", msg);
|
|
220
|
+
if (attempt !== attemptRef.current) return;
|
|
206
221
|
setStatus("error");
|
|
207
222
|
setError(msg);
|
|
208
223
|
}
|
|
209
224
|
}, [obtainTokenForAgent, runConnect]);
|
|
210
225
|
const recoverAgentAuth = useCallback(async () => {
|
|
211
226
|
var _a;
|
|
227
|
+
attemptRef.current += 1;
|
|
228
|
+
const attempt = attemptRef.current;
|
|
212
229
|
try {
|
|
213
230
|
const token = await obtainTokenForAgent(true);
|
|
231
|
+
if (attempt !== attemptRef.current) return true;
|
|
214
232
|
if (!token) {
|
|
215
233
|
setStatus("error");
|
|
216
234
|
setError((_a = mintErrorRef.current) != null ? _a : "Could not remint the CDeCLI system token. Sign in again, then retry.");
|
|
@@ -218,13 +236,15 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
218
236
|
return false;
|
|
219
237
|
}
|
|
220
238
|
const ok = await runConnect({
|
|
239
|
+
attempt,
|
|
221
240
|
token
|
|
222
241
|
});
|
|
223
|
-
if (ok) setError(null);
|
|
242
|
+
if (ok && attempt === attemptRef.current) setError(null);
|
|
224
243
|
return ok;
|
|
225
244
|
} catch (err) {
|
|
226
245
|
const msg = err instanceof Error ? err.message : String(err);
|
|
227
246
|
console.error("[useCdecliAutoConnect] recoverAgentAuth error:", msg);
|
|
247
|
+
if (attempt !== attemptRef.current) return false;
|
|
228
248
|
setStatus("error");
|
|
229
249
|
setError(msg);
|
|
230
250
|
connectedRef.current = false;
|
|
@@ -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, 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';\nimport { isWorkspaceBootstrapPending } from './workspaceBootstrapPending';\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 * The token this connection hands the agent: cdecli names the caller by its subject, and the person's Computer\n * is resolved from that. A call the app makes on that Computer itself (the one-time code card typing into the\n * page) carries the same token, so it reaches the Computer the agent is driving - another token's subject is a\n * different Computer, or none. Null when it cannot be had (not signed in yet, mint failed).\n */\n obtainAgentToken: () => Promise<string | null>;\n /** The cdecli-agent this connection talks to (CDECLI_AGENT_ENDPOINT). */\n agentEndpoint: string;\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, orgName, projectId, tagId, 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 /** Why the last cluster mint returned null, so callers can show the real reason. */\n const mintErrorRef = useRef<string | null>(null);\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 // Same gate web applies before minting (browser useCdecliAutoConnect):\n // no org/project yet means the workspace bootstrap is still running and\n // scope discovery would fail. Skip quietly - the effect re-runs once\n // usePrerequisiteIds resolves them.\n if (!orgName || !projectId) {\n mintErrorRef.current = 'Your workspace is still being set up - this will connect once it finishes.';\n return null;\n }\n try {\n const minted = await mintSurfaceSystemToken({\n graphqlUrl: tokenGraphqlUrl,\n // Preferred scope: kept whenever the cluster knows it (see mintScope.ts).\n orgName,\n projectId,\n tagId,\n discoverScope: true,\n forceRemint: discardExistingRow,\n });\n mintErrorRef.current = null;\n return minted.token?.trim() || null;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const pending = isWorkspaceBootstrapPending(msg);\n // Keep the specific reason. The generic caller message used to hide\n // genuinely actionable text like \"No organization on the canvas\n // backend. Sign in once in the canvas harness, then retry.\"\n mintErrorRef.current = pending ? `${msg} (minting against ${tokenGraphqlUrl})` : msg;\n if (pending) {\n console.warn('[useCdecliAutoConnect] cluster system-token mint not ready yet:', msg);\n } else {\n console.error('[useCdecliAutoConnect] cluster system-token mint failed:', err);\n }\n return null;\n }\n }\n return discardExistingRow ? regenerateSystemToken() : obtainSystemToken();\n },\n [mintOnCluster, tokenGraphqlUrl, orgName, projectId, tagId, 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(mintErrorRef.current ?? '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(\n mintErrorRef.current ?? 'Could not remint the CDeCLI system token. Sign in again, then retry.',\n );\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 // The same token the connect above hands the agent (a reveal of the one system token row, or the cluster mint),\n // never a fresh one: rotating it here would leave the chat's session on a revoked token.\n const obtainAgentToken = useCallback(() => obtainTokenForAgent(false), [obtainTokenForAgent]);\n\n return {\n channelConnected: status === 'connected',\n status,\n error,\n persistenceMode,\n activeSkill,\n accountId,\n connectWithSkill,\n recoverAgentAuth,\n obtainAgentToken,\n agentEndpoint: endpoint,\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';\nimport { isWorkspaceBootstrapPending } from './workspaceBootstrapPending';\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 * The token this connection hands the agent: cdecli names the caller by its subject, and the person's Computer\n * is resolved from that. A call the app makes on that Computer itself (the one-time code card typing into the\n * page) carries the same token, so it reaches the Computer the agent is driving - another token's subject is a\n * different Computer, or none. Null when it cannot be had (not signed in yet, mint failed).\n */\n obtainAgentToken: () => Promise<string | null>;\n /** The cdecli-agent this connection talks to (CDECLI_AGENT_ENDPOINT). */\n agentEndpoint: string;\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 * Each connect takes a number; only the latest may write the status. A chat switch\n * starts a new connect while the last one may still be minting its token, and that\n * superseded attempt failing later must not mark the live connection as down (the\n * composer then reads \"Connecting…\" and refuses to send while the agent works).\n */\n const attemptRef = useRef(0);\n\n const { accountUserId, orgName, projectId, tagId, 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 /** Why the last cluster mint returned null, so callers can show the real reason. */\n const mintErrorRef = useRef<string | null>(null);\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 // Same gate web applies before minting (browser useCdecliAutoConnect):\n // no org/project yet means the workspace bootstrap is still running and\n // scope discovery would fail. Skip quietly - the effect re-runs once\n // usePrerequisiteIds resolves them.\n if (!orgName || !projectId) {\n mintErrorRef.current = 'Your workspace is still being set up - this will connect once it finishes.';\n return null;\n }\n try {\n const minted = await mintSurfaceSystemToken({\n graphqlUrl: tokenGraphqlUrl,\n // Preferred scope: kept whenever the cluster knows it (see mintScope.ts).\n orgName,\n projectId,\n tagId,\n discoverScope: true,\n forceRemint: discardExistingRow,\n });\n mintErrorRef.current = null;\n return minted.token?.trim() || null;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const pending = isWorkspaceBootstrapPending(msg);\n // Keep the specific reason. The generic caller message used to hide\n // genuinely actionable text like \"No organization on the canvas\n // backend. Sign in once in the canvas harness, then retry.\"\n mintErrorRef.current = pending ? `${msg} (minting against ${tokenGraphqlUrl})` : msg;\n if (pending) {\n console.warn('[useCdecliAutoConnect] cluster system-token mint not ready yet:', msg);\n } else {\n console.error('[useCdecliAutoConnect] cluster system-token mint failed:', err);\n }\n return null;\n }\n }\n return discardExistingRow ? regenerateSystemToken() : obtainSystemToken();\n },\n [mintOnCluster, tokenGraphqlUrl, orgName, projectId, tagId, 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 attempt: number;\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 // A newer connect owns the status now; this result is not the current one.\n if (opts.attempt !== attemptRef.current) return true;\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 attemptRef.current += 1;\n const attempt = attemptRef.current;\n const superseded = () => attempt !== attemptRef.current;\n connectingRef.current = true;\n if (!isSilentReconnect) setStatus('connecting');\n setError(null);\n\n try {\n const token = await obtainTokenForAgent(false);\n if (superseded()) return;\n if (!token) {\n if (mintOnCluster) {\n setStatus('error');\n setError(mintErrorRef.current ?? '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 if (superseded()) return;\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 if (superseded()) {\n console.warn('[useCdecliAutoConnect] superseded connect failed (ignored):', msg);\n return;\n }\n console.error('[useCdecliAutoConnect] connect error:', msg);\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n } finally {\n if (!superseded()) 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 attemptRef.current += 1;\n const attempt = attemptRef.current;\n setStatus('connecting');\n setError(null);\n\n try {\n const token = await obtainTokenForAgent(false);\n await runConnect({ attempt, 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 if (attempt !== attemptRef.current) return;\n setStatus('error');\n setError(msg);\n }\n },\n [obtainTokenForAgent, runConnect],\n );\n\n const recoverAgentAuth = useCallback(async (): Promise<boolean> => {\n attemptRef.current += 1;\n const attempt = attemptRef.current;\n try {\n const token = await obtainTokenForAgent(true);\n // A newer connect took over; it reveals the token this one reminted.\n if (attempt !== attemptRef.current) return true;\n if (!token) {\n setStatus('error');\n setError(\n mintErrorRef.current ?? 'Could not remint the CDeCLI system token. Sign in again, then retry.',\n );\n connectedRef.current = false;\n return false;\n }\n const ok = await runConnect({ attempt, token });\n if (ok && attempt === attemptRef.current) 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 if (attempt !== attemptRef.current) return false;\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n return false;\n }\n }, [obtainTokenForAgent, runConnect]);\n\n // The same token the connect above hands the agent (a reveal of the one system token row, or the cluster mint),\n // never a fresh one: rotating it here would leave the chat's session on a revoked token.\n const obtainAgentToken = useCallback(() => obtainTokenForAgent(false), [obtainTokenForAgent]);\n\n return {\n channelConnected: status === 'connected',\n status,\n error,\n persistenceMode,\n activeSkill,\n accountId,\n connectWithSkill,\n recoverAgentAuth,\n obtainAgentToken,\n agentEndpoint: endpoint,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useState,useRef,useCallback,useEffect,useMemo,useSyncExternalStore}from'react';import {Platform}from'react-native';import {useCreateAccountChatFileUploadLinksMutation,useCreateAccountChatFileDownloadLinksMutation}from'common/graphql';import {markChannelHistoryRunning,useChatMutations,useChatMessages,clearChannelHistoryRunning,patchChannelHistoryPreview}from'./useChatApi.js';import {useCdecliChannel}from'./useCdecliChannel.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {isAgentJwtRejected,shouldInlineGatewayMediaForHostedAgent}from'./systemTokenJwt.js';import {config}from'../config/env-config.js';import {queueGatewayMessage,MAX_QUEUED_MESSAGES,dropQueuedGatewayMessage,queuedGatewayMessages,subscribeQueuedGatewayMessages,restoreQueuedGatewayMessages,clearQueuedGatewayMessages}from'../services/queuedGatewayMessages.js';import {isImageAttachment}from'../features/attachments/isImageAttachment.js';import {buildGatewayMedia,buildGatewayMediaPreferUrls}from'../features/attachments/buildGatewayMedia.js';import {preserveAttachmentPreviews}from'../features/attachments/preserveAttachmentPreviews.js';import {releasePreviewBytes}from'../features/attachments/releasePreviewBytes.js';import {resolveAssistantReply}from'../features/chat/emptyChatResponse.js';import {splitPreResponse}from'../features/chat/preResponse.js';import {appendOrReplaceAssistant,appendFollowUpAssistant,mergeOptimisticGatewayMessages,collapseConsecutiveAssistants}from'../state/chatThreadMerge.js';var __defProp = Object.defineProperty;
|
|
1
|
+
import {useState,useRef,useCallback,useEffect,useMemo,useSyncExternalStore}from'react';import {Platform}from'react-native';import {useCreateAccountChatFileUploadLinksMutation,useCreateAccountChatFileDownloadLinksMutation}from'common/graphql';import {markChannelHistoryRunning,useChatMutations,useChatMessages,clearChannelHistoryRunning,patchChannelHistoryPreview}from'./useChatApi.js';import {useCdecliChannel}from'./useCdecliChannel.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {isAgentJwtRejected,shouldInlineGatewayMediaForHostedAgent}from'./systemTokenJwt.js';import {config}from'../config/env-config.js';import {queueGatewayMessage,MAX_QUEUED_MESSAGES,dropQueuedGatewayMessage,queuedGatewayMessages,subscribeQueuedGatewayMessages,restoreQueuedGatewayMessages,isQueuedMessageDelivered,clearQueuedGatewayMessages}from'../services/queuedGatewayMessages.js';import {isImageAttachment}from'../features/attachments/isImageAttachment.js';import {buildGatewayMedia,buildGatewayMediaPreferUrls}from'../features/attachments/buildGatewayMedia.js';import {preserveAttachmentPreviews}from'../features/attachments/preserveAttachmentPreviews.js';import {releasePreviewBytes}from'../features/attachments/releasePreviewBytes.js';import {resolveAssistantReply}from'../features/chat/emptyChatResponse.js';import {splitPreResponse}from'../features/chat/preResponse.js';import {appendOrReplaceAssistant,appendFollowUpAssistant,mergeOptimisticGatewayMessages,collapseConsecutiveAssistants}from'../state/chatThreadMerge.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -501,8 +501,19 @@ function useChatStream(sessionId, routing = {
|
|
|
501
501
|
useEffect(() => {
|
|
502
502
|
void restoreQueuedGatewayMessages();
|
|
503
503
|
}, []);
|
|
504
|
+
const historyRef = useRef({
|
|
505
|
+
sessionId,
|
|
506
|
+
loading: backendMessagesLoading,
|
|
507
|
+
messages: backendMessages
|
|
508
|
+
});
|
|
509
|
+
historyRef.current = {
|
|
510
|
+
sessionId,
|
|
511
|
+
loading: backendMessagesLoading,
|
|
512
|
+
messages: backendMessages
|
|
513
|
+
};
|
|
504
514
|
const flushingRef = useRef(false);
|
|
505
515
|
const flushQueuedMessages = useCallback(async () => {
|
|
516
|
+
var _a;
|
|
506
517
|
if (flushingRef.current) return;
|
|
507
518
|
flushingRef.current = true;
|
|
508
519
|
try {
|
|
@@ -512,8 +523,14 @@ function useChatStream(sessionId, routing = {
|
|
|
512
523
|
if (!rt.channelConnected || !sid) return;
|
|
513
524
|
const next = queuedGatewayMessages(sid, rt.accountId)[0];
|
|
514
525
|
if (!next) return;
|
|
515
|
-
const
|
|
516
|
-
if (
|
|
526
|
+
const history = historyRef.current;
|
|
527
|
+
if (history.sessionId !== sid || history.loading) return;
|
|
528
|
+
if (isQueuedMessageDelivered(next, (_a = history.messages) != null ? _a : [])) {
|
|
529
|
+
dropQueuedGatewayMessage(next.id);
|
|
530
|
+
} else {
|
|
531
|
+
const outcome = await sendMessageRef.current(next.text, next.attachments, next.channelId, false, next.id);
|
|
532
|
+
if (outcome !== "sent") return;
|
|
533
|
+
}
|
|
517
534
|
}
|
|
518
535
|
} finally {
|
|
519
536
|
flushingRef.current = false;
|
|
@@ -527,7 +544,7 @@ function useChatStream(sessionId, routing = {
|
|
|
527
544
|
if (!connected || !queuedChannelId) return;
|
|
528
545
|
if (!reconnected && queuedGatewayMessages(queuedChannelId, routing.accountId).length === 0) return;
|
|
529
546
|
void flushQueuedMessages();
|
|
530
|
-
}, [routing.channelConnected, routing.accountId, queuedChannelId, flushQueuedMessages]);
|
|
547
|
+
}, [routing.channelConnected, routing.accountId, queuedChannelId, flushQueuedMessages, backendMessagesLoading]);
|
|
531
548
|
const discardQueuedMessages = useCallback(() => {
|
|
532
549
|
if (!queuedChannelId) return;
|
|
533
550
|
clearQueuedGatewayMessages(queuedChannelId);
|