@opengeni/db 0.28.9 → 0.36.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 (225) hide show
  1. package/dist/attached-browser-devices.d.ts +42 -0
  2. package/dist/attempt-tool-catalogs.d.ts +16 -0
  3. package/dist/browser-auth.d.ts +299 -0
  4. package/dist/browser-downloads.d.ts +51 -0
  5. package/dist/browser-identities.d.ts +137 -0
  6. package/dist/browser-sessions.d.ts +354 -0
  7. package/dist/browser-state-artifacts.d.ts +91 -0
  8. package/dist/canonical-human-identities.d.ts +48 -0
  9. package/dist/canonical-human-identities.js +28 -0
  10. package/dist/canonical-human-identities.js.map +1 -0
  11. package/dist/capability-components.d.ts +37 -0
  12. package/dist/capability-integrations.d.ts +194 -0
  13. package/dist/chunk-5KTIEDX7.js +13200 -0
  14. package/dist/chunk-5KTIEDX7.js.map +1 -0
  15. package/dist/chunk-CQZ4QSP5.js +995 -0
  16. package/dist/chunk-CQZ4QSP5.js.map +1 -0
  17. package/dist/chunk-F32YT7MP.js +117 -0
  18. package/dist/chunk-F32YT7MP.js.map +1 -0
  19. package/dist/chunk-JC5QPLUM.js +666 -0
  20. package/dist/chunk-JC5QPLUM.js.map +1 -0
  21. package/dist/chunk-P3RE4D2B.js +3535 -0
  22. package/dist/chunk-P3RE4D2B.js.map +1 -0
  23. package/dist/chunk-TAUTWOS5.js +259 -0
  24. package/dist/chunk-TAUTWOS5.js.map +1 -0
  25. package/dist/chunk-VKLB2ZGI.js +2389 -0
  26. package/dist/chunk-VKLB2ZGI.js.map +1 -0
  27. package/dist/codemode-operations.d.ts +117 -0
  28. package/dist/codex-token-resolver.d.ts +2 -2
  29. package/dist/company-profile-schema.d.ts +798 -0
  30. package/dist/company-profile.d.ts +268 -0
  31. package/dist/computer-sessions.d.ts +109 -0
  32. package/dist/connection-token-resolver.d.ts +9 -4
  33. package/dist/database.d.ts +57 -3
  34. package/dist/editable-artifact-durable-export.d.ts +153 -0
  35. package/dist/editable-artifact-durable-export.js +788 -0
  36. package/dist/editable-artifact-durable-export.js.map +1 -0
  37. package/dist/editable-artifact-materialization.d.ts +123 -0
  38. package/dist/editable-artifacts-schema.d.ts +4152 -0
  39. package/dist/editable-artifacts.d.ts +610 -0
  40. package/dist/editable-artifacts.js +26 -0
  41. package/dist/editable-artifacts.js.map +1 -0
  42. package/dist/generated-images.d.ts +139 -0
  43. package/dist/index.d.ts +1122 -126
  44. package/dist/index.js +42329 -18407
  45. package/dist/index.js.map +1 -1
  46. package/dist/insights.d.ts +73 -10
  47. package/dist/integration-bindings.d.ts +70 -0
  48. package/dist/integration-facets.d.ts +99 -0
  49. package/dist/interaction-revisions.d.ts +14 -0
  50. package/dist/interaction-schema.d.ts +6036 -0
  51. package/dist/knowledge-source-sync-schema.d.ts +1679 -0
  52. package/dist/knowledge-source-sync.d.ts +274 -0
  53. package/dist/memory-governance.d.ts +2 -2
  54. package/dist/memory-slack-delivery.d.ts +101 -0
  55. package/dist/new-session-drafts.d.ts +2 -2
  56. package/dist/pack-components.d.ts +76 -0
  57. package/dist/pack-installations.d.ts +86 -0
  58. package/dist/persistence-errors.d.ts +3 -3
  59. package/dist/plugin-packages.d.ts +126 -0
  60. package/dist/preference-registry.d.ts +1 -1
  61. package/dist/provision-roles.d.ts +9 -1
  62. package/dist/provision-roles.js +1 -1
  63. package/dist/runtime-posture.d.ts +19 -6
  64. package/dist/schema.d.ts +22654 -9359
  65. package/dist/schema.js +209 -3
  66. package/dist/scoped-knowledge.d.ts +97 -1
  67. package/dist/session-control.d.ts +33 -7
  68. package/dist/session-queue-commands.d.ts +19 -11
  69. package/dist/session-realtime-context.d.ts +2 -2
  70. package/dist/session-realtime-ledger.d.ts +4 -4
  71. package/dist/session-realtime-mirror.d.ts +2 -2
  72. package/dist/session-realtime-state.d.ts +1 -1
  73. package/dist/session-realtime-terminal.d.ts +1 -1
  74. package/dist/session-realtime.d.ts +7 -7
  75. package/dist/session-tenancy.d.ts +44 -0
  76. package/dist/session-tenancy.js +16 -0
  77. package/dist/session-tenancy.js.map +1 -0
  78. package/dist/session-tool-call-settlement.d.ts +1 -1
  79. package/dist/slack-task-policy-schema.d.ts +847 -0
  80. package/dist/slack-task-policy.d.ts +65 -0
  81. package/dist/slack-user-link-access.d.ts +54 -0
  82. package/dist/task-notes-schema.d.ts +924 -0
  83. package/dist/task-notes.d.ts +85 -0
  84. package/dist/transcription-recordings.d.ts +2 -2
  85. package/dist/turn-initiator.d.ts +2 -2
  86. package/dist/video-generation.d.ts +225 -0
  87. package/dist/video-generation.js +68 -0
  88. package/dist/video-generation.js.map +1 -0
  89. package/dist/workspace-artifacts.d.ts +1 -1
  90. package/dist/workspace-instruction-policies.d.ts +1 -1
  91. package/dist/workspace-learning-policy-schema.d.ts +932 -0
  92. package/dist/workspace-learning-policy.d.ts +161 -0
  93. package/dist/xai-subscription.d.ts +326 -0
  94. package/drizzle/0183_model_call_provider_cost_estimates.sql +39 -0
  95. package/drizzle/0184_sandbox_drain_teardown_fence.sql +682 -0
  96. package/drizzle/0185_temporal_schedule_cleanup_outbox.sql +168 -0
  97. package/drizzle/0186_sandbox_capture_provider_contract.sql +400 -0
  98. package/drizzle/0187_generated_image_artifacts.sql +173 -0
  99. package/drizzle/0188_image_generation_retention_failure.sql +25 -0
  100. package/drizzle/0189_retained_session_image_formats.sql +13 -0
  101. package/drizzle/0190_timeline_annotations.sql +31 -0
  102. package/drizzle/0191_editable_artifact_engine.sql +3853 -0
  103. package/drizzle/0192_editable_artifact_live_tickets.sql +338 -0
  104. package/drizzle/0193_editable_artifact_authorization.sql +190 -0
  105. package/drizzle/0194_editable_artifact_durable_exports.sql +160 -0
  106. package/drizzle/0195_editable_artifact_import_authorization.sql +203 -0
  107. package/drizzle/0196_rig_provider_images.sql +152 -0
  108. package/drizzle/0197_knowledge_source_sync_schedules.sql +523 -0
  109. package/drizzle/0198_memory_slack_publication_delivery.sql +393 -0
  110. package/drizzle/0199_workspace_learning_policy.sql +980 -0
  111. package/drizzle/0201_company_profile_authority.sql +849 -0
  112. package/drizzle/0202_document_index_checkpoints.sql +85 -0
  113. package/drizzle/0203_durable_video_generation.sql +353 -0
  114. package/drizzle/0204_video_generation_funding.sql +44 -0
  115. package/drizzle/0205_attempt_tool_catalogs.sql +230 -0
  116. package/drizzle/0206_browser_sessions.sql +646 -0
  117. package/drizzle/0207_browser_identities.sql +379 -0
  118. package/drizzle/0208_attached_browser_devices.sql +165 -0
  119. package/drizzle/0209_computer_sessions.sql +575 -0
  120. package/drizzle/0210_browser_auth_network_interventions.sql +441 -0
  121. package/drizzle/0211_editable_artifact_session_links.sql +58 -0
  122. package/drizzle/0212_browser_state_transfer_hardening.sql +560 -0
  123. package/drizzle/0212_slack_installation_bindings.sql +348 -0
  124. package/drizzle/0213_browser_interaction_authority.sql +67 -0
  125. package/drizzle/0213_slack_user_link_access_requests.sql +177 -0
  126. package/drizzle/0214_browser_download_saves.sql +34 -0
  127. package/drizzle/0214_session_activity_commit_gate.sql +262 -0
  128. package/drizzle/0215_browser_controller_host.sql +385 -0
  129. package/drizzle/0215_capabilities_platform.sql +980 -0
  130. package/drizzle/0216_browser_auth_health_evidence.sql +137 -0
  131. package/drizzle/0216_pack_component_ownership.sql +235 -0
  132. package/drizzle/0217_capability_definition_delete_authority.sql +32 -0
  133. package/drizzle/0217_external_browser_auth_operations.sql +22 -0
  134. package/drizzle/0218_organization_tenancy_foundation.sql +299 -0
  135. package/drizzle/0219_organization_tenancy_managed_human_provisioning.sql +317 -0
  136. package/drizzle/0219_site_auth_maintenance_sessions.sql +234 -0
  137. package/drizzle/0220_memory_slack_append_only_cascade.sql +38 -0
  138. package/drizzle/0220_session_channels.sql +54 -0
  139. package/drizzle/0221_sessions_channel_index.sql +5 -0
  140. package/drizzle/0222_session_visibility_authority_epochs.sql +158 -0
  141. package/drizzle/0222_sessions_channel_fk.sql +89 -0
  142. package/drizzle/0223_pending_tool_event_output.sql +55 -0
  143. package/drizzle/0223_sessions_channel_fk_validate.sql +118 -0
  144. package/drizzle/0224_slack_post_outcome_reconciliation.sql +123 -0
  145. package/drizzle/0225_session_visibility_fork_activation.sql +1363 -0
  146. package/drizzle/0226_personal_codex_authority_foundation.sql +365 -0
  147. package/drizzle/0227_slack_native_actions.sql +145 -0
  148. package/drizzle/0228_interaction_controller_data_plane.sql +14 -0
  149. package/drizzle/0228_slack_task_policy.sql +400 -0
  150. package/drizzle/0229_slack_inbox_file_fact.sql +10 -0
  151. package/drizzle/0230_user_scoped_variable_sets_rigs.sql +84 -0
  152. package/drizzle/0231_integration_definition_identity_cutover.sql +271 -0
  153. package/drizzle/0232_integration_facet_authority_cutover.sql +610 -0
  154. package/drizzle/0233_skill_and_integration_authority_cutover.sql +1162 -0
  155. package/drizzle/0234_xai_subscription_authority.sql +1139 -0
  156. package/drizzle/0235_canonical_human_login_bindings.sql +920 -0
  157. package/drizzle/0236_browser_identity_lifecycle.sql +39 -0
  158. package/drizzle/0236_session_visibility_slack_policy.sql +72 -0
  159. package/drizzle/0237_interaction_transition_reaper.sql +263 -0
  160. package/drizzle/0238_goal_persistence_policy.sql +471 -0
  161. package/drizzle/0238_supergrok_realtime_model.sql +29 -0
  162. package/drizzle/0239_supergrok_video_funding.sql +50 -0
  163. package/drizzle/0239_task_tree_notes.sql +990 -0
  164. package/package.json +26 -5
  165. package/src/attached-browser-devices.ts +371 -0
  166. package/src/attempt-tool-catalogs.ts +131 -0
  167. package/src/browser-auth.ts +4373 -0
  168. package/src/browser-downloads.ts +517 -0
  169. package/src/browser-identities.ts +1385 -0
  170. package/src/browser-sessions.ts +2294 -0
  171. package/src/browser-state-artifacts.ts +449 -0
  172. package/src/canonical-human-identities.ts +317 -0
  173. package/src/capability-components.ts +166 -0
  174. package/src/capability-integrations.ts +1571 -0
  175. package/src/codemode-operations.ts +674 -0
  176. package/src/company-profile-schema.ts +143 -0
  177. package/src/company-profile.ts +968 -0
  178. package/src/computer-sessions.ts +1130 -0
  179. package/src/connection-token-resolver.ts +270 -25
  180. package/src/database.ts +468 -24
  181. package/src/editable-artifact-durable-export.ts +1201 -0
  182. package/src/editable-artifact-materialization.ts +893 -0
  183. package/src/editable-artifacts-schema.ts +879 -0
  184. package/src/editable-artifacts.ts +5404 -0
  185. package/src/generated-images.ts +740 -0
  186. package/src/index.ts +9963 -1465
  187. package/src/insights.ts +251 -23
  188. package/src/integration-bindings.ts +455 -0
  189. package/src/integration-facets.ts +862 -0
  190. package/src/interaction-revisions.ts +70 -0
  191. package/src/interaction-schema.ts +1606 -0
  192. package/src/knowledge-source-sync-schema.ts +192 -0
  193. package/src/knowledge-source-sync.ts +1922 -0
  194. package/src/memory-slack-delivery.ts +801 -0
  195. package/src/pack-components.ts +954 -0
  196. package/src/pack-installations.ts +886 -0
  197. package/src/persistence-errors.ts +4 -7
  198. package/src/plugin-packages.ts +1186 -0
  199. package/src/provision-roles.ts +534 -1
  200. package/src/runtime-posture-cli.ts +9 -0
  201. package/src/runtime-posture.ts +721 -5
  202. package/src/schema.ts +6270 -3159
  203. package/src/scoped-knowledge-schema.ts +1 -1
  204. package/src/scoped-knowledge.ts +655 -3
  205. package/src/session-control.ts +130 -42
  206. package/src/session-queue-commands.ts +265 -15
  207. package/src/session-realtime-context.ts +2 -2
  208. package/src/session-realtime-ledger.ts +3 -3
  209. package/src/session-realtime.ts +7 -7
  210. package/src/session-tenancy.ts +188 -0
  211. package/src/session-tool-call-settlement.ts +18 -3
  212. package/src/slack-task-policy-schema.ts +130 -0
  213. package/src/slack-task-policy.ts +341 -0
  214. package/src/slack-user-link-access.ts +702 -0
  215. package/src/task-notes-schema.ts +135 -0
  216. package/src/task-notes.ts +178 -0
  217. package/src/video-generation.ts +1530 -0
  218. package/src/workspace-artifacts.ts +1 -1
  219. package/src/workspace-learning-policy-schema.ts +139 -0
  220. package/src/workspace-learning-policy.ts +595 -0
  221. package/src/xai-subscription.ts +1887 -0
  222. package/dist/chunk-P6CUHXQZ.js +0 -7248
  223. package/dist/chunk-P6CUHXQZ.js.map +0 -1
  224. package/dist/chunk-VHBFHMPC.js +0 -1219
  225. package/dist/chunk-VHBFHMPC.js.map +0 -1
