skydive-cli 0.2.0-beta.455 → 0.2.0-beta.482

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as HttpError } from "./rest-BBQrPIwq.mjs";
2
+ import { t as HttpError } from "./rest-DZnAT7BG.mjs";
3
3
  import os from "node:os";
4
4
  import { z } from "zod";
5
5
 
package/dist/js/bin.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { A as getShareMachineDefault, B as saveSession, C as DEFAULT_WEB_URL, D as getPromptHistoryPath, E as getLastSeenVersion, F as resolveConfig, H as setLastSeenVersion, I as resolveManagementAuth, L as resolveSession, M as getStoredApiKeyWorkspaceName, N as getUpdateCheckDisabled, P as resolveAppUrl, T as getConfigPath, _ as setActiveWorkspace, b as API_KEY_PREFIX, d as themes, g as listWorkspaces, h as getSessionIdentity, j as getStoredApiKeyId, m as getActiveWorkspaceId, p as ensureActiveOrganization, v as API_KEYS_URL, w as deleteConfig, x as DEFAULT_API_URL, y as API_KEY_FAMILY_PREFIX, z as saveConfig } from "./theme-CuQhvqzN.mjs";
2
+ import { A as getPromptHistoryPath, B as resolveSession, C as API_KEY_PREFIX, D as deleteConfig, E as DEFAULT_WEB_URL, F as getStoredApiKeyWorkspaceName, G as setLastSeenVersion, H as saveConfig, I as getUpdateCheckDisabled, L as resolveAppUrl, N as getShareMachineDefault, O as getConfigPath, P as getStoredApiKeyId, R as resolveConfig, S as API_KEY_FAMILY_PREFIX, U as saveSession, _ as getActiveWorkspaceId, b as setActiveWorkspace, g as ensureActiveOrganization, k as getLastSeenVersion, m as themes, r as brandHelpArt, v as getSessionIdentity, w as DEFAULT_API_URL, x as API_KEYS_URL, y as listWorkspaces, z as resolveManagementAuth } from "./brand-BVALAEf1.mjs";
3
3
  import { n as printError, r as printTable, t as output } from "./output-B4cW10Ph.mjs";
4
- import { n as createRestClient } from "./rest-BBQrPIwq.mjs";
5
- import { i as resolveAgent } from "./print-YqNIRwdf.mjs";
6
- import { a as registerPortalDevice, c as machineIdentity, n as findThisDevice, o as revokePortalAccess, r as grantPortalAccess, t as fetchPortalDevices } from "./api-zUf_Vrzh.mjs";
4
+ import { n as createRestClient } from "./rest-DZnAT7BG.mjs";
5
+ import { i as resolveAgent } from "./print-XvAHDKqu.mjs";
6
+ import { a as registerPortalDevice, c as machineIdentity, n as findThisDevice, o as revokePortalAccess, r as grantPortalAccess, t as fetchPortalDevices } from "./api-BHq-5eW_.mjs";
7
7
  import { t as SandboxStream } from "./client-CpEvH2Pq.mjs";
8
8
  import { hideBin } from "yargs/helpers";
9
9
  import yargs from "yargs";
@@ -20,7 +20,7 @@ import semver from "semver";
20
20
 
21
21
  //#region package.json
22
22
  var name = "skydive-cli";
23
- var version = "0.2.0-beta.455";
23
+ var version = "0.2.0-beta.482";
24
24
 
25
25
  //#endregion
26
26
  //#region src/types.ts
@@ -1200,7 +1200,7 @@ const importCommand = {
1200
1200
  process.exit(1);
1201
1201
  }
1202
1202
  }
