@opengeni/core 0.28.1 → 1.0.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.
Files changed (224) hide show
  1. package/README.md +1 -1
  2. package/dist/access/external-actor-authority.d.ts +47 -0
  3. package/dist/access/index.d.ts +182 -3
  4. package/dist/access/service-initiator.d.ts +3 -0
  5. package/dist/application/api-integration-servers.d.ts +20 -0
  6. package/dist/application/archived-session-imports.d.ts +6 -0
  7. package/dist/application/composer-submit.d.ts +24 -0
  8. package/dist/application/connect-authority.d.ts +13 -0
  9. package/dist/application/connect-operation.d.ts +31 -0
  10. package/dist/application/external-continuation.d.ts +15 -0
  11. package/dist/application/external-identity-lifecycle.d.ts +20 -0
  12. package/dist/application/external-link-work-admission.d.ts +12 -0
  13. package/dist/application/external-workspace-members.d.ts +34 -0
  14. package/dist/application/modal-native-worker-host-transport.d.ts +297 -0
  15. package/dist/application/new-session-drafts.d.ts +39 -2
  16. package/dist/application/sandbox-recovery.d.ts +58 -0
  17. package/dist/application/session-commands.d.ts +26 -13
  18. package/dist/application/session-mcp-credential-rotation.d.ts +29 -0
  19. package/dist/application/session-tenancy.d.ts +19 -0
  20. package/dist/application/user-resource-grants.d.ts +20 -0
  21. package/dist/billing/limits.d.ts +11 -0
  22. package/dist/canonical-human-identities.d.ts +7 -2
  23. package/dist/canonical-human-identities.js +17 -4
  24. package/dist/canonical-human-identities.js.map +1 -1
  25. package/dist/{chunk-6WKPWE5S.js → chunk-2MKMRGSY.js} +108 -24
  26. package/dist/chunk-2MKMRGSY.js.map +1 -0
  27. package/dist/{chunk-NYPMQ7NO.js → chunk-GVO3N37G.js} +205 -37
  28. package/dist/chunk-GVO3N37G.js.map +1 -0
  29. package/dist/{chunk-JJU6I5XD.js → chunk-LCQXNR62.js} +36 -10
  30. package/dist/chunk-LCQXNR62.js.map +1 -0
  31. package/dist/chunk-MM6N7CTO.js +387 -0
  32. package/dist/chunk-MM6N7CTO.js.map +1 -0
  33. package/dist/chunk-QO5GVFFO.js +235 -0
  34. package/dist/chunk-QO5GVFFO.js.map +1 -0
  35. package/dist/codex-model-availability.d.ts +14 -0
  36. package/dist/default-session-model.d.ts +139 -0
  37. package/dist/dependencies.d.ts +87 -4
  38. package/dist/developer-setup-credential-ceiling.d.ts +10 -0
  39. package/dist/domain/agent-config-resolution.d.ts +49 -0
  40. package/dist/domain/automations.d.ts +58 -0
  41. package/dist/domain/capabilities.d.ts +22 -4
  42. package/dist/domain/company-profile-agent-admin.d.ts +25 -0
  43. package/dist/domain/connection-account-selection-error.d.ts +8 -0
  44. package/dist/domain/connector-tool-permissions.d.ts +36 -0
  45. package/dist/domain/direct-model-provider.d.ts +3 -0
  46. package/dist/domain/editable-artifacts/agent-application.d.ts +10 -1
  47. package/dist/domain/editable-artifacts/durable-export.d.ts +22 -1
  48. package/dist/domain/editable-artifacts/ports.d.ts +10 -0
  49. package/dist/domain/editable-artifacts/service.d.ts +4 -3
  50. package/dist/domain/environments.d.ts +5 -9
  51. package/dist/domain/external-creation-attribution.d.ts +6 -0
  52. package/dist/domain/file-owner.d.ts +8 -0
  53. package/dist/domain/github-action-policies.d.ts +36 -0
  54. package/dist/domain/github-repository-bindings.d.ts +117 -0
  55. package/dist/domain/github-skill-source.d.ts +5 -0
  56. package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
  57. package/dist/domain/insights-timing.d.ts +16 -0
  58. package/dist/domain/insights-usage.d.ts +27 -0
  59. package/dist/domain/insights.d.ts +28 -1
  60. package/dist/domain/knowledge-files.d.ts +20 -0
  61. package/dist/domain/knowledge-messages.d.ts +39 -0
  62. package/dist/domain/knowledge-messages.test.d.ts +1 -0
  63. package/dist/domain/knowledge-preparation.d.ts +10 -0
  64. package/dist/domain/knowledge-preparation.test.d.ts +1 -0
  65. package/dist/domain/knowledge-search.d.ts +15 -0
  66. package/dist/domain/knowledge-search.test.d.ts +1 -0
  67. package/dist/domain/knowledge.d.ts +9 -0
  68. package/dist/domain/mcp-account-bindings.d.ts +19 -0
  69. package/dist/domain/mcp-account-routes.d.ts +21 -0
  70. package/dist/domain/native-mcp-connection-admission.d.ts +3 -0
  71. package/dist/domain/organization-integration-catalog.d.ts +15 -0
  72. package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
  73. package/dist/domain/personal-connection-delegations.d.ts +87 -4
  74. package/dist/domain/pr-review.d.ts +249 -0
  75. package/dist/domain/resources.d.ts +24 -3
  76. package/dist/domain/scheduled-task-access.d.ts +247 -0
  77. package/dist/domain/scheduled-tasks.d.ts +103 -6
  78. package/dist/domain/session-connection-accounts.d.ts +16 -0
  79. package/dist/domain/session-file-access.d.ts +4 -0
  80. package/dist/domain/session-native-connection-replacement.d.ts +14 -0
  81. package/dist/domain/session-start-timing.d.ts +7 -0
  82. package/dist/domain/session-tool-policy.d.ts +93 -4
  83. package/dist/domain/sessions.d.ts +275 -28
  84. package/dist/domain/skill-imports.d.ts +1 -0
  85. package/dist/domain/skill-search.d.ts +30 -0
  86. package/dist/domain/skills.d.ts +117 -0
  87. package/dist/domain/slack-bot.d.ts +46 -1
  88. package/dist/domain/workspace-members.d.ts +8 -1
  89. package/dist/editable-artifact-live/application.d.ts +12 -3
  90. package/dist/editable-artifact-live/ports.d.ts +0 -1
  91. package/dist/editable-artifact-live/server.d.ts +5 -2
  92. package/dist/editable-artifact-live/ticket.d.ts +2 -2
  93. package/dist/editable-artifact-live/types.d.ts +14 -2
  94. package/dist/editable-artifact-live.js +2 -2
  95. package/dist/editable-artifacts.js +10 -4
  96. package/dist/index.d.ts +49 -3
  97. package/dist/index.js +18812 -6833
  98. package/dist/index.js.map +1 -1
  99. package/dist/managed-auth-session-sets.d.ts +131 -0
  100. package/dist/managed-auth-session-sets.js +51 -0
  101. package/dist/managed-auth-session-sets.js.map +1 -0
  102. package/dist/managed-session.d.ts +98 -11
  103. package/dist/model-catalog.d.ts +117 -0
  104. package/dist/product-usage-metrics.d.ts +30 -0
  105. package/dist/rigs/index.d.ts +22 -5
  106. package/dist/rigs/provider-images.d.ts +1 -1
  107. package/dist/sandbox/fleet.d.ts +59 -10
  108. package/dist/sandbox/routing.d.ts +22 -2
  109. package/dist/sandbox/runtime-settings.d.ts +56 -0
  110. package/dist/session-authorization.d.ts +38 -1
  111. package/dist/site-session-origin.d.ts +9 -0
  112. package/dist/transcription.d.ts +12 -1
  113. package/dist/turn-surface.d.ts +18 -0
  114. package/dist/user-presence.d.ts +32 -0
  115. package/package.json +18 -11
  116. package/src/access/external-actor-authority.ts +94 -0
  117. package/src/access/index.ts +1386 -44
  118. package/src/access/service-initiator.ts +50 -0
  119. package/src/application/api-integration-servers.ts +215 -0
  120. package/src/application/archived-session-imports.ts +161 -0
  121. package/src/application/composer-submit.ts +77 -0
  122. package/src/application/connect-authority.ts +77 -0
  123. package/src/application/connect-operation.ts +80 -0
  124. package/src/application/external-continuation.ts +129 -0
  125. package/src/application/external-identity-lifecycle.ts +48 -0
  126. package/src/application/external-link-work-admission.ts +90 -0
  127. package/src/application/external-workspace-members.ts +232 -0
  128. package/src/application/modal-native-worker-host-transport.ts +263 -0
  129. package/src/application/new-session-drafts.ts +222 -29
  130. package/src/application/sandbox-recovery.ts +78 -0
  131. package/src/application/session-commands.ts +430 -73
  132. package/src/application/session-mcp-credential-rotation.ts +217 -0
  133. package/src/application/session-tenancy.ts +445 -0
  134. package/src/application/user-resource-grants.ts +154 -0
  135. package/src/billing/limits.ts +103 -24
  136. package/src/canonical-human-identities.ts +22 -3
  137. package/src/codex-model-availability.ts +136 -0
  138. package/src/default-session-model.ts +656 -0
  139. package/src/dependencies.ts +97 -5
  140. package/src/developer-setup-credential-ceiling.ts +60 -0
  141. package/src/domain/agent-config-resolution.ts +148 -0
  142. package/src/domain/automations.ts +270 -0
  143. package/src/domain/capabilities.ts +244 -151
  144. package/src/domain/company-profile-agent-admin.ts +56 -0
  145. package/src/domain/connection-account-selection-error.ts +19 -0
  146. package/src/domain/connector-tool-permissions.ts +371 -0
  147. package/src/domain/direct-model-provider.ts +62 -0
  148. package/src/domain/editable-artifacts/agent-application.ts +61 -15
  149. package/src/domain/editable-artifacts/durable-export.ts +100 -5
  150. package/src/domain/editable-artifacts/in-memory.ts +6 -0
  151. package/src/domain/editable-artifacts/ports.ts +11 -0
  152. package/src/domain/editable-artifacts/service.ts +32 -1
  153. package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
  154. package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
  155. package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
  156. package/src/domain/environments.ts +25 -45
  157. package/src/domain/external-creation-attribution.ts +22 -0
  158. package/src/domain/file-owner.ts +67 -0
  159. package/src/domain/github-action-policies.ts +169 -0
  160. package/src/domain/github-repository-bindings.ts +295 -0
  161. package/src/domain/github-skill-source.ts +233 -0
  162. package/src/domain/governed-learning-slack-publication.ts +157 -0
  163. package/src/domain/insights-timing.ts +95 -0
  164. package/src/domain/insights-usage.ts +67 -0
  165. package/src/domain/insights.ts +270 -169
  166. package/src/domain/knowledge-files.ts +46 -0
  167. package/src/domain/knowledge-messages.test.ts +91 -0
  168. package/src/domain/knowledge-messages.ts +106 -0
  169. package/src/domain/knowledge-preparation.test.ts +276 -0
  170. package/src/domain/knowledge-preparation.ts +147 -0
  171. package/src/domain/knowledge-search.test.ts +561 -0
  172. package/src/domain/knowledge-search.ts +256 -0
  173. package/src/domain/knowledge.ts +133 -0
  174. package/src/domain/mcp-account-bindings.ts +171 -0
  175. package/src/domain/mcp-account-routes.ts +121 -0
  176. package/src/domain/native-mcp-connection-admission.ts +14 -0
  177. package/src/domain/organization-integration-catalog.ts +88 -0
  178. package/src/domain/organization-membership-lifecycle.ts +10 -0
  179. package/src/domain/personal-connection-delegations.ts +758 -115
  180. package/src/domain/pr-review.ts +687 -0
  181. package/src/domain/resources.ts +182 -25
  182. package/src/domain/scheduled-task-access.ts +1052 -0
  183. package/src/domain/scheduled-tasks.ts +1952 -215
  184. package/src/domain/session-connection-accounts.ts +67 -0
  185. package/src/domain/session-file-access.ts +47 -0
  186. package/src/domain/session-native-connection-replacement.ts +64 -0
  187. package/src/domain/session-start-timing.ts +55 -0
  188. package/src/domain/session-tool-policy.ts +443 -19
  189. package/src/domain/sessions.ts +3417 -619
  190. package/src/domain/skill-imports.ts +137 -17
  191. package/src/domain/skill-search.ts +221 -0
  192. package/src/domain/skills.ts +104 -0
  193. package/src/domain/slack-bot.ts +128 -3
  194. package/src/domain/workspace-members.ts +34 -2
  195. package/src/editable-artifact-live/application.ts +29 -4
  196. package/src/editable-artifact-live/ports.ts +0 -1
  197. package/src/editable-artifact-live/server.ts +167 -13
  198. package/src/editable-artifact-live/ticket.ts +87 -6
  199. package/src/editable-artifact-live/types.ts +16 -2
  200. package/src/editable-artifact-live/wire.ts +0 -10
  201. package/src/index.ts +69 -4
  202. package/src/managed-auth-session-sets.ts +371 -0
  203. package/src/managed-session.ts +520 -2
  204. package/src/model-catalog.ts +873 -0
  205. package/src/product-usage-metrics.ts +101 -0
  206. package/src/rigs/index.ts +179 -63
  207. package/src/rigs/provider-images.ts +2 -3
  208. package/src/sandbox/fleet.ts +471 -59
  209. package/src/sandbox/routing.ts +160 -33
  210. package/src/sandbox/runtime-settings.ts +281 -0
  211. package/src/session-authorization.ts +262 -78
  212. package/src/site-session-origin.ts +22 -0
  213. package/src/transcription.ts +13 -1
  214. package/src/turn-surface.ts +45 -0
  215. package/src/user-presence.ts +134 -0
  216. package/dist/chunk-6WKPWE5S.js.map +0 -1
  217. package/dist/chunk-IBOEYG6N.js +0 -34
  218. package/dist/chunk-IBOEYG6N.js.map +0 -1
  219. package/dist/chunk-JJU6I5XD.js.map +0 -1
  220. package/dist/chunk-NYPMQ7NO.js.map +0 -1
  221. package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
  222. package/dist/domain/packs.d.ts +0 -55
  223. package/src/domain/durable-learning-slack-publication.ts +0 -202
  224. package/src/domain/packs.ts +0 -556
