nolo-cli 0.1.30 → 0.1.31

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 (308) hide show
  1. package/agent-runtime/agentRecordConfig.ts +0 -2
  2. package/agent-runtime/hostAdapter.ts +0 -1
  3. package/agent-runtime/hostRuntimeCapabilities.ts +7 -0
  4. package/agent-runtime/hostRuntimeConfig.ts +86 -0
  5. package/agent-runtime/index.ts +13 -0
  6. package/agent-runtime/localToolPolicy.ts +13 -6
  7. package/agent-runtime/localWorkspaceTools.ts +333 -30
  8. package/agent-runtime/platformChatProvider.ts +4 -1
  9. package/agent-runtime/runtimeToolPolicy.ts +32 -47
  10. package/agent-runtime/shellCommandPolicy.ts +117 -0
  11. package/agent-runtime/toolGate.ts +38 -0
  12. package/agentRecordCommands.ts +1 -1
  13. package/agentRecordHelpers.ts +35 -20
  14. package/agentRunParsing.ts +18 -5
  15. package/agentRuntimeLocal.ts +10 -0
  16. package/ai/agent/agentRunDialogLineage.ts +56 -0
  17. package/ai/agent/agentSlice.ts +45 -10
  18. package/ai/agent/avatarUtils.ts +1 -1
  19. package/ai/agent/buildSystemPrompt.ts +1 -2
  20. package/ai/agent/cliExecutor.ts +330 -19
  21. package/ai/agent/createAgentSchema.ts +22 -0
  22. package/ai/agent/hooks/fetchPublicAgents.ts +14 -39
  23. package/ai/agent/hooks/usePublicAgents.ts +90 -192
  24. package/ai/agent/publicAgentCatalog.ts +359 -0
  25. package/ai/agent/publicAgentIdentity.ts +76 -0
  26. package/ai/agent/server/fetchPublicAgents.ts +21 -46
  27. package/ai/agent/serverOrigin.ts +7 -0
  28. package/ai/agent/streamAgentChatTurn.ts +23 -21
  29. package/ai/agent/streamAgentChatTurnUtils.ts +292 -6
  30. package/ai/agent/types.ts +2 -0
  31. package/ai/agent/web/agentDisplayUtils.ts +171 -0
  32. package/ai/agent/web/useAgentModelAvatarComponent.ts +32 -0
  33. package/ai/ai.locale.ts +8 -0
  34. package/ai/chat/sendOpenAICompletionsRequest.native.ts +15 -0
  35. package/ai/chat/sendOpenAICompletionsRequest.ts +93 -1
  36. package/ai/chat/sendOpenAIResponseRequest.ts +86 -9
  37. package/ai/chat/updateTotalUsage.ts +7 -0
  38. package/ai/llm/deepinfra.ts +30 -15
  39. package/ai/llm/mistral.ts +3 -4
  40. package/ai/llm/providers.ts +2 -0
  41. package/ai/llm/types.ts +15 -0
  42. package/ai/memory/capture.ts +3 -3
  43. package/ai/memory/consolidate.ts +2 -2
  44. package/ai/memory/delete.ts +4 -3
  45. package/ai/memory/query.ts +3 -3
  46. package/ai/memory/remember.ts +2 -2
  47. package/ai/memory/store.ts +3 -3
  48. package/ai/memory/storeShared.ts +3 -2
  49. package/ai/policy/commercialMultiplier.ts +16 -0
  50. package/ai/token/billingAdminAuditEvent.ts +68 -0
  51. package/ai/token/billingAdminAuditEventWriter.ts +41 -0
  52. package/ai/token/billingAnomaly.ts +90 -0
  53. package/ai/token/billingAnomalyLifecycle.ts +54 -0
  54. package/ai/token/billingAnomalyLifecycleWriter.ts +44 -0
  55. package/ai/token/billingAnomalyWriter.ts +41 -0
  56. package/ai/token/calculatePrice.ts +3 -2
  57. package/ai/token/deepinfraReconciliation.ts +153 -0
  58. package/ai/token/deepinfraRequestIdQuery.ts +92 -0
  59. package/ai/token/externalToolCost.ts +54 -41
  60. package/ai/token/missingUsageEstimate.ts +12 -3
  61. package/ai/token/normalizeUsage.ts +45 -5
  62. package/ai/token/openaiReconciliation.ts +188 -0
  63. package/ai/token/providerBillableEvent.ts +84 -0
  64. package/ai/token/providerBillableEventWriter.ts +36 -0
  65. package/ai/token/providerBillingAnomalyDrilldown.ts +308 -0
  66. package/ai/token/providerBillingDrilldown.ts +361 -0
  67. package/ai/token/providerBillingHealthReport.ts +366 -0
  68. package/ai/token/providerCall.ts +144 -0
  69. package/ai/token/providerCallSweeper.ts +168 -0
  70. package/ai/token/providerCallWriter.ts +33 -0
  71. package/ai/token/providerCredential.ts +89 -0
  72. package/ai/token/providerCredentialAnomaly.ts +154 -0
  73. package/ai/token/providerCredentialLifecycle.ts +59 -0
  74. package/ai/token/providerCredentialLifecycleWriter.ts +44 -0
  75. package/ai/token/providerCredentialRegistry.ts +83 -0
  76. package/ai/token/providerCredentialRegistryCommand.ts +97 -0
  77. package/ai/token/providerCredentialRegistryList.ts +124 -0
  78. package/ai/token/providerCredentialRegistryResolver.ts +150 -0
  79. package/ai/token/providerCredentialRegistryWriter.ts +41 -0
  80. package/ai/token/providerCredentialRotationCommand.ts +102 -0
  81. package/ai/token/providerCredentialRuntime.ts +56 -0
  82. package/ai/token/providerDispatchIntent.ts +67 -0
  83. package/ai/token/providerDispatchIntentWriter.ts +36 -0
  84. package/ai/token/providerGateway.ts +260 -0
  85. package/ai/token/providerReconciliation.ts +148 -0
  86. package/ai/token/providerReconciliationLocal.ts +97 -0
  87. package/ai/token/providerReconciliationRunner.ts +165 -0
  88. package/ai/token/providerReconciliationWriter.ts +41 -0
  89. package/ai/token/ratingResult.ts +202 -0
  90. package/ai/token/ratingResultWriter.ts +33 -0
  91. package/ai/token/serverDialogProjection.ts +5 -3
  92. package/ai/token/serverTokenWriter.ts +11 -3
  93. package/ai/token/types.ts +10 -0
  94. package/ai/tools/agent/callAgentTool.ts +3 -4
  95. package/ai/tools/agent/presets/appBuilderPreset.ts +5 -7
  96. package/ai/tools/agent/runStreamingAgentTool.ts +1 -2
  97. package/ai/tools/agent/streamParallelAgentsTool.ts +2 -3
  98. package/ai/tools/readXPostTool.ts +1 -5
  99. package/app/admin/adminPages.ts +9 -0
  100. package/app/i18n/i18n.config.ts +5 -0
  101. package/app/i18n/translations/pricing.locale.ts +397 -0
  102. package/app/utils/desktopAgentRuntimeStatusClient.ts +5 -4
  103. package/app/utils/desktopAgentRuntimeTurnClient.ts +4 -1
  104. package/app/utils/imageUtils.ts +64 -34
  105. package/auth/adminPermissions.ts +18 -0
  106. package/auth/authSlice.ts +26 -12
  107. package/auth/hooks/useFetchUsers.ts +3 -0
  108. package/auth/routes.ts +46 -0
  109. package/auth/server/accessActivity.ts +4 -3
  110. package/auth/server/adminPermissions.ts +43 -0
  111. package/auth/server/adminUsageReport.ts +375 -0
  112. package/auth/server/billingAnomalies.ts +235 -0
  113. package/auth/server/billingUsageReport.ts +41 -2
  114. package/auth/server/creatorEarningCreditConversion.ts +10 -2
  115. package/auth/server/deduct.ts +14 -7
  116. package/auth/server/delete.ts +10 -2
  117. package/auth/server/disable.ts +4 -3
  118. package/auth/server/emailAutomation.ts +7 -5
  119. package/auth/server/emailDbConfig.ts +4 -2
  120. package/auth/server/emailDelivery.ts +24 -20
  121. package/auth/server/emailDeliveryGuards.ts +10 -7
  122. package/auth/server/emailPreferences.ts +6 -4
  123. package/auth/server/emailReport.ts +6 -4
  124. package/auth/server/emailRuntimeConfig.ts +3 -3
  125. package/auth/server/emailScenarioNotifications.ts +9 -8
  126. package/auth/server/enable.ts +4 -3
  127. package/auth/server/getRechargeHistory.ts +3 -2
  128. package/auth/server/getUser.ts +3 -2
  129. package/auth/server/gptProAccess.ts +44 -0
  130. package/auth/server/inviteReward.ts +10 -7
  131. package/auth/server/listusers.ts +6 -4
  132. package/auth/server/login.ts +2 -2
  133. package/auth/server/productEntitlements.ts +7 -4
  134. package/auth/server/productPurchase.ts +10 -2
  135. package/auth/server/providerBillingDrilldown.ts +47 -0
  136. package/auth/server/providerBillingHealth.ts +71 -0
  137. package/auth/server/providerCredentials.ts +136 -0
  138. package/auth/server/recharge.ts +14 -8
  139. package/auth/server/revokeSessions.ts +4 -3
  140. package/auth/server/route.ts +77 -0
  141. package/auth/server/sendEmail.ts +5 -4
  142. package/auth/server/signup.ts +10 -3
  143. package/auth/server/token.ts +53 -55
  144. package/auth/server/tokenUsageBilling.ts +18 -7
  145. package/auth/server/usageManagementAccess.ts +43 -0
  146. package/auth/server/usageReport.ts +59 -0
  147. package/auth/types.ts +12 -0
  148. package/auth/utils.ts +24 -22
  149. package/authCommands.ts +36 -2
  150. package/buildPublish.ts +3 -1
  151. package/buildPublishArtifactAssembly.ts +62 -1
  152. package/buildPublishImportAnalysis.ts +21 -16
  153. package/chat/dialog/dialogLaunchScope.ts +32 -0
  154. package/chat/dialog/objectAssistantRegistry.ts +2 -2
  155. package/chat/dialog/useCreateDialog.ts +1 -1
  156. package/chat/hooks/useChatInput.ts +41 -10
  157. package/chat/messages/messageSlice.ts +5 -1
  158. package/chat/messages/pendingAttachmentParts.ts +28 -2
  159. package/chat/messages/sendFirstMessage.ts +76 -79
  160. package/chat/messages/types.ts +9 -0
  161. package/chat/messages/web/streamingMarkdownModel.ts +41 -0
  162. package/chat/messages/web/useStreamingReveal.ts +91 -0
  163. package/chat/web/messageInputAgentUi.ts +94 -6
  164. package/cliEnvHelpers.ts +8 -0
  165. package/cliRuntimeDoctorReport.ts +193 -0
  166. package/client/agentConfigResolver.test.ts +0 -2
  167. package/client/agentRun.test.ts +171 -10
  168. package/client/agentRun.ts +44 -637
  169. package/client/agentRunInput.ts +10 -0
  170. package/client/agentRunLocalExecutor.ts +179 -0
  171. package/client/agentRunServerTransport.ts +394 -0
  172. package/client/agentRunTaskRunRecorder.ts +176 -0
  173. package/client/localRuntimeAdapter.test.ts +236 -9
  174. package/client/localRuntimeAdapter.ts +221 -23
  175. package/client/localToolPolicy.test.ts +28 -4
  176. package/connector-experimental/daemonLauncher.ts +38 -4
  177. package/connectorRunArtifact.ts +34 -10
  178. package/connectorWebSocketTarget.ts +9 -5
  179. package/core/builtinAgents.ts +155 -11
  180. package/create/space/contentLabels.ts +1 -1
  181. package/create/space/contentTypeMeta.ts +3 -2
  182. package/create/space/sidebarVisibleTypes.ts +27 -0
  183. package/create/space/space.locale.ts +76 -4
  184. package/database/actions/agentReadResolution.ts +2 -8
  185. package/database/actions/read.ts +11 -9
  186. package/database/actions/readAndWait.ts +10 -5
  187. package/database/server/agentDelegation.ts +3 -2
  188. package/database/server/authorityStoreConformance.ts +209 -0
  189. package/database/server/authorityStoreTypes.ts +31 -0
  190. package/database/server/coreDataProxy.ts +1 -1
  191. package/database/server/dataHandlers.ts +13 -5
  192. package/database/server/db.ts +15 -34
  193. package/database/server/delete.ts +88 -71
  194. package/database/server/emailRepository.ts +11 -9
  195. package/database/server/fileService.ts +2 -1
  196. package/database/server/legacyServerDb.ts +58 -0
  197. package/database/server/levelAuthorityStore.ts +75 -0
  198. package/database/server/memoryAuthorityStore.ts +133 -0
  199. package/database/server/patch.ts +13 -12
  200. package/database/server/query.ts +3 -2
  201. package/database/server/read.ts +33 -30
  202. package/database/server/resourceAccess.ts +7 -5
  203. package/database/server/serverStoreFactory.ts +124 -0
  204. package/database/server/spaceMemberAuthority.ts +3 -2
  205. package/database/server/storeBatchCompat.ts +37 -0
  206. package/database/server/testAuthorityStore.ts +76 -0
  207. package/database/server/write.ts +29 -27
  208. package/integrations/anthropic/anthropicModels.ts +12 -12
  209. package/integrations/deepseek/models.ts +67 -67
  210. package/integrations/google/models.ts +111 -53
  211. package/integrations/openai/models.ts +22 -22
  212. package/integrations/xai/models.ts +1 -1
  213. package/localRuntimeDb.ts +39 -2
  214. package/machineCommandPrompts.ts +2 -0
  215. package/machineCommands.ts +193 -63
  216. package/machineWsRunDispatch.ts +80 -15
  217. package/machineWsSession.ts +39 -2
  218. package/noloReadBridgePrompt.ts +16 -0
  219. package/package.json +99 -6
  220. package/render/layout/mainLayoutViewMode.ts +25 -0
  221. package/render/page/server/createPage.ts +3 -1
  222. package/render/page/server/handleCreatePage.ts +2 -11
  223. package/runtimeDoctorCommands.ts +15 -66
  224. package/server/api/methods.ts +34 -12
  225. package/server/api/tableHandlers.ts +6 -3
  226. package/server/authenticateToken.ts +47 -2
  227. package/server/billingLedgerRoutes.ts +15 -0
  228. package/server/buildManifest.ts +54 -0
  229. package/server/coreReadiness.ts +34 -25
  230. package/server/coreRuntime.ts +15 -10
  231. package/server/handlers/activityStatsHandler.ts +5 -2
  232. package/server/handlers/agentEmailRegistrationE2EHandler.ts +25 -23
  233. package/server/handlers/agentRun/agentLookup.ts +1 -7
  234. package/server/handlers/agentRun/autonomousRuntime.ts +11 -10
  235. package/server/handlers/agentRun/builtinAgents.ts +12 -88
  236. package/server/handlers/agentRun/continueDialogContext.ts +15 -3
  237. package/server/handlers/agentRun/{inheritedDialogPrompt.ts → conversationReference.ts} +3 -3
  238. package/server/handlers/agentRun/dialogLookup.ts +4 -3
  239. package/server/handlers/agentRun/index.ts +864 -404
  240. package/server/handlers/agentRun/localRuntimeBootstrap.ts +17 -1
  241. package/server/handlers/agentRun/loop.ts +920 -508
  242. package/server/handlers/agentRun/machineConnector.ts +56 -8
  243. package/server/handlers/agentRun/machineRunAudit.ts +3 -2
  244. package/server/handlers/agentRun/modelUsageToolHandlers.ts +13 -10
  245. package/server/handlers/agentRun/requestContext.ts +12 -8
  246. package/server/handlers/agentRun/runtimeExecutionContext.ts +15 -3
  247. package/server/handlers/agentRun/runtimePreparation.ts +26 -24
  248. package/server/handlers/agentRun/runtimeRouting.ts +25 -40
  249. package/server/handlers/agentRun/runtimeSystemMessages.ts +65 -0
  250. package/server/handlers/agentRun/saveDialog.ts +97 -87
  251. package/server/handlers/agentRun/taskRunToolHandler.ts +9 -7
  252. package/server/handlers/agentRun/toolExecutor.ts +42 -78
  253. package/server/handlers/agentRun/types.ts +2 -1
  254. package/server/handlers/appDeployStatusStore.ts +17 -10
  255. package/server/handlers/appDomainHandler.ts +21 -15
  256. package/server/handlers/appLegacyInfraCleanup.ts +5 -2
  257. package/server/handlers/appSandboxHandler.ts +29 -8
  258. package/server/handlers/appSpaceAccess.ts +15 -8
  259. package/server/handlers/chatHandler.ts +22 -2
  260. package/server/handlers/chatUpstreamRetry.ts +225 -2
  261. package/server/handlers/connector/connectorConnectionRegistry.ts +55 -7
  262. package/server/handlers/connector/connectorWebSocket.ts +14 -10
  263. package/server/handlers/desktopAgentRuntimeAdapter.ts +4 -4
  264. package/server/handlers/desktopAgentRuntimeHostCapabilities.ts +25 -0
  265. package/server/handlers/desktopAgentRuntimeReadiness.ts +62 -0
  266. package/server/handlers/desktopAgentRuntimeStatusHandler.ts +38 -30
  267. package/server/handlers/desktopAgentRuntimeTurnHandler.ts +7 -3
  268. package/server/handlers/desktopAgentRuntimeTurnService.ts +39 -45
  269. package/server/handlers/desktopLlamaRuntimeHandler.ts +85 -51
  270. package/server/handlers/desktopUpdaterHandler.ts +132 -24
  271. package/server/handlers/favoriteHandlers.ts +8 -2
  272. package/server/handlers/getDialogBillingHandler.ts +4 -3
  273. package/server/handlers/getTransactionsHandler.ts +5 -2
  274. package/server/handlers/machines/machineBinding.ts +1 -0
  275. package/server/handlers/machines/machineHandlers.ts +0 -14
  276. package/server/handlers/machines/machineInstallAssets.ts +4 -2
  277. package/server/handlers/notificationEmitter.ts +5 -3
  278. package/server/handlers/notificationsHandler.ts +5 -2
  279. package/server/handlers/providerCallsRecentHandler.ts +234 -0
  280. package/server/handlers/providerReconciliationRunHandler.ts +180 -0
  281. package/server/handlers/providerRequestIdsHandler.ts +105 -0
  282. package/server/handlers/tableAccess.ts +6 -2
  283. package/server/handlers/tableToolHandlers.ts +28 -14
  284. package/server/handlers/userSecretsHandler.ts +17 -6
  285. package/server/handlers/versionHandler.ts +9 -4
  286. package/server/handlers/versionStore.ts +18 -10
  287. package/server/html/ssrPreload.ts +5 -3
  288. package/server/payments/crypto/cryptoUsdcBasePaymentProvider.ts +11 -8
  289. package/server/payments/crypto/cryptoUsdcBaseScanner.ts +12 -7
  290. package/server/payments/crypto/cryptoUsdtTronPaymentProvider.ts +11 -8
  291. package/server/payments/crypto/cryptoUsdtTronScanner.ts +9 -7
  292. package/server/payments/waffo/waffoPaymentProvider.ts +12 -9
  293. package/server/runtimeRole.ts +15 -0
  294. package/server/scheduler/scheduledTaskRuntime.ts +4 -2
  295. package/server/scheduler/schedulerService.ts +2 -2
  296. package/server/taskRunBridgeRoutes.ts +8 -5
  297. package/share/server/fetchCommunityShares.ts +1 -1
  298. package/share/server/helpers.ts +19 -17
  299. package/share/server/importShare.ts +9 -13
  300. package/share/server/listCommunityShares.ts +4 -4
  301. package/share/server/purchaseDoc.ts +8 -16
  302. package/share/server/readCommunityShareCover.ts +3 -2
  303. package/share/server/readSharedTable.ts +14 -13
  304. package/share/server/tableReplication.ts +42 -39
  305. package/spaceAttachCommands.ts +135 -0
  306. package/spaceInternalCommandEntries.ts +2 -0
  307. package/ai/agent/runtimeServerBase.ts +0 -37
  308. package/server/handlers/desktopAgentRuntimeHostFacts.ts +0 -40