package/src/insights.ts CHANGED
@@ -19,10 +19,15 @@ export type ModelCallFactAggregateRow = {
19
19
  inputTokens: number;
20
20
  outputTokens: number;
21
21
  cachedTokens: number;
22
+ cacheInputTokens: number;
22
23
  cacheWriteTokens: number;
23
24
  reasoningTokens: number;
24
25
  totalTokens: number;
26
+ tokenKnownCalls: number;
27
+ cacheKnownCalls: number;
25
28
  pricedCostMicros: number;
29
+ estimatedProviderCostMicros: number;
30
+ estimatedProviderCostKnownCalls: number;
26
31
  };
27
32
 
28
33
  export type InsightsDayBucket = {
@@ -52,15 +57,21 @@ export type RootSessionDriverRow = {
52
57
  rootSessionId: string;
53
58
  title: string | null;
54
59
  pricedCostMicros: number;
55
- inputTokens: number;
60
+ estimatedProviderCostMicros: number;
61
+ estimatedProviderCostKnownCalls: number;
62
+ totalTokens: number;
56
63
  cachedTokens: number;
64
+ cacheInputTokens: number;
57
65
  };
58
66
 
59
67
  export type ScheduleFactAggregate = {
60
68
  scheduledTaskId: string;
61
69
  pricedCostMicros: number;
62
- inputTokens: number;
70
+ estimatedProviderCostMicros: number;
71
+ estimatedProviderCostKnownCalls: number;
72
+ totalTokens: number;
63
73
  cachedTokens: number;
74
+ cacheInputTokens: number;
64
75
  calls: number;
65
76
  /** Prefer credits whenever any credits-path call exists in the window. */
66
77
  billingPath: string;
@@ -88,10 +99,36 @@ export type FloorSessionRow = {
88
99
  createdAt: Date;
89
100
  };
90
101
 
102
+ export type RecentModelCallRow = {
103
+ id: string;
104
+ occurredAt: Date;
105
+ recordedAt: Date;
106
+ sessionId: string;
107
+ sessionTitle: string | null;
108
+ turnId: string;
109
+ provider: string;
110
+ providerApi: string;
111
+ model: string;
112
+ billingPath: string;
113
+ inputTokens: number | null;
114
+ outputTokens: number | null;
115
+ cachedTokens: number | null;
116
+ cacheWriteTokens: number | null;
117
+ reasoningTokens: number | null;
118
+ totalTokens: number | null;
119
+ pricedCostMicros: number;
120
+ estimatedProviderCostMicros: number | null;
121
+ pricingSource: string | null;
122
+ };
123
+
91
124
  function dayKeyUtc(value: Date): string {
92
125
  return value.toISOString().slice(0, 10);
93
126
  }
94
127
 
128
+ function hourKeyUtc(value: Date): string {
129
+ return `${value.toISOString().slice(0, 13)}:00`;
130
+ }
131
+
95
132
  export async function sumUsageQuantityInRange(
96
133
  db: Database,
97
134
  input: {
@@ -120,7 +157,7 @@ export async function sumUsageQuantityInRange(
120
157
  });
121
158
  }
122
159
 
123
- export async function sumUsageQuantityByDay(
160
+ async function sumUsageQuantityByBucket(
124
161
  db: Database,
125
162
  input: {
126
163
  workspaceId: string;
@@ -128,12 +165,21 @@ export async function sumUsageQuantityByDay(
128
165
  since: Date;
129
166
  until: Date;
130
167
  },
168
+ granularity: "day" | "hour",
131
169
  ): Promise<Map<string, number>> {
132
170
  const context = await rlsContextForWorkspace(db, input.workspaceId);
133
171
  return await withRlsContext(db, context, async (scopedDb) => {
172
+ const bucket =
173
+ granularity === "hour"
174
+ ? sql<string>`to_char(date_trunc('hour', ${schema.usageEvents.occurredAt} at time zone 'UTC'), 'YYYY-MM-DD"T"HH24:00')`
175
+ : sql<string>`to_char(date_trunc('day', ${schema.usageEvents.occurredAt} at time zone 'UTC'), 'YYYY-MM-DD')`;
176
+ const bucketGroup =
177
+ granularity === "hour"
178
+ ? sql`date_trunc('hour', ${schema.usageEvents.occurredAt} at time zone 'UTC')`
179
+ : sql`date_trunc('day', ${schema.usageEvents.occurredAt} at time zone 'UTC')`;
134
180
  const rows = await scopedDb
135
181
  .select({
136
- day: sql<string>`to_char(date_trunc('day', ${schema.usageEvents.occurredAt} at time zone 'UTC'), 'YYYY-MM-DD')`,
182
+ bucket,
137
183
  total: sql<number>`coalesce(sum(${schema.usageEvents.quantity}), 0)`,
138
184
  })
139
185
  .from(schema.usageEvents)
@@ -145,11 +191,35 @@ export async function sumUsageQuantityByDay(
145
191
  lt(schema.usageEvents.occurredAt, input.until),
146
192
  ),
147
193
  )
148
- .groupBy(sql`date_trunc('day', ${schema.usageEvents.occurredAt} at time zone 'UTC')`);
149
- return new Map(rows.map((row) => [row.day, Number(row.total)]));
194
+ .groupBy(bucketGroup);
195
+ return new Map(rows.map((row) => [row.bucket, Number(row.total)]));
150
196
  });
151
197
  }
152
198
 
199
+ export async function sumUsageQuantityByDay(
200
+ db: Database,
201
+ input: {
202
+ workspaceId: string;
203
+ eventType: string;
204
+ since: Date;
205
+ until: Date;
206
+ },
207
+ ): Promise<Map<string, number>> {
208
+ return await sumUsageQuantityByBucket(db, input, "day");
209
+ }
210
+
211
+ export async function sumUsageQuantityByHour(
212
+ db: Database,
213
+ input: {
214
+ workspaceId: string;
215
+ eventType: string;
216
+ since: Date;
217
+ until: Date;
218
+ },
219
+ ): Promise<Map<string, number>> {
220
+ return await sumUsageQuantityByBucket(db, input, "hour");
221
+ }
222
+
153
223
  export async function aggregateModelCallFacts(
154
224
  db: Database,
155
225
  input: {
@@ -178,10 +248,15 @@ export async function aggregateModelCallFacts(
178
248
  inputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.inputTokens}), 0)`,
179
249
  outputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.outputTokens}), 0)`,
180
250
  cachedTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.cachedTokens}), 0)`,
251
+ cacheInputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.inputTokens}) filter (where ${schema.modelCallFacts.cachedTokens} is not null and ${schema.modelCallFacts.inputTokens} is not null), 0)`,
181
252
  cacheWriteTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.cacheWriteTokens}), 0)`,
182
253
  reasoningTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.reasoningTokens}), 0)`,
