deepline 0.3.88 → 0.3.89

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 (121) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +534 -4
  2. package/dist/bundling-sources/sdk/src/config.ts +77 -0
  3. package/dist/bundling-sources/sdk/src/http.ts +76 -30
  4. package/dist/bundling-sources/sdk/src/index.ts +6 -0
  5. package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +1 -1
  6. package/dist/bundling-sources/sdk/src/play.ts +15 -1
  7. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +2 -2
  8. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  9. package/dist/bundling-sources/sdk/src/types.ts +83 -4
  10. package/dist/bundling-sources/shared_libs/integration-contracts/AGENTS.md +9 -0
  11. package/dist/bundling-sources/shared_libs/integration-contracts/README.md +3 -0
  12. package/dist/bundling-sources/shared_libs/integration-contracts/index.ts +2 -0
  13. package/dist/bundling-sources/shared_libs/monitors/analytics-install-guides.ts +77 -0
  14. package/dist/bundling-sources/shared_libs/monitors/billing/charges.ts +125 -0
  15. package/dist/bundling-sources/shared_libs/monitors/billing/policy-builders.ts +686 -0
  16. package/dist/bundling-sources/shared_libs/monitors/billing/pricing.ts +54 -0
  17. package/dist/bundling-sources/shared_libs/monitors/billing/public-pricing.ts +72 -0
  18. package/dist/bundling-sources/shared_libs/monitors/billing/session.ts +45 -0
  19. package/dist/bundling-sources/shared_libs/monitors/billing/state.ts +454 -0
  20. package/dist/bundling-sources/shared_libs/monitors/billing/tamradar.ts +260 -0
  21. package/dist/bundling-sources/shared_libs/monitors/billing/types.ts +215 -0
  22. package/dist/bundling-sources/shared_libs/monitors/callback-security.ts +333 -0
  23. package/dist/bundling-sources/shared_libs/monitors/cursor-page.ts +42 -0
  24. package/dist/bundling-sources/shared_libs/monitors/deactivation-reasons.ts +16 -0
  25. package/dist/bundling-sources/shared_libs/monitors/framework-managed-payload-fields.ts +71 -0
  26. package/dist/bundling-sources/shared_libs/monitors/generated/provider-monitor-callback-admission-targets.ts +16 -0
  27. package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +267 -0
  28. package/dist/bundling-sources/shared_libs/monitors/inbound-publish-key.ts +39 -0
  29. package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +82 -0
  30. package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +473 -0
  31. package/dist/bundling-sources/shared_libs/monitors/org-monitor-limits.ts +1 -1
  32. package/dist/bundling-sources/shared_libs/monitors/package.json +29 -0
  33. package/dist/bundling-sources/shared_libs/monitors/provider-callback-verification.ts +1125 -0
  34. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-batch-operation.ts +263 -0
  35. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-capability.ts +964 -0
  36. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +56 -0
  37. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-errors.ts +131 -0
  38. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +535 -0
  39. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-legacy-aliases.ts +16 -0
  40. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-priority-execution.ts +56 -0
  41. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +28 -0
  42. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-runtime-read-eligibility.ts +38 -0
  43. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-state.ts +122 -0
  44. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-status.ts +10 -0
  45. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-upstream-identity.ts +112 -0
  46. package/dist/bundling-sources/shared_libs/monitors/provider-webhook-monitor-runtime.ts +300 -0
  47. package/dist/bundling-sources/shared_libs/monitors/quickstart-prompts.ts +22 -0
  48. package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +93 -0
  49. package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +332 -0
  50. package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +221 -0
  51. package/dist/bundling-sources/shared_libs/monitors/starter-templates.ts +105 -0
  52. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-guidance.ts +73 -0
  53. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +81 -0
  54. package/dist/bundling-sources/shared_libs/monitors/tsconfig.json +10 -0
  55. package/dist/bundling-sources/shared_libs/play-runtime/activity-observation.ts +6 -11
  56. package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +90 -12
  57. package/dist/bundling-sources/shared_libs/play-runtime/async-play-invocation.ts +78 -0
  58. package/dist/bundling-sources/shared_libs/play-runtime/checkpoint-boundaries.ts +70 -0
  59. package/dist/bundling-sources/shared_libs/play-runtime/context.ts +407 -53
  60. package/dist/bundling-sources/shared_libs/play-runtime/coordinator-headers.ts +1 -32
  61. package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +57 -0
  62. package/dist/bundling-sources/shared_libs/play-runtime/default-batch-strategies.ts +20 -1
  63. package/dist/bundling-sources/shared_libs/play-runtime/execution-capabilities.ts +6 -0
  64. package/dist/bundling-sources/shared_libs/play-runtime/execution-protocol.ts +7 -1
  65. package/dist/bundling-sources/shared_libs/play-runtime/lane-retirement-gate.ts +189 -0
  66. package/dist/bundling-sources/shared_libs/play-runtime/play-runtime-semantics.ts +28 -0
  67. package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +2 -0
  68. package/dist/bundling-sources/shared_libs/play-runtime/run-failure.ts +23 -0
  69. package/dist/bundling-sources/shared_libs/play-runtime/run-snapshot-stream.ts +1 -1
  70. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-lifecycle.ts +18 -4
  71. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona-start-admission.ts +68 -0
  72. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/backends/daytona.ts +2 -0
  73. package/dist/bundling-sources/shared_libs/play-runtime/runner-backends/runtime-sandbox-reconciliation.ts +1 -0
  74. package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +10 -8
  75. package/dist/bundling-sources/shared_libs/play-runtime/runtime-capacity-policy.ts +20 -0
  76. package/dist/bundling-sources/shared_libs/play-runtime/runtime-contract.ts +6 -0
  77. package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +6 -0
  78. package/dist/bundling-sources/shared_libs/play-runtime/runtime-scheduler-topology.ts +24 -0
  79. package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backend.ts +30 -1
  80. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +1 -1
  81. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +1 -1
  82. package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +6 -3
  83. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +454 -17
  84. package/dist/bundling-sources/shared_libs/plays/bundling/index.ts +19 -4
  85. package/dist/bundling-sources/shared_libs/plays/contracts.ts +6 -14
  86. package/dist/bundling-sources/shared_libs/plays/core.ts +11 -6
  87. package/dist/bundling-sources/shared_libs/plays/docflow-binding-owner.ts +16 -11
  88. package/dist/bundling-sources/shared_libs/plays/email-status.ts +1 -1
  89. package/dist/bundling-sources/shared_libs/plays/resolve-static-pipeline.ts +1 -1
  90. package/dist/bundling-sources/shared_libs/plays/slug.ts +115 -0
  91. package/dist/bundling-sources/shared_libs/plays/static-pipeline.ts +44 -14
  92. package/dist/bundling-sources/shared_libs/plays/tool-category-descriptions.ts +2 -2
  93. package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +877 -0
  94. package/dist/bundling-sources/shared_libs/product-notifications/configuration-draft.ts +456 -0
  95. package/dist/bundling-sources/shared_libs/product-notifications/configuration.ts +560 -0
  96. package/dist/bundling-sources/shared_libs/product-notifications/content.ts +28 -0
  97. package/dist/bundling-sources/shared_libs/product-notifications/default-policy.ts +59 -0
  98. package/dist/bundling-sources/shared_libs/product-notifications/email.ts +246 -0
  99. package/dist/bundling-sources/shared_libs/product-notifications/evaluation.ts +280 -0
  100. package/dist/bundling-sources/shared_libs/product-notifications/events.ts +433 -0
  101. package/dist/bundling-sources/shared_libs/product-notifications/legacy-slack.ts +80 -0
  102. package/dist/bundling-sources/shared_libs/product-notifications/workspace.ts +38 -0
  103. package/dist/bundling-sources/shared_libs/runtime-env.ts +6 -0
  104. package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +28 -0
  105. package/dist/bundling-sources/shared_libs/security/internal-actor.ts +50 -0
  106. package/dist/bundling-sources/shared_libs/security/secret-redaction.ts +267 -0
  107. package/dist/cli/index.js +1070 -193
  108. package/dist/cli/index.mjs +1057 -180
  109. package/dist/{compiler-manifest-Dbov16F7.d.mts → compiler-manifest-DsLbQaew.d.mts} +132 -8
  110. package/dist/{compiler-manifest-Dbov16F7.d.ts → compiler-manifest-DsLbQaew.d.ts} +132 -8
  111. package/dist/index.d.mts +834 -12
  112. package/dist/index.d.ts +834 -12
  113. package/dist/index.js +469 -40
  114. package/dist/index.mjs +469 -40
  115. package/dist/install-integrity.json +65 -4
  116. package/dist/plays/bundle-play-file.d.mts +8 -3
  117. package/dist/plays/bundle-play-file.d.ts +8 -3
  118. package/dist/plays/bundle-play-file.mjs +100 -20
  119. package/package.json +3 -1
  120. /package/dist/bundling-sources/shared_libs/{integrations/bettercontact-execution-policy.ts → integration-contracts/bettercontact/execution-policy.ts} +0 -0
  121. /package/dist/bundling-sources/shared_libs/{integrations/theirstack-execution-policy.ts → integration-contracts/theirstack/execution-policy.ts} +0 -0
