skydive-cli 0.6.0-beta.13 → 0.6.0-beta.15

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/js/bin.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { c as version, s as name, t as createRestClient } from "./rest-BL0LvgFv.mjs";
3
- import { A as getPromptHistoryPath, B as resolveConfig, D as getDefaultAgent, E as getConfigPath, F as getStoredApiKeyWorkspaceName, G as saveSession, H as resolveSession$1, I as getUpdateCheckDisabled, N as getShareMachineDefault, O as getLastSeenVersion, P as getStoredApiKeyId, R as resolveAppUrl, S as DEFAULT_API_URL, T as deleteConfig, V as resolveManagementAuth, W as saveConfig, b as API_KEY_FAMILY_PREFIX, i as resolveAgent$1, k as getPreference, q as setLastSeenVersion, w as PREFERENCES, x as API_KEY_PREFIX, y as API_KEYS_URL, z as resolveChatAuth } from "./print-Cy646pv0.mjs";
4
- import { A as installAgentAlias, B as resolveWorkspaceId, D as machineOsFromPlatform, E as buildImportSeedPrompt, F as detectShell, I as ensureActiveOrganization, L as getActiveWorkspaceId, M as takenAliasNames, N as SUPPORTED_SHELLS, O as aliasActivationHint, P as defaultInstallEnv, R as getSessionIdentity, V as setActiveWorkspace, c as getAutomaticLogsDir, j as slugifyAliasName, k as dedupeAliasName, l as maybeStartProfiling, o as brandHelpArt, t as installCrashHandler, u as profileDir, w as themes, z as listWorkspaces } from "./install-n38o12q4.mjs";
2
+ import { c as version, s as name, t as createRestClient } from "./rest-Benrf1yZ.mjs";
3
+ import { A as getPromptHistoryPath, B as resolveConfig, D as getDefaultAgent, E as getConfigPath, F as getStoredApiKeyWorkspaceName, G as saveSession, H as resolveSession$1, I as getUpdateCheckDisabled, N as getShareMachineDefault, O as getLastSeenVersion, P as getStoredApiKeyId, R as resolveAppUrl, S as DEFAULT_API_URL, T as deleteConfig, V as resolveManagementAuth, W as saveConfig, b as API_KEY_FAMILY_PREFIX, i as resolveAgent$1, k as getPreference, q as setLastSeenVersion, w as PREFERENCES, x as API_KEY_PREFIX, y as API_KEYS_URL, z as resolveChatAuth } from "./print-C_xW32a6.mjs";
4
+ import { A as installAgentAlias, B as resolveWorkspaceId, D as machineOsFromPlatform, E as buildImportSeedPrompt, F as detectShell, I as ensureActiveOrganization, L as getActiveWorkspaceId, M as takenAliasNames, N as SUPPORTED_SHELLS, O as aliasActivationHint, P as defaultInstallEnv, R as getSessionIdentity, V as setActiveWorkspace, c as getAutomaticLogsDir, j as slugifyAliasName, k as dedupeAliasName, l as maybeStartProfiling, o as brandHelpArt, t as installCrashHandler, u as profileDir, w as themes, z as listWorkspaces } from "./install-Fo1riO1w.mjs";
5
5
  import { n as printError, r as printTable, t as output } from "./output-C9mb3sUB.mjs";
6
6
  import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
7
7
  import "./billing-blocked-D3l5kJlX.mjs";
8
- import { a as resolveMachineIdentity } from "./client-JCeJ0RAt.mjs";
8
+ import { a as resolveMachineIdentity } from "./client-7decrdm4.mjs";
9
9
  import "./tls-cert-Rua2oV7n.mjs";
10
10
  import { i as grantPortalAccess, n as fetchPortalDevices, o as registerPortalDevice, r as findThisDevice, s as revokePortalAccess } from "./api-BFQ4PQDA.mjs";
11
- import { d as daemonPaths, i as queryDaemonStatus, n as ensureDaemonRunning, s as stopDaemon, u as PORTAL_DAEMON_FLAG } from "./daemon-LnRwTN3W.mjs";
11
+ import { d as daemonPaths, i as queryDaemonStatus, n as ensureDaemonRunning, s as stopDaemon, u as PORTAL_DAEMON_FLAG } from "./daemon-5HHkFtaF.mjs";
12
12
  import { a as encodeFsRequest, i as decodeFsResponse, n as SANDBOX_STREAM_PATH, r as streamSpecToQuery, t as SandboxStream } from "./client-l6orYfqq.mjs";
13
13
  import { hideBin } from "yargs/helpers";
14
14
  import yargs from "yargs";
@@ -1584,7 +1584,7 @@ const importCommand = {
1584
1584
  process.exit(1);
1585
1585
  }
1586
1586
  }