183
254
  totalTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.totalTokens}), 0)`,
184
- pricedCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}), 0)`,
255
+ tokenKnownCalls: sql<number>`count(${schema.modelCallFacts.totalTokens})::int`,
256
+ cacheKnownCalls: sql<number>`count(*) filter (where ${schema.modelCallFacts.cachedTokens} is not null and ${schema.modelCallFacts.inputTokens} is not null)::int`,
257
+ pricedCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}) filter (where ${schema.modelCallFacts.billingPath} = 'opengeni_credits'), 0)`,
258
+ estimatedProviderCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.estimatedProviderCostMicros}), 0)`,
259
+ estimatedProviderCostKnownCalls: sql<number>`count(${schema.modelCallFacts.estimatedProviderCostMicros})::int`,
185
260
  })
186
261
  .from(schema.modelCallFacts)
187
262
  .where(and(...clauses))
@@ -198,15 +273,36 @@ export async function aggregateModelCallFacts(
198
273
  inputTokens: Number(row.inputTokens),
199
274
  outputTokens: Number(row.outputTokens),
200
275
  cachedTokens: Number(row.cachedTokens),
276
+ cacheInputTokens: Number(row.cacheInputTokens),
201
277
  cacheWriteTokens: Number(row.cacheWriteTokens),
202
278
  reasoningTokens: Number(row.reasoningTokens),
203
279
  totalTokens: Number(row.totalTokens),
280
+ tokenKnownCalls: Number(row.tokenKnownCalls),
281
+ cacheKnownCalls: Number(row.cacheKnownCalls),
204
282
  pricedCostMicros: Number(row.pricedCostMicros),
283
+ estimatedProviderCostMicros: Number(row.estimatedProviderCostMicros),
284
+ estimatedProviderCostKnownCalls: Number(row.estimatedProviderCostKnownCalls),
205
285
  }));
