@opengeni/core 2.2.0-canary.0 → 2.4.0-canary.0

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,5 +1,5 @@
1
1
  import { type RigChangeVerification, type RigProviderImage, type RigProviderImages, type RigVersion, type SandboxBackend } from "@opengeni/contracts";
2
- export type RigProviderImageDefinition = Pick<RigVersion, "image" | "setupScript" | "checks" | "credentialHooks" | "defaultVariableSetIds">;
2
+ export type RigProviderImageDefinition = Pick<RigVersion, "setupScript" | "checks" | "credentialHooks" | "defaultVariableSetIds">;
3
3
  export declare function rigProviderImageProviderBindingKeyHash(bindingKey: string): string;
4
4
  export declare function rigProviderImageSetupHash(definition: Pick<RigProviderImageDefinition, "setupScript">): string;
5
5
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/core",
3
- "version": "2.2.0-canary.0",
3
+ "version": "2.4.0-canary.0",
4
4
  "description": "OpenGeni framework-agnostic core: the domain, access, and billing layers (neutral access, off-HTTP V2 surface). Behavior-preserving extraction from apps/api — keeps Hono's HTTPException for error throwing (typed-errors cleanup deferred).",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -50,15 +50,15 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@modelcontextprotocol/sdk": "^1.29.0",
53
- "@opengeni/codex": "^0.2.19-canary.0",
54
- "@opengeni/config": "^0.19.1-canary.0",
55
- "@opengeni/contracts": "^2.3.0-canary.0",
56
- "@opengeni/db": "^3.2.0-canary.0",
57
- "@opengeni/documents": "^0.7.0-canary.0",
58
- "@opengeni/events": "^0.3.124-canary.0",
59
- "@opengeni/observability": "^0.8.4-canary.0",
60
- "@opengeni/runtime": "^1.3.1-canary.0",
61
- "@opengeni/storage": "^0.2.106-canary.0",
53
+ "@opengeni/codex": "^0.2.19-canary.1",
54
+ "@opengeni/config": "^0.20.1-canary.0",
55
+ "@opengeni/contracts": "^2.5.0-canary.0",
56
+ "@opengeni/db": "^3.4.0-canary.0",
57
+ "@opengeni/documents": "^0.8.2-canary.0",
58
+ "@opengeni/events": "^0.4.0-canary.0",
59
+ "@opengeni/observability": "^0.8.6-canary.0",
60
+ "@opengeni/runtime": "^1.4.0-canary.0",
61
+ "@opengeni/storage": "^0.2.108-canary.0",
62
62
  "hono": "^4.12.18",
63
63
  "zod": "^4.2.1"
64
64
  },
@@ -388,6 +388,7 @@ async function resolveAccessContext(c: Context, deps: AccessDeps): Promise<Acces
388
388
  email: session.user.email,
389
389
  name: session.user.name,
390
390
  emailVerified: session.user.emailVerified,
391
+ provisionFallbackOrganization: false,
391
392
  });
392
393
  canonicalManagedCookieContexts.add(context);
393
394
  return context;
@@ -103,18 +103,32 @@ async function hydrateNewSessionDraft(
103
103
  }
104
104
 
105
105
  const options = { ...mapped.options };