1587
- const { runChat } = await import("./boot-DME-U2DQ.mjs");
1587
+ const { runChat } = await import("./boot-D5_QVvwk.mjs");
1588
1588
  await runChat({
1589
1589
  appUrl,
1590
1590
  sessionToken: session.value.sessionToken,
@@ -1614,7 +1614,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1614
1614
  printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
1615
1615
  process.exit(1);
1616
1616
  }
1617
- const { connectMachineShare } = await import("./print-share-CsfmGXNy.mjs");
1617
+ const { connectMachineShare } = await import("./print-share-ooQ3IrUT.mjs");
1618
1618
  const machineShare = await connectMachineShare({
1619
1619
  appUrl,
1620
1620
  sessionToken: session.value.sessionToken,
@@ -1622,7 +1622,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1622
1622
  });
1623
1623
  const extra = (argv.print ?? "").trim();
1624
1624
  const prompt = buildImportSeedPrompt(process.cwd(), machineOsFromPlatform(process.platform)) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
1625
- const { runPrint } = await import("./print-CBeqAsBA.mjs");
1625
+ const { runPrint } = await import("./print-sWlw3B6z.mjs");
1626
1626
  try {
1627
1627
  const result = await runPrint({
1628
1628
  appUrl,
@@ -2029,7 +2029,7 @@ const chatCommand = {
2029
2029
  sessionToken: auth.value.token,
2030
2030
  agentSelector: argv.agent ?? null
2031
2031
  });
2032
- const { runChat } = await import("./boot-DME-U2DQ.mjs");
2032
+ const { runChat } = await import("./boot-D5_QVvwk.mjs");
2033
2033
  await runChat({
2034
2034
  appUrl: auth.value.appUrl,
2035
2035
  sessionToken: auth.value.token,
@@ -2113,7 +2113,7 @@ async function runPrintMode({ argv, appUrl }) {
2113
2113
  printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
2114
2114
  process.exit(1);
2115
2115
  }
2116
- const { runPrint, readStdin } = await import("./print-CBeqAsBA.mjs");
2116
+ const { runPrint, readStdin } = await import("./print-sWlw3B6z.mjs");
2117
2117
  const workspaceId = await resolveWorkspaceFlag({
2118
2118
  appUrl,
2119
2119
  sessionToken: auth.value.token,
@@ -2143,7 +2143,7 @@ async function runPrintMode({ argv, appUrl }) {
2143
2143
  process.exit(1);
2144
2144
  }
2145
2145
  } else {
2146
- const { connectMachineShare } = await import("./print-share-CsfmGXNy.mjs");
2146
+ const { connectMachineShare } = await import("./print-share-ooQ3IrUT.mjs");
2147
2147
  machineShare = await connectMachineShare({
2148
2148
  appUrl: auth.value.appUrl,
2149
2149
  sessionToken: auth.value.token,
@@ -2208,7 +2208,7 @@ const getCommand$1 = {
2208
2208
  printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
2209
2209
  process.exit(1);
2210
2210
  }
2211
- const { messageGet } = await import("./print-CBeqAsBA.mjs");
2211
+ const { messageGet } = await import("./print-sWlw3B6z.mjs");
2212
2212
  try {
2213
2213
  const result = await messageGet({
2214
2214
  appUrl: auth.value.appUrl,
@@ -2411,7 +2411,7 @@ const switchCommand = {
2411
2411
  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.");
2412
2412
  process.exit(1);
2413
2413
  }
2414
- const { runWorkspacePicker } = await import("./boot-DME-U2DQ.mjs");
2414
+ const { runWorkspacePicker } = await import("./boot-D5_QVvwk.mjs");
2415
2415
  await runWorkspacePicker(session);
2416
2416
  return;
2417
2417
  }
@@ -2490,7 +2490,7 @@ const openCommand = {
2490
2490
  const agent = argv.agent ? resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent) : null;
2491
2491
  const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
2492
2492
  const { machineName } = await resolveMachineIdentity(null);
2493
- const { PortalDaemonClient } = await import("./daemon-client-DPlUrHM0.mjs");
2493
+ const { PortalDaemonClient } = await import("./daemon-client-CkU1-t5_.mjs");
2494
2494
  let lastLine = "";
2495
2495
  let signalConnected;
2496
2496
  const connected = new Promise((resolve) => {
@@ -2847,8 +2847,8 @@ const sandboxCommand = {
2847
2847
  }).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`"),
2848
2848
  handler: async (argv) => {
2849
2849
  const session = requireSession(argv);
2850
- const { createRestClient } = await import("./rest-CN-FGWu1.mjs");
2851
- const { resolveAgent } = await import("./print-CBeqAsBA.mjs");
2850
+ const { createRestClient } = await import("./rest-8DCRRen0.mjs");
2851
+ const { resolveAgent } = await import("./print-sWlw3B6z.mjs");
2852
2852
  const client = createRestClient({
2853
2853
  appUrl: session.appUrl,
2854
2854
  sessionToken: session.sessionToken
@@ -3119,8 +3119,8 @@ const fsEditCommand = {
3119
3119
  handler: async (argv) => {
3120
3120
  const session = requireSession(argv);
3121
3121
  const remotePath = argv.path;
3122
- const { createRestClient } = await import("./rest-CN-FGWu1.mjs");
3123
- const { resolveAgent } = await import("./print-CBeqAsBA.mjs");
3122
+ const { createRestClient } = await import("./rest-8DCRRen0.mjs");
3123
+ const { resolveAgent } = await import("./print-sWlw3B6z.mjs");
3124
3124
  const client = createRestClient({
3125
3125
  appUrl: session.appUrl,
3126
3126
  sessionToken: session.sessionToken
@@ -4573,7 +4573,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
4573
4573
  process.exit(0);
4574
4574
  }
4575
4575
  if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
4576
- const { runPortalDaemon } = await import("./daemon-DtDwegIj.mjs");
4576
+ const { runPortalDaemon } = await import("./daemon-CLTmAfBt.mjs");
4577
4577
  runPortalDaemon(process.argv);
4578
4578
  } else runCli();
4579
4579
  function runCli() {
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
- import { a as errorMessage, i as sendErrorMessage, n as errorDetail, o as isRecord, t as createRestClient } from "./rest-BL0LvgFv.mjs";
3
- import { C as DEFAULT_APP_URL, E as getConfigPath, K as saveTheme, L as recordDefaultAgent, M as getSavedTheme, S as DEFAULT_API_URL, U as resolveWebUrl, _ as parseConnectCard, c as actionableCardVerb, d as parseOauthConnectParams, f as prioritizeActionableCards, g as computeSettledLabel, h as composeQuestionAnswer, i as resolveAgent, j as getReviewStateDir, l as cardActionErrorMessage, m as resolveConnectUrl, p as reconcileMaskedInput, s as MASK_CHAR, u as parseExternalOauthConnectParams, v as specKeyFor } from "./print-Cy646pv0.mjs";
4
- import { A as installAgentAlias, C as themeVersion, D as machineOsFromPlatform, F as detectShell, L as getActiveWorkspaceId, M as takenAliasNames, O as aliasActivationHint, S as themeModeFromColorFgBg, T as themesForMode, V as setActiveWorkspace, _ as monoTheme, a as WORDMARK, b as themeForMode, d as profilingEnabled, f as record, g as findTheme, h as applyTheme, i as MARK_CELLS, j as slugifyAliasName, k as dedupeAliasName, m as DEFAULT_THEME_ID, n as buildCrashReport, p as writeArtifact, r as writeCrashReport, s as splashFitsWidth, t as installCrashHandler, v as noColorRequested, x as themeMode, y as theme, z as listWorkspaces } from "./install-n38o12q4.mjs";
2
+ import { a as errorMessage, i as sendErrorMessage, n as errorDetail, o as isRecord, t as createRestClient } from "./rest-Benrf1yZ.mjs";
3
+ import { C as DEFAULT_APP_URL, E as getConfigPath, K as saveTheme, L as recordDefaultAgent, M as getSavedTheme, S as DEFAULT_API_URL, U as resolveWebUrl, _ as parseConnectCard, c as actionableCardVerb, d as parseOauthConnectParams, f as prioritizeActionableCards, g as computeSettledLabel, h as composeQuestionAnswer, i as resolveAgent, j as getReviewStateDir, l as cardActionErrorMessage, m as resolveConnectUrl, p as reconcileMaskedInput, s as MASK_CHAR, u as parseExternalOauthConnectParams, v as specKeyFor } from "./print-C_xW32a6.mjs";
4
+ import { A as installAgentAlias, C as themeVersion, D as machineOsFromPlatform, F as detectShell, L as getActiveWorkspaceId, M as takenAliasNames, O as aliasActivationHint, S as themeModeFromColorFgBg, T as themesForMode, V as setActiveWorkspace, _ as monoTheme, a as WORDMARK, b as themeForMode, d as profilingEnabled, f as record, g as findTheme, h as applyTheme, i as MARK_CELLS, j as slugifyAliasName, k as dedupeAliasName, m as DEFAULT_THEME_ID, n as buildCrashReport, p as writeArtifact, r as writeCrashReport, s as splashFitsWidth, t as installCrashHandler, v as noColorRequested, x as themeMode, y as theme, z as listWorkspaces } from "./install-Fo1riO1w.mjs";
5
5
  import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
6
6
  import { i as billingBlockedOutcomeFromSendResponse } from "./billing-blocked-D3l5kJlX.mjs";
7
- import { t as PortalClient } from "./client-JCeJ0RAt.mjs";
7
+ import { t as PortalClient } from "./client-7decrdm4.mjs";
8
8
  import { t as defaultTlsCertSource } from "./tls-cert-Rua2oV7n.mjs";
9
9
  import "./api-BFQ4PQDA.mjs";
10
- import "./daemon-LnRwTN3W.mjs";
10
+ import "./daemon-5HHkFtaF.mjs";
11
11
  import { t as SandboxStream } from "./client-l6orYfqq.mjs";
12
- import { t as PortalDaemonClient } from "./daemon-client-BoLMCnVn.mjs";
12
+ import { t as PortalDaemonClient } from "./daemon-client-BdEu0TK_.mjs";
13
13
  import { t as runRawPtyPassthrough } from "./raw-pty-dndn1Kxc.mjs";
14
14
  import * as os$1 from "node:os";
15
15
  import { homedir, platform, release, tmpdir } from "node:os";
@@ -7028,9 +7028,9 @@ function flattenOutputText(value) {
7028
7028
  * anchor set the screen renders is always at most one.
7029
7029
  */
7030
7030
  function subagentAnchorsFromHistory(items) {
7031
- let latest = null;
7032
- for (const item of items) if (item.kind === "tool" && item.toolName === "subagent") latest = item.id;
7033
- return latest === null ? [] : [latest];
7031
+ const ids = /* @__PURE__ */ new Set();
7032
+ for (const item of items) if (item.kind === "tool" && item.toolName === "subagent") ids.add(item.id);
7033
+ return [...ids];
7034
7034
  }
7035
7035
  /**
7036
7036
  * The next anchor set as a `subagent` call streams in live. Always the single
@@ -7038,8 +7038,8 @@ function subagentAnchorsFromHistory(items) {
7038
7038
  * stacking beside it, so the composer only ever has one fan-out card above it.
7039
7039
  */
7040
7040
  function nextAnchoredSubagentIds({ current, nextId }) {
7041
- if (current.length === 1 && current[0] === nextId) return current;
7042
- return [nextId];
7041
+ if (current.includes(nextId)) return current;
7042
+ return [...current, nextId];
7043
7043
  }
7044
7044
  /**
7045
7045
  * The card's rows: one per queued run (call order), each carrying the
@@ -7051,7 +7051,7 @@ function nextAnchoredSubagentIds({ current, nextId }) {
7051
7051
  * mid-turn.
7052
7052
  */
7053
7053
  function subagentRows({ titles, queued, fetched }) {
7054
- if (queued.length === 0) return titles.map((title, index) => ({
7054
+ if (fetched === null && queued.length === 0) return titles.map((title, index) => ({
7055
7055
  id: `queued:${index}`,
7056
7056
  title,
7057
7057
  status: "queued",
@@ -7062,7 +7062,18 @@ function subagentRows({ titles, queued, fetched }) {
7062
7062
  contextTokens: null,
7063
7063
  latestActivity: null
7064
7064
  }));
7065
- return queued.map((task, index) => {
7065
+ const tasks = new Map(queued.map((task, index) => [task.id, {
7066
+ task,
7067
+ index
7068
+ }]));
7069
+ for (const id of fetched?.keys() ?? []) if (!tasks.has(id)) tasks.set(id, {
7070
+ task: {
7071
+ id,
7072
+ conversationId: null
7073
+ },
7074
+ index: -1
7075
+ });
7076
+ return [...tasks.values()].map(({ task, index }) => {
7066
7077
  const row = fetched?.get(task.id) ?? null;
7067
7078
  return {
7068
7079
  id: task.id,
@@ -7220,34 +7231,57 @@ function SubagentRowView({ row, selected, width, nowMs, onOpen }) {
7220
7231
  * the card reports its rows up through ConversationContext so the screen
7221
7232
  * knows what's selectable).
7222
7233
  */
7223
- function ToolSubagent({ item, isActive: _isActive, onLiveChange, onHeightChange }) {
7234
+ function ToolSubagent({ items, isActive: _isActive, onHeightChange }) {
7224
7235
  const { rest, conversationId, openConversation, onSubagentBatch, subagentSelection } = useConversationContext();
7225
7236
  const { width } = useTerminalDimensions();
7226
- const titles = subagentTaskTitles(item.input);
7227
- const queued = item.output?.kind === "ok" ? queuedSubagentTasks(item.output.value) : [];
7228
- const idsKey = queued.map((task) => task.id).join(",");
7237
+ const optimistic = /* @__PURE__ */ new Map();
7238
+ let pendingIndex = 0;
7239
+ for (const item of items) {
7240
+ const titles = subagentTaskTitles(item.input);
7241
+ const queued = item.output?.kind === "ok" ? queuedSubagentTasks(item.output.value) : [];
7242
+ if (queued.length === 0) {
7243
+ for (const title of titles) {
7244
+ optimistic.set(`pending:${pendingIndex}`, {
7245
+ title,
7246
+ conversationId: null
7247
+ });
7248
+ pendingIndex += 1;
7249
+ }
7250
+ continue;
7251
+ }
7252
+ for (const [index, task] of queued.entries()) if (!optimistic.has(task.id)) optimistic.set(task.id, {
7253
+ title: titles[index] ?? "Subagent task",
7254
+ conversationId: task.conversationId
7255
+ });
7256
+ }
7257
+ const queued = [...optimistic].map(([id, task]) => ({
7258
+ id,
7259
+ conversationId: task.conversationId
7260
+ }));
7261
+ const titles = [...optimistic.values()].map((task) => task.title);
7229
7262
  const [fetched, setFetched] = useState(null);
7230
7263
  const rows = subagentRows({
7231
7264
  titles,
7232
7265
  queued,
7233
7266
  fetched
7234
7267
  });
7235
- const live = rows.length === 0 || rows.some((row) => isLiveStatus(row.status));
7268
+ const live = fetched === null || rows.some((row) => isLiveStatus(row.status));
7236
7269
  const [nowMs, setNowMs] = useState(() => Date.now());
7237
7270
  useEffect(() => {
7238
7271
  if (!live || rows.length === 0) return;
7239
7272
  const id = setInterval(() => setNowMs(Date.now()), 1e3);
7240
7273
  return () => clearInterval(id);
7241
7274
  }, [live, rows.length]);
7242
- useEffect(() => {
7243
- onLiveChange?.(live);
7244
- }, [live, onLiveChange]);
7245
7275
  useEffect(() => {
7246
7276
  const visibleRows = Math.min(rows.length, MAX_VISIBLE_ROWS);
7247
- onHeightChange?.(rows.length === 0 ? 1 : 1 + visibleRows + (rows.length > MAX_VISIBLE_ROWS ? 1 : 0));
7248
- }, [onHeightChange, rows.length]);
7277
+ onHeightChange?.(rows.length === 0 ? items.length > 0 ? 1 : 0 : 1 + visibleRows + (rows.length > MAX_VISIBLE_ROWS ? 1 : 0));
7278
+ }, [
7279
+ items.length,
7280
+ onHeightChange,
7281
+ rows.length
7282
+ ]);
7249
7283
  useEffect(() => {
7250
- if (!rest || !conversationId || idsKey.length === 0 || !live) return;
7284
+ if (!rest || !conversationId || !live) return;
7251
7285
  let cancelled = false;
7252
7286
  let timer = null;
7253
7287
  const tick = async () => {
@@ -7266,14 +7300,12 @@ function ToolSubagent({ item, isActive: _isActive, onLiveChange, onHeightChange
7266
7300
  }, [
7267
7301
  rest,
7268
7302
  conversationId,
7269
- idsKey,
7270
7303
  live
7271
7304
  ]);
7272
- const reportKey = rows.map((row) => `${row.id}:${row.status}:${row.conversationId ?? ""}`).join("|");
7273
7305
  useEffect(() => {
7274
- if (!onSubagentBatch || rows.length === 0) return;
7306
+ if (!onSubagentBatch || !conversationId || rows.length === 0) return;
7275
7307
  onSubagentBatch({
7276
- toolCallId: item.id,
7308
+ toolCallId: conversationId,
7277
7309
  live,
7278
7310
  rows: rows.map((row) => ({
7279
7311
  id: row.id,
@@ -7282,18 +7314,21 @@ function ToolSubagent({ item, isActive: _isActive, onLiveChange, onHeightChange
7282
7314
  });
7283
7315
  }, [
7284
7316
  onSubagentBatch,
7285
- item.id,
7317
+ conversationId,
7286
7318
  live,
7287
- reportKey
7319
+ rows.map((row) => `${row.id}:${row.status}:${row.conversationId ?? ""}`).join("|")
7288
7320
  ]);
7289
- if (rows.length === 0) return /* @__PURE__ */ jsxs("text", { children: [/* @__PURE__ */ jsx("span", {
7290
- fg: theme.warning,
7291
- children: " "
7292
- }), /* @__PURE__ */ jsx("span", {
7293
- fg: theme.tool,
7294
- children: "Delegating to subagents…"
7295
- })] });
7296
- const selectedIndex = subagentSelection && subagentSelection.toolCallId === item.id ? Math.min(subagentSelection.index, rows.length - 1) : null;
7321
+ if (rows.length === 0) {
7322
+ if (items.length === 0 || fetched !== null) return null;
7323
+ return /* @__PURE__ */ jsxs("text", { children: [/* @__PURE__ */ jsx("span", {
7324
+ fg: theme.warning,
7325
+ children: "◐ "
7326
+ }), /* @__PURE__ */ jsx("span", {
7327
+ fg: theme.tool,
7328
+ children: "Delegating to subagents…"
7329
+ })] });
7330
+ }
7331
+ const selectedIndex = subagentSelection && subagentSelection.toolCallId === conversationId ? Math.min(subagentSelection.index, rows.length - 1) : null;
7297
7332
  const start = rows.length > MAX_VISIBLE_ROWS ? windowStart(selectedIndex ?? 0, rows.length, MAX_VISIBLE_ROWS) : 0;
7298
7333
  const visible = rows.slice(start, start + MAX_VISIBLE_ROWS);
7299
7334
  const moreBelow = rows.length - start - visible.length;
@@ -7351,32 +7386,24 @@ function ToolSubagent({ item, isActive: _isActive, onLiveChange, onHeightChange
7351
7386
  * a settled batch keeps its rows — titles and drill-in — until the next
7352
7387
  * fan-out replaces the group.
7353
7388
  */
7354
- function SubagentCardView({ items, isActive, onHeightChange, onBatchLiveChange }) {
7355
- const [heightById, setHeightById] = useState({});
7356
- const setBatchHeight = useCallback((id, height) => {
7357
- setHeightById((current) => current[id] === height ? current : {
7358
- ...current,
7359
- [id]: height
7360
- });
7361
- }, []);
7362
- const height = useMemo(() => items.length === 0 ? 0 : 1 + items.reduce((rows, item) => rows + (heightById[item.id] ?? 0), 0) + (items.length - 1), [heightById, items]);
7363
- useEffect(() => onHeightChange?.(height), [height, onHeightChange]);
7364
- if (items.length === 0) return null;
7389
+ function SubagentCardView({ items, isActive, onHeightChange }) {
7390
+ const [height, setHeight] = useState(0);
7391
+ useEffect(() => onHeightChange?.(height > 0 ? height + 1 : 0), [height, onHeightChange]);
7365
7392
  return /* @__PURE__ */ jsx("box", {
7366
7393
  style: {
7367
7394
  flexDirection: "column",
7368
7395
  flexShrink: 0,
7369
- marginTop: 1,
7370
- paddingLeft: 1,
7371
- paddingRight: 1,
7396
+ height: height > 0 ? void 0 : 0,
7397
+ marginTop: height > 0 ? 1 : 0,
7398
+ paddingLeft: height > 0 ? 1 : 0,
7399
+ paddingRight: height > 0 ? 1 : 0,
7372
7400
  gap: 1
7373
7401
  },
7374
- children: items.map((item) => /* @__PURE__ */ jsx(ToolSubagent, {
7375
- item,
7402
+ children: /* @__PURE__ */ jsx(ToolSubagent, {
7403
+ items,
7376
7404
  isActive,
7377
- onLiveChange: (live) => onBatchLiveChange?.(item.id, live),
7378
- onHeightChange: (batchHeight) => setBatchHeight(item.id, batchHeight)
7379
- }, item.id))
7405
+ onHeightChange: setHeight
7406
+ })
7380
7407
  });
7381
7408
  }
7382
7409
 
@@ -13177,10 +13204,6 @@ function ChatScreen({ agent, conversation, attachRunId, parentConversationId })
13177
13204
  const togglePlanExpanded = useCallback(() => setPlanExpanded((current) => !current), []);
13178
13205
  const [subagentRows, setSubagentRows] = useState(0);
13179
13206
  const [anchoredSubagentIds, setAnchoredSubagentIds] = useState([]);
13180
- const anchoredSubagentLiveRef = useRef({});
13181
- const setAnchoredBatchLive = useCallback((id, live) => {
13182
- anchoredSubagentLiveRef.current[id] = live;
13183
- }, []);
13184
13207
  const [scrolledUp, setScrolledUp] = useState(false);
13185
13208
  const [stickyPromptId, setStickyPromptId] = useState(null);
13186
13209
  const transcriptRowRefs = useRef(/* @__PURE__ */ new Map());
@@ -13381,12 +13404,10 @@ function ChatScreen({ agent, conversation, attachRunId, parentConversationId })
13381
13404
  return items.filter((item) => item.kind === "tool" && item.toolName === "subagent" && ids.has(item.id));
13382
13405
  }, [items, anchoredSubagentIds]);
13383
13406
  const anchoredSubagentBatch = useMemo(() => {
13384
- for (const item of subagentItems) {
13385
- const batch = subagentBatches.get(item.id);
13386
- if (batch && batch.rows.length > 0) return batch;
13387
- }
13388
- return null;
13389
- }, [subagentItems, subagentBatches]);
13407
+ if (!conversationId) return null;
13408
+ const batch = subagentBatches.get(conversationId);
13409
+ return batch && batch.rows.length > 0 ? batch : null;
13410
+ }, [conversationId, subagentBatches]);
13390
13411
  const anchoredSubagentBatchRef = useRef(anchoredSubagentBatch);
13391
13412
  anchoredSubagentBatchRef.current = anchoredSubagentBatch;
13392
13413
  useEffect(() => {
@@ -13487,7 +13508,7 @@ function ChatScreen({ agent, conversation, attachRunId, parentConversationId })
13487
13508
  });
13488
13509
  setItems(loaded);
13489
13510
  const anchors = subagentAnchorsFromHistory(loaded);
13490
- if (anchors.length > 0) setAnchoredSubagentIds((current) => current.length > 0 ? current : anchors);
13511
+ if (anchors.length > 0) setAnchoredSubagentIds((current) => [...new Set([...anchors, ...current])]);
13491
13512
  seedPrLinks(loaded);
13492
13513
  setTodos(latestTodosFromHistory(firstPage.messages));
13493
13514
  setHistoryLoaded(true);
@@ -13562,14 +13583,10 @@ function ChatScreen({ agent, conversation, attachRunId, parentConversationId })
13562
13583
  prScanner.addChunk(event.chunk);
13563
13584
  if ((event.chunk.type === "tool-input-start" || event.chunk.type === "tool-input-available") && event.chunk.toolName === "subagent" && typeof event.chunk.toolCallId === "string") {
13564
13585
  const id = event.chunk.toolCallId;
13565
- setAnchoredSubagentIds((current) => {
13566
- const next = nextAnchoredSubagentIds({
13567
- current,
13568
- nextId: id
13569
- });
13570
- if (next.length === 1 && next[0] === id) anchoredSubagentLiveRef.current = { [id]: true };
13571
- return next;
13572
- });
13586
+ setAnchoredSubagentIds((current) => nextAnchoredSubagentIds({
13587
+ current,
13588
+ nextId: id
13589
+ }));
13573
13590
  }
13574
13591
  if (event.chunk.type === "data-anyone-todos-updated") {
13575
13592
  const next = todosFromPayload(event.chunk.data);
@@ -15603,8 +15620,7 @@ function ChatScreen({ agent, conversation, attachRunId, parentConversationId })
15603
15620
  /* @__PURE__ */ jsx(SubagentCardView, {
15604
15621
  items: subagentItems,
15605
15622
  isActive: run.kind !== "idle",
15606
- onHeightChange: setSubagentRows,
15607
- onBatchLiveChange: setAnchoredBatchLive
15623
+ onHeightChange: setSubagentRows
15608
15624
  }),
15609
15625
  todoCardVisible && todos ? /* @__PURE__ */ jsx(TodoCardView, {
15610
15626
  todos,
@@ -160,7 +160,7 @@ function errorMessage(err) {
160
160
  * the commit time of the built tree is one monotonic clock they share.
161
161
  */
162
162
  function portalDaemonBuild() {
163
- return "1788894076";
163
+ return "1788972427";
164
164
  }
165
165
  /**
166
166
  * Whether a client carrying `mine` should replace a running daemon carrying
@@ -186,6 +186,8 @@ function parseStamp(stamp) {
186
186
 
187
187
  //#endregion
188
188
  //#region ../portal-protocol/src/index.ts
189
+ const CLOSE_SUPERSEDED = 4001;
190
+ const CLOSE_FLAP_COOLDOWN = 4002;
189
191
  const MAX_WS_FRAME_BYTES = 16 * 1024 * 1024;
190
192
  const T_DATA = 1;
191
193
  const T_CTRL = 2;
@@ -584,7 +586,11 @@ const DEFAULT_HEARTBEAT = {
584
586
  wakeGapThresholdMs: 1e4,
585
587
  netWatchTickMs: 2e3,
586
588
  initialBackoffMs: 500,
587
- maxBackoffMs: 1e4
589
+ maxBackoffMs: 1e4,
590
+ supersededBackoffFloorMs: 5e3,
591
+ flapWindowMs: 6e4,
592
+ flapMaxStandDowns: 3,
593
+ flapBackoffMs: 6e4
588
594
  };
589
595
  /**
590
596
  * A cheap fingerprint of the machine's network identity: every non-internal
@@ -669,6 +675,7 @@ var PortalClient = class {
669
675
  netWatch = null;
670
676
  netFingerprint = networkFingerprint();
671
677
  backoffKick = null;
678
+ inStandDownSleep = false;
672
679
  dialReason = null;
673
680
  disconnectedAt = null;
674
681
  handingOff = false;
@@ -739,6 +746,7 @@ var PortalClient = class {
739
746
  this.ws.terminate();
740
747
  return;
741
748
  }
749
+ if (this.inStandDownSleep) return;
742
750
  this.kickBackoff();
743
751
  }
744
752
  kickBackoff() {
@@ -914,11 +922,23 @@ var PortalClient = class {
914
922
  this.opts.onMachineName(identity.machineName, identity.machineNameSource);
915
923
  }
916
924
  let backoff = this.heartbeatConfig.initialBackoffMs;
925
+ let standDowns = [];
917
926
  while (this.enabled && !this.disposed && !this.handingOff) {
918
927
  this.setStatus("connecting");
928
+ let sleepFloor = 0;
919
929
  try {
920
930
  const token = await this.wsToken();
921
- if (await this.runConnection(token)) backoff = this.heartbeatConfig.initialBackoffMs;
931
+ const { healthy, standDown } = await this.runConnection(token);
932
+ if (standDown) {
933
+ const now = Date.now();
934
+ standDowns.push(now);
935
+ standDowns = standDowns.filter((at) => now - at <= this.heartbeatConfig.flapWindowMs);
936
+ if (standDowns.length >= this.heartbeatConfig.flapMaxStandDowns) {
937
+ backoff = this.heartbeatConfig.flapBackoffMs;
938
+ this.setStatus("error", "another portal connection for this machine keeps taking over — if Skydive Desktop or `skydive portal open` is running twice, quit the duplicate; an unstable network can also cause this briefly");
939
+ } else backoff = Math.max(Math.min(backoff * 2, this.heartbeatConfig.maxBackoffMs), this.heartbeatConfig.supersededBackoffFloorMs);
940
+ sleepFloor = backoff / 2;
941
+ } else if (healthy) backoff = this.heartbeatConfig.initialBackoffMs;
922
942
  else backoff = Math.min(backoff * 2, this.heartbeatConfig.maxBackoffMs);
923
943
  } catch (err) {
924
944
  if (!this.enabled || this.disposed || this.handingOff) break;
@@ -926,17 +946,23 @@ var PortalClient = class {
926
946
  backoff = Math.min(backoff * 2, this.heartbeatConfig.maxBackoffMs);
927
947
  }
928
948
  if (!this.enabled || this.disposed || this.handingOff) break;
929
- await this.backoffSleep(Math.random() * backoff);
949
+ this.inStandDownSleep = sleepFloor > 0;
950
+ await this.backoffSleep(sleepFloor + Math.random() * Math.max(backoff - sleepFloor, 0));
951
+ this.inStandDownSleep = false;
930
952
  }
931
953
  }
932
954
  runConnection(token) {
933
955
  return new Promise((resolve) => {
934
956
  let settled = false;
935
957
  let healthy = false;
958
+ let standDown = false;
936
959
  const settle = () => {
937
960
  if (settled) return;
938
961
  settled = true;
939
- resolve(healthy);
962
+ resolve({
963
+ healthy,
964
+ standDown
965
+ });
940
966
  };
941
967
  const ws = new WebSocket(portalWsUrl(this.opts.appUrl, this.machineName, this.friendlyName, {
942
968
  build: portalDaemonBuild(),
@@ -988,7 +1014,9 @@ var PortalClient = class {
988
1014
  ws.on("error", (err) => {
989
1015
  this.error = errorMessage(err);
990
1016
  });
991
- ws.on("close", (code) => {
1017
+ ws.on("close", (code, reason) => {
1018
+ standDown = code === CLOSE_SUPERSEDED || code === CLOSE_FLAP_COOLDOWN || code === 1e3 && reason.toString() === "superseded";
1019
+ if (standDown) this.dialReason ??= `superseded (code ${code})`;
992
1020
  this.dialReason ??= `connection closed (code ${code})`;
993
1021
  this.disconnectedAt ??= Date.now();
994
1022
  clearInterval(heartbeat);
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { t as PortalClient } from "./client-7decrdm4.mjs";
3
+ import "./api-BFQ4PQDA.mjs";
4
+
5
+ export { PortalClient };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as isRecord, n as isNewerBuild, r as portalDaemonBuild, t as PortalClient } from "./client-JCeJ0RAt.mjs";
2
+ import { i as isRecord, n as isNewerBuild, r as portalDaemonBuild, t as PortalClient } from "./client-7decrdm4.mjs";
3
3
  import { t as defaultTlsCertSource } from "./tls-cert-Rua2oV7n.mjs";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import "./client-JCeJ0RAt.mjs";
2
+ import "./client-7decrdm4.mjs";
3
3
  import "./tls-cert-Rua2oV7n.mjs";
4
4
  import "./api-BFQ4PQDA.mjs";
5
- import { a as runPortalDaemon, c as stopDaemonForHandoff, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-LnRwTN3W.mjs";
5
+ import { a as runPortalDaemon, c as stopDaemonForHandoff, i as queryDaemonStatus, n as ensureDaemonRunning, o as startPortalDaemon, r as isDaemonListening, s as stopDaemon, t as PortalDaemon } from "./daemon-5HHkFtaF.mjs";
6
6
 
7
7
  export { runPortalDaemon };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { d as daemonPaths, f as encodeLine, l as LOCAL_PROTOCOL_VERSION, m as parseDaemonMessage, n as ensureDaemonRunning, p as makeLineParser } from "./daemon-LnRwTN3W.mjs";
2
+ import { d as daemonPaths, f as encodeLine, l as LOCAL_PROTOCOL_VERSION, m as parseDaemonMessage, n as ensureDaemonRunning, p as makeLineParser } from "./daemon-5HHkFtaF.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { connect } from "node:net";
5
5
 
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import "./client-7decrdm4.mjs";
3
+ import "./tls-cert-Rua2oV7n.mjs";
4
+ import "./api-BFQ4PQDA.mjs";
5
+ import "./daemon-5HHkFtaF.mjs";
6
+ import { t as PortalDaemonClient } from "./daemon-client-BdEu0TK_.mjs";
7
+
8
+ export { PortalDaemonClient };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { c as version$1 } from "./rest-BL0LvgFv.mjs";
3
- import { E as getConfigPath } from "./print-Cy646pv0.mjs";
2
+ import { c as version$1 } from "./rest-Benrf1yZ.mjs";
3
+ import { E as getConfigPath } from "./print-C_xW32a6.mjs";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { err, ok } from "neverthrow";
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as errorMessage, i as sendErrorMessage, o as isRecord, t as createRestClient } from "./rest-BL0LvgFv.mjs";
2
+ import { a as errorMessage, i as sendErrorMessage, o as isRecord, t as createRestClient } from "./rest-Benrf1yZ.mjs";
3
3
  import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
4
4
  import { i as billingBlockedOutcomeFromSendResponse, n as BillingBlockedError } from "./billing-blocked-D3l5kJlX.mjs";
5
5
  import path from "node:path";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import "./rest-BL0LvgFv.mjs";
3
- import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-Cy646pv0.mjs";
2
+ import "./rest-Benrf1yZ.mjs";
3
+ import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-C_xW32a6.mjs";
4
4
  import "./billing-blocked-D3l5kJlX.mjs";
5
5
 
6
6
  export { messageGet, readStdin, resolveAgent, runPrint };
@@ -14,7 +14,7 @@ import { n as printError } from "./output-C9mb3sUB.mjs";
14
14
  * the reply (and to --json).
15
15
  */
16
16
  async function connectMachineShare({ appUrl, sessionToken, timeoutHint }) {
17
- const { PortalClient } = await import("./client-D0FfDGC9.mjs");
17
+ const { PortalClient } = await import("./client-Dwy-uGNl.mjs");
18
18
  const { defaultTlsCertSource } = await import("./tls-cert-BpCaD5AT.mjs");
19
19
  let signalConnected;
20
20
  const connected = new Promise((resolve) => {
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as sendErrorMessage, n as errorDetail, r as filenameFromContentDisposition, t as createRestClient } from "./rest-BL0LvgFv.mjs";
2
+ import { i as sendErrorMessage, n as errorDetail, r as filenameFromContentDisposition, t as createRestClient } from "./rest-Benrf1yZ.mjs";
3
3
  import { t as HttpError } from "./http-error-BF2NZZE3.mjs";
4
4
  import "./billing-blocked-D3l5kJlX.mjs";
5
5
 
@@ -6,7 +6,7 @@ import { createParser } from "eventsource-parser";
6
6
 
7
7
  //#region package.json
8
8
  var name = "skydive-cli";
9
- var version = "0.6.0-beta.13";
9
+ var version = "0.6.0-beta.15";
10
10
 
11
11
  //#endregion
12
12
  //#region src/chat/util.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.6.0-beta.13",
3
+ "version": "0.6.0-beta.15",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -31,7 +31,7 @@
31
31
  "typecheck": "tsgo --noEmit"
32
32
  },
33
33
  "dependencies": {
34
- "@opentui/react": "0.5.8",
34
+ "@opentui/react": "0.4.3",
35
35
  "conf": "^13.0.1",
36
36
  "diff": "9.0.0",
37
37
  "eventsource-parser": "^3.0.8",
@@ -39,7 +39,7 @@
39
39
  "fuzzysort": "^3.1.0",
40
40
  "neverthrow": "^8.2.0",
41
41
  "open": "^10.1.0",
42
- "react": "^19.2.0",
42
+ "react": "^19.0.0",
43
43
  "react-devtools-core": "^7.0.1",
44
44
  "safe-stable-stringify": "^2.3.1",
45
45
  "semver": "^7.7.4",
@@ -71,9 +71,9 @@
71
71
  },
72
72
  "//optionalDependencies": "The per-platform binary packages (skydive-cli-<os>-<arch>) are INTENTIONALLY not committed here. They are published by release-skydive-cli-binaries.yml and injected — pinned to the exact version being published — into the published launcher's optionalDependencies at release time (scripts/prepare-platform-packages.mjs --pin-version, run in release-skydive-cli.yml). Committing them would make `yarn install` try to resolve versions that only exist post-publish, breaking local dev and CI. npm installs of a published skydive-cli still get them (os/cpu-gated); the launcher (src/launcher.ts) resolves and execs the matching one. Local dev never needs them: it runs the JS bundle or `build:binary` directly.",
73
73
  "optionalDependencies": {
74
- "skydive-cli-darwin-arm64": "0.6.0-beta.13",
75
- "skydive-cli-darwin-x64": "0.6.0-beta.13",
76
- "skydive-cli-linux-x64": "0.6.0-beta.13",
77
- "skydive-cli-linux-arm64": "0.6.0-beta.13"
74
+ "skydive-cli-darwin-arm64": "0.6.0-beta.15",
75
+ "skydive-cli-darwin-x64": "0.6.0-beta.15",
76
+ "skydive-cli-linux-x64": "0.6.0-beta.15",
77
+ "skydive-cli-linux-arm64": "0.6.0-beta.15"
78
78
  }
79
79
  }
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as PortalClient } from "./client-JCeJ0RAt.mjs";
3
- import "./api-BFQ4PQDA.mjs";
4
-
5
- export { PortalClient };
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-JCeJ0RAt.mjs";
3
- import "./tls-cert-Rua2oV7n.mjs";
4
- import "./api-BFQ4PQDA.mjs";
5
- import "./daemon-LnRwTN3W.mjs";
6
- import { t as PortalDaemonClient } from "./daemon-client-BoLMCnVn.mjs";
7
-
8
- export { PortalDaemonClient };