206
286
  });
207
287
  }
208
288
 
209
- export async function aggregateModelCallFactsByDay(
289
+ type ModelCallFactSeriesAggregate = {
290
+ costMicros: number;
291
+ estimatedProviderCostMicros: number;
292
+ estimatedProviderCostKnownCalls: number;
293
+ inputTokens: number;
294
+ outputTokens: number;
295
+ cachedTokens: number;
296
+ cacheInputTokens: number;
297
+ cacheWriteTokens: number;
298
+ reasoningTokens: number;
299
+ totalTokens: number;
300
+ tokenKnownCalls: number;
301
+ cacheKnownCalls: number;
302
+ calls: number;
303
+ };
304
+
305
+ async function aggregateModelCallFactsByBucket(
210
306
  db: Database,
211
307
  input: {
212
308
  workspaceId: string;
@@ -215,9 +311,8 @@ export async function aggregateModelCallFactsByDay(
215
311
  provider?: string | null;
216
312
  model?: string | null;
217
313
  },
218
- ): Promise<
219
- Map<string, { costMicros: number; inputTokens: number; cachedTokens: number; calls: number }>
220
- > {
314
+ granularity: "day" | "hour",
315
+ ): Promise<Map<string, ModelCallFactSeriesAggregate>> {
221
316
  const context = await rlsContextForWorkspace(db, input.workspaceId);
222
317
  return await withRlsContext(db, context, async (scopedDb) => {
223
318
  const clauses = [
@@ -227,24 +322,50 @@ export async function aggregateModelCallFactsByDay(
227
322
  ...(input.provider ? [eq(schema.modelCallFacts.provider, input.provider)] : []),
228
323
  ...(input.model ? [eq(schema.modelCallFacts.model, input.model)] : []),
229
324
  ];
325
+ const bucket =
326
+ granularity === "hour"
327
+ ? sql<string>`to_char(date_trunc('hour', ${schema.modelCallFacts.occurredAt} at time zone 'UTC'), 'YYYY-MM-DD"T"HH24:00')`
328
+ : sql<string>`to_char(date_trunc('day', ${schema.modelCallFacts.occurredAt} at time zone 'UTC'), 'YYYY-MM-DD')`;
329
+ const bucketGroup =
330
+ granularity === "hour"
331
+ ? sql`date_trunc('hour', ${schema.modelCallFacts.occurredAt} at time zone 'UTC')`
332
+ : sql`date_trunc('day', ${schema.modelCallFacts.occurredAt} at time zone 'UTC')`;
230
333
  const rows = await scopedDb
231
334
  .select({
232
- day: sql<string>`to_char(date_trunc('day', ${schema.modelCallFacts.occurredAt} at time zone 'UTC'), 'YYYY-MM-DD')`,
233
- costMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}), 0)`,
335
+ bucket,
336
+ costMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}) filter (where ${schema.modelCallFacts.billingPath} = 'opengeni_credits'), 0)`,
337
+ estimatedProviderCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.estimatedProviderCostMicros}), 0)`,
338
+ estimatedProviderCostKnownCalls: sql<number>`count(${schema.modelCallFacts.estimatedProviderCostMicros})::int`,
234
339
  inputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.inputTokens}), 0)`,
340
+ outputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.outputTokens}), 0)`,
235
341
  cachedTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.cachedTokens}), 0)`,
