@burdenoff/microfe-bigconsole 2026.915.1 → 2026.917.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/dist/bigconsole/assistant/assistantApi.js +58 -57
- package/dist/bigconsole/assistant/assistantApi.js.map +1 -1
- package/dist/bigconsole/assistant/assistantProduct.js +20 -0
- package/dist/bigconsole/assistant/assistantProduct.js.map +1 -0
- package/dist/bigconsole/assistant/assistantPrompt.js +34 -0
- package/dist/bigconsole/assistant/assistantPrompt.js.map +1 -0
- package/dist/bigconsole/assistant/conversationHistoryApi.js +33 -32
- package/dist/bigconsole/assistant/conversationHistoryApi.js.map +1 -1
- package/dist/bigconsole/assistant/index.js +10 -4
- package/dist/bigconsole/assistant/modes.js +29 -0
- package/dist/bigconsole/assistant/modes.js.map +1 -0
- package/dist/bigconsole/assistant/pageContext.js +32 -19
- package/dist/bigconsole/assistant/pageContext.js.map +1 -1
- package/dist/bigconsole/assistant/previewController.js +60 -0
- package/dist/bigconsole/assistant/previewController.js.map +1 -0
- package/dist/bigconsole/assistant/previewRunAction.js +9 -0
- package/dist/bigconsole/assistant/previewRunAction.js.map +1 -0
- package/dist/bigconsole/assistant/product.js +6 -0
- package/dist/bigconsole/assistant/product.js.map +1 -0
- package/dist/bigconsole/assistant/useWorkspaceWriteAccess.js +15 -0
- package/dist/bigconsole/assistant/useWorkspaceWriteAccess.js.map +1 -0
- package/dist/bigconsole/components/preview/AiPreviewContent.js +129 -67
- package/dist/bigconsole/components/preview/AiPreviewContent.js.map +1 -1
- package/dist/bigconsole/hooks/useWidgetOperations.js +1 -1
- package/dist/bigconsole/hooks/useWidgetOperations.js.map +1 -1
- package/package.json +5 -4
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { CreateSandboxDocument as e, ExtendSandboxTtlDocument as t, GetActiveSandboxesDocument as n, GetReusableAssistantSandboxDocument as r, GetSandboxDocument as i, ProxySandboxRequestDocument as a } from "../../generated/wspace-operations.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { BIGCONSOLE_PRODUCT as o } from "./product.js";
|
|
3
|
+
import { Kind as s, print as c } from "graphql";
|
|
4
|
+
import { graphqlFetch as l } from "@burdenoff/fe-libs/shared/graphql";
|
|
5
|
+
import { ASSISTANT_API_AUTH_STRATEGY as u, getImageForMode as d, getImageTagForMode as f, resolveSandboxAssistantMode as p } from "@burdenoff/fe-libs/shared/assistant/images";
|
|
5
6
|
//#region src/bigconsole/assistant/assistantApi.ts
|
|
6
|
-
var
|
|
7
|
-
function
|
|
7
|
+
var m = o;
|
|
8
|
+
function h() {
|
|
8
9
|
return typeof window > "u" ? !1 : window.location.hostname.toLowerCase() === "app.bigconsole.com";
|
|
9
10
|
}
|
|
10
|
-
function
|
|
11
|
-
return
|
|
11
|
+
function g(e, t = h()) {
|
|
12
|
+
return f(e, t);
|
|
12
13
|
}
|
|
13
|
-
function
|
|
14
|
-
return
|
|
14
|
+
function _(e, t = h()) {
|
|
15
|
+
return d(e, t);
|
|
15
16
|
}
|
|
16
|
-
function
|
|
17
|
+
function v(e) {
|
|
17
18
|
switch (e) {
|
|
18
19
|
case "general": return {
|
|
19
20
|
cpuRequest: "100m",
|
|
@@ -31,26 +32,26 @@ function _(e) {
|
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
|
-
function
|
|
35
|
-
return e.definitions.find((e) => e.kind ===
|
|
35
|
+
function y(e) {
|
|
36
|
+
return e.definitions.find((e) => e.kind === s.OPERATION_DEFINITION)?.name?.value;
|
|
36
37
|
}
|
|
37
|
-
function
|
|
38
|
+
function b() {
|
|
38
39
|
try {
|
|
39
40
|
return `bc-assistant-${crypto.randomUUID()}`;
|
|
40
41
|
} catch {
|
|
41
42
|
return `bc-assistant-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
function
|
|
45
|
+
function x(e, t) {
|
|
45
46
|
return {
|
|
46
47
|
workspaceId: e,
|
|
47
48
|
...t?.organizationId ? { custom: { organizationId: t.organizationId } } : {}
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
|
-
async function
|
|
51
|
+
async function S(e) {
|
|
51
52
|
let t;
|
|
52
53
|
for (let n = 0; n < 3; n++) try {
|
|
53
|
-
return await
|
|
54
|
+
return await C(e);
|
|
54
55
|
} catch (e) {
|
|
55
56
|
t = e;
|
|
56
57
|
let r = e instanceof Error ? e.message.toLowerCase() : String(e).toLowerCase();
|
|
@@ -65,10 +66,10 @@ async function x(e) {
|
|
|
65
66
|
}
|
|
66
67
|
throw t;
|
|
67
68
|
}
|
|
68
|
-
async function
|
|
69
|
-
let t =
|
|
69
|
+
async function C(e) {
|
|
70
|
+
let t = y(e.document), n = await l({
|
|
70
71
|
gateway: "workspace",
|
|
71
|
-
query:
|
|
72
|
+
query: c(e.document),
|
|
72
73
|
variables: e.variables,
|
|
73
74
|
operationName: t,
|
|
74
75
|
authToken: e.authContext?.accessToken ?? void 0,
|
|
@@ -76,7 +77,7 @@ async function S(e) {
|
|
|
76
77
|
workspaceId: e.workspaceId,
|
|
77
78
|
organizationId: e.authContext?.organizationId ?? !0,
|
|
78
79
|
suppressGlobalErrorEvent: !0,
|
|
79
|
-
extraHeaders: { "x-request-id":
|
|
80
|
+
extraHeaders: { "x-request-id": b() }
|
|
80
81
|
});
|
|
81
82
|
if (!n) throw Error("Sandbox GraphQL returned empty response");
|
|
82
83
|
if (n.errors?.length) throw console.error("[BigConsole-Assistant] executeSandboxGraphQL errors", {
|
|
@@ -86,32 +87,32 @@ async function S(e) {
|
|
|
86
87
|
if (!n.data) throw Error("Sandbox GraphQL returned no data");
|
|
87
88
|
return n.data;
|
|
88
89
|
}
|
|
89
|
-
function
|
|
90
|
-
return (e.status === "RUNNING" || e.status === "PROVISIONING" || e.status === "PENDING") && e.template === "AI_BUILDER" &&
|
|
90
|
+
function w(e, t, n) {
|
|
91
|
+
return (e.status === "RUNNING" || e.status === "PROVISIONING" || e.status === "PENDING") && e.template === "AI_BUILDER" && p(e) === t && !!n && e.createdBy === n && e.metadata?.authStrategy === u && e.metadata?.assistantProduct === m && e.metadata?.confirmWrites !== !0;
|
|
91
92
|
}
|
|
92
|
-
async function
|
|
93
|
+
async function T(e, t, n, i) {
|
|
93
94
|
if (!i?.userId) return !1;
|
|
94
|
-
let a = (await
|
|
95
|
+
let a = (await S({
|
|
95
96
|
document: r,
|
|
96
97
|
variables: {
|
|
97
98
|
id: e,
|
|
98
|
-
context:
|
|
99
|
+
context: x(t, i)
|
|
99
100
|
},
|
|
100
101
|
workspaceId: t,
|
|
101
102
|
authContext: i
|
|
102
103
|
})).getSandbox;
|
|
103
|
-
return !!a && a.id === e &&
|
|
104
|
+
return !!a && a.id === e && w(a, n, i.userId);
|
|
104
105
|
}
|
|
105
|
-
async function
|
|
106
|
-
return ((await
|
|
106
|
+
async function E(e, t, r) {
|
|
107
|
+
return ((await S({
|
|
107
108
|
document: n,
|
|
108
|
-
variables: { context:
|
|
109
|
+
variables: { context: x(e, r) },
|
|
109
110
|
workspaceId: e,
|
|
110
111
|
authContext: r
|
|
111
|
-
})).getActiveSandboxes ?? []).find((e) =>
|
|
112
|
+
})).getActiveSandboxes ?? []).find((e) => w(e, t, r?.userId))?.id ?? null;
|
|
112
113
|
}
|
|
113
|
-
async function
|
|
114
|
-
let i =
|
|
114
|
+
async function D(t, n, r) {
|
|
115
|
+
let i = v(t), a = [
|
|
115
116
|
{
|
|
116
117
|
name: "AI_ASSISTANT_MODE",
|
|
117
118
|
value: t
|
|
@@ -126,9 +127,9 @@ async function E(t, n, r) {
|
|
|
126
127
|
},
|
|
127
128
|
{
|
|
128
129
|
name: "AI_ASSISTANT_PRODUCT",
|
|
129
|
-
value:
|
|
130
|
+
value: m
|
|
130
131
|
}
|
|
131
|
-
], o =
|
|
132
|
+
], o = h(), s = await S({
|
|
132
133
|
document: e,
|
|
133
134
|
variables: { input: {
|
|
134
135
|
name: `ai-assistant-${t}-${Date.now()}`,
|
|
@@ -137,7 +138,7 @@ async function E(t, n, r) {
|
|
|
137
138
|
mode: "DEPLOYMENT",
|
|
138
139
|
containers: [{
|
|
139
140
|
name: "ai-assistant",
|
|
140
|
-
image:
|
|
141
|
+
image: _(t, o),
|
|
141
142
|
ports: [{
|
|
142
143
|
containerPort: 8080,
|
|
143
144
|
name: "http"
|
|
@@ -153,14 +154,14 @@ async function E(t, n, r) {
|
|
|
153
154
|
ttlSeconds: 600,
|
|
154
155
|
metadata: {
|
|
155
156
|
assistantMode: t,
|
|
156
|
-
assistantProduct:
|
|
157
|
+
assistantProduct: m,
|
|
157
158
|
assistantOwnerUserId: r?.userId ?? null,
|
|
158
159
|
assistantOrganizationId: r?.organizationId ?? null,
|
|
159
|
-
authStrategy:
|
|
160
|
-
imageTag:
|
|
160
|
+
authStrategy: u,
|
|
161
|
+
imageTag: g(t, o),
|
|
161
162
|
confirmWrites: !1
|
|
162
163
|
},
|
|
163
|
-
context:
|
|
164
|
+
context: x(n, r)
|
|
164
165
|
} },
|
|
165
166
|
workspaceId: n,
|
|
166
167
|
authContext: r
|
|
@@ -168,14 +169,14 @@ async function E(t, n, r) {
|
|
|
168
169
|
if (!s?.createSandbox?.id) throw Error("createSandbox returned empty response");
|
|
169
170
|
return s.createSandbox.id;
|
|
170
171
|
}
|
|
171
|
-
async function
|
|
172
|
+
async function O(e, t, n, r = 3e5) {
|
|
172
173
|
let a = Date.now();
|
|
173
174
|
for (; Date.now() - a < r;) {
|
|
174
|
-
let r = await
|
|
175
|
+
let r = await S({
|
|
175
176
|
document: i,
|
|
176
177
|
variables: {
|
|
177
178
|
id: e,
|
|
178
|
-
context:
|
|
179
|
+
context: x(t, n)
|
|
179
180
|
},
|
|
180
181
|
workspaceId: t,
|
|
181
182
|
authContext: n
|
|
@@ -186,7 +187,7 @@ async function D(e, t, n, r = 3e5) {
|
|
|
186
187
|
}
|
|
187
188
|
throw Error("Sandbox startup timed out");
|
|
188
189
|
}
|
|
189
|
-
async function
|
|
190
|
+
async function k(e, t, n, r, i, o) {
|
|
190
191
|
console.log("[BigConsole-Assistant] proxyToSandbox REQUEST", {
|
|
191
192
|
sandboxId: e,
|
|
192
193
|
workspaceId: t,
|
|
@@ -194,14 +195,14 @@ async function O(e, t, n, r, i, o) {
|
|
|
194
195
|
method: r,
|
|
195
196
|
hasBody: !!i
|
|
196
197
|
});
|
|
197
|
-
let s = (await
|
|
198
|
+
let s = (await S({
|
|
198
199
|
document: a,
|
|
199
200
|
variables: { input: {
|
|
200
201
|
sandboxId: e,
|
|
201
202
|
path: n,
|
|
202
203
|
method: r,
|
|
203
204
|
body: i,
|
|
204
|
-
context:
|
|
205
|
+
context: x(t, o)
|
|
205
206
|
} },
|
|
206
207
|
workspaceId: t,
|
|
207
208
|
authContext: o
|
|
@@ -221,11 +222,11 @@ async function O(e, t, n, r, i, o) {
|
|
|
221
222
|
}
|
|
222
223
|
return s.body;
|
|
223
224
|
}
|
|
224
|
-
async function
|
|
225
|
+
async function A(e, t, n, r, i = 9e4) {
|
|
225
226
|
let a = Date.now();
|
|
226
227
|
for (; Date.now() - a < i;) {
|
|
227
228
|
try {
|
|
228
|
-
await
|
|
229
|
+
await k(e, t, "/sessions", "POST", { mode: n }, r);
|
|
229
230
|
return;
|
|
230
231
|
} catch (e) {
|
|
231
232
|
let t = e instanceof Error ? e.message : String(e);
|
|
@@ -235,33 +236,33 @@ async function k(e, t, n, r, i = 9e4) {
|
|
|
235
236
|
}
|
|
236
237
|
throw Error("Assistant service did not become ready before timeout");
|
|
237
238
|
}
|
|
238
|
-
async function
|
|
239
|
-
return { sessionId: (await
|
|
239
|
+
async function j(e, t, n, r) {
|
|
240
|
+
return { sessionId: (await k(e, t, "/sessions", "POST", { mode: n }, r)).session.id };
|
|
240
241
|
}
|
|
241
|
-
async function
|
|
242
|
+
async function M(e, t, n, r, i, a, o) {
|
|
242
243
|
let s = {
|
|
243
244
|
mode: i,
|
|
244
245
|
prompt: r,
|
|
245
246
|
pageContext: a ?? void 0
|
|
246
247
|
};
|
|
247
|
-
await
|
|
248
|
+
await k(e, t, `/sessions/${n}/prompt-async`, "POST", s, o);
|
|
248
249
|
}
|
|
249
|
-
async function
|
|
250
|
-
return (await
|
|
250
|
+
async function N(e, t, n, r, i = 50) {
|
|
251
|
+
return (await k(e, t, `/sessions/${n}/messages?limit=${i}`, "GET", void 0, r)).messages ?? [];
|
|
251
252
|
}
|
|
252
|
-
async function
|
|
253
|
-
await
|
|
253
|
+
async function P(e, n, r = 600, i) {
|
|
254
|
+
await S({
|
|
254
255
|
document: t,
|
|
255
256
|
variables: {
|
|
256
257
|
id: e,
|
|
257
258
|
additionalSeconds: r,
|
|
258
|
-
context:
|
|
259
|
+
context: x(n, i)
|
|
259
260
|
},
|
|
260
261
|
workspaceId: n,
|
|
261
262
|
authContext: i
|
|
262
263
|
});
|
|
263
264
|
}
|
|
264
265
|
//#endregion
|
|
265
|
-
export {
|
|
266
|
+
export { D as createAssistantSandbox, j as createAssistantSession, P as extendAssistantSandboxTTL, E as findExistingSandbox, N as getAssistantMessages, T as isCachedAssistantSandboxReusable, M as sendAssistantPromptAsync, A as waitForAssistantServiceReady, O as waitForSandboxReady };
|
|
266
267
|
|
|
267
268
|
//# 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, Kind, type OperationDefinitionNode } from 'graphql';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n// Deep import: images.js has no dependencies, so this does not pull the shared\n// assistant barrel into the consumer's chunk. fe-libs owns the registry (ECR\n// since the AWS cutover), the image tags, the auth strategy and the mode\n// resolution; this file used to carry byte-for-byte copies of them.\nimport {\n ASSISTANT_API_AUTH_STRATEGY,\n getImageForMode as getSharedImageForMode,\n getImageTagForMode as getSharedImageTagForMode,\n resolveSandboxAssistantMode,\n} from '@burdenoff/fe-libs/shared/assistant/images';\nimport type { AssistantMode, AssistantRawMessage, AssistantRawSession, AssistantSandboxAuthContext } from './types';\nimport type { PageContext } from './pageContext';\nimport {\n CreateSandboxDocument,\n GetSandboxDocument,\n GetReusableAssistantSandboxDocument,\n GetActiveSandboxesDocument,\n ProxySandboxRequestDocument,\n ExtendSandboxTtlDocument,\n} from '../../generated/wspace-operations';\n\n// ── Product, runtime and image ─────────────────────────────────────\n\n/**\n * The product this transport stamps on every assistant sandbox it creates\n * (`metadata.assistantProduct`) — the same key the shared fe-libs assistant\n * stamps for HealthyBowl and VibeControls, and FluidGrids stamps for itself — and\n * the ONLY product whose sandboxes it will reuse (BOFF-7331).\n */\nexport const ASSISTANT_PRODUCT = 'bigconsole';\n\nexport function isProdRuntime(): boolean {\n if (typeof window === 'undefined') {\n return import.meta.env.VITE_DEPLOY_ENV === 'prod' || import.meta.env.VITE_APP_ENV === 'prod';\n }\n const host = window.location.hostname.toLowerCase();\n return host === 'app.bigconsole.com';\n}\n\n/**\n * Image tag recorded on the sandbox. It is metadata only: the sandbox\n * cf-proxy's AiAssistant pin decides which image actually runs (plan decision\n * D9), so the tag is NOT used to pick a sandbox for reuse — see\n * `isReusableAssistantSandbox`.\n */\nexport function getImageTagForMode(mode: AssistantMode, isProd: boolean = isProdRuntime()): string {\n return getSharedImageTagForMode(mode, isProd);\n}\n\n/**\n * `assistant` reuses the api-calls image (mode is selected at runtime via\n * AI_ASSISTANT_MODE). The registry is fe-libs' — the Azure registry this used to\n * hardcode was decommissioned on 2026-08-15 and is not in wspace-sandbox-svc's\n * default ALLOWED_IMAGE_REGISTRIES.\n */\nexport function getImageForMode(mode: AssistantMode, isProd: boolean = isProdRuntime()): string {\n return getSharedImageForMode(mode, isProd);\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\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: always opt into workspace-token auto-minting here instead of\n // passing through a cached token string from the shell. If we pass a stale\n // string, graphqlFetch validates it for the current workspace/project and\n // nulls it out, but it WILL NOT mint a replacement unless the caller used\n // `workspaceToken: true`. That exact behavior caused proxySandboxRequest\n // calls to fail with `Dual-token authentication failed` even though the\n // browser had a login session and the workspace context was known.\n 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 interface ActiveAssistantSandbox {\n id: string;\n name: string;\n status: string;\n metadata: Record<string, unknown> | null;\n template: string;\n createdBy: string;\n}\n\n/**\n * May this active sandbox be reused for `mode`?\n *\n * Every product's assistant runs as the same user in the same workspace, so\n * `getActiveSandboxes` returns HealthyBowl's, VibeControls' and FluidGrids'\n * assistant sandboxes too (BOFF-7331). Adopting one of those runs the wrong\n * product guide, and HB/VC `api-calls` sandboxes have CONFIRM_WRITES on, which\n * the legacy widget BigConsole mounts has no Approve UI for. So:\n * - the sandbox must be stamped `assistantProduct: 'bigconsole'`. Unstamped\n * sandboxes — every BigConsole sandbox created before this change — are NOT\n * reused: their product cannot be proven. They lapse on their 600s TTL.\n * - a sandbox recording `confirmWrites: true` is never reused, whoever\n * stamped it: it waits for approvals this widget cannot give.\n * - the image tag is NOT compared (D9). It never decided what runs.\n * - ownership FAILS CLOSED: with no `userId` nothing is reusable. fe-libs'\n * AuthTokenProvider starts with `userId` null, and a super_admin caller is\n * handed every sandbox in the workspace, so skipping the owner check when\n * the id is unknown would adopt another user's BigConsole sandbox. The\n * caller creates a fresh sandbox instead.\n */\nexport function isReusableAssistantSandbox(\n sandbox: ActiveAssistantSandbox,\n mode: AssistantMode,\n userId?: string | null\n): boolean {\n return (\n (sandbox.status === 'RUNNING' || sandbox.status === 'PROVISIONING' || sandbox.status === 'PENDING') &&\n sandbox.template === 'AI_BUILDER' &&\n resolveSandboxAssistantMode(sandbox) === mode &&\n !!userId &&\n sandbox.createdBy === userId &&\n sandbox.metadata?.authStrategy === ASSISTANT_API_AUTH_STRATEGY &&\n sandbox.metadata?.assistantProduct === ASSISTANT_PRODUCT &&\n sandbox.metadata?.confirmWrites !== true\n );\n}\n\n/**\n * May a sandbox id cached from an earlier page lifecycle (sessionStorage) be\n * adopted for `mode`?\n *\n * A cached id carries no proof of what it points at: a tab that adopted another\n * product's, an unstamped or another user's sandbox under an older build would\n * otherwise keep driving it. So the sandbox is fetched and held to exactly the\n * same predicate as a freshly listed one. A sandbox that no longer exists is not\n * reusable. Transport errors propagate; the caller treats them as \"not reusable\".\n */\nexport async function isCachedAssistantSandboxReusable(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<boolean> {\n // Checked before the call so a caller with no identity yet costs no round-trip.\n if (!authContext?.userId) return false;\n const data = await executeSandboxGraphQL<{ getSandbox: ActiveAssistantSandbox | null }>({\n document: GetReusableAssistantSandboxDocument,\n variables: { id: sandboxId, context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandbox = data.getSandbox;\n return !!sandbox && sandbox.id === sandboxId && isReusableAssistantSandbox(sandbox, mode, authContext.userId);\n}\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: ActiveAssistantSandbox[];\n }>({\n document: GetActiveSandboxesDocument,\n variables: { context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandboxes = data.getActiveSandboxes ?? [];\n\n return sandboxes.find((s) => isReusableAssistantSandbox(s, mode, authContext?.userId))?.id ?? null;\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: ASSISTANT_PRODUCT },\n ];\n // Resolved once so the image and its recorded tag can never disagree.\n const isProd = isProdRuntime();\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, isProd),\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 // Product stamp: the reuse filter only adopts sandboxes carrying it.\n assistantProduct: ASSISTANT_PRODUCT,\n assistantOwnerUserId: authContext?.userId ?? null,\n assistantOrganizationId: authContext?.organizationId ?? null,\n authStrategy: ASSISTANT_API_AUTH_STRATEGY,\n imageTag: getImageTagForMode(mode, isProd),\n // Recorded explicitly (as fe-libs does): the legacy widget has no\n // Approve UI, so these sandboxes never run with\n // AI_ASSISTANT_CONFIRM_WRITES.\n confirmWrites: false,\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 mode: AssistantMode,\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 // Use the documented assistant API contract itself as the readiness\n // signal. The assistant image definitively supports POST /sessions,\n // whereas /health is an implementation assumption and has proven to hang\n // behind the Cloudflare sandbox proxy.\n await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'POST', { mode }, 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|timeout/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 ready 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/**\n * All conversations held by this sandbox, newest first.\n *\n * The sandbox is the source of truth for the transcript, so the History list is\n * built from it rather than from anything we cache client-side — what the user\n * can reopen is exactly what the agent still has.\n */\nexport async function listAssistantSessions(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<AssistantRawSession[]> {\n const body = await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'GET', undefined, authContext);\n const sessions = body.sessions;\n return Array.isArray(sessions) ? (sessions as AssistantRawSession[]) : [];\n}\n\nexport async function deleteAssistantSession(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await proxyToSandbox(sandboxId, workspaceId, `/sessions/${sessionId}`, 'DELETE', undefined, authContext);\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
|
+
{"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';\n// Deep import: images.js has no dependencies, so this does not pull the shared\n// assistant barrel into the consumer's chunk. fe-libs owns the registry (ECR\n// since the AWS cutover), the image tags, the auth strategy and the mode\n// resolution; this file used to carry byte-for-byte copies of them.\nimport {\n ASSISTANT_API_AUTH_STRATEGY,\n getImageForMode as getSharedImageForMode,\n getImageTagForMode as getSharedImageTagForMode,\n resolveSandboxAssistantMode,\n} from '@burdenoff/fe-libs/shared/assistant/images';\nimport type { AssistantMode, AssistantRawMessage, AssistantRawSession, AssistantSandboxAuthContext } from './types';\nimport type { PageContext } from './pageContext';\nimport { BIGCONSOLE_PRODUCT } from './product';\nimport {\n CreateSandboxDocument,\n GetSandboxDocument,\n GetReusableAssistantSandboxDocument,\n GetActiveSandboxesDocument,\n ProxySandboxRequestDocument,\n ExtendSandboxTtlDocument,\n} from '../../generated/wspace-operations';\n\n// ── Product, runtime and image ─────────────────────────────────────\n\n/**\n * The product this transport stamps on every assistant sandbox it creates\n * (`metadata.assistantProduct`) — the same key the shared fe-libs assistant\n * stamps for HealthyBowl and VibeControls, and FluidGrids stamps for itself — and\n * the ONLY product whose sandboxes it will reuse (BOFF-7331).\n *\n * ★ The VALUE now comes from `./product`, which is the one place the id is\n * spelled. This constant keeps its name because the stamp, the env var and the\n * reuse predicate below all read it — but a second literal spelled the same way\n * is exactly how `AI_ASSISTANT_PRODUCT` and `metadata.assistantProduct` drift\n * apart, and `assistantSeamWiring.test.ts` now forbids one.\n */\nexport const ASSISTANT_PRODUCT = BIGCONSOLE_PRODUCT;\n\nexport function isProdRuntime(): boolean {\n if (typeof window === 'undefined') {\n return import.meta.env.VITE_DEPLOY_ENV === 'prod' || import.meta.env.VITE_APP_ENV === 'prod';\n }\n const host = window.location.hostname.toLowerCase();\n return host === 'app.bigconsole.com';\n}\n\n/**\n * Image tag recorded on the sandbox. It is metadata only: the sandbox\n * cf-proxy's AiAssistant pin decides which image actually runs (plan decision\n * D9), so the tag is NOT used to pick a sandbox for reuse — see\n * `isReusableAssistantSandbox`.\n */\nexport function getImageTagForMode(mode: AssistantMode, isProd: boolean = isProdRuntime()): string {\n return getSharedImageTagForMode(mode, isProd);\n}\n\n/**\n * `assistant` reuses the api-calls image (mode is selected at runtime via\n * AI_ASSISTANT_MODE). The registry is fe-libs' — the Azure registry this used to\n * hardcode was decommissioned on 2026-08-15 and is not in wspace-sandbox-svc's\n * default ALLOWED_IMAGE_REGISTRIES.\n */\nexport function getImageForMode(mode: AssistantMode, isProd: boolean = isProdRuntime()): string {\n return getSharedImageForMode(mode, isProd);\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\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: always opt into workspace-token auto-minting here instead of\n // passing through a cached token string from the shell. If we pass a stale\n // string, graphqlFetch validates it for the current workspace/project and\n // nulls it out, but it WILL NOT mint a replacement unless the caller used\n // `workspaceToken: true`. That exact behavior caused proxySandboxRequest\n // calls to fail with `Dual-token authentication failed` even though the\n // browser had a login session and the workspace context was known.\n 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 interface ActiveAssistantSandbox {\n id: string;\n name: string;\n status: string;\n metadata: Record<string, unknown> | null;\n template: string;\n createdBy: string;\n}\n\n/**\n * May this active sandbox be reused for `mode`?\n *\n * Every product's assistant runs as the same user in the same workspace, so\n * `getActiveSandboxes` returns HealthyBowl's, VibeControls' and FluidGrids'\n * assistant sandboxes too (BOFF-7331). Adopting one of those runs the wrong\n * product guide, and HB/VC `api-calls` sandboxes have CONFIRM_WRITES on, which\n * the legacy widget BigConsole mounts has no Approve UI for. So:\n * - the sandbox must be stamped `assistantProduct: 'bigconsole'`. Unstamped\n * sandboxes — every BigConsole sandbox created before this change — are NOT\n * reused: their product cannot be proven. They lapse on their 600s TTL.\n * - a sandbox recording `confirmWrites: true` is never reused, whoever\n * stamped it: it waits for approvals this widget cannot give.\n * - the image tag is NOT compared (D9). It never decided what runs.\n * - ownership FAILS CLOSED: with no `userId` nothing is reusable. fe-libs'\n * AuthTokenProvider starts with `userId` null, and a super_admin caller is\n * handed every sandbox in the workspace, so skipping the owner check when\n * the id is unknown would adopt another user's BigConsole sandbox. The\n * caller creates a fresh sandbox instead.\n */\nexport function isReusableAssistantSandbox(\n sandbox: ActiveAssistantSandbox,\n mode: AssistantMode,\n userId?: string | null\n): boolean {\n return (\n (sandbox.status === 'RUNNING' || sandbox.status === 'PROVISIONING' || sandbox.status === 'PENDING') &&\n sandbox.template === 'AI_BUILDER' &&\n resolveSandboxAssistantMode(sandbox) === mode &&\n !!userId &&\n sandbox.createdBy === userId &&\n sandbox.metadata?.authStrategy === ASSISTANT_API_AUTH_STRATEGY &&\n sandbox.metadata?.assistantProduct === ASSISTANT_PRODUCT &&\n sandbox.metadata?.confirmWrites !== true\n );\n}\n\n/**\n * May a sandbox id cached from an earlier page lifecycle (sessionStorage) be\n * adopted for `mode`?\n *\n * A cached id carries no proof of what it points at: a tab that adopted another\n * product's, an unstamped or another user's sandbox under an older build would\n * otherwise keep driving it. So the sandbox is fetched and held to exactly the\n * same predicate as a freshly listed one. A sandbox that no longer exists is not\n * reusable. Transport errors propagate; the caller treats them as \"not reusable\".\n */\nexport async function isCachedAssistantSandboxReusable(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<boolean> {\n // Checked before the call so a caller with no identity yet costs no round-trip.\n if (!authContext?.userId) return false;\n const data = await executeSandboxGraphQL<{ getSandbox: ActiveAssistantSandbox | null }>({\n document: GetReusableAssistantSandboxDocument,\n variables: { id: sandboxId, context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandbox = data.getSandbox;\n return !!sandbox && sandbox.id === sandboxId && isReusableAssistantSandbox(sandbox, mode, authContext.userId);\n}\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: ActiveAssistantSandbox[];\n }>({\n document: GetActiveSandboxesDocument,\n variables: { context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandboxes = data.getActiveSandboxes ?? [];\n\n return sandboxes.find((s) => isReusableAssistantSandbox(s, mode, authContext?.userId))?.id ?? null;\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: ASSISTANT_PRODUCT },\n ];\n // Resolved once so the image and its recorded tag can never disagree.\n const isProd = isProdRuntime();\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, isProd),\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 // Product stamp: the reuse filter only adopts sandboxes carrying it.\n assistantProduct: ASSISTANT_PRODUCT,\n assistantOwnerUserId: authContext?.userId ?? null,\n assistantOrganizationId: authContext?.organizationId ?? null,\n authStrategy: ASSISTANT_API_AUTH_STRATEGY,\n imageTag: getImageTagForMode(mode, isProd),\n // Recorded explicitly (as fe-libs does): the legacy widget has no\n // Approve UI, so these sandboxes never run with\n // AI_ASSISTANT_CONFIRM_WRITES.\n confirmWrites: false,\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 mode: AssistantMode,\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 // Use the documented assistant API contract itself as the readiness\n // signal. The assistant image definitively supports POST /sessions,\n // whereas /health is an implementation assumption and has proven to hang\n // behind the Cloudflare sandbox proxy.\n await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'POST', { mode }, 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|timeout/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 ready 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/**\n * All conversations held by this sandbox, newest first.\n *\n * The sandbox is the source of truth for the transcript, so the History list is\n * built from it rather than from anything we cache client-side — what the user\n * can reopen is exactly what the agent still has.\n */\nexport async function listAssistantSessions(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<AssistantRawSession[]> {\n const body = await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'GET', undefined, authContext);\n const sessions = body.sessions;\n return Array.isArray(sessions) ? (sessions as AssistantRawSession[]) : [];\n}\n\nexport async function deleteAssistantSession(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await proxyToSandbox(sandboxId, workspaceId, `/sessions/${sessionId}`, 'DELETE', undefined, authContext);\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":";;;;;;AAkDA,IAAa,IAAoB;AAEjC,SAAgB,IAAyB;AAKvC,QAJI,OAAO,SAAW,MACiC,KAE1C,OAAO,SAAS,SAAS,aAAa,KACnC;;AASlB,SAAgB,EAAmB,GAAqB,IAAkB,GAAe,EAAU;AACjG,QAAO,EAAyB,GAAM,EAAO;;AAS/C,SAAgB,EAAgB,GAAqB,IAAkB,GAAe,EAAU;AAC9F,QAAO,EAAsB,GAAM,EAAO;;AAG5C,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;;;AAaP,SAAS,EAAiB,GAA4C;AAIpE,QAHkB,EAAS,YAAY,MACpC,MAAsD,EAAW,SAAS,EAAK,qBACjF,EACiB,MAAM;;AAG1B,SAAS,IAAkC;AACzC,KAAI;AACF,SAAO,gBAAgB,OAAO,YAAY;SACpC;AACN,SAAO,gBAAgB,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE;;;AAI5E,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;CAUjB,IACI;AACJ,MAAK,IAAI,IAAU,GAAG,IAAU,GAAY,IAC1C,KAAI;AACF,SAAO,MAAM,EAAiC,EAAO;UAC9C,GAAO;AACd,MAAY;EACZ,IAAM,IAAU,aAAiB,QAAQ,EAAM,QAAQ,aAAa,GAAG,OAAO,EAAM,CAAC,aAAa;AAWlG,MAAI,EALF,EAAQ,SAAS,kBAAkB,IACnC,EAAQ,SAAS,eAAe,IAChC,EAAQ,SAAS,yBAAyB,IAC1C,EAAQ,SAAS,UAAU,IAC3B,EAAQ,SAAS,cAAc,KACb,MAAY,EAC9B,OAAM;EAIR,IAAM,IAAU,MAAe,KAAG;AAOlC,EANA,QAAQ,KAAK,4EAA4E;GACvF,SAAS,IAAU;GACnB;GACA,WAAW;GACX,OAAO,aAAiB,QAAQ,EAAM,UAAU,OAAO,EAAM;GAC9D,CAAC,EACF,MAAM,IAAI,SAAS,MAAY,WAAW,GAAS,EAAQ,CAAC;;AAIhE,OAAM;;AAGR,eAAe,EAAiC,GAK7B;CACjB,IAAM,IAAgB,EAAiB,EAAO,SAAS,EACjD,IAAS,MAAM,EAAoB;EACvC,SAAS;EACT,OAAO,EAAM,EAAO,SAAS;EAC7B,WAAW,EAAO;EAClB;EACA,WAAW,EAAO,aAAa,eAAe,KAAA;EAQ9C,gBAAgB;EAChB,aAAa,EAAO;EACpB,gBAAgB,EAAO,aAAa,kBAAkB;EACtD,0BAA0B;EAK1B,cAAc,EACZ,gBAAgB,GAAyB,EAC1C;EACF,CAAC;AAEF,KAAI,CAAC,EACH,OAAU,MAAM,0CAA0C;AAG5D,KAAI,EAAO,QAAQ,OAKjB,OAJA,QAAQ,MAAM,uDAAuD;EACnE;EACA,QAAQ,EAAO;EAChB,CAAC,EACQ,MAAM,EAAO,OAAO,KAAK,MAA+B,EAAM,QAAQ,CAAC,KAAK,KAAK,CAAC;AAG9F,KAAI,CAAC,EAAO,KACV,OAAU,MAAM,mCAAmC;AAGrD,QAAO,EAAO;;AAkChB,SAAgB,EACd,GACA,GACA,GACS;AACT,SACG,EAAQ,WAAW,aAAa,EAAQ,WAAW,kBAAkB,EAAQ,WAAW,cACzF,EAAQ,aAAa,gBACrB,EAA4B,EAAQ,KAAK,KACzC,CAAC,CAAC,KACF,EAAQ,cAAc,KACtB,EAAQ,UAAU,iBAAiB,KACnC,EAAQ,UAAU,qBAAqB,KACvC,EAAQ,UAAU,kBAAkB;;AAcxC,eAAsB,EACpB,GACA,GACA,GACA,GACkB;AAElB,KAAI,CAAC,GAAa,OAAQ,QAAO;CAOjC,IAAM,KANO,MAAM,EAAqE;EACtF,UAAU;EACV,WAAW;GAAE,IAAI;GAAW,SAAS,EAAyB,GAAa,EAAY;GAAE;EACzF;EACA;EACD,CAAC,EACmB;AACrB,QAAO,CAAC,CAAC,KAAW,EAAQ,OAAO,KAAa,EAA2B,GAAS,GAAM,EAAY,OAAO;;AAG/G,eAAsB,EACpB,GACA,GACA,GACwB;AAWxB,UAVa,MAAM,EAEhB;EACD,UAAU;EACV,WAAW,EAAE,SAAS,EAAyB,GAAa,EAAY,EAAE;EAC1E;EACA;EACD,CAAC,EACqB,sBAAsB,EAAE,EAE9B,MAAM,MAAM,EAA2B,GAAG,GAAM,GAAa,OAAO,CAAC,EAAE,MAAM;;AAKhG,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;GAAmB;EAC3D,EAEK,IAAS,GAAe,EAExB,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,GAAM,EAAO;IAGpC,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;IAEf,kBAAkB;IAClB,sBAAsB,GAAa,UAAU;IAC7C,yBAAyB,GAAa,kBAAkB;IACxD,cAAc;IACd,UAAU,EAAmB,GAAM,EAAO;IAI1C,eAAe;IAChB;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,GACA,IAAY,KACG;CACf,IAAM,IAAY,KAAK,KAAK;AAE5B,QAAO,KAAK,KAAK,GAAG,IAAY,IAAW;AACzC,MAAI;AAKF,SAAM,EAAe,GAAW,GAAa,aAAa,QAAQ,EAAE,SAAM,EAAE,EAAY;AACxF;WACO,GAAO;GACd,IAAM,IAAU,aAAiB,QAAQ,EAAM,UAAU,OAAO,EAAM;AACtE,OAAI,CAAC,oFAAoF,KAAK,EAAQ,CACpG,OAAM;;AAIV,QAAM,IAAI,SAAS,MAAY,WAAW,GAAS,IAAK,CAAC;;AAG3D,OAAU,MAAM,wDAAwD;;AAK1E,eAAsB,EACpB,GACA,GACA,GACA,GACgC;AAGhC,QAAO,EAAE,YAFI,MAAM,EAAe,GAAW,GAAa,aAAa,QAAQ,EAAE,SAAM,EAAE,EAAY,EAChF,QACO,IAAc;;AA+B5C,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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BIGCONSOLE_PRODUCT as e } from "./product.js";
|
|
2
|
+
import { describeCurrentPage as t, gatherPageContext as n } from "./pageContext.js";
|
|
3
|
+
import { buildPromptForMode as r } from "./assistantPrompt.js";
|
|
4
|
+
import { BIGCONSOLE_ASSISTANT_MODES as i } from "./modes.js";
|
|
5
|
+
//#region src/bigconsole/assistant/assistantProduct.ts
|
|
6
|
+
var a = {
|
|
7
|
+
attachments: "/BigConsole/AI Assistant/Attachments",
|
|
8
|
+
artifacts: "/BigConsole/AI Assistant/Artifacts"
|
|
9
|
+
}, o = {
|
|
10
|
+
product: e,
|
|
11
|
+
modes: i,
|
|
12
|
+
describeCurrentPage: t,
|
|
13
|
+
gatherPageContext: n,
|
|
14
|
+
buildPrompt: r,
|
|
15
|
+
storageFolders: a
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { o as BIGCONSOLE_ASSISTANT_PRODUCT, a as STORAGE_FOLDERS };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=assistantProduct.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistantProduct.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantProduct.ts"],"sourcesContent":["/**\n * BigConsole's answers to the shared assistant's product seam, as one value.\n *\n * ★★ Every capability field on fe-libs' `AssistantProductProvider` is OPTIONAL,\n * and a missing one does not throw — it degrades SILENTLY. No `gatherPageContext`\n * sends every turn with no page context; no `buildPrompt` drops both the\n * confirm-before-mutate guardrail and the language directive; no\n * `storageFolders` starts a second folder beside the one holding every saved\n * attachment.\n *\n * So the value is typed `Required<…>` over the provider's own props: forgetting\n * a field, or fe-libs ADDING one, is a type error in this file rather than a\n * quiet regression in production.\n */\nimport type { AssistantProductProviderProps } from '@burdenoff/fe-libs/shared/assistant/product';\n\nimport { buildPromptForMode } from './assistantPrompt';\nimport { BIGCONSOLE_ASSISTANT_MODES } from './modes';\nimport { describeCurrentPage, gatherPageContext } from './pageContext';\nimport { BIGCONSOLE_PRODUCT } from './product';\n\n/**\n * Where this product's assistant files land in the workspace file library.\n *\n * ★★ Capitalised as a DISPLAY name while the product id is `bigconsole`, which\n * is exactly why fe-libs cannot derive it: its fallback is\n * `/${product}/AI Assistant/…` → `/bigconsole/AI Assistant/…`.\n *\n * These strings become load-bearing the moment anything is saved. A wrong value\n * does not fail loudly — it quietly starts a SECOND folder beside the first and\n * orphans everything in the original. Today nothing can be orphaned: BigConsole's\n * legacy transport extracts uploaded files into the prompt and saves nothing to\n * the library, so no `/BigConsole/AI Assistant/*` path exists yet. That makes\n * this a free choice now and an expensive one the moment BC4 ships attachments.\n */\nexport const STORAGE_FOLDERS = {\n attachments: '/BigConsole/AI Assistant/Attachments',\n artifacts: '/BigConsole/AI Assistant/Artifacts',\n} as const;\n\n/**\n * Every seam field, required — the provider's props minus its children, and\n * minus the one answer BigConsole deliberately does not give.\n *\n * ★★ `reportTokenUsage` is omitted ON EVIDENCE, not by oversight, and this\n * `Omit` member is the only place that decision is expressed in the type system.\n * `wspace-bigconsole-svc` declares NO token-usage mutation at all, and nothing in\n * this MFE reports one today. The obvious \"fix\" — wiring fe-libs' shared\n * reporter — would bill VibeControls for BigConsole turns: `reportAiTokenUsage`\n * is declared only in `wspace-vibecontrols-svc`, against the\n * `vibecontrols.ai.credits` quota. Metering BigConsole is a backend change.\n *\n * Note that BigConsole's `bigconsole.*` quotas are a different mechanism —\n * they cap entity counts, not AI spend — so their presence is not evidence that\n * turns are metered.\n */\nexport type BigConsoleAssistantProduct = Required<Omit<AssistantProductProviderProps, 'children' | 'reportTokenUsage'>>;\n\n/**\n * ★ Each field points at a product-owned implementation BY IDENTITY — no inline\n * arrow wrappers. The seam guard asserts `toBe`, because a wrapper is how a\n * function silently stops being the one the tests cover.\n *\n * ★ `writeAccess` is deliberately absent: it is a PROP on the shared panel, not\n * a seam field, so it belongs to BC4's panel mount rather than here. Same for\n * the preview controller, which the panel takes as `panelProps.preview`.\n */\nexport const BIGCONSOLE_ASSISTANT_PRODUCT: BigConsoleAssistantProduct = {\n product: BIGCONSOLE_PRODUCT,\n modes: BIGCONSOLE_ASSISTANT_MODES,\n describeCurrentPage,\n gatherPageContext,\n buildPrompt: buildPromptForMode,\n storageFolders: STORAGE_FOLDERS,\n};\n"],"mappings":";;;;;AAmCA,IAAa,IAAkB;CAC7B,aAAa;CACb,WAAW;CACZ,EA6BY,IAA2D;CACtE,SAAS;CACT,OAAO;CACP;CACA;CACA,aAAa;CACb,gBAAgB;CACjB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/bigconsole/assistant/assistantPrompt.ts
|
|
2
|
+
var e = "--- User request ---", t = [
|
|
3
|
+
"You are in api-calls mode for BigConsole and can run GraphQL queries and mutations on the user behalf.",
|
|
4
|
+
"Before performing ANY write (create, update, delete — any mutation):",
|
|
5
|
+
"1. State exactly what you are about to change (entity, fields, and values).",
|
|
6
|
+
"2. Ask the user to confirm, and WAIT for an explicit \"yes\"/\"confirm\" before executing it.",
|
|
7
|
+
"Read-only queries may run without confirmation. Confirm each write separately; never batch mutations behind a single confirmation.",
|
|
8
|
+
"",
|
|
9
|
+
e
|
|
10
|
+
].join("\n");
|
|
11
|
+
function n(e, t) {
|
|
12
|
+
return [
|
|
13
|
+
t ? `Reply in ${e}. The user has explicitly chosen this language, so use it even if they write to you in another one.` : `Reply in ${e}, matching the language the user wrote in.`,
|
|
14
|
+
`Translate any English documentation you ground your answer in into ${e} rather than switching to English to quote it.`,
|
|
15
|
+
"Keep identifiers, code, CLI flags, API field names and product nouns in their original form — translating them would make them wrong."
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
18
|
+
var r = [
|
|
19
|
+
"This turn was DICTATED, so treat unusual words as probable speech-recognition errors rather than as what the user meant.",
|
|
20
|
+
"Repair obvious mis-hearings of technical terms, product nouns, column names and command names using the surrounding context.",
|
|
21
|
+
"Do NOT guess at a mis-heard data sink name, dashboard id, parser name, widget id or column header — ask which one the user meant before acting on it."
|
|
22
|
+
].join("\n"), i = ["Your reply will be read aloud. Write it to be heard, not scanned:", "short sentences, no markdown syntax, no tables, and no code blocks unless the user asked for code."].join("\n");
|
|
23
|
+
function a(a, o, s, c = !1, l = !1) {
|
|
24
|
+
let u = [
|
|
25
|
+
s ? n(s, c) : "",
|
|
26
|
+
l ? r : "",
|
|
27
|
+
l ? i : ""
|
|
28
|
+
].filter(Boolean).join("\n\n"), d = u ? `${u}\n\n` : "";
|
|
29
|
+
return a === "api-calls" ? `${d}${t}\n${o}` : d ? `${d}${e}\n${o}` : o;
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { t as API_CALLS_CONFIRM_PREAMBLE, e as USER_REQUEST_DELIMITER, a as buildPromptForMode };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=assistantPrompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assistantPrompt.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantPrompt.ts"],"sourcesContent":["/**\n * Soft confirm-before-mutate guardrail for the assistant's write mode, plus the\n * language and dictation instructions.\n *\n * The in-sandbox agent executes mutations autonomously — there is no hard\n * server-side confirmation — so the describe-and-confirm instruction is\n * prepended to the user's TURN, not to the session system prompt. Sending it as\n * `system` would REPLACE the whole mode prompt and silently delete the agent's\n * api-calls behaviour along with it.\n *\n * This is ADVISORY. The dependable guard is the host-side consent dialog plus\n * the admin gate.\n *\n * Ported from FluidGrids' `assistantPrompt.ts` (itself ported from\n * microfe-vibecontrols), with BigConsole nouns. The composition and the\n * delimiter discipline are unchanged on purpose — see `buildPromptForMode`.\n */\n\n/**\n * ★ The TRANSPORT's mode type, not BigConsole's narrower list. The shared panel\n * calls `buildPrompt` with whatever mode its store holds, which can be one this\n * product no longer offers (a restored conversation, or a build that dropped a\n * mode) until the panel resets it, so the parameter has to accept every mode the\n * panel can send.\n */\nimport type { AssistantMode } from '@burdenoff/fe-libs/shared/assistant/types';\n\n/**\n * Marks where our instructions stop and the user's words begin. Exactly one may\n * exist in a prompt, and it must be last — see `buildPromptForMode`.\n */\nexport const USER_REQUEST_DELIMITER = '--- User request ---';\n\nexport const API_CALLS_CONFIRM_PREAMBLE = [\n 'You are in api-calls mode for BigConsole and can run GraphQL queries and mutations on the user behalf.',\n 'Before performing ANY write (create, update, delete — any mutation):',\n '1. State exactly what you are about to change (entity, fields, and values).',\n '2. Ask the user to confirm, and WAIT for an explicit \"yes\"/\"confirm\" before executing it.',\n 'Read-only queries may run without confirmation. Confirm each write separately; never batch mutations behind a single confirmation.',\n '',\n USER_REQUEST_DELIMITER,\n].join('\\n');\n\n/**\n * Ask for the reply in the user's language.\n *\n * ★ Nothing else does. The sandbox system prompt carries no language\n * instruction and the grounding documentation is entirely in English, so every\n * force in the session pushes the answer towards English regardless of what the\n * user wrote. The model's tendency to mirror its input is the only thing\n * resisting that, and it loses.\n *\n * The second clause is the one that matters: **translate the docs, do not\n * switch to their language.** Without it a grounded answer drifts back to\n * English mid-reply simply because the source material is English.\n */\nfunction buildLanguagePreamble(languageName: string, isExplicitChoice: boolean): string {\n return [\n isExplicitChoice\n ? `Reply in ${languageName}. The user has explicitly chosen this language, so use it even if they write to you in another one.`\n : `Reply in ${languageName}, matching the language the user wrote in.`,\n `Translate any English documentation you ground your answer in into ${languageName} rather than switching to English to quote it.`,\n 'Keep identifiers, code, CLI flags, API field names and product nouns in their original form — translating them would make them wrong.',\n ].join('\\n');\n}\n\n/**\n * Told to the model when the turn was SPOKEN rather than typed.\n *\n * ★ Speech recognition does not fail uniformly — it fails on specific words,\n * and in BigConsole the ones it fails on are predictable: data sink names,\n * dashboard ids, parser names and column headers. A recogniser has no model for\n * a dashboard id read aloud, so a sentence comes back mostly right with the\n * load-bearing words wrong.\n *\n * ★ The refusal to guess is the important half. Charitable interpretation is\n * right for a mangled verb and wrong for a mangled IDENTIFIER: acting on a\n * misheard dashboard id edits somebody else's dashboard. One extra question\n * costs a few seconds.\n */\nconst DICTATION_PREAMBLE = [\n 'This turn was DICTATED, so treat unusual words as probable speech-recognition errors rather than as what the user meant.',\n 'Repair obvious mis-hearings of technical terms, product nouns, column names and command names using the surrounding context.',\n 'Do NOT guess at a mis-heard data sink name, dashboard id, parser name, widget id or column header — ask which one the user meant before acting on it.',\n].join('\\n');\n\n/** A dictated turn is a SPOKEN conversation, so the reply is spoken too. */\nconst VOICE_REPLY_PREAMBLE = [\n 'Your reply will be read aloud. Write it to be heard, not scanned:',\n 'short sentences, no markdown syntax, no tables, and no code blocks unless the user asked for code.',\n].join('\\n');\n\n/**\n * Compose the turn actually sent to the agent.\n *\n * Ordering is deliberate. Dictation goes AFTER the language lines and before the\n * delimiter: it is an instruction about the user's text, so it belongs with the\n * instructions rather than inside them.\n *\n * ★★ `USER_REQUEST_DELIMITER` is emitted exactly once, here. The api-calls\n * preamble ends with its own copy, so it must not be added again — a second one\n * mid-prompt leaves the safety instructions sitting *after* a \"User request\"\n * marker, i.e. presented to the model as though the user had written them.\n *\n * ★ No branch for Ask (`assistant`). It is read-only — its sandbox boots with\n * `AI_ASSISTANT_READONLY=1` — so there is no write to confirm, and inventing a\n * confirmation preamble for it would be copy that lies about what the mode does.\n */\nexport function buildPromptForMode(\n mode: AssistantMode,\n content: string,\n languageName?: string,\n isExplicitChoice = false,\n dictated = false\n): string {\n const preamble = [\n languageName ? buildLanguagePreamble(languageName, isExplicitChoice) : '',\n dictated ? DICTATION_PREAMBLE : '',\n dictated ? VOICE_REPLY_PREAMBLE : '',\n ]\n .filter(Boolean)\n .join('\\n\\n');\n const language = preamble ? `${preamble}\\n\\n` : '';\n if (mode === 'api-calls') return `${language}${API_CALLS_CONFIRM_PREAMBLE}\\n${content}`;\n return language ? `${language}${USER_REQUEST_DELIMITER}\\n${content}` : content;\n}\n"],"mappings":";AA+BA,IAAa,IAAyB,wBAEzB,IAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK;AAeZ,SAAS,EAAsB,GAAsB,GAAmC;AACtF,QAAO;EACL,IACI,YAAY,EAAa,uGACzB,YAAY,EAAa;EAC7B,sEAAsE,EAAa;EACnF;EACD,CAAC,KAAK,KAAK;;AAiBd,IAAM,IAAqB;CACzB;CACA;CACA;CACD,CAAC,KAAK,KAAK,EAGN,IAAuB,CAC3B,qEACA,qGACD,CAAC,KAAK,KAAK;AAkBZ,SAAgB,EACd,GACA,GACA,GACA,IAAmB,IACnB,IAAW,IACH;CACR,IAAM,IAAW;EACf,IAAe,EAAsB,GAAc,EAAiB,GAAG;EACvE,IAAW,IAAqB;EAChC,IAAW,IAAuB;EACnC,CACE,OAAO,QAAQ,CACf,KAAK,OAAO,EACT,IAAW,IAAW,GAAG,EAAS,QAAQ;AAEhD,QADI,MAAS,cAAoB,GAAG,IAAW,EAA2B,IAAI,MACvE,IAAW,GAAG,IAAW,EAAuB,IAAI,MAAY"}
|
|
@@ -1,45 +1,46 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BIGCONSOLE_PRODUCT as e } from "./product.js";
|
|
2
|
+
import { graphqlFetch as t } from "@burdenoff/fe-libs/shared/graphql";
|
|
2
3
|
//#region src/bigconsole/assistant/conversationHistoryApi.ts
|
|
3
|
-
var
|
|
4
|
+
var n = e, r = "\n id\n title\n agentSessionId\n updatedAt\n", i = `
|
|
4
5
|
query AssistantConversations($productId: String!, $first: Int) {
|
|
5
|
-
assistantConversations(productId: $productId, first: $first) { ${
|
|
6
|
+
assistantConversations(productId: $productId, first: $first) { ${r} }
|
|
6
7
|
}
|
|
7
|
-
`,
|
|
8
|
+
`, a = "\n query AssistantConversationMessages($conversationId: ID!, $first: Int) {\n assistantConversationMessages(conversationId: $conversationId, first: $first) {\n id\n role\n content\n createdAt\n }\n }\n", o = `
|
|
8
9
|
mutation SaveAssistantTurn($input: SaveAssistantTurnInput!) {
|
|
9
|
-
saveAssistantTurn(input: $input) { ${
|
|
10
|
+
saveAssistantTurn(input: $input) { ${r} }
|
|
10
11
|
}
|
|
11
|
-
`,
|
|
12
|
-
async function
|
|
13
|
-
let n = await
|
|
12
|
+
`, s = "\n mutation DeleteAssistantConversation($conversationId: ID!) {\n deleteAssistantConversation(conversationId: $conversationId)\n }\n";
|
|
13
|
+
async function c(e) {
|
|
14
|
+
let n = await t({
|
|
14
15
|
gateway: "workspace",
|
|
15
|
-
query:
|
|
16
|
-
operationName:
|
|
17
|
-
variables:
|
|
18
|
-
authToken:
|
|
16
|
+
query: e.query,
|
|
17
|
+
operationName: e.operationName,
|
|
18
|
+
variables: e.variables,
|
|
19
|
+
authToken: e.authContext?.accessToken ?? void 0,
|
|
19
20
|
workspaceToken: !0,
|
|
20
|
-
workspaceId:
|
|
21
|
-
organizationId:
|
|
21
|
+
workspaceId: e.workspaceId,
|
|
22
|
+
organizationId: e.authContext?.organizationId ?? !0,
|
|
22
23
|
suppressGlobalErrorEvent: !0
|
|
23
24
|
});
|
|
24
25
|
if (n.errors?.length) throw Error(n.errors.map((e) => e.message).join(", "));
|
|
25
|
-
if (!n.data) throw Error(`${
|
|
26
|
+
if (!n.data) throw Error(`${e.operationName} returned no data`);
|
|
26
27
|
return n.data;
|
|
27
28
|
}
|
|
28
|
-
async function
|
|
29
|
-
return (await
|
|
30
|
-
query:
|
|
29
|
+
async function l(e, t, r = 25) {
|
|
30
|
+
return (await c({
|
|
31
|
+
query: i,
|
|
31
32
|
operationName: "AssistantConversations",
|
|
32
33
|
variables: {
|
|
33
|
-
productId:
|
|
34
|
-
first:
|
|
34
|
+
productId: n,
|
|
35
|
+
first: r
|
|
35
36
|
},
|
|
36
37
|
workspaceId: e,
|
|
37
38
|
authContext: t
|
|
38
39
|
})).assistantConversations ?? [];
|
|
39
40
|
}
|
|
40
|
-
async function
|
|
41
|
-
return (await
|
|
42
|
-
query:
|
|
41
|
+
async function u(e, t, n, r = 200) {
|
|
42
|
+
return (await c({
|
|
43
|
+
query: a,
|
|
43
44
|
operationName: "AssistantConversationMessages",
|
|
44
45
|
variables: {
|
|
45
46
|
conversationId: e,
|
|
@@ -49,24 +50,24 @@ async function l(e, t, n, r = 200) {
|
|
|
49
50
|
authContext: n
|
|
50
51
|
})).assistantConversationMessages ?? [];
|
|
51
52
|
}
|
|
52
|
-
async function
|
|
53
|
-
return (await
|
|
54
|
-
query:
|
|
53
|
+
async function d(e, t, r) {
|
|
54
|
+
return (await c({
|
|
55
|
+
query: o,
|
|
55
56
|
operationName: "SaveAssistantTurn",
|
|
56
57
|
variables: { input: {
|
|
57
58
|
conversationId: e.conversationId,
|
|
58
|
-
productId:
|
|
59
|
+
productId: n,
|
|
59
60
|
prompt: e.prompt,
|
|
60
61
|
reply: e.reply,
|
|
61
62
|
agentSessionId: e.agentSessionId
|
|
62
63
|
} },
|
|
63
|
-
workspaceId:
|
|
64
|
+
workspaceId: t,
|
|
64
65
|
authContext: r
|
|
65
66
|
})).saveAssistantTurn;
|
|
66
67
|
}
|
|
67
|
-
async function
|
|
68
|
-
await
|
|
69
|
-
query:
|
|
68
|
+
async function f(e, t, n) {
|
|
69
|
+
await c({
|
|
70
|
+
query: s,
|
|
70
71
|
operationName: "DeleteAssistantConversation",
|
|
71
72
|
variables: { conversationId: e },
|
|
72
73
|
workspaceId: t,
|
|
@@ -74,6 +75,6 @@ async function d(e, t, n) {
|
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
77
|
//#endregion
|
|
77
|
-
export {
|
|
78
|
+
export { f as deleteAssistantConversation, u as getAssistantConversationMessages, l as listAssistantConversations, d as saveAssistantTurn };
|
|
78
79
|
|
|
79
80
|
//# sourceMappingURL=conversationHistoryApi.js.map
|