1203
- const { runChat } = await import("./boot-SxcKHfwY.mjs");
1203
+ const { runChat } = await import("./boot-B8ownO3n.mjs");
1204
1204
  await runChat({
1205
1205
  appUrl,
1206
1206
  sessionToken: session.value.sessionToken,
@@ -1226,7 +1226,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1226
1226
  printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
1227
1227
  process.exit(1);
1228
1228
  }
1229
- const { connectMachineShare } = await import("./print-share-B8sZ0w-M.mjs");
1229
+ const { connectMachineShare } = await import("./print-share-DT89ADa6.mjs");
1230
1230
  const machineShare = await connectMachineShare({
1231
1231
  appUrl,
1232
1232
  sessionToken: session.value.sessionToken,
@@ -1234,7 +1234,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1234
1234
  });
1235
1235
  const extra = (argv.print ?? "").trim();
1236
1236
  const prompt = buildImportSeedPrompt(process.cwd()) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
1237
- const { runPrint } = await import("./print-DUsH9Dlh.mjs");
1237
+ const { runPrint } = await import("./print-Chg635Ye.mjs");
1238
1238
  try {
1239
1239
  const result = await runPrint({
1240
1240
  appUrl,
@@ -1511,7 +1511,7 @@ const chatCommand = {
1511
1511
  printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
1512
1512
  process.exit(1);
1513
1513
  }
1514
- const { runChat } = await import("./boot-SxcKHfwY.mjs");
1514
+ const { runChat } = await import("./boot-B8ownO3n.mjs");
1515
1515
  await runChat({
1516
1516
  appUrl,
1517
1517
  sessionToken: session.value.sessionToken,
@@ -1539,7 +1539,7 @@ async function runPrintMode({ argv, appUrl }) {
1539
1539
  printError(`${session.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_SESSION_TOKEN.`);
1540
1540
  process.exit(1);
1541
1541
  }
1542
- const { runPrint, readStdin } = await import("./print-DUsH9Dlh.mjs");
1542
+ const { runPrint, readStdin } = await import("./print-Chg635Ye.mjs");
1543
1543
  let prompt = (argv.print ?? "").trim();
1544
1544
  if (!prompt) {
1545
1545
  if (process.stdin.isTTY) {
@@ -1554,7 +1554,7 @@ async function runPrintMode({ argv, appUrl }) {
1554
1554
  }
1555
1555
  let machineShare = null;
1556
1556
  if (resolveShareMachine(argv)) {
1557
- const { connectMachineShare } = await import("./print-share-B8sZ0w-M.mjs");
1557
+ const { connectMachineShare } = await import("./print-share-DT89ADa6.mjs");
1558
1558
  machineShare = await connectMachineShare({
1559
1559
  appUrl,
1560
1560
  sessionToken: session.value.sessionToken,
@@ -1613,7 +1613,7 @@ const getCommand = {
1613
1613
  printError(`${session.error.message} Run \`skydive auth login\` first, or set SKYDIVE_SESSION_TOKEN.`);
1614
1614
  process.exit(1);
1615
1615
  }
1616
- const { messageGet } = await import("./print-DUsH9Dlh.mjs");
1616
+ const { messageGet } = await import("./print-Chg635Ye.mjs");
1617
1617
  try {
1618
1618
  const result = await messageGet({
1619
1619
  appUrl,
@@ -1810,7 +1810,7 @@ const switchCommand = {
1810
1810
  printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
1811
1811
  process.exit(1);
1812
1812
  }
1813
- const { runWorkspacePicker } = await import("./boot-SxcKHfwY.mjs");
1813
+ const { runWorkspacePicker } = await import("./boot-B8ownO3n.mjs");
1814
1814
  await runWorkspacePicker(session);
1815
1815
  return;
1816
1816
  }
@@ -1888,7 +1888,7 @@ const openCommand = {
1888
1888
  const agent = argv.agent ? resolveAgent((await fetchPortalDevices(session)).agents, argv.agent) : null;
1889
1889
  const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
1890
1890
  const { machineName } = machineIdentity();
1891
- const { PortalClient } = await import("./client-A4m7lNDg.mjs");
1891
+ const { PortalClient } = await import("./client-CRAGDqp0.mjs");
1892
1892
  let lastLine = "";
1893
1893
  let signalConnected;
1894
1894
  const connected = new Promise((resolve) => {
@@ -2060,8 +2060,8 @@ const sandboxCommand = {
2060
2060
  }).example("skydive sandbox --agent grace", "Live terminal (Ctrl-] detaches)").example("skydive sandbox --agent grace -- tail -n 50 /tmp/harness.log", "One-shot command (use `--` so its flags reach the sandbox)").example("skydive sandbox --agent grace -- sh -c 'ls /tmp | wc -l'", "Shell features go through an explicit `sh -c`"),
2061
2061
  handler: async (argv) => {
2062
2062
  const session = requireSession(argv);
2063
- const { createRestClient } = await import("./rest-Cz0VLhmi.mjs");
2064
- const { resolveAgent } = await import("./print-DUsH9Dlh.mjs");
2063
+ const { createRestClient } = await import("./rest-Ci_yRwoi.mjs");
2064
+ const { resolveAgent } = await import("./print-Chg635Ye.mjs");
2065
2065
  const client = createRestClient({
2066
2066
  appUrl: session.appUrl,
2067
2067
  sessionToken: session.sessionToken
@@ -2608,7 +2608,10 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
2608
2608
  }
2609
2609
  maybePrintWhatsNew();
2610
2610
  setupUpdateCheck(hideBin(process.argv));
2611
- createCli(resolveArgv(hideBin(process.argv))).parse();
2611
+ const resolved = resolveArgv(hideBin(process.argv));
2612
+ const raw = hideBin(process.argv);
2613
+ if ((raw.includes("--help") || raw.includes("-h")) && !raw.some((a) => !a.startsWith("-")) && !raw.includes("--json") && !raw.includes("--quiet")) process.stdout.write(brandHelpArt());
2614
+ createCli(resolved).parse();
2612
2615
 
2613
2616
  //#endregion
2614
2617
  export { };
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { O as getReviewStateDir, R as resolveWebUrl, S as DEFAULT_APP_URL, T as getConfigPath, V as saveTheme, _ as setActiveWorkspace, a as noColorRequested, c as themeMode, f as themesForMode, g as listWorkspaces, i as monoTheme, k as getSavedTheme, l as themeModeFromColorFgBg, m as getActiveWorkspaceId, n as applyTheme, o as theme, r as findTheme, s as themeForMode, t as DEFAULT_THEME_ID, u as themeVersion, x as DEFAULT_API_URL } from "./theme-CuQhvqzN.mjs";
3
- import { n as createRestClient, r as errorDetail, t as HttpError } from "./rest-BBQrPIwq.mjs";
2
+ import { M as getSavedTheme, O as getConfigPath, T as DEFAULT_APP_URL, V as resolveWebUrl, W as saveTheme, _ as getActiveWorkspaceId, a as applyTheme, b as setActiveWorkspace, c as noColorRequested, d as themeMode, f as themeModeFromColorFgBg, h as themesForMode, i as DEFAULT_THEME_ID, j as getReviewStateDir, l as theme, n as WORDMARK, o as findTheme, p as themeVersion, s as monoTheme, t as MARK_CELLS, u as themeForMode, w as DEFAULT_API_URL, y as listWorkspaces } from "./brand-BVALAEf1.mjs";
3
+ import { n as createRestClient, r as errorDetail, t as HttpError } from "./rest-DZnAT7BG.mjs";
4
4
  import { n as isRecord, t as errorMessage } from "./util-CeisaZVY.mjs";
5
- import { c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams } from "./print-YqNIRwdf.mjs";
6
- import "./api-zUf_Vrzh.mjs";
5
+ import { c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, m as specKeyFor, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams } from "./print-XvAHDKqu.mjs";
6
+ import "./api-BHq-5eW_.mjs";
7
7
  import { t as SandboxStream } from "./client-CpEvH2Pq.mjs";
8
- import { t as PortalClient } from "./client-DuPxkSoH.mjs";
8
+ import { t as PortalClient } from "./client-t6GmE8TI.mjs";
9
9
  import { t as runRawPtyPassthrough } from "./raw-pty-GAvxm2ol.mjs";
10
10
  import * as os$1 from "node:os";
11
11
  import { homedir, platform, release, tmpdir } from "node:os";
@@ -183,8 +183,8 @@ async function resolveInitialScreen({ rest, agentSelector, conversationId, newCo
183
183
  createdAt: conversation.createdAt,
184
184
  updatedAt: conversation.updatedAt,
185
185
  preview: null,
186
- channel: null,
187
- channelLabel: null,
186
+ channel: conversation.channel,
187
+ channelLabel: conversation.channelLabel,
188
188
  agent: {
189
189
  id: agent.id,
190
190
  name: agent.name,
@@ -931,6 +931,76 @@ function FuzzyText({ text, indexes, fg }) {
931
931
  }, i)) });
932
932
  }
933
933
 
934
+ //#endregion
935
+ //#region src/chat/tui/skydive-mark.tsx
936
+ /**
937
+ * The Skydive brand splash for the interactive TUI, rendered as OpenTUI React
938
+ * nodes (not raw ANSI). Two variants:
939
+ *
940
+ * - `variant="full"` — the six-petal mark beside the SKYDIVE wordmark.
941
+ * - `variant="mark"` — just the pinwheel mark (compact picker headers).
942
+ *
943
+ * The mark paints fixed brand hexes (a logo is a logo). The wordmark follows
944
+ * the current theme's body text (`theme.fg`) so it reads white-ish on dark
945
+ * themes and dark on light ones. Under NO_COLOR the theme palette is all
946
+ * `undefined`; we detect that and render uncolored so it degrades with the
947
+ * rest of the UI.
948
+ */
949
+ function noColor() {
950
+ return theme.fg === void 0;
951
+ }
952
+ function MarkRows({ mono }) {
953
+ return /* @__PURE__ */ jsx("box", {
954
+ style: { flexDirection: "column" },
955
+ children: MARK_CELLS.map((row, r) => /* @__PURE__ */ jsx("text", { children: row.map((cell, c) => cell === " " ? /* @__PURE__ */ jsx("span", { children: " " }, c) : /* @__PURE__ */ jsx("span", {
956
+ fg: mono ? void 0 : cell[1],
957
+ children: cell[0]
958
+ }, c)) }, r))
959
+ });
960
+ }
961
+ function WordmarkRows({ mono }) {
962
+ const color = mono ? void 0 : theme.fg;
963
+ return /* @__PURE__ */ jsx("box", {
964
+ style: { flexDirection: "column" },
965
+ children: WORDMARK.map((line, r) => /* @__PURE__ */ jsx("text", { children: /* @__PURE__ */ jsx("span", {
966
+ fg: color,
967
+ children: line
968
+ }) }, r))
969
+ });
970
+ }
971
+ /**
972
+ * Height-aware brand header for the pickers. Always shown (including while the
973
+ * list is loading / empty / erroring) so the splash is never gated on data.
974
+ * Full mark + wordmark when the terminal is tall enough; compact one-liner on
975
+ * short terminals so the list still has room.
976
+ */
977
+ function BrandHeader({ height }) {
978
+ if (height >= 24) return /* @__PURE__ */ jsxs("box", {
979
+ style: { flexDirection: "column" },
980
+ children: [/* @__PURE__ */ jsx(SkydiveMark, { variant: "full" }), /* @__PURE__ */ jsx("box", { style: { height: 1 } })]
981
+ });
982
+ return /* @__PURE__ */ jsx("text", {
983
+ fg: theme.fg,
984
+ children: "✦ Skydive"
985
+ });
986
+ }
987
+ /** Row budget the BrandHeader consumes; callers subtract this from visibleRows. */
988
+ function brandHeaderRows(height) {
989
+ return height >= 24 ? 7 : 1;
990
+ }
991
+ function SkydiveMark({ variant = "full" }) {
992
+ const mono = noColor();
993
+ if (variant === "mark") return /* @__PURE__ */ jsx(MarkRows, { mono });
994
+ return /* @__PURE__ */ jsxs("box", {
995
+ style: { flexDirection: "row" },
996
+ children: [
997
+ /* @__PURE__ */ jsx(MarkRows, { mono }),
998
+ /* @__PURE__ */ jsx("box", { style: { width: 3 } }),
999
+ /* @__PURE__ */ jsx(WordmarkRows, { mono })
1000
+ ]
1001
+ });
1002
+ }
1003
+
934
1004
  //#endregion
935
1005
  //#region src/chat/tui/screens/agent-picker.tsx
936
1006
  function AgentPickerScreen() {
@@ -998,17 +1068,36 @@ function AgentPickerScreen() {
998
1068
  scope,
999
1069
  cache
1000
1070
  ]);
1001
- if (state.kind === "loading") return /* @__PURE__ */ jsx("text", {
1002
- fg: theme.muted,
1003
- children: "loading agents…"
1071
+ const { height } = useTerminalDimensions();
1072
+ if (state.kind === "loading") return /* @__PURE__ */ jsxs("box", {
1073
+ style: {
1074
+ flexDirection: "column",
1075
+ flexGrow: 1
1076
+ },
1077
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsx("text", {
1078
+ fg: theme.muted,
1079
+ children: "loading agents…"
1080
+ })]
1004
1081
  });
1005
- if (state.kind === "error") return /* @__PURE__ */ jsxs("text", {
1006
- fg: theme.error,
1007
- children: ["error: ", state.message]
1082
+ if (state.kind === "error") return /* @__PURE__ */ jsxs("box", {
1083
+ style: {
1084
+ flexDirection: "column",
1085
+ flexGrow: 1
1086
+ },
1087
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsxs("text", {
1088
+ fg: theme.error,
1089
+ children: ["error: ", state.message]
1090
+ })]
1008
1091
  });
1009
- if (state.agents.length === 0) return /* @__PURE__ */ jsx("text", {
1010
- fg: theme.muted,
1011
- children: scope === "mine" ? "no agents of yours yet. press tab to see all agents, ctrl+w to switch workspace, or ctrl+n to create one." : "no agents in this workspace. press ctrl+w to switch workspace, or ctrl+n to create one."
1092
+ if (state.agents.length === 0) return /* @__PURE__ */ jsxs("box", {
1093
+ style: {
1094
+ flexDirection: "column",
1095
+ flexGrow: 1
1096
+ },
1097
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsx("text", {
1098
+ fg: theme.muted,
1099
+ children: scope === "mine" ? "no agents of yours yet. press tab to see all agents, ctrl+w to switch workspace, or ctrl+n to create one." : "no agents in this workspace. press ctrl+w to switch workspace, or ctrl+n to create one."
1100
+ })]
1012
1101
  });
1013
1102
  return /* @__PURE__ */ jsx(FilterableAgentList, {
1014
1103
  agents: state.agents,
@@ -1026,7 +1115,7 @@ function FilterableAgentList({ agents, scope, onPick }) {
1026
1115
  const hits = fuzzyFilter(query, agents, agentKeys);
1027
1116
  const filtered = hits.map((h) => h.item);
1028
1117
  const clamped = Math.min(highlight, Math.max(0, filtered.length - 1));
1029
- const visibleRows = Math.max(3, height - 6);
1118
+ const visibleRows = Math.max(3, height - 6 - brandHeaderRows(height));
1030
1119
  const start = windowStart(clamped, filtered.length, visibleRows);
1031
1120
  const windowed = hits.slice(start, start + visibleRows);
1032
1121
  useKeyboard((key) => {
@@ -1055,6 +1144,7 @@ function FilterableAgentList({ agents, scope, onPick }) {
1055
1144
  flexGrow: 1
1056
1145
  },
1057
1146
  children: [
1147
+ /* @__PURE__ */ jsx(BrandHeader, { height }),
1058
1148
  /* @__PURE__ */ jsxs("box", {
1059
1149
  style: {
1060
1150
  flexDirection: "row",
@@ -1515,27 +1605,45 @@ function singleLine(text) {
1515
1605
  return out;
1516
1606
  }
1517
1607
  /**
1518
- * Fit a row's title and trailing detail into `width` columns. The title
1519
- * identifies the row so it gets the space first; the detail takes what is
1520
- * left and is dropped when nothing is.
1608
+ * Fit a row's title, an optional mid-row `tag` and a trailing detail into
1609
+ * `width` columns, each rendered separated by one space. The title identifies
1610
+ * the row so it gets the space first; the tag comes next because it carries
1611
+ * information the detail does not; the detail takes what is left and is
1612
+ * dropped when nothing is.
1521
1613
  */
1522
- function fitRowText(title, detail, width) {
1614
+ function fitRowText(title, detail, width, tag = "") {
1523
1615
  if (width <= 0) return {
1524
1616
  title: "",
1525
- detail: ""
1617
+ detail: "",
1618
+ tag: ""
1526
1619
  };
1527
- if ((detail ? title.length + 1 + detail.length : title.length) <= width) return {
1620
+ const segment = (text) => text ? text.length + 1 : 0;
1621
+ if (title.length + segment(tag) + segment(detail) <= width) return {
1528
1622
  title,
1529
- detail
1623
+ detail,
1624
+ tag
1530
1625
  };
1531
1626
  const room = width - title.length - 1;
1532
1627
  if (room < 2) return {
1533
1628
  title: truncate(title, width),
1534
- detail: ""
1629
+ detail: "",
1630
+ tag: ""
1631
+ };
1632
+ if (title.length + segment(tag) > width) return {
1633
+ title,
1634
+ detail: "",
1635
+ tag: truncate(tag, room)
1636
+ };
1637
+ const detailRoom = room - segment(tag);
1638
+ if (detailRoom < 2) return {
1639
+ title,
1640
+ detail: "",
1641
+ tag
1535
1642
  };
1536
1643
  return {
1537
1644
  title,
1538
- detail: truncate(detail, room)
1645
+ detail: truncate(detail, detailRoom),
1646
+ tag
1539
1647
  };
1540
1648
  }
1541
1649
  /**
@@ -1624,13 +1732,26 @@ function ConversationPickerScreen({ agent }) {
1624
1732
  cacheKey,
1625
1733
  cache
1626
1734
  ]);
1627
- if (state.kind === "loading") return /* @__PURE__ */ jsx("text", {
1628
- fg: theme.muted,
1629
- children: "loading conversations…"
1735
+ const { height } = useTerminalDimensions();
1736
+ if (state.kind === "loading") return /* @__PURE__ */ jsxs("box", {
1737
+ style: {
1738
+ flexDirection: "column",
1739
+ flexGrow: 1
1740
+ },
1741
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsx("text", {
1742
+ fg: theme.muted,
1743
+ children: "loading conversations…"
1744
+ })]
1630
1745
  });
1631
- if (state.kind === "error") return /* @__PURE__ */ jsxs("text", {
1632
- fg: theme.error,
1633
- children: ["error: ", state.message]
1746
+ if (state.kind === "error") return /* @__PURE__ */ jsxs("box", {
1747
+ style: {
1748
+ flexDirection: "column",
1749
+ flexGrow: 1
1750
+ },
1751
+ children: [/* @__PURE__ */ jsx(BrandHeader, { height }), /* @__PURE__ */ jsxs("text", {
1752
+ fg: theme.error,
1753
+ children: ["error: ", state.message]
1754
+ })]
1634
1755
  });
1635
1756
  return /* @__PURE__ */ jsx(ConversationList, {
1636
1757
  agent,
@@ -1687,7 +1808,7 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
1687
1808
  "↵ open"
1688
1809
  ], innerWidth)
1689
1810
  };
1690
- const visibleRows = Math.max(3, height - 6);
1811
+ const visibleRows = Math.max(3, height - 6 - brandHeaderRows(height));
1691
1812
  const start = windowStart(clamped, rows.length, visibleRows);
1692
1813
  const windowed = rows.slice(start, start + visibleRows);
1693
1814
  const open = (row) => {
@@ -1786,6 +1907,7 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
1786
1907
  flexGrow: 1
1787
1908
  },
1788
1909
  children: [
1910
+ /* @__PURE__ */ jsx(BrandHeader, { height }),
1789
1911
  /* @__PURE__ */ jsxs("box", {
1790
1912
  style: {
1791
1913
  flexDirection: "row",
@@ -1822,7 +1944,7 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
1822
1944
  children: [marker, truncate("+ new conversation", rowWidth)]
1823
1945
  }, "new");
1824
1946
  const archived = isArchived(row.hit.item);
1825
- const { title, detail } = fitRowText(singleLine(row.hit.item.title ?? "(untitled)"), `${archived ? "archived · " : ""}${previewLine(row.hit.item)}`, rowWidth);
1947
+ const { title, detail, tag } = fitRowText(singleLine(row.hit.item.title ?? "(untitled)"), `${archived ? "archived · " : ""}${previewLine(row.hit.item)}`, rowWidth, coAgentsTag(row.hit.item, agent.id));
1826
1948
  return /* @__PURE__ */ jsxs("text", {
1827
1949
  fg: archived && !selected ? theme.dim : fg,
1828
1950
  children: [
@@ -1831,6 +1953,10 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
1831
1953
  text: title,
1832
1954
  indexes: row.hit.item.title ? row.hit.highlights[0] : null
1833
1955
  }),
1956
+ tag ? /* @__PURE__ */ jsxs("span", {
1957
+ fg: theme.muted,
1958
+ children: [" ", tag]
1959
+ }) : null,
1834
1960
  detail ? /* @__PURE__ */ jsxs("span", {
1835
1961
  fg: theme.dim,
1836
1962
  children: [" ", detail]
@@ -1842,7 +1968,27 @@ function ConversationList({ agent, conversations, complete, showAutomated, onTog
1842
1968
  ]
1843
1969
  });
1844
1970
  }
1845
- const conversationKeys = [(c) => singleLine(c.title ?? ""), (c) => c.preview ?? ""];
1971
+ /**
1972
+ * Tags a row with the thread's other agents (`+Grace, Ada`). The picker is
1973
+ * entered per agent, so that agent stays implicit and only its co-participants
1974
+ * are named — this tag is the only thing in the list distinguishing a
1975
+ * multi-agent thread from a solo one. Empty for a solo thread.
1976
+ *
1977
+ * A string rather than a component because the row's one-line budget has to
1978
+ * account for it (see list-line.ts).
1979
+ */
1980
+ function coAgentsTag(conv, viewerAgentId) {
1981
+ const names = conversationAgents(conv).filter((a) => a.id !== viewerAgentId).map((a) => a.name);
1982
+ return names.length > 0 ? `+${names.join(", ")}` : "";
1983
+ }
1984
+ function conversationAgents(c) {
1985
+ return c.agents && c.agents.length > 0 ? c.agents : [c.agent];
1986
+ }
1987
+ const conversationKeys = [
1988
+ (c) => singleLine(c.title ?? ""),
1989
+ (c) => c.preview ?? "",
1990
+ (c) => conversationAgents(c).map((a) => a.name).join(" ")
1991
+ ];
1846
1992
  function previewLine(c) {
1847
1993
  const when = relativeTime(c.updatedAt);
1848
1994
  const prefix = c.preview ? c.preview.replace(/\s+/g, " ").trim() : "";
@@ -3949,10 +4095,13 @@ function TodoCardView({ todos, isActive }) {
3949
4095
  //#endregion
3950
4096
  //#region src/chat/tui/chat/footer-hints.ts
3951
4097
  /** Build the chat footer copy independently of its colors and layout. */
3952
- function formatChatFooterStatus({ runKind, ctrlCArmed, isShared, hasPendingSteer, hasActionableCard }) {
4098
+ function formatChatFooterStatus({ runKind, ctrlCArmed, isShared, hasPendingSteer, hasActionableCard, nonWebChannel }) {
3953
4099
  const connectHint = hasActionableCard ? "ctrl+r connect · " : "";
3954
4100
  switch (runKind) {
3955
- case "idle": return ctrlCArmed ? "press ctrl+c again to quit" : `${connectHint}↵ send · / commands · esc back · ctrl+c quit · ctrl+t ${isShared ? "revoke access" : "local access"} · ? keybinds`;
4101
+ case "idle":
4102
+ if (ctrlCArmed) return "press ctrl+c again to quit";
4103
+ if (nonWebChannel) return `esc back · ctrl+c quit · ctrl+t ${isShared ? "revoke access" : "local access"} · ? keybinds`;
4104
+ return `${connectHint}↵ send · / commands · esc back · ctrl+c quit · ctrl+t ${isShared ? "revoke access" : "local access"} · ? keybinds`;
3956
4105
  case "sending": return "sending…";
3957
4106
  case "streaming": return hasPendingSteer ? `${connectHint}↵ steer · esc cancel steer · ctrl+c cancel run` : `${connectHint}↵ steer · esc leave (keeps running) · ctrl+c cancel`;
3958
4107
  }
@@ -4288,6 +4437,10 @@ const keybindGroups = [
4288
4437
  keys: "ctrl+l",
4289
4438
  action: "open this conversation in the browser"
4290
4439
  },
4440
+ {
4441
+ keys: "ctrl+f",
4442
+ action: "fork a channel conversation into a chat you can continue here"
4443
+ },
4291
4444
  {
4292
4445
  keys: "ctrl+r",
4293
4446
  action: "run the newest connect card (auth cards)"
@@ -7797,6 +7950,47 @@ async function runPtySession(opts) {
7797
7950
  }
7798
7951
  }
7799
7952
 
7953
+ //#endregion
7954
+ //#region src/chat/tui/screens/conversation-channel.ts
7955
+ function isNewConversation(c) {
7956
+ return "kind" in c && c.kind === "new";
7957
+ }
7958
+ /**
7959
+ * The channel a conversation belongs to when it isn't the web channel, else
7960
+ * null. A brand-new conversation is always web, and the list route reports the
7961
+ * web channel as either `'web'` or (legacy) null — both mean "post from here".
7962
+ * Anything else (slack, email, imessage, webhook, cron) is owned by that
7963
+ * channel: the message has to go through it, so the TUI can't send here.
7964
+ * Mirrors the web app's `channel !== null && channel !== 'web'` gate.
7965
+ */
7966
+ function nonWebChannelLabel(c) {
7967
+ if (isNewConversation(c)) return null;
7968
+ if (c.channel === null || c.channel === "web") return null;
7969
+ return c.channelLabel ?? c.channel;
7970
+ }
7971
+ /**
7972
+ * The list-row shape for a conversation the TUI just forked, so the chat
7973
+ * screen can reopen on the copy without waiting for a list refetch. A fork
7974
+ * carries none of the source's channel binding — that's what makes it
7975
+ * sendable from here — so it is always a web conversation.
7976
+ */
7977
+ function forkedConversationRef({ agent, forked }) {
7978
+ const now = (/* @__PURE__ */ new Date()).toISOString();
7979
+ return {
7980
+ id: forked.id,
7981
+ title: forked.title,
7982
+ createdAt: now,
7983
+ updatedAt: now,
7984
+ preview: null,
7985
+ channel: "web",
7986
+ channelLabel: null,
7987
+ agent: {
7988
+ id: agent.id,
7989
+ name: agent.name
7990
+ }
7991
+ };
7992
+ }
7993
+
7800
7994
  //#endregion
7801
7995
  //#region src/chat/tui/screens/chat.tsx
7802
7996
  /**
@@ -7833,9 +8027,6 @@ const pageScrollFraction = .75;
7833
8027
  /** Cap composer growth; past this the textarea scrolls its content instead. */
7834
8028
  const maxComposerRows = 8;
7835
8029
  const maxAttachments = 10;
7836
- function isNewConversation(c) {
7837
- return "kind" in c && c.kind === "new";
7838
- }
7839
8030
  /**
7840
8031
  * A server timestamp as local epoch ms, or null when it is missing or
7841
8032
  * unparseable so the caller can fall back to its own clock.
@@ -7888,6 +8079,7 @@ function ChatScreen({ agent, conversation }) {
7888
8079
  const isShared = portal.status !== "off";
7889
8080
  const initialConversationId = isNewConversation(conversation) ? null : conversation.id;
7890
8081
  const agentHost = useAgentHost();
8082
+ const nonWebChannel = nonWebChannelLabel(conversation);
7891
8083
  const [conversationId, setConversationId] = useState(initialConversationId);
7892
8084
  const [items, setItems] = useState([]);
7893
8085
  const [todos, setTodos] = useState(null);
@@ -7920,6 +8112,8 @@ function ChatScreen({ agent, conversation }) {
7920
8112
  const [composerRows, setComposerRows] = useState(1);
7921
8113
  const [attachments, setAttachments] = useState([]);
7922
8114
  const uploadsRef = useRef(/* @__PURE__ */ new Map());
8115
+ const [forking, setForking] = useState(false);
8116
+ const forkingRef = useRef(false);
7923
8117
  const [credPrompt, setCredPrompt] = useState(null);
7924
8118
  const credPromptOpen = credPrompt !== null;
7925
8119
  const [menuDismissed, setMenuDismissed] = useState(false);
@@ -7958,10 +8152,11 @@ function ChatScreen({ agent, conversation }) {
7958
8152
  const reviewBelowBoxRef = useRef(null);
7959
8153
  const paneDragRef = useRef(null);
7960
8154
  const composerBoxHeight = composerRows + 2;
7961
- const pendingVisible = !grantPrompt && !credPrompt && attachments.length > 0;
8155
+ const pendingVisible = !grantPrompt && !credPrompt && !nonWebChannel && attachments.length > 0;
7962
8156
  const pendingRows = pendingVisible ? attachments.length + 2 : 0;
7963
8157
  const credPromptHeight = 4 + (credPrompt && (credPrompt.error || credPrompt.submitting) ? 1 : 0);
7964
- const bottomBoxHeight = credPrompt ? credPromptHeight : composerBoxHeight;
8158
+ const nonWebNoticeHeight = 5;
8159
+ const bottomBoxHeight = credPrompt ? credPromptHeight : nonWebChannel ? nonWebNoticeHeight : composerBoxHeight;
7965
8160
  const bodyWidth = Math.max(1, width - 2);
7966
8161
  const bodyHeight = Math.max(1, height - 3);
7967
8162
  const reviewPlacement = filePanePlacement({
@@ -8608,7 +8803,7 @@ function ChatScreen({ agent, conversation }) {
8608
8803
  stageAttachment(image);
8609
8804
  }, [stageAttachment]);
8610
8805
  usePaste((event) => {
8611
- if (modelPickerOpen || themePickerOpen || helpOpen || credPromptOpen || grantPrompt) return;
8806
+ if (modelPickerOpen || themePickerOpen || helpOpen || credPromptOpen || grantPrompt || nonWebChannel) return;
8612
8807
  const text = event.metadata?.kind === "binary" ? "" : decodePasteBytes(event.bytes);
8613
8808
  const route = routePaste({
8614
8809
  kind: event.metadata?.kind,
@@ -8694,6 +8889,39 @@ function ChatScreen({ agent, conversation }) {
8694
8889
  text: `couldn't open a browser — visit ${url} (${errorMessage(err)})`
8695
8890
  }]));
8696
8891
  }, [appUrl, conversationId]);
8892
+ const forkAndContinue = useCallback(() => {
8893
+ if (!rest || !conversationId || forkingRef.current) return;
8894
+ forkingRef.current = true;
8895
+ setForking(true);
8896
+ const stillHere = () => {
8897
+ const screen = useStore.getState().screen;
8898
+ return screen.kind === "chat" && !isNewConversation(screen.conversation) && screen.conversation.id === conversationId;
8899
+ };
8900
+ rest.forkConversation({ conversationId }).then((forked) => {
8901
+ if (!stillHere()) return;
8902
+ goTo({
8903
+ kind: "chat",
8904
+ agent,
8905
+ conversation: forkedConversationRef({
8906
+ agent,
8907
+ forked
8908
+ })
8909
+ });
8910
+ }).catch((err) => {
8911
+ forkingRef.current = false;
8912
+ setForking(false);
8913
+ if (!stillHere()) return;
8914
+ useStore.getState().showToast({
8915
+ variant: "error",
8916
+ message: `couldn't fork this conversation: ${errorMessage(err)}`
8917
+ });
8918
+ });
8919
+ }, [
8920
+ rest,
8921
+ conversationId,
8922
+ agent,
8923
+ goTo
8924
+ ]);
8697
8925
  const toggleShare = useCallback(() => {
8698
8926
  if (!portalClient) return;
8699
8927
  if (portal.status === "off") {
@@ -8989,11 +9217,17 @@ function ChatScreen({ agent, conversation }) {
8989
9217
  toggleShare();
8990
9218
  return;
8991
9219
  }
9220
+ if (nonWebChannel && key.name === "f" && key.ctrl) {
9221
+ forkAndContinue();
9222
+ return;
9223
+ }
8992
9224
  if (key.name === "v" && key.ctrl) {
9225
+ if (nonWebChannel) return;
8993
9226
  pasteImage();
8994
9227
  return;
8995
9228
  }
8996
9229
  if (key.name === "x" && key.ctrl || key.name === "backspace" && attachmentsRef.current.length > 0 && !composerRef.current?.plainText) {
9230
+ if (nonWebChannel) return;
8997
9231
  const last = attachmentsRef.current.at(-1);
8998
9232
  if (last) {
8999
9233
  uploadsRef.current.delete(last.tempId);
@@ -9058,13 +9292,15 @@ function ChatScreen({ agent, conversation }) {
9058
9292
  ctrlCArmed,
9059
9293
  isShared,
9060
9294
  hasPendingSteer,
9061
- hasActionableCard
9295
+ hasActionableCard,
9296
+ nonWebChannel: nonWebChannel !== null
9062
9297
  }), [
9063
9298
  run.kind,
9064
9299
  ctrlCArmed,
9065
9300
  isShared,
9066
9301
  hasPendingSteer,
9067
- hasActionableCard
9302
+ hasActionableCard,
9303
+ nonWebChannel
9068
9304
  ]);
9069
9305
  const modelHint = formatModelHint(model);
9070
9306
  if (modelPickerOpen) return /* @__PURE__ */ jsx(ModelPicker, {
@@ -9293,6 +9529,35 @@ function ChatScreen({ agent, conversation }) {
9293
9529
  children: "n"
9294
9530
  })
9295
9531
  ]
9532
+ }) : nonWebChannel ? /* @__PURE__ */ jsxs("box", {
9533
+ style: {
9534
+ border: true,
9535
+ borderColor: theme.faint,
9536
+ paddingLeft: 1,
9537
+ paddingRight: 1,
9538
+ height: nonWebNoticeHeight,
9539
+ flexShrink: 0,
9540
+ marginTop: 1,
9541
+ flexDirection: "column"
9542
+ },
9543
+ children: [
9544
+ /* @__PURE__ */ jsxs("text", {
9545
+ fg: theme.muted,
9546
+ children: [
9547
+ "this conversation is on ",
9548
+ nonWebChannel,
9549
+ "."
9550
+ ]
9551
+ }),
9552
+ /* @__PURE__ */ jsx("text", {
9553
+ fg: theme.dim,
9554
+ children: "reply there to continue it."
9555
+ }),
9556
+ /* @__PURE__ */ jsx("text", {
9557
+ fg: theme.dim,
9558
+ children: forking ? "forking into a new chat…" : "ctrl+f fork it into a chat you can continue here."
9559
+ })
9560
+ ]
9296
9561
  }) : /* @__PURE__ */ jsx("box", {
9297
9562
  style: {
9298
9563
  border: true,
@@ -987,4 +987,147 @@ function applyTheme(def) {
987
987
  }
988
988
 
989
989
  //#endregion
990
- export { getShareMachineDefault as A, saveSession as B, DEFAULT_WEB_URL as C, getPromptHistoryPath as D, getLastSeenVersion as E, resolveConfig as F, setLastSeenVersion as H, resolveManagementAuth as I, resolveSession as L, getStoredApiKeyWorkspaceName as M, getUpdateCheckDisabled as N, getReviewStateDir as O, resolveAppUrl as P, resolveWebUrl as R, DEFAULT_APP_URL as S, getConfigPath as T, saveTheme as V, setActiveWorkspace as _, noColorRequested as a, API_KEY_PREFIX as b, themeMode as c, themes as d, themesForMode as f, listWorkspaces as g, getSessionIdentity as h, monoTheme as i, getStoredApiKeyId as j, getSavedTheme as k, themeModeFromColorFgBg as l, getActiveWorkspaceId as m, applyTheme as n, theme as o, ensureActiveOrganization as p, findTheme as r, themeForMode as s, DEFAULT_THEME_ID as t, themeVersion as u, API_KEYS_URL as v, deleteConfig as w, DEFAULT_API_URL as x, API_KEY_FAMILY_PREFIX as y, saveConfig as z };
990
+ //#region src/brand.ts
991
+ const MARK_CELLS = [
992
+ [
993
+ " ",
994
+ " ",
995
+ ["▆", "#517a9d"],
996
+ ["█", "#6192bb"],
997
+ ["█", "#6191ba"],
998
+ ["▌", "#4d7494"],
999
+ ["▆", "#90572e"],
1000
+ ["█", "#e18948"],
1001
+ ["█", "#df8747"],
1002
+ ["▅", "#bd733c"],
1003
+ " ",
1004
+ " "
1005
+ ],
1006
+ [
1007
+ " ",
1008
+ ["▂", "#cb5556"],
1009
+ ["▝", "#6699c4"],
1010
+ ["█", "#446683"],
1011
+ ["█", "#527c9f"],
1012
+ ["▘", "#436582"],
1013
+ ["▝", "#ba713b"],
1014
+ ["█", "#be733c"],
1015
+ ["█", "#9c5e31"],
1016
+ ["▚", "#9d6345"],
1017
+ ["▂", "#c1859b"],
1018
+ " "
1019
+ ],
1020
+ [
1021
+ ["█", "#c05152"],
1022
+ ["█", "#e15f60"],
1023
+ ["█", "#d65a5b"],
1024
+ ["▅", "#a24445"],
1025
+ " ",
1026
+ " ",
1027
+ " ",
1028
+ " ",
1029
+ ["▅", "#b17a8e"],
1030
+ ["█", "#d895ad"],
1031
+ ["█", "#df9ab3"],
1032
+ ["▉", "#c3869c"]
1033
+ ],
1034
+ [
1035
+ ["▝", "#d25859"],
1036
+ ["█", "#953f3f"],
1037
+ ["▘", "#df5e5f"],
1038
+ ["▂", "#4d8d6f"],
1039
+ ["▃", "#539877"],
1040
+ " ",
1041
+ " ",
1042
+ ["▃", "#e5aa47"],
1043
+ ["▂", "#d29d41"],
1044
+ ["█", "#7f585e"],
1045
+ ["█", "#916475"],
1046
+ ["▘", "#c6899f"]
1047
+ ],
1048
+ [
1049
+ " ",
1050
+ " ",
1051
+ ["█", "#4e8f70"],
1052
+ ["█", "#569d7b"],
1053
+ ["█", "#569d7b"],
1054
+ ["▌", "#4a876a"],
1055
+ ["▆", "#8f6a2c"],
1056
+ ["█", "#efb24a"],
1057
+ ["█", "#efb24a"],
1058
+ ["▉", "#e2a845"],
1059
+ " ",
1060
+ " "
1061
+ ],
1062
+ [
1063
+ " ",
1064
+ " ",
1065
+ " ",
1066
+ ["▝", "#488467"],
1067
+ ["▘", "#488367"],
1068
+ " ",
1069
+ " ",
1070
+ ["▝", "#ca963e"],
1071
+ ["▘", "#c8953d"],
1072
+ " ",
1073
+ " ",
1074
+ " "
1075
+ ]
1076
+ ];
1077
+ const MARK_WIDTH = 12;
1078
+ /** Accent hex used for the compact one-liner star (matches the blue petal). */
1079
+ const BRAND_ACCENT = "#5b8fc7";
1080
+ /** "Skydive" in an ANSI-shadow block font. */
1081
+ const WORDMARK = [
1082
+ "███████╗██╗ ██╗██╗ ██╗██████╗ ██╗██╗ ██╗███████╗",
1083
+ "██╔════╝██║ ██╔╝╚██╗ ██╔╝██╔══██╗██║██║ ██║██╔════╝",
1084
+ "███████╗█████╔╝ ╚████╔╝ ██║ ██║██║██║ ██║█████╗ ",
1085
+ "╚════██║██╔═██╗ ╚██╔╝ ██║ ██║██║╚██╗ ██╔╝██╔══╝ ",
1086
+ "███████║██║ ██╗ ██║ ██████╔╝██║ ╚████╔╝ ███████╗",
1087
+ "╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚══════╝"
1088
+ ];
1089
+ const RESET = "\x1B[0m";
1090
+ function hexToRgb(hex) {
1091
+ const n = Number.parseInt(hex.slice(1), 16);
1092
+ return [
1093
+ n >> 16 & 255,
1094
+ n >> 8 & 255,
1095
+ n & 255
1096
+ ];
1097
+ }
1098
+ function sgr(text, hex) {
1099
+ const [r, g, b] = hexToRgb(hex);
1100
+ return `\x1b[38;2;${r};${g};${b}m${text}${RESET}`;
1101
+ }
1102
+ function markLinesAnsi() {
1103
+ return MARK_CELLS.map((row) => row.map((cell) => cell === " " ? " " : sgr(cell[0], cell[1])).join(""));
1104
+ }
1105
+ /**
1106
+ * Build the splash for the `skydive --help` screen as a string, gated on the
1107
+ * terminal's capabilities. Returns '' when it should be suppressed (non-TTY
1108
+ * target such as a pipe or CI). The wordmark is left uncolored (terminal default
1109
+ * fg = neutral on both light and dark); only the mark uses brand petal hexes.
1110
+ * A narrow terminal collapses to a one-liner.
1111
+ */
1112
+ function brandHelpArt(stream = process.stdout) {
1113
+ if (!stream.isTTY) return "";
1114
+ const truecolor = (typeof stream.getColorDepth === "function" ? stream.getColorDepth() : 1) >= 24;
1115
+ if ((stream.columns ?? 80) < 66) return truecolor ? `\n ${sgr("✦", BRAND_ACCENT)} Skydive\n` : "\n ✦ Skydive\n";
1116
+ if (!truecolor) return `\n${WORDMARK.map((l) => ` ${l}`).join("\n")}\n`;
1117
+ const mark = markLinesAnsi();
1118
+ const word = [...WORDMARK];
1119
+ const height = Math.max(mark.length, word.length);
1120
+ const topPad = Math.max(0, Math.floor((word.length - mark.length) / 2));
1121
+ const blank = " ".repeat(MARK_WIDTH);
1122
+ const lines = [];
1123
+ for (let i = 0; i < height; i++) {
1124
+ const mi = i - topPad;
1125
+ const m = mi >= 0 && mi < mark.length ? mark[mi] : blank;
1126
+ const w = i < word.length ? word[i] : "";
1127
+ lines.push(` ${m} ${w}`);
1128
+ }
1129
+ return `\n${lines.join("\n")}\n`;
1130
+ }
1131
+
1132
+ //#endregion
1133
+ export { getPromptHistoryPath as A, resolveSession as B, API_KEY_PREFIX as C, deleteConfig as D, DEFAULT_WEB_URL as E, getStoredApiKeyWorkspaceName as F, setLastSeenVersion as G, saveConfig as H, getUpdateCheckDisabled as I, resolveAppUrl as L, getSavedTheme as M, getShareMachineDefault as N, getConfigPath as O, getStoredApiKeyId as P, resolveConfig as R, API_KEY_FAMILY_PREFIX as S, DEFAULT_APP_URL as T, saveSession as U, resolveWebUrl as V, saveTheme as W, getActiveWorkspaceId as _, applyTheme as a, setActiveWorkspace as b, noColorRequested as c, themeMode as d, themeModeFromColorFgBg as f, ensureActiveOrganization as g, themesForMode as h, DEFAULT_THEME_ID as i, getReviewStateDir as j, getLastSeenVersion as k, theme as l, themes as m, WORDMARK as n, findTheme as o, themeVersion as p, brandHelpArt as r, monoTheme as s, MARK_CELLS as t, themeForMode as u, getSessionIdentity as v, DEFAULT_API_URL as w, API_KEYS_URL as x, listWorkspaces as y, resolveManagementAuth as z };
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import "./rest-DZnAT7BG.mjs";
3
+ import "./api-BHq-5eW_.mjs";
4
+ import { t as PortalClient } from "./client-t6GmE8TI.mjs";
5
+
6
+ export { PortalClient };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as errorMessage } from "./util-CeisaZVY.mjs";
3
- import { c as machineIdentity, i as mintPortalDeviceToken, l as portalWsUrl, n as findThisDevice, r as grantPortalAccess, s as buildEnv, t as fetchPortalDevices } from "./api-zUf_Vrzh.mjs";
3
+ import { c as machineIdentity, i as mintPortalDeviceToken, l as portalWsUrl, n as findThisDevice, r as grantPortalAccess, s as buildEnv, t as fetchPortalDevices } from "./api-BHq-5eW_.mjs";
4
4
  import { z } from "zod";
5
5
  import { spawn } from "node:child_process";
6
6
  import { WebSocket } from "ws";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "./rest-BBQrPIwq.mjs";
3
- import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-YqNIRwdf.mjs";
2
+ import "./rest-DZnAT7BG.mjs";
3
+ import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-XvAHDKqu.mjs";
4
4
 
5
5
  export { messageGet, readStdin, resolveAgent, runPrint };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createRestClient, t as HttpError } from "./rest-BBQrPIwq.mjs";
2
+ import { n as createRestClient, t as HttpError } from "./rest-DZnAT7BG.mjs";
3
3
  import { n as isRecord, t as errorMessage } from "./util-CeisaZVY.mjs";
4
4
  import stableStringify from "safe-stable-stringify";
5
5
 
@@ -14,7 +14,7 @@ import { n as printError } from "./output-B4cW10Ph.mjs";
14
14
  * the reply (and to --json).
15
15
  */
16
16
  async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
17
- const { PortalClient } = await import("./client-A4m7lNDg.mjs");
17
+ const { PortalClient } = await import("./client-CRAGDqp0.mjs");
18
18
  let signalConnected;
19
19
  const connected = new Promise((resolve) => {
20
20
  signalConnected = resolve;
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { n as createRestClient, r as errorDetail, t as HttpError } from "./rest-BBQrPIwq.mjs";
2
+ import { n as createRestClient, r as errorDetail, t as HttpError } from "./rest-DZnAT7BG.mjs";
3
3
 
4
4
  export { createRestClient };
@@ -223,6 +223,10 @@ function createRestClient({ appUrl, sessionToken }) {
223
223
  sizeBytes: finalized.sizeBytes ?? size
224
224
  };
225
225
  },
226
+ forkConversation: async ({ conversationId }) => {
227
+ const { conversation } = await post(`/api/v1/conversations/${encodeURIComponent(conversationId)}/fork`, {}, forkConversationResponseSchema);
228
+ return conversation;
229
+ },
226
230
  deleteConversation: async ({ conversationId }) => {
227
231
  await del(`/api/v1/conversations/${encodeURIComponent(conversationId)}`);
228
232
  },
@@ -347,6 +351,12 @@ const listAgentsResponseSchema = z.object({
347
351
  totalCount: z.number().nullable().optional()
348
352
  });
349
353
  const createAgentResponseSchema = z.object({ agent: agentSummarySchema });
354
+ const conversationAgentSchema = z.object({
355
+ id: z.string().uuid(),
356
+ name: z.string(),
357
+ slug: z.string().nullable().optional(),
358
+ title: z.string().nullable().optional()
359
+ });
350
360
  const conversationSummarySchema = z.object({
351
361
  id: z.string().uuid(),
352
362
  title: z.string().nullable(),
@@ -356,20 +366,23 @@ const conversationSummarySchema = z.object({
356
366
  channel: z.string().nullable(),
357
367
  channelLabel: z.string().nullable(),
358
368
  viewerArchivedAt: z.string().nullable().optional(),
359
- agent: z.object({
360
- id: z.string().uuid(),
361
- name: z.string(),
362
- slug: z.string().nullable().optional(),
363
- title: z.string().nullable().optional()
364
- })
369
+ agent: conversationAgentSchema,
370
+ agents: z.array(conversationAgentSchema).optional()
365
371
  });
366
372
  const conversationDetailSchema = z.object({
367
373
  id: z.string().uuid(),
368
374
  title: z.string().nullable(),
369
375
  agentId: z.string().uuid(),
370
376
  createdAt: z.string(),
371
- updatedAt: z.string()
377
+ updatedAt: z.string(),
378
+ channel: z.string().nullable(),
379
+ channelLabel: z.string().nullable()
380
+ });
381
+ const conversationTitleSchema = z.object({
382
+ id: z.string().uuid(),
383
+ title: z.string().nullable()
372
384
  });
385
+ const forkConversationResponseSchema = z.object({ conversation: conversationTitleSchema });
373
386
  const getConversationResponseSchema = z.object({ conversation: conversationDetailSchema });
374
387
  const listConversationsResponseSchema = z.object({
375
388
  conversations: z.array(conversationSummarySchema),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.2.0-beta.455",
3
+ "version": "0.2.0-beta.482",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./rest-BBQrPIwq.mjs";
3
- import "./api-zUf_Vrzh.mjs";
4
- import { t as PortalClient } from "./client-DuPxkSoH.mjs";
5
-
6
- export { PortalClient };