342
+ cacheInputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.inputTokens}) filter (where ${schema.modelCallFacts.cachedTokens} is not null and ${schema.modelCallFacts.inputTokens} is not null), 0)`,
343
+ cacheWriteTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.cacheWriteTokens}), 0)`,
344
+ reasoningTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.reasoningTokens}), 0)`,
345
+ totalTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.totalTokens}), 0)`,
346
+ tokenKnownCalls: sql<number>`count(${schema.modelCallFacts.totalTokens})::int`,
347
+ cacheKnownCalls: sql<number>`count(*) filter (where ${schema.modelCallFacts.cachedTokens} is not null and ${schema.modelCallFacts.inputTokens} is not null)::int`,
236
348
  calls: sql<number>`count(*)::int`,
237
349
  })
238
350
  .from(schema.modelCallFacts)
239
351
  .where(and(...clauses))
240
- .groupBy(sql`date_trunc('day', ${schema.modelCallFacts.occurredAt} at time zone 'UTC')`);
352
+ .groupBy(bucketGroup);
241
353
  return new Map(
242
354
  rows.map((row) => [
243
- row.day,
355
+ row.bucket,
244
356
  {
245
357
  costMicros: Number(row.costMicros),
358
+ estimatedProviderCostMicros: Number(row.estimatedProviderCostMicros),
359
+ estimatedProviderCostKnownCalls: Number(row.estimatedProviderCostKnownCalls),
246
360
  inputTokens: Number(row.inputTokens),
361
+ outputTokens: Number(row.outputTokens),
247
362
  cachedTokens: Number(row.cachedTokens),
363
+ cacheInputTokens: Number(row.cacheInputTokens),
364
+ cacheWriteTokens: Number(row.cacheWriteTokens),
365
+ reasoningTokens: Number(row.reasoningTokens),
366
+ totalTokens: Number(row.totalTokens),
367
+ tokenKnownCalls: Number(row.tokenKnownCalls),
368
+ cacheKnownCalls: Number(row.cacheKnownCalls),
248
369
  calls: Number(row.calls),
249
370
  },
250
371
  ]),
@@ -252,6 +373,32 @@ export async function aggregateModelCallFactsByDay(
252
373
  });
