deepline 0.3.13 → 0.3.15

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.
@@ -98,6 +98,7 @@ import {
98
98
  TOOL_EXECUTION_ERROR_SCHEMA_HEADER,
99
99
  TOOL_EXECUTION_ERROR_SCHEMA_VERSION,
100
100
  } from '../../shared_libs/tool-execution-error.js';
101
+
101
102
  import { decodePlayRunPublicStatus } from '../../shared_libs/play-runtime/run-lifecycle-policy.js';
102
103
  import {
103
104
  legacyRawFromToolResponseRawV2,
@@ -105,6 +106,20 @@ import {
105
106
  RAW_V2_TOOL_RESPONSE_CONTRACT,
106
107
  } from '../../shared_libs/play-runtime/tool-response-contract.js';
107
108
 
109
+ export type SlackNotificationTarget =
110
+ | { channel: string; memberId?: never }
111
+ | { channel?: never; memberId: string };
112
+
113
+ export type CreateNotificationInput = {
114
+ name: string;
115
+ provider: 'slack';
116
+ eventTypes: string[];
117
+ } & SlackNotificationTarget;
118
+
119
+ export type UpdateNotificationInput =
120
+ | { enabled: boolean }
121
+ | ({ name: string; eventTypes: string[] } & SlackNotificationTarget);
122
+
108
123
  const TERMINAL_PLAY_STATUSES = new Set(['completed', 'failed', 'cancelled']);
109
124
  const INCLUDE_TOOL_METADATA_HEADER = 'x-deepline-include-tool-metadata';
110
125
  const EXECUTE_RESPONSE_CONTRACT_HEADER = 'x-deepline-execute-response-contract';
@@ -878,13 +893,10 @@ export type MonitorsNamespace = {
878
893
  key: string,
879
894
  patch: Record<string, unknown>,
880
895
  ) => Promise<MonitorUpdateResult>;
881
- /**
882
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
883
- * resource unless `localOnly` is set. `dryRun` returns the delete plan.
884
- */
896
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. */
885
897
  delete: (
886
898
  key: string,
887
- options?: { localOnly?: boolean; dryRun?: boolean },
899
+ options?: { dryRun?: boolean },
888
900
  ) => Promise<MonitorDeleteResult>;
889
901
  /** Reactivate a disabled monitor. `dryRun` returns the reactivation cost. */
890
902
  reactivate: (
@@ -3934,9 +3946,12 @@ export class DeeplineClient {
3934
3946
  return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
3935
3947
  }
3936
3948
 
3937
- /** Select the Slack channel used for product notifications. */
3938
- async setNotificationSlack(channel: string) {
3939
- return this.http.put('/api/v2/settings/notifications', { channel });
3949
+ /** Select a Slack channel or direct member used for product notifications. */
3950
+ async setNotificationSlack(destination: string | { memberId: string }) {
3951
+ return this.http.put(
3952
+ '/api/v2/settings/notifications',
3953
+ typeof destination === 'string' ? { channel: destination } : destination,
3954
+ );
3940
3955
  }
3941
3956
 
3942
3957
  /** Send one synchronous test ping and return Slack's delivery result. */
@@ -4002,21 +4017,14 @@ export class DeeplineClient {
4002
4017
  }
4003
4018
 
4004
4019
  /** Create a named notification routed through an existing provider integration. */
4005
- async createNotification(input: {
4006
- name: string;
4007
- provider: 'slack';
4008
- channel: string;
4009
- eventTypes: string[];
4010
- }) {
4020
+ async createNotification(input: CreateNotificationInput) {
4011
4021
  return this.http.post('/api/v2/notifications', input);
4012
4022
  }
4013
4023
 
4014
4024
  /** Update a notification's target, event selection, or enabled state. */
4015
4025
  async updateNotification(
4016
4026
  notificationId: string,
4017
- input:
4018
- | { enabled: boolean }
4019
- | { name: string; channel: string; eventTypes: string[] },
4027
+ input: UpdateNotificationInput,
4020
4028
  ) {
4021
4029
  return this.http.patch(
4022
4030
  `/api/v2/notifications/${encodeURIComponent(notificationId)}`,
@@ -4788,17 +4796,23 @@ export class DeeplineClient {
4788
4796
  );
4789
4797
  }
4790
4798
 
4791
- /**
4792
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
4793
- * resource unless `localOnly`; `dryRun` returns the delete plan. Prefer
4794
- * `client.monitors.delete(...)`.
4795
- */
4799
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. Prefer `client.monitors.delete(...)`. */
4796
4800
  async deleteMonitor(
4797
4801
  key: string,
4798
- options?: { localOnly?: boolean; dryRun?: boolean },
4802
+ options?: { dryRun?: boolean },
4799
4803
  ): Promise<MonitorDeleteResult> {
4804
+ // The public type intentionally excludes `localOnly`, but JavaScript
4805
+ // callers (and compiled older SDK clients) can still pass it at runtime.
4806
+ // Never silently reinterpret an old local-only request as an upstream
4807
+ // deletion. The server applies the same guard for direct API callers.
4808
+ if ((options as { localOnly?: unknown } | undefined)?.localOnly === true) {
4809
+ throw new DeeplineError(
4810
+ 'localOnly monitor deletion is no longer supported. Monitor deletion always deprovisions the upstream provider resource.',
4811
+ undefined,
4812
+ 'MONITOR_LOCAL_ONLY_DELETE_NOT_SUPPORTED',
4813
+ );
4814
+ }
4800
4815
  const params = new URLSearchParams();
4801
- if (options?.localOnly) params.set('local_only', 'true');
4802
4816
  if (options?.dryRun) params.set('dry_run', 'true');
4803
4817
  const query = params.toString();
4804
4818
  return this.http.request<MonitorDeleteResult>(
@@ -192,7 +192,7 @@ export const SDK_RELEASE = {
192
192
  // 0.3.0 introduces raw-v2: complete scrubbed provider responses are
193
193
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
194
194
  // getters keep their established compatibility behavior.
195
- version: '0.3.13',
195
+ version: '0.3.15',
196
196
  updateSummary:
197
197
  'New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.',
198
198
  contracts: {
@@ -1136,7 +1136,7 @@ export interface ProductNotification {
1136
1136
  id: string;
1137
1137
  name: string;
1138
1138
  provider: string;
1139
- target: { id: string; name: string };
1139
+ target: { id: string; name: string; kind?: 'channel' | 'member' };
1140
1140
  enabled: boolean;
1141
1141
  status: string;
1142
1142
  eventTypes: string[];
@@ -79,10 +79,25 @@ export const PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
79
79
  'channels:read',
80
80
  'chat:write',
81
81
  'groups:read',
82
+ 'im:write',
82
83
  ] as const;
83
84
  export type ProductNotificationDestinationKind =
84
85
  (typeof PRODUCT_NOTIFICATION_DESTINATION_KINDS)[number];
85
86
 
87
+ export const PRODUCT_NOTIFICATION_SLACK_TARGET_KINDS = [
88
+ 'channel',
89
+ 'member',
90
+ ] as const;
91
+ export type ProductNotificationSlackTargetKind =
92
+ (typeof PRODUCT_NOTIFICATION_SLACK_TARGET_KINDS)[number];
93
+
94
+ /** Slack member IDs begin with U (or W for Enterprise Grid workspaces). */
95
+ export function isProductNotificationSlackMemberId(
96
+ value: unknown,
97
+ ): value is string {
98
+ return typeof value === 'string' && /^[UW][A-Z0-9]{8,}$/.test(value.trim());
99
+ }
100
+
86
101
  export const PRODUCT_NOTIFICATION_DELIVERY_STATES = [
87
102
  'pending',
88
103
  'delivering',
package/dist/cli/index.js CHANGED
@@ -1047,7 +1047,7 @@ var SDK_RELEASE = {
1047
1047
  // 0.3.0 introduces raw-v2: complete scrubbed provider responses are
1048
1048
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
1049
1049
  // getters keep their established compatibility behavior.
1050
- version: "0.3.13",
1050
+ version: "0.3.15",
1051
1051
  updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
1052
1052
  contracts: {
1053
1053
  api: {
@@ -1907,7 +1907,8 @@ var PRODUCT_NOTIFICATION_EVENT_TYPE_SET = new Set(
1907
1907
  var PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
1908
1908
  "channels:read",
1909
1909
  "chat:write",
1910
- "groups:read"
1910
+ "groups:read",
1911
+ "im:write"
1911
1912
  ];
1912
1913
  var PRODUCT_NOTIFICATION_RETRY_DELAYS_MS = [
1913
1914
  0,
@@ -5987,9 +5988,12 @@ var DeeplineClient = class {
5987
5988
  const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
5988
5989
  return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
5989
5990
  }
5990
- /** Select the Slack channel used for product notifications. */
5991
- async setNotificationSlack(channel) {
5992
- return this.http.put("/api/v2/settings/notifications", { channel });
5991
+ /** Select a Slack channel or direct member used for product notifications. */
5992
+ async setNotificationSlack(destination) {
5993
+ return this.http.put(
5994
+ "/api/v2/settings/notifications",
5995
+ typeof destination === "string" ? { channel: destination } : destination
5996
+ );
5993
5997
  }
5994
5998
  /** Send one synchronous test ping and return Slack's delivery result. */
5995
5999
  async testNotificationSlack() {
@@ -6627,14 +6631,16 @@ var DeeplineClient = class {
6627
6631
  { method: "PATCH", body: patch }
6628
6632
  );
6629
6633
  }
6630
- /**
6631
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
6632
- * resource unless `localOnly`; `dryRun` returns the delete plan. Prefer
6633
- * `client.monitors.delete(...)`.
6634
- */
6634
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. Prefer `client.monitors.delete(...)`. */
6635
6635
  async deleteMonitor(key, options) {
6636
+ if (options?.localOnly === true) {
6637
+ throw new DeeplineError(
6638
+ "localOnly monitor deletion is no longer supported. Monitor deletion always deprovisions the upstream provider resource.",
6639
+ void 0,
6640
+ "MONITOR_LOCAL_ONLY_DELETE_NOT_SUPPORTED"
6641
+ );
6642
+ }
6636
6643
  const params = new URLSearchParams();
6637
- if (options?.localOnly) params.set("local_only", "true");
6638
6644
  if (options?.dryRun) params.set("dry_run", "true");
6639
6645
  const query = params.toString();
6640
6646
  return this.http.request(
@@ -32984,13 +32990,12 @@ function assertMonitorDryRunAcknowledged(payload, context) {
32984
32990
  `${context.command} --dry-run: the server response did not acknowledge dry-run mode (missing "dry_run": true). This Deepline server may not support --dry-run for ${context.mutation}, so the response cannot be trusted as a plan. Nothing was rendered as a plan. Verify current state with \`deepline monitors get <key> --json\`, and re-run without --dry-run only when you intend the real ${context.mutation}.`
32985
32991
  );
32986
32992
  }
32987
- function monitorDeleteRequiresYesError(key, options = {}) {
32988
- const flags = options.localOnly ? " --local-only" : "";
32993
+ function monitorDeleteRequiresYesError(key) {
32989
32994
  return new MonitorsUsageError(
32990
- `monitors delete is destructive: it deletes monitor "${key}"${options.localOnly ? " (Deepline record only)" : " and deprovisions its upstream provider resource"}. Non-interactive runs must confirm with --yes:
32991
- deepline monitors delete ${key}${flags} --yes
32995
+ `monitors delete is destructive: it deletes monitor "${key}" and deprovisions its upstream provider resource. Non-interactive runs must confirm with --yes:
32996
+ deepline monitors delete ${key} --yes
32992
32997
  Preview the plan first with:
32993
- deepline monitors delete ${key}${flags} --dry-run`
32998
+ deepline monitors delete ${key} --dry-run`
32994
32999
  );
32995
33000
  }
32996
33001
  async function handleMonitorsStatus(options) {
@@ -33097,17 +33102,19 @@ function renderDeployedListText(payload, requestedStatus) {
33097
33102
  const name = asString(entry.name);
33098
33103
  const outputTable = asString(entry.output_table);
33099
33104
  const webhookState = asString(entry.webhook_state);
33105
+ const executionType = asString(entry.execution_type);
33100
33106
  const hasLastReceivedEvent = "last_received_event" in entry;
33101
33107
  const lastReceivedEvent = asString(entry.last_received_event);
33102
33108
  const boundPlays = Array.isArray(entry.bound_plays) ? entry.bound_plays.length : void 0;
33103
33109
  lines.push(
33104
33110
  ` ${key}${status ? ` ${status}` : ""}${tool ? ` ${tool}` : ""}${name ? ` (${name})` : ""}`
33105
33111
  );
33106
- if (outputTable || webhookState || boundPlays !== void 0) {
33112
+ if (outputTable || webhookState || executionType || boundPlays !== void 0) {
33107
33113
  lines.push(
33108
33114
  ` ${[
33109
33115
  outputTable ? `table: ${outputTable}` : null,
33110
33116
  webhookState ? `webhook: ${webhookState}` : null,
33117
+ executionType ? `execution: ${executionType}` : null,
33111
33118
  boundPlays !== void 0 ? `bound Plays: ${boundPlays}` : null
33112
33119
  ].filter(Boolean).join(" ")}`
33113
33120
  );
@@ -33370,15 +33377,14 @@ async function handleMonitorsValidate(key, options) {
33370
33377
  printCommandEnvelope(result, { json: options.json });
33371
33378
  if (result.valid === false) process.exitCode = 7;
33372
33379
  }
33373
- async function confirmMonitorDelete(key, options) {
33380
+ async function confirmMonitorDelete(key) {
33374
33381
  const rl = (0, import_promises8.createInterface)({
33375
33382
  input: process.stdin,
33376
33383
  output: process.stderr
33377
33384
  });
33378
33385
  try {
33379
- const consequence = options.localOnly ? "This removes the Deepline record only (the upstream provider resource is left in place)." : "This deprovisions the upstream provider resource.";
33380
33386
  const answer = await rl.question(
33381
- `Delete monitor "${key}"? ${consequence} [y/N] `
33387
+ `Delete monitor "${key}"? This deprovisions the upstream provider resource. [y/N] `
33382
33388
  );
33383
33389
  return /^y(es)?$/i.test(answer.trim());
33384
33390
  } finally {
@@ -33388,10 +33394,7 @@ async function confirmMonitorDelete(key, options) {
33388
33394
  async function handleMonitorsDelete(key, options) {
33389
33395
  const client2 = new DeeplineClient();
33390
33396
  if (options.dryRun) {
33391
- const payload2 = await client2.monitors.delete(key, {
33392
- ...options.localOnly ? { localOnly: true } : {},
33393
- dryRun: true
33394
- });
33397
+ const payload2 = await client2.monitors.delete(key, { dryRun: true });
33395
33398
  assertMonitorDryRunAcknowledged(payload2, {
33396
33399
  command: "deepline monitors delete",
33397
33400
  mutation: "delete"
@@ -33405,13 +33408,9 @@ async function handleMonitorsDelete(key, options) {
33405
33408
  if (!options.yes) {
33406
33409
  const interactive = process.stdout.isTTY === true && process.stdin.isTTY === true;
33407
33410
  if (!interactive) {
33408
- throw monitorDeleteRequiresYesError(key, {
33409
- localOnly: options.localOnly
33410
- });
33411
+ throw monitorDeleteRequiresYesError(key);
33411
33412
  }
33412
- const confirmed = await confirmMonitorDelete(key, {
33413
- localOnly: options.localOnly
33414
- });
33413
+ const confirmed = await confirmMonitorDelete(key);
33415
33414
  if (!confirmed) {
33416
33415
  process.stderr.write(`Aborted. Monitor "${key}" was not deleted.
33417
33416
  `);
@@ -33419,9 +33418,7 @@ async function handleMonitorsDelete(key, options) {
33419
33418
  return;
33420
33419
  }
33421
33420
  }
33422
- const payload = await client2.monitors.delete(key, {
33423
- ...options.localOnly ? { localOnly: true } : {}
33424
- });
33421
+ const payload = await client2.monitors.delete(key);
33425
33422
  printCommandEnvelope(payload, { json: options.json });
33426
33423
  }
33427
33424
  async function handleMonitorsUpdate(key, patch, options) {
@@ -33622,8 +33619,10 @@ Notes:
33622
33619
  \`deepline monitors available deepline_native.company_radar --json\`.
33623
33620
  For a bounded urgent Deepline Native preview, use
33624
33621
  controls.execution_type="priority". Deepline injects the upstream marker;
33625
- do not author custom_fields yourself. Priority is capped at ten active or
33626
- in-flight radars per org and is not for regular or bulk-scale monitoring.
33622
+ do not author custom_fields yourself. Priority is capped at ten upstream or
33623
+ reserved recovery, replacement, or reactivation slots per org. Use
33624
+ \`monitors list --status all\` to find priority holders; it is not for regular
33625
+ or bulk-scale monitoring.
33627
33626
 
33628
33627
  Examples:
33629
33628
  deepline monitors check '{"key":"job-openings","tool":"deepline_native.company_radar","payload":{"domain":"stripe.com","radar_type":"company_job_openings"}}'
@@ -33651,7 +33650,7 @@ Notes:
33651
33650
  for a patch-style change.
33652
33651
  For a bounded urgent Deepline Native preview, set
33653
33652
  controls.execution_type="priority". Deepline injects the provider custom
33654
- field and enforces a ten-radar per-org cap; do not use it for regular or bulk
33653
+ field and enforces a ten-slot per-org cap; do not use it for regular or bulk
33655
33654
  monitoring, and do not delete another radar automatically to make room.
33656
33655
 
33657
33656
  Examples:
@@ -33698,20 +33697,17 @@ Examples:
33698
33697
  "after",
33699
33698
  `
33700
33699
  Notes:
33701
- DESTRUCTIVE. By default deprovisions the upstream provider resource; pass
33702
- --local-only to remove only the Deepline-managed record.
33703
- --dry-run shows the delete plan without deleting anything.
33700
+ DESTRUCTIVE. Deprovisions the upstream provider resource and removes its
33701
+ Deepline-managed monitor. Retries are safe: a delete that already reached its requested terminal state
33702
+ returns already_deleted without making another upstream call.
33703
+ --dry-run shows the delete plan without deleting anything.
33704
33704
  Interactive terminals get a y/N confirmation; non-interactive runs (agents,
33705
33705
  scripts, pipes) must pass --yes.
33706
33706
 
33707
33707
  Examples:
33708
33708
  deepline monitors delete my-monitor --dry-run
33709
33709
  deepline monitors delete my-monitor --yes --json
33710
- deepline monitors delete my-monitor --local-only --yes --json
33711
33710
  `
33712
- ).option(
33713
- "--local-only",
33714
- "Remove only the Deepline-managed record, leaving the upstream resource"
33715
33711
  ).option("--dry-run", "Show the delete plan without deleting anything").option(
33716
33712
  "--yes",
33717
33713
  "Skip the confirmation prompt (required non-interactively)"
@@ -33754,10 +33750,7 @@ Examples:
33754
33750
  )
33755
33751
  ).action(monitorsAction(handleMonitorsUpdate));
33756
33752
  withJsonOption(
33757
- deployed.command("delete <key>").description("Alias of `monitors delete <key>`.").option(
33758
- "--local-only",
33759
- "Remove only the Deepline-managed record, leaving the upstream resource"
33760
- ).option("--dry-run", "Show the delete plan without deleting anything").option(
33753
+ deployed.command("delete <key>").description("Alias of `monitors delete <key>`.").option("--dry-run", "Show the delete plan without deleting anything").option(
33761
33754
  "--yes",
33762
33755
  "Skip the confirmation prompt (required non-interactively)"
33763
33756
  )
@@ -36131,7 +36124,7 @@ async function printSettings(options) {
36131
36124
  title: "Slack destination",
36132
36125
  lines: slack ? [
36133
36126
  `status: ${String(slack.status ?? "unknown")}`,
36134
- `channel: #${String(slack.channelName ?? slack.channelId ?? "unknown")}`,
36127
+ `${slack.targetKind === "member" ? "member" : "channel"}: ${slack.targetKind === "member" ? "@" : "#"}${String(slack.channelName ?? slack.channelId ?? "unknown")}`,
36135
36128
  ...slack.lastErrorMessage ? [`issue: ${String(slack.lastErrorMessage)}`] : []
36136
36129
  ] : ["Not configured."]
36137
36130
  },
@@ -36235,27 +36228,32 @@ notifications; there is no general integrations CLI.
36235
36228
  { json: options.json }
36236
36229
  );
36237
36230
  });
36238
- notifications.command("set").description("Set a product-notification destination.").argument("<destination>", "Destination kind (currently slack)").requiredOption("--channel <channel>", "Slack channel name or ID").option("--dry-run", "Validate intent without changing settings").option("--json", "Emit JSON output").action(
36231
+ notifications.command("set").description("Set a product-notification destination.").argument("<destination>", "Destination kind (currently slack)").option("--channel <channel>", "Slack channel name or ID").option("--member-id <id>", "Slack member ID, for example U0123456789").option("--dry-run", "Validate intent without changing settings").option("--json", "Emit JSON output").action(
36239
36232
  async (destination, options) => {
36240
36233
  if (destination !== "slack")
36241
36234
  throw new Error("Only slack is supported.");
36235
+ if (Boolean(options.channel) === Boolean(options.memberId)) {
36236
+ throw new Error("Provide exactly one of --channel or --member-id.");
36237
+ }
36238
+ const target = options.memberId ? { memberId: options.memberId } : options.channel;
36242
36239
  if (options.dryRun) {
36243
36240
  printCommandEnvelope(
36244
- { dryRun: true, destination, channel: options.channel },
36241
+ { dryRun: true, destination, target },
36245
36242
  { json: options.json }
36246
36243
  );
36247
36244
  return;
36248
36245
  }
36249
- const result = await new DeeplineClient().setNotificationSlack(
36250
- options.channel
36251
- );
36246
+ const result = await new DeeplineClient().setNotificationSlack(target);
36252
36247
  printCommandEnvelope(
36253
36248
  {
36254
36249
  ok: true,
36255
36250
  result,
36256
36251
  render: {
36257
36252
  sections: [
36258
- { title: "Slack destination saved", lines: [options.channel] }
36253
+ {
36254
+ title: "Slack destination saved",
36255
+ lines: [options.memberId ?? options.channel]
36256
+ }
36259
36257
  ],
36260
36258
  actions: [
36261
36259
  {
@@ -36457,9 +36455,22 @@ function notificationByName(settings, reference) {
36457
36455
  function parseSlackTarget(value) {
36458
36456
  const match = /^slack:(.+)$/i.exec(value.trim());
36459
36457
  if (!match?.[1]) {
36460
- throw new Error("Use --to slack:#channel.");
36458
+ throw new Error(
36459
+ "Use --to slack:#channel or --to slack:member:U0123456789."
36460
+ );
36461
36461
  }
36462
- return match[1];
36462
+ const target = match[1].trim();
36463
+ const member = /^member:([UW][A-Z0-9]{8,})$/i.exec(target);
36464
+ if (member?.[1]) return { memberId: member[1].toUpperCase() };
36465
+ if (target.toLowerCase().startsWith("member:")) {
36466
+ throw new Error(
36467
+ "Slack member IDs must look like slack:member:U0123456789."
36468
+ );
36469
+ }
36470
+ return { channel: target };
36471
+ }
36472
+ function displaySlackTarget(target) {
36473
+ return `${target.kind === "member" ? "@" : "#"}${target.name}`;
36463
36474
  }
36464
36475
  function collectEvent(value, previous = []) {
36465
36476
  return [...previous, value];
@@ -36472,10 +36483,11 @@ Examples:
36472
36483
  deepline notifications events
36473
36484
  deepline notifications slack channels --search pipeline
36474
36485
  deepline notifications add pipeline-watchdog --to slack:#pipeline-alerts --for play.cron.failed
36486
+ deepline notifications add owner-alerts --to slack:member:U0123456789 --for play.cron.failed
36475
36487
  deepline notifications test pipeline-watchdog
36476
36488
 
36477
36489
  Slack connections are managed in Dashboard \u2192 Integrations. This command only
36478
- chooses the connected Slack channel and the events it receives.
36490
+ chooses the connected Slack channel or member and the events it receives.
36479
36491
  `
36480
36492
  );
36481
36493
  notifications.command("events").description("List the Play events available to a notification.").option("--json", "Emit JSON output").action(async (options) => {
@@ -36506,7 +36518,7 @@ chooses the connected Slack channel and the events it receives.
36506
36518
  id: rule.id,
36507
36519
  name: rule.name,
36508
36520
  enabled: rule.enabled,
36509
- target: `#${rule.target.name}`,
36521
+ target: displaySlackTarget(rule.target),
36510
36522
  eventTypes: rule.eventTypes
36511
36523
  })) : rules,
36512
36524
  count: rules.length,
@@ -36515,7 +36527,7 @@ chooses the connected Slack channel and the events it receives.
36515
36527
  {
36516
36528
  title: "notifications",
36517
36529
  lines: rules.length ? rules.map(
36518
- (rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192 #${rule.target.name} (${rule.eventTypes.join(", ") || "no events"})`
36530
+ (rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192 ${displaySlackTarget(rule.target)} (${rule.eventTypes.join(", ") || "no events"})`
36519
36531
  ) : [
36520
36532
  "None yet. Add one with deepline notifications add <name> --to slack:#channel --for play.cron.failed."
36521
36533
  ]
@@ -36535,7 +36547,7 @@ chooses the connected Slack channel and the events it receives.
36535
36547
  });
36536
36548
  notifications.command("add").argument("<name>", "Short stable name, for example pipeline-watchdog").requiredOption(
36537
36549
  "--to <target>",
36538
- "Target, for example slack:#pipeline-alerts"
36550
+ "Target, for example slack:#pipeline-alerts or slack:member:U0123456789"
36539
36551
  ).option(
36540
36552
  "--for <event>",
36541
36553
  "Event ID; repeat for more events. Run notifications events to list them",
@@ -36546,7 +36558,7 @@ chooses the connected Slack channel and the events it receives.
36546
36558
  if (!options.for.length) {
36547
36559
  throw new Error("Choose at least one event with --for <event>.");
36548
36560
  }
36549
- const channel = parseSlackTarget(options.to);
36561
+ const target = parseSlackTarget(options.to);
36550
36562
  if (options.dryRun) {
36551
36563
  printCommandEnvelope(
36552
36564
  { dryRun: true, name, target: options.to, eventTypes: options.for },
@@ -36557,7 +36569,7 @@ chooses the connected Slack channel and the events it receives.
36557
36569
  const result = await new DeeplineClient().createNotification({
36558
36570
  name,
36559
36571
  provider: "slack",
36560
- channel,
36572
+ ...target,
36561
36573
  eventTypes: options.for
36562
36574
  });
36563
36575
  printCommandEnvelope(
@@ -36577,7 +36589,10 @@ chooses the connected Slack channel and the events it receives.
36577
36589
  );
36578
36590
  }
36579
36591
  );
36580
- notifications.command("edit").argument("<name>", "Notification name or ID").option("--to <target>", "New target, for example slack:#pipeline-alerts").option(
36592
+ notifications.command("edit").argument("<name>", "Notification name or ID").option(
36593
+ "--to <target>",
36594
+ "New target, for example slack:#pipeline-alerts or slack:member:U0123456789"
36595
+ ).option(
36581
36596
  "--for <event>",
36582
36597
  "Replace selected events; repeat for more events. Run notifications events to list them",
36583
36598
  collectEvent,
@@ -36586,18 +36601,18 @@ chooses the connected Slack channel and the events it receives.
36586
36601
  async (name, options) => {
36587
36602
  const client2 = new DeeplineClient();
36588
36603
  const rule = notificationByName(await client2.getNotifications(), name);
36589
- const channel = options.to ? parseSlackTarget(options.to) : rule.target.name;
36604
+ const target = options.to ? parseSlackTarget(options.to) : rule.target.kind === "member" ? { memberId: rule.target.id } : { channel: rule.target.name };
36590
36605
  const eventTypes = options.for.length ? options.for : rule.eventTypes;
36591
36606
  if (options.dryRun) {
36592
36607
  printCommandEnvelope(
36593
- { dryRun: true, notification: rule.id, channel, eventTypes },
36608
+ { dryRun: true, notification: rule.id, target, eventTypes },
36594
36609
  { json: options.json }
36595
36610
  );
36596
36611
  return;
36597
36612
  }
36598
36613
  const result = await client2.updateNotification(rule.id, {
36599
36614
  name: rule.name,
36600
- channel,
36615
+ ...target,
36601
36616
  eventTypes
36602
36617
  });
36603
36618
  printCommandEnvelope({ notification: result }, { json: options.json });
@@ -1033,7 +1033,7 @@ var SDK_RELEASE = {
1033
1033
  // 0.3.0 introduces raw-v2: complete scrubbed provider responses are
1034
1034
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
1035
1035
  // getters keep their established compatibility behavior.
1036
- version: "0.3.13",
1036
+ version: "0.3.15",
1037
1037
  updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
1038
1038
  contracts: {
1039
1039
  api: {
@@ -1893,7 +1893,8 @@ var PRODUCT_NOTIFICATION_EVENT_TYPE_SET = new Set(
1893
1893
  var PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
1894
1894
  "channels:read",
1895
1895
  "chat:write",
1896
- "groups:read"
1896
+ "groups:read",
1897
+ "im:write"
1897
1898
  ];
1898
1899
  var PRODUCT_NOTIFICATION_RETRY_DELAYS_MS = [
1899
1900
  0,
@@ -5973,9 +5974,12 @@ var DeeplineClient = class {
5973
5974
  const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
5974
5975
  return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
5975
5976
  }
5976
- /** Select the Slack channel used for product notifications. */
5977
- async setNotificationSlack(channel) {
5978
- return this.http.put("/api/v2/settings/notifications", { channel });
5977
+ /** Select a Slack channel or direct member used for product notifications. */
5978
+ async setNotificationSlack(destination) {
5979
+ return this.http.put(
5980
+ "/api/v2/settings/notifications",
5981
+ typeof destination === "string" ? { channel: destination } : destination
5982
+ );
5979
5983
  }
5980
5984
  /** Send one synchronous test ping and return Slack's delivery result. */
5981
5985
  async testNotificationSlack() {
@@ -6613,14 +6617,16 @@ var DeeplineClient = class {
6613
6617
  { method: "PATCH", body: patch }
6614
6618
  );
6615
6619
  }
6616
- /**
6617
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
6618
- * resource unless `localOnly`; `dryRun` returns the delete plan. Prefer
6619
- * `client.monitors.delete(...)`.
6620
- */
6620
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. Prefer `client.monitors.delete(...)`. */
6621
6621
  async deleteMonitor(key, options) {
6622
+ if (options?.localOnly === true) {
6623
+ throw new DeeplineError(
6624
+ "localOnly monitor deletion is no longer supported. Monitor deletion always deprovisions the upstream provider resource.",
6625
+ void 0,
6626
+ "MONITOR_LOCAL_ONLY_DELETE_NOT_SUPPORTED"
6627
+ );
6628
+ }
6622
6629
  const params = new URLSearchParams();
6623
- if (options?.localOnly) params.set("local_only", "true");
6624
6630
  if (options?.dryRun) params.set("dry_run", "true");
6625
6631
  const query = params.toString();
6626
6632
  return this.http.request(
@@ -33054,13 +33060,12 @@ function assertMonitorDryRunAcknowledged(payload, context) {
33054
33060
  `${context.command} --dry-run: the server response did not acknowledge dry-run mode (missing "dry_run": true). This Deepline server may not support --dry-run for ${context.mutation}, so the response cannot be trusted as a plan. Nothing was rendered as a plan. Verify current state with \`deepline monitors get <key> --json\`, and re-run without --dry-run only when you intend the real ${context.mutation}.`
33055
33061
  );
33056
33062
  }
33057
- function monitorDeleteRequiresYesError(key, options = {}) {
33058
- const flags = options.localOnly ? " --local-only" : "";
33063
+ function monitorDeleteRequiresYesError(key) {
33059
33064
  return new MonitorsUsageError(
33060
- `monitors delete is destructive: it deletes monitor "${key}"${options.localOnly ? " (Deepline record only)" : " and deprovisions its upstream provider resource"}. Non-interactive runs must confirm with --yes:
33061
- deepline monitors delete ${key}${flags} --yes
33065
+ `monitors delete is destructive: it deletes monitor "${key}" and deprovisions its upstream provider resource. Non-interactive runs must confirm with --yes:
33066
+ deepline monitors delete ${key} --yes
33062
33067
  Preview the plan first with:
33063
- deepline monitors delete ${key}${flags} --dry-run`
33068
+ deepline monitors delete ${key} --dry-run`
33064
33069
  );
33065
33070
  }
33066
33071
  async function handleMonitorsStatus(options) {
@@ -33167,17 +33172,19 @@ function renderDeployedListText(payload, requestedStatus) {
33167
33172
  const name = asString(entry.name);
33168
33173
  const outputTable = asString(entry.output_table);
33169
33174
  const webhookState = asString(entry.webhook_state);
33175
+ const executionType = asString(entry.execution_type);
33170
33176
  const hasLastReceivedEvent = "last_received_event" in entry;
33171
33177
  const lastReceivedEvent = asString(entry.last_received_event);
33172
33178
  const boundPlays = Array.isArray(entry.bound_plays) ? entry.bound_plays.length : void 0;
33173
33179
  lines.push(
33174
33180
  ` ${key}${status ? ` ${status}` : ""}${tool ? ` ${tool}` : ""}${name ? ` (${name})` : ""}`
33175
33181
  );
33176
- if (outputTable || webhookState || boundPlays !== void 0) {
33182
+ if (outputTable || webhookState || executionType || boundPlays !== void 0) {
33177
33183
  lines.push(
33178
33184
  ` ${[
33179
33185
  outputTable ? `table: ${outputTable}` : null,
33180
33186
  webhookState ? `webhook: ${webhookState}` : null,
33187
+ executionType ? `execution: ${executionType}` : null,
33181
33188
  boundPlays !== void 0 ? `bound Plays: ${boundPlays}` : null
33182
33189
  ].filter(Boolean).join(" ")}`
33183
33190
  );
@@ -33440,15 +33447,14 @@ async function handleMonitorsValidate(key, options) {
33440
33447
  printCommandEnvelope(result, { json: options.json });
33441
33448
  if (result.valid === false) process.exitCode = 7;
33442
33449
  }
33443
- async function confirmMonitorDelete(key, options) {
33450
+ async function confirmMonitorDelete(key) {
33444
33451
  const rl = createInterface({
33445
33452
  input: process.stdin,
33446
33453
  output: process.stderr
33447
33454
  });
33448
33455
  try {
33449
- const consequence = options.localOnly ? "This removes the Deepline record only (the upstream provider resource is left in place)." : "This deprovisions the upstream provider resource.";
33450
33456
  const answer = await rl.question(
33451
- `Delete monitor "${key}"? ${consequence} [y/N] `
33457
+ `Delete monitor "${key}"? This deprovisions the upstream provider resource. [y/N] `
33452
33458
  );
33453
33459
  return /^y(es)?$/i.test(answer.trim());
33454
33460
  } finally {
@@ -33458,10 +33464,7 @@ async function confirmMonitorDelete(key, options) {
33458
33464
  async function handleMonitorsDelete(key, options) {
33459
33465
  const client2 = new DeeplineClient();
33460
33466
  if (options.dryRun) {
33461
- const payload2 = await client2.monitors.delete(key, {
33462
- ...options.localOnly ? { localOnly: true } : {},
33463
- dryRun: true
33464
- });
33467
+ const payload2 = await client2.monitors.delete(key, { dryRun: true });
33465
33468
  assertMonitorDryRunAcknowledged(payload2, {
33466
33469
  command: "deepline monitors delete",
33467
33470
  mutation: "delete"
@@ -33475,13 +33478,9 @@ async function handleMonitorsDelete(key, options) {
33475
33478
  if (!options.yes) {
33476
33479
  const interactive = process.stdout.isTTY === true && process.stdin.isTTY === true;
33477
33480
  if (!interactive) {
33478
- throw monitorDeleteRequiresYesError(key, {
33479
- localOnly: options.localOnly
33480
- });
33481
+ throw monitorDeleteRequiresYesError(key);
33481
33482
  }
33482
- const confirmed = await confirmMonitorDelete(key, {
33483
- localOnly: options.localOnly
33484
- });
33483
+ const confirmed = await confirmMonitorDelete(key);
33485
33484
  if (!confirmed) {
33486
33485
  process.stderr.write(`Aborted. Monitor "${key}" was not deleted.
33487
33486
  `);
@@ -33489,9 +33488,7 @@ async function handleMonitorsDelete(key, options) {
33489
33488
  return;
33490
33489
  }
33491
33490
  }
33492
- const payload = await client2.monitors.delete(key, {
33493
- ...options.localOnly ? { localOnly: true } : {}
33494
- });
33491
+ const payload = await client2.monitors.delete(key);
33495
33492
  printCommandEnvelope(payload, { json: options.json });
33496
33493
  }
33497
33494
  async function handleMonitorsUpdate(key, patch, options) {
@@ -33692,8 +33689,10 @@ Notes:
33692
33689
  \`deepline monitors available deepline_native.company_radar --json\`.
33693
33690
  For a bounded urgent Deepline Native preview, use
33694
33691
  controls.execution_type="priority". Deepline injects the upstream marker;
33695
- do not author custom_fields yourself. Priority is capped at ten active or
33696
- in-flight radars per org and is not for regular or bulk-scale monitoring.
33692
+ do not author custom_fields yourself. Priority is capped at ten upstream or
33693
+ reserved recovery, replacement, or reactivation slots per org. Use
33694
+ \`monitors list --status all\` to find priority holders; it is not for regular
33695
+ or bulk-scale monitoring.
33697
33696
 
33698
33697
  Examples:
33699
33698
  deepline monitors check '{"key":"job-openings","tool":"deepline_native.company_radar","payload":{"domain":"stripe.com","radar_type":"company_job_openings"}}'
@@ -33721,7 +33720,7 @@ Notes:
33721
33720
  for a patch-style change.
33722
33721
  For a bounded urgent Deepline Native preview, set
33723
33722
  controls.execution_type="priority". Deepline injects the provider custom
33724
- field and enforces a ten-radar per-org cap; do not use it for regular or bulk
33723
+ field and enforces a ten-slot per-org cap; do not use it for regular or bulk
33725
33724
  monitoring, and do not delete another radar automatically to make room.
33726
33725
 
33727
33726
  Examples:
@@ -33768,20 +33767,17 @@ Examples:
33768
33767
  "after",
33769
33768
  `
33770
33769
  Notes:
33771
- DESTRUCTIVE. By default deprovisions the upstream provider resource; pass
33772
- --local-only to remove only the Deepline-managed record.
33773
- --dry-run shows the delete plan without deleting anything.
33770
+ DESTRUCTIVE. Deprovisions the upstream provider resource and removes its
33771
+ Deepline-managed monitor. Retries are safe: a delete that already reached its requested terminal state
33772
+ returns already_deleted without making another upstream call.
33773
+ --dry-run shows the delete plan without deleting anything.
33774
33774
  Interactive terminals get a y/N confirmation; non-interactive runs (agents,
33775
33775
  scripts, pipes) must pass --yes.
33776
33776
 
33777
33777
  Examples:
33778
33778
  deepline monitors delete my-monitor --dry-run
33779
33779
  deepline monitors delete my-monitor --yes --json
33780
- deepline monitors delete my-monitor --local-only --yes --json
33781
33780
  `
33782
- ).option(
33783
- "--local-only",
33784
- "Remove only the Deepline-managed record, leaving the upstream resource"
33785
33781
  ).option("--dry-run", "Show the delete plan without deleting anything").option(
33786
33782
  "--yes",
33787
33783
  "Skip the confirmation prompt (required non-interactively)"
@@ -33824,10 +33820,7 @@ Examples:
33824
33820
  )
33825
33821
  ).action(monitorsAction(handleMonitorsUpdate));
33826
33822
  withJsonOption(
33827
- deployed.command("delete <key>").description("Alias of `monitors delete <key>`.").option(
33828
- "--local-only",
33829
- "Remove only the Deepline-managed record, leaving the upstream resource"
33830
- ).option("--dry-run", "Show the delete plan without deleting anything").option(
33823
+ deployed.command("delete <key>").description("Alias of `monitors delete <key>`.").option("--dry-run", "Show the delete plan without deleting anything").option(
33831
33824
  "--yes",
33832
33825
  "Skip the confirmation prompt (required non-interactively)"
33833
33826
  )
@@ -36209,7 +36202,7 @@ async function printSettings(options) {
36209
36202
  title: "Slack destination",
36210
36203
  lines: slack ? [
36211
36204
  `status: ${String(slack.status ?? "unknown")}`,
36212
- `channel: #${String(slack.channelName ?? slack.channelId ?? "unknown")}`,
36205
+ `${slack.targetKind === "member" ? "member" : "channel"}: ${slack.targetKind === "member" ? "@" : "#"}${String(slack.channelName ?? slack.channelId ?? "unknown")}`,
36213
36206
  ...slack.lastErrorMessage ? [`issue: ${String(slack.lastErrorMessage)}`] : []
36214
36207
  ] : ["Not configured."]
36215
36208
  },
@@ -36313,27 +36306,32 @@ notifications; there is no general integrations CLI.
36313
36306
  { json: options.json }
36314
36307
  );
36315
36308
  });
36316
- notifications.command("set").description("Set a product-notification destination.").argument("<destination>", "Destination kind (currently slack)").requiredOption("--channel <channel>", "Slack channel name or ID").option("--dry-run", "Validate intent without changing settings").option("--json", "Emit JSON output").action(
36309
+ notifications.command("set").description("Set a product-notification destination.").argument("<destination>", "Destination kind (currently slack)").option("--channel <channel>", "Slack channel name or ID").option("--member-id <id>", "Slack member ID, for example U0123456789").option("--dry-run", "Validate intent without changing settings").option("--json", "Emit JSON output").action(
36317
36310
  async (destination, options) => {
36318
36311
  if (destination !== "slack")
36319
36312
  throw new Error("Only slack is supported.");
36313
+ if (Boolean(options.channel) === Boolean(options.memberId)) {
36314
+ throw new Error("Provide exactly one of --channel or --member-id.");
36315
+ }
36316
+ const target = options.memberId ? { memberId: options.memberId } : options.channel;
36320
36317
  if (options.dryRun) {
36321
36318
  printCommandEnvelope(
36322
- { dryRun: true, destination, channel: options.channel },
36319
+ { dryRun: true, destination, target },
36323
36320
  { json: options.json }
36324
36321
  );
36325
36322
  return;
36326
36323
  }
36327
- const result = await new DeeplineClient().setNotificationSlack(
36328
- options.channel
36329
- );
36324
+ const result = await new DeeplineClient().setNotificationSlack(target);
36330
36325
  printCommandEnvelope(
36331
36326
  {
36332
36327
  ok: true,
36333
36328
  result,
36334
36329
  render: {
36335
36330
  sections: [
36336
- { title: "Slack destination saved", lines: [options.channel] }
36331
+ {
36332
+ title: "Slack destination saved",
36333
+ lines: [options.memberId ?? options.channel]
36334
+ }
36337
36335
  ],
36338
36336
  actions: [
36339
36337
  {
@@ -36535,9 +36533,22 @@ function notificationByName(settings, reference) {
36535
36533
  function parseSlackTarget(value) {
36536
36534
  const match = /^slack:(.+)$/i.exec(value.trim());
36537
36535
  if (!match?.[1]) {
36538
- throw new Error("Use --to slack:#channel.");
36536
+ throw new Error(
36537
+ "Use --to slack:#channel or --to slack:member:U0123456789."
36538
+ );
36539
36539
  }
36540
- return match[1];
36540
+ const target = match[1].trim();
36541
+ const member = /^member:([UW][A-Z0-9]{8,})$/i.exec(target);
36542
+ if (member?.[1]) return { memberId: member[1].toUpperCase() };
36543
+ if (target.toLowerCase().startsWith("member:")) {
36544
+ throw new Error(
36545
+ "Slack member IDs must look like slack:member:U0123456789."
36546
+ );
36547
+ }
36548
+ return { channel: target };
36549
+ }
36550
+ function displaySlackTarget(target) {
36551
+ return `${target.kind === "member" ? "@" : "#"}${target.name}`;
36541
36552
  }
36542
36553
  function collectEvent(value, previous = []) {
36543
36554
  return [...previous, value];
@@ -36550,10 +36561,11 @@ Examples:
36550
36561
  deepline notifications events
36551
36562
  deepline notifications slack channels --search pipeline
36552
36563
  deepline notifications add pipeline-watchdog --to slack:#pipeline-alerts --for play.cron.failed
36564
+ deepline notifications add owner-alerts --to slack:member:U0123456789 --for play.cron.failed
36553
36565
  deepline notifications test pipeline-watchdog
36554
36566
 
36555
36567
  Slack connections are managed in Dashboard \u2192 Integrations. This command only
36556
- chooses the connected Slack channel and the events it receives.
36568
+ chooses the connected Slack channel or member and the events it receives.
36557
36569
  `
36558
36570
  );
36559
36571
  notifications.command("events").description("List the Play events available to a notification.").option("--json", "Emit JSON output").action(async (options) => {
@@ -36584,7 +36596,7 @@ chooses the connected Slack channel and the events it receives.
36584
36596
  id: rule.id,
36585
36597
  name: rule.name,
36586
36598
  enabled: rule.enabled,
36587
- target: `#${rule.target.name}`,
36599
+ target: displaySlackTarget(rule.target),
36588
36600
  eventTypes: rule.eventTypes
36589
36601
  })) : rules,
36590
36602
  count: rules.length,
@@ -36593,7 +36605,7 @@ chooses the connected Slack channel and the events it receives.
36593
36605
  {
36594
36606
  title: "notifications",
36595
36607
  lines: rules.length ? rules.map(
36596
- (rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192 #${rule.target.name} (${rule.eventTypes.join(", ") || "no events"})`
36608
+ (rule) => `${rule.name}: ${rule.enabled ? "on" : "paused"} \u2192 ${displaySlackTarget(rule.target)} (${rule.eventTypes.join(", ") || "no events"})`
36597
36609
  ) : [
36598
36610
  "None yet. Add one with deepline notifications add <name> --to slack:#channel --for play.cron.failed."
36599
36611
  ]
@@ -36613,7 +36625,7 @@ chooses the connected Slack channel and the events it receives.
36613
36625
  });
36614
36626
  notifications.command("add").argument("<name>", "Short stable name, for example pipeline-watchdog").requiredOption(
36615
36627
  "--to <target>",
36616
- "Target, for example slack:#pipeline-alerts"
36628
+ "Target, for example slack:#pipeline-alerts or slack:member:U0123456789"
36617
36629
  ).option(
36618
36630
  "--for <event>",
36619
36631
  "Event ID; repeat for more events. Run notifications events to list them",
@@ -36624,7 +36636,7 @@ chooses the connected Slack channel and the events it receives.
36624
36636
  if (!options.for.length) {
36625
36637
  throw new Error("Choose at least one event with --for <event>.");
36626
36638
  }
36627
- const channel = parseSlackTarget(options.to);
36639
+ const target = parseSlackTarget(options.to);
36628
36640
  if (options.dryRun) {
36629
36641
  printCommandEnvelope(
36630
36642
  { dryRun: true, name, target: options.to, eventTypes: options.for },
@@ -36635,7 +36647,7 @@ chooses the connected Slack channel and the events it receives.
36635
36647
  const result = await new DeeplineClient().createNotification({
36636
36648
  name,
36637
36649
  provider: "slack",
36638
- channel,
36650
+ ...target,
36639
36651
  eventTypes: options.for
36640
36652
  });
36641
36653
  printCommandEnvelope(
@@ -36655,7 +36667,10 @@ chooses the connected Slack channel and the events it receives.
36655
36667
  );
36656
36668
  }
36657
36669
  );
36658
- notifications.command("edit").argument("<name>", "Notification name or ID").option("--to <target>", "New target, for example slack:#pipeline-alerts").option(
36670
+ notifications.command("edit").argument("<name>", "Notification name or ID").option(
36671
+ "--to <target>",
36672
+ "New target, for example slack:#pipeline-alerts or slack:member:U0123456789"
36673
+ ).option(
36659
36674
  "--for <event>",
36660
36675
  "Replace selected events; repeat for more events. Run notifications events to list them",
36661
36676
  collectEvent,
@@ -36664,18 +36679,18 @@ chooses the connected Slack channel and the events it receives.
36664
36679
  async (name, options) => {
36665
36680
  const client2 = new DeeplineClient();
36666
36681
  const rule = notificationByName(await client2.getNotifications(), name);
36667
- const channel = options.to ? parseSlackTarget(options.to) : rule.target.name;
36682
+ const target = options.to ? parseSlackTarget(options.to) : rule.target.kind === "member" ? { memberId: rule.target.id } : { channel: rule.target.name };
36668
36683
  const eventTypes = options.for.length ? options.for : rule.eventTypes;
36669
36684
  if (options.dryRun) {
36670
36685
  printCommandEnvelope(
36671
- { dryRun: true, notification: rule.id, channel, eventTypes },
36686
+ { dryRun: true, notification: rule.id, target, eventTypes },
36672
36687
  { json: options.json }
36673
36688
  );
36674
36689
  return;
36675
36690
  }
36676
36691
  const result = await client2.updateNotification(rule.id, {
36677
36692
  name: rule.name,
36678
- channel,
36693
+ ...target,
36679
36694
  eventTypes
36680
36695
  });
36681
36696
  printCommandEnvelope({ notification: result }, { json: options.json });
package/dist/index.d.mts CHANGED
@@ -1243,6 +1243,7 @@ interface ProductNotification {
1243
1243
  target: {
1244
1244
  id: string;
1245
1245
  name: string;
1246
+ kind?: 'channel' | 'member';
1246
1247
  };
1247
1248
  enabled: boolean;
1248
1249
  status: string;
@@ -1894,6 +1895,24 @@ type EnrichCompiledConfig = {
1894
1895
  };
1895
1896
  };
1896
1897
 
1898
+ type SlackNotificationTarget = {
1899
+ channel: string;
1900
+ memberId?: never;
1901
+ } | {
1902
+ channel?: never;
1903
+ memberId: string;
1904
+ };
1905
+ type CreateNotificationInput = {
1906
+ name: string;
1907
+ provider: 'slack';
1908
+ eventTypes: string[];
1909
+ } & SlackNotificationTarget;
1910
+ type UpdateNotificationInput = {
1911
+ enabled: boolean;
1912
+ } | ({
1913
+ name: string;
1914
+ eventTypes: string[];
1915
+ } & SlackNotificationTarget);
1897
1916
  type IngestionStorageRepairResult = {
1898
1917
  status?: 'repaired';
1899
1918
  connection_grants: {
@@ -2358,12 +2377,8 @@ type MonitorsNamespace = {
2358
2377
  dependents: (key: string) => Promise<MonitorDependents>;
2359
2378
  /** Update a deployed monitor by public key. */
2360
2379
  update: (key: string, patch: Record<string, unknown>) => Promise<MonitorUpdateResult>;
2361
- /**
2362
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
2363
- * resource unless `localOnly` is set. `dryRun` returns the delete plan.
2364
- */
2380
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. */
2365
2381
  delete: (key: string, options?: {
2366
- localOnly?: boolean;
2367
2382
  dryRun?: boolean;
2368
2383
  }) => Promise<MonitorDeleteResult>;
2369
2384
  /** Reactivate a disabled monitor. `dryRun` returns the reactivation cost. */
@@ -3368,8 +3383,10 @@ declare class DeeplineClient {
3368
3383
  isPrivate: boolean;
3369
3384
  }>;
3370
3385
  }>;
3371
- /** Select the Slack channel used for product notifications. */
3372
- setNotificationSlack(channel: string): Promise<unknown>;
3386
+ /** Select a Slack channel or direct member used for product notifications. */
3387
+ setNotificationSlack(destination: string | {
3388
+ memberId: string;
3389
+ }): Promise<unknown>;
3373
3390
  /** Send one synchronous test ping and return Slack's delivery result. */
3374
3391
  testNotificationSlack(): Promise<{
3375
3392
  ok: boolean;
@@ -3402,20 +3419,9 @@ declare class DeeplineClient {
3402
3419
  }>;
3403
3420
  }>;
3404
3421
  /** Create a named notification routed through an existing provider integration. */
3405
- createNotification(input: {
3406
- name: string;
3407
- provider: 'slack';
3408
- channel: string;
3409
- eventTypes: string[];
3410
- }): Promise<unknown>;
3422
+ createNotification(input: CreateNotificationInput): Promise<unknown>;
3411
3423
  /** Update a notification's target, event selection, or enabled state. */
3412
- updateNotification(notificationId: string, input: {
3413
- enabled: boolean;
3414
- } | {
3415
- name: string;
3416
- channel: string;
3417
- eventTypes: string[];
3418
- }): Promise<unknown>;
3424
+ updateNotification(notificationId: string, input: UpdateNotificationInput): Promise<unknown>;
3419
3425
  /** Send a validation ping to one notification. */
3420
3426
  testNotification(notificationId: string): Promise<{
3421
3427
  ok: boolean;
@@ -3711,13 +3717,8 @@ declare class DeeplineClient {
3711
3717
  getMonitorDependents(key: string): Promise<MonitorDependents>;
3712
3718
  /** Update a deployed monitor by public key. Prefer `client.monitors.update(...)`. */
3713
3719
  updateMonitor(key: string, patch: Record<string, unknown>): Promise<MonitorUpdateResult>;
3714
- /**
3715
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
3716
- * resource unless `localOnly`; `dryRun` returns the delete plan. Prefer
3717
- * `client.monitors.delete(...)`.
3718
- */
3720
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. Prefer `client.monitors.delete(...)`. */
3719
3721
  deleteMonitor(key: string, options?: {
3720
- localOnly?: boolean;
3721
3722
  dryRun?: boolean;
3722
3723
  }): Promise<MonitorDeleteResult>;
3723
3724
  /**
package/dist/index.d.ts CHANGED
@@ -1243,6 +1243,7 @@ interface ProductNotification {
1243
1243
  target: {
1244
1244
  id: string;
1245
1245
  name: string;
1246
+ kind?: 'channel' | 'member';
1246
1247
  };
1247
1248
  enabled: boolean;
1248
1249
  status: string;
@@ -1894,6 +1895,24 @@ type EnrichCompiledConfig = {
1894
1895
  };
1895
1896
  };
1896
1897
 
1898
+ type SlackNotificationTarget = {
1899
+ channel: string;
1900
+ memberId?: never;
1901
+ } | {
1902
+ channel?: never;
1903
+ memberId: string;
1904
+ };
1905
+ type CreateNotificationInput = {
1906
+ name: string;
1907
+ provider: 'slack';
1908
+ eventTypes: string[];
1909
+ } & SlackNotificationTarget;
1910
+ type UpdateNotificationInput = {
1911
+ enabled: boolean;
1912
+ } | ({
1913
+ name: string;
1914
+ eventTypes: string[];
1915
+ } & SlackNotificationTarget);
1897
1916
  type IngestionStorageRepairResult = {
1898
1917
  status?: 'repaired';
1899
1918
  connection_grants: {
@@ -2358,12 +2377,8 @@ type MonitorsNamespace = {
2358
2377
  dependents: (key: string) => Promise<MonitorDependents>;
2359
2378
  /** Update a deployed monitor by public key. */
2360
2379
  update: (key: string, patch: Record<string, unknown>) => Promise<MonitorUpdateResult>;
2361
- /**
2362
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
2363
- * resource unless `localOnly` is set. `dryRun` returns the delete plan.
2364
- */
2380
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. */
2365
2381
  delete: (key: string, options?: {
2366
- localOnly?: boolean;
2367
2382
  dryRun?: boolean;
2368
2383
  }) => Promise<MonitorDeleteResult>;
2369
2384
  /** Reactivate a disabled monitor. `dryRun` returns the reactivation cost. */
@@ -3368,8 +3383,10 @@ declare class DeeplineClient {
3368
3383
  isPrivate: boolean;
3369
3384
  }>;
3370
3385
  }>;
3371
- /** Select the Slack channel used for product notifications. */
3372
- setNotificationSlack(channel: string): Promise<unknown>;
3386
+ /** Select a Slack channel or direct member used for product notifications. */
3387
+ setNotificationSlack(destination: string | {
3388
+ memberId: string;
3389
+ }): Promise<unknown>;
3373
3390
  /** Send one synchronous test ping and return Slack's delivery result. */
3374
3391
  testNotificationSlack(): Promise<{
3375
3392
  ok: boolean;
@@ -3402,20 +3419,9 @@ declare class DeeplineClient {
3402
3419
  }>;
3403
3420
  }>;
3404
3421
  /** Create a named notification routed through an existing provider integration. */
3405
- createNotification(input: {
3406
- name: string;
3407
- provider: 'slack';
3408
- channel: string;
3409
- eventTypes: string[];
3410
- }): Promise<unknown>;
3422
+ createNotification(input: CreateNotificationInput): Promise<unknown>;
3411
3423
  /** Update a notification's target, event selection, or enabled state. */
3412
- updateNotification(notificationId: string, input: {
3413
- enabled: boolean;
3414
- } | {
3415
- name: string;
3416
- channel: string;
3417
- eventTypes: string[];
3418
- }): Promise<unknown>;
3424
+ updateNotification(notificationId: string, input: UpdateNotificationInput): Promise<unknown>;
3419
3425
  /** Send a validation ping to one notification. */
3420
3426
  testNotification(notificationId: string): Promise<{
3421
3427
  ok: boolean;
@@ -3711,13 +3717,8 @@ declare class DeeplineClient {
3711
3717
  getMonitorDependents(key: string): Promise<MonitorDependents>;
3712
3718
  /** Update a deployed monitor by public key. Prefer `client.monitors.update(...)`. */
3713
3719
  updateMonitor(key: string, patch: Record<string, unknown>): Promise<MonitorUpdateResult>;
3714
- /**
3715
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
3716
- * resource unless `localOnly`; `dryRun` returns the delete plan. Prefer
3717
- * `client.monitors.delete(...)`.
3718
- */
3720
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. Prefer `client.monitors.delete(...)`. */
3719
3721
  deleteMonitor(key: string, options?: {
3720
- localOnly?: boolean;
3721
3722
  dryRun?: boolean;
3722
3723
  }): Promise<MonitorDeleteResult>;
3723
3724
  /**
package/dist/index.js CHANGED
@@ -783,7 +783,7 @@ var SDK_RELEASE = {
783
783
  // 0.3.0 introduces raw-v2: complete scrubbed provider responses are
784
784
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
785
785
  // getters keep their established compatibility behavior.
786
- version: "0.3.13",
786
+ version: "0.3.15",
787
787
  updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
788
788
  contracts: {
789
789
  api: {
@@ -1643,7 +1643,8 @@ var PRODUCT_NOTIFICATION_EVENT_TYPE_SET = new Set(
1643
1643
  var PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
1644
1644
  "channels:read",
1645
1645
  "chat:write",
1646
- "groups:read"
1646
+ "groups:read",
1647
+ "im:write"
1647
1648
  ];
1648
1649
  var PRODUCT_NOTIFICATION_RETRY_DELAYS_MS = [
1649
1650
  0,
@@ -5723,9 +5724,12 @@ var DeeplineClient = class {
5723
5724
  const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
5724
5725
  return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
5725
5726
  }
5726
- /** Select the Slack channel used for product notifications. */
5727
- async setNotificationSlack(channel) {
5728
- return this.http.put("/api/v2/settings/notifications", { channel });
5727
+ /** Select a Slack channel or direct member used for product notifications. */
5728
+ async setNotificationSlack(destination) {
5729
+ return this.http.put(
5730
+ "/api/v2/settings/notifications",
5731
+ typeof destination === "string" ? { channel: destination } : destination
5732
+ );
5729
5733
  }
5730
5734
  /** Send one synchronous test ping and return Slack's delivery result. */
5731
5735
  async testNotificationSlack() {
@@ -6363,14 +6367,16 @@ var DeeplineClient = class {
6363
6367
  { method: "PATCH", body: patch }
6364
6368
  );
6365
6369
  }
6366
- /**
6367
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
6368
- * resource unless `localOnly`; `dryRun` returns the delete plan. Prefer
6369
- * `client.monitors.delete(...)`.
6370
- */
6370
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. Prefer `client.monitors.delete(...)`. */
6371
6371
  async deleteMonitor(key, options) {
6372
+ if (options?.localOnly === true) {
6373
+ throw new DeeplineError(
6374
+ "localOnly monitor deletion is no longer supported. Monitor deletion always deprovisions the upstream provider resource.",
6375
+ void 0,
6376
+ "MONITOR_LOCAL_ONLY_DELETE_NOT_SUPPORTED"
6377
+ );
6378
+ }
6372
6379
  const params = new URLSearchParams();
6373
- if (options?.localOnly) params.set("local_only", "true");
6374
6380
  if (options?.dryRun) params.set("dry_run", "true");
6375
6381
  const query = params.toString();
6376
6382
  return this.http.request(
package/dist/index.mjs CHANGED
@@ -706,7 +706,7 @@ var SDK_RELEASE = {
706
706
  // 0.3.0 introduces raw-v2: complete scrubbed provider responses are
707
707
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
708
708
  // getters keep their established compatibility behavior.
709
- version: "0.3.13",
709
+ version: "0.3.15",
710
710
  updateSummary: "New raw-v2 tool responses preserve complete scrubbed provider envelopes at toolResponse.rawV2, including JSON:API included resources, links, cursors, and totals. Existing toolResponse.raw and declared getters keep working unchanged.",
711
711
  contracts: {
712
712
  api: {
@@ -1566,7 +1566,8 @@ var PRODUCT_NOTIFICATION_EVENT_TYPE_SET = new Set(
1566
1566
  var PRODUCT_NOTIFICATION_SLACK_OAUTH_SCOPES = [
1567
1567
  "channels:read",
1568
1568
  "chat:write",
1569
- "groups:read"
1569
+ "groups:read",
1570
+ "im:write"
1570
1571
  ];
1571
1572
  var PRODUCT_NOTIFICATION_RETRY_DELAYS_MS = [
1572
1573
  0,
@@ -5646,9 +5647,12 @@ var DeeplineClient = class {
5646
5647
  const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
5647
5648
  return this.http.get(`/api/v2/settings/notifications/channels${suffix}`);
5648
5649
  }
5649
- /** Select the Slack channel used for product notifications. */
5650
- async setNotificationSlack(channel) {
5651
- return this.http.put("/api/v2/settings/notifications", { channel });
5650
+ /** Select a Slack channel or direct member used for product notifications. */
5651
+ async setNotificationSlack(destination) {
5652
+ return this.http.put(
5653
+ "/api/v2/settings/notifications",
5654
+ typeof destination === "string" ? { channel: destination } : destination
5655
+ );
5652
5656
  }
5653
5657
  /** Send one synchronous test ping and return Slack's delivery result. */
5654
5658
  async testNotificationSlack() {
@@ -6286,14 +6290,16 @@ var DeeplineClient = class {
6286
6290
  { method: "PATCH", body: patch }
6287
6291
  );
6288
6292
  }
6289
- /**
6290
- * Delete a deployed monitor by public key. Deprovisions the upstream provider
6291
- * resource unless `localOnly`; `dryRun` returns the delete plan. Prefer
6292
- * `client.monitors.delete(...)`.
6293
- */
6293
+ /** Delete a deployed monitor and its upstream provider resource. `dryRun` returns the delete plan. Prefer `client.monitors.delete(...)`. */
6294
6294
  async deleteMonitor(key, options) {
6295
+ if (options?.localOnly === true) {
6296
+ throw new DeeplineError(
6297
+ "localOnly monitor deletion is no longer supported. Monitor deletion always deprovisions the upstream provider resource.",
6298
+ void 0,
6299
+ "MONITOR_LOCAL_ONLY_DELETE_NOT_SUPPORTED"
6300
+ );
6301
+ }
6295
6302
  const params = new URLSearchParams();
6296
- if (options?.localOnly) params.set("local_only", "true");
6297
6303
  if (options?.dryRun) params.set("dry_run", "true");
6298
6304
  const query = params.toString();
6299
6305
  return this.http.request(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepline",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "GTM data CLI and TypeScript SDK for coding agents",
5
5
  "license": "MIT",
6
6
  "homepage": "https://code.deepline.com",