deepline 0.3.85 → 0.3.87

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 (65) hide show
  1. package/dist/bundling-sources/sdk/src/client.ts +2 -2
  2. package/dist/bundling-sources/sdk/src/monitor-fleet-contract.ts +1 -1
  3. package/dist/bundling-sources/sdk/src/plays/bundle-play-file.ts +1 -1
  4. package/dist/bundling-sources/sdk/src/release.ts +1 -1
  5. package/dist/bundling-sources/shared_libs/integration-contracts/AGENTS.md +9 -0
  6. package/dist/bundling-sources/shared_libs/integration-contracts/README.md +3 -0
  7. package/dist/bundling-sources/shared_libs/integration-contracts/index.ts +2 -0
  8. package/dist/bundling-sources/shared_libs/monitors/analytics-install-guides.ts +77 -0
  9. package/dist/bundling-sources/shared_libs/monitors/billing/charges.ts +125 -0
  10. package/dist/bundling-sources/shared_libs/monitors/billing/policy-builders.ts +686 -0
  11. package/dist/bundling-sources/shared_libs/monitors/billing/pricing.ts +54 -0
  12. package/dist/bundling-sources/shared_libs/monitors/billing/public-pricing.ts +72 -0
  13. package/dist/bundling-sources/shared_libs/monitors/billing/session.ts +45 -0
  14. package/dist/bundling-sources/shared_libs/monitors/billing/state.ts +454 -0
  15. package/dist/bundling-sources/shared_libs/monitors/billing/tamradar.ts +260 -0
  16. package/dist/bundling-sources/shared_libs/monitors/billing/types.ts +215 -0
  17. package/dist/bundling-sources/shared_libs/monitors/callback-security.ts +333 -0
  18. package/dist/bundling-sources/shared_libs/monitors/cursor-page.ts +42 -0
  19. package/dist/bundling-sources/shared_libs/monitors/framework-managed-payload-fields.ts +71 -0
  20. package/dist/bundling-sources/shared_libs/monitors/generated/provider-monitor-callback-admission-targets.ts +16 -0
  21. package/dist/bundling-sources/shared_libs/monitors/http-error-response.ts +267 -0
  22. package/dist/bundling-sources/shared_libs/monitors/inbound-publish-key.ts +39 -0
  23. package/dist/bundling-sources/shared_libs/monitors/inbound-queue.ts +82 -0
  24. package/dist/bundling-sources/shared_libs/monitors/lifecycle-notifications.ts +473 -0
  25. package/dist/bundling-sources/shared_libs/monitors/org-monitor-limits.ts +1 -1
  26. package/dist/bundling-sources/shared_libs/monitors/package.json +29 -0
  27. package/dist/bundling-sources/shared_libs/monitors/provider-callback-verification.ts +1125 -0
  28. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-capability.ts +964 -0
  29. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-consumer-health.ts +56 -0
  30. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-errors.ts +131 -0
  31. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-identity-claims.ts +527 -0
  32. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-legacy-aliases.ts +16 -0
  33. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-priority-execution.ts +56 -0
  34. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-public-aliases.ts +28 -0
  35. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-runtime-read-eligibility.ts +38 -0
  36. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-status.ts +10 -0
  37. package/dist/bundling-sources/shared_libs/monitors/provider-monitor-upstream-identity.ts +112 -0
  38. package/dist/bundling-sources/shared_libs/monitors/provider-webhook-monitor-runtime.ts +300 -0
  39. package/dist/bundling-sources/shared_libs/monitors/quickstart-prompts.ts +22 -0
  40. package/dist/bundling-sources/shared_libs/monitors/snitcher-install-validation.ts +93 -0
  41. package/dist/bundling-sources/shared_libs/monitors/snitcher-ip2company.ts +332 -0
  42. package/dist/bundling-sources/shared_libs/monitors/snitcher-radar.ts +221 -0
  43. package/dist/bundling-sources/shared_libs/monitors/starter-templates.ts +105 -0
  44. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-guidance.ts +73 -0
  45. package/dist/bundling-sources/shared_libs/monitors/tracking-provider-integrations.ts +81 -0
  46. package/dist/bundling-sources/shared_libs/monitors/tsconfig.json +10 -0
  47. package/dist/bundling-sources/shared_libs/play-runtime/runtime-env.ts +6 -0
  48. package/dist/bundling-sources/shared_libs/play-runtime/tool-http-errors.ts +1 -1
  49. package/dist/bundling-sources/shared_libs/play-runtime/tool-result.ts +1 -1
  50. package/dist/bundling-sources/shared_libs/plays/authoring-contract.ts +302 -0
  51. package/dist/bundling-sources/shared_libs/plays/email-status.ts +1 -1
  52. package/dist/bundling-sources/shared_libs/plays/tool-category-descriptions.ts +2 -2
  53. package/dist/bundling-sources/shared_libs/runtime-env.ts +6 -0
  54. package/dist/bundling-sources/shared_libs/security/header-safe-env.ts +28 -0
  55. package/dist/bundling-sources/shared_libs/security/secret-redaction.ts +267 -0
  56. package/dist/cli/index.js +25 -14
  57. package/dist/cli/index.mjs +25 -14
  58. package/dist/index.d.mts +1 -1
  59. package/dist/index.d.ts +1 -1
  60. package/dist/index.js +3 -3
  61. package/dist/index.mjs +3 -3
  62. package/dist/install-integrity.json +45 -2
  63. package/package.json +3 -1
  64. /package/dist/bundling-sources/shared_libs/{integrations/bettercontact-execution-policy.ts → integration-contracts/bettercontact/execution-policy.ts} +0 -0
  65. /package/dist/bundling-sources/shared_libs/{integrations/theirstack-execution-policy.ts → integration-contracts/theirstack/execution-policy.ts} +0 -0
