@gravity-rail/sdk 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/agui.cjs +13 -1
  2. package/dist/agui.cjs.map +1 -1
  3. package/dist/agui.d.cts +5 -1
  4. package/dist/agui.d.ts +5 -1
  5. package/dist/agui.js +12 -1
  6. package/dist/agui.js.map +1 -1
  7. package/dist/{chunk-V7ZKEH4V.js → chunk-2YLKG4TT.js} +1 -1
  8. package/dist/chunk-2YLKG4TT.js.map +1 -0
  9. package/dist/{chunk-K4MDFGMO.js → chunk-4B7T6MBH.js} +267 -8
  10. package/dist/chunk-4B7T6MBH.js.map +1 -0
  11. package/dist/{client-CKudWmUT.d.cts → client-3xYGmF3t.d.ts} +1335 -18
  12. package/dist/{client-EAvh7w0y.d.ts → client-BhO_UFFz.d.cts} +1335 -18
  13. package/dist/helpers.d.cts +2 -2
  14. package/dist/helpers.d.ts +2 -2
  15. package/dist/index.cjs +593 -28
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +164 -9
  18. package/dist/index.d.ts +164 -9
  19. package/dist/index.js +334 -23
  20. package/dist/index.js.map +1 -1
  21. package/dist/internal.cjs +296 -7
  22. package/dist/internal.cjs.map +1 -1
  23. package/dist/internal.d.cts +301 -6
  24. package/dist/internal.d.ts +301 -6
  25. package/dist/internal.js +35 -2
  26. package/dist/internal.js.map +1 -1
  27. package/dist/{revision-state-BUBt9Rir.d.ts → revision-state-Dma10N9s.d.ts} +23 -3
  28. package/dist/{revision-state-CY4LBdfa.d.cts → revision-state-Dx8gCQIj.d.cts} +23 -3
  29. package/dist/schemas.cjs +44 -21
  30. package/dist/schemas.cjs.map +1 -1
  31. package/dist/schemas.d.cts +54 -23
  32. package/dist/schemas.d.ts +54 -23
  33. package/dist/schemas.js +42 -21
  34. package/dist/schemas.js.map +1 -1
  35. package/dist/{tasks-iRrM4SaF.d.cts → tasks-vupYoJgG.d.cts} +82 -15
  36. package/dist/{tasks-iRrM4SaF.d.ts → tasks-vupYoJgG.d.ts} +82 -15
  37. package/package.json +2 -2
  38. package/dist/chunk-K4MDFGMO.js.map +0 -1
  39. package/dist/chunk-V7ZKEH4V.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -110,6 +110,7 @@ __export(src_exports, {
110
110
  PIDS_SOURCES: () => PIDS_SOURCES,
111
111
  POSITIVE_RATING_THRESHOLD: () => POSITIVE_RATING_THRESHOLD,
112
112
  ProductType: () => ProductType,
113
+ PromoCodeStatus: () => PromoCodeStatus,
113
114
  QUERY_VALUELESS_OPS: () => QUERY_VALUELESS_OPS,
114
115
  QueriesApi: () => QueriesApi,
115
116
  REALTIME_MODEL_CUSTOM: () => REALTIME_MODEL_CUSTOM,
@@ -146,10 +147,12 @@ __export(src_exports, {
146
147
  TemplatesApi: () => TemplatesApi,
147
148
  ToolkitState: () => ToolkitState,
148
149
  WIDGET_SLUGS: () => WIDGET_SLUGS,
150
+ WORKFLOW_IMPLICIT_USE_SCOPE: () => WORKFLOW_IMPLICIT_USE_SCOPE,
149
151
  WorkflowAccessWarningResourceType: () => WorkflowAccessWarningResourceType,
150
152
  WorkflowBuilder: () => WorkflowBuilder,
151
153
  WorkflowResourceType: () => WorkflowAccessWarningResourceType,
152
154
  WorkspaceBuilder: () => WorkspaceBuilder,
155
+ WorkspaceBuilderResource: () => WorkspaceBuilderResource,
153
156
  WorkspaceFeature: () => WorkspaceFeature,
154
157
  WorkspaceProvisionStatus: () => WorkspaceProvisionStatus,
155
158
  areDependenciesSatisfied: () => areDependenciesSatisfied,
@@ -184,6 +187,9 @@ __export(src_exports, {
184
187
  isSMSDeliveryFailureStatus: () => isSMSDeliveryFailureStatus,
185
188
  parseOrgAuthChallenge: () => parseOrgAuthChallenge,
186
189
  requiresAssistantParticipant: () => requiresAssistantParticipant,
190
+ roleCanUseWorkflow: () => roleCanUseWorkflow,
191
+ roleImplicitWorkflowUseScope: () => roleImplicitWorkflowUseScope,
192
+ roleWorkflowAccess: () => roleWorkflowAccess,
187
193
  runWorkspaceScript: () => runWorkspaceScript,
188
194
  usesBuiltinAssistant: () => usesBuiltinAssistant,
189
195
  validateFeatureDependencies: () => validateFeatureDependencies
@@ -920,6 +926,18 @@ var AIModelsApi = class extends BaseApiClient {
920
926
  const url = queryString ? `ai-models/realtime?${queryString}` : "ai-models/realtime";
921
927
  return this.fetch(url);
922
928
  }
929
+ /**
930
+ * Get the voice presets offered by the simplified voice picker.
931
+ *
932
+ * A preset bundles a realtime speech-to-speech model with one of its native
933
+ * voices and the settings recommended alongside it, so choosing a voice is
934
+ * a single decision. Apply one by assigning its `config` wholesale; compare
935
+ * against it to tell a preset selection from a customised ("Custom") one.
936
+ * The full engine/voice/settings matrix lives on `getRealtimeModels`.
937
+ */
938
+ async getVoicePresets() {
939
+ return this.fetch("ai-models/voice-presets");
940
+ }
923
941
  /**
924
942
  * Get available voice models with metadata
925
943
  */
@@ -1954,6 +1972,8 @@ var ChatsApi = class extends BaseApiClient {
1954
1972
  if (options.mine !== void 0) params.append("mine", options.mine.toString());
1955
1973
  if (options.needsResponse !== void 0)
1956
1974
  params.append("needsResponse", options.needsResponse.toString());
1975
+ if (options.needsResponseFirst !== void 0)
1976
+ params.append("needsResponseFirst", options.needsResponseFirst.toString());
1957
1977
  if (options.paused !== void 0) params.append("paused", options.paused.toString());
1958
1978
  if (options.archived !== void 0) params.append("archived", options.archived.toString());
1959
1979
  if (options.channel) params.append("channel", options.channel);
@@ -2003,6 +2023,25 @@ var ChatsApi = class extends BaseApiClient {
2003
2023
  async getChat(wid, id) {
2004
2024
  return this.fetch(`w/${wid}/chats/${id}`);
2005
2025
  }
2026
+ /**
2027
+ * Create-chat preflight for SMS: is a conversation already live between this
2028
+ * workspace number and this member?
2029
+ *
2030
+ * An SMS conversation with a member is a single stream — inbound SMS resolves
2031
+ * to ONE current chat per (member phone, workspace phone) pair — so creating
2032
+ * a new SMS chat SUPERSEDES the existing one and the member's next reply
2033
+ * lands on the new chat. Callers use this to say so before committing.
2034
+ *
2035
+ * Read-only, `chats:read`, and PHI-safe by construction: status and timing
2036
+ * only, no message content and no chat title.
2037
+ */
2038
+ async getCurrentSmsChat(wid, options) {
2039
+ const params = new URLSearchParams({
2040
+ memberId: options.memberId.toString(),
2041
+ phoneNumberId: options.phoneNumberId.toString()
2042
+ });
2043
+ return this.fetch(`w/${wid}/chats/current-sms?${params}`);
2044
+ }
2006
2045
  /** Get token usage statistics for a chat. */
2007
2046
  async getChatTokenUsage(wid, chatId) {
2008
2047
  return this.fetch(`w/${wid}/chats/${chatId}/token-usage`);
@@ -2028,6 +2067,10 @@ var ChatsApi = class extends BaseApiClient {
2028
2067
  `w/${wid}/chats/${chatId}/skills`
2029
2068
  );
2030
2069
  }
2070
+ /** List built-in skills that can be selected before starting a Manager Chat. */
2071
+ async getManagerSkills(wid) {
2072
+ return this.fetch(`w/${wid}/chats/manager/skills`);
2073
+ }
2031
2074
  /** List chat IDs that need a response. */
2032
2075
  async getChatsNeedsResponse(wid) {
2033
2076
  return this.fetch(`w/${wid}/chats/needs-response`);
@@ -3655,9 +3698,9 @@ var DocumoFaxApi = class extends BaseApiClient {
3655
3698
  params?.status?.forEach((value) => query.append("status", value));
3656
3699
  if (params?.receivedAfter) query.set("receivedAfter", params.receivedAfter);
3657
3700
  if (params?.receivedBefore) query.set("receivedBefore", params.receivedBefore);
3658
- const suffix = query.toString() === "" ? "" : `?${query.toString()}`;
3701
+ const suffix2 = query.toString() === "" ? "" : `?${query.toString()}`;
3659
3702
  return this.fetch(
3660
- `w/${wid}/app-connections/documo-fax/faxes${suffix}`
3703
+ `w/${wid}/app-connections/documo-fax/faxes${suffix2}`
3661
3704
  );
3662
3705
  }
3663
3706
  /**
@@ -5731,15 +5774,19 @@ var OrgApi = class extends BaseApiClient {
5731
5774
  async getA2PStatus(orgUuid) {
5732
5775
  return this.fetch(`o/${orgUuid}/phone-numbers/a2p/status`);
5733
5776
  }
5777
+ /** Return fixed phone-number and outbound-messaging prices. */
5778
+ async getPhoneBillingPricing(orgUuid) {
5779
+ return this.fetch(`o/${orgUuid}/phone-numbers/pricing`);
5780
+ }
5734
5781
  /**
5735
5782
  * Start A2P brand registration under a legal entity. `legalEntityUuid`
5736
5783
  * is required — brands register per legal entity (Customer Profile);
5737
5784
  * there is no primary-entity fallback.
5738
5785
  */
5739
- async startA2PRegistration(orgUuid, legalEntityUuid, brandName, brandDescription) {
5786
+ async startA2PRegistration(orgUuid, legalEntityUuid, brandName, brandDescription, brandUuid = null) {
5740
5787
  return this.fetch(`o/${orgUuid}/phone-numbers/a2p/start`, {
5741
5788
  method: "POST",
5742
- body: JSON.stringify({ legalEntityUuid, brandName, brandDescription })
5789
+ body: JSON.stringify({ legalEntityUuid, brandName, brandDescription, brandUuid })
5743
5790
  });
5744
5791
  }
5745
5792
  /**
@@ -5945,14 +5992,34 @@ var OrgApi = class extends BaseApiClient {
5945
5992
  );
5946
5993
  }
5947
5994
  /**
5948
- * The brand-addressable enrollment form URL (carries `?brand=…`). Outreach
5949
- * appends each patient's `external_id`. `url` is null until a Site is linked.
5995
+ * The brand's enrollment-form template URL (carries `?brand=…`, no
5996
+ * credential). `url` is null until a Site is linked. Do not append a
5997
+ * patient identifier to this URL — use {@link mintA2PBrandEnrollmentLink}
5998
+ * to get a patient-ready link instead.
5950
5999
  */
5951
6000
  async getA2PBrandEnrollmentUrl(orgUuid, brandUuid) {
5952
6001
  return this.fetch(
5953
6002
  `o/${orgUuid}/a2p-brands/${brandUuid}/enrollment-url`
5954
6003
  );
5955
6004
  }
6005
+ /**
6006
+ * Mint a patient-ready enrollment link for one patient.
6007
+ *
6008
+ * The returned URL carries a fresh, expiring, single-use `?grant=…` bound to
6009
+ * the member that `externalId` resolves to. `externalId` (the patient's
6010
+ * CRM/EHR identifier) travels in the request body, never the query string,
6011
+ * so it stays out of access logs. When it matches no member, the
6012
+ * credential-free template URL is returned rather than an error.
6013
+ */
6014
+ async mintA2PBrandEnrollmentLink(orgUuid, brandUuid, externalId) {
6015
+ return this.fetch(
6016
+ `o/${orgUuid}/a2p-brands/${brandUuid}/enrollment-url`,
6017
+ {
6018
+ method: "POST",
6019
+ body: JSON.stringify({ externalId })
6020
+ }
6021
+ );
6022
+ }
5956
6023
  /**
5957
6024
  * List available member roles for a workspace in the organization.
5958
6025
  */
@@ -7755,6 +7822,118 @@ var ToolkitsApi = class extends BaseApiClient {
7755
7822
  }
7756
7823
  };
7757
7824
 
7825
+ // src/api/tracked-links.ts
7826
+ function withFilters(params, options) {
7827
+ if (options.dateFrom) params.append("dateFrom", options.dateFrom);
7828
+ if (options.dateTo) params.append("dateTo", options.dateTo);
7829
+ if (options.workflowId !== void 0) params.append("workflowId", String(options.workflowId));
7830
+ return params;
7831
+ }
7832
+ function suffix(params) {
7833
+ const query = params.toString();
7834
+ return query ? `?${query}` : "";
7835
+ }
7836
+ var TrackedLinksApi = class extends BaseApiClient {
7837
+ /** Header counters plus whether shortening is configured and enabled. */
7838
+ async getTrackedLinkSummary(wid, options = {}) {
7839
+ const params = withFilters(new URLSearchParams(), options);
7840
+ return this.fetch(
7841
+ `w/${wid}/tracked-links/summary${suffix(params)}`
7842
+ );
7843
+ }
7844
+ /**
7845
+ * Known destination URLs, most recent first. Powers the "Clicked link"
7846
+ * member-filter autocomplete, so it stays unaggregated and cheap.
7847
+ */
7848
+ async getTrackedLinkDestinations(wid, options = {}) {
7849
+ const params = new URLSearchParams();
7850
+ if (options.q) params.append("q", options.q);
7851
+ if (options.limit !== void 0) params.append("limit", String(options.limit));
7852
+ return this.fetch(
7853
+ `w/${wid}/tracked-links/destinations${suffix(params)}`
7854
+ );
7855
+ }
7856
+ /**
7857
+ * Register a destination an author can reference as `{{links.<key>}}`.
7858
+ *
7859
+ * The URL is deduplicated server-side, so registering one the workspace
7860
+ * already knows adopts that destination — including a row the retired
7861
+ * auto-harvest left unnamed, whose click history then carries forward
7862
+ * under the new key. Registering a URL that already has a *different* key
7863
+ * is a 409: renaming is what `updateTrackedLinkDestination` is for.
7864
+ *
7865
+ * Keys use the same grammar as snippet keys: lowercase letters, digits,
7866
+ * underscores and dots, starting with a letter (`intake_form`,
7867
+ * `docs.privacy`). No hyphens — Jinja2 reads `links.spring-2026` as
7868
+ * subtraction.
7869
+ */
7870
+ async createTrackedLinkDestination(wid, data) {
7871
+ return this.fetch(`w/${wid}/tracked-links/destinations`, {
7872
+ method: "POST",
7873
+ body: JSON.stringify(data)
7874
+ });
7875
+ }
7876
+ /**
7877
+ * Re-key or relabel a destination.
7878
+ *
7879
+ * Changing `key` breaks templates: `{{links.old}}` stops resolving and the
7880
+ * message ships without its link. Relabelling (`name`) is always safe.
7881
+ * Passing `key: null` releases the handle so another destination can take
7882
+ * it — the non-destructive alternative to deleting, which would take the
7883
+ * click history with it. The URL is not editable.
7884
+ */
7885
+ async updateTrackedLinkDestination(wid, destinationId, data) {
7886
+ return this.fetch(
7887
+ `w/${wid}/tracked-links/destinations/${destinationId}`,
7888
+ { method: "PUT", body: JSON.stringify(data) }
7889
+ );
7890
+ }
7891
+ /** Per-destination sent / clicks / unique clickers / CTR for the window. */
7892
+ async getTrackedLinkStats(wid, options = {}) {
7893
+ const params = withFilters(new URLSearchParams(), options);
7894
+ if (options.page !== void 0) params.append("page", String(options.page));
7895
+ if (options.pageSize !== void 0) params.append("pageSize", String(options.pageSize));
7896
+ if (options.q) params.append("q", options.q);
7897
+ if (options.sortBy) params.append("sortBy", options.sortBy);
7898
+ if (options.sortOrder) params.append("sortOrder", options.sortOrder);
7899
+ return this.fetch(
7900
+ `w/${wid}/tracked-links${suffix(params)}`
7901
+ );
7902
+ }
7903
+ /** Clicks over time for the current filter selection. */
7904
+ async getTrackedLinkTimeseries(wid, options = {}) {
7905
+ const params = withFilters(new URLSearchParams(), options);
7906
+ if (options.destinationId !== void 0) {
7907
+ params.append("destinationId", String(options.destinationId));
7908
+ }
7909
+ if (options.granularity) params.append("granularity", options.granularity);
7910
+ if (options.tz) params.append("tz", options.tz);
7911
+ return this.fetch(
7912
+ `w/${wid}/tracked-links/timeseries${suffix(params)}`
7913
+ );
7914
+ }
7915
+ /**
7916
+ * Delete one destination: its short links stop resolving and its recorded
7917
+ * clicks are removed.
7918
+ *
7919
+ * Turning the workspace setting off only stops NEW links being minted —
7920
+ * links already delivered to members keep working. This is the deliberate
7921
+ * way to break them.
7922
+ */
7923
+ async deleteTrackedLinkDestination(wid, destinationId) {
7924
+ return this.fetch(
7925
+ `w/${wid}/tracked-links/destinations/${destinationId}`,
7926
+ { method: "DELETE" }
7927
+ );
7928
+ }
7929
+ /** Delete every destination, its links, and its clicks. Irreversible. */
7930
+ async clearTrackedLinkDestinations(wid) {
7931
+ return this.fetch(`w/${wid}/tracked-links/destinations`, {
7932
+ method: "DELETE"
7933
+ });
7934
+ }
7935
+ };
7936
+
7758
7937
  // src/api/journeys.ts
7759
7938
  var JourneysApi = class extends BaseApiClient {
7760
7939
  /**
@@ -8412,6 +8591,21 @@ function encodePermissions(permissions) {
8412
8591
  if (permissions.delete) accessMode |= PERMISSION_DELETE;
8413
8592
  return accessMode;
8414
8593
  }
8594
+ var WORKFLOW_IMPLICIT_USE_SCOPE = "workflows:admin";
8595
+ function roleImplicitWorkflowUseScope(role) {
8596
+ return (role.grantedScopes ?? []).includes(WORKFLOW_IMPLICIT_USE_SCOPE) ? WORKFLOW_IMPLICIT_USE_SCOPE : null;
8597
+ }
8598
+ function roleWorkflowAccess(role, workflowId) {
8599
+ const viaScope = roleImplicitWorkflowUseScope(role);
8600
+ if (viaScope) {
8601
+ return { accessMode: PERMISSION_USE | PERMISSION_EDIT, viaScope };
8602
+ }
8603
+ const entry = (role.workflowAccess ?? []).find((wa) => wa.workflowId === workflowId);
8604
+ return { accessMode: entry?.accessMode ?? PERMISSION_NONE, viaScope: null };
8605
+ }
8606
+ function roleCanUseWorkflow(role, workflowId) {
8607
+ return (roleWorkflowAccess(role, workflowId).accessMode & PERMISSION_USE) !== 0;
8608
+ }
8415
8609
  var Scope = /* @__PURE__ */ ((Scope2) => {
8416
8610
  Scope2["WORKSPACE_READ"] = "workspace:read";
8417
8611
  Scope2["WORKSPACE_WRITE"] = "workspace:write";
@@ -8565,11 +8759,13 @@ var EventRuleEventType = /* @__PURE__ */ ((EventRuleEventType2) => {
8565
8759
  EventRuleEventType2["DATA_RECORD_UPDATED"] = "data_record:updated";
8566
8760
  EventRuleEventType2["DATA_RECORD_CHANGED"] = "data_record:changed";
8567
8761
  EventRuleEventType2["CHAT_CREATED"] = "chat:created";
8762
+ EventRuleEventType2["CHAT_SUMMARY"] = "chat:summary";
8568
8763
  EventRuleEventType2["CHAT_CALLBACK"] = "chat:callback";
8569
8764
  EventRuleEventType2["MEMBER_UPDATED"] = "member:updated";
8570
8765
  EventRuleEventType2["ROUTINE_TRIGGERED"] = "routine:triggered";
8571
8766
  EventRuleEventType2["ASSISTANT_MESSAGE"] = "workflow:assistant_message";
8572
8767
  EventRuleEventType2["USER_MESSAGE"] = "workflow:user_message";
8768
+ EventRuleEventType2["WORKFLOW_EXECUTE"] = "workflow:execute";
8573
8769
  EventRuleEventType2["CALENDAR_EVENT_CREATED"] = "calendar_event:created";
8574
8770
  EventRuleEventType2["CALENDAR_EVENT_UPDATED"] = "calendar_event:updated";
8575
8771
  EventRuleEventType2["CALENDAR_EVENT_CANCELLED"] = "calendar_event:cancelled";
@@ -8639,7 +8835,6 @@ var WorkspaceFeature = /* @__PURE__ */ ((WorkspaceFeature2) => {
8639
8835
  WorkspaceFeature2["WEB_SEARCH"] = "web_search";
8640
8836
  WorkspaceFeature2["CALENDAR"] = "calendar";
8641
8837
  WorkspaceFeature2["EXPERIMENTS"] = "experiments";
8642
- WorkspaceFeature2["OPERATOR"] = "operator";
8643
8838
  WorkspaceFeature2["GMAIL"] = "gmail";
8644
8839
  WorkspaceFeature2["GCALENDAR"] = "gcalendar";
8645
8840
  WorkspaceFeature2["GDRIVE"] = "gdrive";
@@ -9330,6 +9525,20 @@ var WorkspacesApi = class extends BaseApiClient {
9330
9525
  if (featured !== void 0) params.set("featured", String(featured));
9331
9526
  return this.fetch(`products?${params.toString()}`);
9332
9527
  }
9528
+ /**
9529
+ * Check whether a promo code is usable, without redeeming it.
9530
+ *
9531
+ * Reserves nothing, so it is safe to call as a form field settles. Redemption
9532
+ * happens server-side after checkout completes, when the organization and its
9533
+ * subscription exist.
9534
+ */
9535
+ async previewPromoCode(code, options) {
9536
+ return this.fetch("promo-codes/preview", {
9537
+ method: "POST",
9538
+ body: JSON.stringify({ code }),
9539
+ signal: options?.signal
9540
+ });
9541
+ }
9333
9542
  /** Get a publicly accessible product by ID. */
9334
9543
  async getPublicProduct(id) {
9335
9544
  return this.fetch(`products/${id}`);
@@ -9549,6 +9758,7 @@ function createApiModules(apiKey, baseUrl, customFetch) {
9549
9758
  members: new MembersApi(apiKey, baseUrl, customFetch),
9550
9759
  milestones: new MilestonesApi(apiKey, baseUrl, customFetch),
9551
9760
  motd: new MotdApi(apiKey, baseUrl, customFetch),
9761
+ trackedLinks: new TrackedLinksApi(apiKey, baseUrl, customFetch),
9552
9762
  confluence: new ConfluenceApi(apiKey, baseUrl, customFetch),
9553
9763
  mondayIntegration: new MondayIntegrationApi(apiKey, baseUrl, customFetch),
9554
9764
  notificationRules: new NotificationRulesApi(apiKey, baseUrl, customFetch),
@@ -9614,6 +9824,7 @@ var WORKSPACE_METHOD_NAMES = [
9614
9824
  "checkSiteSlugAvailability",
9615
9825
  "clearConnectionMemberExclusion",
9616
9826
  "clearMemberSwitch",
9827
+ "clearTrackedLinkDestinations",
9617
9828
  "clickWorkspaceSupportRequestResource",
9618
9829
  "cloneDataType",
9619
9830
  "cloneTask",
@@ -9689,6 +9900,7 @@ var WORKSPACE_METHOD_NAMES = [
9689
9900
  "createSupervisor",
9690
9901
  "createTask",
9691
9902
  "createTeamsApp",
9903
+ "createTrackedLinkDestination",
9692
9904
  "createWorkflow",
9693
9905
  "createWorkflowContributor",
9694
9906
  "createWorkflowFromTemplate",
@@ -9754,6 +9966,7 @@ var WORKSPACE_METHOD_NAMES = [
9754
9966
  "deleteSupervisor",
9755
9967
  "deleteTask",
9756
9968
  "deleteTeamsApp",
9969
+ "deleteTrackedLinkDestination",
9757
9970
  "deleteWidgetDomain",
9758
9971
  "deleteWorkflow",
9759
9972
  "deleteWorkflowContributor",
@@ -9878,6 +10091,7 @@ var WORKSPACE_METHOD_NAMES = [
9878
10091
  "getChatModelOverride",
9879
10092
  "getChatPhoneCalls",
9880
10093
  "getChatSkills",
10094
+ "getManagerSkills",
9881
10095
  "getChatTodos",
9882
10096
  "getChatTokenUsage",
9883
10097
  "getChatToolCallMessages",
@@ -9893,6 +10107,7 @@ var WORKSPACE_METHOD_NAMES = [
9893
10107
  "getConnectionSyncConfig",
9894
10108
  "getCredentialUpdateInvites",
9895
10109
  "getCurrentMember",
10110
+ "getCurrentSmsChat",
9896
10111
  "getCustomToolkit",
9897
10112
  "getCustomToolkits",
9898
10113
  "getDashboard",
@@ -10057,6 +10272,7 @@ var WORKSPACE_METHOD_NAMES = [
10057
10272
  "getPublicFolder",
10058
10273
  "getPublicFolderFiles",
10059
10274
  "getPublicProducts",
10275
+ "previewPromoCode",
10060
10276
  "getQualification",
10061
10277
  "getQualifications",
10062
10278
  "getRecordingInfo",
@@ -10103,6 +10319,10 @@ var WORKSPACE_METHOD_NAMES = [
10103
10319
  "getTool",
10104
10320
  "getToolCallNames",
10105
10321
  "getToolkits",
10322
+ "getTrackedLinkDestinations",
10323
+ "getTrackedLinkStats",
10324
+ "getTrackedLinkSummary",
10325
+ "getTrackedLinkTimeseries",
10106
10326
  "getUnreadNotificationCount",
10107
10327
  "getUsageDashboard",
10108
10328
  "getVariableRegistry",
@@ -10337,6 +10557,7 @@ var WORKSPACE_METHOD_NAMES = [
10337
10557
  "updateTask",
10338
10558
  "updateTeamsApp",
10339
10559
  "updateThemeSettings",
10560
+ "updateTrackedLinkDestination",
10340
10561
  "uploadFile",
10341
10562
  "updateVoicemailStatus",
10342
10563
  "updateWorkflow",
@@ -10428,11 +10649,13 @@ function bindTasksMethods(target, tasks, abilities) {
10428
10649
  function bindChatsMethods(target, chats) {
10429
10650
  target.getChats = chats.getChats.bind(chats);
10430
10651
  target.getChat = chats.getChat.bind(chats);
10652
+ target.getCurrentSmsChat = chats.getCurrentSmsChat.bind(chats);
10431
10653
  target.getChatCallbacks = chats.getChatCallbacks.bind(chats);
10432
10654
  target.cancelChatCallback = chats.cancelChatCallback.bind(chats);
10433
10655
  target.getChatTokenUsage = chats.getChatTokenUsage.bind(chats);
10434
10656
  target.getChatTodos = chats.getChatTodos.bind(chats);
10435
10657
  target.getChatSkills = chats.getChatSkills.bind(chats);
10658
+ target.getManagerSkills = chats.getManagerSkills.bind(chats);
10436
10659
  target.getChatsNeedsResponse = chats.getChatsNeedsResponse.bind(chats);
10437
10660
  target.getChatsDailyCreationStats = chats.getChatsDailyCreationStats.bind(chats);
10438
10661
  target.getToolCallNames = chats.getToolCallNames.bind(chats);
@@ -10506,6 +10729,18 @@ function bindMotdMethods(target, motd) {
10506
10729
  target.getMotd = motd.getMotd.bind(motd);
10507
10730
  }
10508
10731
 
10732
+ // src/client/bindings/tracked-links.ts
10733
+ function bindTrackedLinksMethods(target, trackedLinks) {
10734
+ target.getTrackedLinkSummary = trackedLinks.getTrackedLinkSummary.bind(trackedLinks);
10735
+ target.getTrackedLinkDestinations = trackedLinks.getTrackedLinkDestinations.bind(trackedLinks);
10736
+ target.getTrackedLinkStats = trackedLinks.getTrackedLinkStats.bind(trackedLinks);
10737
+ target.getTrackedLinkTimeseries = trackedLinks.getTrackedLinkTimeseries.bind(trackedLinks);
10738
+ target.createTrackedLinkDestination = trackedLinks.createTrackedLinkDestination.bind(trackedLinks);
10739
+ target.updateTrackedLinkDestination = trackedLinks.updateTrackedLinkDestination.bind(trackedLinks);
10740
+ target.deleteTrackedLinkDestination = trackedLinks.deleteTrackedLinkDestination.bind(trackedLinks);
10741
+ target.clearTrackedLinkDestinations = trackedLinks.clearTrackedLinkDestinations.bind(trackedLinks);
10742
+ }
10743
+
10509
10744
  // src/client/bindings/data-types.ts
10510
10745
  function bindDataTypesMethods(target, dataTypes) {
10511
10746
  target.getDataTypes = dataTypes.getDataTypes.bind(dataTypes);
@@ -11001,6 +11236,7 @@ function bindToolkitsMethods(target, toolkits, templates, aiModels, customToolki
11001
11236
  target.getSTTModels = aiModels.getSTTModels.bind(aiModels);
11002
11237
  target.getTTSModels = aiModels.getTTSModels.bind(aiModels);
11003
11238
  target.getVoiceModels = aiModels.getVoiceModels.bind(aiModels);
11239
+ target.getVoicePresets = aiModels.getVoicePresets.bind(aiModels);
11004
11240
  target.getAllModels = aiModels.getAllModels.bind(aiModels);
11005
11241
  target.getCustomToolkits = customToolkits.getCustomToolkits.bind(customToolkits);
11006
11242
  target.getCustomToolkit = customToolkits.getCustomToolkit.bind(customToolkits);
@@ -11466,11 +11702,13 @@ var GravityRailClient = class {
11466
11702
  // Chats API
11467
11703
  getChats;
11468
11704
  getChat;
11705
+ getCurrentSmsChat;
11469
11706
  getChatCallbacks;
11470
11707
  cancelChatCallback;
11471
11708
  getChatTokenUsage;
11472
11709
  getChatTodos;
11473
11710
  getChatSkills;
11711
+ getManagerSkills;
11474
11712
  getChatsNeedsResponse;
11475
11713
  getChatsDailyCreationStats;
11476
11714
  getToolCallNames;
@@ -11658,6 +11896,15 @@ var GravityRailClient = class {
11658
11896
  getDashboardWorkflowAssignmentStats;
11659
11897
  // MOTD API
11660
11898
  getMotd;
11899
+ // Tracked Links analytics (#10934)
11900
+ getTrackedLinkSummary;
11901
+ getTrackedLinkDestinations;
11902
+ getTrackedLinkStats;
11903
+ getTrackedLinkTimeseries;
11904
+ createTrackedLinkDestination;
11905
+ updateTrackedLinkDestination;
11906
+ deleteTrackedLinkDestination;
11907
+ clearTrackedLinkDestinations;
11661
11908
  // Journeys API
11662
11909
  getJourneys;
11663
11910
  getJourney;
@@ -11762,6 +12009,7 @@ var GravityRailClient = class {
11762
12009
  getProduct;
11763
12010
  getWorkspaceProduct;
11764
12011
  getPublicProducts;
12012
+ previewPromoCode;
11765
12013
  getPublicProduct;
11766
12014
  createProduct;
11767
12015
  updateProduct;
@@ -12116,6 +12364,7 @@ var GravityRailClient = class {
12116
12364
  getSTTModels;
12117
12365
  getTTSModels;
12118
12366
  getVoiceModels;
12367
+ getVoicePresets;
12119
12368
  getAllModels;
12120
12369
  getCustomToolkits;
12121
12370
  getCustomToolkit;
@@ -12446,6 +12695,7 @@ var GravityRailClient = class {
12446
12695
  bindQueriesMethods(this, apis.queries);
12447
12696
  bindDashboardsMethods(this, apis.dashboards);
12448
12697
  bindMotdMethods(this, apis.motd);
12698
+ bindTrackedLinksMethods(this, apis.trackedLinks);
12449
12699
  bindJourneysMethods(this, apis.journeys);
12450
12700
  bindExperimentsMethods(this, apis.experiments);
12451
12701
  bindOperatorMethods(this, apis.operatorGroups);
@@ -12638,6 +12888,14 @@ var WorkspaceBuilderResource = class {
12638
12888
  }
12639
12889
  return this._uuid;
12640
12890
  }
12891
+ /**
12892
+ * True for resources the server creates on our behalf (currently a
12893
+ * journey's `Start` step). They are kept in the resource list so edges can
12894
+ * reference them, but they are never created, printed, or counted.
12895
+ */
12896
+ get isImplicit() {
12897
+ return false;
12898
+ }
12641
12899
  matchKey() {
12642
12900
  return void 0;
12643
12901
  }
@@ -12660,6 +12918,19 @@ function resolveRefValue(value) {
12660
12918
  }
12661
12919
  return out;
12662
12920
  }
12921
+ function resolveIdRefs(value) {
12922
+ return resolveIdRefValue(value);
12923
+ }
12924
+ function resolveIdRefValue(value) {
12925
+ if (value instanceof WorkspaceBuilderResource) return value.id;
12926
+ if (Array.isArray(value)) return value.map(resolveIdRefValue);
12927
+ if (!isPlainRecord(value)) return value;
12928
+ const out = {};
12929
+ for (const [key, nestedValue] of Object.entries(value)) {
12930
+ out[key] = resolveIdRefValue(nestedValue);
12931
+ }
12932
+ return out;
12933
+ }
12663
12934
 
12664
12935
  // src/workspace-builder/automation.ts
12665
12936
  var MemberFilterBuilder = class extends WorkspaceBuilderResource {
@@ -12673,7 +12944,12 @@ var MemberFilterBuilder = class extends WorkspaceBuilderResource {
12673
12944
  description;
12674
12945
  /** Query-engine predicate (JSON IR) authored on MemberFilter.query. */
12675
12946
  query = null;
12676
- /** Set the filter's query-engine predicate (JSON IR). */
12947
+ /**
12948
+ * Set the filter's query-engine predicate (JSON IR).
12949
+ *
12950
+ * Anywhere the IR wants a numeric id you may pass the builder that will own
12951
+ * it (e.g. a label in a `member.labelId` value); refs resolve at apply time.
12952
+ */
12677
12953
  setQuery(query) {
12678
12954
  this.query = query;
12679
12955
  return this;
@@ -12682,10 +12958,12 @@ var MemberFilterBuilder = class extends WorkspaceBuilderResource {
12682
12958
  return `MemberFilter: ${this.name}${this.query ? " (query)" : ""}`;
12683
12959
  }
12684
12960
  async apply(client) {
12961
+ if (this._existed) return;
12685
12962
  const payload = {
12686
12963
  name: this.name,
12687
12964
  description: this.description,
12688
- query: this.query
12965
+ // Builder refs inside the IR become ids here; see resolveIdRefs.
12966
+ query: resolveIdRefs(this.query)
12689
12967
  };
12690
12968
  const result = await client.createMemberFilter(payload);
12691
12969
  this._id = result.id;
@@ -12980,35 +13258,134 @@ var MemberLabelBuilder = class extends WorkspaceBuilderResource {
12980
13258
  };
12981
13259
 
12982
13260
  // src/workspace-builder/journeys.ts
13261
+ var SECONDS_PER_DAY = 86400;
13262
+ function edgeKey(parentId, childId) {
13263
+ return `${parentId}:${childId}`;
13264
+ }
13265
+ function edgeCondition(options) {
13266
+ const clauses = [];
13267
+ if (options.afterDays !== void 0) {
13268
+ clauses.push(`days_since(parent_step.completed_at) >= ${options.afterDays}`);
13269
+ }
13270
+ if (options.when) clauses.push(`(${options.when})`);
13271
+ if (!clauses.length) return void 0;
13272
+ return clauses.join(" && ");
13273
+ }
12983
13274
  var JourneyStepBuilder = class extends WorkspaceBuilderResource {
12984
13275
  // journey-decoupling Phase 7: a Journey step no longer links to a Workflow.
12985
13276
  // A step nudges via EventRule actions instead of running a Workflow, so the
12986
13277
  // builder no longer accepts or emits a workflow reference.
12987
- constructor(name, journey, order = 0, description = "") {
13278
+ constructor(name, journey, order = 0, description = "", options = {}) {
12988
13279
  super();
12989
13280
  this.name = name;
12990
13281
  this.journey = journey;
12991
13282
  this.order = order;
12992
13283
  this.description = description;
13284
+ this.assigneeId = options.assigneeId;
13285
+ this.posX = options.posX;
13286
+ this.posY = options.posY;
12993
13287
  }
12994
13288
  name;
12995
13289
  journey;
12996
13290
  order;
12997
13291
  description;
12998
13292
  connections = [];
12999
- connectsTo(child, condition, required = false, timeoutSeconds) {
13000
- this.connections.push({ parent: this, child, condition, required, timeoutSeconds });
13293
+ rules = [];
13294
+ assigneeId;
13295
+ posX;
13296
+ posY;
13297
+ /** Wire an edge to `child` and return the CHILD, so chains read linearly. */
13298
+ connectsTo(child, options = {}) {
13299
+ if (child === this) {
13300
+ throw new Error(`Journey step '${this.name}' cannot connect to itself`);
13301
+ }
13302
+ const abandonSeconds = options.abandonAfterDays !== void 0 ? options.abandonAfterDays * SECONDS_PER_DAY : options.timeoutSeconds;
13303
+ this.connections.push({
13304
+ parent: this,
13305
+ child,
13306
+ condition: edgeCondition(options),
13307
+ required: options.required ?? false,
13308
+ timeoutSeconds: abandonSeconds,
13309
+ order: options.order,
13310
+ filterId: options.filterId
13311
+ });
13312
+ return child;
13313
+ }
13314
+ /**
13315
+ * Attach a `JOURNEY_STEP_EXECUTE` action — what this step actually DOES
13316
+ * when a member reaches it. A step with no rules is inert: the member
13317
+ * lands on it, nothing is sent, and the journey moves on.
13318
+ */
13319
+ onExecute(actionType, params = {}, options = {}) {
13320
+ this.rules.push({
13321
+ uuid: crypto.randomUUID(),
13322
+ actionType,
13323
+ actionParams: params,
13324
+ options
13325
+ });
13001
13326
  return this;
13002
13327
  }
13328
+ /**
13329
+ * Text the member. `phoneNumberUUID` is a workspace PhoneNumber with SMS
13330
+ * enabled. Pass `workflowUUID` to start an SMS workflow chat, or
13331
+ * `messageTemplate` for a one-off send.
13332
+ */
13333
+ sendsSms(params, options = {}) {
13334
+ return this.onExecute("sms:send" /* SMS_SEND */, params, options);
13335
+ }
13336
+ /**
13337
+ * Call the member. Both the workspace PhoneNumber uuid (voice enabled) and
13338
+ * the Workflow uuid that drives the call are required.
13339
+ */
13340
+ callsMember(params, options = {}) {
13341
+ return this.onExecute("phone:call" /* PHONE_CALL */, params, options);
13342
+ }
13343
+ /** Email the member from a workspace inbox. */
13344
+ sendsEmail(params, options = {}) {
13345
+ return this.onExecute("email:send" /* EMAIL_SEND */, params, options);
13346
+ }
13347
+ /** Label the member on arrival — pass label builders, not ids. */
13348
+ addsLabels(labels, options = {}) {
13349
+ return this.onExecute(
13350
+ "member:label" /* MEMBER_LABEL_ACTION */,
13351
+ { addLabelIds: labels },
13352
+ options
13353
+ );
13354
+ }
13355
+ /** True once this step has something to do when a member reaches it. */
13356
+ get hasOutreach() {
13357
+ return this.rules.length > 0;
13358
+ }
13359
+ eventRuleInputs() {
13360
+ return this.rules.map((rule) => ({
13361
+ uuid: rule.uuid,
13362
+ eventType: "journey_step:execute" /* JOURNEY_STEP_EXECUTE */,
13363
+ actionType: rule.actionType,
13364
+ actionParams: resolveRefs(rule.actionParams),
13365
+ ...rule.options.name ? { name: rule.options.name } : {},
13366
+ ...rule.options.when ? { conditions: rule.options.when } : {},
13367
+ ...rule.options.delaySeconds ? { delay: rule.options.delaySeconds } : {},
13368
+ active: rule.options.active ?? true
13369
+ }));
13370
+ }
13003
13371
  planLine() {
13004
- return `Step: ${this.name}`;
13372
+ const actions = this.rules.map((rule) => rule.actionType).join(" + ");
13373
+ return `Step: ${this.name}${actions ? ` (${actions})` : ""}`;
13005
13374
  }
13006
13375
  async apply(client) {
13376
+ if (this._existed) return;
13007
13377
  if (this.journey._id === void 0) return;
13378
+ const rules = this.eventRuleInputs();
13008
13379
  const payload = {
13009
13380
  name: this.name,
13010
13381
  description: this.description,
13011
- order: this.order
13382
+ order: this.order,
13383
+ ...this.assigneeId !== void 0 ? { assigneeId: this.assigneeId } : {},
13384
+ ...this.posX !== void 0 ? { posX: this.posX } : {},
13385
+ ...this.posY !== void 0 ? { posY: this.posY } : {},
13386
+ // Saved atomically with the step, so a partial failure cannot leave
13387
+ // an inert step behind.
13388
+ ...rules.length ? { eventRules: rules } : {}
13012
13389
  };
13013
13390
  const result = await client.createStep(this.journey._id, payload);
13014
13391
  this._id = result.id;
@@ -13016,26 +13393,59 @@ var JourneyStepBuilder = class extends WorkspaceBuilderResource {
13016
13393
  this._created = true;
13017
13394
  }
13018
13395
  };
13396
+ var StartStepBuilder = class extends JourneyStepBuilder {
13397
+ get isImplicit() {
13398
+ return true;
13399
+ }
13400
+ planLine() {
13401
+ return "Step: Start (auto-created)";
13402
+ }
13403
+ async apply() {
13404
+ }
13405
+ };
13019
13406
  var JourneyBuilder = class extends WorkspaceBuilderResource {
13020
13407
  constructor(name, options = {}) {
13021
13408
  super();
13022
13409
  this.name = name;
13023
13410
  this.description = options.description ?? "";
13411
+ this.start = new StartStepBuilder("Start", this, 0);
13412
+ this.steps.push(this.start);
13024
13413
  }
13025
13414
  name;
13026
13415
  description;
13027
13416
  steps = [];
13028
13417
  goals = [];
13418
+ /** The auto-created `Start` step — connect the first real step to it. */
13419
+ start;
13420
+ /** `parentId:childId` for every edge the server already has (see `adopt`). */
13421
+ existingEdges = /* @__PURE__ */ new Set();
13029
13422
  step(name, options = {}) {
13030
13423
  const step = new JourneyStepBuilder(
13031
13424
  name,
13032
13425
  this,
13033
- options.order ?? 0,
13034
- options.description ?? ""
13426
+ options.order ?? this.steps.length,
13427
+ options.description ?? "",
13428
+ options
13035
13429
  );
13036
13430
  this.steps.push(step);
13037
13431
  return step;
13038
13432
  }
13433
+ /**
13434
+ * Declare a linear cadence: `Start → steps[0] → steps[1] → …`, each edge
13435
+ * carrying the matching entry from `spacing`.
13436
+ *
13437
+ * Chaining is not optional polish. A step created with no incoming edge is
13438
+ * a ROOT, and every root fires the moment a member enrols — a "three
13439
+ * follow-ups over four days" journey built without edges sends all three
13440
+ * at once.
13441
+ */
13442
+ sequence(steps, spacing = []) {
13443
+ let parent = this.start;
13444
+ steps.forEach((step, index) => {
13445
+ parent = parent.connectsTo(step, spacing[index] ?? {});
13446
+ });
13447
+ return this;
13448
+ }
13039
13449
  goal(name, options = {}) {
13040
13450
  this.goals.push({
13041
13451
  name,
@@ -13043,17 +13453,85 @@ var JourneyBuilder = class extends WorkspaceBuilderResource {
13043
13453
  step: options.step,
13044
13454
  completionCel: options.completionCel ?? options.completion_cel,
13045
13455
  value: options.value,
13046
- order: options.order ?? 0
13456
+ order: options.order ?? this.goals.length
13047
13457
  });
13048
13458
  return this;
13049
13459
  }
13460
+ /**
13461
+ * Authoring problems that make a journey silently misbehave at runtime.
13462
+ * Reported at plan/preview time, before anything is written.
13463
+ */
13464
+ validate() {
13465
+ const problems = [];
13466
+ const withParents = /* @__PURE__ */ new Set();
13467
+ for (const step of this.steps) {
13468
+ for (const connection of step.connections) withParents.add(connection.child);
13469
+ }
13470
+ for (const step of this.steps) {
13471
+ if (step === this.start) continue;
13472
+ if (!withParents.has(step)) {
13473
+ problems.push(
13474
+ `step '${step.name}' has no incoming edge \u2014 it is a root and fires for every member the moment they enrol. Connect it from journey.start or a prior step.`
13475
+ );
13476
+ }
13477
+ if (!step.hasOutreach) {
13478
+ problems.push(
13479
+ `step '${step.name}' has no JOURNEY_STEP_EXECUTE action \u2014 a member reaching it gets nothing. Add one with .sendsSms / .callsMember / .sendsEmail / .onExecute.`
13480
+ );
13481
+ }
13482
+ }
13483
+ if (!this.goals.length) {
13484
+ problems.push(`journey '${this.name}' has no goals \u2014 nothing measures whether it works.`);
13485
+ }
13486
+ return problems;
13487
+ }
13488
+ /**
13489
+ * Adopt an existing journey, its steps, and its EDGES, matched by name.
13490
+ *
13491
+ * Re-running a builder script is normal — a program is usually built in
13492
+ * stages as provisioning lands — and without adoption the second run would
13493
+ * create a duplicate journey and a duplicate set of steps beside the live
13494
+ * one, with members enrolled in neither.
13495
+ *
13496
+ * The edge set is recorded from the server's own `childConnections` rather
13497
+ * than inferred from "both steps exist". A first run that created the steps
13498
+ * and then failed before writing an edge leaves exactly that state, and
13499
+ * inferring would skip the missing edge on every subsequent run — leaving a
13500
+ * permanent root step that fires for every member at enrolment, silently.
13501
+ * The journey detail read eager-loads connections, so the real answer is
13502
+ * already in hand; there is no reason to guess.
13503
+ */
13504
+ adopt(existing) {
13505
+ super.adopt(existing);
13506
+ const existingSteps = existing.steps ?? [];
13507
+ const byName = new Map(existingSteps.map((step) => [step.name, step]));
13508
+ const startStep = existingSteps.find((step) => step.id === existing.startStepId) ?? byName.get("Start");
13509
+ if (startStep) this.start.adopt(startStep);
13510
+ for (const step of this.steps) {
13511
+ if (step === this.start) continue;
13512
+ const match = byName.get(step.name);
13513
+ if (match) step.adopt(match);
13514
+ }
13515
+ for (const step of existingSteps) {
13516
+ for (const edge of step.childConnections ?? []) {
13517
+ this.existingEdges.add(edgeKey(edge.parentStepId, edge.childStepId));
13518
+ }
13519
+ }
13520
+ }
13521
+ /** True when the server already has this exact edge. */
13522
+ hasEdge(parent, child) {
13523
+ if (parent._id === void 0 || child._id === void 0) return false;
13524
+ return this.existingEdges.has(edgeKey(parent._id, child._id));
13525
+ }
13050
13526
  planLine() {
13051
13527
  const parts = [];
13052
- if (this.steps.length) parts.push(`${this.steps.length} steps`);
13528
+ const real = this.steps.filter((step) => step !== this.start);
13529
+ if (real.length) parts.push(`${real.length} steps`);
13053
13530
  if (this.goals.length) parts.push(`${this.goals.length} goals`);
13054
13531
  return `Journey: ${this.name}${parts.length ? ` (${parts.join(", ")})` : ""}`;
13055
13532
  }
13056
13533
  async apply(client) {
13534
+ if (this._existed) return;
13057
13535
  const result = await client.createJourney({
13058
13536
  name: this.name,
13059
13537
  description: this.description
@@ -13061,6 +13539,15 @@ var JourneyBuilder = class extends WorkspaceBuilderResource {
13061
13539
  this._id = result.id;
13062
13540
  this._uuid = result.uuid;
13063
13541
  this._created = true;
13542
+ const startStep = (result.steps ?? []).find(
13543
+ (step) => step.id === result.startStepId || step.name === "Start"
13544
+ );
13545
+ if (!startStep) {
13546
+ throw new Error(
13547
+ `Journey '${this.name}' was created without a Start step; edges from journey.start cannot be wired.`
13548
+ );
13549
+ }
13550
+ this.start.adopt(startStep);
13064
13551
  }
13065
13552
  };
13066
13553
  function buildJourneyConnectionPayload(connection) {
@@ -13070,7 +13557,9 @@ function buildJourneyConnectionPayload(connection) {
13070
13557
  childStepUuid: connection.child._uuid,
13071
13558
  ...connection.condition ? { celCondition: connection.condition } : {},
13072
13559
  ...connection.required ? { required: true } : {},
13073
- ...connection.timeoutSeconds ? { timeoutSeconds: connection.timeoutSeconds } : {}
13560
+ ...connection.timeoutSeconds ? { timeoutSeconds: connection.timeoutSeconds } : {},
13561
+ ...connection.order !== void 0 ? { order: connection.order } : {},
13562
+ ...connection.filterId !== void 0 ? { filterId: connection.filterId } : {}
13074
13563
  };
13075
13564
  }
13076
13565
  function buildJourneyGoalPayload(goal) {
@@ -13356,6 +13845,15 @@ var PHASES = /* @__PURE__ */ new Map([
13356
13845
  [JourneyBuilder, 7],
13357
13846
  [JourneyStepBuilder, 8]
13358
13847
  ]);
13848
+ function phaseFor(resource) {
13849
+ let ctor = resource.constructor;
13850
+ while (ctor) {
13851
+ const phase = PHASES.get(ctor);
13852
+ if (phase !== void 0) return phase;
13853
+ ctor = Object.getPrototypeOf(ctor);
13854
+ }
13855
+ return 99;
13856
+ }
13359
13857
  var WorkspaceBuilder = class {
13360
13858
  constructor(client) {
13361
13859
  this.client = client;
@@ -13418,7 +13916,8 @@ var WorkspaceBuilder = class {
13418
13916
  ${"=".repeat(50)}`);
13419
13917
  writeLine("Plan");
13420
13918
  writeLine("=".repeat(50));
13421
- for (const resource of resources) {
13919
+ const declared = resources.filter((resource) => !resource.isImplicit);
13920
+ for (const resource of declared) {
13422
13921
  writeLine(` + ${resource.planLine()}`);
13423
13922
  }
13424
13923
  const wiring = this.collectWiringSummary(resources);
@@ -13426,8 +13925,9 @@ ${"=".repeat(50)}`);
13426
13925
  writeLine("\n Wiring:");
13427
13926
  for (const line of wiring) writeLine(` ${line}`);
13428
13927
  }
13928
+ this.reportJourneyWarnings();
13429
13929
  writeLine(`
13430
- Total: ${resources.length} resources, ${wiring.length} connections`);
13930
+ Total: ${declared.length} resources, ${wiring.length} connections`);
13431
13931
  writeLine(`${"=".repeat(50)}
13432
13932
  `);
13433
13933
  }
@@ -13438,7 +13938,8 @@ ${"=".repeat(50)}`);
13438
13938
  ${"=".repeat(50)}`);
13439
13939
  writeLine("Preview");
13440
13940
  writeLine("=".repeat(50));
13441
- for (const resource of resources) {
13941
+ const declared = resources.filter((resource) => !resource.isImplicit);
13942
+ for (const resource of declared) {
13442
13943
  if (resource._existed) {
13443
13944
  writeLine(` = ${resource.planLine()} (exists, id=${resource._id})`);
13444
13945
  } else {
@@ -13450,8 +13951,9 @@ ${"=".repeat(50)}`);
13450
13951
  writeLine("\n Wiring:");
13451
13952
  for (const line of wiring) writeLine(` ${line}`);
13452
13953
  }
13453
- const toCreate = resources.filter((resource) => !resource._existed);
13454
- const existing = resources.filter((resource) => resource._existed);
13954
+ this.reportJourneyWarnings();
13955
+ const toCreate = declared.filter((resource) => !resource._existed);
13956
+ const existing = declared.filter((resource) => resource._existed);
13455
13957
  writeLine(
13456
13958
  `
13457
13959
  Total: ${toCreate.length} to create, ${existing.length} existing, ${wiring.length} connections`
@@ -13459,6 +13961,18 @@ ${"=".repeat(50)}`);
13459
13961
  writeLine(`${"=".repeat(50)}
13460
13962
  `);
13461
13963
  }
13964
+ /**
13965
+ * Print journey authoring problems that only bite at runtime — a step with
13966
+ * no incoming edge fires for every member at enrolment, and a step with no
13967
+ * action does nothing at all. Both look fine in the editor, so they are
13968
+ * surfaced before the write rather than discovered from a member's phone.
13969
+ */
13970
+ reportJourneyWarnings() {
13971
+ const problems = this.resources.filter((resource) => resource instanceof JourneyBuilder).flatMap((journey) => journey.validate());
13972
+ if (!problems.length) return;
13973
+ writeLine("\n Journey warnings:");
13974
+ for (const problem of problems) writeLine(` ! ${problem}`);
13975
+ }
13462
13976
  async apply() {
13463
13977
  const resources = this.sortedResources();
13464
13978
  await this.resolveExisting(resources);
@@ -13466,6 +13980,7 @@ ${"=".repeat(50)}`);
13466
13980
  writeLine("Applying...");
13467
13981
  for (const resource of resources) {
13468
13982
  try {
13983
+ if (resource.isImplicit) continue;
13469
13984
  if (resource._existed) {
13470
13985
  const key = resourceKey(resource);
13471
13986
  summary[key] ??= [];
@@ -13523,6 +14038,29 @@ Done! Created ${count} resources.`);
13523
14038
  }
13524
14039
  }
13525
14040
  }
14041
+ const filterNames = new Map(
14042
+ resources.filter(
14043
+ (resource) => resource instanceof MemberFilterBuilder
14044
+ ).map((resource) => [resource.name, resource])
14045
+ );
14046
+ if (filterNames.size) {
14047
+ for (const existing of await this.client.getMemberFilters()) {
14048
+ if (existing.name && filterNames.has(existing.name)) {
14049
+ filterNames.get(existing.name).adopt(existing);
14050
+ }
14051
+ }
14052
+ }
14053
+ const journeyNames = new Map(
14054
+ resources.filter((resource) => resource instanceof JourneyBuilder).map((resource) => [resource.name, resource])
14055
+ );
14056
+ if (journeyNames.size) {
14057
+ const existingJourneys = await this.client.getJourneys();
14058
+ for (const existing of existingJourneys.items) {
14059
+ if (!existing.name || !journeyNames.has(existing.name)) continue;
14060
+ const detail = await this.client.getJourney(existing.id);
14061
+ journeyNames.get(existing.name).adopt(detail);
14062
+ }
14063
+ }
13526
14064
  if (!workflowNames.size) return;
13527
14065
  const existingWorkflows = await this.client.getWorkflows();
13528
14066
  for (const { workflow: existing } of existingWorkflows.items) {
@@ -13573,9 +14111,7 @@ Done! Created ${count} resources.`);
13573
14111
  all.push(...resource.steps);
13574
14112
  }
13575
14113
  }
13576
- return all.sort(
13577
- (a, b) => (PHASES.get(a.constructor) ?? 99) - (PHASES.get(b.constructor) ?? 99)
13578
- );
14114
+ return all.sort((a, b) => phaseFor(a) - phaseFor(b));
13579
14115
  }
13580
14116
  collectWiringSummary(resources) {
13581
14117
  const lines = [];
@@ -13596,7 +14132,11 @@ Done! Created ${count} resources.`);
13596
14132
  lines.push(`${rule.actionType} rule on '${resource.name}'`);
13597
14133
  } else if (resource instanceof JourneyStepBuilder) {
13598
14134
  for (const connection of resource.connections) {
13599
- lines.push(`${connection.parent.name} -> ${connection.child.name}`);
14135
+ const guard = connection.condition ? ` [${connection.condition}]` : "";
14136
+ lines.push(`${connection.parent.name} -> ${connection.child.name}${guard}`);
14137
+ }
14138
+ for (const rule of resource.rules) {
14139
+ lines.push(`${rule.actionType} on step '${resource.name}'`);
13600
14140
  }
13601
14141
  } else if (resource instanceof JourneyBuilder) {
13602
14142
  for (const goal of resource.goals) lines.push(`Goal: ${goal.name}`);
@@ -13712,6 +14252,10 @@ Done! Created ${count} resources.`);
13712
14252
  if (!(resource instanceof JourneyStepBuilder)) continue;
13713
14253
  for (const connection of resource.connections) {
13714
14254
  if (resource.journey._id === void 0) continue;
14255
+ if (resource.journey.hasEdge(connection.parent, connection.child)) {
14256
+ writeLine(` Exists: ${connection.parent.name} -> ${connection.child.name}`);
14257
+ continue;
14258
+ }
13715
14259
  const payload = buildJourneyConnectionPayload(connection);
13716
14260
  if (!payload) continue;
13717
14261
  await this.client.createConnection(resource.journey._id, payload);
@@ -13722,6 +14266,12 @@ Done! Created ${count} resources.`);
13722
14266
  async applyJourneyGoals(resources) {
13723
14267
  for (const resource of resources) {
13724
14268
  if (!(resource instanceof JourneyBuilder) || resource._id === void 0) continue;
14269
+ if (resource._existed) {
14270
+ for (const goal of resource.goals) {
14271
+ writeLine(` Skipped (journey already exists): Goal: ${goal.name}`);
14272
+ }
14273
+ continue;
14274
+ }
13725
14275
  for (const goal of resource.goals) {
13726
14276
  await this.client.createGoal(resource._id, buildJourneyGoalPayload(goal));
13727
14277
  writeLine(` Created goal: ${goal.name}`);
@@ -14148,6 +14698,15 @@ var WorkflowAccessWarningResourceType = {
14148
14698
  // src/types/widget-domain-types.ts
14149
14699
  var WIDGET_SLUGS = ["chat", "operator", "operator-tools", "chat-app"];
14150
14700
 
14701
+ // src/types/generated/promo_code_service.ts
14702
+ var PromoCodeStatus = {
14703
+ active: "active",
14704
+ scheduled: "scheduled",
14705
+ expired: "expired",
14706
+ inactive: "inactive",
14707
+ exhausted: "exhausted"
14708
+ };
14709
+
14151
14710
  // src/features/registry.ts
14152
14711
  var FeatureCategory = /* @__PURE__ */ ((FeatureCategory2) => {
14153
14712
  FeatureCategory2["COMMUNICATION"] = "communication";
@@ -15160,6 +15719,7 @@ function getRevisionState(workflow, revision) {
15160
15719
  PIDS_SOURCES,
15161
15720
  POSITIVE_RATING_THRESHOLD,
15162
15721
  ProductType,
15722
+ PromoCodeStatus,
15163
15723
  QUERY_VALUELESS_OPS,
15164
15724
  QueriesApi,
15165
15725
  REALTIME_MODEL_CUSTOM,
@@ -15196,10 +15756,12 @@ function getRevisionState(workflow, revision) {
15196
15756
  TemplatesApi,
15197
15757
  ToolkitState,
15198
15758
  WIDGET_SLUGS,
15759
+ WORKFLOW_IMPLICIT_USE_SCOPE,
15199
15760
  WorkflowAccessWarningResourceType,
15200
15761
  WorkflowBuilder,
15201
15762
  WorkflowResourceType,
15202
15763
  WorkspaceBuilder,
15764
+ WorkspaceBuilderResource,
15203
15765
  WorkspaceFeature,
15204
15766
  WorkspaceProvisionStatus,
15205
15767
  areDependenciesSatisfied,
@@ -15234,6 +15796,9 @@ function getRevisionState(workflow, revision) {
15234
15796
  isSMSDeliveryFailureStatus,
15235
15797
  parseOrgAuthChallenge,
15236
15798
  requiresAssistantParticipant,
15799
+ roleCanUseWorkflow,
15800
+ roleImplicitWorkflowUseScope,
15801
+ roleWorkflowAccess,
15237
15802
  runWorkspaceScript,
15238
15803
  usesBuiltinAssistant,
15239
15804
  validateFeatureDependencies