253
374
  }
254
375
 
376
+ export async function aggregateModelCallFactsByDay(
377
+ db: Database,
378
+ input: {
379
+ workspaceId: string;
380
+ since: Date;
381
+ until: Date;
382
+ provider?: string | null;
383
+ model?: string | null;
384
+ },
385
+ ): Promise<Map<string, ModelCallFactSeriesAggregate>> {
386
+ return await aggregateModelCallFactsByBucket(db, input, "day");
387
+ }
388
+
389
+ export async function aggregateModelCallFactsByHour(
390
+ db: Database,
391
+ input: {
392
+ workspaceId: string;
393
+ since: Date;
394
+ until: Date;
395
+ provider?: string | null;
396
+ model?: string | null;
397
+ },
398
+ ): Promise<Map<string, ModelCallFactSeriesAggregate>> {
399
+ return await aggregateModelCallFactsByBucket(db, input, "hour");
400
+ }
401
+
255
402
  export async function aggregateWarmSecondsByGroup(
256
403
  db: Database,
257
404
  input: InsightsTimeWindow & { workspaceId: string; limit?: number },
@@ -378,9 +525,12 @@ export async function aggregateRootSessionDrivers(
378
525
  .select({
379
526
  rootSessionId: childSessions.rootSessionId,
380
527
  title: rootSessions.title,
381
- pricedCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}), 0)`,
382
- inputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.inputTokens}), 0)`,
528
+ pricedCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}) filter (where ${schema.modelCallFacts.billingPath} = 'opengeni_credits'), 0)`,
529
+ estimatedProviderCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.estimatedProviderCostMicros}), 0)`,
530
+ estimatedProviderCostKnownCalls: sql<number>`count(${schema.modelCallFacts.estimatedProviderCostMicros})::int`,
531
+ totalTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.totalTokens}), 0)`,
383
532
  cachedTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.cachedTokens}), 0)`,