@@ -0,0 +1,964 @@
1
+ import {
2
+ monitorSdkIssue,
3
+ type MonitorSdkValidationIssue,
4
+ } from '@deepline/monitors/validation';
5
+
6
+ export type ProviderMonitorCapabilityOutput = {
7
+ table: string;
8
+ rowType: string;
9
+ description?: string;
10
+ condition?: {
11
+ payload?: Record<string, unknown>;
12
+ };
13
+ };
14
+
15
+ export type ProviderMonitorSubjectPathCandidate = {
16
+ path: string;
17
+ subjectType: string;
18
+ description?: string;
19
+ };
20
+
21
+ export type ProviderMonitorSubjectDerivation = {
22
+ kind: 'payload_path';
23
+ candidates: ProviderMonitorSubjectPathCandidate[];
24
+ };
25
+
26
+ export type ProviderMonitorSubjectContract = {
27
+ types: string[];
28
+ derivation: ProviderMonitorSubjectDerivation;
29
+ };
30
+
31
+ /**
32
+ * Customer-visible event category for one deployed monitor. Providers own this
33
+ * mapping in their monitor bundle; shared monitor code only evaluates it.
34
+ */
35
+ export type ProviderMonitorTypeDerivation =
36
+ | { kind: 'payload_path'; path: string }
37
+ | { kind: 'static'; value: string };
38
+
39
+ // A single conditional-requirement branch: when the payload matches `if`
40
+ // (property === const, ANDed across entries), the fields in `then.required` are
41
+ // required. This is the JSON-schema `if/then` idiom, scoped to the subset the
42
+ // monitor validator needs (const-equality guards + a required list). It lets a
43
+ // payloadSchema express requirements that depend on another field's value —
44
+ // e.g. TAMRadar contact_radar requires `domain` only when
45
+ // `radar_type === 'contact_job_changes'` — without a bespoke validation hook.
46
+ export type ProviderMonitorCapabilityConditionalRequirement = {
47
+ if: {
48
+ properties: Record<string, { const: unknown }>;
49
+ };
50
+ then: {
51
+ required: string[];
52
+ };
53
+ };
54
+
55
+ export type ProviderMonitorCapabilityPayloadSchema = {
56
+ type: 'object';
57
+ additionalProperties?: boolean;
58
+ required?: string[];
59
+ properties?: Record<string, ProviderMonitorCapabilitySchemaProperty>;
60
+ allOf?: ProviderMonitorCapabilityConditionalRequirement[];
61
+ };
62
+
63
+ export type ProviderMonitorCapabilitySchemaProperty = {
64
+ type?: 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object';
65
+ /** JSON Schema string format, passed through from an OpenAPI-derived payload. */
66
+ format?: string;
67
+ description?: string;
68
+ enum?: readonly unknown[];
69
+ pattern?: string;
70
+ minItems?: number;
71
+ items?: ProviderMonitorCapabilitySchemaProperty;
72
+ properties?: Record<string, ProviderMonitorCapabilitySchemaProperty>;
73
+ additionalProperties?: boolean;
74
+ required?: string[];
75
+ };
76
+
77
+ /**
78
+ * Provider-owned validation that cannot be represented in the monitor payload
79
+ * JSON schema alone, such as a provider's boolean-expression grammar.
80
+ */
81
+ export type ProviderMonitorCapabilityPayloadValidator = (input: {
82
+ payload: Record<string, unknown>;
83
+ entityKey: string;
84
+ }) => MonitorSdkValidationIssue[];
85
+
86
+ export type ProviderMonitorCapabilityExample = {
87
+ label: string;
88
+ definition: {
89
+ key: string;
90
+ name?: string;
91
+ tool: string;
92
+ payload?: Record<string, unknown>;
93
+ controls?: Record<string, unknown>;
94
+ };
95
+ };
96
+
97
+ /**
98
+ * Factual, customer-visible monitor reference data. This is deliberately data,
99
+ * not TSDoc: the monitor validator, live tools surface, CLI, and generated
100
+ * references all consume the same capability object.
101
+ */
102
+ export type ProviderMonitorCapabilityDocumentation = {
103
+ fields: Record<
104
+ string,
105
+ {
106
+ semantics: string;
107
+ applicability?: string;
108
+ grammar?: {
109
+ syntax: string;
110
+ precedence?: readonly string[];
111
+ example?: string;
112
+ examples?: readonly string[];
113
+ };
114
+ precedence?: string;
115
+ }
116
+ >;
117
+ pricing: { presentation: string };
118
+ identity: { semantics: string; update: string; backfill: string };
119
+ troubleshooting: Array<{ symptom: string; guidance: string }>;
120
+ };
121
+
122
+ /** Lifecycle facts shared by every monitor type; render once, never per provider. */
123
+ export const PROVIDER_MONITOR_SHARED_DOCUMENTATION = {
124
+ lifecycle: [
125
+ 'monitors check validates a definition locally and does not deploy, spend credits, or prove a future event will arrive.',
126
+ 'monitors deploy is a full desired definition. Use deploy --dry-run to inspect provider and Deepline-credit effects; use monitors update for a patch.',
127
+ 'monitors get returns the stored deployed definition plus monitor_spec, whose fields list the deployable payload paths, descriptions, constraints, and provider-specific semantics for that monitor type.',
128
+ ],
129
+ errorMeanings: [
130
+ 'Validation errors name the payload path and expected contract value; correct the definition and rerun monitors check.',
131
+ 'A paused monitor needs a balance or entitlement correction before reactivation. Deepline exposes Deepline pricing only.',
132
+ ],
133
+ } as const;
134
+
135
+ export type ProviderMonitorInboundAuthDescriptor =
136
+ | { mode: 'hmac_sha256'; secretSource: 'provider' | 'deepline' }
137
+ | { mode: 'body_secret'; secretSource: 'deepline' }
138
+ | {
139
+ mode: 'custom_header';
140
+ secretSource: 'deepline';
141
+ headerName: string;
142
+ }
143
+ | { mode: 'callback_token' };
144
+
145
+ /**
146
+ * Provider-owned instructions shown only after Deepline has minted a callback
147
+ * endpoint for a monitor. `{{callback_url}}` is interpolated at read time; the
148
+ * opaque URL itself is deliberately never stored in this metadata.
149
+ */
150
+ export type ProviderMonitorPostDeployGuidance = {
151
+ title: string;
152
+ steps: string[];
153
+ documentation: Array<{ label: string; url: string }>;
154
+ event_type?: string;
155
+ payload_example?: Record<string, unknown>;
156
+ };
157
+
158
+ export type ProviderMonitorCapabilityDefinition = {
159
+ id: string;
160
+ provider: string;
161
+ capability: string;
162
+ providerCapability: string;
163
+ displayName: string;
164
+ description: string;
165
+ payloadSchema: ProviderMonitorCapabilityPayloadSchema;
166
+ validatePayload?: ProviderMonitorCapabilityPayloadValidator;
167
+ /**
168
+ * A narrow compatibility declaration for payload fields whose validation was
169
+ * tightened after customer monitors were already stored. It applies only
170
+ * when Deepline recognizes an unchanged stored definition; new definitions
171
+ * and changed payloads remain fully strict.
172
+ */
173
+ legacyPayloadValidation?: {
174
+ grandfatheredPaths: readonly string[];
175
+ /** Generic schema issue codes eligible for those same legacy paths. */
176
+ grandfatheredSchemaIssueCodes?: readonly string[];
177
+ };
178
+ payloadExamples: ProviderMonitorCapabilityExample[];
179
+ documentation: ProviderMonitorCapabilityDocumentation;
180
+ postDeployGuidance?: ProviderMonitorPostDeployGuidance;
181
+ /** Friendly provider-specific setup target, e.g. deepline.analytics. */
182
+ setupAlias?: string;
183
+ /**
184
+ * The payload fields that define WHAT this monitor watches (its coverage
185
+ * scope), used by reuse detection to warn before deploying a duplicate
186
+ * monitor. Field names MUST be real `payloadSchema.properties` keys.
187
+ * Undefined means tool-level coverage: any same-tool monitor is a reuse
188
+ * candidate (used when the payload is too fuzzy to compare structurally,
189
+ * e.g. TheirStack saved-search filter bodies). See
190
+ * `MONITOR_COVERAGE_KEY_FIELDS` in provider-monitor-capabilities.ts for the
191
+ * central declarations of existing capabilities.
192
+ */
193
+ coverageKeyFields?: readonly string[];
194
+ /**
195
+ * Provider-account identity for this monitor. Shared monitor code checks it
196
+ * against Convex before calling the upstream provider.
197
+ */
198
+ upstreamCollision?: {
199
+ identityKeyFields: readonly string[];
200
+ /**
201
+ * This provider can create an updated resource for the same Deepline
202
+ * monitor key before the prior upstream resource is deprovisioned. This
203
+ * never permits a second key to occupy the same identity in one org.
204
+ */
205
+ supportsSameIdentityReplacement?: boolean;
206
+ };
207
+ pricing: Record<string, unknown>;
208
+ subject: ProviderMonitorSubjectContract;
209
+ /**
210
+ * The generic, customer-visible event type for this monitor. Use a payload
211
+ * path when the provider lets the deployed definition select the type.
212
+ */
213
+ type: ProviderMonitorTypeDerivation;
214
+ runtime: {
215
+ kind:
216
+ | 'provider_native_monitor'
217
+ | 'provider_webhook_subscription'
218
+ // The provider dashboard owns webhook creation. Deepline owns the
219
+ // unguessable callback endpoint and tells the customer where to paste it.
220
+ | 'provider_manual_webhook';
221
+ deployAction?: string;
222
+ statusAction?: string;
223
+ disableAction?: string;
224
+ deleteAction?: string;
225
+ pollAction?: string;
226
+ /** Shared provider-governor bucket for scalar lifecycle work. */
227
+ rateLimitId?: string;
228
+ /** Additional ordered provider actions owned by a managed deploy plan. */
229
+ managedDeployActions?: readonly string[];
230
+ /** Internal diagnostics and lifecycle actions owned by this capability. */
231
+ supportActions?: readonly string[];
232
+ /**
233
+ * Optional asynchronous batch-create transport. This is intentionally a
234
+ * capability declaration rather than a Fleet special case: fleets and a
235
+ * future ordinary `monitors create-batch` API both ask the same adapter
236
+ * registry whether a capability can be submitted in batches.
237
+ *
238
+ * `adapter` identifies provider-owned wire parsing/mapping code. The
239
+ * remaining fields are declarative capability facts and are checked before
240
+ * a provider call is made.
241
+ */
242
+ batchCreate?: {
243
+ adapter: string;
244
+ createAction: string;
245
+ statusAction: string;
246
+ rateLimitId: string;
247
+ };
248
+ /**
249
+ * Optional synchronous batch-cleanup transport. This follows the same
250
+ * capability-owned adapter boundary as batch creation so Fleet removal and
251
+ * a future ordinary monitor batch API never grow provider-specific paths.
252
+ */
253
+ batchDelete?: {
254
+ adapter: string;
255
+ deleteAction: string;
256
+ rateLimitId: string;
257
+ };
258
+ /**
259
+ * Optional batch READ transport: ask the provider, by id, what it holds.
260
+ *
261
+ * This is what makes the audit verb provider-generic. The verb pages
262
+ * monitors, chunks their resource ids, and asks; the only per-provider
263
+ * facts are which action answers, which parameter carries the ids, and how
264
+ * many ids one request may name. `maxIds` is a DOCUMENTED provider
265
+ * maximum, not a tuning knob - it belongs here rather than as a literal in
266
+ * the walker because two walkers with two literals is how the number drifts
267
+ * away from the contract it came from.
268
+ *
269
+ * A capability with no `batchRead` is not auditable, and the verb says so
270
+ * rather than guessing from a full listing: absence of a resource from a
271
+ * page that was never asked about it is not evidence.
272
+ */
273
+ batchRead?: {
274
+ adapter: string;
275
+ readAction: string;
276
+ /** Request parameter that carries the comma-separated resource ids. */
277
+ idsParam: string;
278
+ /** Ids one request may name, per the provider's published contract. */
279
+ maxIds: number;
280
+ rateLimitId: string;
281
+ };
282
+ // Declares that this monitor delivers data to Deepline via an inbound
283
+ // webhook callback (handled by the provider-callbacks route). Webhook
284
+ // subscription monitors are inherently inbound; native monitors must set
285
+ // this explicitly when they post callbacks (e.g. theirstack, tamradar) so
286
+ // the endpoint-coverage gate can require a registered inbound handler.
287
+ inbound?: 'webhook';
288
+ // Authentication contract for callbacks on the Deepline-owned endpoint.
289
+ // This lives with the provider capability so deploy and receive cannot drift
290
+ // into separate provider allowlists or invented signature conventions.
291
+ inboundAuth?: ProviderMonitorInboundAuthDescriptor;
292
+ // For native `inbound: 'webhook'` monitors, declares HOW OUR Deepline
293
+ // callback URL reaches the upstream provider at deploy time. The
294
+ // endpoint-coverage gate keys on this so a webhook-push monitor cannot ship
295
+ // without a registered way to hand the provider our endpoint:
296
+ // - 'per_monitor_create_url': the create call takes a per-monitor callback
297
+ // URL; the capability MUST register a `webhookRegistration` descriptor
298
+ // (e.g. Findymail `webhook_url`).
299
+ // - 'managed_deploy_action': a bespoke multi-step deploy action wires our
300
+ // URL into a dedicated webhook resource (e.g. TheirStack saved-search +
301
+ // webhook).
302
+ // - 'account_marker': the provider posts to an account-level webhook and
303
+ // events are correlated by a marker, not a per-monitor URL (e.g.
304
+ // TamRadar custom_fields). No per-monitor URL is registered.
305
+ webhookRegistrationStrategy?:
306
+ | 'per_monitor_create_url'
307
+ | 'managed_deploy_action'
308
+ | 'account_marker';
309
+ };
310
+ outputs: Record<string, ProviderMonitorCapabilityOutput>;
311
+ lifecycle: {
312
+ check: boolean;
313
+ deploy: boolean;
314
+ status: boolean;
315
+ disable: boolean;
316
+ };
317
+ };
318
+
319
+ export type ProviderMonitorCapabilityPublicLifecycle = {
320
+ check: boolean;
321
+ deploy: boolean;
322
+ status: boolean;
323
+ update: boolean;
324
+ delete: boolean;
325
+ };
326
+
327
+ export type ProviderMonitorCapabilityEntitlementStatus = {
328
+ /** True when this capability is gated behind a Deepline add-on entitlement. */
329
+ required: boolean;
330
+ /** True when the entitlement is required but not active for the requesting org. */
331
+ locked: boolean;
332
+ entitlementId?: string;
333
+ priceUsdMonthly?: number | null;
334
+ includedActiveMonitors?: number | null;
335
+ /** Deepline-facing reason shown when locked (never exposes provider spend). */
336
+ reason?: string;
337
+ };
338
+
339
+ const UNLOCKED_ENTITLEMENT_STATUS: ProviderMonitorCapabilityEntitlementStatus =
340
+ {
341
+ required: false,
342
+ locked: false,
343
+ };
344
+
345
+ export type ProviderMonitorCapabilitySerializable = {
346
+ provider: string;
347
+ tool: string;
348
+ display_name: string;
349
+ description: string;
350
+ payload: {
351
+ schema: ProviderMonitorCapabilityPayloadSchema;
352
+ examples: ProviderMonitorCapabilityExample[];
353
+ };
354
+ payload_schema: ProviderMonitorCapabilityPayloadSchema;
355
+ payload_examples: ProviderMonitorCapabilityExample[];
356
+ documentation: ProviderMonitorCapabilityDocumentation;
357
+ outputs: Record<string, ProviderMonitorCapabilityOutput>;
358
+ subject: ProviderMonitorSubjectContract;
359
+ type_derivation: ProviderMonitorTypeDerivation;
360
+ lifecycle: ProviderMonitorCapabilityPublicLifecycle;
361
+ billing: Record<string, unknown>;
362
+ deployable: boolean;
363
+ diagnostics: Array<{
364
+ code: string;
365
+ path: string;
366
+ message: string;
367
+ suggestion?: string;
368
+ }>;
369
+ entitlement: {
370
+ required: boolean;
371
+ locked: boolean;
372
+ entitlement_id?: string;
373
+ price_usd_monthly?: number | null;
374
+ included_active_monitors?: number | null;
375
+ reason?: string;
376
+ };
377
+ next_commands: string[];
378
+ };
379
+
380
+ export function providerMonitorCapabilityId(input: {
381
+ provider: string;
382
+ capability: string;
383
+ }) {
384
+ return `${input.provider}.${input.capability}`;
385
+ }
386
+
387
+ export function parseProviderMonitorCapabilityId(value: string) {
388
+ const trimmed = value.trim();
389
+ const [provider, capability, ...extra] = trimmed.split('.');
390
+ if (!provider || !capability || extra.length > 0) return null;
391
+ return {
392
+ provider,
393
+ capability,
394
+ providerCapability: providerMonitorCapabilityId({ provider, capability }),
395
+ };
396
+ }
397
+
398
+ export function defineProviderMonitorCapability(input: {
399
+ provider: string;
400
+ capability: string;
401
+ displayName: string;
402
+ description: string;
403
+ payloadSchema: ProviderMonitorCapabilityPayloadSchema;
404
+ validatePayload?: ProviderMonitorCapabilityPayloadValidator;
405
+ legacyPayloadValidation?: ProviderMonitorCapabilityDefinition['legacyPayloadValidation'];
406
+ payloadExamples: ProviderMonitorCapabilityExample[];
407
+ documentation?: ProviderMonitorCapabilityDocumentation;
408
+ postDeployGuidance?: ProviderMonitorPostDeployGuidance;
409
+ setupAlias?: string;
410
+ coverageKeyFields?: readonly string[];
411
+ upstreamCollision?: ProviderMonitorCapabilityDefinition['upstreamCollision'];
412
+ pricing: Record<string, unknown>;
413
+ subject: ProviderMonitorSubjectContract;
414
+ type?: ProviderMonitorTypeDerivation;
415
+ runtime: ProviderMonitorCapabilityDefinition['runtime'];
416
+ outputs: Record<string, ProviderMonitorCapabilityOutput>;
417
+ lifecycle: ProviderMonitorCapabilityDefinition['lifecycle'];
418
+ }): ProviderMonitorCapabilityDefinition {
419
+ const providerCapability = providerMonitorCapabilityId(input);
420
+ const fields = Object.fromEntries(
421
+ Object.entries(input.payloadSchema.properties ?? {}).map(
422
+ ([field, schema]) => [
423
+ field,
424
+ {
425
+ semantics:
426
+ schema.description ?? `Provider-specific ${field} monitor filter.`,
427
+ },
428
+ ],
429
+ ),
430
+ );
431
+ return {
432
+ ...input,
433
+ id: providerCapability,
434
+ providerCapability,
435
+ // Existing monitor bundles predate the generic type contract. Their
436
+ // capability is still an accurate, stable category until they opt into a
437
+ // provider-specific payload mapping.
438
+ type: input.type ?? { kind: 'static', value: input.capability },
439
+ documentation: input.documentation ?? {
440
+ fields,
441
+ pricing: {
442
+ presentation:
443
+ 'Use the Deepline pricing returned by tools get, monitors available, check, or deploy --dry-run. Provider spend is not exposed.',
444
+ },
445
+ identity: {
446
+ semantics: input.upstreamCollision
447
+ ? `One monitor identity uses ${input.upstreamCollision.identityKeyFields.join(', ')}.`
448
+ : 'This monitor uses the provider capability identity declared by Deepline.',
449
+ update:
450
+ 'Use monitors update for a patch or deploy for a complete desired definition.',
451
+ backfill:
452
+ 'Existing Customer DB rows are retained; this capability does not promise provider backfill unless its provider documentation says otherwise.',
453
+ },
454
+ troubleshooting: [
455
+ {
456
+ symptom: 'Validation failed',
457
+ guidance:
458
+ 'Correct the reported payload path, then run monitors check again.',
459
+ },
460
+ ],
461
+ },
462
+ };
463
+ }
464
+
465
+ /**
466
+ * Resolves the generic monitor `type` without leaking provider-specific field
467
+ * names into registry, API, or CLI code. A malformed or legacy payload returns
468
+ * null rather than guessing; callers can state that the type is unknown.
469
+ */
470
+ export function deriveProviderMonitorType(
471
+ capability: ProviderMonitorCapabilityDefinition,
472
+ payload: Record<string, unknown>,
473
+ ): string | null {
474
+ if (capability.type.kind === 'static') {
475
+ const value = capability.type.value.trim();
476
+ return value || null;
477
+ }
478
+ let value: unknown = payload;
479
+ for (const segment of capability.type.path.split('.')) {
480
+ if (
481
+ !segment ||
482
+ segment === '__proto__' ||
483
+ segment === 'prototype' ||
484
+ segment === 'constructor' ||
485
+ !value ||
486
+ typeof value !== 'object' ||
487
+ Array.isArray(value)
488
+ ) {
489
+ return null;
490
+ }
491
+ value = (value as Record<string, unknown>)[segment];
492
+ }
493
+ return typeof value === 'string' && value.trim() ? value.trim() : null;
494
+ }
495
+
496
+ export function isProviderMonitorCapabilityDeployable(
497
+ capability: ProviderMonitorCapabilityDefinition,
498
+ ) {
499
+ return Boolean(
500
+ capability.lifecycle.deploy &&
501
+ (capability.runtime.deployAction ||
502
+ capability.runtime.kind === 'provider_manual_webhook'),
503
+ );
504
+ }
505
+
506
+ function providerMonitorCapabilityHasCleanupAction(
507
+ capability: ProviderMonitorCapabilityDefinition,
508
+ ) {
509
+ return Boolean(
510
+ capability.runtime.deleteAction ||
511
+ capability.runtime.disableAction ||
512
+ capability.runtime.kind === 'provider_manual_webhook',
513
+ );
514
+ }
515
+
516
+ function publicLifecycle(
517
+ capability: ProviderMonitorCapabilityDefinition,
518
+ ): ProviderMonitorCapabilityPublicLifecycle {
519
+ const deployable = isProviderMonitorCapabilityDeployable(capability);
520
+ const cleanup = providerMonitorCapabilityHasCleanupAction(capability);
521
+ return {
522
+ check: capability.lifecycle.check,
523
+ deploy: deployable,
524
+ status: true,
525
+ update: deployable && cleanup,
526
+ delete: cleanup,
527
+ };
528
+ }
529
+
530
+ function cloneSerializable<T>(value: T): T {
531
+ return JSON.parse(JSON.stringify(value)) as T;
532
+ }
533
+
534
+ function lifecycleDiagnostic(input: {
535
+ capability: ProviderMonitorCapabilityDefinition;
536
+ mode: 'deploy';
537
+ }) {
538
+ const deployActionMissing =
539
+ input.mode === 'deploy' &&
540
+ !input.capability.runtime.deployAction &&
541
+ input.capability.runtime.kind !== 'provider_manual_webhook';
542
+ return {
543
+ code: `provider_monitor_${input.mode}_not_supported`,
544
+ path: 'tool',
545
+ message: `Monitor tool ${input.capability.providerCapability} does not support ${input.mode}.`,
546
+ suggestion:
547
+ deployActionMissing || input.mode === 'deploy'
548
+ ? 'This monitor tool is modeled but its upstream deploy action is not wired yet.'
549
+ : 'Choose a monitor tool with lifecycle support from the platform-admin monitor catalog.',
550
+ };
551
+ }
552
+
553
+ function lifecycleDiagnostics(capability: ProviderMonitorCapabilityDefinition) {
554
+ const diagnostics: ReturnType<typeof lifecycleDiagnostic>[] = [];
555
+ if (!isProviderMonitorCapabilityDeployable(capability)) {
556
+ diagnostics.push(lifecycleDiagnostic({ capability, mode: 'deploy' }));
557
+ }
558
+ return diagnostics;
559
+ }
560
+
561
+ export function serializeProviderMonitorCapability(
562
+ capability: ProviderMonitorCapabilityDefinition,
563
+ entitlement: ProviderMonitorCapabilityEntitlementStatus = UNLOCKED_ENTITLEMENT_STATUS,
564
+ ): ProviderMonitorCapabilitySerializable {
565
+ const payloadSchema = cloneSerializable(capability.payloadSchema);
566
+ const payloadExamples = cloneSerializable(capability.payloadExamples);
567
+ return {
568
+ provider: capability.provider,
569
+ tool: capability.providerCapability,
570
+ display_name: capability.displayName,
571
+ description: capability.description,
572
+ payload: {
573
+ schema: payloadSchema,
574
+ examples: payloadExamples,
575
+ },
576
+ payload_schema: payloadSchema,
577
+ payload_examples: payloadExamples,
578
+ documentation: cloneSerializable(capability.documentation),
579
+ outputs: cloneSerializable(capability.outputs),
580
+ subject: cloneSerializable(capability.subject),
581
+ type_derivation: cloneSerializable(capability.type),
582
+ lifecycle: publicLifecycle(capability),
583
+ billing: cloneSerializable(capability.pricing),
584
+ deployable: isProviderMonitorCapabilityDeployable(capability),
585
+ diagnostics: lifecycleDiagnostics(capability),
586
+ entitlement: {
587
+ required: entitlement.required,
588
+ locked: entitlement.locked,
589
+ ...(entitlement.entitlementId
590
+ ? { entitlement_id: entitlement.entitlementId }
591
+ : {}),
592
+ ...(entitlement.priceUsdMonthly !== undefined
593
+ ? { price_usd_monthly: entitlement.priceUsdMonthly }
594
+ : {}),
595
+ ...(entitlement.includedActiveMonitors !== undefined
596
+ ? { included_active_monitors: entitlement.includedActiveMonitors }
597
+ : {}),
598
+ ...(entitlement.reason ? { reason: entitlement.reason } : {}),
599
+ },
600
+ next_commands: [],
601
+ };
602
+ }
603
+
604
+ function issue(
605
+ input: Omit<MonitorSdkValidationIssue, 'severity'> & {
606
+ severity?: MonitorSdkValidationIssue['severity'];
607
+ },
608
+ ) {
609
+ return monitorSdkIssue(input);
610
+ }
611
+
612
+ function isRecord(value: unknown): value is Record<string, unknown> {
613
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
614
+ }
615
+
616
+ function validationIssueMatchesGrandfatheredPath(input: {
617
+ issue: MonitorSdkValidationIssue;
618
+ paths: readonly string[];
619
+ }) {
620
+ return input.paths.some(
621
+ (path) =>
622
+ input.issue.path === path ||
623
+ input.issue.path.startsWith(`${path}.`) ||
624
+ input.issue.path.startsWith(`${path}[`),
625
+ );
626
+ }
627
+
628
+ function typeName(value: unknown) {
629
+ if (Array.isArray(value)) return 'array';
630
+ if (value === null) return 'null';
631
+ return typeof value;
632
+ }
633
+
634
+ function validateSchemaProperty(input: {
635
+ schema: ProviderMonitorCapabilitySchemaProperty;
636
+ value: unknown;
637
+ path: string;
638
+ entityKey: string;
639
+ }): MonitorSdkValidationIssue[] {
640
+ const issues: MonitorSdkValidationIssue[] = [];
641
+ const schemaType = input.schema.type;
642
+ if (schemaType) {
643
+ const valid =
644
+ schemaType === 'array'
645
+ ? Array.isArray(input.value)
646
+ : schemaType === 'object'
647
+ ? isRecord(input.value)
648
+ : schemaType === 'integer'
649
+ ? Number.isInteger(input.value)
650
+ : typeof input.value === schemaType;
651
+ if (!valid) {
652
+ issues.push(
653
+ issue({
654
+ code: 'invalid_provider_monitor_payload_type',
655
+ path: input.path,
656
+ message: `Provider monitor payload field ${input.path} must be ${schemaType}.`,
657
+ expected: schemaType,
658
+ received: input.value,
659
+ suggestion:
660
+ 'Match the payload_schema returned by the platform-admin monitor tools API.',
661
+ entity_type: 'tool',
662
+ entity_key: input.entityKey,
663
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
664
+ }),
665
+ );
666
+ return issues;
667
+ }
668
+ }
669
+ if (input.schema.enum && !input.schema.enum.includes(input.value)) {
670
+ issues.push(
671
+ issue({
672
+ code: 'invalid_provider_monitor_payload_enum',
673
+ path: input.path,
674
+ message: `Provider monitor payload field ${input.path} must be one of the supported values.`,
675
+ expected: input.schema.enum,
676
+ received: input.value,
677
+ suggestion:
678
+ 'Choose a value from payload_schema.enum for this monitor tool.',
679
+ entity_type: 'tool',
680
+ entity_key: input.entityKey,
681
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
682
+ }),
683
+ );
684
+ }
685
+ if (typeof input.value === 'string' && input.schema.pattern) {
686
+ const pattern = new RegExp(input.schema.pattern);
687
+ if (!pattern.test(input.value)) {
688
+ issues.push(
689
+ issue({
690
+ code: 'invalid_provider_monitor_payload_pattern',
691
+ path: input.path,
692
+ message: `Provider monitor payload field ${input.path} does not match the required pattern.`,
693
+ expected: input.schema.pattern,
694
+ received: input.value,
695
+ suggestion:
696
+ 'Match the payload_schema returned by the platform-admin monitor tools API.',
697
+ entity_type: 'tool',
698
+ entity_key: input.entityKey,
699
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
700
+ }),
701
+ );
702
+ }
703
+ }
704
+ if (Array.isArray(input.value) && input.schema.items) {
705
+ if (
706
+ typeof input.schema.minItems === 'number' &&
707
+ input.value.length < input.schema.minItems
708
+ ) {
709
+ issues.push(
710
+ issue({
711
+ code: 'invalid_provider_monitor_payload_min_items',
712
+ path: input.path,
713
+ message: `Provider monitor payload field ${input.path} has too few items.`,
714
+ expected: input.schema.minItems,
715
+ received: input.value.length,
716
+ entity_type: 'tool',
717
+ entity_key: input.entityKey,
718
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
719
+ }),
720
+ );
721
+ }
722
+ input.value.forEach((item, index) => {
723
+ issues.push(
724
+ ...validateSchemaProperty({
725
+ schema: input.schema.items!,
726
+ value: item,
727
+ path: `${input.path}.${index}`,
728
+ entityKey: input.entityKey,
729
+ }),
730
+ );
731
+ });
732
+ }
733
+ if (isRecord(input.value) && input.schema.type === 'object') {
734
+ issues.push(
735
+ ...validateObjectAgainstSchema({
736
+ schema: {
737
+ type: 'object',
738
+ additionalProperties: input.schema.additionalProperties,
739
+ required: input.schema.required,
740
+ properties: input.schema.properties,
741
+ },
742
+ value: input.value,
743
+ path: input.path,
744
+ entityKey: input.entityKey,
745
+ }),
746
+ );
747
+ }
748
+ return issues;
749
+ }
750
+
751
+ function validateObjectAgainstSchema(input: {
752
+ schema: ProviderMonitorCapabilityPayloadSchema;
753
+ value: Record<string, unknown>;
754
+ path: string;
755
+ entityKey: string;
756
+ }) {
757
+ const issues: MonitorSdkValidationIssue[] = [];
758
+ const properties = input.schema.properties ?? {};
759
+ for (const requiredField of input.schema.required ?? []) {
760
+ if (input.value[requiredField] === undefined) {
761
+ issues.push(
762
+ issue({
763
+ code: 'missing_provider_monitor_payload_field',
764
+ path: `${input.path}.${requiredField}`,
765
+ message: `Provider monitor payload field ${requiredField} is required.`,
766
+ expected: Object.keys(properties).sort(),
767
+ received: undefined,
768
+ suggestion:
769
+ 'Use the platform-admin monitor tools API and provide the required payload fields.',
770
+ entity_type: 'tool',
771
+ entity_key: input.entityKey,
772
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
773
+ }),
774
+ );
775
+ }
776
+ }
777
+ for (const conditional of input.schema.allOf ?? []) {
778
+ const guardMatches = Object.entries(conditional.if.properties).every(
779
+ ([key, guard]) => input.value[key] === guard.const,
780
+ );
781
+ if (!guardMatches) continue;
782
+ const guardDescription = Object.entries(conditional.if.properties)
783
+ .map(([key, guard]) => `${key}=${JSON.stringify(guard.const)}`)
784
+ .join(', ');
785
+ for (const requiredField of conditional.then.required) {
786
+ if (input.value[requiredField] !== undefined) continue;
787
+ issues.push(
788
+ issue({
789
+ code: 'missing_provider_monitor_payload_field',
790
+ path: `${input.path}.${requiredField}`,
791
+ message: `Provider monitor payload field ${requiredField} is required when ${guardDescription}.`,
792
+ expected: Object.keys(properties).sort(),
793
+ received: undefined,
794
+ suggestion:
795
+ 'Use the platform-admin monitor tools API and provide the required payload fields.',
796
+ entity_type: 'tool',
797
+ entity_key: input.entityKey,
798
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
799
+ }),
800
+ );
801
+ }
802
+ }
803
+ if (input.schema.additionalProperties === false) {
804
+ for (const key of Object.keys(input.value)) {
805
+ if (properties[key]) continue;
806
+ issues.push(
807
+ issue({
808
+ code: 'unknown_provider_monitor_payload_field',
809
+ path: `${input.path}.${key}`,
810
+ message: `Provider monitor payload field ${key} is not supported for this monitor tool.`,
811
+ expected: Object.keys(properties).sort(),
812
+ received: input.value[key],
813
+ suggestion:
814
+ 'Remove the field or choose a monitor tool whose payload_schema includes it.',
815
+ entity_type: 'tool',
816
+ entity_key: input.entityKey,
817
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
818
+ }),
819
+ );
820
+ }
821
+ }
822
+ for (const [key, propertySchema] of Object.entries(properties)) {
823
+ const value = input.value[key];
824
+ if (value === undefined) continue;
825
+ issues.push(
826
+ ...validateSchemaProperty({
827
+ schema: propertySchema,
828
+ value,
829
+ path: `${input.path}.${key}`,
830
+ entityKey: input.entityKey,
831
+ }),
832
+ );
833
+ }
834
+ return issues;
835
+ }
836
+
837
+ export function validateProviderMonitorCapabilityPayload(input: {
838
+ capability: ProviderMonitorCapabilityDefinition;
839
+ value: unknown;
840
+ entityKey: string;
841
+ mode?: 'strict' | 'legacy_stored';
842
+ }) {
843
+ if (!isRecord(input.value)) {
844
+ return [
845
+ issue({
846
+ code: 'invalid_provider_monitor_payload',
847
+ path: 'payload',
848
+ message: 'Provider monitor payload must be an object when provided.',
849
+ expected: 'object',
850
+ received: input.value,
851
+ suggestion:
852
+ 'Use payload: { ... } with fields from the monitor tool payload_schema.',
853
+ entity_type: 'monitor',
854
+ entity_key: input.entityKey,
855
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
856
+ }),
857
+ ];
858
+ }
859
+ const schemaIssues = validateObjectAgainstSchema({
860
+ schema: input.capability.payloadSchema,
861
+ value: input.value,
862
+ path: 'payload',
863
+ entityKey: input.capability.providerCapability,
864
+ });
865
+ const capabilityIssues = input.capability.validatePayload
866
+ ? input.capability.validatePayload({
867
+ payload: input.value,
868
+ entityKey: input.entityKey,
869
+ })
870
+ : [];
871
+ if (input.mode !== 'legacy_stored') {
872
+ return [...schemaIssues, ...capabilityIssues];
873
+ }
874
+ const grandfatheredPaths =
875
+ input.capability.legacyPayloadValidation?.grandfatheredPaths ?? [];
876
+ const grandfatheredSchemaIssueCodes =
877
+ input.capability.legacyPayloadValidation?.grandfatheredSchemaIssueCodes ??
878
+ [];
879
+ return [
880
+ ...schemaIssues.filter(
881
+ (validationIssue) =>
882
+ validationIssue.severity !== 'error' ||
883
+ !grandfatheredSchemaIssueCodes.includes(validationIssue.code) ||
884
+ !validationIssueMatchesGrandfatheredPath({
885
+ issue: validationIssue,
886
+ paths: grandfatheredPaths,
887
+ }),
888
+ ),
889
+ ...capabilityIssues.filter(
890
+ (validationIssue) =>
891
+ validationIssue.severity !== 'error' ||
892
+ !validationIssueMatchesGrandfatheredPath({
893
+ issue: validationIssue,
894
+ paths: grandfatheredPaths,
895
+ }),
896
+ ),
897
+ ];
898
+ }
899
+
900
+ function conditionMatchesPayload(
901
+ output: ProviderMonitorCapabilityOutput,
902
+ payload: Record<string, unknown>,
903
+ ) {
904
+ const conditionPayload = output.condition?.payload;
905
+ if (!conditionPayload) return true;
906
+ return Object.entries(conditionPayload).every(
907
+ ([key, value]) => payload[key] === value,
908
+ );
909
+ }
910
+
911
+ export function resolveProviderMonitorCapabilityOutputsForPayload(input: {
912
+ capability: ProviderMonitorCapabilityDefinition;
913
+ payload: Record<string, unknown>;
914
+ }) {
915
+ const entries = Object.entries(input.capability.outputs);
916
+ const conditionalEntries = entries.filter(
917
+ ([, output]) => output.condition?.payload,
918
+ );
919
+ if (conditionalEntries.length === 0) return input.capability.outputs;
920
+ const selected = entries.filter(([, output]) =>
921
+ conditionMatchesPayload(output, input.payload),
922
+ );
923
+ return Object.fromEntries(selected.length ? selected : entries);
924
+ }
925
+
926
+ export function providerMonitorCapabilityLifecycleIssue(input: {
927
+ capability: ProviderMonitorCapabilityDefinition;
928
+ mode: 'deploy' | 'status' | 'disable' | 'check';
929
+ }): MonitorSdkValidationIssue | null {
930
+ if (input.mode === 'check' && input.capability.lifecycle.check) return null;
931
+ if (input.mode !== 'check' && input.capability.lifecycle[input.mode]) {
932
+ if (
933
+ input.mode !== 'deploy' ||
934
+ input.capability.runtime.deployAction ||
935
+ input.capability.runtime.kind === 'provider_manual_webhook'
936
+ ) {
937
+ return null;
938
+ }
939
+ }
940
+ const code = `provider_monitor_${input.mode}_not_supported`;
941
+ return issue({
942
+ code,
943
+ path: 'tool',
944
+ message: `Monitor tool ${input.capability.providerCapability} does not support ${input.mode}.`,
945
+ expected: `${input.mode} lifecycle support`,
946
+ received:
947
+ input.mode === 'deploy'
948
+ ? {
949
+ lifecycle: input.capability.lifecycle.deploy,
950
+ deploy_action_wired: Boolean(
951
+ input.capability.runtime.deployAction ||
952
+ input.capability.runtime.kind === 'provider_manual_webhook',
953
+ ),
954
+ }
955
+ : input.capability.lifecycle[input.mode],
956
+ suggestion:
957
+ input.mode === 'deploy'
958
+ ? 'This monitor tool is modeled but its upstream deploy action is not wired yet.'
959
+ : 'Choose a monitor tool with lifecycle support from the platform-admin monitor catalog.',
960
+ entity_type: 'tool',
961
+ entity_key: input.capability.providerCapability,
962
+ docs_ref: 'docs/monitor-sdk-architecture-plan.html#v2',
963
+ });
964
+ }