@@ -0,0 +1,535 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import {
3
+ PriorityMonitorExecutionLimitError,
4
+ ProviderMonitorCapacityBackfillInProgressError,
5
+ ProviderMonitorCapacityCleanupRequiredError,
6
+ ProviderMonitorIdentityBackfillInProgressError,
7
+ ProviderMonitorEntityLimitExceededError,
8
+ ProviderMonitorOrgLimitExceededError,
9
+ } from '@deepline/monitors/provider-monitor-errors';
10
+ import { MAX_PRIORITY_MONITORS_PER_ORG } from '@deepline/monitors/provider-monitor-priority-execution';
11
+ import { internal } from '@convex/_generated/api';
12
+ import { getRequiredInternalConvexClient } from '@/lib/convex/internal-client';
13
+ import {
14
+ providerMonitorUpstreamIdentitySignature,
15
+ type ProviderMonitorUpstreamIdentitySignature,
16
+ } from '@deepline/monitors/provider-monitor-upstream-identity';
17
+
18
+ export type ProviderMonitorIdentityClaim = {
19
+ tool: string;
20
+ identityKey: string;
21
+ identity: Record<string, string | null>;
22
+ };
23
+
24
+ type OrgAuthContext = { orgId: string };
25
+
26
+ /**
27
+ * The durable control-plane release operation needs only the opaque claim
28
+ * locator. Keep the display identity out of this boundary: Convex validates
29
+ * release arguments strictly so a rich conflict claim cannot turn a completed
30
+ * upstream delete into a failed local transition.
31
+ */
32
+ export type ProviderMonitorIdentityClaimReference = Pick<
33
+ ProviderMonitorIdentityClaim,
34
+ 'tool' | 'identityKey'
35
+ >;
36
+
37
+ export function providerMonitorIdentityClaimReference(
38
+ claim: ProviderMonitorIdentityClaim | null | undefined,
39
+ ): ProviderMonitorIdentityClaimReference | null {
40
+ return claim ? { tool: claim.tool, identityKey: claim.identityKey } : null;
41
+ }
42
+
43
+ export type ProviderMonitorIdentityReservation =
44
+ | {
45
+ kind: 'acquired';
46
+ claim: ProviderMonitorIdentityClaim;
47
+ restoredState: 'release' | 'active';
48
+ operationId: string;
49
+ }
50
+ | {
51
+ kind: 'occupied';
52
+ claim: ProviderMonitorIdentityClaim;
53
+ monitorKey: string;
54
+ state: 'provisioning' | 'active' | 'migration_conflict';
55
+ conflictingMonitorKeys?: string[];
56
+ }
57
+ | {
58
+ kind: 'in_progress';
59
+ claim: ProviderMonitorIdentityClaim;
60
+ monitorKey: string;
61
+ retryAt: number;
62
+ };
63
+
64
+ /**
65
+ * An exact, per-monitor-key fence around an upstream lifecycle request.
66
+ * Unlike an identity claim, this applies even when a provider does not expose
67
+ * an account-unique identity.
68
+ */
69
+ export type ProviderMonitorLifecycleReservation =
70
+ | {
71
+ kind: 'acquired';
72
+ state: 'provisioning' | 'deleting';
73
+ operationId: string;
74
+ }
75
+ | {
76
+ kind: 'in_progress';
77
+ state: 'provisioning' | 'deleting';
78
+ retryAt: number;
79
+ }
80
+ | {
81
+ kind: 'deleting';
82
+ state: 'deleting';
83
+ retryAt: number;
84
+ }
85
+ | {
86
+ kind: 'not_eligible';
87
+ };
88
+
89
+ function record(value: unknown): Record<string, unknown> | null {
90
+ return value && typeof value === 'object' && !Array.isArray(value)
91
+ ? (value as Record<string, unknown>)
92
+ : null;
93
+ }
94
+
95
+ function finiteNumber(value: unknown): number | null {
96
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
97
+ }
98
+
99
+ /** Convex owns lifecycle admission and validates every durable result. */
100
+ export function normalizeProviderMonitorLifecycleReservation(input: {
101
+ result: unknown;
102
+ state: 'provisioning' | 'deleting';
103
+ operationId: string;
104
+ }): ProviderMonitorLifecycleReservation {
105
+ const result = record(input.result);
106
+ if (!result || typeof result.kind !== 'string') {
107
+ throw new Error(
108
+ 'Provider monitor lifecycle admission returned an invalid control-plane result.',
109
+ );
110
+ }
111
+ if (result.kind === 'acquired') {
112
+ return {
113
+ kind: 'acquired',
114
+ state: input.state,
115
+ operationId: input.operationId,
116
+ };
117
+ }
118
+ if (result.kind === 'not_eligible') {
119
+ return { kind: 'not_eligible' };
120
+ }
121
+ if (
122
+ (result.kind !== 'in_progress' && result.kind !== 'deleting') ||
123
+ (result.state !== 'provisioning' && result.state !== 'deleting') ||
124
+ finiteNumber(result.retryAt) === null
125
+ ) {
126
+ throw new Error(
127
+ 'Provider monitor lifecycle admission returned an unknown control-plane result.',
128
+ );
129
+ }
130
+ if (result.kind === 'deleting' && result.state !== 'deleting') {
131
+ throw new Error(
132
+ 'Provider monitor lifecycle deletion admission returned an invalid operation state.',
133
+ );
134
+ }
135
+ if (result.kind === 'in_progress') {
136
+ return {
137
+ kind: 'in_progress',
138
+ state: result.state as 'provisioning' | 'deleting',
139
+ retryAt: result.retryAt as number,
140
+ };
141
+ }
142
+ return {
143
+ kind: 'deleting',
144
+ state: 'deleting',
145
+ retryAt: result.retryAt as number,
146
+ };
147
+ }
148
+
149
+ function claimFromSignature(
150
+ signature: ProviderMonitorUpstreamIdentitySignature,
151
+ ): ProviderMonitorIdentityClaim {
152
+ const identity = Object.fromEntries(
153
+ Object.entries(signature.key).sort(([a], [b]) => a.localeCompare(b)),
154
+ );
155
+ return {
156
+ tool: signature.tool,
157
+ identityKey: JSON.stringify(identity),
158
+ identity,
159
+ };
160
+ }
161
+
162
+ export function providerMonitorIdentityClaim(input: {
163
+ tool: string;
164
+ payload: Record<string, unknown> | null | undefined;
165
+ }) {
166
+ // Claims protect provider resources, including legacy rows whose public
167
+ // capability id was later retired. The Convex-safe identity contract owns
168
+ // those aliases; the live capability registry intentionally does not.
169
+ const signature = providerMonitorUpstreamIdentitySignature(
170
+ input.tool,
171
+ input.payload,
172
+ );
173
+ return signature ? claimFromSignature(signature) : null;
174
+ }
175
+
176
+ /**
177
+ * Makes a legacy Convex org safe for indexed identity admission. A new org
178
+ * gets one bounded page inline so normal small workspaces stay transparent;
179
+ * a larger org continues in a durable, no-provider-side-effect Convex job and
180
+ * receives a retryable response rather than scanning tens of thousands of
181
+ * legacy definitions synchronously. Later calls are a single complete-marker
182
+ * read.
183
+ */
184
+ export async function ensureProviderMonitorIdentityBackfill(input: {
185
+ auth: OrgAuthContext;
186
+ }) {
187
+ const client = getRequiredInternalConvexClient();
188
+ const globallyComplete = await client.query(
189
+ internal.providerMonitorIdentityClaims.globalBackfillComplete,
190
+ {},
191
+ );
192
+ if (globallyComplete) return;
193
+ const state = await client.query(
194
+ internal.providerMonitorIdentityClaims.backfillStatus,
195
+ { orgId: input.auth.orgId },
196
+ );
197
+ if (state === 'complete') return;
198
+
199
+ if (state === 'not_started') {
200
+ // The marker/fence is committed before the first page reads the legacy
201
+ // index. Therefore every caller, including a rolling pre-claim app
202
+ // process, reaches the current Convex upsert and cannot slip an unclaimed
203
+ // identity behind this cursor.
204
+ await client.mutation(
205
+ internal.providerMonitorIdentityClaims.startBackfill,
206
+ { orgId: input.auth.orgId, schedule: false },
207
+ );
208
+ const initialPage = await client.mutation(
209
+ internal.providerMonitorIdentityClaims.backfillNextPage,
210
+ { orgId: input.auth.orgId },
211
+ );
212
+ if (initialPage.complete) return;
213
+ }
214
+
215
+ const started = await client.mutation(
216
+ internal.providerMonitorIdentityClaims.startBackfill,
217
+ { orgId: input.auth.orgId },
218
+ );
219
+ if (started.state === 'complete') return;
220
+ throw new ProviderMonitorIdentityBackfillInProgressError();
221
+ }
222
+
223
+ export async function getProviderMonitorIdentityOwner(input: {
224
+ auth: OrgAuthContext;
225
+ claim: ProviderMonitorIdentityClaim;
226
+ }) {
227
+ return getRequiredInternalConvexClient().query(
228
+ internal.providerMonitorIdentityClaims.get,
229
+ {
230
+ orgId: input.auth.orgId,
231
+ tool: input.claim.tool,
232
+ identityKey: input.claim.identityKey,
233
+ },
234
+ );
235
+ }
236
+
237
+ export function providerMonitorIdentityProvisioningForMonitor(input: {
238
+ auth: OrgAuthContext;
239
+ monitorKey: string;
240
+ }) {
241
+ return getRequiredInternalConvexClient().query(
242
+ internal.providerMonitorIdentityClaims.provisioningForMonitor,
243
+ { orgId: input.auth.orgId, monitorKey: input.monitorKey },
244
+ );
245
+ }
246
+
247
+ export function priorityMonitorExecutionLimitFromLifecycleError(
248
+ error: unknown,
249
+ ) {
250
+ if (
251
+ error instanceof Error &&
252
+ error.message.includes('PRIORITY_MONITOR_LIMIT_EXCEEDED')
253
+ ) {
254
+ return new PriorityMonitorExecutionLimitError(
255
+ MAX_PRIORITY_MONITORS_PER_ORG,
256
+ );
257
+ }
258
+ return null;
259
+ }
260
+
261
+ // Convex retries mutations itself. This is the bounded outer retry for the
262
+ // rare case in which that budget is exhausted while the short capacity-fold
263
+ // mutation is updating the same organization fence. Lifecycle admission is
264
+ // still pre-provider and reuses one operation id, so replaying it cannot make
265
+ // an upstream monitor, charge, or fence duplicate.
266
+ const LIFECYCLE_ADMISSION_WRITE_CONFLICT =
267
+ /write conflict|optimisticconcurrencycontrolfailure|documents read from or written to .* changed while this mutation was being run/i;
268
+ const MAX_LIFECYCLE_ADMISSION_RETRIES = 3;
269
+
270
+ function lifecycleAdmissionWriteConflict(error: unknown) {
271
+ const message =
272
+ error instanceof Error
273
+ ? error.message
274
+ : typeof error === 'string'
275
+ ? error
276
+ : '';
277
+ return LIFECYCLE_ADMISSION_WRITE_CONFLICT.test(message);
278
+ }
279
+
280
+ async function sleep(ms: number) {
281
+ await new Promise((resolve) => setTimeout(resolve, ms));
282
+ }
283
+
284
+ export async function retryProviderMonitorLifecycleAdmission<T>(
285
+ operation: () => Promise<T>,
286
+ ): Promise<T> {
287
+ for (
288
+ let attempt = 0;
289
+ attempt <= MAX_LIFECYCLE_ADMISSION_RETRIES;
290
+ attempt += 1
291
+ ) {
292
+ try {
293
+ return await operation();
294
+ } catch (error) {
295
+ if (
296
+ !lifecycleAdmissionWriteConflict(error) ||
297
+ attempt === MAX_LIFECYCLE_ADMISSION_RETRIES
298
+ ) {
299
+ throw error;
300
+ }
301
+ await sleep(25 * 2 ** attempt);
302
+ }
303
+ }
304
+ throw new Error('Provider monitor lifecycle admission retry exhausted.');
305
+ }
306
+
307
+ export async function beginProviderMonitorLifecycleReservation(input: {
308
+ auth: OrgAuthContext;
309
+ monitorKey: string;
310
+ /**
311
+ * A durable bulk operation supplies its stable item attempt id. The
312
+ * ordinary scalar path intentionally leaves this unset and mints a fresh
313
+ * UUID. Reusing the same id lets a reclaimed batch refresh its own fence
314
+ * without ever turning an already-recorded provider intent into a second
315
+ * create.
316
+ */
317
+ operationId?: string;
318
+ /** Tool and payload of the monitor being provisioned, so capacity can be
319
+ * counted against the account it watches rather than against the row. */
320
+ entityKey?: string | null;
321
+ state: 'provisioning' | 'deleting';
322
+ priorityExecutionSlots?: number;
323
+ expectedControlStateRevision?: number;
324
+ requiredMonitorStatus?: 'active';
325
+ }): Promise<ProviderMonitorLifecycleReservation> {
326
+ const operationId = input.operationId ?? randomUUID();
327
+ const action =
328
+ input.state === 'provisioning'
329
+ ? internal.providerMonitorIdentityClaims.beginLifecycleProvisioning
330
+ : internal.providerMonitorIdentityClaims.beginLifecycleDeletion;
331
+ const request = () =>
332
+ getRequiredInternalConvexClient().mutation(action, {
333
+ orgId: input.auth.orgId,
334
+ monitorKey: input.monitorKey,
335
+ operationId,
336
+ ...(input.entityKey ? { entityKey: input.entityKey } : {}),
337
+ ...(input.priorityExecutionSlots === 1
338
+ ? { priorityExecutionSlots: 1 }
339
+ : {}),
340
+ ...(typeof input.expectedControlStateRevision === 'number'
341
+ ? { expectedControlStateRevision: input.expectedControlStateRevision }
342
+ : {}),
343
+ ...(input.requiredMonitorStatus
344
+ ? { requiredMonitorStatus: input.requiredMonitorStatus }
345
+ : {}),
346
+ });
347
+ // A priority-slot refusal arrives as a thrown lifecycle error, while the
348
+ // capacity outcomes below arrive as a returned kind. Both paths have to work
349
+ // on the same call, and the backfill retry has to re-run the same request.
350
+ const attempt = async () => {
351
+ try {
352
+ return await request();
353
+ } catch (error) {
354
+ const priorityLimit =
355
+ priorityMonitorExecutionLimitFromLifecycleError(error);
356
+ if (priorityLimit) throw priorityLimit;
357
+ throw error;
358
+ }
359
+ };
360
+ let result = await retryProviderMonitorLifecycleAdmission(attempt);
361
+ if (result.kind === 'backfill_required') {
362
+ await ensureProviderMonitorCapacityBackfill({ auth: input.auth });
363
+ result = await retryProviderMonitorLifecycleAdmission(attempt);
364
+ }
365
+ if (result.kind === 'limit_exceeded') {
366
+ throw new ProviderMonitorOrgLimitExceededError(result.capacity);
367
+ }
368
+ if (result.kind === 'entity_limit_exceeded') {
369
+ throw new ProviderMonitorEntityLimitExceededError(
370
+ result.entityKey,
371
+ result.capacity.monitors_per_entity,
372
+ );
373
+ }
374
+ if (result.kind === 'cleanup_blocked') {
375
+ throw new ProviderMonitorCapacityCleanupRequiredError(input.monitorKey);
376
+ }
377
+ if (result.kind === 'backfill_required') {
378
+ throw new ProviderMonitorCapacityBackfillInProgressError();
379
+ }
380
+ return normalizeProviderMonitorLifecycleReservation({
381
+ result,
382
+ state: input.state,
383
+ operationId,
384
+ });
385
+ }
386
+
387
+ async function ensureProviderMonitorCapacityBackfill(input: {
388
+ auth: OrgAuthContext;
389
+ }) {
390
+ const client = getRequiredInternalConvexClient();
391
+ const state = await client.query(
392
+ internal.providerMonitorCapacity.backfillStatus,
393
+ { orgId: input.auth.orgId },
394
+ );
395
+ if (state === 'complete') return;
396
+
397
+ if (state === 'not_started') {
398
+ await client.mutation(internal.providerMonitorCapacity.startBackfill, {
399
+ orgId: input.auth.orgId,
400
+ schedule: false,
401
+ });
402
+ const initialPage = await client.mutation(
403
+ internal.providerMonitorCapacity.backfillNextPage,
404
+ { orgId: input.auth.orgId },
405
+ );
406
+ if (initialPage.complete) return;
407
+ }
408
+
409
+ const started = await client.mutation(
410
+ internal.providerMonitorCapacity.startBackfill,
411
+ { orgId: input.auth.orgId },
412
+ );
413
+ if (started.state === 'complete') return;
414
+ throw new ProviderMonitorCapacityBackfillInProgressError();
415
+ }
416
+
417
+ export function failProviderMonitorLifecycleReservation(input: {
418
+ auth: OrgAuthContext;
419
+ monitorKey: string;
420
+ reservation: Extract<
421
+ ProviderMonitorLifecycleReservation,
422
+ { kind: 'acquired' }
423
+ >;
424
+ }) {
425
+ return getRequiredInternalConvexClient().mutation(
426
+ internal.providerMonitorIdentityClaims.failLifecycleOperation,
427
+ {
428
+ orgId: input.auth.orgId,
429
+ monitorKey: input.monitorKey,
430
+ state: input.reservation.state,
431
+ operationId: input.reservation.operationId,
432
+ },
433
+ );
434
+ }
435
+
436
+ export async function beginProviderMonitorIdentityReservation(input: {
437
+ auth: OrgAuthContext;
438
+ monitorKey: string;
439
+ tool: string;
440
+ payload: Record<string, unknown> | null | undefined;
441
+ /**
442
+ * Resume rather than wait when THIS monitor key already holds a provisioning
443
+ * claim. Only for callers that serialise their own passes for a member -
444
+ * see `reclaimSelfProvisioning` on the mutation.
445
+ */
446
+ reclaimSelfProvisioning?: boolean;
447
+ }): Promise<ProviderMonitorIdentityReservation | null> {
448
+ const claim = providerMonitorIdentityClaim(input);
449
+ if (!claim) return null;
450
+ await ensureProviderMonitorIdentityBackfill({ auth: input.auth });
451
+ const operationId = randomUUID();
452
+ const result = await getRequiredInternalConvexClient().mutation(
453
+ internal.providerMonitorIdentityClaims.begin,
454
+ {
455
+ orgId: input.auth.orgId,
456
+ tool: claim.tool,
457
+ identityKey: claim.identityKey,
458
+ monitorKey: input.monitorKey,
459
+ operationId,
460
+ ...(input.reclaimSelfProvisioning
461
+ ? { reclaimSelfProvisioning: true }
462
+ : {}),
463
+ },
464
+ );
465
+ if (result.kind === 'acquired') {
466
+ return {
467
+ kind: 'acquired',
468
+ claim,
469
+ restoredState: result.restoredState,
470
+ operationId,
471
+ };
472
+ }
473
+ if (result.kind === 'occupied') {
474
+ return { ...result, claim };
475
+ }
476
+ return { ...result, claim };
477
+ }
478
+
479
+ async function mutateClaim(
480
+ action: 'activate' | 'release',
481
+ input: {
482
+ auth: OrgAuthContext;
483
+ monitorKey: string;
484
+ claim: ProviderMonitorIdentityClaim;
485
+ operationId?: string;
486
+ },
487
+ ) {
488
+ return getRequiredInternalConvexClient().mutation(
489
+ internal.providerMonitorIdentityClaims[action],
490
+ {
491
+ orgId: input.auth.orgId,
492
+ tool: input.claim.tool,
493
+ identityKey: input.claim.identityKey,
494
+ monitorKey: input.monitorKey,
495
+ ...(input.operationId ? { operationId: input.operationId } : {}),
496
+ },
497
+ );
498
+ }
499
+
500
+ export function activateProviderMonitorIdentityReservation(input: {
501
+ auth: OrgAuthContext;
502
+ monitorKey: string;
503
+ claim: ProviderMonitorIdentityClaim;
504
+ operationId: string;
505
+ }) {
506
+ return mutateClaim('activate', input);
507
+ }
508
+
509
+ export function releaseProviderMonitorIdentityClaim(input: {
510
+ auth: OrgAuthContext;
511
+ monitorKey: string;
512
+ claim: ProviderMonitorIdentityClaim;
513
+ }) {
514
+ return mutateClaim('release', input);
515
+ }
516
+
517
+ export function failProviderMonitorIdentityReservation(input: {
518
+ auth: OrgAuthContext;
519
+ monitorKey: string;
520
+ reservation: Extract<
521
+ ProviderMonitorIdentityReservation,
522
+ { kind: 'acquired' }
523
+ >;
524
+ }) {
525
+ return getRequiredInternalConvexClient().mutation(
526
+ internal.providerMonitorIdentityClaims.fail,
527
+ {
528
+ orgId: input.auth.orgId,
529
+ tool: input.reservation.claim.tool,
530
+ identityKey: input.reservation.claim.identityKey,
531
+ monitorKey: input.monitorKey,
532
+ operationId: input.reservation.operationId,
533
+ },
534
+ );
535
+ }
@@ -0,0 +1,16 @@
1
+ const LEGACY_STORED_PROVIDER_MONITOR_CAPABILITY_ALIASES: Readonly<
2
+ Record<string, string>
3
+ > = {
4
+ 'tamradar.company_radar': 'deepline_native.company_radar',
5
+ 'tamradar.contact_radar': 'deepline_native.contact_radar',
6
+ 'tamradar.industry_radar': 'deepline_native.industry_radar',
7
+ };
8
+
9
+ export function canonicalizeStoredProviderMonitorCapabilityId(
10
+ providerCapability: string,
11
+ ): string {
12
+ return (
13
+ LEGACY_STORED_PROVIDER_MONITOR_CAPABILITY_ALIASES[providerCapability] ??
14
+ providerCapability
15
+ );
16
+ }
@@ -0,0 +1,56 @@
1
+ import type { ProviderMonitorCapabilityDefinition } from '@deepline/monitors/provider-monitor-capability';
2
+ import type { ProviderMonitorStatus } from './provider-monitor-status';
3
+
4
+ export const PRIORITY_MONITOR_EXECUTION_TYPE = 'priority' as const;
5
+ export const MAX_PRIORITY_MONITORS_PER_ORG = 10;
6
+
7
+ export const PRIORITY_MONITOR_SLOT_STATUSES = [
8
+ 'active',
9
+ // A billing suspension writes disabled before upstream deprovisioning. Keep
10
+ // the slot occupied until that delete confirms success and clears the
11
+ // projection; a failed cleanup can leave the priority radar live.
12
+ 'disabled',
13
+ 'deleting',
14
+ 'error',
15
+ 'deleted',
16
+ // A fleet monitor holds its priority reservation from the commit that
17
+ // records the desired state, not from the provider create that follows it.
18
+ 'pending_create',
19
+ ] as const satisfies readonly ProviderMonitorStatus[];
20
+
21
+ const PRIORITY_MONITOR_SLOT_STATUS_SET = new Set<ProviderMonitorStatus>(
22
+ PRIORITY_MONITOR_SLOT_STATUSES,
23
+ );
24
+
25
+ export function requestedPriorityMonitorExecution(definition: {
26
+ controls?: Record<string, unknown>;
27
+ }) {
28
+ return (
29
+ definition.controls?.execution_type === PRIORITY_MONITOR_EXECUTION_TYPE
30
+ );
31
+ }
32
+
33
+ export function priorityMonitorExecutionIsSupported(
34
+ capability: ProviderMonitorCapabilityDefinition,
35
+ ) {
36
+ return capability.provider === 'deepline_native';
37
+ }
38
+
39
+ export function priorityMonitorConsumesSlot(input: {
40
+ priorityExecution: boolean;
41
+ status: ProviderMonitorStatus;
42
+ }) {
43
+ return (
44
+ input.priorityExecution &&
45
+ PRIORITY_MONITOR_SLOT_STATUS_SET.has(input.status)
46
+ );
47
+ }
48
+
49
+ export function priorityMonitorReservedSlotCount(input: {
50
+ priorityExecution: boolean;
51
+ status: ProviderMonitorStatus;
52
+ priorityExecutionSlots?: number;
53
+ }) {
54
+ if (!priorityMonitorConsumesSlot(input)) return 0;
55
+ return input.priorityExecutionSlots === 2 ? 2 : 1;
56
+ }
@@ -0,0 +1,28 @@
1
+ import {
2
+ ANALYTICS_INTEGRATION_SLUG,
3
+ ANALYTICS_PUBLIC_PROVIDER,
4
+ ANALYTICS_PUBLIC_TOOL,
5
+ } from '@/lib/analytics/public';
6
+
7
+ export const ANALYTICS_MONITOR_KEY = ANALYTICS_INTEGRATION_SLUG;
8
+ export const ANALYTICS_INTERNAL_MONITOR_KEY = 'snitcher-website-sessions';
9
+ export const ANALYTICS_INTERNAL_TOOL = 'snitcher.website_sessions';
10
+
11
+ export function canonicalProviderMonitorKey(key: string) {
12
+ return key === ANALYTICS_MONITOR_KEY ? ANALYTICS_INTERNAL_MONITOR_KEY : key;
13
+ }
14
+
15
+ /** Convert a customer-facing monitor tool id to its internal registry id. */
16
+ export function canonicalProviderMonitorTool(tool: string) {
17
+ return tool === ANALYTICS_PUBLIC_TOOL ? ANALYTICS_INTERNAL_TOOL : tool;
18
+ }
19
+
20
+ /** Convert an internal registry id to the stable customer-facing tool id. */
21
+ export function publicProviderMonitorTool(tool: string) {
22
+ return tool === ANALYTICS_INTERNAL_TOOL ? ANALYTICS_PUBLIC_TOOL : tool;
23
+ }
24
+
25
+ /** Convert an internal provider name to the stable customer-facing name. */
26
+ export function publicProviderMonitorProvider(provider: string) {
27
+ return provider === 'snitcher' ? ANALYTICS_PUBLIC_PROVIDER : provider;
28
+ }
@@ -0,0 +1,38 @@
1
+ type ProviderMonitorCallbackRuntimeRow = {
2
+ status: string;
3
+ billing_state: unknown;
4
+ };
5
+
6
+ function billingStateKind(value: unknown) {
7
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
8
+ const state = (value as Record<string, unknown>).state;
9
+ return typeof state === 'string' && state.trim() ? state.trim() : null;
10
+ }
11
+
12
+ /** True when an admitted callback is allowed to write an event for this monitor. */
13
+ export function isProviderMonitorCallbackWriteEnabled(
14
+ row: Pick<ProviderMonitorCallbackRuntimeRow, 'status'>,
15
+ ) {
16
+ return row.status === 'active';
17
+ }
18
+
19
+ /**
20
+ * Matches the Customer DB callback lookup predicate for a row that has already
21
+ * been selected from the Convex callback-token projection.
22
+ */
23
+ export function isProviderMonitorCallbackRuntimeEligible(
24
+ row: ProviderMonitorCallbackRuntimeRow,
25
+ ) {
26
+ if (isProviderMonitorCallbackWriteEnabled(row)) {
27
+ return true;
28
+ }
29
+ return (
30
+ row.status === 'disabled' &&
31
+ (billingStateKind(row.billing_state) === 'upstream_disable_pending' ||
32
+ billingStateKind(row.billing_state) === 'upstream_disable_failed' ||
33
+ // A credit suspension turns the provider resource off but does not turn
34
+ // off receipt capture. The inbound processor stores raw provenance and
35
+ // returns its explicit billing-suspended outcome without typed writes.
36
+ billingStateKind(row.billing_state) === 'billing_suspended')
37
+ );
38
+ }