533
+ cacheInputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.inputTokens}) filter (where ${schema.modelCallFacts.cachedTokens} is not null and ${schema.modelCallFacts.inputTokens} is not null), 0)`,
384
534
  })
385
535
  .from(schema.modelCallFacts)
386
536
  .innerJoin(
@@ -399,14 +549,17 @@ export async function aggregateRootSessionDrivers(
399
549
  )
400
550
  .where(and(...clauses))
401
551
  .groupBy(childSessions.rootSessionId, rootSessions.title)
402
- .orderBy(sql`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}), 0) desc`);
552
+ .orderBy(sql`coalesce(sum(${schema.modelCallFacts.totalTokens}), 0) desc`);
403
553
  const rows = input.rootSessionIds ? await query : await query.limit(input.limit ?? 8);
404
554
  return rows.map((row) => ({
405
555
  rootSessionId: row.rootSessionId,
406
556
  title: row.title,
407
557
  pricedCostMicros: Number(row.pricedCostMicros),
408
- inputTokens: Number(row.inputTokens),
558
+ estimatedProviderCostMicros: Number(row.estimatedProviderCostMicros),
559
+ estimatedProviderCostKnownCalls: Number(row.estimatedProviderCostKnownCalls),
560
+ totalTokens: Number(row.totalTokens),
409
561
  cachedTokens: Number(row.cachedTokens),
562
+ cacheInputTokens: Number(row.cacheInputTokens),
410
563
  }));
411
564
  });
412
565
  }
@@ -436,6 +589,62 @@ export async function listModelCallFacets(
436
589
  });
437
590
  }
438
591
 
592
+ export async function listRecentModelCalls(
593
+ db: Database,
594
+ input: InsightsTimeWindow & {
595
+ workspaceId: string;
596
+ provider?: string | null;
597
+ model?: string | null;
598
+ limit?: number;
599
+ },
600
+ ): Promise<RecentModelCallRow[]> {
601
+ const context = await rlsContextForWorkspace(db, input.workspaceId);
602
+ const factSessions = alias(schema.sessions, "insight_recent_fact_sessions");
603
+ return await withRlsContext(db, context, async (scopedDb) => {
604
+ const clauses = [
605
+ eq(schema.modelCallFacts.workspaceId, input.workspaceId),
606
+ gte(schema.modelCallFacts.occurredAt, input.since),
607
+ lt(schema.modelCallFacts.occurredAt, input.until),
608
+ ...(input.provider ? [eq(schema.modelCallFacts.provider, input.provider)] : []),
609
+ ...(input.model ? [eq(schema.modelCallFacts.model, input.model)] : []),
610
+ ];
611
+ const rows = await scopedDb
612
+ .select({
613
+ id: schema.modelCallFacts.id,
614
+ occurredAt: schema.modelCallFacts.occurredAt,
615
+ recordedAt: schema.modelCallFacts.recordedAt,
616
+ sessionId: schema.modelCallFacts.sessionId,
617
+ sessionTitle: factSessions.title,
618
+ turnId: schema.modelCallFacts.turnId,
619
+ provider: schema.modelCallFacts.provider,
620
+ providerApi: schema.modelCallFacts.providerApi,
621
+ model: schema.modelCallFacts.model,
622
+ billingPath: schema.modelCallFacts.billingPath,
623
+ inputTokens: schema.modelCallFacts.inputTokens,
624
+ outputTokens: schema.modelCallFacts.outputTokens,
625
+ cachedTokens: schema.modelCallFacts.cachedTokens,
626
+ cacheWriteTokens: schema.modelCallFacts.cacheWriteTokens,
627
+ reasoningTokens: schema.modelCallFacts.reasoningTokens,
628
+ totalTokens: schema.modelCallFacts.totalTokens,
629
+ pricedCostMicros: schema.modelCallFacts.pricedCostMicros,
630
+ estimatedProviderCostMicros: schema.modelCallFacts.estimatedProviderCostMicros,
631
+ pricingSource: schema.modelCallFacts.pricingSource,
632
+ })
633
+ .from(schema.modelCallFacts)
634
+ .leftJoin(
635
+ factSessions,
636
+ and(
637
+ eq(factSessions.workspaceId, schema.modelCallFacts.workspaceId),
638
+ eq(factSessions.id, schema.modelCallFacts.sessionId),
639
+ ),
640
+ )
641
+ .where(and(...clauses))
642
+ .orderBy(desc(schema.modelCallFacts.occurredAt), desc(schema.modelCallFacts.id))
643
+ .limit(Math.max(1, Math.min(input.limit ?? 50, 100)));
644
+ return rows;
645
+ });
646
+ }
647
+
439
648
  export async function aggregateScheduleFacts(
440
649
  db: Database,
441
650
  input: InsightsTimeWindow & {
@@ -457,9 +666,12 @@ export async function aggregateScheduleFacts(
457
666
  const rows = await scopedDb
458
667
  .select({
459
668
  scheduledTaskId: schema.modelCallFacts.scheduledTaskId,
460
- pricedCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}), 0)`,
461
- inputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.inputTokens}), 0)`,
669
+ pricedCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.pricedCostMicros}) filter (where ${schema.modelCallFacts.billingPath} = 'opengeni_credits'), 0)`,
670
+ estimatedProviderCostMicros: sql<number>`coalesce(sum(${schema.modelCallFacts.estimatedProviderCostMicros}), 0)`,
671
+ estimatedProviderCostKnownCalls: sql<number>`count(${schema.modelCallFacts.estimatedProviderCostMicros})::int`,
672
+ totalTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.totalTokens}), 0)`,
462
673
  cachedTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.cachedTokens}), 0)`,