@@ -0,0 +1,260 @@
1
+ import crypto from "crypto";
2
+
3
+ import {
4
+ createProviderCallFailedEvent,
5
+ createProviderCallPendingEvent,
6
+ } from "./providerCall";
7
+ import { writeProviderCallEvent, type ProviderCallStore } from "./providerCallWriter";
8
+ import { writeBillingAnomaly } from "./billingAnomalyWriter";
9
+ import {
10
+ writeRevokedProviderCredentialBlockedAnomalyIfNeeded,
11
+ writeRevokedProviderCredentialUsedAnomalyIfNeeded,
12
+ } from "./providerCredentialAnomaly";
13
+ import { createProviderDispatchIntent } from "./providerDispatchIntent";
14
+ import {
15
+ writeProviderDispatchIntent,
16
+ type ProviderDispatchIntentStore,
17
+ } from "./providerDispatchIntentWriter";
18
+ import type { ProviderCredentialIdentity } from "./providerCredential";
19
+
20
+ type ProviderGatewayStore = ProviderCallStore & ProviderDispatchIntentStore;
21
+
22
+ type ProviderGatewayIds = {
23
+ providerCallId?: () => string;
24
+ pendingEventId?: () => string;
25
+ dispatchIntentId?: () => string;
26
+ failedEventId?: () => string;
27
+ };
28
+
29
+ type ProviderGatewayWriters = {
30
+ providerCallWriter?: typeof writeProviderCallEvent;
31
+ providerDispatchIntentWriter?: typeof writeProviderDispatchIntent;
32
+ billingAnomalyWriter?: typeof writeBillingAnomaly;
33
+ };
34
+
35
+ type ProviderRequestCall = {
36
+ userId: string;
37
+ dialogId?: string;
38
+ agentId?: string;
39
+ provider: string;
40
+ model: string;
41
+ endpoint?: string;
42
+ serviceTier?: string;
43
+ credential?: ProviderCredentialIdentity;
44
+ url: string;
45
+ init?: RequestInit;
46
+ };
47
+
48
+ type DispatchProviderRequestInput = {
49
+ store: ProviderGatewayStore;
50
+ call: ProviderRequestCall;
51
+ fetchImpl?: typeof fetch;
52
+ now?: () => Date;
53
+ ids?: ProviderGatewayIds;
54
+ writers?: ProviderGatewayWriters;
55
+ };
56
+
57
+ type DispatchProviderRequestResult = {
58
+ providerCallId: string;
59
+ pendingEventId: string;
60
+ dispatchIntentId: string;
61
+ response: Response;
62
+ };
63
+
64
+ export class ProviderCredentialRevokedError extends Error {
65
+ constructor() {
66
+ super("Provider credential is revoked");
67
+ this.name = "ProviderCredentialRevokedError";
68
+ }
69
+ }
70
+
71
+ const randomId = (prefix: string) => `${prefix}_${crypto.randomUUID()}`;
72
+
73
+ const methodOf = (init?: RequestInit) => init?.method?.toUpperCase() || "GET";
74
+
75
+ const bodyHashOf = (body: BodyInit | null | undefined) => {
76
+ if (body == null) return undefined;
77
+ if (typeof body === "string") {
78
+ return `sha256:${crypto.createHash("sha256").update(body).digest("hex")}`;
79
+ }
80
+ if (body instanceof URLSearchParams) {
81
+ return `sha256:${crypto.createHash("sha256").update(body.toString()).digest("hex")}`;
82
+ }
83
+ return `sha256:${crypto
84
+ .createHash("sha256")
85
+ .update(String(Object.prototype.toString.call(body)))
86
+ .digest("hex")}`;
87
+ };
88
+
89
+ const withClientRequestIdHeader = (
90
+ init: RequestInit | undefined,
91
+ providerCallId: string
92
+ ): RequestInit => {
93
+ if (init?.headers instanceof Headers) {
94
+ const headers = new Headers(init.headers);
95
+ if (!headers.has("x-client-request-id")) {
96
+ headers.set("x-client-request-id", providerCallId);
97
+ }
98
+ return {
99
+ ...init,
100
+ headers,
101
+ };
102
+ }
103
+
104
+ if (Array.isArray(init?.headers)) {
105
+ const headers = new Headers(init.headers);
106
+ if (!headers.has("x-client-request-id")) {
107
+ headers.set("x-client-request-id", providerCallId);
108
+ }
109
+ return {
110
+ ...init,
111
+ headers,
112
+ };
113
+ }
114
+
115
+ const headers = {
116
+ ...((init?.headers as Record<string, string> | undefined) ?? {}),
117
+ };
118
+ const hasClientRequestId = Object.keys(headers).some(
119
+ (key) => key.toLowerCase() === "x-client-request-id"
120
+ );
121
+ if (!hasClientRequestId) {
122
+ headers["x-client-request-id"] = providerCallId;
123
+ }
124
+ return {
125
+ ...init,
126
+ headers,
127
+ };
128
+ };
129
+
130
+ function errorShape(error: unknown) {
131
+ if (error instanceof Error) {
132
+ return {
133
+ name: error.name,
134
+ message: error.message,
135
+ };
136
+ }
137
+ return { message: String(error) };
138
+ }
139
+
140
+ export async function dispatchProviderRequest({
141
+ store,
142
+ call,
143
+ fetchImpl = fetch,
144
+ now = () => new Date(),
145
+ ids = {},
146
+ writers = {},
147
+ }: DispatchProviderRequestInput): Promise<DispatchProviderRequestResult> {
148
+ const providerCallId = ids.providerCallId?.() ?? randomId("pcall");
149
+ const pendingEventId = ids.pendingEventId?.() ?? randomId("evt_pending");
150
+ const dispatchIntentId = ids.dispatchIntentId?.() ?? randomId("intent");
151
+ const failedEventIdFactory = ids.failedEventId ?? (() => randomId("evt_failed"));
152
+ const startedAt = now().toISOString();
153
+
154
+ await (writers.providerCallWriter ?? writeProviderCallEvent)({
155
+ store,
156
+ event: createProviderCallPendingEvent({
157
+ providerCallId,
158
+ eventId: pendingEventId,
159
+ userId: call.userId,
160
+ dialogId: call.dialogId,
161
+ agentId: call.agentId,
162
+ provider: call.provider,
163
+ model: call.model,
164
+ endpoint: call.endpoint,
165
+ serviceTier: call.serviceTier,
166
+ credential: call.credential,
167
+ startedAt,
168
+ }),
169
+ });
170
+
171
+ if (call.credential?.registryStatus === "revoked") {
172
+ const error = new ProviderCredentialRevokedError();
173
+ const failedEvent = createProviderCallFailedEvent({
174
+ providerCallId,
175
+ eventId: failedEventIdFactory(),
176
+ userId: call.userId,
177
+ dialogId: call.dialogId,
178
+ agentId: call.agentId,
179
+ provider: call.provider,
180
+ model: call.model,
181
+ endpoint: call.endpoint,
182
+ serviceTier: call.serviceTier,
183
+ credential: call.credential,
184
+ startedAt,
185
+ failedAt: now().toISOString(),
186
+ error: errorShape(error),
187
+ });
188
+ await (writers.providerCallWriter ?? writeProviderCallEvent)({
189
+ store,
190
+ event: failedEvent,
191
+ });
192
+ await writeRevokedProviderCredentialBlockedAnomalyIfNeeded({
193
+ store,
194
+ event: failedEvent,
195
+ now,
196
+ billingAnomalyWriter: writers.billingAnomalyWriter ?? writeBillingAnomaly,
197
+ });
198
+ throw error;
199
+ }
200
+
201
+ await (writers.providerDispatchIntentWriter ?? writeProviderDispatchIntent)({
202
+ store,
203
+ intent: createProviderDispatchIntent({
204
+ providerCallId,
205
+ intentId: dispatchIntentId,
206
+ userId: call.userId,
207
+ dialogId: call.dialogId,
208
+ agentId: call.agentId,
209
+ provider: call.provider,
210
+ model: call.model,
211
+ endpoint: call.endpoint,
212
+ serviceTier: call.serviceTier,
213
+ credential: call.credential,
214
+ url: call.url,
215
+ method: methodOf(call.init),
216
+ bodyHash: bodyHashOf(call.init?.body),
217
+ createdAt: now().toISOString(),
218
+ }),
219
+ });
220
+
221
+ try {
222
+ const response = await fetchImpl(
223
+ call.url,
224
+ withClientRequestIdHeader(call.init, providerCallId)
225
+ );
226
+ return {
227
+ providerCallId,
228
+ pendingEventId,
229
+ dispatchIntentId,
230
+ response,
231
+ };
232
+ } catch (error) {
233
+ const failedEvent = createProviderCallFailedEvent({
234
+ providerCallId,
235
+ eventId: failedEventIdFactory(),
236
+ userId: call.userId,
237
+ dialogId: call.dialogId,
238
+ agentId: call.agentId,
239
+ provider: call.provider,
240
+ model: call.model,
241
+ endpoint: call.endpoint,
242
+ serviceTier: call.serviceTier,
243
+ credential: call.credential,
244
+ startedAt,
245
+ failedAt: now().toISOString(),
246
+ error: errorShape(error),
247
+ });
248
+ await (writers.providerCallWriter ?? writeProviderCallEvent)({
249
+ store,
250
+ event: failedEvent,
251
+ });
252
+ await writeRevokedProviderCredentialUsedAnomalyIfNeeded({
253
+ store,
254
+ event: failedEvent,
255
+ now,
256
+ billingAnomalyWriter: writers.billingAnomalyWriter ?? writeBillingAnomaly,
257
+ });
258
+ throw error;
259
+ }
260
+ }
@@ -0,0 +1,148 @@
1
+ export type ProviderReconciliationStatus =
2
+ | "matched"
3
+ | "missing_local"
4
+ | "extra_local"
5
+ | "mismatched_usage"
6
+ | "mismatched_cost"
7
+ | "waiting_provider";
8
+
9
+ export type ProviderReconciliationBucketTotals = {
10
+ requestCount: number;
11
+ inputTokens: number;
12
+ outputTokens: number;
13
+ rawProviderCostUsd: number;
14
+ };
15
+
16
+ export type LocalProviderReconciliationBucketTotals =
17
+ ProviderReconciliationBucketTotals & {
18
+ platformCredits: number;
19
+ };
20
+
21
+ export type ProviderReconciliationBucketDiff = {
22
+ requestCount: number;
23
+ inputTokens: number;
24
+ outputTokens: number;
25
+ rawProviderCostUsd: number;
26
+ platformCredits: number;
27
+ absoluteRawProviderCostUsd: number;
28
+ };
29
+
30
+ export type ProviderReconciliationBucket = {
31
+ id: string;
32
+ provider: string;
33
+ model?: string;
34
+ endpoint?: string;
35
+ serviceTier?: string;
36
+ officialEvidence?: Record<string, unknown>;
37
+ bucketStart: string;
38
+ bucketEnd: string;
39
+ status: ProviderReconciliationStatus;
40
+ official: ProviderReconciliationBucketTotals;
41
+ local: LocalProviderReconciliationBucketTotals;
42
+ diff: ProviderReconciliationBucketDiff;
43
+ needsAnomaly: boolean;
44
+ createdAt: string;
45
+ };
46
+
47
+ export type CreateProviderReconciliationBucketInput = {
48
+ bucketId: string;
49
+ provider: string;
50
+ model?: string;
51
+ endpoint?: string;
52
+ serviceTier?: string;
53
+ bucketStart: string;
54
+ bucketEnd: string;
55
+ official: ProviderReconciliationBucketTotals;
56
+ local: LocalProviderReconciliationBucketTotals;
57
+ createdAt: string;
58
+ rawProviderCostToleranceUsd?: number;
59
+ };
60
+
61
+ export const buildProviderReconciliationBucketKey = (bucketId: string) =>
62
+ `provider-reconciliation-bucket-${bucketId}`;
63
+
64
+ const hasOfficialEvidence = (totals: ProviderReconciliationBucketTotals) =>
65
+ totals.requestCount > 0 ||
66
+ totals.inputTokens > 0 ||
67
+ totals.outputTokens > 0 ||
68
+ totals.rawProviderCostUsd > 0;
69
+
70
+ const hasLocalEvidence = (totals: LocalProviderReconciliationBucketTotals) =>
71
+ totals.requestCount > 0 ||
72
+ totals.inputTokens > 0 ||
73
+ totals.outputTokens > 0 ||
74
+ totals.rawProviderCostUsd > 0 ||
75
+ totals.platformCredits > 0;
76
+
77
+ const roundMoney = (value: number) => Number(value.toFixed(12));
78
+
79
+ const classifyBucket = ({
80
+ official,
81
+ local,
82
+ diff,
83
+ costTolerance,
84
+ }: {
85
+ official: ProviderReconciliationBucketTotals;
86
+ local: LocalProviderReconciliationBucketTotals;
87
+ diff: ProviderReconciliationBucketDiff;
88
+ costTolerance: number;
89
+ }): ProviderReconciliationStatus => {
90
+ const officialExists = hasOfficialEvidence(official);
91
+ const localExists = hasLocalEvidence(local);
92
+ if (officialExists && !localExists) return "missing_local";
93
+ if (!officialExists && localExists) return "extra_local";
94
+ if (!officialExists && !localExists) return "waiting_provider";
95
+ if (
96
+ diff.requestCount !== 0 ||
97
+ diff.inputTokens !== 0 ||
98
+ diff.outputTokens !== 0
99
+ ) {
100
+ return "mismatched_usage";
101
+ }
102
+ if (diff.absoluteRawProviderCostUsd > costTolerance) {
103
+ return "mismatched_cost";
104
+ }
105
+ return "matched";
106
+ };
107
+
108
+ export const createProviderReconciliationBucket = (
109
+ input: CreateProviderReconciliationBucketInput
110
+ ): ProviderReconciliationBucket => {
111
+ const diff: ProviderReconciliationBucketDiff = {
112
+ requestCount: input.official.requestCount - input.local.requestCount,
113
+ inputTokens: input.official.inputTokens - input.local.inputTokens,
114
+ outputTokens: input.official.outputTokens - input.local.outputTokens,
115
+ rawProviderCostUsd: roundMoney(
116
+ input.official.rawProviderCostUsd - input.local.rawProviderCostUsd
117
+ ),
118
+ platformCredits: input.local.platformCredits,
119
+ absoluteRawProviderCostUsd: roundMoney(
120
+ Math.abs(input.official.rawProviderCostUsd - input.local.rawProviderCostUsd)
121
+ ),
122
+ };
123
+ const status = classifyBucket({
124
+ official: input.official,
125
+ local: input.local,
126
+ diff,
127
+ costTolerance: input.rawProviderCostToleranceUsd ?? 0.000001,
128
+ });
129
+
130
+ return {
131
+ id: input.bucketId,
132
+ provider: input.provider,
133
+ model: input.model,
134
+ endpoint: input.endpoint,
135
+ serviceTier: input.serviceTier,
136
+ officialEvidence: input.officialEvidence
137
+ ? { ...input.officialEvidence }
138
+ : undefined,
139
+ bucketStart: input.bucketStart,
140
+ bucketEnd: input.bucketEnd,
141
+ status,
142
+ official: { ...input.official },
143
+ local: { ...input.local },
144
+ diff,
145
+ needsAnomaly: status !== "matched" && status !== "waiting_provider",
146
+ createdAt: input.createdAt,
147
+ };
148
+ };
@@ -0,0 +1,97 @@
1
+ import type { ProviderCallCompletedEvent, ProviderCallEvent } from "./providerCall";
2
+ import type { RatingResult } from "./ratingResult";
3
+ import type { LocalProviderReconciliationBucketTotals } from "./providerReconciliation";
4
+
5
+ export type ProviderReconciliationLocalStore = {
6
+ iterator(options: { gte?: string; lte?: string }): AsyncIterable<[string, unknown]>;
7
+ };
8
+
9
+ const PROVIDER_CALL_PREFIX = "provider-call-";
10
+ const PROVIDER_CALL_RANGE_END = `${PROVIDER_CALL_PREFIX}\uffff`;
11
+ const RATING_RESULT_PREFIX = "rating-result-";
12
+ const RATING_RESULT_RANGE_END = `${RATING_RESULT_PREFIX}\uffff`;
13
+
14
+ const isProviderCallCompletedEvent = (
15
+ value: unknown
16
+ ): value is ProviderCallCompletedEvent =>
17
+ Boolean(
18
+ value &&
19
+ typeof value === "object" &&
20
+ (value as ProviderCallEvent).status === "completed" &&
21
+ typeof (value as { provider?: unknown }).provider === "string" &&
22
+ typeof (value as { model?: unknown }).model === "string" &&
23
+ typeof (value as { completedAt?: unknown }).completedAt === "string"
24
+ );
25
+
26
+ const isRatingResult = (value: unknown): value is RatingResult =>
27
+ Boolean(
28
+ value &&
29
+ typeof value === "object" &&
30
+ typeof (value as { id?: unknown }).id === "string" &&
31
+ typeof (value as { provider?: unknown }).provider === "string" &&
32
+ typeof (value as { model?: unknown }).model === "string" &&
33
+ typeof (value as { platformCredits?: unknown }).platformCredits === "number" &&
34
+ typeof (value as { createdAt?: unknown }).createdAt === "string"
35
+ );
36
+
37
+ const isWithinWindow = (iso: string, bucketStartMs: number, bucketEndMs: number) => {
38
+ const time = Date.parse(iso);
39
+ return Number.isFinite(time) && time >= bucketStartMs && time < bucketEndMs;
40
+ };
41
+
42
+ const roundMoney = (value: number) => Number(value.toFixed(12));
43
+
44
+ export async function aggregateLocalProviderBillingBucket({
45
+ store,
46
+ provider,
47
+ model,
48
+ bucketStart,
49
+ bucketEnd,
50
+ }: {
51
+ store: ProviderReconciliationLocalStore;
52
+ provider: string;
53
+ model?: string;
54
+ bucketStart: string;
55
+ bucketEnd: string;
56
+ }): Promise<LocalProviderReconciliationBucketTotals> {
57
+ const bucketStartMs = Date.parse(bucketStart);
58
+ const bucketEndMs = Date.parse(bucketEnd);
59
+ const totals: LocalProviderReconciliationBucketTotals = {
60
+ requestCount: 0,
61
+ inputTokens: 0,
62
+ outputTokens: 0,
63
+ rawProviderCostUsd: 0,
64
+ platformCredits: 0,
65
+ };
66
+
67
+ for await (const [, value] of store.iterator({
68
+ gte: PROVIDER_CALL_PREFIX,
69
+ lte: PROVIDER_CALL_RANGE_END,
70
+ })) {
71
+ if (!isProviderCallCompletedEvent(value)) continue;
72
+ if (value.provider !== provider) continue;
73
+ if (model && value.model !== model) continue;
74
+ if (!isWithinWindow(value.completedAt, bucketStartMs, bucketEndMs)) continue;
75
+ totals.requestCount += 1;
76
+ totals.inputTokens += value.usage.inputTokens;
77
+ totals.outputTokens += value.usage.outputTokens;
78
+ if (value.cost.rawProviderCurrency === "USD") {
79
+ totals.rawProviderCostUsd += value.cost.rawProviderCost ?? 0;
80
+ }
81
+ }
82
+
83
+ for await (const [, value] of store.iterator({
84
+ gte: RATING_RESULT_PREFIX,
85
+ lte: RATING_RESULT_RANGE_END,
86
+ })) {
87
+ if (!isRatingResult(value)) continue;
88
+ if (value.provider !== provider) continue;
89
+ if (model && value.model !== model) continue;
90
+ if (!isWithinWindow(value.createdAt, bucketStartMs, bucketEndMs)) continue;
91
+ totals.platformCredits += value.platformCredits;
92
+ }
93
+
94
+ totals.rawProviderCostUsd = roundMoney(totals.rawProviderCostUsd);
95
+ totals.platformCredits = roundMoney(totals.platformCredits);
96
+ return totals;
97
+ }
@@ -0,0 +1,165 @@
1
+ import { resolveCommercialMultiplier } from "../policy/commercialMultiplier";
2
+ import { createBillingAnomaly } from "./billingAnomaly";
3
+ import {
4
+ writeBillingAnomaly,
5
+ type BillingAnomalyStore,
6
+ } from "./billingAnomalyWriter";
7
+ import type { NormalizedOfficialProviderBucket } from "./deepinfraReconciliation";
8
+ import {
9
+ aggregateLocalProviderBillingBucket,
10
+ type ProviderReconciliationLocalStore,
11
+ } from "./providerReconciliationLocal";
12
+ import {
13
+ createProviderReconciliationBucket,
14
+ type ProviderReconciliationBucket,
15
+ } from "./providerReconciliation";
16
+ import {
17
+ writeProviderReconciliationBucket,
18
+ type ProviderReconciliationBucketStore,
19
+ } from "./providerReconciliationWriter";
20
+
21
+ type ProviderReconciliationStore =
22
+ ProviderReconciliationLocalStore &
23
+ ProviderReconciliationBucketStore &
24
+ BillingAnomalyStore;
25
+
26
+ export type RunProviderReconciliationResult = {
27
+ bucketsProcessed: number;
28
+ bucketsWritten: number;
29
+ anomaliesWritten: number;
30
+ bucketsProposed: number;
31
+ anomaliesProposed: number;
32
+ riskPlatformCreditsProposed: number;
33
+ alreadyExisted: number;
34
+ dryRun: boolean;
35
+ };
36
+
37
+ const sanitizeIdPart = (value: string) =>
38
+ value.replace(/[^A-Za-z0-9_.-]+/g, "-");
39
+
40
+ const buildBucketId = (bucket: NormalizedOfficialProviderBucket) =>
41
+ [
42
+ "recon",
43
+ bucket.provider,
44
+ bucket.model ? sanitizeIdPart(bucket.model) : "all-models",
45
+ sanitizeIdPart(bucket.bucketStart),
46
+ sanitizeIdPart(bucket.bucketEnd),
47
+ ].join("_");
48
+
49
+ const isAlreadyExistsError = (error: unknown) =>
50
+ error instanceof Error && error.message.includes("already exists:");
51
+
52
+ const estimateRiskPlatformCredits = (bucket: ProviderReconciliationBucket) => {
53
+ const model = bucket.model ?? "";
54
+ const multiplier = resolveCommercialMultiplier(bucket.provider, model);
55
+ return Number((bucket.diff.absoluteRawProviderCostUsd * multiplier).toFixed(6));
56
+ };
57
+
58
+ export async function runProviderReconciliation({
59
+ store,
60
+ officialBuckets,
61
+ createdAt,
62
+ dryRun = false,
63
+ }: {
64
+ store: ProviderReconciliationStore;
65
+ officialBuckets: NormalizedOfficialProviderBucket[];
66
+ createdAt: string;
67
+ dryRun?: boolean;
68
+ }): Promise<RunProviderReconciliationResult> {
69
+ const result: RunProviderReconciliationResult = {
70
+ bucketsProcessed: 0,
71
+ bucketsWritten: 0,
72
+ anomaliesWritten: 0,
73
+ bucketsProposed: 0,
74
+ anomaliesProposed: 0,
75
+ riskPlatformCreditsProposed: 0,
76
+ alreadyExisted: 0,
77
+ dryRun,
78
+ };
79
+
80
+ for (const officialBucket of officialBuckets) {
81
+ result.bucketsProcessed += 1;
82
+ const local = await aggregateLocalProviderBillingBucket({
83
+ store,
84
+ provider: officialBucket.provider,
85
+ model: officialBucket.model,
86
+ bucketStart: officialBucket.bucketStart,
87
+ bucketEnd: officialBucket.bucketEnd,
88
+ });
89
+ const bucket = createProviderReconciliationBucket({
90
+ bucketId: buildBucketId(officialBucket),
91
+ provider: officialBucket.provider,
92
+ model: officialBucket.model,
93
+ endpoint: officialBucket.endpoint,
94
+ officialEvidence: officialBucket.evidence,
95
+ bucketStart: officialBucket.bucketStart,
96
+ bucketEnd: officialBucket.bucketEnd,
97
+ official: officialBucket.official,
98
+ local,
99
+ createdAt,
100
+ });
101
+ result.bucketsProposed += 1;
102
+
103
+ if (!dryRun) {
104
+ try {
105
+ await writeProviderReconciliationBucket({ store, bucket });
106
+ result.bucketsWritten += 1;
107
+ } catch (error) {
108
+ if (isAlreadyExistsError(error)) {
109
+ result.alreadyExisted += 1;
110
+ } else {
111
+ throw error;
112
+ }
113
+ }
114
+ }
115
+
116
+ if (!bucket.needsAnomaly) continue;
117
+ result.anomaliesProposed += 1;
118
+ result.riskPlatformCreditsProposed = Number(
119
+ (
120
+ result.riskPlatformCreditsProposed + estimateRiskPlatformCredits(bucket)
121
+ ).toFixed(6)
122
+ );
123
+ if (dryRun) continue;
124
+
125
+ try {
126
+ await writeBillingAnomaly({
127
+ store,
128
+ anomaly: createBillingAnomaly({
129
+ anomalyId: `anom_${bucket.id}`,
130
+ kind: "provider_reconciliation_diff",
131
+ severity:
132
+ bucket.status === "missing_local" || bucket.status === "mismatched_cost"
133
+ ? "critical"
134
+ : "high",
135
+ stage: "reconciliation",
136
+ userId: "provider-reconciliation",
137
+ provider: bucket.provider,
138
+ model: bucket.model ?? "unknown",
139
+ riskPlatformCredits: estimateRiskPlatformCredits(bucket),
140
+ message: `Provider official bucket differs from local billing evidence: ${bucket.status}`,
141
+ evidence: {
142
+ bucketId: bucket.id,
143
+ bucketStart: bucket.bucketStart,
144
+ bucketEnd: bucket.bucketEnd,
145
+ status: bucket.status,
146
+ official: bucket.official,
147
+ local: bucket.local,
148
+ diff: bucket.diff,
149
+ officialEvidence: officialBucket.evidence,
150
+ },
151
+ createdAt,
152
+ }),
153
+ });
154
+ result.anomaliesWritten += 1;
155
+ } catch (error) {
156
+ if (isAlreadyExistsError(error)) {
157
+ result.alreadyExisted += 1;
158
+ } else {
159
+ throw error;
160
+ }
161
+ }
162
+ }
163
+
164
+ return result;
165
+ }
@@ -0,0 +1,41 @@
1
+ import {
2
+ buildProviderReconciliationBucketKey,
3
+ type ProviderReconciliationBucket,
4
+ } from "./providerReconciliation";
5
+
6
+ export type ProviderReconciliationBucketStore = {
7
+ get(key: string): Promise<unknown>;
8
+ put(key: string, value: unknown): Promise<void>;
9
+ };
10
+
11
+ const isMissingRecordError = (error: unknown) => {
12
+ if (!error || typeof error !== "object") return false;
13
+ const code = (error as { code?: unknown }).code;
14
+ const message = (error as { message?: unknown }).message;
15
+ return (
16
+ code === "LEVEL_NOT_FOUND" ||
17
+ code === "NOT_FOUND" ||
18
+ message === "NotFound"
19
+ );
20
+ };
21
+
22
+ export async function writeProviderReconciliationBucket({
23
+ store,
24
+ bucket,
25
+ }: {
26
+ store: ProviderReconciliationBucketStore;
27
+ bucket: ProviderReconciliationBucket;
28
+ }): Promise<{ key: string }> {
29
+ const key = buildProviderReconciliationBucketKey(bucket.id);
30
+ let existing: unknown;
31
+ try {
32
+ existing = await store.get(key);
33
+ } catch (error) {
34
+ if (!isMissingRecordError(error)) throw error;
35
+ }
36
+ if (existing) {
37
+ throw new Error(`provider reconciliation bucket already exists: ${key}`);
38
+ }
39
+ await store.put(key, bucket);
40
+ return { key };
41
+ }