@burdenoff/microfe-bigconsole 2026.709.1 → 2026.709.3
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.
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { CreateSandboxDocument as e, ExtendSandboxTtlDocument as t, GetActiveSandboxesDocument as n, GetSandboxDocument as r, ProxySandboxRequestDocument as i } from "../../generated/wspace-operations.js";
|
|
2
|
-
import {
|
|
2
|
+
import { Kind as a, print as o } from "graphql";
|
|
3
|
+
import { graphqlFetch as s } from "@burdenoff/fe-libs/shared/graphql";
|
|
3
4
|
//#region src/bigconsole/assistant/assistantApi.ts
|
|
4
|
-
var
|
|
5
|
-
function
|
|
5
|
+
var c = "burdenoffregistry.azurecr.io", l = "alpha-proxy-auth-v1", u = "alpha-proxy-auth-v1", d = "alpha-delegated-auth-v11", f = "sandbox-app-client-headers-v1";
|
|
6
|
+
function p(e) {
|
|
6
7
|
switch (e) {
|
|
7
|
-
case "general": return
|
|
8
|
-
case "vibe-plugins": return
|
|
8
|
+
case "general": return l;
|
|
9
|
+
case "vibe-plugins": return u;
|
|
9
10
|
case "assistant":
|
|
10
|
-
case "api-calls": return
|
|
11
|
+
case "api-calls": return d;
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
-
return `${
|
|
14
|
+
function m(e) {
|
|
15
|
+
return `${c}/wspace/ai-assistant-${e === "assistant" ? "api-calls" : e}:${p(e)}`;
|
|
15
16
|
}
|
|
16
|
-
function
|
|
17
|
+
function h(e) {
|
|
17
18
|
switch (e) {
|
|
18
19
|
case "general": return {
|
|
19
20
|
cpuRequest: "100m",
|
|
@@ -31,59 +32,77 @@ function p(e) {
|
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
|
-
function
|
|
35
|
+
function g(e) {
|
|
35
36
|
let t = e.metadata?.assistantMode;
|
|
36
37
|
if (t === "assistant" || t === "general" || t === "api-calls" || t === "vibe-plugins") return t;
|
|
37
38
|
let n = e.name ?? "";
|
|
38
39
|
return n.startsWith("ai-assistant-vibe-plugins-") ? "vibe-plugins" : n.startsWith("ai-assistant-api-calls-") ? "api-calls" : n.startsWith("ai-assistant-general-") ? "general" : n.startsWith("ai-assistant-assistant-") ? "assistant" : null;
|
|
39
40
|
}
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
return t?.accessToken && (n.authorization = `Bearer ${t.accessToken}`), t?.workspaceToken && (n["x-workspace-authorization"] = `Bearer ${t.workspaceToken}`), e && (n["x-workspace-id"] = e), t?.userId && (n["x-actor-id"] = t.userId, n["x-actor-type"] = "user"), t?.organizationId && (n["x-org-id"] = t.organizationId), Object.keys(n).length > 0 ? { headers: n } : void 0;
|
|
41
|
+
function _(e) {
|
|
42
|
+
return e.definitions.find((e) => e.kind === a.OPERATION_DEFINITION)?.name?.value;
|
|
43
43
|
}
|
|
44
|
-
function
|
|
44
|
+
function v() {
|
|
45
|
+
try {
|
|
46
|
+
return `bc-assistant-${crypto.randomUUID()}`;
|
|
47
|
+
} catch {
|
|
48
|
+
return `bc-assistant-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function y(e, t) {
|
|
45
52
|
return {
|
|
46
53
|
workspaceId: e,
|
|
47
54
|
...t?.organizationId ? { custom: { organizationId: t.organizationId } } : {}
|
|
48
55
|
};
|
|
49
56
|
}
|
|
50
|
-
async function
|
|
51
|
-
let t
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
status: t.status,
|
|
66
|
-
messages: e,
|
|
67
|
-
result: n
|
|
68
|
-
});
|
|
69
|
-
let r = Error(e || `Sandbox GraphQL HTTP ${t.status}`);
|
|
70
|
-
throw r.statusCode = t.status, r.result = n ?? void 0, r;
|
|
57
|
+
async function b(e) {
|
|
58
|
+
let t;
|
|
59
|
+
for (let n = 0; n < 3; n++) try {
|
|
60
|
+
return await x(e);
|
|
61
|
+
} catch (e) {
|
|
62
|
+
t = e;
|
|
63
|
+
let r = e instanceof Error ? e.message.toLowerCase() : String(e).toLowerCase();
|
|
64
|
+
if (!(r.includes("failed to fetch") || r.includes("networkerror") || r.includes("network request failed") || r.includes("aborted") || r.includes("load failed")) || n === 2) throw e;
|
|
65
|
+
let i = 250 * 2 ** n;
|
|
66
|
+
console.warn("[BigConsole-Assistant] executeSandboxGraphQL transient failure, retrying", {
|
|
67
|
+
attempt: n + 1,
|
|
68
|
+
maxRetries: 3,
|
|
69
|
+
backoffMs: i,
|
|
70
|
+
error: e instanceof Error ? e.message : String(e)
|
|
71
|
+
}), await new Promise((e) => setTimeout(e, i));
|
|
71
72
|
}
|
|
73
|
+
throw t;
|
|
74
|
+
}
|
|
75
|
+
async function x(e) {
|
|
76
|
+
let t = _(e.document), n = await s({
|
|
77
|
+
gateway: "workspace",
|
|
78
|
+
query: o(e.document),
|
|
79
|
+
variables: e.variables,
|
|
80
|
+
operationName: t,
|
|
81
|
+
authToken: e.authContext?.accessToken ?? void 0,
|
|
82
|
+
workspaceToken: e.authContext?.workspaceToken || !0,
|
|
83
|
+
workspaceId: e.workspaceId,
|
|
84
|
+
organizationId: e.authContext?.organizationId ?? !0,
|
|
85
|
+
suppressGlobalErrorEvent: !0,
|
|
86
|
+
extraHeaders: { "x-request-id": v() }
|
|
87
|
+
});
|
|
72
88
|
if (!n) throw Error("Sandbox GraphQL returned empty response");
|
|
73
|
-
if (n.errors?.length) throw console.error("[BigConsole-Assistant] executeSandboxGraphQL errors", {
|
|
89
|
+
if (n.errors?.length) throw console.error("[BigConsole-Assistant] executeSandboxGraphQL errors", {
|
|
90
|
+
operationName: t,
|
|
91
|
+
errors: n.errors
|
|
92
|
+
}), Error(n.errors.map((e) => e.message).join(", "));
|
|
74
93
|
if (!n.data) throw Error("Sandbox GraphQL returned no data");
|
|
75
94
|
return n.data;
|
|
76
95
|
}
|
|
77
|
-
async function
|
|
78
|
-
return ((await
|
|
96
|
+
async function S(e, t, r) {
|
|
97
|
+
return ((await b({
|
|
79
98
|
document: n,
|
|
80
|
-
variables: { context:
|
|
99
|
+
variables: { context: y(e, r) },
|
|
81
100
|
workspaceId: e,
|
|
82
101
|
authContext: r
|
|
83
|
-
})).getActiveSandboxes ?? []).find((e) => (e.status === "RUNNING" || e.status === "PROVISIONING" || e.status === "PENDING") && e.template === "AI_BUILDER" &&
|
|
102
|
+
})).getActiveSandboxes ?? []).find((e) => (e.status === "RUNNING" || e.status === "PROVISIONING" || e.status === "PENDING") && e.template === "AI_BUILDER" && g(e) === t && (!r?.userId || e.createdBy === r.userId) && e.metadata?.authStrategy === f && e.metadata?.imageTag === p(t))?.id ?? null;
|
|
84
103
|
}
|
|
85
|
-
async function
|
|
86
|
-
let i =
|
|
104
|
+
async function C(t, n, r) {
|
|
105
|
+
let i = h(t), a = [
|
|
87
106
|
{
|
|
88
107
|
name: "AI_ASSISTANT_MODE",
|
|
89
108
|
value: t
|
|
@@ -100,7 +119,7 @@ async function y(t, n, r) {
|
|
|
100
119
|
name: "AI_ASSISTANT_PRODUCT",
|
|
101
120
|
value: "bigconsole"
|
|
102
121
|
}
|
|
103
|
-
], o = await
|
|
122
|
+
], o = await b({
|
|
104
123
|
document: e,
|
|
105
124
|
variables: { input: {
|
|
106
125
|
name: `ai-assistant-${t}-${Date.now()}`,
|
|
@@ -109,7 +128,7 @@ async function y(t, n, r) {
|
|
|
109
128
|
mode: "DEPLOYMENT",
|
|
110
129
|
containers: [{
|
|
111
130
|
name: "ai-assistant",
|
|
112
|
-
image:
|
|
131
|
+
image: m(t),
|
|
113
132
|
ports: [{
|
|
114
133
|
containerPort: 8080,
|
|
115
134
|
name: "http"
|
|
@@ -127,10 +146,10 @@ async function y(t, n, r) {
|
|
|
127
146
|
assistantMode: t,
|
|
128
147
|
assistantOwnerUserId: r?.userId ?? null,
|
|
129
148
|
assistantOrganizationId: r?.organizationId ?? null,
|
|
130
|
-
authStrategy:
|
|
131
|
-
imageTag:
|
|
149
|
+
authStrategy: f,
|
|
150
|
+
imageTag: p(t)
|
|
132
151
|
},
|
|
133
|
-
context:
|
|
152
|
+
context: y(n, r)
|
|
134
153
|
} },
|
|
135
154
|
workspaceId: n,
|
|
136
155
|
authContext: r
|
|
@@ -138,14 +157,14 @@ async function y(t, n, r) {
|
|
|
138
157
|
if (!o?.createSandbox?.id) throw Error("createSandbox returned empty response");
|
|
139
158
|
return o.createSandbox.id;
|
|
140
159
|
}
|
|
141
|
-
async function
|
|
160
|
+
async function w(e, t, n, i = 3e5) {
|
|
142
161
|
let a = Date.now();
|
|
143
162
|
for (; Date.now() - a < i;) {
|
|
144
|
-
let i = await
|
|
163
|
+
let i = await b({
|
|
145
164
|
document: r,
|
|
146
165
|
variables: {
|
|
147
166
|
id: e,
|
|
148
|
-
context:
|
|
167
|
+
context: y(t, n)
|
|
149
168
|
},
|
|
150
169
|
workspaceId: t,
|
|
151
170
|
authContext: n
|
|
@@ -156,7 +175,7 @@ async function b(e, t, n, i = 3e5) {
|
|
|
156
175
|
}
|
|
157
176
|
throw Error("Sandbox startup timed out");
|
|
158
177
|
}
|
|
159
|
-
async function
|
|
178
|
+
async function T(e, t, n, r, a, o) {
|
|
160
179
|
console.log("[BigConsole-Assistant] proxyToSandbox REQUEST", {
|
|
161
180
|
sandboxId: e,
|
|
162
181
|
workspaceId: t,
|
|
@@ -164,14 +183,14 @@ async function x(e, t, n, r, a, o) {
|
|
|
164
183
|
method: r,
|
|
165
184
|
hasBody: !!a
|
|
166
185
|
});
|
|
167
|
-
let s = (await
|
|
186
|
+
let s = (await b({
|
|
168
187
|
document: i,
|
|
169
188
|
variables: { input: {
|
|
170
189
|
sandboxId: e,
|
|
171
190
|
path: n,
|
|
172
191
|
method: r,
|
|
173
192
|
body: a,
|
|
174
|
-
context:
|
|
193
|
+
context: y(t, o)
|
|
175
194
|
} },
|
|
176
195
|
workspaceId: t,
|
|
177
196
|
authContext: o
|
|
@@ -191,11 +210,11 @@ async function x(e, t, n, r, a, o) {
|
|
|
191
210
|
}
|
|
192
211
|
return s.body;
|
|
193
212
|
}
|
|
194
|
-
async function
|
|
213
|
+
async function E(e, t, n, r = 9e4) {
|
|
195
214
|
let i = Date.now();
|
|
196
215
|
for (; Date.now() - i < r;) {
|
|
197
216
|
try {
|
|
198
|
-
await
|
|
217
|
+
await T(e, t, "/health", "GET", void 0, n);
|
|
199
218
|
return;
|
|
200
219
|
} catch (e) {
|
|
201
220
|
let t = e instanceof Error ? e.message : String(e);
|
|
@@ -205,33 +224,33 @@ async function S(e, t, n, r = 9e4) {
|
|
|
205
224
|
}
|
|
206
225
|
throw Error("Assistant service did not become healthy before timeout");
|
|
207
226
|
}
|
|
208
|
-
async function
|
|
209
|
-
return { sessionId: (await
|
|
227
|
+
async function D(e, t, n, r) {
|
|
228
|
+
return { sessionId: (await T(e, t, "/sessions", "POST", { mode: n }, r)).session.id };
|
|
210
229
|
}
|
|
211
|
-
async function
|
|
230
|
+
async function O(e, t, n, r, i, a, o) {
|
|
212
231
|
let s = {
|
|
213
232
|
mode: i,
|
|
214
233
|
prompt: r,
|
|
215
234
|
pageContext: a ?? void 0
|
|
216
235
|
};
|
|
217
|
-
await
|
|
236
|
+
await T(e, t, `/sessions/${n}/prompt-async`, "POST", s, o);
|
|
218
237
|
}
|
|
219
|
-
async function
|
|
220
|
-
return (await
|
|
238
|
+
async function k(e, t, n, r, i = 50) {
|
|
239
|
+
return (await T(e, t, `/sessions/${n}/messages?limit=${i}`, "GET", void 0, r)).messages ?? [];
|
|
221
240
|
}
|
|
222
|
-
async function
|
|
223
|
-
await
|
|
241
|
+
async function A(e, n, r = 600, i) {
|
|
242
|
+
await b({
|
|
224
243
|
document: t,
|
|
225
244
|
variables: {
|
|
226
245
|
id: e,
|
|
227
246
|
additionalSeconds: r,
|
|
228
|
-
context:
|
|
247
|
+
context: y(n, i)
|
|
229
248
|
},
|
|
230
249
|
workspaceId: n,
|
|
231
250
|
authContext: i
|
|
232
251
|
});
|
|
233
252
|
}
|
|
234
253
|
//#endregion
|
|
235
|
-
export {
|
|
254
|
+
export { C as createAssistantSandbox, D as createAssistantSession, A as extendAssistantSandboxTTL, S as findExistingSandbox, k as getAssistantMessages, O as sendAssistantPromptAsync, E as waitForAssistantServiceReady, w as waitForSandboxReady };
|
|
236
255
|
|
|
237
256
|
//# sourceMappingURL=assistantApi.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistantApi.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantApi.ts"],"sourcesContent":["/**\n * AI Assistant API service for BigConsole.\n *\n * All requests go through the wspace-public-gateway (GraphQL Federation):\n * Frontend → wspace-public-gateway → wspace-sandbox-svc → ai-assistant (sandbox)\n *\n * This is a faithful port of microfe-vibecontrols' `services/assistantApi.ts`,\n * adapted for BigConsole: the only product-specific difference is\n * `AI_ASSISTANT_PRODUCT=bigconsole` in the sandbox container env (drives the\n * agent's per-product docs fetch + system prompt).\n */\n\nimport { print, type DocumentNode } from 'graphql';\nimport type { AssistantMode, AssistantRawMessage, AssistantSandboxAuthContext } from './types';\nimport type { PageContext } from './pageContext';\nimport {\n CreateSandboxDocument,\n GetSandboxDocument,\n GetActiveSandboxesDocument,\n ProxySandboxRequestDocument,\n ExtendSandboxTtlDocument,\n} from '../../generated/wspace-operations';\n\n// ── Image registry ─────────────────────────────────────────────────\n\nconst ACR_REGISTRY = 'burdenoffregistry.azurecr.io';\nconst GENERAL_IMAGE_TAG = import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-proxy-auth-v1';\nconst VIBE_PLUGINS_IMAGE_TAG = import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-proxy-auth-v1';\nconst API_CALLS_IMAGE_TAG = import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-delegated-auth-v11';\nconst ASSISTANT_API_AUTH_STRATEGY = 'sandbox-app-client-headers-v1';\n\nfunction getImageTagForMode(mode: AssistantMode): string {\n switch (mode) {\n case 'general':\n return GENERAL_IMAGE_TAG;\n case 'vibe-plugins':\n return VIBE_PLUGINS_IMAGE_TAG;\n // The combined default mode runs in the api-calls image (it can make calls).\n case 'assistant':\n case 'api-calls':\n return API_CALLS_IMAGE_TAG;\n }\n}\n\nfunction getImageForMode(mode: AssistantMode): string {\n // `assistant` reuses the api-calls image — one image, mode selected at runtime\n // via AI_ASSISTANT_MODE. So the image segment is api-calls for both.\n const imageMode = mode === 'assistant' ? 'api-calls' : mode;\n return `${ACR_REGISTRY}/wspace/ai-assistant-${imageMode}:${getImageTagForMode(mode)}`;\n}\n\nfunction getResourcesForMode(mode: AssistantMode): {\n cpuRequest: string;\n cpuLimit: string;\n memoryRequest: string;\n memoryLimit: string;\n} {\n switch (mode) {\n case 'general':\n return {\n cpuRequest: '100m',\n cpuLimit: '500m',\n memoryRequest: '256Mi',\n memoryLimit: '1Gi',\n };\n case 'vibe-plugins':\n case 'assistant':\n case 'api-calls':\n return {\n cpuRequest: '150m',\n cpuLimit: '750m',\n memoryRequest: '384Mi',\n memoryLimit: '1536Mi',\n };\n }\n}\n\nfunction resolveSandboxAssistantMode(sandbox: {\n metadata: Record<string, unknown> | null;\n name?: string | null;\n}): AssistantMode | null {\n const metadataMode = sandbox.metadata?.assistantMode;\n if (\n metadataMode === 'assistant' ||\n metadataMode === 'general' ||\n metadataMode === 'api-calls' ||\n metadataMode === 'vibe-plugins'\n ) {\n return metadataMode;\n }\n\n const name = sandbox.name ?? '';\n if (name.startsWith('ai-assistant-vibe-plugins-')) return 'vibe-plugins';\n if (name.startsWith('ai-assistant-api-calls-')) return 'api-calls';\n if (name.startsWith('ai-assistant-general-')) return 'general';\n if (name.startsWith('ai-assistant-assistant-')) return 'assistant';\n return null;\n}\n\n// ── Types ───────────────────────────────────────────────────────────\n\ninterface GraphQLResponse<TData> {\n data?: TData;\n errors?: Array<{ message: string }>;\n}\n\ninterface ProxyResponse {\n proxySandboxRequest: {\n status: number;\n body: Record<string, unknown>;\n };\n}\n\nfunction getGatewayRequestContext(\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): { headers?: Record<string, string> } | undefined {\n const headers: Record<string, string> = {};\n\n if (authContext?.accessToken) {\n headers.authorization = `Bearer ${authContext.accessToken}`;\n }\n\n if (authContext?.workspaceToken) {\n headers['x-workspace-authorization'] = `Bearer ${authContext.workspaceToken}`;\n }\n\n if (workspaceId) {\n headers['x-workspace-id'] = workspaceId;\n }\n\n if (authContext?.userId) {\n headers['x-actor-id'] = authContext.userId;\n headers['x-actor-type'] = 'user';\n }\n\n if (authContext?.organizationId) {\n headers['x-org-id'] = authContext.organizationId;\n }\n\n return Object.keys(headers).length > 0 ? { headers } : undefined;\n}\n\nfunction buildSandboxContextInput(\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Record<string, unknown> {\n return {\n workspaceId,\n ...(authContext?.organizationId\n ? {\n custom: {\n organizationId: authContext.organizationId,\n },\n }\n : {}),\n };\n}\n\nasync function executeSandboxGraphQL<TData>(params: {\n document: DocumentNode;\n variables: Record<string, unknown>;\n workspaceId: string;\n authContext?: AssistantSandboxAuthContext;\n}): Promise<TData> {\n const response = await fetch('/workspaces/graphql', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(getGatewayRequestContext(params.workspaceId, params.authContext)?.headers ?? {}),\n },\n body: JSON.stringify({\n query: print(params.document),\n variables: params.variables,\n }),\n });\n\n const result = (await response.json().catch(() => null)) as GraphQLResponse<TData> | null;\n\n if (!response.ok) {\n const messages = result?.errors?.map((error: { message: string }) => error.message).join(', ');\n console.error('[BigConsole-Assistant] executeSandboxGraphQL HTTP error', {\n status: response.status,\n messages,\n result,\n });\n const httpError = new Error(messages || `Sandbox GraphQL HTTP ${response.status}`) as Error & {\n statusCode?: number;\n result?: unknown;\n };\n httpError.statusCode = response.status;\n httpError.result = result ?? undefined;\n throw httpError;\n }\n\n if (!result) {\n throw new Error('Sandbox GraphQL returned empty response');\n }\n\n if (result.errors?.length) {\n console.error('[BigConsole-Assistant] executeSandboxGraphQL errors', { errors: result.errors });\n throw new Error(result.errors.map((error: { message: string }) => error.message).join(', '));\n }\n\n if (!result.data) {\n throw new Error('Sandbox GraphQL returned no data');\n }\n\n return result.data;\n}\n\n// ── Find existing sandbox for a mode ───────────────────────────────\n\nexport async function findExistingSandbox(\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<string | null> {\n const data = await executeSandboxGraphQL<{\n getActiveSandboxes: Array<{\n id: string;\n name: string;\n status: string;\n metadata: Record<string, unknown> | null;\n template: string;\n createdBy: string;\n }>;\n }>({\n document: GetActiveSandboxesDocument,\n variables: { context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandboxes = data.getActiveSandboxes ?? [];\n\n return (\n sandboxes.find(\n (s) =>\n (s.status === 'RUNNING' || s.status === 'PROVISIONING' || s.status === 'PENDING') &&\n s.template === 'AI_BUILDER' &&\n resolveSandboxAssistantMode(s) === mode &&\n (!authContext?.userId || s.createdBy === authContext.userId) &&\n s.metadata?.authStrategy === ASSISTANT_API_AUTH_STRATEGY &&\n s.metadata?.imageTag === getImageTagForMode(mode)\n )?.id ?? null\n );\n}\n\n// ── Sandbox lifecycle ──────────────────────────────────────────────\n\nexport async function createAssistantSandbox(\n mode: AssistantMode,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<string> {\n const resources = getResourcesForMode(mode);\n const env = [\n { name: 'AI_ASSISTANT_MODE', value: mode },\n { name: 'HOSTNAME', value: '0.0.0.0' },\n { name: 'WORKSPACE_ID', value: workspaceId },\n // Product the user is in — drives the agent's dynamic per-product docs fetch\n // (the docs repo is `<product>-docs`) and personalizes its system prompt.\n // This package is the BigConsole MFE, so the product is bigconsole.\n { name: 'AI_ASSISTANT_PRODUCT', value: 'bigconsole' },\n ];\n\n const data = await executeSandboxGraphQL<{ createSandbox: { id: string } }>({\n document: CreateSandboxDocument,\n variables: {\n input: {\n name: `ai-assistant-${mode}-${Date.now()}`,\n description: `AI Assistant sandbox (${mode} mode)`,\n template: 'AI_BUILDER',\n mode: 'DEPLOYMENT',\n containers: [\n {\n name: 'ai-assistant',\n image: getImageForMode(mode),\n // The agent (start-assistant.sh) binds :8080; the sandbox svc proxies\n // to this declared containerPort, so it MUST be 8080.\n ports: [{ containerPort: 8080, name: 'http' }],\n env,\n },\n ],\n resources: {\n cpuRequest: resources.cpuRequest,\n cpuLimit: resources.cpuLimit,\n memoryRequest: resources.memoryRequest,\n memoryLimit: resources.memoryLimit,\n },\n ttlSeconds: 600,\n metadata: {\n assistantMode: mode,\n assistantOwnerUserId: authContext?.userId ?? null,\n assistantOrganizationId: authContext?.organizationId ?? null,\n authStrategy: ASSISTANT_API_AUTH_STRATEGY,\n imageTag: getImageTagForMode(mode),\n },\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n if (!data?.createSandbox?.id) {\n throw new Error('createSandbox returned empty response');\n }\n return data.createSandbox.id;\n}\n\nexport async function waitForSandboxReady(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 300_000\n): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < maxWaitMs) {\n const data = await executeSandboxGraphQL<{\n getSandbox: { status?: string; errorMessage?: string } | null;\n }>({\n document: GetSandboxDocument,\n variables: { id: sandboxId, context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const status = data?.getSandbox?.status;\n const errorMessage = data?.getSandbox?.errorMessage as string | undefined;\n if (status === 'RUNNING') return;\n if (status === 'FAILED' || status === 'STOPPED' || status === 'EXPIRED') {\n throw new Error(\n errorMessage ? `Sandbox ${status.toLowerCase()}: ${errorMessage}` : `Sandbox ${status.toLowerCase()}`\n );\n }\n await new Promise((r) => setTimeout(r, 2000));\n }\n throw new Error('Sandbox startup timed out');\n}\n\n// ── Proxy calls to ai-assistant (via gateway → sandbox-svc) ───────\n\nasync function proxyToSandbox(\n sandboxId: string,\n workspaceId: string,\n path: string,\n method: string,\n body?: unknown,\n authContext?: AssistantSandboxAuthContext\n): Promise<Record<string, unknown>> {\n console.log('[BigConsole-Assistant] proxyToSandbox REQUEST', {\n sandboxId,\n workspaceId,\n path,\n method,\n hasBody: !!body,\n });\n const result = await executeSandboxGraphQL<ProxyResponse>({\n document: ProxySandboxRequestDocument,\n variables: {\n input: {\n sandboxId,\n path,\n method,\n body,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n const proxyResult = result.proxySandboxRequest;\n console.log('[BigConsole-Assistant] proxyToSandbox RESPONSE', { sandboxId, path, status: proxyResult?.status });\n if (!proxyResult || proxyResult.status >= 400) {\n const errBody = proxyResult?.body as Record<string, unknown> | undefined;\n console.error('[BigConsole-Assistant] proxyToSandbox ERROR', {\n sandboxId,\n path,\n status: proxyResult?.status,\n errBody,\n });\n throw new Error((errBody?.error as string) ?? `Proxy error: ${proxyResult?.status ?? 'unknown'}`);\n }\n\n return proxyResult.body;\n}\n\nexport async function waitForAssistantServiceReady(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 90_000\n): Promise<void> {\n const startedAt = Date.now();\n\n while (Date.now() - startedAt < maxWaitMs) {\n try {\n await proxyToSandbox(sandboxId, workspaceId, '/health', 'GET', undefined, authContext);\n return;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!/proxy error: 502|proxy error: 503|proxy error: 404|service not available/i.test(message)) {\n throw error;\n }\n }\n\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n throw new Error('Assistant service did not become healthy before timeout');\n}\n\n// ── Session management ─────────────────────────────────────────────\n\nexport async function createAssistantSession(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<{ sessionId: string }> {\n const body = await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'POST', { mode }, authContext);\n const session = body.session as Record<string, unknown>;\n return { sessionId: session.id as string };\n}\n\n// ── Send prompt ────────────────────────────────────────────────────\n\nexport async function sendAssistantPromptAsync(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n prompt: string,\n mode: AssistantMode,\n pageContext?: PageContext,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n const requestBody = {\n mode,\n prompt,\n pageContext: pageContext ?? undefined,\n };\n\n await proxyToSandbox(sandboxId, workspaceId, `/sessions/${sessionId}/prompt-async`, 'POST', requestBody, authContext);\n}\n\nexport async function getAssistantMessages(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext,\n limit = 50\n): Promise<AssistantRawMessage[]> {\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/messages?limit=${limit}`,\n 'GET',\n undefined,\n authContext\n );\n\n return (body.messages as AssistantRawMessage[] | undefined) ?? [];\n}\n\n// ── TTL extension (keep the sandbox warm across the turn) ──────────\n\nexport async function extendAssistantSandboxTTL(\n sandboxId: string,\n workspaceId: string,\n additionalSeconds = 600,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await executeSandboxGraphQL<{ extendSandboxTTL: { id: string } }>({\n document: ExtendSandboxTtlDocument,\n variables: {\n id: sandboxId,\n additionalSeconds,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n workspaceId,\n authContext,\n });\n}\n"],"mappings":";;;AAyBA,IAAM,IAAe,gCACf,IAAuE,uBACvE,IAA4E,uBAC5E,IAAyE,4BACzE,IAA8B;AAEpC,SAAS,EAAmB,GAA6B;AACvD,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;EACT,KAAK,eACH,QAAO;EAET,KAAK;EACL,KAAK,YACH,QAAO;;;AAIb,SAAS,EAAgB,GAA6B;AAIpD,QAAO,GAAG,EAAa,uBADL,MAAS,cAAc,cAAc,EACC,GAAG,EAAmB,EAAK;;AAGrF,SAAS,EAAoB,GAK3B;AACA,SAAQ,GAAR;EACE,KAAK,UACH,QAAO;GACL,YAAY;GACZ,UAAU;GACV,eAAe;GACf,aAAa;GACd;EACH,KAAK;EACL,KAAK;EACL,KAAK,YACH,QAAO;GACL,YAAY;GACZ,UAAU;GACV,eAAe;GACf,aAAa;GACd;;;AAIP,SAAS,EAA4B,GAGZ;CACvB,IAAM,IAAe,EAAQ,UAAU;AACvC,KACE,MAAiB,eACjB,MAAiB,aACjB,MAAiB,eACjB,MAAiB,eAEjB,QAAO;CAGT,IAAM,IAAO,EAAQ,QAAQ;AAK7B,QAJI,EAAK,WAAW,6BAA6B,GAAS,iBACtD,EAAK,WAAW,0BAA0B,GAAS,cACnD,EAAK,WAAW,wBAAwB,GAAS,YACjD,EAAK,WAAW,0BAA0B,GAAS,cAChD;;AAiBT,SAAS,EACP,GACA,GACkD;CAClD,IAAM,IAAkC,EAAE;AAuB1C,QArBI,GAAa,gBACf,EAAQ,gBAAgB,UAAU,EAAY,gBAG5C,GAAa,mBACf,EAAQ,+BAA+B,UAAU,EAAY,mBAG3D,MACF,EAAQ,oBAAoB,IAG1B,GAAa,WACf,EAAQ,gBAAgB,EAAY,QACpC,EAAQ,kBAAkB,SAGxB,GAAa,mBACf,EAAQ,cAAc,EAAY,iBAG7B,OAAO,KAAK,EAAQ,CAAC,SAAS,IAAI,EAAE,YAAS,GAAG,KAAA;;AAGzD,SAAS,EACP,GACA,GACyB;AACzB,QAAO;EACL;EACA,GAAI,GAAa,iBACb,EACE,QAAQ,EACN,gBAAgB,EAAY,gBAC7B,EACF,GACD,EAAE;EACP;;AAGH,eAAe,EAA6B,GAKzB;CACjB,IAAM,IAAW,MAAM,MAAM,uBAAuB;EAClD,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,GAAI,EAAyB,EAAO,aAAa,EAAO,YAAY,EAAE,WAAW,EAAE;GACpF;EACD,MAAM,KAAK,UAAU;GACnB,OAAO,EAAM,EAAO,SAAS;GAC7B,WAAW,EAAO;GACnB,CAAC;EACH,CAAC,EAEI,IAAU,MAAM,EAAS,MAAM,CAAC,YAAY,KAAK;AAEvD,KAAI,CAAC,EAAS,IAAI;EAChB,IAAM,IAAW,GAAQ,QAAQ,KAAK,MAA+B,EAAM,QAAQ,CAAC,KAAK,KAAK;AAC9F,UAAQ,MAAM,2DAA2D;GACvE,QAAQ,EAAS;GACjB;GACA;GACD,CAAC;EACF,IAAM,IAAgB,MAAM,KAAY,wBAAwB,EAAS,SAAS;AAMlF,QAFA,EAAU,aAAa,EAAS,QAChC,EAAU,SAAS,KAAU,KAAA,GACvB;;AAGR,KAAI,CAAC,EACH,OAAU,MAAM,0CAA0C;AAG5D,KAAI,EAAO,QAAQ,OAEjB,OADA,QAAQ,MAAM,uDAAuD,EAAE,QAAQ,EAAO,QAAQ,CAAC,EACrF,MAAM,EAAO,OAAO,KAAK,MAA+B,EAAM,QAAQ,CAAC,KAAK,KAAK,CAAC;AAG9F,KAAI,CAAC,EAAO,KACV,OAAU,MAAM,mCAAmC;AAGrD,QAAO,EAAO;;AAKhB,eAAsB,EACpB,GACA,GACA,GACwB;AAkBxB,UAjBa,MAAM,EAShB;EACD,UAAU;EACV,WAAW,EAAE,SAAS,EAAyB,GAAa,EAAY,EAAE;EAC1E;EACA;EACD,CAAC,EACqB,sBAAsB,EAAE,EAGnC,MACP,OACE,EAAE,WAAW,aAAa,EAAE,WAAW,kBAAkB,EAAE,WAAW,cACvE,EAAE,aAAa,gBACf,EAA4B,EAAE,KAAK,MAClC,CAAC,GAAa,UAAU,EAAE,cAAc,EAAY,WACrD,EAAE,UAAU,iBAAiB,KAC7B,EAAE,UAAU,aAAa,EAAmB,EAAK,CACpD,EAAE,MAAM;;AAMb,eAAsB,EACpB,GACA,GACA,GACiB;CACjB,IAAM,IAAY,EAAoB,EAAK,EACrC,IAAM;EACV;GAAE,MAAM;GAAqB,OAAO;GAAM;EAC1C;GAAE,MAAM;GAAY,OAAO;GAAW;EACtC;GAAE,MAAM;GAAgB,OAAO;GAAa;EAI5C;GAAE,MAAM;GAAwB,OAAO;GAAc;EACtD,EAEK,IAAO,MAAM,EAAyD;EAC1E,UAAU;EACV,WAAW,EACT,OAAO;GACL,MAAM,gBAAgB,EAAK,GAAG,KAAK,KAAK;GACxC,aAAa,yBAAyB,EAAK;GAC3C,UAAU;GACV,MAAM;GACN,YAAY,CACV;IACE,MAAM;IACN,OAAO,EAAgB,EAAK;IAG5B,OAAO,CAAC;KAAE,eAAe;KAAM,MAAM;KAAQ,CAAC;IAC9C;IACD,CACF;GACD,WAAW;IACT,YAAY,EAAU;IACtB,UAAU,EAAU;IACpB,eAAe,EAAU;IACzB,aAAa,EAAU;IACxB;GACD,YAAY;GACZ,UAAU;IACR,eAAe;IACf,sBAAsB,GAAa,UAAU;IAC7C,yBAAyB,GAAa,kBAAkB;IACxD,cAAc;IACd,UAAU,EAAmB,EAAK;IACnC;GACD,SAAS,EAAyB,GAAa,EAAY;GAC5D,EACF;EACD;EACA;EACD,CAAC;AAEF,KAAI,CAAC,GAAM,eAAe,GACxB,OAAU,MAAM,wCAAwC;AAE1D,QAAO,EAAK,cAAc;;AAG5B,eAAsB,EACpB,GACA,GACA,GACA,IAAY,KACG;CACf,IAAM,IAAQ,KAAK,KAAK;AACxB,QAAO,KAAK,KAAK,GAAG,IAAQ,IAAW;EACrC,IAAM,IAAO,MAAM,EAEhB;GACD,UAAU;GACV,WAAW;IAAE,IAAI;IAAW,SAAS,EAAyB,GAAa,EAAY;IAAE;GACzF;GACA;GACD,CAAC,EACI,IAAS,GAAM,YAAY,QAC3B,IAAe,GAAM,YAAY;AACvC,MAAI,MAAW,UAAW;AAC1B,MAAI,MAAW,YAAY,MAAW,aAAa,MAAW,UAC5D,OAAU,MACR,IAAe,WAAW,EAAO,aAAa,CAAC,IAAI,MAAiB,WAAW,EAAO,aAAa,GACpG;AAEH,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAK,CAAC;;AAE/C,OAAU,MAAM,4BAA4B;;AAK9C,eAAe,EACb,GACA,GACA,GACA,GACA,GACA,GACkC;AAClC,SAAQ,IAAI,iDAAiD;EAC3D;EACA;EACA;EACA;EACA,SAAS,CAAC,CAAC;EACZ,CAAC;CAgBF,IAAM,KAfS,MAAM,EAAqC;EACxD,UAAU;EACV,WAAW,EACT,OAAO;GACL;GACA;GACA;GACA;GACA,SAAS,EAAyB,GAAa,EAAY;GAC5D,EACF;EACD;EACA;EACD,CAAC,EAEyB;AAE3B,KADA,QAAQ,IAAI,kDAAkD;EAAE;EAAW;EAAM,QAAQ,GAAa;EAAQ,CAAC,EAC3G,CAAC,KAAe,EAAY,UAAU,KAAK;EAC7C,IAAM,IAAU,GAAa;AAO7B,QANA,QAAQ,MAAM,+CAA+C;GAC3D;GACA;GACA,QAAQ,GAAa;GACrB;GACD,CAAC,EACQ,MAAO,GAAS,SAAoB,gBAAgB,GAAa,UAAU,YAAY;;AAGnG,QAAO,EAAY;;AAGrB,eAAsB,EACpB,GACA,GACA,GACA,IAAY,KACG;CACf,IAAM,IAAY,KAAK,KAAK;AAE5B,QAAO,KAAK,KAAK,GAAG,IAAY,IAAW;AACzC,MAAI;AACF,SAAM,EAAe,GAAW,GAAa,WAAW,OAAO,KAAA,GAAW,EAAY;AACtF;WACO,GAAO;GACd,IAAM,IAAU,aAAiB,QAAQ,EAAM,UAAU,OAAO,EAAM;AACtE,OAAI,CAAC,4EAA4E,KAAK,EAAQ,CAC5F,OAAM;;AAIV,QAAM,IAAI,SAAS,MAAY,WAAW,GAAS,IAAK,CAAC;;AAG3D,OAAU,MAAM,0DAA0D;;AAK5E,eAAsB,EACpB,GACA,GACA,GACA,GACgC;AAGhC,QAAO,EAAE,YAFI,MAAM,EAAe,GAAW,GAAa,aAAa,QAAQ,EAAE,SAAM,EAAE,EAAY,EAChF,QACO,IAAc;;AAK5C,eAAsB,EACpB,GACA,GACA,GACA,GACA,GACA,GACA,GACe;CACf,IAAM,IAAc;EAClB;EACA;EACA,aAAa,KAAe,KAAA;EAC7B;AAED,OAAM,EAAe,GAAW,GAAa,aAAa,EAAU,gBAAgB,QAAQ,GAAa,EAAY;;AAGvH,eAAsB,EACpB,GACA,GACA,GACA,GACA,IAAQ,IACwB;AAUhC,SATa,MAAM,EACjB,GACA,GACA,aAAa,EAAU,kBAAkB,KACzC,OACA,KAAA,GACA,EACD,EAEY,YAAkD,EAAE;;AAKnE,eAAsB,EACpB,GACA,GACA,IAAoB,KACpB,GACe;AACf,OAAM,EAA4D;EAChE,UAAU;EACV,WAAW;GACT,IAAI;GACJ;GACA,SAAS,EAAyB,GAAa,EAAY;GAC5D;EACD;EACA;EACD,CAAC"}
|
|
1
|
+
{"version":3,"file":"assistantApi.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantApi.ts"],"sourcesContent":["/**\n * AI Assistant API service for BigConsole.\n *\n * All requests go through the wspace-public-gateway (GraphQL Federation):\n * Frontend → wspace-public-gateway → wspace-sandbox-svc → ai-assistant (sandbox)\n *\n * This is a faithful port of microfe-vibecontrols' `services/assistantApi.ts`,\n * adapted for BigConsole: the only product-specific difference is\n * `AI_ASSISTANT_PRODUCT=bigconsole` in the sandbox container env (drives the\n * agent's per-product docs fetch + system prompt).\n */\n\nimport { print, type DocumentNode, Kind, type OperationDefinitionNode } from 'graphql';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type { AssistantMode, AssistantRawMessage, AssistantSandboxAuthContext } from './types';\nimport type { PageContext } from './pageContext';\nimport {\n CreateSandboxDocument,\n GetSandboxDocument,\n GetActiveSandboxesDocument,\n ProxySandboxRequestDocument,\n ExtendSandboxTtlDocument,\n} from '../../generated/wspace-operations';\n\n// ── Image registry ─────────────────────────────────────────────────\n\nconst ACR_REGISTRY = 'burdenoffregistry.azurecr.io';\nconst GENERAL_IMAGE_TAG = import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-proxy-auth-v1';\nconst VIBE_PLUGINS_IMAGE_TAG = import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-proxy-auth-v1';\nconst API_CALLS_IMAGE_TAG = import.meta.env.VITE_APP_ENV === 'prod' ? 'prod' : 'alpha-delegated-auth-v11';\nconst ASSISTANT_API_AUTH_STRATEGY = 'sandbox-app-client-headers-v1';\n\nfunction getImageTagForMode(mode: AssistantMode): string {\n switch (mode) {\n case 'general':\n return GENERAL_IMAGE_TAG;\n case 'vibe-plugins':\n return VIBE_PLUGINS_IMAGE_TAG;\n // The combined default mode runs in the api-calls image (it can make calls).\n case 'assistant':\n case 'api-calls':\n return API_CALLS_IMAGE_TAG;\n }\n}\n\nfunction getImageForMode(mode: AssistantMode): string {\n // `assistant` reuses the api-calls image — one image, mode selected at runtime\n // via AI_ASSISTANT_MODE. So the image segment is api-calls for both.\n const imageMode = mode === 'assistant' ? 'api-calls' : mode;\n return `${ACR_REGISTRY}/wspace/ai-assistant-${imageMode}:${getImageTagForMode(mode)}`;\n}\n\nfunction getResourcesForMode(mode: AssistantMode): {\n cpuRequest: string;\n cpuLimit: string;\n memoryRequest: string;\n memoryLimit: string;\n} {\n switch (mode) {\n case 'general':\n return {\n cpuRequest: '100m',\n cpuLimit: '500m',\n memoryRequest: '256Mi',\n memoryLimit: '1Gi',\n };\n case 'vibe-plugins':\n case 'assistant':\n case 'api-calls':\n return {\n cpuRequest: '150m',\n cpuLimit: '750m',\n memoryRequest: '384Mi',\n memoryLimit: '1536Mi',\n };\n }\n}\n\nfunction resolveSandboxAssistantMode(sandbox: {\n metadata: Record<string, unknown> | null;\n name?: string | null;\n}): AssistantMode | null {\n const metadataMode = sandbox.metadata?.assistantMode;\n if (\n metadataMode === 'assistant' ||\n metadataMode === 'general' ||\n metadataMode === 'api-calls' ||\n metadataMode === 'vibe-plugins'\n ) {\n return metadataMode;\n }\n\n const name = sandbox.name ?? '';\n if (name.startsWith('ai-assistant-vibe-plugins-')) return 'vibe-plugins';\n if (name.startsWith('ai-assistant-api-calls-')) return 'api-calls';\n if (name.startsWith('ai-assistant-general-')) return 'general';\n if (name.startsWith('ai-assistant-assistant-')) return 'assistant';\n return null;\n}\n\n// ── Types ───────────────────────────────────────────────────────────\n\ninterface ProxyResponse {\n proxySandboxRequest: {\n status: number;\n body: Record<string, unknown>;\n };\n}\n\nfunction getOperationName(document: DocumentNode): string | undefined {\n const operation = document.definitions.find(\n (definition): definition is OperationDefinitionNode => definition.kind === Kind.OPERATION_DEFINITION\n );\n return operation?.name?.value;\n}\n\nfunction buildAssistantRequestId(): string {\n try {\n return `bc-assistant-${crypto.randomUUID()}`;\n } catch {\n return `bc-assistant-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n }\n}\n\nfunction buildSandboxContextInput(\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Record<string, unknown> {\n return {\n workspaceId,\n ...(authContext?.organizationId\n ? {\n custom: {\n organizationId: authContext.organizationId,\n },\n }\n : {}),\n };\n}\n\nasync function executeSandboxGraphQL<TData>(params: {\n document: DocumentNode;\n variables: Record<string, unknown>;\n workspaceId: string;\n authContext?: AssistantSandboxAuthContext;\n}): Promise<TData> {\n // Retry transient failures (network drops, CORS preflight rejections,\n // gateway 524s, etc.) with bounded exponential backoff. The browser\n // surfaces these as \"Failed to fetch\" via fetch() throwing TypeError\n // before the runtime can inspect the response — so the only signal we\n // have is the throw itself. Bounded retries mask the gateway's\n // occasional CORS preflight races without papering over hard failures\n // (unauth, validation) which the inner block below raises as typed\n // errors that bubble up to the assistant's existing `isRecoverable`\n // retry path.\n const maxRetries = 3;\n let lastError: unknown;\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await executeSandboxGraphQLOnce<TData>(params);\n } catch (error) {\n lastError = error;\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n // Only retry on transient network-level failures. Let domain errors\n // (auth, validation, server 5xx with a body) bubble immediately so\n // the outer `isRecoverable` logic can decide whether to drop the\n // sandbox and try a clean runtime instead.\n const isTransient =\n message.includes('failed to fetch') ||\n message.includes('networkerror') ||\n message.includes('network request failed') ||\n message.includes('aborted') ||\n message.includes('load failed');\n if (!isTransient || attempt === maxRetries - 1) {\n throw error;\n }\n // 250ms, 500ms, 1000ms — bounded so we don't blow the 90s gateway\n // execution timeout on a stuck connection.\n const backoff = 250 * Math.pow(2, attempt);\n console.warn('[BigConsole-Assistant] executeSandboxGraphQL transient failure, retrying', {\n attempt: attempt + 1,\n maxRetries,\n backoffMs: backoff,\n error: error instanceof Error ? error.message : String(error),\n });\n await new Promise((resolve) => setTimeout(resolve, backoff));\n }\n }\n // Unreachable — the loop either returns or throws.\n throw lastError;\n}\n\nasync function executeSandboxGraphQLOnce<TData>(params: {\n document: DocumentNode;\n variables: Record<string, unknown>;\n workspaceId: string;\n authContext?: AssistantSandboxAuthContext;\n}): Promise<TData> {\n const operationName = getOperationName(params.document);\n const result = await graphqlFetch<TData>({\n gateway: 'workspace',\n query: print(params.document),\n variables: params.variables,\n operationName,\n authToken: params.authContext?.accessToken ?? undefined,\n // Critical: opt into workspace token auto-minting when the current token\n // is missing or stale for the active workspace/project. Relying on raw\n // browser x-workspace-id alone is insufficient on the public gateway\n // because the gateway strips spoof-sensitive headers unless they can be\n // reconstructed from trusted auth state.\n workspaceToken: params.authContext?.workspaceToken || true,\n workspaceId: params.workspaceId,\n organizationId: params.authContext?.organizationId ?? true,\n suppressGlobalErrorEvent: true,\n // Give assistant calls a unique queue key so they never batch with the\n // dashboard's burst of background widget requests. This avoids mixing\n // long-lived assistant polls with unrelated queries in the shared\n // graphqlFetch batching queue.\n extraHeaders: {\n 'x-request-id': buildAssistantRequestId(),\n },\n });\n\n if (!result) {\n throw new Error('Sandbox GraphQL returned empty response');\n }\n\n if (result.errors?.length) {\n console.error('[BigConsole-Assistant] executeSandboxGraphQL errors', {\n operationName,\n errors: result.errors,\n });\n throw new Error(result.errors.map((error: { message: string }) => error.message).join(', '));\n }\n\n if (!result.data) {\n throw new Error('Sandbox GraphQL returned no data');\n }\n\n return result.data;\n}\n\n// ── Find existing sandbox for a mode ───────────────────────────────\n\nexport async function findExistingSandbox(\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<string | null> {\n const data = await executeSandboxGraphQL<{\n getActiveSandboxes: Array<{\n id: string;\n name: string;\n status: string;\n metadata: Record<string, unknown> | null;\n template: string;\n createdBy: string;\n }>;\n }>({\n document: GetActiveSandboxesDocument,\n variables: { context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandboxes = data.getActiveSandboxes ?? [];\n\n return (\n sandboxes.find(\n (s) =>\n (s.status === 'RUNNING' || s.status === 'PROVISIONING' || s.status === 'PENDING') &&\n s.template === 'AI_BUILDER' &&\n resolveSandboxAssistantMode(s) === mode &&\n (!authContext?.userId || s.createdBy === authContext.userId) &&\n s.metadata?.authStrategy === ASSISTANT_API_AUTH_STRATEGY &&\n s.metadata?.imageTag === getImageTagForMode(mode)\n )?.id ?? null\n );\n}\n\n// ── Sandbox lifecycle ──────────────────────────────────────────────\n\nexport async function createAssistantSandbox(\n mode: AssistantMode,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<string> {\n const resources = getResourcesForMode(mode);\n const env = [\n { name: 'AI_ASSISTANT_MODE', value: mode },\n { name: 'HOSTNAME', value: '0.0.0.0' },\n { name: 'WORKSPACE_ID', value: workspaceId },\n // Product the user is in — drives the agent's dynamic per-product docs fetch\n // (the docs repo is `<product>-docs`) and personalizes its system prompt.\n // This package is the BigConsole MFE, so the product is bigconsole.\n { name: 'AI_ASSISTANT_PRODUCT', value: 'bigconsole' },\n ];\n\n const data = await executeSandboxGraphQL<{ createSandbox: { id: string } }>({\n document: CreateSandboxDocument,\n variables: {\n input: {\n name: `ai-assistant-${mode}-${Date.now()}`,\n description: `AI Assistant sandbox (${mode} mode)`,\n template: 'AI_BUILDER',\n mode: 'DEPLOYMENT',\n containers: [\n {\n name: 'ai-assistant',\n image: getImageForMode(mode),\n // The agent (start-assistant.sh) binds :8080; the sandbox svc proxies\n // to this declared containerPort, so it MUST be 8080.\n ports: [{ containerPort: 8080, name: 'http' }],\n env,\n },\n ],\n resources: {\n cpuRequest: resources.cpuRequest,\n cpuLimit: resources.cpuLimit,\n memoryRequest: resources.memoryRequest,\n memoryLimit: resources.memoryLimit,\n },\n ttlSeconds: 600,\n metadata: {\n assistantMode: mode,\n assistantOwnerUserId: authContext?.userId ?? null,\n assistantOrganizationId: authContext?.organizationId ?? null,\n authStrategy: ASSISTANT_API_AUTH_STRATEGY,\n imageTag: getImageTagForMode(mode),\n },\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n if (!data?.createSandbox?.id) {\n throw new Error('createSandbox returned empty response');\n }\n return data.createSandbox.id;\n}\n\nexport async function waitForSandboxReady(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 300_000\n): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < maxWaitMs) {\n const data = await executeSandboxGraphQL<{\n getSandbox: { status?: string; errorMessage?: string } | null;\n }>({\n document: GetSandboxDocument,\n variables: { id: sandboxId, context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const status = data?.getSandbox?.status;\n const errorMessage = data?.getSandbox?.errorMessage as string | undefined;\n if (status === 'RUNNING') return;\n if (status === 'FAILED' || status === 'STOPPED' || status === 'EXPIRED') {\n throw new Error(\n errorMessage ? `Sandbox ${status.toLowerCase()}: ${errorMessage}` : `Sandbox ${status.toLowerCase()}`\n );\n }\n await new Promise((r) => setTimeout(r, 2000));\n }\n throw new Error('Sandbox startup timed out');\n}\n\n// ── Proxy calls to ai-assistant (via gateway → sandbox-svc) ───────\n\nasync function proxyToSandbox(\n sandboxId: string,\n workspaceId: string,\n path: string,\n method: string,\n body?: unknown,\n authContext?: AssistantSandboxAuthContext\n): Promise<Record<string, unknown>> {\n console.log('[BigConsole-Assistant] proxyToSandbox REQUEST', {\n sandboxId,\n workspaceId,\n path,\n method,\n hasBody: !!body,\n });\n const result = await executeSandboxGraphQL<ProxyResponse>({\n document: ProxySandboxRequestDocument,\n variables: {\n input: {\n sandboxId,\n path,\n method,\n body,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n const proxyResult = result.proxySandboxRequest;\n console.log('[BigConsole-Assistant] proxyToSandbox RESPONSE', { sandboxId, path, status: proxyResult?.status });\n if (!proxyResult || proxyResult.status >= 400) {\n const errBody = proxyResult?.body as Record<string, unknown> | undefined;\n console.error('[BigConsole-Assistant] proxyToSandbox ERROR', {\n sandboxId,\n path,\n status: proxyResult?.status,\n errBody,\n });\n throw new Error((errBody?.error as string) ?? `Proxy error: ${proxyResult?.status ?? 'unknown'}`);\n }\n\n return proxyResult.body;\n}\n\nexport async function waitForAssistantServiceReady(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 90_000\n): Promise<void> {\n const startedAt = Date.now();\n\n while (Date.now() - startedAt < maxWaitMs) {\n try {\n await proxyToSandbox(sandboxId, workspaceId, '/health', 'GET', undefined, authContext);\n return;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!/proxy error: 502|proxy error: 503|proxy error: 404|service not available/i.test(message)) {\n throw error;\n }\n }\n\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n throw new Error('Assistant service did not become healthy before timeout');\n}\n\n// ── Session management ─────────────────────────────────────────────\n\nexport async function createAssistantSession(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<{ sessionId: string }> {\n const body = await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'POST', { mode }, authContext);\n const session = body.session as Record<string, unknown>;\n return { sessionId: session.id as string };\n}\n\n// ── Send prompt ────────────────────────────────────────────────────\n\nexport async function sendAssistantPromptAsync(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n prompt: string,\n mode: AssistantMode,\n pageContext?: PageContext,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n const requestBody = {\n mode,\n prompt,\n pageContext: pageContext ?? undefined,\n };\n\n await proxyToSandbox(sandboxId, workspaceId, `/sessions/${sessionId}/prompt-async`, 'POST', requestBody, authContext);\n}\n\nexport async function getAssistantMessages(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext,\n limit = 50\n): Promise<AssistantRawMessage[]> {\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/messages?limit=${limit}`,\n 'GET',\n undefined,\n authContext\n );\n\n return (body.messages as AssistantRawMessage[] | undefined) ?? [];\n}\n\n// ── TTL extension (keep the sandbox warm across the turn) ──────────\n\nexport async function extendAssistantSandboxTTL(\n sandboxId: string,\n workspaceId: string,\n additionalSeconds = 600,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await executeSandboxGraphQL<{ extendSandboxTTL: { id: string } }>({\n document: ExtendSandboxTtlDocument,\n variables: {\n id: sandboxId,\n additionalSeconds,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n workspaceId,\n authContext,\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../../src/bigconsole/assistant/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * BigConsole adapter for the shared fe-libs AssistantWidget.\n *\n * The floating widget (fe-libs, Layer 1) is backend-agnostic — it calls an\n * injected `AssistantTransport`. This hook builds a transport that drives the\n * sandbox AI assistant (combined `assistant` mode = docs Q&A + api-calls): it\n * provisions/reuses a sandbox, opens a session, dispatches the prompt async,\n * then polls for the streamed answer. The agent introspects the GraphQL schema\n * and performs API calls on the user's behalf using their workspace token,\n * contextual to the current screen (via `gatherPageContext`).\n *\n * This is a faithful port of microfe-vibecontrols'\n * `services/createSandboxAssistantTransport.ts`; the only product-specific\n * difference lives in `assistantApi.createAssistantSandbox`\n * (`AI_ASSISTANT_PRODUCT=bigconsole`).\n */\n\nimport { useCallback, useMemo, useRef } from 'react';\nimport { useAuthToken } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n createAssistantSandbox,\n createAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantMessages,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n} from './assistantApi';\nimport { gatherPageContext } from './pageContext';\nimport type { AssistantMode, AssistantRawMessage, AssistantSandboxAuthContext } from './types';\n\n/**\n * Locally-defined mirror of the fe-libs `AssistantTransport` contract.\n *\n * Intentionally NOT imported from `@burdenoff/fe-libs`: microfe's tsconfig maps\n * `@burdenoff/fe-libs/*` to fe-libs *source*, so vite-plugin-dts would rewrite a\n * cross-package type used in this hook's public signature to a broken\n * source-relative path in the emitted `.d.ts`. Structural typing makes this\n * shape assignable to fe-libs' `AssistantTransport` at the call site\n * (bigconsole-app's AppShell), which is where compatibility is enforced.\n */\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n}\n\n// The sandbox runs the `ai-assistant-api-calls` image (see assistantApi\n// `getImageForMode` — `assistant` maps to that image). The combined `assistant`\n// mode IS supported by the CF proxy images (v2026.618.3+). VibeControls uses\n// `assistant` mode in production successfully. This enables docs Q&A + API calls.\nconst MODE: AssistantMode = 'assistant';\nconst STREAM_BUDGET_MS = 180_000;\nconst POLL_INTERVAL_MS = 1500;\nconst TTL_EXTEND_INTERVAL_MS = 30_000;\n\n// ── Context helpers (mirror vibecontrols' resolution) ────────────────\n\nfunction getProfileContextValue(key: 'workspaceId' | 'organizationId'): string {\n try {\n const activeContextKey =\n key === 'workspaceId' ? 'burdenoff-active-context-workspace' : 'burdenoff-active-context-organization';\n const activeContextValue = localStorage.getItem(activeContextKey);\n if (activeContextValue) return activeContextValue;\n\n const activeProfileId = sessionStorage.getItem('bf-active-profile');\n if (!activeProfileId) return '';\n const raw = localStorage.getItem(`bf-p-${activeProfileId}-context`);\n if (!raw) return '';\n const context = JSON.parse(raw) as { workspaceId?: string; organizationId?: string };\n return context[key] ?? '';\n } catch {\n return '';\n }\n}\n\nfunction getWorkspaceId(fallback: string | null): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('workspace') ?? getProfileContextValue('workspaceId') ?? fallback ?? '';\n}\n\nfunction getOrganizationId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('org') ?? getProfileContextValue('organizationId');\n}\n\n// ── Message-progress helpers (pure; mirror vibecontrols) ─────────────\n\nfunction getMessageRole(message: AssistantRawMessage): string | undefined {\n // Check nested format first, then flat format\n return message.info?.role ?? message.role;\n}\n\nfunction getRawMessageCreatedAt(message: AssistantRawMessage): number {\n // Check nested format first (epoch ms), then flat format (ISO string or epoch ms)\n const nested = message.info?.time?.created;\n if (nested !== undefined) return nested;\n const flat = message.createdAt;\n if (flat === undefined) return 0;\n // If it's a string (ISO), parse it; otherwise treat as epoch ms\n if (typeof flat === 'string') {\n const parsed = Date.parse(flat);\n return isNaN(parsed) ? 0 : parsed;\n }\n return flat;\n}\n\nfunction getMessageCompleted(message: AssistantRawMessage): number | undefined {\n // Check nested format first, then flat format\n const nested = message.info?.time?.completed;\n if (nested !== undefined) return nested;\n const flat = message.completedAt;\n if (flat === undefined) return undefined;\n if (typeof flat === 'string') {\n const parsed = Date.parse(flat);\n return isNaN(parsed) ? undefined : parsed;\n }\n return flat;\n}\n\nfunction getAssistantText(message: AssistantRawMessage): string {\n // Check parts format first (nested), then flat content\n const parts = message.parts ?? [];\n const textFromParts = (parts ?? [])\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text?.trim() ?? '')\n .filter(Boolean)\n .join('\\n');\n if (textFromParts) return textFromParts;\n // Fallback to flat content field\n return typeof message.content === 'string' ? message.content.trim() : '';\n}\n\nfunction getToolProgress(message: AssistantRawMessage): string[] {\n const parts = message.parts ?? [];\n return parts\n .filter((part) => part.type === 'tool' && part.tool)\n .map((part) => {\n const status = part.state?.status ?? 'running';\n const tool = part.tool ?? 'tool';\n if (status === 'completed') return `Completed: ${tool}`;\n if (status === 'failed') return `Failed: ${tool}`;\n return `Running: ${tool}`;\n });\n}\n\nfunction buildProgress(\n messages: AssistantRawMessage[],\n sinceMs: number,\n previousContent?: string,\n previousContentAtMs?: number\n): { content: string; done: boolean } {\n const relevant = messages\n .filter((message) => getMessageRole(message) === 'assistant' && getRawMessageCreatedAt(message) >= sinceMs)\n .sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n // Debug: log message filtering when no relevant messages found\n if (relevant.length === 0 && messages.length > 0) {\n console.log('[BigConsole-Assistant] buildProgress: no relevant messages', {\n totalMessages: messages.length,\n sinceMs,\n messageRoles: messages.map((m) => getMessageRole(m)),\n messageTimestamps: messages.map((m) => getRawMessageCreatedAt(m)),\n });\n }\n\n let content: string;\n let done: boolean;\n\n if (relevant.length === 0) {\n content = 'Thinking…';\n // Staleness fallback: if no messages have appeared after 15+ seconds of polling,\n // treat the response as complete even when messages get filtered out\n // (e.g., due to timestamp skew or role mismatch).\n done =\n previousContent === content && previousContentAtMs !== undefined && Date.now() - previousContentAtMs >= 15_000;\n } else {\n const latest = relevant[relevant.length - 1]!;\n const text = getAssistantText(latest);\n const toolProgress = getToolProgress(latest);\n content = [text, ...toolProgress].filter(Boolean).join('\\n\\n') || 'Thinking…';\n\n const officiallyDone = Boolean(getMessageCompleted(latest)) && (Boolean(text) || toolProgress.length > 0);\n\n // Staleness fallback: if content hasn't changed in 15 s, treat the build as\n // complete even when the assistant forgot to set `completed`.\n const staleDone =\n !officiallyDone &&\n previousContent === content &&\n previousContentAtMs !== undefined &&\n Date.now() - previousContentAtMs >= 15_000;\n\n done = officiallyDone || staleDone;\n }\n\n return { content, done };\n}\n\nfunction sanitize(response: string): string {\n return response\n .replace(/(Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/(X-Workspace-Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9._-]+\\.[A-Za-z0-9._-]+\\b/g, '[REDACTED_JWT]')\n .replace(/\\bsk-ant-[A-Za-z0-9-]+\\b/g, '[REDACTED_API_KEY]');\n}\n\nfunction isRecoverable(error: unknown): boolean {\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n if (\n message.includes('rate limit exceeded') ||\n message.includes('unauthorized') ||\n message.includes('k8s api error 401')\n ) {\n return false;\n }\n return [\n 'sandbox not found',\n 'sandbox is not running',\n 'sandbox service not available yet',\n 'sandbox failed',\n 'sandbox startup timed out',\n 'assistant service did not become healthy',\n 'proxy error: 404',\n 'proxy error: 502',\n 'proxy error: 503',\n 'unable to connect',\n 'image pull',\n 'container failed',\n 'bootstrap failed',\n ].some((fragment) => message.includes(fragment));\n}\n\n/**\n * Returns a memoized `AssistantTransport` wired to the BigConsole sandbox\n * assistant agent. The sandbox + session are cached in refs so follow-up turns\n * reuse the warm environment for the lifetime of the host shell.\n */\nexport function useSandboxAssistantTransport(): AssistantTransport {\n const { getAccessToken, getWorkspaceToken, userId, workspaceId: ctxWorkspaceId } = useAuthToken();\n\n // Initialize from sessionStorage so page refresh doesn't create a new sandbox\n const [initialSandboxId, initialSessionId] = useMemo(() => {\n try {\n return [sessionStorage.getItem('bc-assistant-sandbox-id'), sessionStorage.getItem('bc-assistant-session-id')];\n } catch {\n return [null, null] as const;\n }\n }, []);\n\n const sandboxIdRef = useRef<string | null>(initialSandboxId);\n const sessionIdRef = useRef<string | null>(initialSessionId);\n\n const persistSandboxId = useCallback((id: string | null) => {\n sandboxIdRef.current = id;\n if (id) {\n sessionStorage.setItem('bc-assistant-sandbox-id', id);\n } else {\n sessionStorage.removeItem('bc-assistant-sandbox-id');\n }\n }, []);\n\n const persistSessionId = useCallback((id: string | null) => {\n sessionIdRef.current = id;\n if (id) {\n sessionStorage.setItem('bc-assistant-session-id', id);\n } else {\n sessionStorage.removeItem('bc-assistant-session-id');\n }\n }, []);\n\n const ensureRuntime = useCallback(\n async (\n workspaceId: string,\n authContext: AssistantSandboxAuthContext\n ): Promise<{ sandboxId: string; sessionId: string }> => {\n let sandboxId = sandboxIdRef.current;\n console.log('[BigConsole-Assistant] ensureRuntime start', { sandboxId, workspaceId });\n if (!sandboxId) {\n console.log('[BigConsole-Assistant] findExistingSandbox called');\n sandboxId = await findExistingSandbox(workspaceId, MODE, authContext);\n console.log('[BigConsole-Assistant] findExistingSandbox result', { sandboxId });\n if (!sandboxId) {\n if (!getAccessToken()) {\n throw new Error('The assistant requires an authenticated session. Please sign in again.');\n }\n sandboxId = await createAssistantSandbox(MODE, workspaceId, authContext);\n console.log('[BigConsole-Assistant] createAssistantSandbox result', { sandboxId });\n }\n console.log('[BigConsole-Assistant] waitForSandboxReady called', { sandboxId });\n await waitForSandboxReady(sandboxId, workspaceId, authContext);\n console.log('[BigConsole-Assistant] waitForSandboxReady done', { sandboxId });\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady called', { sandboxId });\n await waitForAssistantServiceReady(sandboxId, workspaceId, authContext);\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady done', { sandboxId });\n persistSandboxId(sandboxId);\n }\n\n let sessionId = sessionIdRef.current;\n console.log('[BigConsole-Assistant] session check', { sessionId, sandboxId });\n if (!sessionId) {\n console.log('[BigConsole-Assistant] createAssistantSession called', { sandboxId, workspaceId });\n const result = await createAssistantSession(sandboxId, workspaceId, MODE, authContext);\n sessionId = result.sessionId;\n persistSessionId(sessionId);\n }\n\n return { sandboxId, sessionId };\n },\n [getAccessToken]\n );\n\n const sendPrompt = useCallback(\n async ({ prompt, onProgress, signal }: AssistantSendArgs): Promise<{ text: string }> => {\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n console.log('[BigConsole-Assistant] sendPrompt called', {\n promptLength: prompt.length,\n workspaceId,\n hasCtxWorkspaceId: !!ctxWorkspaceId,\n authContextKeys: {\n hasAccessToken: !!getAccessToken(),\n hasWorkspaceToken: !!getWorkspaceToken(),\n hasUserId: !!userId,\n },\n locationSearch: window.location.search,\n });\n if (!workspaceId) {\n throw new Error('The assistant needs an active workspace. Open a workspace and try again.');\n }\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n console.log('[BigConsole-Assistant] authContext prepared', {\n hasAccessToken: !!authContext.accessToken,\n hasWorkspaceToken: !!authContext.workspaceToken,\n hasUserId: !!authContext.userId,\n hasOrgId: !!authContext.organizationId,\n });\n\n const run = async (attempt: 1 | 2): Promise<{ text: string }> => {\n try {\n console.log('[BigConsole-Assistant] run attempt', attempt);\n const { sandboxId, sessionId } = await ensureRuntime(workspaceId, authContext);\n console.log('[BigConsole-Assistant] ensureRuntime resolved', { sandboxId, sessionId });\n\n const startedAt = Date.now();\n console.log('[BigConsole-Assistant] calling sendAssistantPromptAsync', {\n sandboxId,\n workspaceId,\n sessionId,\n promptLength: prompt.length,\n });\n await sendAssistantPromptAsync(\n sandboxId,\n workspaceId,\n sessionId,\n prompt,\n MODE,\n gatherPageContext(),\n authContext\n );\n\n const timeoutAt = Date.now() + STREAM_BUDGET_MS;\n let lastTtlExtensionAt = Date.now();\n let lastContent = '';\n let lastContentChangeAt = Date.now();\n let progress = buildProgress([], startedAt);\n\n while (Date.now() < timeoutAt) {\n if (signal.aborted) throw new Error('Cancelled');\n\n const messages = await getAssistantMessages(sandboxId, workspaceId, sessionId, authContext, 50);\n console.log('[BigConsole-Assistant] poll', {\n elapsedMs: Date.now() - startedAt,\n messageCount: messages.length,\n firstFewRoles: messages.slice(0, 3).map((m) => getMessageRole(m)),\n firstFewTimestamps: messages.slice(0, 3).map((m) => getRawMessageCreatedAt(m)),\n });\n progress = buildProgress(messages, startedAt, lastContent, lastContentChangeAt);\n console.log('[BigConsole-Assistant] progress', {\n contentPreview: progress.content.slice(0, 100),\n done: progress.done,\n lastContentChangeAt: Date.now() - lastContentChangeAt,\n });\n if (progress.content !== lastContent) {\n lastContent = progress.content;\n lastContentChangeAt = Date.now();\n }\n onProgress(sanitize(progress.content));\n if (progress.done) {\n console.log('[BigConsole-Assistant] progress.done=true, breaking poll loop');\n break;\n }\n\n if (Date.now() - lastTtlExtensionAt > TTL_EXTEND_INTERVAL_MS) {\n await extendAssistantSandboxTTL(sandboxId, workspaceId, 600, authContext).catch(() => undefined);\n lastTtlExtensionAt = Date.now();\n }\n\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n }\n\n if (!progress.done) {\n throw new Error('The assistant timed out while responding. Please try again.');\n }\n\n return { text: sanitize(progress.content) };\n } catch (error) {\n console.error('[BigConsole-Assistant] run error', {\n attempt,\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n sandboxId: sandboxIdRef.current,\n sessionId: sessionIdRef.current,\n });\n // A stale/expired sandbox or session is recoverable — drop the warm\n // refs and retry once from a clean runtime.\n if (attempt === 1 && isRecoverable(error)) {\n persistSandboxId(null);\n persistSessionId(null);\n return run(2);\n }\n throw error;\n }\n };\n\n return run(1);\n },\n [ctxWorkspaceId, ensureRuntime, getAccessToken, getWorkspaceToken, userId, persistSandboxId, persistSessionId]\n );\n\n return useMemo<AssistantTransport>(() => ({ sendPrompt }), [sendPrompt]);\n}\n"],"mappings":";;;;;AAwDA,IAAM,IAAsB,aACtB,IAAmB,MACnB,IAAmB,MACnB,IAAyB;AAI/B,SAAS,EAAuB,GAA+C;AAC7E,KAAI;EACF,IAAM,IACJ,MAAQ,gBAAgB,uCAAuC,yCAC3D,IAAqB,aAAa,QAAQ,EAAiB;AACjE,MAAI,EAAoB,QAAO;EAE/B,IAAM,IAAkB,eAAe,QAAQ,oBAAoB;AACnE,MAAI,CAAC,EAAiB,QAAO;EAC7B,IAAM,IAAM,aAAa,QAAQ,QAAQ,EAAgB,UAAU;AAGnE,SAFK,IACW,KAAK,MAAM,EAAI,CAChB,MAAQ,KAFN;SAGX;AACN,SAAO;;;AAIX,SAAS,EAAe,GAAiC;AAEvD,QADe,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC5C,IAAI,YAAY,IAAI,EAAuB,cAAc,IAAI,KAAY;;AAGzF,SAAS,IAA4B;AAEnC,QADe,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC5C,IAAI,MAAM,IAAI,EAAuB,iBAAiB;;AAKtE,SAAS,EAAe,GAAkD;AAExE,QAAO,EAAQ,MAAM,QAAQ,EAAQ;;AAGvC,SAAS,EAAuB,GAAsC;CAEpE,IAAM,IAAS,EAAQ,MAAM,MAAM;AACnC,KAAI,MAAW,KAAA,EAAW,QAAO;CACjC,IAAM,IAAO,EAAQ;AACrB,KAAI,MAAS,KAAA,EAAW,QAAO;AAE/B,KAAI,OAAO,KAAS,UAAU;EAC5B,IAAM,IAAS,KAAK,MAAM,EAAK;AAC/B,SAAO,MAAM,EAAO,GAAG,IAAI;;AAE7B,QAAO;;AAGT,SAAS,EAAoB,GAAkD;CAE7E,IAAM,IAAS,EAAQ,MAAM,MAAM;AACnC,KAAI,MAAW,KAAA,EAAW,QAAO;CACjC,IAAM,IAAO,EAAQ;AACjB,WAAS,KAAA,GACb;MAAI,OAAO,KAAS,UAAU;GAC5B,IAAM,IAAS,KAAK,MAAM,EAAK;AAC/B,UAAO,MAAM,EAAO,GAAG,KAAA,IAAY;;AAErC,SAAO;;;AAGT,SAAS,EAAiB,GAAsC;AAU9D,SARc,EAAQ,SAAS,EAAE,IACD,EAAE,EAC/B,QAAQ,MAAS,EAAK,SAAS,UAAU,OAAO,EAAK,QAAS,SAAS,CACvE,KAAK,MAAS,EAAK,MAAM,MAAM,IAAI,GAAG,CACtC,OAAO,QAAQ,CACf,KAAK,KAAK,KAGN,OAAO,EAAQ,WAAY,WAAW,EAAQ,QAAQ,MAAM,GAAG;;AAGxE,SAAS,EAAgB,GAAwC;AAE/D,SADc,EAAQ,SAAS,EAAE,EAE9B,QAAQ,MAAS,EAAK,SAAS,UAAU,EAAK,KAAK,CACnD,KAAK,MAAS;EACb,IAAM,IAAS,EAAK,OAAO,UAAU,WAC/B,IAAO,EAAK,QAAQ;AAG1B,SAFI,MAAW,cAAoB,cAAc,MAC7C,MAAW,WAAiB,WAAW,MACpC,YAAY;GACnB;;AAGN,SAAS,EACP,GACA,GACA,GACA,GACoC;CACpC,IAAM,IAAW,EACd,QAAQ,MAAY,EAAe,EAAQ,KAAK,eAAe,EAAuB,EAAQ,IAAI,EAAQ,CAC1G,MAAM,GAAM,MAAU,EAAuB,EAAK,GAAG,EAAuB,EAAM,CAAC;AAGtF,CAAI,EAAS,WAAW,KAAK,EAAS,SAAS,KAC7C,QAAQ,IAAI,8DAA8D;EACxE,eAAe,EAAS;EACxB;EACA,cAAc,EAAS,KAAK,MAAM,EAAe,EAAE,CAAC;EACpD,mBAAmB,EAAS,KAAK,MAAM,EAAuB,EAAE,CAAC;EAClE,CAAC;CAGJ,IAAI,GACA;AAEJ,KAAI,EAAS,WAAW,EAKtB,CAJA,IAAU,aAIV,IACE,MAAoB,KAAW,MAAwB,KAAA,KAAa,KAAK,KAAK,GAAG,KAAuB;MACrG;EACL,IAAM,IAAS,EAAS,EAAS,SAAS,IACpC,IAAO,EAAiB,EAAO,EAC/B,IAAe,EAAgB,EAAO;AAC5C,MAAU,CAAC,GAAM,GAAG,EAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,OAAO,IAAI;EAElE,IAAM,IAAiB,EAAQ,EAAoB,EAAO,KAAM,EAAQ,KAAS,EAAa,SAAS,IAIjG,IACJ,CAAC,KACD,MAAoB,KACpB,MAAwB,KAAA,KACxB,KAAK,KAAK,GAAG,KAAuB;AAEtC,MAAO,KAAkB;;AAG3B,QAAO;EAAE;EAAS;EAAM;;AAG1B,SAAS,EAAS,GAA0B;AAC1C,QAAO,EACJ,QAAQ,6CAA6C,eAAe,CACpE,QAAQ,yDAAyD,eAAe,CAChF,QAAQ,4DAA4D,iBAAiB,CACrF,QAAQ,6BAA6B,qBAAqB;;AAG/D,SAAS,EAAc,GAAyB;CAC9C,IAAM,IAAU,aAAiB,QAAQ,EAAM,QAAQ,aAAa,GAAG,OAAO,EAAM,CAAC,aAAa;AAQlG,QANE,EAAQ,SAAS,sBAAsB,IACvC,EAAQ,SAAS,eAAe,IAChC,EAAQ,SAAS,oBAAoB,GAE9B,KAEF;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,MAAM,MAAa,EAAQ,SAAS,EAAS,CAAC;;AAQlD,SAAgB,IAAmD;CACjE,IAAM,EAAE,mBAAgB,sBAAmB,WAAQ,aAAa,MAAmB,GAAc,EAG3F,CAAC,GAAkB,KAAoB,QAAc;AACzD,MAAI;AACF,UAAO,CAAC,eAAe,QAAQ,0BAA0B,EAAE,eAAe,QAAQ,0BAA0B,CAAC;UACvG;AACN,UAAO,CAAC,MAAM,KAAK;;IAEpB,EAAE,CAAC,EAEA,IAAe,EAAsB,EAAiB,EACtD,IAAe,EAAsB,EAAiB,EAEtD,IAAmB,GAAa,MAAsB;AAE1D,EADA,EAAa,UAAU,GACnB,IACF,eAAe,QAAQ,2BAA2B,EAAG,GAErD,eAAe,WAAW,0BAA0B;IAErD,EAAE,CAAC,EAEA,IAAmB,GAAa,MAAsB;AAE1D,EADA,EAAa,UAAU,GACnB,IACF,eAAe,QAAQ,2BAA2B,EAAG,GAErD,eAAe,WAAW,0BAA0B;IAErD,EAAE,CAAC,EAEA,IAAgB,EACpB,OACE,GACA,MACsD;EACtD,IAAI,IAAY,EAAa;AAE7B,MADA,QAAQ,IAAI,8CAA8C;GAAE;GAAW;GAAa,CAAC,EACjF,CAAC,GAAW;AAId,OAHA,QAAQ,IAAI,oDAAoD,EAChE,IAAY,MAAM,EAAoB,GAAa,GAAM,EAAY,EACrE,QAAQ,IAAI,qDAAqD,EAAE,cAAW,CAAC,EAC3E,CAAC,GAAW;AACd,QAAI,CAAC,GAAgB,CACnB,OAAU,MAAM,yEAAyE;AAG3F,IADA,IAAY,MAAM,EAAuB,GAAM,GAAa,EAAY,EACxE,QAAQ,IAAI,wDAAwD,EAAE,cAAW,CAAC;;AAQpF,GANA,QAAQ,IAAI,qDAAqD,EAAE,cAAW,CAAC,EAC/E,MAAM,EAAoB,GAAW,GAAa,EAAY,EAC9D,QAAQ,IAAI,mDAAmD,EAAE,cAAW,CAAC,EAC7E,QAAQ,IAAI,8DAA8D,EAAE,cAAW,CAAC,EACxF,MAAM,EAA6B,GAAW,GAAa,EAAY,EACvE,QAAQ,IAAI,4DAA4D,EAAE,cAAW,CAAC,EACtF,EAAiB,EAAU;;EAG7B,IAAI,IAAY,EAAa;AAS7B,SARA,QAAQ,IAAI,wCAAwC;GAAE;GAAW;GAAW,CAAC,EACxE,MACH,QAAQ,IAAI,wDAAwD;GAAE;GAAW;GAAa,CAAC,EAE/F,KADe,MAAM,EAAuB,GAAW,GAAa,GAAM,EAAY,EACnE,WACnB,EAAiB,EAAU,GAGtB;GAAE;GAAW;GAAW;IAEjC,CAAC,EAAe,CACjB,EAEK,IAAa,EACjB,OAAO,EAAE,WAAQ,eAAY,gBAA2D;EACtF,IAAM,IAAc,EAAe,EAAe;AAYlD,MAXA,QAAQ,IAAI,4CAA4C;GACtD,cAAc,EAAO;GACrB;GACA,mBAAmB,CAAC,CAAC;GACrB,iBAAiB;IACf,gBAAgB,CAAC,CAAC,GAAgB;IAClC,mBAAmB,CAAC,CAAC,GAAmB;IACxC,WAAW,CAAC,CAAC;IACd;GACD,gBAAgB,OAAO,SAAS;GACjC,CAAC,EACE,CAAC,EACH,OAAU,MAAM,2EAA2E;EAE7F,IAAM,IAA2C;GAC/C,aAAa,GAAgB;GAC7B,gBAAgB,GAAmB;GACnC;GACA,gBAAgB,GAAmB;GACpC;AACD,UAAQ,IAAI,+CAA+C;GACzD,gBAAgB,CAAC,CAAC,EAAY;GAC9B,mBAAmB,CAAC,CAAC,EAAY;GACjC,WAAW,CAAC,CAAC,EAAY;GACzB,UAAU,CAAC,CAAC,EAAY;GACzB,CAAC;EAEF,IAAM,IAAM,OAAO,MAA8C;AAC/D,OAAI;AACF,YAAQ,IAAI,sCAAsC,EAAQ;IAC1D,IAAM,EAAE,cAAW,iBAAc,MAAM,EAAc,GAAa,EAAY;AAC9E,YAAQ,IAAI,iDAAiD;KAAE;KAAW;KAAW,CAAC;IAEtF,IAAM,IAAY,KAAK,KAAK;AAO5B,IANA,QAAQ,IAAI,2DAA2D;KACrE;KACA;KACA;KACA,cAAc,EAAO;KACtB,CAAC,EACF,MAAM,EACJ,GACA,GACA,GACA,GACA,GACA,GAAmB,EACnB,EACD;IAED,IAAM,IAAY,KAAK,KAAK,GAAG,GAC3B,IAAqB,KAAK,KAAK,EAC/B,IAAc,IACd,IAAsB,KAAK,KAAK,EAChC,IAAW,EAAc,EAAE,EAAE,EAAU;AAE3C,WAAO,KAAK,KAAK,GAAG,IAAW;AAC7B,SAAI,EAAO,QAAS,OAAU,MAAM,YAAY;KAEhD,IAAM,IAAW,MAAM,EAAqB,GAAW,GAAa,GAAW,GAAa,GAAG;AAkB/F,SAjBA,QAAQ,IAAI,+BAA+B;MACzC,WAAW,KAAK,KAAK,GAAG;MACxB,cAAc,EAAS;MACvB,eAAe,EAAS,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,EAAe,EAAE,CAAC;MACjE,oBAAoB,EAAS,MAAM,GAAG,EAAE,CAAC,KAAK,MAAM,EAAuB,EAAE,CAAC;MAC/E,CAAC,EACF,IAAW,EAAc,GAAU,GAAW,GAAa,EAAoB,EAC/E,QAAQ,IAAI,mCAAmC;MAC7C,gBAAgB,EAAS,QAAQ,MAAM,GAAG,IAAI;MAC9C,MAAM,EAAS;MACf,qBAAqB,KAAK,KAAK,GAAG;MACnC,CAAC,EACE,EAAS,YAAY,MACvB,IAAc,EAAS,SACvB,IAAsB,KAAK,KAAK,GAElC,EAAW,EAAS,EAAS,QAAQ,CAAC,EAClC,EAAS,MAAM;AACjB,cAAQ,IAAI,gEAAgE;AAC5E;;AAQF,KALI,KAAK,KAAK,GAAG,IAAqB,MACpC,MAAM,EAA0B,GAAW,GAAa,KAAK,EAAY,CAAC,YAAY,KAAA,EAAU,EAChG,IAAqB,KAAK,KAAK,GAGjC,MAAM,IAAI,SAAS,MAAY,WAAW,GAAS,EAAiB,CAAC;;AAGvE,QAAI,CAAC,EAAS,KACZ,OAAU,MAAM,8DAA8D;AAGhF,WAAO,EAAE,MAAM,EAAS,EAAS,QAAQ,EAAE;YACpC,GAAO;AAUd,QATA,QAAQ,MAAM,oCAAoC;KAChD;KACA,OAAO,aAAiB,QAAQ,EAAM,UAAU,OAAO,EAAM;KAC7D,OAAO,aAAiB,QAAQ,EAAM,QAAQ,KAAA;KAC9C,WAAW,EAAa;KACxB,WAAW,EAAa;KACzB,CAAC,EAGE,MAAY,KAAK,EAAc,EAAM,CAGvC,QAFA,EAAiB,KAAK,EACtB,EAAiB,KAAK,EACf,EAAI,EAAE;AAEf,UAAM;;;AAIV,SAAO,EAAI,EAAE;IAEf;EAAC;EAAgB;EAAe;EAAgB;EAAmB;EAAQ;EAAkB;EAAiB,CAC/G;AAED,QAAO,SAAmC,EAAE,eAAY,GAAG,CAAC,EAAW,CAAC"}
|
|
1
|
+
{"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../../src/bigconsole/assistant/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * BigConsole adapter for the shared fe-libs AssistantWidget.\n *\n * The floating widget (fe-libs, Layer 1) is backend-agnostic — it calls an\n * injected `AssistantTransport`. This hook builds a transport that drives the\n * sandbox AI assistant (combined `assistant` mode = docs Q&A + api-calls): it\n * provisions/reuses a sandbox, opens a session, dispatches the prompt async,\n * then polls for the streamed answer. The agent introspects the GraphQL schema\n * and performs API calls on the user's behalf using their workspace token,\n * contextual to the current screen (via `gatherPageContext`).\n *\n * This is a faithful port of microfe-vibecontrols'\n * `services/createSandboxAssistantTransport.ts`; the only product-specific\n * difference lives in `assistantApi.createAssistantSandbox`\n * (`AI_ASSISTANT_PRODUCT=bigconsole`).\n */\n\nimport { useCallback, useMemo, useRef } from 'react';\nimport { useAuthToken } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n createAssistantSandbox,\n createAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantMessages,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n} from './assistantApi';\nimport { gatherPageContext } from './pageContext';\nimport type { AssistantMode, AssistantRawMessage, AssistantSandboxAuthContext } from './types';\n\n/**\n * Locally-defined mirror of the fe-libs `AssistantTransport` contract.\n *\n * Intentionally NOT imported from `@burdenoff/fe-libs`: microfe's tsconfig maps\n * `@burdenoff/fe-libs/*` to fe-libs *source*, so vite-plugin-dts would rewrite a\n * cross-package type used in this hook's public signature to a broken\n * source-relative path in the emitted `.d.ts`. Structural typing makes this\n * shape assignable to fe-libs' `AssistantTransport` at the call site\n * (bigconsole-app's AppShell), which is where compatibility is enforced.\n */\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n}\n\n// The sandbox runs the `ai-assistant-api-calls` image (see assistantApi\n// `getImageForMode` — `assistant` maps to that image). The combined `assistant`\n// mode IS supported by the CF proxy images (v2026.618.3+). VibeControls uses\n// `assistant` mode in production successfully. This enables docs Q&A + API calls.\nconst MODE: AssistantMode = 'assistant';\nconst STREAM_BUDGET_MS = 180_000;\nconst POLL_INTERVAL_MS = 1500;\nconst TTL_EXTEND_INTERVAL_MS = 30_000;\n\n// ── Context helpers (mirror vibecontrols' resolution) ────────────────\n\nfunction getProfileContextValue(key: 'workspaceId' | 'organizationId'): string {\n try {\n const activeContextKey =\n key === 'workspaceId' ? 'burdenoff-active-context-workspace' : 'burdenoff-active-context-organization';\n const activeContextValue = localStorage.getItem(activeContextKey);\n if (activeContextValue) return activeContextValue;\n\n const activeProfileId = sessionStorage.getItem('bf-active-profile');\n if (!activeProfileId) return '';\n const raw = localStorage.getItem(`bf-p-${activeProfileId}-context`);\n if (!raw) return '';\n const context = JSON.parse(raw) as { workspaceId?: string; organizationId?: string };\n return context[key] ?? '';\n } catch {\n return '';\n }\n}\n\nfunction getWorkspaceId(fallback: string | null): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('workspace') ?? getProfileContextValue('workspaceId') ?? fallback ?? '';\n}\n\nfunction getOrganizationId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('org') ?? getProfileContextValue('organizationId');\n}\n\n// ── Message-progress helpers (pure; mirror vibecontrols) ─────────────\n\nfunction getMessageRole(message: AssistantRawMessage): string | undefined {\n // Check nested format first, then flat format\n return message.info?.role ?? message.role;\n}\n\nfunction getRawMessageCreatedAt(message: AssistantRawMessage): number {\n // Check nested format first (epoch ms), then flat format (ISO string or epoch ms)\n const nested = message.info?.time?.created;\n if (nested !== undefined) return nested;\n const flat = message.createdAt;\n if (flat === undefined) return 0;\n // If it's a string (ISO), parse it; otherwise treat as epoch ms\n if (typeof flat === 'string') {\n const parsed = Date.parse(flat);\n return isNaN(parsed) ? 0 : parsed;\n }\n return flat;\n}\n\nfunction getMessageCompleted(message: AssistantRawMessage): number | undefined {\n // Check nested format first, then flat format\n const nested = message.info?.time?.completed;\n if (nested !== undefined) return nested;\n const flat = message.completedAt;\n if (flat === undefined) return undefined;\n if (typeof flat === 'string') {\n const parsed = Date.parse(flat);\n return isNaN(parsed) ? undefined : parsed;\n }\n return flat;\n}\n\nfunction getAssistantText(message: AssistantRawMessage): string {\n // Check parts format first (nested), then flat content\n const parts = message.parts ?? [];\n const textFromParts = (parts ?? [])\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text?.trim() ?? '')\n .filter(Boolean)\n .join('\\n');\n if (textFromParts) return textFromParts;\n // Fallback to flat content field\n return typeof message.content === 'string' ? message.content.trim() : '';\n}\n\nfunction getToolProgress(message: AssistantRawMessage): string[] {\n const parts = message.parts ?? [];\n return parts\n .filter((part) => part.type === 'tool' && part.tool)\n .map((part) => {\n const status = part.state?.status ?? 'running';\n const tool = part.tool ?? 'tool';\n if (status === 'completed') return `Completed: ${tool}`;\n if (status === 'failed') return `Failed: ${tool}`;\n return `Running: ${tool}`;\n });\n}\n\nfunction buildProgress(\n messages: AssistantRawMessage[],\n sinceMs: number,\n previousContent?: string,\n previousContentAtMs?: number\n): { content: string; done: boolean } {\n const relevant = messages\n .filter((message) => getMessageRole(message) === 'assistant' && getRawMessageCreatedAt(message) >= sinceMs)\n .sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n // Debug: log message filtering when no relevant messages found\n if (relevant.length === 0 && messages.length > 0) {\n console.log('[BigConsole-Assistant] buildProgress: no relevant messages', {\n totalMessages: messages.length,\n sinceMs,\n messageRoles: messages.map((m) => getMessageRole(m)),\n messageTimestamps: messages.map((m) => getRawMessageCreatedAt(m)),\n });\n }\n\n let content: string;\n let done: boolean;\n\n if (relevant.length === 0) {\n content = 'Thinking…';\n // Staleness fallback: if no messages have appeared after 15+ seconds of polling,\n // treat the response as complete even when messages get filtered out\n // (e.g., due to timestamp skew or role mismatch).\n done =\n previousContent === content && previousContentAtMs !== undefined && Date.now() - previousContentAtMs >= 15_000;\n } else {\n const latest = relevant[relevant.length - 1]!;\n const text = getAssistantText(latest);\n const toolProgress = getToolProgress(latest);\n content = [text, ...toolProgress].filter(Boolean).join('\\n\\n') || 'Thinking…';\n\n const officiallyDone = Boolean(getMessageCompleted(latest)) && (Boolean(text) || toolProgress.length > 0);\n\n // Staleness fallback: if content hasn't changed in 15 s, treat the build as\n // complete even when the assistant forgot to set `completed`.\n const staleDone =\n !officiallyDone &&\n previousContent === content &&\n previousContentAtMs !== undefined &&\n Date.now() - previousContentAtMs >= 15_000;\n\n done = officiallyDone || staleDone;\n }\n\n return { content, done };\n}\n\nfunction sanitize(response: string): string {\n return response\n .replace(/(Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/(X-Workspace-Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9._-]+\\.[A-Za-z0-9._-]+\\b/g, '[REDACTED_JWT]')\n .replace(/\\bsk-ant-[A-Za-z0-9-]+\\b/g, '[REDACTED_API_KEY]');\n}\n\nfunction isRecoverable(error: unknown): boolean {\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n if (\n message.includes('rate limit exceeded') ||\n message.includes('unauthorized') ||\n message.includes('k8s api error 401')\n ) {\n return false;\n }\n return [\n 'sandbox not found',\n 'sandbox is not running',\n 'sandbox service not available yet',\n 'sandbox failed',\n 'sandbox startup timed out',\n 'assistant service did not become healthy',\n 'proxy error: 404',\n 'proxy error: 502',\n 'proxy error: 503',\n 'unable to connect',\n 'image pull',\n 'container failed',\n 'bootstrap failed',\n // Transient network errors from the browser fetch — gateway CORS preflight\n // failures, mid-stream resets, and Cloudflare 524s all surface as\n // \"Failed to fetch\" via TypeError. They're worth retrying on a clean\n // runtime since the underlying sandbox state is unaffected.\n 'failed to fetch',\n // Subgraph returned 500 with a generic message — gateway returns this as\n // a GraphQL error rather than an HTTP error. The actual underlying cause\n // (e.g., transient Prisma timeout) is recoverable, but a fresh sandbox\n // may be needed.\n 'unexpected error',\n ].some((fragment) => message.includes(fragment));\n}\n\n/**\n * Returns a memoized `AssistantTransport` wired to the BigConsole sandbox\n * assistant agent. The sandbox + session are cached in refs so follow-up turns\n * reuse the warm environment for the lifetime of the host shell.\n */\nexport function useSandboxAssistantTransport(): AssistantTransport {\n const { getAccessToken, getWorkspaceToken, userId, workspaceId: ctxWorkspaceId } = useAuthToken();\n\n // Initialize from sessionStorage so page refresh doesn't create a new sandbox\n const [initialSandboxId, initialSessionId] = useMemo(() => {\n try {\n return [sessionStorage.getItem('bc-assistant-sandbox-id'), sessionStorage.getItem('bc-assistant-session-id')];\n } catch {\n return [null, null] as const;\n }\n }, []);\n\n const sandboxIdRef = useRef<string | null>(initialSandboxId);\n const sessionIdRef = useRef<string | null>(initialSessionId);\n\n const persistSandboxId = useCallback((id: string | null) => {\n sandboxIdRef.current = id;\n if (id) {\n sessionStorage.setItem('bc-assistant-sandbox-id', id);\n } else {\n sessionStorage.removeItem('bc-assistant-sandbox-id');\n }\n }, []);\n\n const persistSessionId = useCallback((id: string | null) => {\n sessionIdRef.current = id;\n if (id) {\n sessionStorage.setItem('bc-assistant-session-id', id);\n } else {\n sessionStorage.removeItem('bc-assistant-session-id');\n }\n }, []);\n\n const ensureRuntime = useCallback(\n async (\n workspaceId: string,\n authContext: AssistantSandboxAuthContext\n ): Promise<{ sandboxId: string; sessionId: string }> => {\n let sandboxId = sandboxIdRef.current;\n console.log('[BigConsole-Assistant] ensureRuntime start', { sandboxId, workspaceId });\n if (!sandboxId) {\n console.log('[BigConsole-Assistant] findExistingSandbox called');\n sandboxId = await findExistingSandbox(workspaceId, MODE, authContext);\n console.log('[BigConsole-Assistant] findExistingSandbox result', { sandboxId });\n if (!sandboxId) {\n if (!getAccessToken()) {\n throw new Error('The assistant requires an authenticated session. Please sign in again.');\n }\n sandboxId = await createAssistantSandbox(MODE, workspaceId, authContext);\n console.log('[BigConsole-Assistant] createAssistantSandbox result', { sandboxId });\n }\n console.log('[BigConsole-Assistant] waitForSandboxReady called', { sandboxId });\n await waitForSandboxReady(sandboxId, workspaceId, authContext);\n console.log('[BigConsole-Assistant] waitForSandboxReady done', { sandboxId });\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady called', { sandboxId });\n await waitForAssistantServiceReady(sandboxId, workspaceId, authContext);\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady done', { sandboxId });\n persistSandboxId(sandboxId);\n }\n\n let sessionId = sessionIdRef.current;\n console.log('[BigConsole-Assistant] session check', { sessionId, sandboxId });\n if (!sessionId) {\n console.log('[BigConsole-Assistant] createAssistantSession called', { sandboxId, workspaceId });\n const result = await createAssistantSession(sandboxId, workspaceId, MODE, authContext);\n sessionId = result.sessionId;\n persistSessionId(sessionId);\n }\n\n return { sandboxId, sessionId };\n },\n [getAccessToken]\n );\n\n const sendPrompt = useCallback(\n async ({ prompt, onProgress, signal }: AssistantSendArgs): Promise<{ text: string }> => {\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n console.log('[BigConsole-Assistant] sendPrompt called', {\n promptLength: prompt.length,\n workspaceId,\n hasCtxWorkspaceId: !!ctxWorkspaceId,\n authContextKeys: {\n hasAccessToken: !!getAccessToken(),\n hasWorkspaceToken: !!getWorkspaceToken(),\n hasUserId: !!userId,\n },\n locationSearch: window.location.search,\n });\n if (!workspaceId) {\n throw new Error('The assistant needs an active workspace. Open a workspace and try again.');\n }\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n console.log('[BigConsole-Assistant] authContext prepared', {\n hasAccessToken: !!authContext.accessToken,\n hasWorkspaceToken: !!authContext.workspaceToken,\n hasUserId: !!authContext.userId,\n hasOrgId: !!authContext.organizationId,\n });\n\n const run = async (attempt: 1 | 2): Promise<{ text: string }> => {\n try {\n console.log('[BigConsole-Assistant] run attempt', attempt);\n const { sandboxId, sessionId } = await ensureRuntime(workspaceId, authContext);\n console.log('[BigConsole-Assistant] ensureRuntime resolved', { sandboxId, sessionId });\n\n const startedAt = Date.now();\n console.log('[BigConsole-Assistant] calling sendAssistantPromptAsync', {\n sandboxId,\n workspaceId,\n sessionId,\n promptLength: prompt.length,\n });\n await sendAssistantPromptAsync(\n sandboxId,\n workspaceId,\n sessionId,\n prompt,\n MODE,\n gatherPageContext(),\n authContext\n );\n\n const timeoutAt = Date.now() + STREAM_BUDGET_MS;\n let lastTtlExtensionAt = Date.now();\n let lastContent = '';\n let lastContentChangeAt = Date.now();\n let progress = buildProgress([], startedAt);\n\n while (Date.now() < timeoutAt) {\n if (signal.aborted) throw new Error('Cancelled');\n\n const messages = await getAssistantMessages(sandboxId, workspaceId, sessionId, authContext, 50);\n console.log('[BigConsole-Assistant] poll', {\n elapsedMs: Date.now() - startedAt,\n messageCount: messages.length,\n firstFewRoles: messages.slice(0, 3).map((m) => getMessageRole(m)),\n firstFewTimestamps: messages.slice(0, 3).map((m) => getRawMessageCreatedAt(m)),\n });\n progress = buildProgress(messages, startedAt, lastContent, lastContentChangeAt);\n console.log('[BigConsole-Assistant] progress', {\n contentPreview: progress.content.slice(0, 100),\n done: progress.done,\n lastContentChangeAt: Date.now() - lastContentChangeAt,\n });\n if (progress.content !== lastContent) {\n lastContent = progress.content;\n lastContentChangeAt = Date.now();\n }\n onProgress(sanitize(progress.content));\n if (progress.done) {\n console.log('[BigConsole-Assistant] progress.done=true, breaking poll loop');\n break;\n }\n\n if (Date.now() - lastTtlExtensionAt > TTL_EXTEND_INTERVAL_MS) {\n await extendAssistantSandboxTTL(sandboxId, workspaceId, 600, authContext).catch(() => undefined);\n lastTtlExtensionAt = Date.now();\n }\n\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n }\n\n if (!progress.done) {\n throw new Error('The assistant timed out while responding. Please try again.');\n }\n\n return { text: sanitize(progress.content) };\n } catch (error) {\n console.error('[BigConsole-Assistant] run error', {\n attempt,\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n sandboxId: sandboxIdRef.current,\n sessionId: sessionIdRef.current,\n });\n // A stale/expired sandbox or session is recoverable — drop the warm\n // refs and retry once from a clean runtime.\n if (attempt === 1 && isRecoverable(error)) {\n persistSandboxId(null);\n persistSessionId(null);\n return run(2);\n }\n throw error;\n }\n };\n\n return run(1);\n },\n [ctxWorkspaceId, ensureRuntime, getAccessToken, getWorkspaceToken, userId, persistSandboxId, persistSessionId]\n );\n\n return useMemo<AssistantTransport>(() => ({ sendPrompt }), [sendPrompt]);\n}\n"],"mappings":"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"}
|