@gravity-rail/cli 0.13.0 → 0.13.1

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/gr.js CHANGED
@@ -3247,7 +3247,7 @@ var init_libphonenumber_js = __esm({
3247
3247
  }
3248
3248
  });
3249
3249
 
3250
- // ../sdk/dist/chunk-MUEM2BLY.js
3250
+ // ../sdk/dist/chunk-YRIBZ3RM.js
3251
3251
  function sanitizePhoneForBackend(phoneNumber) {
3252
3252
  try {
3253
3253
  let parsed = parsePhoneNumber4(phoneNumber);
@@ -4439,8 +4439,8 @@ function bindSupportRequestMethods(target, supportRequests) {
4439
4439
  target.clickWorkspaceSupportRequestResource = supportRequests.clickWorkspaceSupportRequestResource.bind(supportRequests);
4440
4440
  }
4441
4441
  var AbilitiesApi, AccessGrantsApi, AgentTasksApi, AgentsApi, AIModelsApi, ApiKeysApi, AppsApi, AssignmentsApi, VoiceApi, AuthApi, BuildersApi, CalendarsApi, CallsApi, CELApi, ChatsApi, ConfluenceApi, CustomToolkitsApi, DataTypesApi, DiscordBotsApi, SlackAppsApi, EventsApi, FeaturesApi, FhirConnectionsApi, HealthieApi, FilesApi, InboxesApi, InquiriesApi, AppConnectionsApi, McpServersApi, MemberImportValidationError, MembersApi, MilestonesApi, MondayIntegrationApi, NotificationRulesApi, OperatorApi, OrgApi, OAuthConnectionsApi, OAuthApi, PhoneNumbersApi, PronunciationsApi, PromptsApi, PersonasApi, QueriesApi, QualificationsApi, ReportsApi, SalesforceApi, ConnectionSyncApi, HubspotApi, SitesApi, SubscriptionsApi, SupportRequestsApi, CustomersApi, ResellersApi, SupervisorsApi, TasksApi, TemplatesApi, ToolkitsApi, JourneysApi, WorkflowsApi, WorkflowNotesApi, EventRuleEventType, DriverMode, WorkspaceInvitationsApi, WorkspacesApi, DashboardsApi, TeamsAppsApi, ExperimentsApi, PatientSyncConnectionsApi, MotdApi, WORKSPACE_METHOD_NAMES, WORKSPACE_METHOD_NAME_SET, GravityRailClient, WIDGET_SLUGS, CHAT_TYPES, BUILTIN_ASSISTANT_CHAT_TYPES, MEMBER_AGENT_CHAT_TYPES;
4442
- var init_chunk_MUEM2BLY = __esm({
4443
- "../sdk/dist/chunk-MUEM2BLY.js"() {
4442
+ var init_chunk_YRIBZ3RM = __esm({
4443
+ "../sdk/dist/chunk-YRIBZ3RM.js"() {
4444
4444
  init_chunk_FSSXKQR5();
4445
4445
  init_libphonenumber_js();
4446
4446
  AbilitiesApi = class extends BaseApiClient {
@@ -14138,7 +14138,7 @@ var init_chunk_V7ZKEH4V = __esm({
14138
14138
  var SEARCHABLE_EVENT_RULE_SOURCE_FIELDS;
14139
14139
  var init_dist = __esm({
14140
14140
  "../sdk/dist/index.js"() {
14141
- init_chunk_MUEM2BLY();
14141
+ init_chunk_YRIBZ3RM();
14142
14142
  init_chunk_V7ZKEH4V();
14143
14143
  init_chunk_FSSXKQR5();
14144
14144
  SEARCHABLE_EVENT_RULE_SOURCE_FIELDS = [
@@ -15018,6 +15018,67 @@ var init_ui = __esm({
15018
15018
  });
15019
15019
 
15020
15020
  // src/cli-utils/agui.ts
15021
+ async function parseSseStream(body, onEvent, onId) {
15022
+ const reader = body.getReader();
15023
+ const decoder = new TextDecoder();
15024
+ let buffer = "";
15025
+ let pendingEventName;
15026
+ while (true) {
15027
+ const { done, value } = await reader.read();
15028
+ if (done) break;
15029
+ buffer += decoder.decode(value, { stream: true });
15030
+ const lines = buffer.split("\n");
15031
+ buffer = lines.pop() ?? "";
15032
+ for (const rawLine of lines) {
15033
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
15034
+ if (line === "") {
15035
+ pendingEventName = void 0;
15036
+ continue;
15037
+ }
15038
+ if (line.startsWith(":")) continue;
15039
+ if (line.startsWith("event:")) {
15040
+ const nameValue = line.slice(6);
15041
+ pendingEventName = nameValue.startsWith(" ") ? nameValue.slice(1) : nameValue;
15042
+ continue;
15043
+ }
15044
+ if (line.startsWith("id:")) {
15045
+ const idValue = line.slice(3);
15046
+ onId?.(idValue.startsWith(" ") ? idValue.slice(1) : idValue);
15047
+ continue;
15048
+ }
15049
+ if (line.startsWith("data: ")) {
15050
+ const data = line.slice(6);
15051
+ if (data === "[DONE]") {
15052
+ onEvent({ type: "done" }, pendingEventName);
15053
+ pendingEventName = void 0;
15054
+ continue;
15055
+ }
15056
+ try {
15057
+ onEvent(JSON.parse(data), pendingEventName);
15058
+ } catch {
15059
+ }
15060
+ pendingEventName = void 0;
15061
+ }
15062
+ }
15063
+ }
15064
+ }
15065
+ function abortableDelay(ms, signal) {
15066
+ return new Promise((resolve3) => {
15067
+ if (signal.aborted) {
15068
+ resolve3(false);
15069
+ return;
15070
+ }
15071
+ const onAbort = () => {
15072
+ clearTimeout(timer);
15073
+ resolve3(false);
15074
+ };
15075
+ const timer = setTimeout(() => {
15076
+ signal.removeEventListener("abort", onAbort);
15077
+ resolve3(true);
15078
+ }, ms);
15079
+ signal.addEventListener("abort", onAbort, { once: true });
15080
+ });
15081
+ }
15021
15082
  function isTextContentEvent(event) {
15022
15083
  const type = event.type.toLowerCase();
15023
15084
  return type === "text_message_content";
@@ -15149,29 +15210,70 @@ var init_agui = __esm({
15149
15210
  `Chat request failed: ${response.status}${errorText ? `: ${errorText.slice(0, 200)}` : ""}`
15150
15211
  );
15151
15212
  }
15152
- const reader = response.body.getReader();
15153
- const decoder = new TextDecoder();
15154
- let buffer = "";
15155
- while (true) {
15156
- const { done, value } = await reader.read();
15157
- if (done) break;
15158
- buffer += decoder.decode(value, { stream: true });
15159
- const lines = buffer.split("\n");
15160
- buffer = lines.pop() || "";
15161
- for (const line of lines) {
15162
- if (line.startsWith("data: ")) {
15163
- const data = line.slice(6);
15164
- if (data === "[DONE]") {
15165
- onEvent({ type: "done" });
15166
- continue;
15167
- }
15168
- try {
15169
- const event = JSON.parse(data);
15213
+ await parseSseStream(response.body, onEvent);
15214
+ }
15215
+ /**
15216
+ * Subscribe to the chat's persistent AG-UI event feed and surface
15217
+ * server-initiated turns that arrive while we're idle between our own turns.
15218
+ *
15219
+ * The feed at ``/chats/{chatId}/events`` replays every AG-UI event for the
15220
+ * chat across all turns (assistant events plus user-message echoes). It never
15221
+ * self-terminates — it ends only on disconnect. On reconnect we send
15222
+ * ``Last-Event-ID`` so the server replays the gap. The loop reconnects with
15223
+ * capped exponential backoff and resolves quietly once ``signal`` aborts;
15224
+ * it never throws except when required args are missing (callers gate on
15225
+ * availability, so that path is a programming error).
15226
+ */
15227
+ async subscribeToFeed(opts) {
15228
+ const { onEvent, signal, onDebug } = opts;
15229
+ const noChat = typeof this.chatId === "number" ? this.chatId <= 0 : this.chatId.length === 0;
15230
+ if (!this.baseUrl || !this.workspaceUuid || noChat) {
15231
+ throw new Error("subscribeToFeed requires baseUrl, workspaceUuid, and a chatId");
15232
+ }
15233
+ const feedUrl = `${this.baseUrl}/api/v2/w/${this.workspaceUuid}/chats/${this.chatId}/events`;
15234
+ let lastEventId;
15235
+ let backoffMs = 1e3;
15236
+ const maxBackoffMs = 15e3;
15237
+ const healthyConnectionMs = 3e4;
15238
+ while (!signal.aborted) {
15239
+ let receivedData = false;
15240
+ const connectedAt = Date.now();
15241
+ try {
15242
+ const headers2 = {
15243
+ Authorization: `Bearer ${this.accessToken}`,
15244
+ Accept: "text/event-stream"
15245
+ };
15246
+ if (lastEventId !== void 0) headers2["Last-Event-ID"] = lastEventId;
15247
+ const response = await fetch(feedUrl, { method: "GET", headers: headers2, signal });
15248
+ if (!response.ok || !response.body) {
15249
+ throw new Error(`feed request failed: ${response.status}`);
15250
+ }
15251
+ onDebug?.(`feed connected${lastEventId ? ` (resume ${lastEventId})` : ""}`);
15252
+ await parseSseStream(
15253
+ response.body,
15254
+ (event, eventName) => {
15255
+ receivedData = true;
15256
+ if (eventName === "feed_reset" || typeof event.type === "string" && event.type.toUpperCase() === "FEED_RESET") {
15257
+ onDebug?.("feed reset \u2014 server could not replay missed events");
15258
+ return;
15259
+ }
15170
15260
  onEvent(event);
15171
- } catch {
15261
+ },
15262
+ (id) => {
15263
+ receivedData = true;
15264
+ lastEventId = id;
15172
15265
  }
15173
- }
15266
+ );
15267
+ } catch (err) {
15268
+ if (signal.aborted) return;
15269
+ onDebug?.(`feed error: ${err instanceof Error ? err.message : String(err)}`);
15174
15270
  }
15271
+ if (signal.aborted) return;
15272
+ const wasHealthy = receivedData || Date.now() - connectedAt >= healthyConnectionMs;
15273
+ if (wasHealthy) backoffMs = 1e3;
15274
+ const completed = await abortableDelay(backoffMs, signal);
15275
+ if (!completed) return;
15276
+ if (!wasHealthy) backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
15175
15277
  }
15176
15278
  }
15177
15279
  };
@@ -33829,10 +33931,10 @@ var init_pinned_fetch = __esm({
33829
33931
  import_undici = __toESM(require_undici());
33830
33932
  }
33831
33933
  });
33832
- function makeApiClient(accessToken, apiUrl) {
33833
- const fetchFn = createSandboxFetchFromEnv(apiUrl);
33834
- const apiClient = new GravityRailClient(accessToken, apiUrl, fetchFn);
33835
- registerOrgAuthHandler(apiClient, apiUrl, accessToken);
33934
+ function makeApiClient(accessToken, apiUrl, fetchFn) {
33935
+ const resolvedFetch = createSandboxFetchFromEnv(apiUrl);
33936
+ const apiClient = new GravityRailClient(accessToken, apiUrl, resolvedFetch);
33937
+ registerOrgAuthHandler(apiClient, apiUrl, accessToken ?? "");
33836
33938
  return apiClient;
33837
33939
  }
33838
33940
  async function tryTokenRefresh(ctx, creds) {
@@ -43275,8 +43377,10 @@ Caller-facing messages
43275
43377
  smsUnavailableMessage (string)
43276
43378
  voicemailGreeting (string)
43277
43379
 
43278
- Work modes (voiceWorkMode / smsWorkMode)
43279
- always_forward | forward_off_hours | always_message | message_off_hours | voicemail | voicemail_off_hours
43380
+ Inbound calls (inboundCallHandling)
43381
+ agent | fax | always_forward | forward_off_hours | always_message | message_off_hours | voicemail | voicemail_off_hours | ring_operators
43382
+ SMS work modes (smsWorkMode)
43383
+ always_forward | forward_off_hours | always_message | message_off_hours
43280
43384
  voiceForwardNumber, smsForwardNumber, voiceOffHoursMessage, smsOffHoursMessage
43281
43385
 
43282
43386
  Provider link
@@ -43559,24 +43663,32 @@ function simulateInboundRouting(phone, opts = {}) {
43559
43663
  });
43560
43664
  return finish("anonymous_rejection_tts", false, true, steps, recommendations, callerRole);
43561
43665
  }
43562
- const voiceWorkMode = phone.voiceWorkMode;
43563
- if (voiceWorkMode === "always_forward" && phone.voiceForwardNumber) {
43564
- steps.push({ step: "voice_work_mode", outcome: "forward", detail: voiceWorkMode });
43666
+ const inboundCallHandling = phone.inboundCallHandling ?? "agent";
43667
+ if (inboundCallHandling === "fax") {
43668
+ steps.push({ step: "inbound_call_handling", outcome: "receive_fax" });
43669
+ return finish("fax_receive", true, true, steps, recommendations, callerRole);
43670
+ }
43671
+ if (inboundCallHandling === "always_forward" && phone.voiceForwardNumber) {
43672
+ steps.push({
43673
+ step: "inbound_call_handling",
43674
+ outcome: "forward",
43675
+ detail: inboundCallHandling
43676
+ });
43565
43677
  return finish("forward_pstn", false, true, steps, recommendations, callerRole);
43566
43678
  }
43567
- if (voiceWorkMode === "forward_off_hours" && !isBusinessHours && phone.voiceForwardNumber) {
43568
- steps.push({ step: "voice_work_mode", outcome: "forward_off_hours" });
43679
+ if (inboundCallHandling === "forward_off_hours" && !isBusinessHours && phone.voiceForwardNumber) {
43680
+ steps.push({ step: "inbound_call_handling", outcome: "forward_off_hours" });
43569
43681
  return finish("forward_pstn", false, true, steps, recommendations, callerRole);
43570
43682
  }
43571
- if (voiceWorkMode === "voicemail" || voiceWorkMode === "voicemail_off_hours" && !isBusinessHours) {
43572
- steps.push({ step: "voice_work_mode", outcome: "voicemail" });
43683
+ if (inboundCallHandling === "voicemail" || inboundCallHandling === "voicemail_off_hours" && !isBusinessHours) {
43684
+ steps.push({ step: "inbound_call_handling", outcome: "voicemail" });
43573
43685
  return finish("voicemail_record", true, true, steps, recommendations, callerRole);
43574
43686
  }
43575
- if ((voiceWorkMode === "always_message" || voiceWorkMode === "message_off_hours") && phone.voiceOffHoursMessage) {
43576
- steps.push({ step: "voice_work_mode", outcome: "play_message" });
43687
+ if ((inboundCallHandling === "always_message" || inboundCallHandling === "message_off_hours") && phone.voiceOffHoursMessage) {
43688
+ steps.push({ step: "inbound_call_handling", outcome: "play_message" });
43577
43689
  return finish("play_message_hangup", false, false, steps, recommendations, callerRole);
43578
43690
  }
43579
- steps.push({ step: "voice_work_mode", outcome: "ai_path" });
43691
+ steps.push({ step: "inbound_call_handling", outcome: "ai_path" });
43580
43692
  const workflowId = phone.defaultWorkflowId;
43581
43693
  if (workflowId === null || workflowId === void 0) {
43582
43694
  steps.push({
@@ -43631,6 +43743,7 @@ function finish(code, createsChat, createsPhoneCall, steps, recommendations, cal
43631
43743
  forward_pstn: "Forward to voiceForwardNumber",
43632
43744
  voicemail_record: "Record voicemail",
43633
43745
  play_message_hangup: "Play off-hours message, hang up",
43746
+ fax_receive: "Create fax chat + receive document",
43634
43747
  no_inbound_workflow: "Play unavailable message (no inbound workflow)",
43635
43748
  workflow_member_role_denied: "Reject \u2014 caller member role lacks workflow USE (memberRoleAccess)",
43636
43749
  ai_voice_chat: "Create chat + connect AI voice"
@@ -43703,9 +43816,7 @@ function formatPhoneNumberBehaviorMatrix(phone, outboundUsers, warnings = collec
43703
43816
  lines.push(
43704
43817
  ` allowAnonymous: ${phone.allowAnonymous} (role ${phone.anonymousMemberRoleId ?? "\u2014"}) allowSignup: ${phone.allowSignup} (role ${phone.signupMemberRoleId ?? "\u2014"})`
43705
43818
  );
43706
- if (phone.voiceWorkMode) {
43707
- lines.push(` voiceWorkMode: ${phone.voiceWorkMode}`);
43708
- }
43819
+ lines.push(` inboundCallHandling: ${phone.inboundCallHandling ?? "agent"}`);
43709
43820
  lines.push("");
43710
43821
  lines.push("## Outbound users");
43711
43822
  if (outboundUsers.length === 0) {
@@ -50173,6 +50284,10 @@ var ChatTUI = class {
50173
50284
  this.activeToolName = "";
50174
50285
  this.dirty();
50175
50286
  }
50287
+ /** Whether a local user→assistant turn is currently in progress. */
50288
+ get isTurnInProgress() {
50289
+ return this.turnInProgress;
50290
+ }
50176
50291
  /** Temporarily leave alt-screen for an external prompt (HITL). */
50177
50292
  pauseForPrompt() {
50178
50293
  if (!this.started || this.pausedForPrompt) return;
@@ -50215,6 +50330,10 @@ var ChatTUI = class {
50215
50330
  }
50216
50331
  /** Add a transcription of the user's spoken words (voice mode) */
50217
50332
  addTranscription(text) {
50333
+ this.addUserMessage(text);
50334
+ }
50335
+ /** Append a user message (e.g. a feed echo of a member's message). */
50336
+ addUserMessage(text) {
50218
50337
  this.items.push({ role: "user", content: text });
50219
50338
  this.scrollOffset = 0;
50220
50339
  this.dirty();
@@ -50982,6 +51101,38 @@ function handleAguiEvent(tui, event) {
50982
51101
  }
50983
51102
  }
50984
51103
  }
51104
+ function createFeedRenderer(tui, isTurnInProgress) {
51105
+ const renderedMessageIds = /* @__PURE__ */ new Set();
51106
+ const userMessageBuffers = /* @__PURE__ */ new Map();
51107
+ return (event) => {
51108
+ if (isTurnInProgress()) return;
51109
+ const type = event.type?.toLowerCase() ?? "";
51110
+ const messageId = event.messageId;
51111
+ if (messageId && renderedMessageIds.has(messageId)) return;
51112
+ if (type === "text_message_start" && (event.role ?? "").toLowerCase() === "user") {
51113
+ if (messageId) userMessageBuffers.set(messageId, "");
51114
+ return;
51115
+ }
51116
+ if (messageId !== void 0 && userMessageBuffers.has(messageId)) {
51117
+ if (type === "text_message_content") {
51118
+ userMessageBuffers.set(
51119
+ messageId,
51120
+ (userMessageBuffers.get(messageId) ?? "") + (event.delta ?? "")
51121
+ );
51122
+ } else if (type === "text_message_end") {
51123
+ const text = (userMessageBuffers.get(messageId) ?? "").trim();
51124
+ userMessageBuffers.delete(messageId);
51125
+ renderedMessageIds.add(messageId);
51126
+ if (text) tui.addUserMessage(text);
51127
+ }
51128
+ return;
51129
+ }
51130
+ handleAguiEvent(tui, event);
51131
+ if (type === "text_message_end" && messageId) {
51132
+ renderedMessageIds.add(messageId);
51133
+ }
51134
+ };
51135
+ }
50985
51136
  var SAMPLE_RATE = 24e3;
50986
51137
  var MIC_CHUNK_BYTES = 4800;
50987
51138
  function checkSoxAvailable() {
@@ -51488,6 +51639,8 @@ async function interactiveChatSession(session, debug, autoApprove, allowWrite) {
51488
51639
  tui.runFinished();
51489
51640
  }
51490
51641
  };
51642
+ const feedController = new AbortController();
51643
+ const canSubscribeToFeed = !!session.workspaceUuid && session.chatId > 0;
51491
51644
  await new Promise((resolve3) => {
51492
51645
  tui = new ChatTUI({
51493
51646
  title: session.label,
@@ -51496,6 +51649,7 @@ async function interactiveChatSession(session, debug, autoApprove, allowWrite) {
51496
51649
  void sendTurn(text);
51497
51650
  },
51498
51651
  onExit: () => {
51652
+ feedController.abort();
51499
51653
  tui.stop();
51500
51654
  resolve3();
51501
51655
  },
@@ -51504,6 +51658,24 @@ async function interactiveChatSession(session, debug, autoApprove, allowWrite) {
51504
51658
  });
51505
51659
  tui.loadHistory(history);
51506
51660
  tui.start();
51661
+ if (canSubscribeToFeed) {
51662
+ const renderFeedEvent = createFeedRenderer(tui, () => tui.isTurnInProgress);
51663
+ void aguiClient.subscribeToFeed({
51664
+ onEvent: renderFeedEvent,
51665
+ signal: feedController.signal,
51666
+ onDebug: (msg) => tui.logDebug("in", { type: "feed", message: msg })
51667
+ }).catch((err) => {
51668
+ tui.logDebug("in", {
51669
+ type: "feed",
51670
+ message: `feed subscription ended: ${err instanceof Error ? err.message : String(err)}`
51671
+ });
51672
+ });
51673
+ } else {
51674
+ tui.logDebug("in", {
51675
+ type: "feed",
51676
+ message: "feed subscription skipped (concierge session has no workspace/chat feed)"
51677
+ });
51678
+ }
51507
51679
  });
51508
51680
  }
51509
51681
  function buildConversationMessages(messages) {
@@ -52695,7 +52867,7 @@ init_concierge_api();
52695
52867
 
52696
52868
  // package.json
52697
52869
  var package_default = {
52698
- version: "0.13.0"};
52870
+ version: "0.13.1"};
52699
52871
 
52700
52872
  // src/gr.ts
52701
52873
  var VERSION = package_default.version;