deepline 0.2.47 → 0.2.49

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.
@@ -818,10 +818,14 @@ export type MonitorsNamespace = {
818
818
  list: (options?: MonitorsListOptions) => Promise<MonitorsListResult>;
819
819
  /** Fetch one deployed monitor by public key (without dependents). */
820
820
  get: (key: string) => Promise<MonitorDetail>;
821
- /** Send an explicit payload through the deployed monitor's normal webhook path. */
821
+ /**
822
+ * Test a deployed monitor. `validationOnly` safely verifies the callback
823
+ * envelope; omitted options preserve the historic full-ingestion behavior.
824
+ */
822
825
  test: (
823
826
  key: string,
824
827
  payload: Record<string, unknown>,
828
+ options?: { validationOnly?: boolean },
825
829
  ) => Promise<MonitorTestResult>;
826
830
  validate: (key: string) => Promise<MonitorValidateResult>;
827
831
  /** List the published plays depending on one monitor's output streams. */
@@ -1634,7 +1638,8 @@ export class DeeplineClient {
1634
1638
  deploy: (definition, options) => this.deployMonitor(definition, options),
1635
1639
  list: (options) => this.listMonitors(options),
1636
1640
  get: (key) => this.getMonitor(key),
1637
- test: (key, payload) => this.testMonitorWebhook(key, payload),
1641
+ test: (key, payload, options) =>
1642
+ this.testMonitorWebhook(key, payload, options),
1638
1643
  validate: (key) => this.validateMonitor(key),
1639
1644
  dependents: (key) => this.getMonitorDependents(key),
1640
1645
  update: (key, patch) => this.updateMonitor(key, patch),
@@ -4547,10 +4552,17 @@ export class DeeplineClient {
4547
4552
  async testMonitorWebhook(
4548
4553
  key: string,
4549
4554
  payload: Record<string, unknown>,
4555
+ options?: { validationOnly?: boolean },
4550
4556
  ): Promise<MonitorTestResult> {
4551
4557
  return this.http.request<MonitorTestResult>(
4552
4558
  `/api/v2/monitors/deployed/${encodeURIComponent(key)}/test`,
4553
- { method: 'POST', body: { payload } },
4559
+ {
4560
+ method: 'POST',
4561
+ body: {
4562
+ payload,
4563
+ ...(options?.validationOnly ? { mode: 'validation_only' } : {}),
4564
+ },
4565
+ },
4554
4566
  );
4555
4567
  }
4556
4568
 
@@ -176,7 +176,8 @@ export type PlayFetchResponse = PlayAuthoringFetchResponse;
176
176
  *
177
177
  * A play can be triggered three ways, declared as the third argument to
178
178
  * {@link definePlay}:
179
- * - `webhook` — an inbound HTTP call (with optional HMAC signature verification);
179
+ * - `webhook` — an inbound HTTP call (with optional legacy HMAC or Standard
180
+ * Webhooks signature verification);
180
181
  * - `cron` — a schedule; or
181
182
  * - `sqlListeners` — a **monitor**: the play runs whenever a monitor writes a new
182
183
  * row to its output stream. This is how you build a play "on top of" a monitor
@@ -198,6 +199,19 @@ export type PlayFetchResponse = PlayAuthoringFetchResponse;
198
199
  * });
199
200
  * ```
200
201
  *
202
+ * @example Svix / Standard Webhooks verification with Deepline Secrets
203
+ * ```typescript
204
+ * definePlay('visitor-webhook', handler, {
205
+ * webhook: {
206
+ * auth: {
207
+ * type: 'standard-webhooks',
208
+ * headerFamily: 'svix',
209
+ * signingSecrets: ['VECTOR_WEBHOOK_SECRET'],
210
+ * },
211
+ * },
212
+ * });
213
+ * ```
214
+ *
201
215
  * @example Cron schedule
202
216
  * ```typescript
203
217
  * definePlay('nightly-sync', handler, {
@@ -160,7 +160,7 @@ export const SDK_RELEASE = {
160
160
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
161
161
  // exposed storage-dependent synchronous access. This deliberate minor
162
162
  // release keeps lazy paging semantics independent of row residency.
163
- version: '0.2.47',
163
+ version: '0.2.49',
164
164
  contracts: {
165
165
  api: {
166
166
  name: 'sdk-http-api',
@@ -1102,6 +1102,7 @@ function durableCtxKey(input: {
1102
1102
  id: string;
1103
1103
  semanticKey?: string | null;
1104
1104
  staleAfterSeconds?: number | null;
1105
+ cacheEpochMs?: number;
1105
1106
  }): string {
1106
1107
  if (input.operation === 'tool') {
1107
1108
  throw new Error('Tool calls use tool receipt keys.');
@@ -1113,6 +1114,7 @@ function durableCtxKey(input: {
1113
1114
  id: input.id,
1114
1115
  semanticKey: input.semanticKey,
1115
1116
  staleAfterSeconds: input.staleAfterSeconds,
1117
+ cacheEpochMs: input.cacheEpochMs,
1116
1118
  });
1117
1119
  }
1118
1120
 
@@ -1391,6 +1393,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
1391
1393
  private fixtureProviderPacingBypassLogged = false;
1392
1394
  private fixtureProviderPacingEnforcementLogged = false;
1393
1395
  private checkpoint: PlayCheckpoint;
1396
+ private readonly durableCallCacheEpochMs: number;
1394
1397
  /**
1395
1398
  * Durable tool receipts are the replay/cache authority for the execution
1396
1399
  * paths the host supports. Keeping the same completed result in this
@@ -1664,6 +1667,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
1664
1667
  };
1665
1668
  this.#options = options;
1666
1669
  this.checkpoint = options.checkpoint ?? emptyCheckpoint();
1670
+ const checkpointCacheEpochMs = this.checkpoint.durableCallCacheEpochMs;
1671
+ this.durableCallCacheEpochMs =
1672
+ typeof checkpointCacheEpochMs === 'number' &&
1673
+ Number.isFinite(checkpointCacheEpochMs) &&
1674
+ checkpointCacheEpochMs >= 0
1675
+ ? checkpointCacheEpochMs
1676
+ : Date.now();
1677
+ this.checkpoint.durableCallCacheEpochMs = this.durableCallCacheEpochMs;
1667
1678
  this.durableMappedToolResultsBackedByReceipts = Boolean(
1668
1679
  (options.claimRuntimeStepReceipt || options.claimRuntimeStepReceipts) &&
1669
1680
  (options.getRuntimeStepReceipt || options.getRuntimeStepReceipts) &&
@@ -3104,6 +3115,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
3104
3115
  }),
3105
3116
  providerActionVersion,
3106
3117
  staleAfterSeconds: input.staleAfterSeconds,
3118
+ cacheEpochMs: this.durableCallCacheEpochMs,
3107
3119
  });
3108
3120
  }
3109
3121
 
@@ -3286,6 +3298,7 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
3286
3298
  id,
3287
3299
  semanticKey: opts.semanticKey,
3288
3300
  staleAfterSeconds: stalePolicy.staleAfterSeconds,
3301
+ cacheEpochMs: this.durableCallCacheEpochMs,
3289
3302
  });
3290
3303
  return await executeWithDurableRuntimeReceipt<T>({
3291
3304
  operation,
@@ -779,6 +779,11 @@ export interface ContextOptions {
779
779
  }
780
780
 
781
781
  export interface PlayCheckpoint {
782
+ /**
783
+ * Clock captured when this logical run first creates its context. All
784
+ * staleAfterSeconds receipt buckets use this value across durable resumes.
785
+ */
786
+ durableCallCacheEpochMs?: number;
782
787
  /** Waterfall batches that have completed: key = `${toolName}:${provider}`, value = results array. */
783
788
  completedBatches: Record<string, BatchResult[]>;
784
789
  /** Tool call batches that have completed: key = toolId, value = sparse row-cache-key -> result map. */
@@ -42,6 +42,8 @@ export function buildDurableToolCallCacheKey(input: {
42
42
  providerActionVersion?: string | null;
43
43
  cachePolicyVersion?: string | null;
44
44
  staleAfterSeconds?: number | null;
45
+ /** Run-stable clock used to choose the stale bucket. */
46
+ cacheEpochMs?: number;
45
47
  playLocalScope?: string | null;
46
48
  }): string {
47
49
  const orgId = input.orgId?.trim() || 'org';
@@ -70,6 +72,7 @@ export function buildDurableToolCallCacheKey(input: {
70
72
  input.cachePolicyVersion ?? DURABLE_CALL_CACHE_POLICY_VERSION,
71
73
  staleBucket: durableCacheStaleBucket({
72
74
  staleAfterSeconds: input.staleAfterSeconds,
75
+ nowMs: input.cacheEpochMs,
73
76
  }),
74
77
  }),
75
78
  );
@@ -84,6 +87,8 @@ export function buildDurableCtxCallCacheKey(input: {
84
87
  semanticKey?: string | null;
85
88
  cachePolicyVersion?: string | null;
86
89
  staleAfterSeconds?: number | null;
90
+ /** Run-stable clock used to choose the stale bucket. */
91
+ cacheEpochMs?: number;
87
92
  }): string {
88
93
  const orgId = input.orgId?.trim() || 'org';
89
94
  const playId = input.playId?.trim() || 'play';
@@ -104,6 +109,7 @@ export function buildDurableCtxCallCacheKey(input: {
104
109
  input.cachePolicyVersion ?? DURABLE_CALL_CACHE_POLICY_VERSION,
105
110
  staleBucket: durableCacheStaleBucket({
106
111
  staleAfterSeconds: input.staleAfterSeconds,
112
+ nowMs: input.cacheEpochMs,
107
113
  }),
108
114
  }),
109
115
  );
@@ -32,6 +32,7 @@ export type PlayRunnerRuntimeResource = {
32
32
 
33
33
  export type RuntimeResourceTerminalReason =
34
34
  | 'completed'
35
+ | 'suspended'
35
36
  | 'runner_failed'
36
37
  | 'sandbox_missing'
37
38
  | 'sandbox_killed'
@@ -56,6 +56,7 @@ export const PLAY_AUTHORING_CONTRACT_ISSUE_CODES = [
56
56
  'play_authoring_billing_limit_invalid',
57
57
  'play_authoring_billing_limit_unresolved',
58
58
  'play_authoring_webhook_hmac_invalid',
59
+ 'play_authoring_standard_webhooks_invalid',
59
60
  'play_authoring_secret_invalid',
60
61
  'play_authoring_cron_timezone_invalid',
61
62
  'play_authoring_tool_request_invalid',
@@ -83,6 +84,16 @@ export type PlayAuthoringContractIssue = {
83
84
  };
84
85
 
85
86
  export type PlaySqlListenerOperation = 'INSERT' | 'UPDATE' | 'DELETE';
87
+ export type PlayStandardWebhookHeaderFamily = 'standard' | 'svix';
88
+ export type PlayStandardWebhookAuth = {
89
+ type: 'standard-webhooks';
90
+ /** `webhook-*` for Standard Webhooks, `svix-*` for Svix senders. */
91
+ headerFamily: PlayStandardWebhookHeaderFamily;
92
+ /** Deepline Secret names used for ordinary operation and rotation overlap. */
93
+ signingSecrets: string[];
94
+ /** Replay-protection window. Omitted means the Standard Webhooks 5-minute default. */
95
+ toleranceSeconds?: number;
96
+ };
86
97
  export const PLAY_SQL_LISTENER_WHERE_OPERATORS = [
87
98
  'eq',
88
99
  'neq',
@@ -259,6 +270,7 @@ export type PlayAuthoringAstBindings = {
259
270
  header?: string;
260
271
  secretEnv: string;
261
272
  };
273
+ auth?: PlayStandardWebhookAuth;
262
274
  };
263
275
  cron?: { schedule: string; timezone?: string };
264
276
  sqlListeners?: PlayAuthoringAstSqlListenerDeclaration[];
@@ -287,6 +299,7 @@ export type PlayAuthoringBindings = {
287
299
  header?: string;
288
300
  secretEnv: string;
289
301
  };
302
+ auth?: PlayStandardWebhookAuth;
290
303
  };
291
304
  cron?: {
292
305
  schedule: string;
@@ -973,6 +986,81 @@ export const PLAY_AUTHORING_FIELD_REGISTRY = {
973
986
  errorMessage:
974
987
  'bindings.webhook.hmac.header must be a non-empty static string.',
975
988
  },
989
+ 'bindings.webhook.auth.type': {
990
+ schema: Type.Literal('standard-webhooks'),
991
+ fixtures: {
992
+ valid: 'standard-webhooks',
993
+ invalid: 'svix',
994
+ absent: undefined,
995
+ unresolved: { expression: 'type' },
996
+ edition1: undefined,
997
+ },
998
+ referenceType: "'standard-webhooks'",
999
+ // auth itself is optional; once present, the AST adapter requires this
1000
+ // field together with headerFamily and signingSecrets.
1001
+ required: false,
1002
+ resolution: 'static-required',
1003
+ issueCode: 'play_authoring_standard_webhooks_invalid',
1004
+ description: 'Uses the Standard Webhooks v1 symmetric signing scheme.',
1005
+ errorMessage:
1006
+ 'bindings.webhook.auth.type must be the static literal "standard-webhooks".',
1007
+ },
1008
+ 'bindings.webhook.auth.headerFamily': {
1009
+ schema: Type.Union([Type.Literal('standard'), Type.Literal('svix')]),
1010
+ fixtures: {
1011
+ valid: 'svix',
1012
+ invalid: 'webhook',
1013
+ absent: undefined,
1014
+ unresolved: { expression: 'headerFamily' },
1015
+ edition1: undefined,
1016
+ },
1017
+ referenceType: "'standard' | 'svix'",
1018
+ // auth itself is optional; once present, the AST adapter requires this
1019
+ // field together with type and signingSecrets.
1020
+ required: false,
1021
+ resolution: 'static-required',
1022
+ issueCode: 'play_authoring_standard_webhooks_invalid',
1023
+ description: 'Header namespace expected from the webhook provider.',
1024
+ errorMessage:
1025
+ 'bindings.webhook.auth.headerFamily must be the static literal "standard" or "svix".',
1026
+ },
1027
+ 'bindings.webhook.auth.signingSecrets[]': {
1028
+ schema: SecretEnvironmentNameSchema,
1029
+ fixtures: {
1030
+ valid: 'VECTOR_WEBHOOK_SECRET',
1031
+ invalid: 'vector_webhook_secret',
1032
+ absent: undefined,
1033
+ unresolved: { expression: 'secret' },
1034
+ edition1: undefined,
1035
+ },
1036
+ referenceType: 'string',
1037
+ // auth itself is optional; once present, the AST adapter requires this
1038
+ // field together with type and headerFamily.
1039
+ required: false,
1040
+ resolution: 'static-required',
1041
+ issueCode: 'play_authoring_standard_webhooks_invalid',
1042
+ description: 'Deepline Secret name used to verify Standard Webhooks.',
1043
+ errorMessage:
1044
+ 'bindings.webhook.auth.signingSecrets entries must be uppercase Deepline Secret names beginning with a letter.',
1045
+ },
1046
+ 'bindings.webhook.auth.toleranceSeconds': {
1047
+ schema: Type.Integer({ minimum: 1, maximum: 3600 }),
1048
+ fixtures: {
1049
+ valid: 300,
1050
+ invalid: 0,
1051
+ absent: undefined,
1052
+ unresolved: { expression: 'toleranceSeconds' },
1053
+ edition1: undefined,
1054
+ },
1055
+ referenceType: 'number',
1056
+ required: false,
1057
+ resolution: 'static-required',
1058
+ issueCode: 'play_authoring_standard_webhooks_invalid',
1059
+ description:
1060
+ 'Accepted delivery timestamp skew in seconds, from 1 through 3600.',
1061
+ errorMessage:
1062
+ 'bindings.webhook.auth.toleranceSeconds must be a static whole number from 1 through 3600.',
1063
+ },
976
1064
  'bindings.cron.schedule': {
977
1065
  schema: Type.String({ minLength: 1 }),
978
1066
  fixtures: {
@@ -2157,7 +2245,7 @@ export const PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
2157
2245
  ` inline?: ${cloudReferenceType('inline')};`,
2158
2246
  ` billing?: { maxCreditsPerRun?: ${cloudReferenceType('billing.maxCreditsPerRun')} };`,
2159
2247
  ` runtime?: { timeout?: ${cloudReferenceType('runtime.timeout')}; size?: ${cloudReferenceType('runtime.size')} };`,
2160
- ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType('bindings.webhook.hmac.algorithm')}; header?: ${cloudReferenceType('bindings.webhook.hmac.header')}; secretEnv: ${cloudReferenceType('bindings.webhook.hmac.secretEnv')} } };`,
2248
+ ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType('bindings.webhook.hmac.algorithm')}; header?: ${cloudReferenceType('bindings.webhook.hmac.header')}; secretEnv: ${cloudReferenceType('bindings.webhook.hmac.secretEnv')} }; auth?: { type: ${cloudReferenceType('bindings.webhook.auth.type')}; headerFamily: ${cloudReferenceType('bindings.webhook.auth.headerFamily')}; signingSecrets: readonly ${cloudReferenceType('bindings.webhook.auth.signingSecrets[]')}[]; toleranceSeconds?: ${cloudReferenceType('bindings.webhook.auth.toleranceSeconds')} } };`,
2161
2249
  ` cron?: { schedule: ${cloudReferenceType('bindings.cron.schedule')}; timezone?: ${cloudReferenceType('bindings.cron.timezone')} };`,
2162
2250
  ' sqlListeners?: readonly SqlListenerDeclaration[];',
2163
2251
  ` secrets?: readonly ${cloudReferenceType('bindings.secrets[]')}[];`,
package/dist/cli/index.js CHANGED
@@ -1044,7 +1044,7 @@ var SDK_RELEASE = {
1044
1044
  // 0.2.0 makes Dataset Handles uniformly async-only after 0.1.320 briefly
1045
1045
  // exposed storage-dependent synchronous access. This deliberate minor
1046
1046
  // release keeps lazy paging semantics independent of row residency.
1047
- version: "0.2.47",
1047
+ version: "0.2.49",
1048
1048
  contracts: {
1049
1049
  api: {
1050
1050
  name: "sdk-http-api",
@@ -3879,7 +3879,7 @@ var DeeplineClient = class {
3879
3879
  deploy: (definition, options2) => this.deployMonitor(definition, options2),
3880
3880
  list: (options2) => this.listMonitors(options2),
3881
3881
  get: (key) => this.getMonitor(key),
3882
- test: (key, payload) => this.testMonitorWebhook(key, payload),
3882
+ test: (key, payload, options2) => this.testMonitorWebhook(key, payload, options2),
3883
3883
  validate: (key) => this.validateMonitor(key),
3884
3884
  dependents: (key) => this.getMonitorDependents(key),
3885
3885
  update: (key, patch) => this.updateMonitor(key, patch),
@@ -6048,10 +6048,16 @@ var DeeplineClient = class {
6048
6048
  { method: "GET" }
6049
6049
  );
6050
6050
  }
6051
- async testMonitorWebhook(key, payload) {
6051
+ async testMonitorWebhook(key, payload, options) {
6052
6052
  return this.http.request(
6053
6053
  `/api/v2/monitors/deployed/${encodeURIComponent(key)}/test`,
6054
- { method: "POST", body: { payload } }
6054
+ {
6055
+ method: "POST",
6056
+ body: {
6057
+ payload,
6058
+ ...options?.validationOnly ? { mode: "validation_only" } : {}
6059
+ }
6060
+ }
6055
6061
  );
6056
6062
  }
6057
6063
  async setupMonitor(tool, payload) {
@@ -15021,6 +15027,76 @@ var PLAY_AUTHORING_FIELD_REGISTRY = {
15021
15027
  description: "HTTP header containing the webhook signature.",
15022
15028
  errorMessage: "bindings.webhook.hmac.header must be a non-empty static string."
15023
15029
  },
15030
+ "bindings.webhook.auth.type": {
15031
+ schema: Type.Literal("standard-webhooks"),
15032
+ fixtures: {
15033
+ valid: "standard-webhooks",
15034
+ invalid: "svix",
15035
+ absent: void 0,
15036
+ unresolved: { expression: "type" },
15037
+ edition1: void 0
15038
+ },
15039
+ referenceType: "'standard-webhooks'",
15040
+ // auth itself is optional; once present, the AST adapter requires this
15041
+ // field together with headerFamily and signingSecrets.
15042
+ required: false,
15043
+ resolution: "static-required",
15044
+ issueCode: "play_authoring_standard_webhooks_invalid",
15045
+ description: "Uses the Standard Webhooks v1 symmetric signing scheme.",
15046
+ errorMessage: 'bindings.webhook.auth.type must be the static literal "standard-webhooks".'
15047
+ },
15048
+ "bindings.webhook.auth.headerFamily": {
15049
+ schema: Type.Union([Type.Literal("standard"), Type.Literal("svix")]),
15050
+ fixtures: {
15051
+ valid: "svix",
15052
+ invalid: "webhook",
15053
+ absent: void 0,
15054
+ unresolved: { expression: "headerFamily" },
15055
+ edition1: void 0
15056
+ },
15057
+ referenceType: "'standard' | 'svix'",
15058
+ // auth itself is optional; once present, the AST adapter requires this
15059
+ // field together with type and signingSecrets.
15060
+ required: false,
15061
+ resolution: "static-required",
15062
+ issueCode: "play_authoring_standard_webhooks_invalid",
15063
+ description: "Header namespace expected from the webhook provider.",
15064
+ errorMessage: 'bindings.webhook.auth.headerFamily must be the static literal "standard" or "svix".'
15065
+ },
15066
+ "bindings.webhook.auth.signingSecrets[]": {
15067
+ schema: SecretEnvironmentNameSchema,
15068
+ fixtures: {
15069
+ valid: "VECTOR_WEBHOOK_SECRET",
15070
+ invalid: "vector_webhook_secret",
15071
+ absent: void 0,
15072
+ unresolved: { expression: "secret" },
15073
+ edition1: void 0
15074
+ },
15075
+ referenceType: "string",
15076
+ // auth itself is optional; once present, the AST adapter requires this
15077
+ // field together with type and headerFamily.
15078
+ required: false,
15079
+ resolution: "static-required",
15080
+ issueCode: "play_authoring_standard_webhooks_invalid",
15081
+ description: "Deepline Secret name used to verify Standard Webhooks.",
15082
+ errorMessage: "bindings.webhook.auth.signingSecrets entries must be uppercase Deepline Secret names beginning with a letter."
15083
+ },
15084
+ "bindings.webhook.auth.toleranceSeconds": {
15085
+ schema: Type.Integer({ minimum: 1, maximum: 3600 }),
15086
+ fixtures: {
15087
+ valid: 300,
15088
+ invalid: 0,
15089
+ absent: void 0,
15090
+ unresolved: { expression: "toleranceSeconds" },
15091
+ edition1: void 0
15092
+ },
15093
+ referenceType: "number",
15094
+ required: false,
15095
+ resolution: "static-required",
15096
+ issueCode: "play_authoring_standard_webhooks_invalid",
15097
+ description: "Accepted delivery timestamp skew in seconds, from 1 through 3600.",
15098
+ errorMessage: "bindings.webhook.auth.toleranceSeconds must be a static whole number from 1 through 3600."
15099
+ },
15024
15100
  "bindings.cron.schedule": {
15025
15101
  schema: Type.String({ minLength: 1 }),
15026
15102
  fixtures: {
@@ -16045,7 +16121,7 @@ var PLAY_AUTHORING_CLOUD_TYPE_DECLARATIONS = [
16045
16121
  ` inline?: ${cloudReferenceType("inline")};`,
16046
16122
  ` billing?: { maxCreditsPerRun?: ${cloudReferenceType("billing.maxCreditsPerRun")} };`,
16047
16123
  ` runtime?: { timeout?: ${cloudReferenceType("runtime.timeout")}; size?: ${cloudReferenceType("runtime.size")} };`,
16048
- ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType("bindings.webhook.hmac.algorithm")}; header?: ${cloudReferenceType("bindings.webhook.hmac.header")}; secretEnv: ${cloudReferenceType("bindings.webhook.hmac.secretEnv")} } };`,
16124
+ ` webhook?: { hmac?: { algorithm?: ${cloudReferenceType("bindings.webhook.hmac.algorithm")}; header?: ${cloudReferenceType("bindings.webhook.hmac.header")}; secretEnv: ${cloudReferenceType("bindings.webhook.hmac.secretEnv")} }; auth?: { type: ${cloudReferenceType("bindings.webhook.auth.type")}; headerFamily: ${cloudReferenceType("bindings.webhook.auth.headerFamily")}; signingSecrets: readonly ${cloudReferenceType("bindings.webhook.auth.signingSecrets[]")}[]; toleranceSeconds?: ${cloudReferenceType("bindings.webhook.auth.toleranceSeconds")} } };`,
16049
16125
  ` cron?: { schedule: ${cloudReferenceType("bindings.cron.schedule")}; timezone?: ${cloudReferenceType("bindings.cron.timezone")} };`,
16050
16126
  " sqlListeners?: readonly SqlListenerDeclaration[];",
16051
16127
  ` secrets?: readonly ${cloudReferenceType("bindings.secrets[]")}[];`,
@@ -30757,6 +30833,31 @@ function readDeployOutputContract(payload) {
30757
30833
  });
30758
30834
  return { tool: asString(contract.tool), streams };
30759
30835
  }
30836
+ function renderDeployReplacementWarning(input2) {
30837
+ const summary = asRecord2(input2.payload.change_summary);
30838
+ const upstream = summary ? asRecord2(summary.upstream) : void 0;
30839
+ if (!summary || !upstream || upstream.resource_replaced !== true) return [];
30840
+ const definition = asRecord2(summary.definition);
30841
+ const changed = definition && Array.isArray(definition.changed) ? definition.changed : [];
30842
+ const lines = [
30843
+ input2.completed ? "WARNING: this deploy replaced the existing upstream monitor." : "WARNING: this deploy replaces the existing upstream monitor.",
30844
+ "Existing Customer DB rows are preserved. Provider backfill is not implied."
30845
+ ];
30846
+ for (const raw of changed) {
30847
+ const item = asRecord2(raw);
30848
+ const path = item ? asString(item.path) : void 0;
30849
+ if (!item || !path) continue;
30850
+ lines.push(
30851
+ ` ${path}: ${JSON.stringify(item.before ?? null)} \u2192 ${JSON.stringify(
30852
+ item.after ?? null
30853
+ )}`
30854
+ );
30855
+ }
30856
+ lines.push(
30857
+ input2.monitorKey ? `Use \`deepline monitors update ${input2.monitorKey} <patch>\` for a patch-style change.` : "Use `deepline monitors update <key> <patch>` for a patch-style change."
30858
+ );
30859
+ return lines;
30860
+ }
30760
30861
  function renderMonitorDeployCompletion(payload) {
30761
30862
  const monitor = asRecord2(payload.monitor);
30762
30863
  const key = monitor ? asString(monitor.key) : void 0;
@@ -30784,6 +30885,12 @@ function renderMonitorDeployCompletion(payload) {
30784
30885
  if (pricingLine) {
30785
30886
  lines.push("", `Pricing: ${pricingLine}`);
30786
30887
  }
30888
+ const replacementWarning = renderDeployReplacementWarning({
30889
+ payload,
30890
+ completed: true,
30891
+ monitorKey: key
30892
+ });
30893
+ if (replacementWarning.length) lines.push("", ...replacementWarning);
30787
30894
  const guidance = asRecord2(payload.setup_guidance);
30788
30895
  if (guidance) {
30789
30896
  const callbackUrl = asString(guidance.callback_url);
@@ -30835,6 +30942,11 @@ function renderMonitorDeployPlan(payload) {
30835
30942
  if (message) lines.push(` - ${path ? `${path}: ` : ""}${message}`);
30836
30943
  }
30837
30944
  }
30945
+ const replacementWarning = renderDeployReplacementWarning({
30946
+ payload,
30947
+ completed: false
30948
+ });
30949
+ if (replacementWarning.length) lines.push("", ...replacementWarning);
30838
30950
  const estimate = asRecord2(payload.deploy_cost_estimate);
30839
30951
  const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
30840
30952
  if (credits !== void 0) {
@@ -31201,10 +31313,17 @@ async function handleMonitorsGet(key, options) {
31201
31313
  }
31202
31314
  async function handleMonitorsTest(key, payload, options) {
31203
31315
  const explicitPayload = parseJsonObjectArg(payload, "<payload>");
31204
- const result = await new DeeplineClient().monitors.test(key, explicitPayload);
31205
- const text = `Webhook test for ${key}: ${result.accepted === true ? "accepted" : "rejected"}
31206
- persisted rows: ${asFiniteNumber(result.persisted_rows) ?? 0}
31207
- bound Plays dispatched: ${asFiniteNumber(result.dispatched_bound_plays) ?? 0}
31316
+ const result = await new DeeplineClient().monitors.test(
31317
+ key,
31318
+ explicitPayload,
31319
+ {
31320
+ validationOnly: options.dispatch !== true
31321
+ }
31322
+ );
31323
+ const dispatch = options.dispatch === true;
31324
+ const text = `Monitor diagnostic for ${key}: ${result.accepted === true ? "accepted" : "rejected"}
31325
+ ` + (dispatch ? " mode: dispatch (writes rows and may dispatch bound Plays)\n" : " mode: validation_only (no rows written, credits spent, or Plays dispatched)\n") + `${dispatch ? "persisted" : "would persist"} rows: ${asFiniteNumber(result.persisted_rows) ?? 0}
31326
+ ${dispatch ? "dispatched" : "would dispatch"} bound Plays: ${asFiniteNumber(result.dispatched_bound_plays) ?? 0}
31208
31327
  `;
31209
31328
  printCommandEnvelope(result, { json: options.json, text });
31210
31329
  }
@@ -31416,12 +31535,18 @@ Examples:
31416
31535
  withJsonOption(
31417
31536
  monitors.command("test <key> <payload>").description(
31418
31537
  "Send an explicit payload through a monitor\u2019s webhook ingestion path."
31538
+ ).option(
31539
+ "--dispatch",
31540
+ "Inject the test event through normal ingestion (writes rows and may dispatch bound Plays)"
31419
31541
  ).addHelpText(
31420
31542
  "after",
31421
31543
  `
31422
31544
  Notes:
31423
31545
  <payload> must be an explicit JSON object. The command uses the deployed
31424
- monitor\u2019s real validation, persistence, and inline Play dispatch path; it does
31546
+ monitor\u2019s real binding and payload validation, but is a side-effect-free
31547
+ diagnostic: it does not persist rows, spend credits, dispatch Plays, or alter
31548
+ monitor state. Pass --dispatch only when you deliberately need the historic
31549
+ full-ingestion test event; it can write rows and trigger bound Plays. It does
31425
31550
  not synthesize a provider event or accept an omitted payload.
31426
31551
 
31427
31552
  Examples:
@@ -31442,7 +31567,9 @@ Notes:
31442
31567
  via --file <path>, or
31443
31568
  from stdin with --file -. Does not deploy or spend credits.
31444
31569
  For Deepline Native Company Radar monitors, check validates persona-filter enums and the
31445
- job_titles Boolean-expression grammar locally. Inspect the exact schema with
31570
+ job_titles Boolean-expression grammar locally (parentheses are unsupported;
31571
+ NOT > AND > OR).
31572
+ Inspect the exact schema with
31446
31573
  \`deepline monitors available deepline_native.company_radar --json\`.
31447
31574
 
31448
31575
  Examples:
@@ -31466,6 +31593,9 @@ Notes:
31466
31593
  --dry-run validates the definition and shows the plan (deploy cost in Deepline
31467
31594
  credits when the server reports it, plus any existing monitors that may
31468
31595
  already cover this scope) WITHOUT deploying. Exits 0 when valid, 7 when not.
31596
+ Deploy is a full desired definition for its key: omitting a previously stored
31597
+ field removes it and can replace the upstream resource. Use \`monitors update\`
31598
+ for a patch-style change.
31469
31599
 
31470
31600
  Examples:
31471
31601
  deepline monitors deploy '{"key":"job-openings","tool":"deepline_native.company_radar","payload":{"domain":"stripe.com","radar_type":"company_job_openings"}}'