@@ -5,38 +5,82 @@ import {
5
5
  type WorkspaceInsightsResponse,
6
6
  } from "@opengeni/contracts";
7
7
  import {
8
- aggregateModelCallFacts,
9
- aggregateModelCallFactsByDay,
10
- aggregateModelCallFactsByHour,
11
- aggregateRootSessionDrivers,
12
- aggregateScheduleFacts,
13
8
  aggregateSessionDepth,
14
- aggregateWarmSecondsByGroup,
15
9
  countOnlineMachines,
16
10
  countScheduledTaskFires,
17
11
  countSessionsAttachedToGroups,
18
12
  enumerateUtcDays,
19
13
  enumerateUtcHours,
14
+ getSessionAccessProjection,
20
15
  listFloorSessions,
21
16
  listLiveWarmLeases,
22
- listModelCallFacets,
23
- listRecentModelCalls,
24
17
  listScheduledTasks,
18
+ readWorkspaceInsightsModelBundle,
19
+ readWorkspaceInsightsUsageBundle,
25
20
  requireWorkspace,
26
- sumUsageQuantity,
27
- sumUsageQuantityByDay,
28
- sumUsageQuantityByHour,
29
- sumUsageQuantityInRange,
30
21
  type Database,
31
22
  } from "@opengeni/db";