106
- if (options.variableSetId) {
107
- if (
108
- !hasPermission(grant.permissions, "variable-sets:attach") ||
109
- !hasPermission(grant.permissions, "variable-sets:use") ||
110
- !(await getVariableSet(
111
- deps.db,
112
- { accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
113
- options.variableSetId,
114
- ))
115
- ) {
106
+ const variableSetIds = options.variableSetIds ?? [];
107
+ if (variableSetIds.length > 0) {
108
+ const selectionsAuthorized =
109
+ hasPermission(grant.permissions, "variable-sets:attach") &&
110
+ hasPermission(grant.permissions, "variable-sets:use") &&
111
+ (
112
+ await Promise.all(
113
+ variableSetIds.map((variableSetId) =>
114
+ getVariableSet(
115
+ deps.db,
116
+ { accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
117
+ variableSetId,
118
+ ),
119
+ ),
120
+ )
121
+ ).every(Boolean);
122
+ if (selectionsAuthorized) {
123
+ options.variableSetIds = variableSetIds;
124
+ options.variableSetId = variableSetIds[variableSetIds.length - 1];
125
+ } else {
126
+ delete options.variableSetIds;
116
127
  delete options.variableSetId;
117
128
  }
129
+ } else {
130
+ delete options.variableSetIds;
131
+ delete options.variableSetId;
118
132
  }
119
133
  if (options.rigId) {
120
134
  const rig = await getRig(deps.db, workspaceId, options.rigId);
@@ -7,9 +7,11 @@ import {
7
7
  type ForkSessionRequest,
8
8
  type SessionAuthorizationSurface,
9
9
  type UpdateSessionVisibilityRequest,
10
+ type VariableSet,
10
11
  } from "@opengeni/contracts";
11
12
  import {
12
13
  forkSessionContent,
14
+ getRig,
13
15
  getPrivateSessionCreatePolicy,
14
16
  getSessionEventForSubject,
15
17
  isRetryableDatabaseTransportFailure,
@@ -17,6 +19,7 @@ import {
17
19
  replayAppliedSessionFork,
18
20
  sessionTenancyProductActivated,
19
21
  SessionTenancyAccessError,
22
+ SessionTenancyInvalidRequestError,
20
23
  SessionTenancyNotActivatedError,
21
24
  transitionSessionVisibility,
22
25
  type Database,
@@ -26,8 +29,12 @@ import { publishDurableSessionEvents, type EventBus } from "@opengeni/events";
26
29
  import { requirePermission, type AccessGrantAuthorization } from "../access";
27
30
  import { requireSessionAuthorization } from "../session-authorization";
28
31
  import type { AppDependencies } from "../dependencies";
32
+ import { validateVariableSetAttachment } from "../domain/environments";
29
33
 
30
- type SessionTenancyDependencies = Pick<AppDependencies, "db" | "bus" | "sessionAuthorization">;
34
+ type SessionTenancyDependencies = Pick<
35
+ AppDependencies,
36
+ "db" | "bus" | "sessionAuthorization" | "settings"
37
+ >;
31
38
 
32
39
  export class SessionTenancyManagedHumanRequiredError extends Error {
33
40
  readonly name = "SessionTenancyManagedHumanRequiredError";
@@ -286,9 +293,72 @@ export async function forkManagedHumanSession(
286
293
  request.visibility === "private" ? ("user_private" as const) : ("workspace_shared" as const),
287
294
  workspaceSharedAcknowledged: request.workspaceSharedAcknowledged,
288
295
  operationKey: request.idempotencyKey,
296
+ ...(request.rigId !== undefined || request.variableSetIds !== undefined
297
+ ? {
298
+ runtimeRequest: {
299
+ rigId: request.rigId ?? null,
300
+ variableSetIds: request.variableSetIds ?? [],
301
+ },
302
+ }
303
+ : {}),
289
304
  };
290
305
  const recoverAppliedReceipt = async () =>
291
306
  await runTenancyMutation(async () => await replayAppliedSessionFork(deps.db, forkInput));
307
+ const runtimeSetupRequested = request.rigId !== undefined || request.variableSetIds !== undefined;
308
+ let runtimeConfigurationPromise: Promise<{
309
+ variableSetIds: string[];
310
+ rigId: string | null;
311
+ rigVersionId: string | null;
312
+ }> | null = null;
313
+ const resolveRuntimeConfiguration = async () => {
314
+ if (runtimeConfigurationPromise) return await runtimeConfigurationPromise;
315
+ runtimeConfigurationPromise = (async () => {
316
+ const variableSets: VariableSet[] = [];
317
+ for (const variableSetId of request.variableSetIds ?? []) {
318
+ variableSets.push(
319
+ await validateVariableSetAttachment(
320
+ { settings: deps.settings, db: deps.db },
321
+ authorization.grant,
322
+ workspaceId,
323
+ variableSetId,
324
+ ),
325
+ );
326
+ }
327
+ let rigVersionId: string | null = null;
328
+ if (request.rigId) {
329
+ requirePermission(authorization.grant, "rigs:use");
330
+ const rig = await getRig(deps.db, authorization.grant, request.rigId);
331
+ if (!rig?.activeVersion) {
332
+ throw new SessionTenancyInvalidRequestError();
333
+ }
334
+ for (const defaultVariableSetId of new Set(rig.activeVersion.defaultVariableSetIds)) {
335
+ await validateVariableSetAttachment(
336
+ { settings: deps.settings, db: deps.db },
337
+ authorization.grant,
338
+ workspaceId,
339
+ defaultVariableSetId,
340
+ );
341
+ }
342
+ rigVersionId = rig.activeVersion.id;
343
+ }
344
+ return {
345
+ variableSetIds: variableSets.map((variableSet) => variableSet.id),
346
+ rigId: request.rigId ?? null,
347
+ rigVersionId,
348
+ };
349
+ })();
350
+ return await runtimeConfigurationPromise;
351
+ };
352
+ const publishRuntimeConfiguration = async (result: ForkSessionContentResult) => {
353
+ if (!result.runtimeEventId || !result.runtimeEventSequence) return;
354
+ await publishExactCommittedEvent(deps, {
355
+ workspaceId,
356
+ sessionId: result.sessionId,
357
+ subjectId: authorization.grant.subjectId,
358
+ eventId: result.runtimeEventId,
359
+ eventSequence: result.runtimeEventSequence,
360
+ });
361
+ };
292
362
 
293
363
  // A committed response is owned by the exact actor/key/request tuple and no
294
364
  // longer depends on mutable source visibility. Resolve it before source/host
@@ -296,7 +366,13 @@ export async function forkManagedHumanSession(
296
366
  // owner makes a formerly shared session private.
297
367
  const applied = await recoverAppliedReceipt();
298
368
  if (applied) {
299
- return await projectAndPublishSessionFork(deps, authorization.grant.subjectId, applied);
369
+ const response = await projectAndPublishSessionFork(
370
+ deps,
371
+ authorization.grant.subjectId,
372
+ applied,
373
+ );
374
+ await publishRuntimeConfiguration(applied);
375
+ return response;
300
376
  }
301
377
 
302
378
  try {
@@ -311,11 +387,28 @@ export async function forkManagedHumanSession(
311
387
  // exact applied receipt can convert the authorization failure into replay.
312
388
  const racedApplied = await recoverAppliedReceipt();
313
389
  if (racedApplied) {
314
- return await projectAndPublishSessionFork(deps, authorization.grant.subjectId, racedApplied);
390
+ const response = await projectAndPublishSessionFork(
391
+ deps,
392
+ authorization.grant.subjectId,
393
+ racedApplied,
394
+ );
395
+ await publishRuntimeConfiguration(racedApplied);
396
+ return response;
315
397
  }
316
398
  throw authorizationError;
317
399
  }
318
400
 
319
- const result = await runTenancyMutation(async () => await forkSessionContent(deps.db, forkInput));
320
- return await projectAndPublishSessionFork(deps, authorization.grant.subjectId, result);
401
+ // Validate all requested runtime resources before committing the independent
402
+ // fork so ordinary authorization failures cannot leave an unusable copy.
403
+ const runtimeConfiguration = runtimeSetupRequested ? await resolveRuntimeConfiguration() : null;
404
+ const result = await runTenancyMutation(
405
+ async () =>
406
+ await forkSessionContent(deps.db, {
407
+ ...forkInput,
408
+ ...(runtimeConfiguration ? { runtimeConfiguration } : {}),
409
+ }),
410
+ );
411
+ const response = await projectAndPublishSessionFork(deps, authorization.grant.subjectId, result);
412
+ await publishRuntimeConfiguration(result);
413
+ return response;
321
414
  }
@@ -96,6 +96,36 @@ export type DocumentIndexClient = {
96
96
  }) => Promise<Document | void>;
97
97
  };
98
98
 
99
+ export type ManagedEmailMessage = {
100
+ kind: "email_verification" | "password_reset" | "organization_user_setup";
101
+ from: string;
102
+ to: string;
103
+ subject: string;
104
+ text: string;
105
+ html: string;
106
+ idempotencyKey?: string;
107
+ };
108
+
109
+ export type ManagedEmailDeliveryResult =
110
+ | { status: "sent"; providerMessageId: string | null }
111
+ | { status: "failed"; errorClass: string }
112
+ | { status: "outcome_unknown"; errorClass: string };
113
+
114
+ /** Provider-neutral, injectable boundary for every managed-auth email. */
115
+ export type ManagedEmailTransport = {
116
+ /** Effective provider sender; frozen into any idempotent payload digest. */
117
+ readonly sender: string;
118
+ /**
119
+ * Stable non-secret provider/account/policy namespace plus the provider's
120
+ * guaranteed key-retention window. Both are durably fenced before I/O.
121
+ */
122
+ readonly idempotency: {
123
+ readonly scope: string;
124
+ readonly retentionSeconds: number;
125
+ };
126
+ send(message: ManagedEmailMessage): Promise<ManagedEmailDeliveryResult>;
127
+ };
128
+
99
129
  export type AppDependencies = {
100
130
  settings: Settings;
101
131
  db: Database;
@@ -132,6 +162,8 @@ export type AppDependencies = {
132
162
  * App credentials; standalone deployments fall back to @opengeni/github.
133
163
  */
134
164
  githubAppApi?: GitHubAppApiPort;
165
+ /** Optional provider seam for the separately registered OpenGeni Lens App. */
166
+ prReviewGithubAppApi?: GitHubAppApiPort;
135
167
  /**
136
168
  * Optional host-owned connection credential seam. API-side consumers use
137
169
  * the MCP leg for Codemode/Code Mode; worker consumers bind the same port
@@ -145,6 +177,8 @@ export type AppDependencies = {
145
177
  */
146
178
  sessionAuthorization?: SessionAuthorizationPort | null;
147
179
  managedAuth?: ManagedAuth | null;
180
+ /** Injectable managed-email transport; standalone API defaults to Resend or local capture. */
181
+ managedEmailTransport?: ManagedEmailTransport;
148
182
  /** Injectable Codex HTTP transport for deterministic API/provider tests. */
149
183
  codexFetch?: typeof fetch;
150
184
  /** Injectable GitHub transport for deterministic personal-OAuth tests. */
@@ -188,6 +222,7 @@ export type AppDependencies = {
188
222
  export type ObjectStorageDependency = ReturnType<typeof createObjectStorage>;
189
223
 
190
224
  export type ApiRouteDeps = AppDependencies & {
225
+ managedEmailTransport: ManagedEmailTransport;
191
226
  objectStorage: ObjectStorageDependency;
192
227
  githubStateSecret: string;
193
228
  documentIndexer: DocumentIndexClient;
@@ -5,14 +5,7 @@ import {
5
5
  type WorkspaceInsightsResponse,
6
6
  } from "@opengeni/contracts";
7
7
  import {
8
- aggregateModelCallFacts,
9
- aggregateModelCallFactsByDay,
10
- aggregateModelCallFactsByHour,
11
- aggregateModelContextContributions,
12
- aggregateRootSessionDrivers,
13
- aggregateScheduleFacts,
14
8
  aggregateSessionDepth,
15
- aggregateWarmSecondsByGroup,
16
9
  countOnlineMachines,
17
10
  countScheduledTaskFires,
18
11
  countSessionsAttachedToGroups,
@@ -20,18 +13,47 @@ import {
20
13
  enumerateUtcHours,
21
14
  listFloorSessions,
22
15
  listLiveWarmLeases,
23
- listModelCallFacets,
24
- listRecentModelCalls,
25
16
  listScheduledTasks,
17
+ readWorkspaceInsightsModelBundle,
18
+ readWorkspaceInsightsUsageBundle,
26
19
  requireWorkspace,
27
- sumUsageQuantity,
28
- sumUsageQuantityByDay,
29
- sumUsageQuantityByHour,
30
- sumUsageQuantityInRange,
31
20
  type Database,
32
21
  } from "@opengeni/db";
33
22
 
34
23
  const MACHINE_HEARTBEAT_FRESH_MS = 120_000;
24
+ export const WORKSPACE_INSIGHTS_PROVIDER_FILTER_MAX_UTF8_BYTES = 256;
25
+ export const WORKSPACE_INSIGHTS_MODEL_FILTER_MAX_UTF8_BYTES = 512;
26
+
27
+ export type WorkspaceInsightsFilterField = "provider" | "model";
28
+
29
+ export class WorkspaceInsightsFilterValidationError extends Error {
30
+ readonly field: WorkspaceInsightsFilterField;
31
+ readonly maxUtf8Bytes: number;
32
+
33
+ constructor(field: WorkspaceInsightsFilterField, maxUtf8Bytes: number) {
34
+ super(`${field} must be at most ${maxUtf8Bytes} UTF-8 bytes`);
35
+ this.name = "WorkspaceInsightsFilterValidationError";
36
+ this.field = field;
37
+ this.maxUtf8Bytes = maxUtf8Bytes;
38
+ }
39
+ }
40
+
41
+ /** Normalize the public filter sentinel and enforce the database authority envelope. */
42
+ export function normalizeWorkspaceInsightsFilter(
43
+ value: string | null | undefined,
44
+ field: WorkspaceInsightsFilterField,
45
+ ): string | null {
46
+ const normalized = value?.trim() || null;
47
+ if (normalized === null || normalized === "all") return null;
48
+ const maxUtf8Bytes =
49
+ field === "provider"
50
+ ? WORKSPACE_INSIGHTS_PROVIDER_FILTER_MAX_UTF8_BYTES
51
+ : WORKSPACE_INSIGHTS_MODEL_FILTER_MAX_UTF8_BYTES;
52
+ if (new TextEncoder().encode(normalized).byteLength > maxUtf8Bytes) {
53
+ throw new WorkspaceInsightsFilterValidationError(field, maxUtf8Bytes);
54
+ }
55
+ return normalized;
56
+ }
35
57
 
36
58
  export type GetWorkspaceInsightsInput = {
37
59
  workspaceId: string;
@@ -174,167 +196,81 @@ function pricingSourceOf(
174
196
  return value === "configured_list_price" || value === "gateway_reported" ? value : null;
175
197
  }
176
198
 
199
+ export function insightsSessionLabel(input: {
200
+ id: string;
201
+ title: string | null;
202
+ depth?: number | null;
203
+ }): string {
204
+ const title = input.title?.trim();
205
+ if (title) return title;
206
+ return `${(input.depth ?? 0) > 0 ? "Agent" : "Session"} ${input.id.slice(0, 8)}`;
207
+ }
208
+
177
209
  export async function getWorkspaceInsights(
178
210
  db: Database,
179
211
  settings: Settings,
180
212
  input: GetWorkspaceInsightsInput,
181
213
  ): Promise<WorkspaceInsightsResponse> {
214
+ const provider = normalizeWorkspaceInsightsFilter(input.provider, "provider");
215
+ const model = normalizeWorkspaceInsightsFilter(input.model, "model");
182
216
  await requireWorkspace(db, input.workspaceId);
183
217
  const now = input.now ?? new Date();
184
218
  const window = resolveRangeWindow(input.range, now);
185
- const provider = input.provider?.trim() || null;
186
- const model = input.model?.trim() || null;
187
219
  const modelFilterActive = Boolean(provider || model);
188
220
  const filter = { provider, model };
189
- const aggregateFactsForSeries =
190
- input.range === "today" ? aggregateModelCallFactsByHour : aggregateModelCallFactsByDay;
191
- const sumUsageForSeries =
192
- input.range === "today" ? sumUsageQuantityByHour : sumUsageQuantityByDay;
193
221
 
194
- const [
195
- workspaceCreditMicros,
196
- priorWorkspaceCreditMicros,
197
- warmSeconds,
198
- priorWarmSeconds,
222
+ const [modelBundle, usageBundle, liveWarm, tasks, depth, floorRows, machinesOnline] =
223
+ await Promise.all([
224
+ readWorkspaceInsightsModelBundle(db, {
225
+ workspaceId: input.workspaceId,
226
+ since: window.since,
227
+ until: window.until,
228
+ priorSince: window.priorSince,
229
+ priorUntil: window.priorUntil,
230
+ granularity: input.range === "today" ? "hour" : "day",
231
+ ...filter,
232
+ }),
233
+ readWorkspaceInsightsUsageBundle(db, {
234
+ workspaceId: input.workspaceId,
235
+ since: window.since,
236
+ until: window.until,
237
+ priorSince: window.priorSince,
238
+ priorUntil: window.priorUntil,
239
+ monthSince: startOfUtcMonth(now),
240
+ granularity: input.range === "today" ? "hour" : "day",
241
+ warmGroupLimit: 24,
242
+ }),
243
+ listLiveWarmLeases(db, input.workspaceId),
244
+ listScheduledTasks(db, input.workspaceId, 100),
245
+ aggregateSessionDepth(db, input.workspaceId),
246
+ listFloorSessions(db, input.workspaceId, 24),
247
+ settings.sandboxSelfhostedEnabled
248
+ ? countOnlineMachines(db, input.workspaceId, MACHINE_HEARTBEAT_FRESH_MS, now)
249
+ : Promise.resolve(0),
250
+ ]);
251
+
252
+ const {
199
253
  modelRows,
200
254
  priorModelRows,
201
- factDays,
202
- warmDays,
203
- costDays,
204
- warmGroups,
205
- liveWarm,
255
+ factBuckets: factDays,
206
256
  rootDrivers,
257
+ priorRootDrivers,
207
258
  scheduleFacts,
208
- tasks,
209
- depth,
210
- floorRows,
211
- machinesOnline,
212
- billableTokensUsed,
213
- agentRunsUsed,
214
259
  facets,
215
260
  recentCalls,
216
261
  promptContributions,
217
- ] = await Promise.all([
218
- sumUsageQuantityInRange(db, {
219
- workspaceId: input.workspaceId,
220
- eventType: "model.cost",
221
- since: window.since,
222
- until: window.until,
223
- }),
224
- sumUsageQuantityInRange(db, {
225
- workspaceId: input.workspaceId,
226
- eventType: "model.cost",
227
- since: window.priorSince,
228
- until: window.priorUntil,
229
- }),
230
- sumUsageQuantityInRange(db, {
231
- workspaceId: input.workspaceId,
232
- eventType: "sandbox.warm_seconds",
233
- since: window.since,
234
- until: window.until,
235
- }),
236
- sumUsageQuantityInRange(db, {
237
- workspaceId: input.workspaceId,
238
- eventType: "sandbox.warm_seconds",
239
- since: window.priorSince,
240
- until: window.priorUntil,
241
- }),
242
- aggregateModelCallFacts(db, {
243
- workspaceId: input.workspaceId,
244
- since: window.since,
245
- until: window.until,
246
- ...filter,
247
- }),
248
- aggregateModelCallFacts(db, {
249
- workspaceId: input.workspaceId,
250
- since: window.priorSince,
251
- until: window.priorUntil,
252
- ...filter,
253
- }),
254
- aggregateFactsForSeries(db, {
255
- workspaceId: input.workspaceId,
256
- since: window.since,
257
- until: window.until,
258
- ...filter,
259
- }),
260
- sumUsageForSeries(db, {
261
- workspaceId: input.workspaceId,
262
- eventType: "sandbox.warm_seconds",
263
- since: window.since,
264
- until: window.until,
265
- }),
266
- sumUsageForSeries(db, {
267
- workspaceId: input.workspaceId,
268
- eventType: "model.cost",
269
- since: window.since,
270
- until: window.until,
271
- }),
272
- aggregateWarmSecondsByGroup(db, {
273
- workspaceId: input.workspaceId,
274
- since: window.since,
275
- until: window.until,
276
- limit: 24,
277
- }),
278
- listLiveWarmLeases(db, input.workspaceId),
279
- aggregateRootSessionDrivers(db, {
280
- workspaceId: input.workspaceId,
281
- since: window.since,
282
- until: window.until,
283
- ...filter,
284
- limit: 8,
285
- }),
286
- aggregateScheduleFacts(db, {
287
- workspaceId: input.workspaceId,
288
- since: window.since,
289
- until: window.until,
290
- ...filter,
291
- }),
292
- listScheduledTasks(db, input.workspaceId, 100),
293
- aggregateSessionDepth(db, input.workspaceId),
294
- listFloorSessions(db, input.workspaceId, 24),
295
- settings.sandboxSelfhostedEnabled
296
- ? countOnlineMachines(db, input.workspaceId, MACHINE_HEARTBEAT_FRESH_MS, now)
297
- : Promise.resolve(0),
298
- sumUsageQuantity(db, {
299
- workspaceId: input.workspaceId,
300
- eventType: "model.tokens",
301
- since: startOfUtcMonth(now),
302
- }),
303
- sumUsageQuantity(db, {
304
- workspaceId: input.workspaceId,
305
- eventType: "agent_run.created",
306
- since: startOfUtcMonth(now),
307
- }),
308
- listModelCallFacets(db, {
309
- workspaceId: input.workspaceId,
310
- since: window.since,
311
- until: window.until,
312
- }),
313
- listRecentModelCalls(db, {
314
- workspaceId: input.workspaceId,
315
- since: window.since,
316
- until: window.until,
317
- ...filter,
318
- limit: 50,
319
- }),
320
- aggregateModelContextContributions(db, {
321
- workspaceId: input.workspaceId,
322
- since: window.since,
323
- until: window.until,
324
- ...filter,
325
- }),
326
- ]);
327
-
328
- const [priorRootDrivers, attached, fireCounts] = await Promise.all([
329
- // Exact prior costs for the current top drivers — never a separate top-N page that
330
- // drops roots and invents +$full as "new" spend.
331
- aggregateRootSessionDrivers(db, {
332
- workspaceId: input.workspaceId,
333
- since: window.priorSince,
334
- until: window.priorUntil,
335
- ...filter,
336
- rootSessionIds: rootDrivers.map((row) => row.rootSessionId),
337
- }),
262
+ } = modelBundle;
263
+ const {
264
+ workspaceCreditMicros,
265
+ priorWorkspaceCreditMicros,
266
+ warmSeconds,
267
+ priorWarmSeconds,
268
+ buckets: usageBuckets,
269
+ warmGroups,
270
+ billableTokensUsed,
271
+ agentRunsUsed,
272
+ } = usageBundle;
273
+ const [attached, fireCounts] = await Promise.all([
338
274
  countSessionsAttachedToGroups(
339
275
  db,
340
276
  input.workspaceId,
@@ -419,13 +355,13 @@ export async function getWorkspaceInsights(
419
355
  };
420
356
  const modelCostMicros = modelFilterActive
421
357
  ? facts.costMicros
422
- : (costDays.get(bucket) ?? facts.costMicros);
358
+ : (usageBuckets.get(bucket)?.costMicros ?? facts.costMicros);
423
359
  return {
424
360
  label: input.range === "today" ? bucket.slice(11) : bucket.slice(5),
425
361
  modelCostUsd: microsToUsd(modelCostMicros),
426
362
  estimatedProviderUsd: microsToUsd(facts.estimatedProviderCostMicros),
427
363
  estimatedProviderCostKnownCalls: facts.estimatedProviderCostKnownCalls,
428
- warmSeconds: warmDays.get(bucket) ?? 0,
364
+ warmSeconds: usageBuckets.get(bucket)?.warmSeconds ?? 0,
429
365
  inputTokens: facts.inputTokens,
430
366
  outputTokens: facts.outputTokens,
431
367
  cachedTokens: facts.cachedTokens,
@@ -453,7 +389,7 @@ export async function getWorkspaceInsights(
453
389
  return {
454
390
  id: `root:${row.rootSessionId}`,
455
391
  groupBy: "root_session" as const,
456
- label: row.title?.trim() || row.rootSessionId.slice(0, 8),
392
+ label: insightsSessionLabel({ id: row.rootSessionId, title: row.title, depth: 0 }),
457
393
  creditUsd,
458
394
  estimatedProviderUsd: microsToUsd(row.estimatedProviderCostMicros),
459
395
  estimatedProviderCostKnownCalls: row.estimatedProviderCostKnownCalls,
@@ -494,7 +430,11 @@ export async function getWorkspaceInsights(
494
430
  })
495
431
  .map((row) => ({
496
432
  id: row.id,
497
- title: row.title?.trim() || "Untitled session",
433
+ title: insightsSessionLabel({
434
+ id: row.id,
435
+ title: row.title,
436
+ depth: row.nestedAgentDepth,
437
+ }),
498
438
  state: floorState(row),
499
439
  depth: row.nestedAgentDepth,
500
440
  model: row.model,
@@ -528,7 +468,11 @@ export async function getWorkspaceInsights(
528
468
  occurredAt: row.occurredAt.toISOString(),
529
469
  recordedAt: row.recordedAt.toISOString(),
530
470
  sessionId: row.sessionId,
531
- sessionTitle: row.sessionTitle?.trim() || "Untitled session",
471
+ sessionTitle: insightsSessionLabel({
472
+ id: row.sessionId,
473
+ title: row.sessionTitle,
474
+ depth: row.sessionDepth,
475
+ }),
532
476
  turnId: row.turnId,
533
477
  provider: row.provider,
534
478
  providerApi: row.providerApi,
@@ -588,7 +532,13 @@ export async function getWorkspaceInsights(
588
532
  sessionsTouched: depth.sessionsTouched,
589
533
  rootSessions: depth.rootSessions,
590
534
  deepestDepth: depth.deepestDepth,
591
- deepestSessionTitle: depth.deepestSessionTitle || "",
535
+ deepestSessionTitle: depth.deepestSessionId
536
+ ? insightsSessionLabel({
537
+ id: depth.deepestSessionId,
538
+ title: depth.deepestSessionTitle,
539
+ depth: depth.deepestDepth,
540
+ })
541
+ : "",
592
542
  avgDepth: Math.round(depth.avgDepth * 10) / 10,
593
543
  warmIdleNow: liveWarm.filter((lease) => lease.turnHolders === 0).length,
594
544
  billableTokensUsed,
@@ -4,6 +4,7 @@ export function organizationMembershipHttpStatus(
4
4
  if (sqlState === "22023" || sqlState === "23514") return 400;
5
5
  if (sqlState === "42501") return 403;
6
6
  if (sqlState === "P0002") return 404;
7
- if (sqlState === "23505" || sqlState === "40001" || sqlState === "55000") return 409;
7
+ if (sqlState === "23505" || sqlState === "40001" || sqlState === "55000" || sqlState === "55P03")
8
+ return 409;
8
9
  return null;
9
10
  }