deepline 0.3.55 → 0.3.56

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.
package/dist/index.js CHANGED
@@ -41,6 +41,11 @@ __export(src_exports, {
41
41
  DeeplineContext: () => DeeplineContext,
42
42
  DeeplineError: () => DeeplineError,
43
43
  JOB_CHANGE_STATUS_VALUES: () => JOB_CHANGE_STATUS_VALUES,
44
+ MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG: () => MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG,
45
+ MONITOR_FLEET_AUTHORING_CONTRACT_EDITION: () => MONITOR_FLEET_AUTHORING_CONTRACT_EDITION,
46
+ MONITOR_FLEET_DOCUMENTATION: () => MONITOR_FLEET_DOCUMENTATION,
47
+ MONITOR_FLEET_FRONTIER_MAX_ROWS: () => MONITOR_FLEET_FRONTIER_MAX_ROWS,
48
+ MONITOR_FLEET_MAX_MEMBERS: () => MONITOR_FLEET_MAX_MEMBERS,
44
49
  PHONE_STATUS_VALUES: () => PHONE_STATUS_VALUES,
45
50
  PLAY_BOOTSTRAP_COMPANY_FIELDS: () => PLAY_BOOTSTRAP_COMPANY_FIELDS,
46
51
  PLAY_BOOTSTRAP_COMPANY_PROVIDER_CATEGORY: () => PLAY_BOOTSTRAP_COMPANY_PROVIDER_CATEGORY,
@@ -60,11 +65,15 @@ __export(src_exports, {
60
65
  SDK_VERSION: () => SDK_VERSION,
61
66
  ToolExecutionError: () => ToolExecutionError,
62
67
  ToolRateLimitError: () => ToolRateLimitError,
68
+ admitMonitorFleetAuthoringContract: () => admitMonitorFleetAuthoringContract,
63
69
  defineInput: () => defineInput,
64
70
  defineMonitor: () => defineMonitor,
71
+ defineMonitorFleet: () => defineMonitorFleet,
65
72
  definePlay: () => definePlay,
66
73
  defineWorkflow: () => defineWorkflow,
67
74
  extractSummaryFields: () => extractSummaryFields,
75
+ fleetColumn: () => fleetColumn,
76
+ fleetKey: () => fleetKey,
68
77
  formatPlayBootstrapFinderKinds: () => formatPlayBootstrapFinderKinds,
69
78
  formatPlayBootstrapFinderKindsForSentence: () => formatPlayBootstrapFinderKindsForSentence,
70
79
  formatPlayBootstrapTemplates: () => formatPlayBootstrapTemplates,
@@ -79,6 +88,7 @@ __export(src_exports, {
79
88
  runIf: () => runIf,
80
89
  steps: () => steps,
81
90
  tryConvertToList: () => tryConvertToList,
91
+ validateMonitorFleetDefinition: () => validateMonitorFleetDefinition,
82
92
  writeCsvOutputFile: () => writeCsvOutputFile,
83
93
  writeJsonOutputFile: () => writeJsonOutputFile
84
94
  });
@@ -816,7 +826,7 @@ var SDK_RELEASE = {
816
826
  // available at toolResponse.rawV2 while toolResponse.raw and all declared
817
827
  // getters keep their established compatibility behavior.
818
828
  // 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
819
- version: "0.3.55",
829
+ version: "0.3.56",
820
830
  updateSummary: "Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.",
821
831
  packageCapabilities: {
822
832
  updatePreferences: 1
@@ -2822,9 +2832,9 @@ function nonEmptyString(value) {
2822
2832
  }
2823
2833
  function normalizeDatasetBornFrom(value) {
2824
2834
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
2825
- const record = value;
2826
- const table = nonEmptyString(record.table);
2827
- const rowCountIn = finiteNonNegativeInteger2(record.rowCountIn);
2835
+ const record2 = value;
2836
+ const table = nonEmptyString(record2.table);
2837
+ const rowCountIn = finiteNonNegativeInteger2(record2.rowCountIn);
2828
2838
  if (!table || rowCountIn === null) return null;
2829
2839
  return { table, rowCountIn };
2830
2840
  }
@@ -3884,21 +3894,21 @@ function normalizePlayRuntimeNamespace(value) {
3884
3894
  }
3885
3895
  function normalizePlayRuntimeSelection(value) {
3886
3896
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
3887
- const record = value;
3888
- if (Object.keys(record).some(
3897
+ const record2 = value;
3898
+ if (Object.keys(record2).some(
3889
3899
  (key) => key !== "environment" && key !== "namespace" && key !== "backend"
3890
- ) || record.environment !== "preview") {
3900
+ ) || record2.environment !== "preview") {
3891
3901
  return null;
3892
3902
  }
3893
- const namespace = normalizePlayRuntimeNamespace(record.namespace);
3903
+ const namespace = normalizePlayRuntimeNamespace(record2.namespace);
3894
3904
  if (!namespace) return null;
3895
- if (record.backend === void 0) {
3905
+ if (record2.backend === void 0) {
3896
3906
  return { environment: "preview", namespace };
3897
3907
  }
3898
- if (record.backend !== PLAY_RUNTIME_BACKENDS.daytona && record.backend !== PLAY_RUNTIME_BACKENDS.modal) {
3908
+ if (record2.backend !== PLAY_RUNTIME_BACKENDS.daytona && record2.backend !== PLAY_RUNTIME_BACKENDS.modal) {
3899
3909
  return null;
3900
3910
  }
3901
- return { environment: "preview", namespace, backend: record.backend };
3911
+ return { environment: "preview", namespace, backend: record2.backend };
3902
3912
  }
3903
3913
  function normalizePlayRuntimeEnvironment(value) {
3904
3914
  return typeof value === "string" && PLAY_RUNTIME_ENVIRONMENTS.includes(value) ? value : null;
@@ -4482,7 +4492,14 @@ var DeeplineClient = class {
4482
4492
  dependents: (key) => this.getMonitorDependents(key),
4483
4493
  update: (key, patch) => this.updateMonitor(key, patch),
4484
4494
  delete: (key, options2) => this.deleteMonitor(key, options2),
4485
- reactivate: (key, options2) => this.reactivateMonitor(key, options2)
4495
+ reactivate: (key, options2) => this.reactivateMonitor(key, options2),
4496
+ fleets: {
4497
+ sync: (definitionOrId, options2) => this.syncMonitorFleet(definitionOrId, options2),
4498
+ get: (fleetId, options2) => fleetId === void 0 ? this.listMonitorFleets() : this.getMonitorFleet(fleetId, options2),
4499
+ list: () => this.listMonitorFleets(),
4500
+ deactivate: (fleetId, options2) => this.deactivateMonitorFleet(fleetId, options2),
4501
+ waitForConvergence: (fleetId, options2) => this.waitForMonitorFleetConvergence(fleetId, options2)
4502
+ }
4486
4503
  };
4487
4504
  }
4488
4505
  /** The resolved base URL this client is targeting (e.g. `"http://localhost:3000"`). */
@@ -6790,6 +6807,104 @@ var DeeplineClient = class {
6790
6807
  { method: "POST", body: {} }
6791
6808
  );
6792
6809
  }
6810
+ // ——————————————————————————————————————————————————————————
6811
+ // Monitor Fleets
6812
+ // ——————————————————————————————————————————————————————————
6813
+ monitorFleetIdempotencyKey(operation) {
6814
+ const uuid = globalThis.crypto?.randomUUID?.();
6815
+ return `monitor-fleet-${operation}-${uuid ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`}`;
6816
+ }
6817
+ monitorFleetHeaders(operation, idempotencyKey) {
6818
+ return {
6819
+ "Idempotency-Key": idempotencyKey?.trim() || this.monitorFleetIdempotencyKey(operation)
6820
+ };
6821
+ }
6822
+ monitorFleetPath(fleetId) {
6823
+ return `/api/v2/monitors/fleets/${encodeURIComponent(fleetId)}`;
6824
+ }
6825
+ /**
6826
+ * Create, update, or re-plan one fleet.
6827
+ *
6828
+ * The fleet id is always the resource path, so the same definition PUT twice
6829
+ * is the same operation and the server can answer `replayed: true` instead of
6830
+ * building a second set of monitors. Re-planning an existing fleet from its
6831
+ * stored definition sends an EMPTY body: there is no second definition to
6832
+ * send, and an empty body cannot be mistaken for "replace the definition with
6833
+ * nothing".
6834
+ */
6835
+ async syncMonitorFleet(definitionOrId, options) {
6836
+ const fleetId = typeof definitionOrId === "string" ? definitionOrId : definitionOrId.id;
6837
+ const body = {
6838
+ ...typeof definitionOrId === "string" ? {} : { definition: definitionOrId },
6839
+ ...options?.dryRun ? { dry_run: true } : {},
6840
+ ...options?.expectedGeneration !== void 0 ? { expected_generation: options.expectedGeneration } : {}
6841
+ };
6842
+ return this.http.request(
6843
+ this.monitorFleetPath(fleetId),
6844
+ {
6845
+ method: "PUT",
6846
+ body,
6847
+ headers: this.monitorFleetHeaders("sync", options?.idempotencyKey),
6848
+ maxRetries: 0,
6849
+ exactUrlOnly: true
6850
+ }
6851
+ );
6852
+ }
6853
+ async listMonitorFleets() {
6854
+ return this.http.request("/api/v2/monitors/fleets", {
6855
+ method: "GET"
6856
+ });
6857
+ }
6858
+ async getMonitorFleet(fleetId, options) {
6859
+ const params = new URLSearchParams();
6860
+ if (options?.drift) params.set("drift", "1");
6861
+ if (options?.limit !== void 0)
6862
+ params.set("limit", String(options.limit));
6863
+ const query = params.toString();
6864
+ return this.http.request(
6865
+ `${this.monitorFleetPath(fleetId)}${query ? `?${query}` : ""}`,
6866
+ { method: "GET" }
6867
+ );
6868
+ }
6869
+ async deactivateMonitorFleet(fleetId, options) {
6870
+ return this.http.request(
6871
+ this.monitorFleetPath(fleetId),
6872
+ {
6873
+ method: "DELETE",
6874
+ body: options?.dryRun ? { dry_run: true } : {},
6875
+ headers: this.monitorFleetHeaders(
6876
+ "deactivate",
6877
+ options?.idempotencyKey
6878
+ ),
6879
+ maxRetries: 0,
6880
+ exactUrlOnly: true
6881
+ }
6882
+ );
6883
+ }
6884
+ /**
6885
+ * Poll one fleet until the server reports a terminal status.
6886
+ *
6887
+ * Convergence is the server's verdict, read from `status`. The timeout is not
6888
+ * a failure and does not throw: a fleet still `converging` after ten minutes
6889
+ * is healthy and slow, not broken, so the caller gets the last snapshot and
6890
+ * decides what that means. Throwing here would have made "still working" look
6891
+ * identical to "the request failed".
6892
+ */
6893
+ async waitForMonitorFleetConvergence(fleetId, options) {
6894
+ const timeoutMs = Math.max(1, options?.timeoutMs ?? 10 * 6e4);
6895
+ const pollIntervalMs = Math.max(1, options?.pollIntervalMs ?? 2e3);
6896
+ const terminal = options?.until === "deactivated" ? /* @__PURE__ */ new Set(["deactivated", "degraded"]) : /* @__PURE__ */ new Set(["converged", "degraded", "deactivated"]);
6897
+ const startedAt = Date.now();
6898
+ let latest = await this.getMonitorFleet(fleetId);
6899
+ while (true) {
6900
+ options?.onProgress?.(latest);
6901
+ const status = typeof latest.status === "string" ? latest.status : void 0;
6902
+ if (status && terminal.has(status)) return latest;
6903
+ if (Date.now() - startedAt >= timeoutMs) return latest;
6904
+ await sleep2(pollIntervalMs);
6905
+ latest = await this.getMonitorFleet(fleetId);
6906
+ }
6907
+ }
6793
6908
  /**
6794
6909
  * Check API connectivity and server health.
6795
6910
  *
@@ -7836,8 +7951,8 @@ function firstExperienceDate(value) {
7836
7951
  return null;
7837
7952
  }
7838
7953
  function normalizeJobChange(value) {
7839
- const record = isRecord8(value) ? value : {};
7840
- const nested = isRecord8(record.job_change) ? record.job_change : record;
7954
+ const record2 = isRecord8(value) ? value : {};
7955
+ const nested = isRecord8(record2.job_change) ? record2.job_change : record2;
7841
7956
  const output = isRecord8(nested.output) ? nested.output : nested;
7842
7957
  const person = isRecord8(output.person) ? output.person : {};
7843
7958
  const status = normalizeJobChangeStatus(
@@ -10384,6 +10499,341 @@ function defineMonitor(definition) {
10384
10499
  return definition;
10385
10500
  }
10386
10501
 
10502
+ // src/monitor-fleet-contract.ts
10503
+ var MONITOR_FLEET_MAX_MEMBERS = 1e3;
10504
+ var MONITOR_FLEET_FRONTIER_MAX_ROWS = 1e4;
10505
+ var MONITOR_FLEET_AUTHORING_CONTRACT_EDITION = 1;
10506
+ var SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS = [
10507
+ MONITOR_FLEET_AUTHORING_CONTRACT_EDITION
10508
+ ];
10509
+ var MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG = [
10510
+ {
10511
+ edition: 1,
10512
+ changed: "Initial Fleet JSON contract: bounded sticky table membership, one daily sync, and a seven-day removal grace.",
10513
+ compatibilityOwner: "Monitors Runtime",
10514
+ newWritesEnd: null,
10515
+ readerRemoval: null
10516
+ }
10517
+ ];
10518
+ var MONITOR_FLEET_REMOVAL_GRACE_MS = 7 * 24 * 60 * 6e4;
10519
+ var MONITOR_FLEET_DOCUMENTATION = {
10520
+ summary: "A Monitor Fleet keeps one bounded, sticky set of ordinary monitors aligned with Customer DB rows.",
10521
+ authoredFields: {
10522
+ id: "Stable lowercase fleet id used by sync, pause, resume, and deactivate.",
10523
+ source: "Customer DB table, unique row key, and optional equality filters that select candidate rows.",
10524
+ member: "Ordinary monitor tool, stable key template, and payload evaluated for each selected source row.",
10525
+ selection: "Deterministic ranking and an active-member limit between 1 and 1,000."
10526
+ },
10527
+ fixedPolicy: {
10528
+ cadence: "daily",
10529
+ membership: "sticky",
10530
+ removalGrace: "7d",
10531
+ onRemoved: "deactivate",
10532
+ ownership: "A Fleet may adopt a same-tool ordinary monitor with its deterministic member key. The first Fleet claim wins; another Fleet or a different tool receives a conflict.",
10533
+ billing: "No Fleet fee, permit, or renewal. Dry-run reports ordinary monitor lifecycle charges due now."
10534
+ },
10535
+ stickyMembership: "Ranking fills vacancies but never replaces a current member merely because another row ranks higher.",
10536
+ columnExpression: 'Use { "$fleet": "column", "name": "column_name" } in a member key or payload to read a value from each source row.'
10537
+ };
10538
+ function record(value) {
10539
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
10540
+ }
10541
+ function identifier(value) {
10542
+ return typeof value === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
10543
+ }
10544
+ function exactKeys(input, keys, path, issues) {
10545
+ if (!input) return;
10546
+ for (const key of Object.keys(input)) {
10547
+ if (keys.includes(key)) continue;
10548
+ issues.push({
10549
+ path: path ? `${path}.${key}` : key,
10550
+ code: "unknown_fleet_field",
10551
+ message: `${path || "Fleet"} does not accept '${key}'.`
10552
+ });
10553
+ }
10554
+ }
10555
+ function jsonValue(value, path, issues) {
10556
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) {
10557
+ return;
10558
+ }
10559
+ if (Array.isArray(value)) {
10560
+ value.forEach((item, index) => jsonValue(item, `${path}.${index}`, issues));
10561
+ return;
10562
+ }
10563
+ const input = record(value);
10564
+ if (!input) {
10565
+ issues.push({
10566
+ path,
10567
+ code: "invalid_fleet_json_value",
10568
+ message: "Fleet payload values must be JSON values."
10569
+ });
10570
+ return;
10571
+ }
10572
+ for (const [key, item] of Object.entries(input)) {
10573
+ jsonValue(item, `${path}.${key}`, issues);
10574
+ }
10575
+ }
10576
+ function fleetColumn(name) {
10577
+ if (!identifier(name)) throw new Error(`Invalid fleet column '${name}'.`);
10578
+ return { $fleet: "column", name };
10579
+ }
10580
+ function fleetKey(...parts) {
10581
+ if (parts.length === 0)
10582
+ throw new Error("A fleet key needs at least one part.");
10583
+ return { $fleet: "template", parts };
10584
+ }
10585
+ function defineMonitorFleet(definition) {
10586
+ return definition;
10587
+ }
10588
+ function validateExpression(value, path, issues) {
10589
+ const input = record(value);
10590
+ if (!input || input.$fleet !== "column" || !identifier(input.name)) {
10591
+ issues.push({
10592
+ path,
10593
+ code: "invalid_fleet_expression",
10594
+ message: "Fleet expressions must be an exact tagged column reference."
10595
+ });
10596
+ return;
10597
+ }
10598
+ exactKeys(input, ["$fleet", "name"], path, issues);
10599
+ }
10600
+ function walkPayload(value, path, issues) {
10601
+ if (Array.isArray(value)) {
10602
+ value.forEach(
10603
+ (item, index) => walkPayload(item, `${path}.${index}`, issues)
10604
+ );
10605
+ return;
10606
+ }
10607
+ const input = record(value);
10608
+ if (!input) return;
10609
+ if ("$fleet" in input) {
10610
+ validateExpression(input, path, issues);
10611
+ return;
10612
+ }
10613
+ for (const [key, item] of Object.entries(input)) {
10614
+ walkPayload(item, `${path}.${key}`, issues);
10615
+ }
10616
+ }
10617
+ function validateMonitorFleetDefinition(value) {
10618
+ const issues = [];
10619
+ const input = record(value);
10620
+ if (!input) {
10621
+ return {
10622
+ valid: false,
10623
+ issues: [
10624
+ {
10625
+ path: "",
10626
+ code: "invalid_fleet",
10627
+ message: "Fleet must be an object."
10628
+ }
10629
+ ]
10630
+ };
10631
+ }
10632
+ exactKeys(input, ["id", "source", "member", "selection"], "", issues);
10633
+ if (typeof input.id !== "string" || !/^[a-z][a-z0-9-]{0,199}$/.test(input.id)) {
10634
+ issues.push({
10635
+ path: "id",
10636
+ code: "invalid_fleet_id",
10637
+ message: "Fleet id must be lowercase kebab-case."
10638
+ });
10639
+ }
10640
+ const source = record(input.source);
10641
+ exactKeys(
10642
+ source,
10643
+ ["kind", "schema", "table", "key", "where"],
10644
+ "source",
10645
+ issues
10646
+ );
10647
+ if (!source || source.kind !== "customer_db_table") {
10648
+ issues.push({
10649
+ path: "source.kind",
10650
+ code: "invalid_fleet_source",
10651
+ message: "Beta fleets require customer_db_table."
10652
+ });
10653
+ }
10654
+ if (!identifier(source?.schema) || !identifier(source?.table)) {
10655
+ issues.push({
10656
+ path: "source",
10657
+ code: "invalid_fleet_table",
10658
+ message: "Source schema and table must be SQL identifiers."
10659
+ });
10660
+ }
10661
+ const sourceKey = record(source?.key);
10662
+ exactKeys(sourceKey, ["column", "type"], "source.key", issues);
10663
+ if (!sourceKey || !identifier(sourceKey.column) || !["text", "uuid", "int4", "int8"].includes(String(sourceKey.type))) {
10664
+ issues.push({
10665
+ path: "source.key",
10666
+ code: "invalid_fleet_source_key",
10667
+ message: "Source key must name a supported typed column."
10668
+ });
10669
+ }
10670
+ if (source?.where !== void 0) {
10671
+ const where = record(source.where);
10672
+ if (!where) {
10673
+ issues.push({
10674
+ path: "source.where",
10675
+ code: "invalid_fleet_where",
10676
+ message: "source.where must be an object of equality values."
10677
+ });
10678
+ } else {
10679
+ for (const [column, expected] of Object.entries(where)) {
10680
+ if (!identifier(column)) {
10681
+ issues.push({
10682
+ path: `source.where.${column}`,
10683
+ code: "invalid_fleet_where_column",
10684
+ message: "source.where keys must be SQL identifiers."
10685
+ });
10686
+ }
10687
+ if (expected !== null && typeof expected !== "string" && typeof expected !== "boolean" && !(typeof expected === "number" && Number.isFinite(expected))) {
10688
+ issues.push({
10689
+ path: `source.where.${column}`,
10690
+ code: "invalid_fleet_where_value",
10691
+ message: "source.where values must be finite JSON scalars or null."
10692
+ });
10693
+ }
10694
+ }
10695
+ }
10696
+ }
10697
+ const member = record(input.member);
10698
+ exactKeys(member, ["tool", "key", "payload"], "member", issues);
10699
+ const key = record(member?.key);
10700
+ if (!member || typeof member.tool !== "string" || !member.tool.trim()) {
10701
+ issues.push({
10702
+ path: "member.tool",
10703
+ code: "invalid_fleet_tool",
10704
+ message: "Member tool is required."
10705
+ });
10706
+ }
10707
+ if (!key || key.$fleet !== "template" || !Array.isArray(key.parts) || key.parts.length === 0) {
10708
+ issues.push({
10709
+ path: "member.key",
10710
+ code: "invalid_fleet_key",
10711
+ message: "Member key must be a tagged fleet template."
10712
+ });
10713
+ } else {
10714
+ exactKeys(key, ["$fleet", "parts"], "member.key", issues);
10715
+ for (const [index, part] of key.parts.entries()) {
10716
+ if (typeof part !== "string")
10717
+ validateExpression(part, `member.key.parts.${index}`, issues);
10718
+ }
10719
+ if (sourceKey && !key.parts.some(
10720
+ (part) => record(part)?.$fleet === "column" && record(part)?.name === sourceKey.column
10721
+ )) {
10722
+ issues.push({
10723
+ path: "member.key.parts",
10724
+ code: "fleet_key_missing_source_key",
10725
+ message: "Member key must include the source key column."
10726
+ });
10727
+ }
10728
+ }
10729
+ if (!member || !("payload" in member)) {
10730
+ issues.push({
10731
+ path: "member.payload",
10732
+ code: "invalid_fleet_payload",
10733
+ message: "Member payload is required."
10734
+ });
10735
+ } else {
10736
+ jsonValue(member.payload, "member.payload", issues);
10737
+ }
10738
+ walkPayload(member?.payload, "member.payload", issues);
10739
+ const selection = record(input.selection);
10740
+ exactKeys(selection, ["limit", "orderBy", "membership"], "selection", issues);
10741
+ if (!selection || !Number.isSafeInteger(selection.limit) || Number(selection.limit) < 1 || Number(selection.limit) > MONITOR_FLEET_MAX_MEMBERS) {
10742
+ issues.push({
10743
+ path: "selection.limit",
10744
+ code: "MONITOR_FLEET_LIMIT_EXCEEDED",
10745
+ message: "selection.limit must be between 1 and 1,000."
10746
+ });
10747
+ }
10748
+ if (selection?.membership !== "sticky") {
10749
+ issues.push({
10750
+ path: "selection.membership",
10751
+ code: "invalid_fleet_membership",
10752
+ message: `Fleet membership is fixed to ${MONITOR_FLEET_DOCUMENTATION.fixedPolicy.membership}. ${MONITOR_FLEET_DOCUMENTATION.stickyMembership}`
10753
+ });
10754
+ }
10755
+ const orderBy = selection?.orderBy;
10756
+ if (!Array.isArray(orderBy) || orderBy.length === 0) {
10757
+ issues.push({
10758
+ path: "selection.orderBy",
10759
+ code: "invalid_fleet_order",
10760
+ message: "At least one deterministic order field is required."
10761
+ });
10762
+ } else {
10763
+ const orderColumns = /* @__PURE__ */ new Set();
10764
+ for (const [index, order] of orderBy.entries()) {
10765
+ const item = record(order);
10766
+ exactKeys(
10767
+ item,
10768
+ ["column", "direction"],
10769
+ `selection.orderBy.${index}`,
10770
+ issues
10771
+ );
10772
+ if (!item || !identifier(item.column) || !["asc", "desc"].includes(String(item.direction))) {
10773
+ issues.push({
10774
+ path: `selection.orderBy.${index}`,
10775
+ code: "invalid_fleet_order",
10776
+ message: "Order fields need a column and asc/desc direction."
10777
+ });
10778
+ } else if (orderColumns.has(item.column)) {
10779
+ issues.push({
10780
+ path: `selection.orderBy.${index}.column`,
10781
+ code: "duplicate_fleet_order_column",
10782
+ message: "Each selection order column may appear only once."
10783
+ });
10784
+ } else {
10785
+ orderColumns.add(item.column);
10786
+ }
10787
+ }
10788
+ if (sourceKey && record(orderBy.at(-1))?.column !== sourceKey.column) {
10789
+ issues.push({
10790
+ path: "selection.orderBy",
10791
+ code: "fleet_order_missing_key",
10792
+ message: "The final order field must be the source key."
10793
+ });
10794
+ }
10795
+ }
10796
+ for (const removedOption of ["spendLimits", "lifecycle", "reconciliation"]) {
10797
+ if (removedOption in input) {
10798
+ issues.push({
10799
+ path: removedOption,
10800
+ code: "unsupported_fleet_option",
10801
+ message: `${removedOption} is fixed by Monitor Fleets and must be omitted. Fleets sync ${MONITOR_FLEET_DOCUMENTATION.fixedPolicy.cadence} and ${MONITOR_FLEET_DOCUMENTATION.fixedPolicy.onRemoved} members after ${MONITOR_FLEET_DOCUMENTATION.fixedPolicy.removalGrace} absent from the source.`
10802
+ });
10803
+ }
10804
+ }
10805
+ return issues.length === 0 ? { valid: true, definition: value, issues } : { valid: false, issues };
10806
+ }
10807
+ function admitMonitorFleetAuthoringContract(value, edition = MONITOR_FLEET_AUTHORING_CONTRACT_EDITION) {
10808
+ if (!SUPPORTED_MONITOR_FLEET_AUTHORING_CONTRACT_EDITIONS.includes(
10809
+ edition
10810
+ )) {
10811
+ return {
10812
+ valid: false,
10813
+ issues: [
10814
+ {
10815
+ path: "authoring_contract_edition",
10816
+ code: "unsupported_fleet_authoring_contract_edition",
10817
+ message: `Monitor Fleet authoring contract edition ${edition} is not supported.`
10818
+ }
10819
+ ]
10820
+ };
10821
+ }
10822
+ const result = validateMonitorFleetDefinition(value);
10823
+ return result.valid && result.definition ? {
10824
+ valid: true,
10825
+ contract: {
10826
+ edition,
10827
+ // JSON cloning prevents later caller mutation from changing the
10828
+ // admitted snapshot.
10829
+ definition: JSON.parse(
10830
+ JSON.stringify(result.definition)
10831
+ )
10832
+ },
10833
+ issues: []
10834
+ } : { valid: false, issues: result.issues };
10835
+ }
10836
+
10387
10837
  // ../plays/bootstrap-routes.ts
10388
10838
  var DEEPLINE_TOOL_CATEGORIES = [
10389
10839
  "company_search",
@@ -10666,6 +11116,11 @@ function extractSummaryFields(payload) {
10666
11116
  DeeplineContext,
10667
11117
  DeeplineError,
10668
11118
  JOB_CHANGE_STATUS_VALUES,
11119
+ MONITOR_FLEET_AUTHORING_CONTRACT_CHANGELOG,
11120
+ MONITOR_FLEET_AUTHORING_CONTRACT_EDITION,
11121
+ MONITOR_FLEET_DOCUMENTATION,
11122
+ MONITOR_FLEET_FRONTIER_MAX_ROWS,
11123
+ MONITOR_FLEET_MAX_MEMBERS,
10669
11124
  PHONE_STATUS_VALUES,
10670
11125
  PLAY_BOOTSTRAP_COMPANY_FIELDS,
10671
11126
  PLAY_BOOTSTRAP_COMPANY_PROVIDER_CATEGORY,
@@ -10685,11 +11140,15 @@ function extractSummaryFields(payload) {
10685
11140
  SDK_VERSION,
10686
11141
  ToolExecutionError,
10687
11142
  ToolRateLimitError,
11143
+ admitMonitorFleetAuthoringContract,
10688
11144
  defineInput,
10689
11145
  defineMonitor,
11146
+ defineMonitorFleet,
10690
11147
  definePlay,
10691
11148
  defineWorkflow,
10692
11149
  extractSummaryFields,
11150
+ fleetColumn,
11151
+ fleetKey,
10693
11152
  formatPlayBootstrapFinderKinds,
10694
11153
  formatPlayBootstrapFinderKindsForSentence,
10695
11154
  formatPlayBootstrapTemplates,
@@ -10704,6 +11163,7 @@ function extractSummaryFields(payload) {
10704
11163
  runIf,
10705
11164
  steps,
10706
11165
  tryConvertToList,
11166
+ validateMonitorFleetDefinition,
10707
11167
  writeCsvOutputFile,
10708
11168
  writeJsonOutputFile
10709
11169
  });