32
23
 
24
+ import { measureInsightsPhase, type InsightsPhaseObserver } from "./insights-timing";
25
+ export { measureInsightsPhase, workspaceInsightsPhaseMetricObserver } from "./insights-timing";
26
+ export type { InsightsPhaseObservation, InsightsPhaseObserver } from "./insights-timing";
33
27
  const MACHINE_HEARTBEAT_FRESH_MS = 120_000;
28
+ export const WORKSPACE_INSIGHTS_PROVIDER_FILTER_MAX_UTF8_BYTES = 256;
29
+ export const WORKSPACE_INSIGHTS_MODEL_FILTER_MAX_UTF8_BYTES = 512;
30
+
31
+ export type WorkspaceInsightsFilterField = "provider" | "model" | "rootSessionId" | "sessionId";
32
+
33
+ export class WorkspaceInsightsFilterValidationError extends Error {
34
+ readonly field: WorkspaceInsightsFilterField;
35
+ readonly maxUtf8Bytes: number;
36
+
37
+ constructor(field: WorkspaceInsightsFilterField, maxUtf8Bytes: number, message?: string) {
38
+ super(message ?? `${field} must be at most ${maxUtf8Bytes} UTF-8 bytes`);
39
+ this.name = "WorkspaceInsightsFilterValidationError";
40
+ this.field = field;
41
+ this.maxUtf8Bytes = maxUtf8Bytes;
42
+ }
43
+ }
44
+
45
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
46
+
47
+ /** Normalize a session drilldown scope; only exact UUIDs are accepted. */
48
+ export function normalizeWorkspaceInsightsSessionScope(
49
+ value: string | null | undefined,
50
+ field: "rootSessionId" | "sessionId",
51
+ ): string | null {
52
+ const normalized = value?.trim() || null;
53
+ if (normalized === null || normalized === "all") return null;
54
+ if (!UUID_PATTERN.test(normalized)) {
55
+ throw new WorkspaceInsightsFilterValidationError(field, 36, `${field} must be a UUID`);
56
+ }
57
+ return normalized.toLowerCase();
58
+ }
59
+
60
+ /** Normalize the public filter sentinel and enforce the database authority envelope. */
61
+ export function normalizeWorkspaceInsightsFilter(
62
+ value: string | null | undefined,
63
+ field: WorkspaceInsightsFilterField,
64
+ ): string | null {
65
+ const normalized = value?.trim() || null;
66
+ if (normalized === null || normalized === "all") return null;
67
+ const maxUtf8Bytes =
68
+ field === "provider"
69
+ ? WORKSPACE_INSIGHTS_PROVIDER_FILTER_MAX_UTF8_BYTES
70
+ : WORKSPACE_INSIGHTS_MODEL_FILTER_MAX_UTF8_BYTES;
71
+ if (new TextEncoder().encode(normalized).byteLength > maxUtf8Bytes) {
72
+ throw new WorkspaceInsightsFilterValidationError(field, maxUtf8Bytes);
73
+ }
74
+ return normalized;
75
+ }
34
76
 