674
+ cacheInputTokens: sql<number>`coalesce(sum(${schema.modelCallFacts.inputTokens}) filter (where ${schema.modelCallFacts.cachedTokens} is not null and ${schema.modelCallFacts.inputTokens} is not null), 0)`,
463
675
  calls: sql<number>`count(*)::int`,
464
676
  billingPath: sql<string>`case
465
677
  when bool_or(${schema.modelCallFacts.billingPath} = 'opengeni_credits')
@@ -475,8 +687,11 @@ export async function aggregateScheduleFacts(
475
687
  .map((row) => ({
476
688
  scheduledTaskId: row.scheduledTaskId,
477
689
  pricedCostMicros: Number(row.pricedCostMicros),
478
- inputTokens: Number(row.inputTokens),
690
+ estimatedProviderCostMicros: Number(row.estimatedProviderCostMicros),
691
+ estimatedProviderCostKnownCalls: Number(row.estimatedProviderCostKnownCalls),
692
+ totalTokens: Number(row.totalTokens),
479
693
  cachedTokens: Number(row.cachedTokens),
694
+ cacheInputTokens: Number(row.cacheInputTokens),
480
695
  calls: Number(row.calls),
481
696
  billingPath: row.billingPath,
482
697
  }));
@@ -605,10 +820,11 @@ export async function countOnlineMachines(
605
820
  db: Database,
606
821
  workspaceId: string,
607
822
  heartbeatFreshMs: number,
823
+ now = new Date(),
608
824
  ): Promise<number> {
609
825
  const context = await rlsContextForWorkspace(db, workspaceId);
610
826
  return await withRlsContext(db, context, async (scopedDb) => {
611
- const cutoff = new Date(Date.now() - heartbeatFreshMs);
827
+ const cutoff = new Date(now.getTime() - heartbeatFreshMs);
612
828
  const [{ n } = { n: 0 }] = await scopedDb
613
829
  .select({ n: sql<number>`count(*)::int` })
614
830
  .from(schema.enrollments)
@@ -636,6 +852,18 @@ export function enumerateUtcDays(since: Date, until: Date): string[] {
636
852
  return days;
637
853
  }
638
854
 
855
+ export function enumerateUtcHours(since: Date, until: Date): string[] {
856
+ const hours: string[] = [];
857
+ const cursor = new Date(since);
858
+ cursor.setUTCMinutes(0, 0, 0);
859
+ const end = until.getTime();
860
+ while (cursor.getTime() < end) {
861
+ hours.push(hourKeyUtc(cursor));
862
+ cursor.setUTCHours(cursor.getUTCHours() + 1);
863
+ }
864
+ return hours;
865
+ }
866
+
639
867
  /**
640
868
  * Idempotent YTD backfill from authoritative agent.model.usage into model_call_facts.
641
869
  * Never rewrites billing rows or existing live facts (onConflictDoNothing).