@wayai/cli 0.3.157 → 0.3.159

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
@@ -7376,9 +7376,26 @@ var init_contracts = __esm({
7376
7376
  listConversationsQuery = external_exports.object({
7377
7377
  hub_id: external_exports.string().uuid(),
7378
7378
  status: external_exports.enum(["agent", "team", "ended"]).optional(),
7379
+ /**
7380
+ * Last-activity window, ISO-8601 UTC, both bounds inclusive. Filters the hub
7381
+ * conversation index on its last activity (`last_message_at`, falling back to
7382
+ * `created_at` for a row that has no message yet) — NOT on conversation start.
7383
+ *
7384
+ * Activity is the axis a support-queue check needs and the one this listing
7385
+ * already sorts by and returns as `updated_at`: `activity_to` alone answers
7386
+ * "team conversations with nothing since X" (idle), which a start-time window
7387
+ * cannot express at all. The ClickHouse-backed analytics list
7388
+ * (`POST /api/analytics/conversations`) windows `created_at` instead and only
7389
+ * ever holds ENDED conversations, so it can never answer this for `agent`/`team`.
7390
+ */
7391
+ activity_from: external_exports.string().datetime().optional(),
7392
+ activity_to: external_exports.string().datetime().optional(),
7379
7393
  limit: external_exports.coerce.number().int().min(1).max(100).default(20),
7380
7394
  offset: external_exports.coerce.number().int().min(0).default(0)
7381
- });
7395
+ }).refine(
7396
+ (q) => q.activity_from === void 0 || q.activity_to === void 0 || Date.parse(q.activity_from) <= Date.parse(q.activity_to),
7397
+ { message: "activity_from must not be after activity_to", path: ["activity_from"] }
7398
+ );
7382
7399
  flagSourceSchema = external_exports.enum(["evaluator", "monitor", "both"]).nullable();