35
77
  export type GetWorkspaceInsightsInput = {
36
78
  workspaceId: string;
37
79
  range: InsightsRange;
38
80
  provider?: string | null;
39
81
  model?: string | null;
82
+ rootSessionId?: string | null;
83
+ sessionId?: string | null;
40
84
  now?: Date;
41
85
  };
42
86
 
@@ -173,171 +217,150 @@ function pricingSourceOf(
173
217
  return value === "configured_list_price" || value === "gateway_reported" ? value : null;
174
218
  }
175
219
 
220
+ export function insightsSessionLabel(input: {
221
+ id: string;
222
+ title: string | null;
223
+ depth?: number | null;
224
+ }): string {
225
+ const title = input.title?.trim();
226
+ if (title) return title;
227
+ return `${(input.depth ?? 0) > 0 ? "Agent" : "Session"} ${input.id.slice(0, 8)}`;
228
+ }
229
+
230
+ export function insightsProjectLabel(input: {
231
+ kind: "project" | "other" | "unfiled" | "unavailable" | "deleted";
232
+ name: string | null;
233
+ projects: number;
234
+ }): string {
235
+ switch (input.kind) {
236
+ case "project":
237
+ return input.name?.trim() || "Untitled project";
238
+ case "other":
239
+ return `${input.projects.toLocaleString("en-US")} other ${input.projects === 1 ? "project" : "projects"}`;
240
+ case "unfiled":
241
+ return "No project";
242
+ case "unavailable":
243
+ return "Root session not visible";
244
+ case "deleted":
245
+ return "Deleted chats";
246
+ }
247
+ }
248
+
176
249
  export async function getWorkspaceInsights(
177
250
  db: Database,
178
251
  settings: Settings,
179
252
  input: GetWorkspaceInsightsInput,
253
+ observePhase?: InsightsPhaseObserver,
180
254
  ): Promise<WorkspaceInsightsResponse> {
181
- await requireWorkspace(db, input.workspaceId);
255
+ const provider = normalizeWorkspaceInsightsFilter(input.provider, "provider");
256
+ const model = normalizeWorkspaceInsightsFilter(input.model, "model");
257
+ const rootSessionId = normalizeWorkspaceInsightsSessionScope(
258
+ input.rootSessionId,
259
+ "rootSessionId",
260
+ );
261
+ const sessionId = normalizeWorkspaceInsightsSessionScope(input.sessionId, "sessionId");
262
+ await measureInsightsPhase(observePhase, "require_workspace", () =>
263
+ requireWorkspace(db, input.workspaceId),
264
+ );
265
+ const [visibleRootScope, visibleSessionScope] = await Promise.all([
266
+ rootSessionId ? getSessionAccessProjection(db, input.workspaceId, rootSessionId) : null,
267
+ sessionId ? getSessionAccessProjection(db, input.workspaceId, sessionId) : null,
268
+ ]);
182
269
  const now = input.now ?? new Date();
183
270
  const window = resolveRangeWindow(input.range, now);
184
- const provider = input.provider?.trim() || null;
185
- const model = input.model?.trim() || null;
186
271
  const modelFilterActive = Boolean(provider || model);
187
- const filter = { provider, model };
188
- const aggregateFactsForSeries =
189
- input.range === "today" ? aggregateModelCallFactsByHour : aggregateModelCallFactsByDay;
190
- const sumUsageForSeries =
191
- input.range === "today" ? sumUsageQuantityByHour : sumUsageQuantityByDay;
272
+ const scopeActive = Boolean(rootSessionId || sessionId);
273
+ const filter = { provider, model, rootSessionId, sessionId };
274
+
275
+ const [modelBundle, usageBundle, liveWarm, tasks, depth, floorRows, machinesOnline] =
276
+ await Promise.all([
277
+ measureInsightsPhase(observePhase, "model_bundle", () =>
278
+ readWorkspaceInsightsModelBundle(db, {
279
+ workspaceId: input.workspaceId,
280
+ since: window.since,
281
+ until: window.until,
282
+ priorSince: window.priorSince,
283
+ priorUntil: window.priorUntil,
284
+ granularity: input.range === "today" ? "hour" : "day",
285
+ ...filter,
286
+ }),
287
+ ),
288
+ measureInsightsPhase(observePhase, "usage_bundle", () =>
289
+ readWorkspaceInsightsUsageBundle(db, {
290
+ workspaceId: input.workspaceId,
291
+ since: window.since,
292
+ until: window.until,
293
+ priorSince: window.priorSince,
294
+ priorUntil: window.priorUntil,
295
+ monthSince: startOfUtcMonth(now),
296
+ granularity: input.range === "today" ? "hour" : "day",
297
+ warmGroupLimit: 24,
298
+ }),
299
+ ),
300
+ measureInsightsPhase(observePhase, "live_warm", () =>
301
+ listLiveWarmLeases(db, input.workspaceId),
302
+ ),
303
+ measureInsightsPhase(observePhase, "scheduled_tasks", () =>
304
+ listScheduledTasks(db, input.workspaceId, 100),
305
+ ),
306
+ measureInsightsPhase(observePhase, "session_depth", () =>
307
+ aggregateSessionDepth(db, input.workspaceId),
308
+ ),
309
+ measureInsightsPhase(observePhase, "floor_sessions", () =>
310
+ listFloorSessions(db, input.workspaceId, 24),
311
+ ),
312
+ settings.sandboxSelfhostedEnabled
313
+ ? measureInsightsPhase(observePhase, "online_machines", () =>
314
+ countOnlineMachines(db, input.workspaceId, MACHINE_HEARTBEAT_FRESH_MS, now),
315
+ )
316
+ : Promise.resolve(0),
317
+ ]);
192
318
 
193
- const [
319
+ const {
320
+ modelRows,
321
+ priorModelRows,
322
+ factBuckets: factDays,
323
+ rootDrivers,
324
+ priorRootDrivers,
325
+ projects,
326
+ privateChats,
327
+ privateChatsTruncated,
328
+ scheduleFacts,
329
+ facets,
330
+ recentCalls,
331
+ promptContributions,
332
+ dataThrough,
333
+ driverGroups,
334
+ driversTruncated,
335
+ facetsTruncated,
336
+ recentCallsTruncated,
337
+ } = modelBundle;
338
+ const {
194
339
  workspaceCreditMicros,
195
340
  priorWorkspaceCreditMicros,
196
341
  warmSeconds,
197
342
  priorWarmSeconds,
198
- modelRows,
199
- priorModelRows,
200
- factDays,
201
- warmDays,
202
- costDays,
343
+ buckets: usageBuckets,
203
344
  warmGroups,
204
- liveWarm,
205
- rootDrivers,
206
- scheduleFacts,
207
- tasks,
208
- depth,
209
- floorRows,
210
- machinesOnline,
211
345
  billableTokensUsed,
212
346
  agentRunsUsed,
213
- facets,
214
- recentCalls,
215
- ] = await Promise.all([
216
- sumUsageQuantityInRange(db, {
217
- workspaceId: input.workspaceId,
218
- eventType: "model.cost",
219
- since: window.since,
220
- until: window.until,
221
- }),
222
- sumUsageQuantityInRange(db, {
223
- workspaceId: input.workspaceId,
224
- eventType: "model.cost",
225
- since: window.priorSince,
226
- until: window.priorUntil,
227
- }),
228
- sumUsageQuantityInRange(db, {
229
- workspaceId: input.workspaceId,
230
- eventType: "sandbox.warm_seconds",
231
- since: window.since,
232
- until: window.until,
233
- }),
234
- sumUsageQuantityInRange(db, {
235
- workspaceId: input.workspaceId,
236
- eventType: "sandbox.warm_seconds",
237
- since: window.priorSince,
238
- until: window.priorUntil,
239
- }),
240
- aggregateModelCallFacts(db, {
241
- workspaceId: input.workspaceId,
242
- since: window.since,
243
- until: window.until,
244
- ...filter,
245
- }),
246
- aggregateModelCallFacts(db, {
247
- workspaceId: input.workspaceId,
248
- since: window.priorSince,
249
- until: window.priorUntil,
250
- ...filter,
251
- }),
252
- aggregateFactsForSeries(db, {
253
- workspaceId: input.workspaceId,
254
- since: window.since,
255
- until: window.until,
256
- ...filter,
257
- }),
258
- sumUsageForSeries(db, {
259
- workspaceId: input.workspaceId,
260
- eventType: "sandbox.warm_seconds",
261
- since: window.since,
262
- until: window.until,
263
- }),
264
- sumUsageForSeries(db, {
265
- workspaceId: input.workspaceId,
266
- eventType: "model.cost",
267
- since: window.since,
268
- until: window.until,
269
- }),
270
- aggregateWarmSecondsByGroup(db, {
271
- workspaceId: input.workspaceId,
272
- since: window.since,
273
- until: window.until,
274
- limit: 24,
275
- }),
276
- listLiveWarmLeases(db, input.workspaceId),
277
- aggregateRootSessionDrivers(db, {
278
- workspaceId: input.workspaceId,
279
- since: window.since,
280
- until: window.until,
281
- ...filter,
282
- limit: 8,
283
- }),
284
- aggregateScheduleFacts(db, {
285
- workspaceId: input.workspaceId,
286
- since: window.since,
287
- until: window.until,
288
- ...filter,
289
- }),
290
- listScheduledTasks(db, input.workspaceId, 100),
291
- aggregateSessionDepth(db, input.workspaceId),
292
- listFloorSessions(db, input.workspaceId, 24),
293
- settings.sandboxSelfhostedEnabled
294
- ? countOnlineMachines(db, input.workspaceId, MACHINE_HEARTBEAT_FRESH_MS, now)
295
- : Promise.resolve(0),
296
- sumUsageQuantity(db, {
297
- workspaceId: input.workspaceId,
298
- eventType: "model.tokens",
299
- since: startOfUtcMonth(now),
300
- }),
301
- sumUsageQuantity(db, {
302
- workspaceId: input.workspaceId,
303
- eventType: "agent_run.created",
304
- since: startOfUtcMonth(now),
305
- }),
306
- listModelCallFacets(db, {
307
- workspaceId: input.workspaceId,
308
- since: window.since,
309
- until: window.until,
310
- }),
311
- listRecentModelCalls(db, {
312
- workspaceId: input.workspaceId,
313
- since: window.since,
314
- until: window.until,
315
- ...filter,
316
- limit: 50,
317
- }),
318
- ]);
319
-
320
- const [priorRootDrivers, attached, fireCounts] = await Promise.all([
321
- // Exact prior costs for the current top drivers — never a separate top-N page that
322
- // drops roots and invents +$full as "new" spend.
323
- aggregateRootSessionDrivers(db, {
324
- workspaceId: input.workspaceId,
325
- since: window.priorSince,
326
- until: window.priorUntil,
327
- ...filter,
328
- rootSessionIds: rootDrivers.map((row) => row.rootSessionId),
329
- }),
330
- countSessionsAttachedToGroups(
331
- db,
332
- input.workspaceId,
333
- warmGroups.map((group) => group.groupId),
347
+ } = usageBundle;
348
+ const [attached, fireCounts] = await Promise.all([
349
+ measureInsightsPhase(observePhase, "attached_sessions", () =>
350
+ countSessionsAttachedToGroups(
351
+ db,
352
+ input.workspaceId,
353
+ warmGroups.map((group) => group.groupId),
354
+ ),
355
+ ),
356
+ measureInsightsPhase(observePhase, "scheduled_fires", () =>
357
+ countScheduledTaskFires(db, {
358
+ workspaceId: input.workspaceId,
359
+ since: window.since,
360
+ until: window.until,
361
+ taskIds: tasks.map((task) => task.id),
362
+ }),
334
363
  ),
335
- countScheduledTaskFires(db, {
336
- workspaceId: input.workspaceId,
337
- since: window.since,
338
- until: window.until,
339
- taskIds: tasks.map((task) => task.id),
340
- }),
341
364
  ]);
342
365
  const backendByGroup = new Map(liveWarm.map((lease) => [lease.groupId, lease.backend]));
343
366
  const warmSecondsByGroup = new Map(warmGroups.map((group) => [group.groupId, group.warmSeconds]));
@@ -361,6 +384,8 @@ export async function getWorkspaceInsights(
361
384
  creditUsd: microsToUsd(row.pricedCostMicros),
362
385
  estimatedProviderUsd: microsToUsd(row.estimatedProviderCostMicros),
363
386
  estimatedProviderCostKnownCalls: row.estimatedProviderCostKnownCalls,
387
+ equivalentCreditUsd: microsToUsd(row.equivalentCreditCostMicros),
388
+ equivalentCreditCostKnownCalls: row.equivalentCreditCostKnownCalls,
364
389
  }))
365
390
  .sort((a, b) => b.totalTokens - a.totalTokens);
366
391
 
@@ -382,7 +407,25 @@ export async function getWorkspaceInsights(
382
407
  (sum, row) => sum + row.estimatedProviderCostKnownCalls,
383
408
  0,
384
409
  );
410
+ const equivalentCreditCostMicros = modelRows.reduce(
411
+ (sum, row) => sum + row.equivalentCreditCostMicros,
412
+ 0,
413
+ );
414
+ const priorEquivalentCreditCostMicros = priorModelRows.reduce(
415
+ (sum, row) => sum + row.equivalentCreditCostMicros,
416
+ 0,
417
+ );
418
+ const equivalentCreditCostKnownCalls = modelRows.reduce(
419
+ (sum, row) => sum + row.equivalentCreditCostKnownCalls,
420
+ 0,
421
+ );
422
+ const priorEquivalentCreditCostKnownCalls = priorModelRows.reduce(
423
+ (sum, row) => sum + row.equivalentCreditCostKnownCalls,
424
+ 0,
425
+ );
385
426
  const modelCalls = modelRows.reduce((sum, row) => sum + row.calls, 0);
427
+ const cachedTokens = modelRows.reduce((sum, row) => sum + row.cachedTokens, 0);
428
+ const cacheInputTokens = modelRows.reduce((sum, row) => sum + row.cacheInputTokens, 0);
386
429
  const priorInputTokens = priorModelRows.reduce((sum, row) => sum + row.inputTokens, 0);
387
430
  const priorTotalTokens = priorModelRows.reduce((sum, row) => sum + row.totalTokens, 0);
388
431
  const priorCachedTokens = priorModelRows.reduce((sum, row) => sum + row.cachedTokens, 0);
@@ -398,6 +441,8 @@ export async function getWorkspaceInsights(
398
441
  costMicros: 0,
399
442
  estimatedProviderCostMicros: 0,
400
443
  estimatedProviderCostKnownCalls: 0,
444
+ equivalentCreditCostMicros: 0,
445
+ equivalentCreditCostKnownCalls: 0,
401
446
  inputTokens: 0,
402
447
  outputTokens: 0,
403
448
  cachedTokens: 0,
@@ -409,15 +454,18 @@ export async function getWorkspaceInsights(
409
454
  cacheKnownCalls: 0,
410
455
  calls: 0,
411
456
  };
412
- const modelCostMicros = modelFilterActive
413
- ? facts.costMicros
414
- : (costDays.get(bucket) ?? facts.costMicros);
457
+ const modelCostMicros =
458
+ modelFilterActive || scopeActive
459
+ ? facts.costMicros
460
+ : (usageBuckets.get(bucket)?.costMicros ?? facts.costMicros);
415
461
  return {
416
462
  label: input.range === "today" ? bucket.slice(11) : bucket.slice(5),
417
463
  modelCostUsd: microsToUsd(modelCostMicros),
418
464
  estimatedProviderUsd: microsToUsd(facts.estimatedProviderCostMicros),
419
465
  estimatedProviderCostKnownCalls: facts.estimatedProviderCostKnownCalls,
420
- warmSeconds: warmDays.get(bucket) ?? 0,
466
+ equivalentCreditUsd: microsToUsd(facts.equivalentCreditCostMicros),
467
+ equivalentCreditCostKnownCalls: facts.equivalentCreditCostKnownCalls,
468
+ warmSeconds: usageBuckets.get(bucket)?.warmSeconds ?? 0,
421
469
  inputTokens: facts.inputTokens,
422
470
  outputTokens: facts.outputTokens,
423
471
  cachedTokens: facts.cachedTokens,
@@ -445,10 +493,12 @@ export async function getWorkspaceInsights(
445
493
  return {
446
494
  id: `root:${row.rootSessionId}`,
447
495
  groupBy: "root_session" as const,
448
- label: row.title?.trim() || row.rootSessionId.slice(0, 8),
496
+ label: insightsSessionLabel({ id: row.rootSessionId, title: row.title, depth: 0 }),
449
497
  creditUsd,
450
498
  estimatedProviderUsd: microsToUsd(row.estimatedProviderCostMicros),
451
499
  estimatedProviderCostKnownCalls: row.estimatedProviderCostKnownCalls,
500
+ equivalentCreditUsd: microsToUsd(row.equivalentCreditCostMicros),
501
+ equivalentCreditCostKnownCalls: row.equivalentCreditCostKnownCalls,
452
502
  tokens: row.totalTokens,
453
503
  cacheHitPct: cacheHitPct(row.cachedTokens, row.cacheInputTokens),
454
504
  pctOfCreditUsd:
@@ -460,6 +510,20 @@ export async function getWorkspaceInsights(
460
510
  };
461
511
  });
462
512
 
513
+ const projectRows = projects.map((row) => ({
514
+ id: row.kind === "project" && row.channelId ? `project:${row.channelId}` : row.kind,
515
+ kind: row.kind,
516
+ label: insightsProjectLabel(row),
517
+ projects: row.projects,
518
+ rootSessions: row.rootSessions,
519
+ calls: row.calls,
520
+ creditUsd: microsToUsd(row.pricedCostMicros),
521
+ estimatedProviderUsd: microsToUsd(row.estimatedProviderCostMicros),
522
+ estimatedProviderCostKnownCalls: row.estimatedProviderCostKnownCalls,
523
+ tokens: row.totalTokens,
524
+ cacheHitPct: cacheHitPct(row.cachedTokens, row.cacheInputTokens),
525
+ }));
526
+
463
527
  const scheduleFactById = new Map(scheduleFacts.map((row) => [row.scheduledTaskId, row] as const));
464
528
  const schedules = tasks.map((task) => {
465
529
  const fact = scheduleFactById.get(task.id);
@@ -470,6 +534,8 @@ export async function getWorkspaceInsights(
470
534
  creditUsd: fact ? microsToUsd(fact.pricedCostMicros) : null,
471
535
  estimatedProviderUsd: fact ? microsToUsd(fact.estimatedProviderCostMicros) : null,
472
536
  estimatedProviderCostKnownCalls: fact ? fact.estimatedProviderCostKnownCalls : null,
537
+ equivalentCreditUsd: fact ? microsToUsd(fact.equivalentCreditCostMicros) : null,
538
+ equivalentCreditCostKnownCalls: fact ? fact.equivalentCreditCostKnownCalls : null,
473
539
  tokens: fact ? fact.totalTokens : null,
474
540
  cacheHitPct: fact ? cacheHitPct(fact.cachedTokens, fact.cacheInputTokens) : null,
475
541
  billing: fact ? billingPathOf(fact.billingPath) : null,
@@ -486,7 +552,11 @@ export async function getWorkspaceInsights(
486
552
  })
487
553
  .map((row) => ({
488
554
  id: row.id,
489
- title: row.title?.trim() || "Untitled session",
555
+ title: insightsSessionLabel({
556
+ id: row.id,
557
+ title: row.title,
558
+ depth: row.nestedAgentDepth,
559
+ }),
490
560
  state: floorState(row),
491
561
  depth: row.nestedAgentDepth,
492
562
  model: row.model,
@@ -514,13 +584,20 @@ export async function getWorkspaceInsights(
514
584
  sessions: bucket.sessions,
515
585
  })),
516
586
  drivers,
587
+ projects: projectRows,
588
+ privateChats,
589
+ privateChatsTruncated,
517
590
  schedules,
518
591
  recentCalls: recentCalls.map((row) => ({
519
592
  id: row.id,
520
593
  occurredAt: row.occurredAt.toISOString(),
521
594
  recordedAt: row.recordedAt.toISOString(),
522
595
  sessionId: row.sessionId,
523
- sessionTitle: row.sessionTitle?.trim() || "Untitled session",
596
+ sessionTitle: insightsSessionLabel({
597
+ id: row.sessionId,
598
+ title: row.sessionTitle,
599
+ depth: row.sessionDepth,
600
+ }),
524
601
  turnId: row.turnId,
525
602
  provider: row.provider,
526
603
  providerApi: row.providerApi,
@@ -537,8 +614,11 @@ export async function getWorkspaceInsights(
537
614
  row.estimatedProviderCostMicros == null
538
615
  ? null
539
616
  : microsToUsd(row.estimatedProviderCostMicros),
617
+ equivalentCreditUsd:
618
+ row.equivalentCreditCostMicros == null ? null : microsToUsd(row.equivalentCreditCostMicros),
540
619
  pricingSource: pricingSourceOf(row.pricingSource),
541
620
  })),
621
+ promptContributions,
542
622
  warmSeconds,
543
623
  priorWarmSeconds,
544
624
  warmGroups: warmGroups.map((group) => ({
@@ -569,24 +649,45 @@ export async function getWorkspaceInsights(
569
649
  priorEstimatedProviderUsd: microsToUsd(priorEstimatedProviderCostMicros),
570
650
  estimatedProviderCostKnownCalls,
571
651
  priorEstimatedProviderCostKnownCalls,
652
+ equivalentCreditUsd: microsToUsd(equivalentCreditCostMicros),
653
+ priorEquivalentCreditUsd: microsToUsd(priorEquivalentCreditCostMicros),
654
+ equivalentCreditCostKnownCalls,
655
+ priorEquivalentCreditCostKnownCalls,
572
656
  modelCalls,
573
657
  priorInputTokens,
574
658
  priorTotalTokens,
575
659
  priorCacheHitPct: cacheHitPct(priorCachedTokens, priorCacheInputTokens),
660
+ priorCacheInputTokens,
576
661
  priorCalls,
577
662
  goalsActive: depth.goalsActive,
578
663
  goalsCompleted: depth.goalsCompleted,
579
664
  sessionsTouched: depth.sessionsTouched,
580
665
  rootSessions: depth.rootSessions,
581
666
  deepestDepth: depth.deepestDepth,
582
- deepestSessionTitle: depth.deepestSessionTitle || "",
667
+ deepestSessionTitle: depth.deepestSessionId
668
+ ? insightsSessionLabel({
669
+ id: depth.deepestSessionId,
670
+ title: depth.deepestSessionTitle,
671
+ depth: depth.deepestDepth,
672
+ })
673
+ : "",
583
674
  avgDepth: Math.round(depth.avgDepth * 10) / 10,
584
675
  warmIdleNow: liveWarm.filter((lease) => lease.turnHolders === 0).length,
585
676
  billableTokensUsed,
586
677
  billableTokenCap: limits.maxMonthlyTokensPerWorkspace ?? null,
587
678
  agentRunsUsed,
588
679
  agentRunCap: limits.maxMonthlyAgentRunsPerWorkspace ?? null,
589
- modelFilterActive,
680
+ modelFilterActive: modelFilterActive || scopeActive,
681
+ dataThrough: dataThrough?.toISOString() ?? null,
682
+ cacheHitPct: cacheHitPct(cachedTokens, cacheInputTokens),
683
+ scope: {
684
+ rootSessionId: visibleRootScope ? rootSessionId : null,
685
+ sessionId: visibleSessionScope ? sessionId : null,
686
+ },
687
+ driverGroups,
688
+ driversTruncated,
689
+ facetsTruncated,
690
+ recentCallsTruncated,
590
691
  });
591
692
 
592
693
  return { snapshot };
@@ -0,0 +1,46 @@
1
+ import { createHash } from "node:crypto";
2
+ import {
3
+ completeKnowledgeFilePreparation,
4
+ inspectKnowledgeFilePreparation,
5
+ type KnowledgeContext,
6
+ } from "@opengeni/db";
7
+ import { retryWhileMissing } from "@opengeni/storage";
8
+ import type { ApiRouteDeps } from "../dependencies";
9
+
10
+ /** Existing original -> one source revision, through the ordinary task policy. */
11
+ export async function prepareKnowledgeFile(
12
+ deps: Pick<ApiRouteDeps, "db" | "objectStorage" | "getDocumentServices">,
13
+ context: KnowledgeContext,
14
+ fileId: string,
15
+ purpose: "evidence" | "reference" = "evidence",
16
+ ) {
17
+ const inspected = await inspectKnowledgeFilePreparation(deps.db, context, fileId);
18
+ if (inspected.status !== "prepare") return inspected;
19
+ if (!deps.objectStorage) throw new Error("File storage is unavailable");
20
+ const object = await retryWhileMissing(() =>
21
+ deps.objectStorage!.getObjectBytes(inspected.file.objectKey),
22
+ );
23
+ if (!object) throw new Error("The original file is unavailable");
24
+ const sourceVersion = createHash("sha256").update(object.bytes).digest("hex");
25
+ if (
26
+ object.bytes.byteLength !== inspected.file.sizeBytes ||
27
+ (inspected.file.sha256 && inspected.file.sha256.toLowerCase() !== sourceVersion)
28
+ ) {
29
+ throw new Error("The original file no longer matches its retained metadata");
30
+ }
31
+ // A screenshot/diagram can support a visual finding without containing text.
32
+ // Keep its exact original; OCR is neither required evidence nor the finding.
33
+ const originalOnly = purpose === "evidence" && inspected.file.contentType.startsWith("image/");
34
+ const parsed = originalOnly
35
+ ? { text: "" }
36
+ : await deps.getDocumentServices().parser.parse(object.bytes, inspected.file);
37
+ if (!originalOnly && !parsed.text.trim())
38
+ throw new Error("No searchable text could be extracted from this file");
39
+ return completeKnowledgeFilePreparation(deps.db, context, {
40
+ fileId,
41
+ title: inspected.file.filename,
42
+ sourceVersion,
43
+ content: parsed.text,
44
+ purpose,
45
+ });
46
+ }