7383
7400
  getConversationsResponse = external_exports.object({
7384
7401
  conversations: external_exports.array(
@@ -9164,6 +9181,8 @@ var init_api_client = __esm({
9164
9181
  if (opts?.status) params.set("status", opts.status);
9165
9182
  if (opts?.limit) params.set("limit", String(opts.limit));
9166
9183
  if (opts?.offset) params.set("offset", String(opts.offset));
9184
+ if (opts?.activityFrom) params.set("activity_from", opts.activityFrom);
9185
+ if (opts?.activityTo) params.set("activity_to", opts.activityTo);
9167
9186
  return getConversationsResponse.parse(
9168
9187
  await this.request("GET", `/api/conversations?${params.toString()}`)
9169
9188
  );
@@ -10148,6 +10167,12 @@ function refineToolConfig2(value, ctx) {
10148
10167
  }
10149
10168
  }
10150
10169
  }
10170
+ function toIso(epochMs) {
10171
+ return new Date(epochMs).toISOString();
10172
+ }
10173
+ function fromIso(iso) {
10174
+ return new Date(iso).getTime();
10175
+ }
10151
10176
  function isIsoTimestamp2(value) {
10152
10177
  return typeof value === "string" && ISO_UTC_RE2.test(value);
10153
10178
  }
@@ -13089,9 +13114,26 @@ var init_dist = __esm({
13089
13114
  listConversationsQuery2 = external_exports.object({
13090
13115
  hub_id: external_exports.string().uuid(),
13091
13116
  status: external_exports.enum(["agent", "team", "ended"]).optional(),
13117
+ /**
13118
+ * Last-activity window, ISO-8601 UTC, both bounds inclusive. Filters the hub
13119
+ * conversation index on its last activity (`last_message_at`, falling back to
13120
+ * `created_at` for a row that has no message yet) — NOT on conversation start.
13121
+ *
13122
+ * Activity is the axis a support-queue check needs and the one this listing
13123
+ * already sorts by and returns as `updated_at`: `activity_to` alone answers
13124
+ * "team conversations with nothing since X" (idle), which a start-time window
13125
+ * cannot express at all. The ClickHouse-backed analytics list
13126
+ * (`POST /api/analytics/conversations`) windows `created_at` instead and only
13127
+ * ever holds ENDED conversations, so it can never answer this for `agent`/`team`.
13128
+ */
13129
+ activity_from: external_exports.string().datetime().optional(),
13130
+ activity_to: external_exports.string().datetime().optional(),
13092
13131
  limit: external_exports.coerce.number().int().min(1).max(100).default(20),
13093
13132
  offset: external_exports.coerce.number().int().min(0).default(0)
13094
- });
13133
+ }).refine(
13134
+ (q) => q.activity_from === void 0 || q.activity_to === void 0 || Date.parse(q.activity_from) <= Date.parse(q.activity_to),
13135
+ { message: "activity_from must not be after activity_to", path: ["activity_from"] }
13136
+ );
13095
13137
  flagSourceSchema2 = external_exports.enum(["evaluator", "monitor", "both"]).nullable();
13096
13138
  getConversationsResponse2 = external_exports.object({
13097
13139
  conversations: external_exports.array(
@@ -15277,7 +15319,7 @@ function planOrgMigration(gitRoot) {
15277
15319
  function writeRepoConfig(config, root) {
15278
15320
  const gitRoot = root ?? findGitRoot();
15279
15321
  if (!gitRoot) {
15280
- throw new Error("Not inside a git repository. Run `git init` first.");
15322
+ throw expected("Not inside a git repository. Run `git init` first.");
15281
15323
  }
15282
15324
  const stale = declarationFrom(loadYamlMapping(rootConfigPath(gitRoot)));
15283
15325
  if (stale.kind === "declared" && stale.config.organization_id !== config.organization_id) {
@@ -21773,6 +21815,140 @@ var init_eval_format = __esm({
21773
21815
  }
21774
21816
  });
21775
21817
 
21818
+ // src/lib/cross-hub-conversations.ts
21819
+ async function mapWithConcurrency(items, concurrency, worker) {
21820
+ const results = new Array(items.length);
21821
+ let next = 0;
21822
+ const runners = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
21823
+ for (; ; ) {
21824
+ const index = next++;
21825
+ if (index >= items.length) return;
21826
+ results[index] = await worker(items[index]);
21827
+ }
21828
+ });
21829
+ await Promise.all(runners);
21830
+ return results;
21831
+ }
21832
+ async function resolveTargets(client, orgs, errors) {
21833
+ const perOrg = await mapWithConcurrency(orgs, CROSS_HUB_CONCURRENCY, async (org) => {
21834
+ try {
21835
+ const { hubs } = await client.listHubs(org.id);
21836
+ const targets = [];
21837
+ for (const hub of hubs) {
21838
+ targets.push({
21839
+ hub_id: hub.hub_id,
21840
+ hub_name: hub.hub_name,
21841
+ hub_environment: "preview",
21842
+ organization_id: org.id,
21843
+ organization_name: org.name,
21844
+ derived: false
21845
+ });
21846
+ if (hub.production_hub_id) {
21847
+ targets.push({
21848
+ hub_id: hub.production_hub_id,
21849
+ hub_name: hub.hub_name,
21850
+ hub_environment: "production",
21851
+ organization_id: org.id,
21852
+ organization_name: org.name,
21853
+ derived: true
21854
+ });
21855
+ }
21856
+ }
21857
+ return targets;
21858
+ } catch (err) {
21859
+ errors.push({
21860
+ scope_kind: "org",
21861
+ hub_id: null,
21862
+ hub_name: null,
21863
+ organization_id: org.id,
21864
+ error: extractApiMessage(err)
21865
+ });
21866
+ return [];
21867
+ }
21868
+ });
21869
+ const seen = /* @__PURE__ */ new Set();
21870
+ return perOrg.flat().filter((target) => {
21871
+ if (seen.has(target.hub_id)) return false;
21872
+ seen.add(target.hub_id);
21873
+ return true;
21874
+ });
21875
+ }
21876
+ async function listConversationsAcrossHubs(client, scope, opts) {
21877
+ const errors = [];
21878
+ const orgs = scope.mode === "all" ? (await client.organizations()).organizations : [{ id: scope.orgId, name: null }];
21879
+ const targets = await resolveTargets(client, orgs, errors);
21880
+ if (targets.length > MAX_CROSS_HUB_HUBS) {
21881
+ throw expected(
21882
+ `${targets.length} hubs are in scope, above the ${MAX_CROSS_HUB_HUBS}-hub cap for one cross-hub listing. Narrow the scope with --org <organization_id>.`
21883
+ );
21884
+ }
21885
+ const outOfScope = [];
21886
+ let hubsRead = 0;
21887
+ const perHub = await mapWithConcurrency(targets, CROSS_HUB_CONCURRENCY, async (target) => {
21888
+ const { derived, ...row } = target;
21889
+ try {
21890
+ const result2 = await client.getConversations(target.hub_id, opts);
21891
+ hubsRead++;
21892
+ return result2.conversations.map((conv) => ({ ...conv, ...row }));
21893
+ } catch (err) {
21894
+ if (derived && isAuthorizationRefusal(err)) {
21895
+ outOfScope.push(target.hub_id);
21896
+ return [];
21897
+ }
21898
+ errors.push({
21899
+ scope_kind: "hub",
21900
+ hub_id: target.hub_id,
21901
+ hub_name: target.hub_name,
21902
+ organization_id: target.organization_id,
21903
+ error: extractApiMessage(err)
21904
+ });
21905
+ return [];
21906
+ }
21907
+ });
21908
+ const conversations = perHub.flat().map((row) => ({ row, key: Date.parse(row.updated_at ?? row.created_at) })).sort((a, b) => b.key - a.key).map(({ row }) => row);
21909
+ const scopesTotal = targets.length - outOfScope.length + errors.filter((f) => f.scope_kind === "org").length;
21910
+ const result = {
21911
+ scope,
21912
+ organizations_queried: orgs.length,
21913
+ hubs_queried: targets.length,
21914
+ hubs_read: hubsRead,
21915
+ hubs_out_of_scope: outOfScope,
21916
+ scopes_total: scopesTotal,
21917
+ scopes_failed: errors.length,
21918
+ conversations,
21919
+ partial: errors.length > 0,
21920
+ errors
21921
+ };
21922
+ assertScopeAccounting(result);
21923
+ return result;
21924
+ }
21925
+ function isAuthorizationRefusal(err) {
21926
+ return err instanceof ApiError && err.status === 403;
21927
+ }
21928
+ function assertScopeAccounting(result) {
21929
+ const hubFailures = result.errors.filter((f) => f.scope_kind === "hub").length;
21930
+ const accountedFor = result.hubs_read + result.hubs_out_of_scope.length + hubFailures;
21931
+ if (accountedFor !== result.hubs_queried) {
21932
+ throw new Error(
21933
+ `cross-hub accounting: ${result.hubs_queried} hubs attempted but ${accountedFor} accounted for (read ${result.hubs_read}, out of scope ${result.hubs_out_of_scope.length}, failed ${hubFailures})`
21934
+ );
21935
+ }
21936
+ if (result.partial !== result.scopes_failed > 0) {
21937
+ throw new Error(`cross-hub accounting: partial=${result.partial} disagrees with scopes_failed=${result.scopes_failed}`);
21938
+ }
21939
+ }
21940
+ var MAX_CROSS_HUB_HUBS, CROSS_HUB_CONCURRENCY;
21941
+ var init_cross_hub_conversations = __esm({
21942
+ "src/lib/cross-hub-conversations.ts"() {
21943
+ "use strict";
21944
+ init_api_client();
21945
+ init_errors2();
21946
+ init_expected();
21947
+ MAX_CROSS_HUB_HUBS = 100;
21948
+ CROSS_HUB_CONCURRENCY = 6;
21949
+ }
21950
+ });
21951
+
21776
21952
  // src/commands/observability.ts
21777
21953
  async function runConversationObservability(client, hubId, conversationId, opts) {
21778
21954
  if (opts.messageId) {
@@ -21895,8 +22071,36 @@ var init_observability = __esm({
21895
22071
  // src/commands/conversations.ts
21896
22072
  var conversations_exports = {};
21897
22073
  __export(conversations_exports, {
21898
- conversationsCommand: () => conversationsCommand
22074
+ conversationsCommand: () => conversationsCommand,
22075
+ detectCrossHubScope: () => detectCrossHubScope
21899
22076
  });
22077
+ function detectCrossHubScope(args2) {
22078
+ const all = args2.includes("--all");
22079
+ const orgIdx = args2.findIndex((a) => a === "--org" || a.startsWith("--org="));
22080
+ const inlineOrg = orgIdx !== -1 && args2[orgIdx].startsWith("--org=");
22081
+ const orgValue = orgIdx === -1 ? void 0 : inlineOrg ? args2[orgIdx].slice("--org=".length) : args2[orgIdx + 1];
22082
+ if (orgIdx !== -1) {
22083
+ if (!orgValue || orgValue.startsWith("-")) {
22084
+ console.error("--org requires an organization id (uuid). Use --all to list every organization.");
22085
+ process.exit(1);
22086
+ }
22087
+ if (!UUID_RE2.test(orgValue)) {
22088
+ console.error(`Invalid --org value: ${orgValue}. Expected an organization id (uuid).`);
22089
+ process.exit(1);
22090
+ }
22091
+ }
22092
+ if (all && orgIdx !== -1) {
22093
+ console.error("--all and --org are mutually exclusive: --all already spans every organization.");
22094
+ process.exit(1);
22095
+ }
22096
+ if ((all || orgIdx !== -1) && args2.includes("--hub")) {
22097
+ console.error("--hub cannot be combined with --all/--org: a cross-hub listing spans every accessible hub.");
22098
+ process.exit(1);
22099
+ }
22100
+ if (all) return { mode: "all" };
22101
+ if (orgIdx !== -1) return { mode: "org", orgId: orgValue };
22102
+ return null;
22103
+ }
21900
22104
  function printConversationsHelp() {
21901
22105
  console.error(`
21902
22106
  wayai conversations \u2014 list conversations or inspect what an agent received
@@ -21904,8 +22108,11 @@ wayai conversations \u2014 list conversations or inspect what an agent received
21904
22108
  Usage:
21905
22109
  wayai conversations List conversations
21906
22110
  wayai conversations --status <agent|team|ended> Filter by status
22111
+ wayai conversations --status team --period 7d Status + a last-activity window
21907
22112
  wayai conversations --period 7d Analytics-powered list
21908
22113
  wayai conversations --from <date> --to <date> List within a date range
22114
+ wayai conversations --org <organization_id> Every hub in one organization
22115
+ wayai conversations --all Every hub this login can access
21909
22116
  wayai conversations <conversation_id> Show detail + messages
21910
22117
  wayai conversations <conversation_id> observability List the LLM turns (per-message id, latency, tool_calls)
21911
22118
  wayai conversations <conversation_id> observability --message-id <id>
@@ -21923,10 +22130,32 @@ Usage:
21923
22130
 
21924
22131
  Options:
21925
22132
  --message-id <id> Expand one turn (only with the \`observability\` subcommand)
21926
- --limit <n> Max rows (list view)
21927
- --offset <n> Skip rows (list view)
22133
+ --limit <n> Max rows (list view; PER HUB with --all/--org)
22134
+ --offset <n> Skip rows (list view; per hub with --all/--org)
21928
22135
  --json Raw JSON output
21929
22136
 
22137
+ Time windows:
22138
+ With \`--status\`, or with \`--all\`/\`--org\`, the listing comes from the live hub
22139
+ index and \`--period\`/\`--from\`/\`--to\` bound LAST ACTIVITY. So
22140
+ \`--status team --to 2026-08-22\` is "team conversations with nothing since the
22141
+ 22nd" \u2014 the idle queue. Without \`--status\` AND without \`--all\`/\`--org\`, a bare
22142
+ \`--period\`/\`--from\`/\`--to\` still runs the ClickHouse-backed analytics list,
22143
+ which windows conversation START and only ever holds ended conversations.
22144
+
22145
+ Cross-hub:
22146
+ \`--all\` / \`--org <id>\` list across the hubs \`wayai list\` enumerates PLUS the
22147
+ production hubs those name as their parents. Two different cases end in a hub
22148
+ going unread, and they are reported differently:
22149
+ - A production PARENT this login cannot read is NAMED as skipped and does not
22150
+ make the run fail \u2014 it is a standing property of the login, not an outage.
22151
+ - A production hub whose PREVIEW this login cannot see is not enumerable
22152
+ anywhere, so it is not covered and NOT reported at all. Reach it with
22153
+ \`--hub <id>\`.
22154
+ Each hub is read with the caller's own grants; a hub IN SCOPE that refuses is
22155
+ reported and the listing exits non-zero as partial rather than looking like a
22156
+ smaller queue, and an empty scope is reported as such, never as an empty queue.
22157
+ \`--org\` accepts \`--org <id>\` and \`--org=<id>\`.
22158
+
21930
22159
  The default text list omits message ids \u2014 use \`--json\` or the \`observability\`
21931
22160
  subcommand to discover them. \`observability\` is the answer to "the agent did X \u2014
21932
22161
  what did it ACTUALLY see?".
@@ -21937,9 +22166,10 @@ async function conversationsCommand(args2) {
21937
22166
  printConversationsHelp();
21938
22167
  return;
21939
22168
  }
21940
- const hubId = resolveActiveHubId(args2);
22169
+ const crossHubScope = detectCrossHubScope(args2);
22170
+ const hubId = crossHubScope ? "" : resolveActiveHubId(args2);
21941
22171
  const { config, accessToken } = await requireAuth();
21942
- requireRepoConfig();
22172
+ if (!crossHubScope) requireRepoConfig();
21943
22173
  let status;
21944
22174
  let conversationId;
21945
22175
  let subcommand;
@@ -22012,6 +22242,9 @@ async function conversationsCommand(args2) {
22012
22242
  }
22013
22243
  } else if (arg === "--json") {
22014
22244
  jsonOutput = true;
22245
+ } else if (arg === "--all" || arg.startsWith("--org=")) {
22246
+ } else if (arg === "--org") {
22247
+ i++;
22015
22248
  } else if (!arg.startsWith("-")) {
22016
22249
  if (conversationId === void 0 && arg === "observability") {
22017
22250
  subcommand = arg;
@@ -22027,6 +22260,10 @@ async function conversationsCommand(args2) {
22027
22260
  }
22028
22261
  const client = new ApiClient({ apiUrl: config.api_url, accessToken });
22029
22262
  const hasAnalyticsFlags = !!(period || from || to);
22263
+ if (crossHubScope && (conversationId || subcommand)) {
22264
+ console.error("--all/--org apply to the conversation LIST only. Drop them and pass --hub <id> to open one conversation.");
22265
+ process.exit(1);
22266
+ }
22030
22267
  if (subcommand === "observability") {
22031
22268
  if (!conversationId) {
22032
22269
  console.error("Usage: wayai conversations <conversation_id> observability [--message-id X] [--json]");
@@ -22152,11 +22389,65 @@ async function conversationsCommand(args2) {
22152
22389
  }
22153
22390
  console.log("Diagnosing agent behavior? The transcript shows what was sent \u2014 not what the model received.");
22154
22391
  console.log(`Drill in: wayai conversations ${conversationId} observability (per-turn resolved prompt, tool calls, latency)`);
22155
- } else if (hasAnalyticsFlags) {
22156
- if (status) {
22157
- console.error("The --status flag is not supported with --period/--from/--to. Without these flags, --status works as before.");
22392
+ } else if (crossHubScope) {
22393
+ const window = resolveActivityWindow(period, from, to);
22394
+ const result = await listConversationsAcrossHubs(client, crossHubScope, {
22395
+ status,
22396
+ limit,
22397
+ offset,
22398
+ activityFrom: window.start,
22399
+ activityTo: window.end
22400
+ });
22401
+ if (jsonOutput) {
22402
+ console.log(JSON.stringify(result, null, 2));
22403
+ if (result.partial || result.hubs_queried === 0) process.exit(1);
22404
+ return;
22405
+ }
22406
+ for (const failure of result.errors) {
22407
+ const where = failure.scope_kind === "hub" ? `hub ${sanitizeTerminalText(failure.hub_name || failure.hub_id || "")} (${failure.hub_id})` : `organization ${failure.organization_id}`;
22408
+ console.error(`Could not read ${where}: ${sanitizeTerminalText(failure.error)}`);
22409
+ }
22410
+ if (result.hubs_queried === 0) {
22411
+ console.error(
22412
+ `No hubs were in scope across ${result.organizations_queried} organization(s), so no queue was read. \`wayai list\` shows the hubs this login can enumerate; use \`--hub <id>\` for one hub outside that set.`
22413
+ );
22414
+ process.exit(1);
22415
+ }
22416
+ if (result.hubs_out_of_scope.length > 0) {
22417
+ const ids = result.hubs_out_of_scope.map(sanitizeTerminalText).join(", ");
22418
+ console.error(
22419
+ `Note: ${result.hubs_out_of_scope.length} production hub(s) are outside this login's grants and were not read: ${ids}. Reach one with --hub <id>.`
22420
+ );
22421
+ }
22422
+ if (result.conversations.length === 0) {
22423
+ console.log(`No conversations found across ${result.hubs_read} hub(s).`);
22424
+ } else {
22425
+ console.log(`Conversations (${result.conversations.length} across ${result.hubs_read} hubs):
22426
+ `);
22427
+ for (const conv of result.conversations) {
22428
+ const user = sanitizeTerminalText(conv.user_name || conv.user_email || "unknown");
22429
+ const updated = conv.updated_at ? new Date(conv.updated_at).toLocaleString() : "-";
22430
+ const env = conv.hub_environment === "production" ? "prod" : "prev";
22431
+ const hub = truncate(sanitizeTerminalText(conv.hub_name || conv.hub_id), HUB_COL_WIDTH);
22432
+ console.log(` ${hub.padEnd(HUB_COL_WIDTH)} ${env} ${conv.conversation_id} ${conv.conversation_status.padEnd(7)} ${user.padEnd(20)} ${updated}`);
22433
+ }
22434
+ }
22435
+ if (result.partial) {
22436
+ console.error(`
22437
+ Partial listing: ${result.scopes_failed} of ${result.scopes_total} scope(s) could not be read.`);
22158
22438
  process.exit(1);
22159
22439
  }
22440
+ } else if (status && hasAnalyticsFlags) {
22441
+ const window = resolveActivityWindow(period, from, to);
22442
+ const result = await client.getConversations(hubId, {
22443
+ status,
22444
+ limit,
22445
+ offset,
22446
+ activityFrom: window.start,
22447
+ activityTo: window.end
22448
+ });
22449
+ printIndexList(result, jsonOutput);
22450
+ } else if (hasAnalyticsFlags) {
22160
22451
  if (from && !to || !from && to) {
22161
22452
  console.error("--from and --to must both be provided together.");
22162
22453
  process.exit(1);
@@ -22211,28 +22502,62 @@ async function conversationsCommand(args2) {
22211
22502
  }
22212
22503
  } else {
22213
22504
  const result = await client.getConversations(hubId, { status, limit, offset });
22214
- if (jsonOutput) {
22215
- console.log(JSON.stringify(result, null, 2));
22216
- return;
22505
+ printIndexList(result, jsonOutput);
22506
+ }
22507
+ }
22508
+ function printIndexList(result, jsonOutput) {
22509
+ if (jsonOutput) {
22510
+ console.log(JSON.stringify(result, null, 2));
22511
+ return;
22512
+ }
22513
+ if (result.conversations.length === 0) {
22514
+ console.log("No conversations found.");
22515
+ return;
22516
+ }
22517
+ console.log(`Conversations (${result.conversations.length} of ${result.total}):
22518
+ `);
22519
+ for (const conv of result.conversations) {
22520
+ const user = conv.user_name || conv.user_email || "unknown";
22521
+ const updated = conv.updated_at ? new Date(conv.updated_at).toLocaleString() : "-";
22522
+ const kanban = truncate(
22523
+ conv.outcome ? `${conv.kanban_status ?? "-"}/${conv.outcome}` : conv.kanban_status ?? "-",
22524
+ KANBAN_COL_WIDTH
22525
+ );
22526
+ console.log(` ${conv.conversation_id} ${conv.conversation_status.padEnd(7)} ${kanban.padEnd(KANBAN_COL_WIDTH)} ${user.padEnd(20)} ${updated}`);
22527
+ }
22528
+ }
22529
+ function resolveActivityWindow(period, from, to) {
22530
+ let start;
22531
+ let end;
22532
+ if (from || to) {
22533
+ try {
22534
+ start = from ? canonicalIso(parseDateArg(from, "from")) : void 0;
22535
+ end = to ? canonicalIso(parseDateArg(to, "to")) : void 0;
22536
+ } catch (err) {
22537
+ console.error(err instanceof Error ? err.message : String(err));
22538
+ process.exit(1);
22217
22539
  }
22218
- if (result.conversations.length === 0) {
22219
- console.log("No conversations found.");
22220
- return;
22540
+ if (start && end && Date.parse(start) > Date.parse(end)) {
22541
+ console.error("--from must be before --to.");
22542
+ process.exit(1);
22221
22543
  }
22222
- console.log(`Conversations (${result.conversations.length} of ${result.total}):
22223
- `);
22224
- for (const conv of result.conversations) {
22225
- const user = conv.user_name || conv.user_email || "unknown";
22226
- const updated = conv.updated_at ? new Date(conv.updated_at).toLocaleString() : "-";
22227
- const kanban = truncate(
22228
- conv.outcome ? `${conv.kanban_status ?? "-"}/${conv.outcome}` : conv.kanban_status ?? "-",
22229
- KANBAN_COL_WIDTH
22230
- );
22231
- console.log(` ${conv.conversation_id} ${conv.conversation_status.padEnd(7)} ${kanban.padEnd(KANBAN_COL_WIDTH)} ${user.padEnd(20)} ${updated}`);
22544
+ return { start, end };
22545
+ }
22546
+ if (period) {
22547
+ try {
22548
+ const parsed = parsePeriod(period);
22549
+ return { start: parsed.start, end: parsed.end };
22550
+ } catch (err) {
22551
+ console.error(err instanceof Error ? err.message : String(err));
22552
+ process.exit(1);
22232
22553
  }
22233
22554
  }
22555
+ return {};
22234
22556
  }
22235
- var KANBAN_COL_WIDTH;
22557
+ function canonicalIso(value) {
22558
+ return toIso(fromIso(value));
22559
+ }
22560
+ var KANBAN_COL_WIDTH, HUB_COL_WIDTH;
22236
22561
  var init_conversations = __esm({
22237
22562
  "src/commands/conversations.ts"() {
22238
22563
  "use strict";
@@ -22242,8 +22567,12 @@ var init_conversations = __esm({
22242
22567
  init_api_client();
22243
22568
  init_utils();
22244
22569
  init_eval_format();
22570
+ init_terminal_output();
22571
+ init_cross_hub_conversations();
22245
22572
  init_observability();
22573
+ init_dist();
22246
22574
  KANBAN_COL_WIDTH = 30;
22575
+ HUB_COL_WIDTH = 24;
22247
22576
  }
22248
22577
  });
22249
22578
 
@@ -24519,7 +24848,7 @@ function resolveTagIds(tagRefs, orgTags) {
24519
24848
  }
24520
24849
  if (missing.length > 0) {
24521
24850
  const available = orgTags.map((t) => t.name).join(", ") || "(none)";
24522
- throw new Error(
24851
+ throw expected(
24523
24852
  `Unknown tag(s): ${missing.join(", ")}. Available org tags: ${available}`
24524
24853
  );
24525
24854
  }
@@ -24536,6 +24865,7 @@ var VALID_AUTH_TYPES4, LEGACY_AUTH_TYPE_MAP4, VALID_ENVIRONMENTS;
24536
24865
  var init_credential_utils = __esm({
24537
24866
  "src/lib/credential-utils.ts"() {
24538
24867
  "use strict";
24868
+ init_expected();
24539
24869
  VALID_AUTH_TYPES4 = ["api_key", "bearer", "basic_auth"];
24540
24870
  LEGACY_AUTH_TYPE_MAP4 = {
24541
24871
  "API Key": "api_key",