skydive-cli 0.6.0-beta.23 → 0.6.0-beta.25

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-CQ76yHFR.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-CHSPHoin.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-CdbqsofY.mjs";
2
+ import { c as version, s as name, t as createRestClient } from "./rest-DyrLMPnC.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-BjWK1R7z.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-cm5SdN8E.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-DJ0bPjqx.mjs";
8
+ import { a as resolveMachineIdentity } from "./client-pKI_QTmT.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-ccNQRdJd.mjs";
11
+ import { d as daemonPaths, i as queryDaemonStatus, n as ensureDaemonRunning, s as stopDaemon, u as PORTAL_DAEMON_FLAG } from "./daemon--k2fcWhI.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";
@@ -1586,7 +1586,7 @@ const importCommand = {
1586
1586
  process.exit(1);
1587
1587
  }
1588
1588
  }
1589
- const { runChat } = await import("./boot-BtEzGVMb.mjs");
1589
+ const { runChat } = await import("./boot-KM9tlCQv.mjs");
1590
1590
  await runChat({
1591
1591
  appUrl,
1592
1592
  sessionToken: session.value.sessionToken,
@@ -1616,7 +1616,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1616
1616
  printError(`${session.error.message} For non-interactive use, run \`skydive auth login --web\` first, or set SKYDIVE_SESSION_TOKEN.`);
1617
1617
  process.exit(1);
1618
1618
  }
1619
- const { connectMachineShare } = await import("./print-share-ZtOgZIWk.mjs");
1619
+ const { connectMachineShare } = await import("./print-share-C9wWXtly.mjs");
1620
1620
  const machineShare = await connectMachineShare({
1621
1621
  appUrl,
1622
1622
  sessionToken: session.value.sessionToken,
@@ -1624,7 +1624,7 @@ async function runImportPrintMode({ argv, appUrl }) {
1624
1624
  });
1625
1625
  const extra = (argv.print ?? "").trim();
1626
1626
  const prompt = buildImportSeedPrompt(process.cwd(), machineOsFromPlatform(process.platform)) + (extra ? `\n\nAdditional instructions: ${extra}` : "");
1627
- const { runPrint } = await import("./print-D79MKwnp.mjs");
1627
+ const { runPrint } = await import("./print-DxckoQq_.mjs");
1628
1628
  try {
1629
1629
  const result = await runPrint({
1630
1630
  appUrl,
@@ -2031,7 +2031,7 @@ const chatCommand = {
2031
2031
  sessionToken: auth.value.token,
2032
2032
  agentSelector: argv.agent ?? null
2033
2033
  });
2034
- const { runChat } = await import("./boot-BtEzGVMb.mjs");
2034
+ const { runChat } = await import("./boot-KM9tlCQv.mjs");
2035
2035
  await runChat({
2036
2036
  appUrl: auth.value.appUrl,
2037
2037
  sessionToken: auth.value.token,
@@ -2115,7 +2115,7 @@ async function runPrintMode({ argv, appUrl }) {
2115
2115
  printError(`${auth.error.message} For non-interactive use, run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
2116
2116
  process.exit(1);
2117
2117
  }
2118
- const { runPrint, readStdin } = await import("./print-D79MKwnp.mjs");
2118
+ const { runPrint, readStdin } = await import("./print-DxckoQq_.mjs");
2119
2119
  const workspaceId = await resolveWorkspaceFlag({
2120
2120
  appUrl,
2121
2121
  sessionToken: auth.value.token,
@@ -2145,7 +2145,7 @@ async function runPrintMode({ argv, appUrl }) {
2145
2145
  process.exit(1);
2146
2146
  }
2147
2147
  } else {
2148
- const { connectMachineShare } = await import("./print-share-ZtOgZIWk.mjs");
2148
+ const { connectMachineShare } = await import("./print-share-C9wWXtly.mjs");
2149
2149
  machineShare = await connectMachineShare({
2150
2150
  appUrl: auth.value.appUrl,
2151
2151
  sessionToken: auth.value.token,
@@ -2210,7 +2210,7 @@ const getCommand$1 = {
2210
2210
  printError(`${auth.error.message} Run \`skydive auth login\` first, or set SKYDIVE_API_KEY / SKYDIVE_SESSION_TOKEN.`);
2211
2211
  process.exit(1);
2212
2212
  }
2213
- const { messageGet } = await import("./print-D79MKwnp.mjs");
2213
+ const { messageGet } = await import("./print-DxckoQq_.mjs");
2214
2214
  try {
2215
2215
  const result = await messageGet({
2216
2216
  appUrl: auth.value.appUrl,
@@ -2413,7 +2413,7 @@ const switchCommand = {
2413
2413
  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.");
2414
2414
  process.exit(1);
2415
2415
  }
2416
- const { runWorkspacePicker } = await import("./boot-BtEzGVMb.mjs");
2416
+ const { runWorkspacePicker } = await import("./boot-KM9tlCQv.mjs");
2417
2417
  await runWorkspacePicker(session);
2418
2418
  return;
2419
2419
  }
@@ -2492,7 +2492,7 @@ const openCommand = {
2492
2492
  const agent = argv.agent ? resolveAgent$1((await fetchPortalDevices(session)).agents, argv.agent) : null;
2493
2493
  const cwd = argv.cwd ? path.resolve(argv.cwd) : process.cwd();
2494
2494
  const { machineName } = await resolveMachineIdentity(null);
2495
- const { PortalDaemonClient } = await import("./daemon-client-BvbBPCJF.mjs");
2495
+ const { PortalDaemonClient } = await import("./daemon-client-hu2vyzDR.mjs");
2496
2496
  let lastLine = "";
2497
2497
  let signalConnected;
2498
2498
  const connected = new Promise((resolve) => {
@@ -2849,8 +2849,8 @@ const sandboxCommand = {
2849
2849
  }).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`"),
2850
2850
  handler: async (argv) => {
2851
2851
  const session = requireSession(argv);
2852
- const { createRestClient } = await import("./rest-B5EVWw0j.mjs");
2853
- const { resolveAgent } = await import("./print-D79MKwnp.mjs");
2852
+ const { createRestClient } = await import("./rest-D_Bz9P6h.mjs");
2853
+ const { resolveAgent } = await import("./print-DxckoQq_.mjs");
2854
2854
  const client = createRestClient({
2855
2855
  appUrl: session.appUrl,
2856
2856
  sessionToken: session.sessionToken
@@ -3121,8 +3121,8 @@ const fsEditCommand = {
3121
3121
  handler: async (argv) => {
3122
3122
  const session = requireSession(argv);
3123
3123
  const remotePath = argv.path;
3124
- const { createRestClient } = await import("./rest-B5EVWw0j.mjs");
3125
- const { resolveAgent } = await import("./print-D79MKwnp.mjs");
3124
+ const { createRestClient } = await import("./rest-D_Bz9P6h.mjs");
3125
+ const { resolveAgent } = await import("./print-DxckoQq_.mjs");
3126
3126
  const client = createRestClient({
3127
3127
  appUrl: session.appUrl,
3128
3128
  sessionToken: session.sessionToken
@@ -4575,7 +4575,7 @@ if (process.argv.includes(UPDATE_WORKER_FLAG)) {
4575
4575
  process.exit(0);
4576
4576
  }
4577
4577
  if (process.argv.includes(PORTAL_DAEMON_FLAG)) {
4578
- const { runPortalDaemon } = await import("./daemon-BNxycRkd.mjs");
4578
+ const { runPortalDaemon } = await import("./daemon-C8z0gjiS.mjs");
4579
4579
  runPortalDaemon(process.argv);
4580
4580
  } else runCli();
4581
4581
  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-CQ76yHFR.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-CHSPHoin.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-CdbqsofY.mjs";
2
+ import { a as errorMessage, i as sendErrorMessage, n as errorDetail, o as isRecord, t as createRestClient } from "./rest-DyrLMPnC.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-BjWK1R7z.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-cm5SdN8E.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-DJ0bPjqx.mjs";
7
+ import { t as PortalClient } from "./client-pKI_QTmT.mjs";
8
8
  import { t as defaultTlsCertSource } from "./tls-cert-Rua2oV7n.mjs";
9
9
  import "./api-BFQ4PQDA.mjs";
10
- import "./daemon-ccNQRdJd.mjs";
10
+ import "./daemon--k2fcWhI.mjs";
11
11
  import { t as SandboxStream } from "./client-l6orYfqq.mjs";
12
- import { t as PortalDaemonClient } from "./daemon-client-1Nh5ITt9.mjs";
12
+ import { t as PortalDaemonClient } from "./daemon-client-BxNqcNzX.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";
@@ -3598,6 +3598,19 @@ function isAgentRunConversation(c) {
3598
3598
  return !humanChannels.some((human) => human === channel);
3599
3599
  }
3600
3600
  /**
3601
+ * Whether the signed-in user has their own participant row in this thread,
3602
+ * which is what per-user archive marks. An org admin's list includes every
3603
+ * thread in the org, so it can hold threads they never joined (a Slack thread
3604
+ * between a colleague and the agent, say); on those the server has no row to
3605
+ * mark and refuses, so the UI should not offer archive. Read from
3606
+ * the list row's `viewerParticipates`; an older server omits it, and a
3607
+ * brand-new conversation has no roster yet, so both read as true.
3608
+ */
3609
+ function viewerIsParticipant(c) {
3610
+ if (isNewConversation(c)) return true;
3611
+ return c.viewerParticipates ?? true;
3612
+ }
3613
+ /**
3601
3614
  * The channel a conversation belongs to when it isn't the web channel, else
3602
3615
  * null. A brand-new conversation is always web, and the list route reports the
3603
3616
  * web channel as either `'web'` or (legacy) null — both mean "post from here".
@@ -3892,6 +3905,7 @@ function ConversationHome({ agent, initialQuery, conversations, showAutomated, o
3892
3905
  const highlightRow = rows[clamped];
3893
3906
  const highlightArchived = highlightRow?.kind === "conv" && isArchived(highlightRow.hit.item);
3894
3907
  const highlightAgentRun = highlightRow?.kind === "conv" && isAgentRunConversation(highlightRow.hit.item);
3908
+ const highlightOutsider = highlightRow?.kind === "conv" && !viewerIsParticipant(highlightRow.hit.item);
3895
3909
  const confirmConv = confirmId !== null ? list.find((c) => c.id === confirmId) : void 0;
3896
3910
  const searchState = isSearching ? search?.state : void 0;
3897
3911
  const searchStatusSegment = !isSearching ? "" : searchState?.kind === "loading" || search === null ? "searching…" : searchState?.kind === "error" ? `search failed: ${searchState.message}` : list.length === 0 ? "no matches" : "";
@@ -3913,7 +3927,7 @@ function ConversationHome({ agent, initialQuery, conversations, showAutomated, o
3913
3927
  searchStatusSegment,
3914
3928
  enterHint,
3915
3929
  "/ search",
3916
- ...highlightAgentRun ? [] : [`ctrl+a ${highlightArchived ? "unarchive" : "archive"}`],
3930
+ ...highlightAgentRun || highlightOutsider ? [] : [`ctrl+a ${highlightArchived ? "unarchive" : "archive"}`],
3917
3931
  "ctrl+d delete",
3918
3932
  `tab ${showAutomated ? "hide" : "show"} agent runs`,
3919
3933
  `shift+tab ${showArchived ? "hide" : "show"} archived`,
@@ -4068,6 +4082,10 @@ function ConversationHome({ agent, initialQuery, conversations, showAutomated, o
4068
4082
  setError("agent runs can't be archived — ctrl+d deletes");
4069
4083
  return;
4070
4084
  }
4085
+ if (!viewerIsParticipant(row.hit.item)) {
4086
+ setError("you're not in this thread, so there's nothing to archive");
4087
+ return;
4088
+ }
4071
4089
  setError(null);
4072
4090
  toggleArchived(row.hit.item);
4073
4091
  }
@@ -10432,9 +10450,74 @@ function effortLabel(model, level) {
10432
10450
  return model.thinkingLevelLabels?.[level] ?? level;
10433
10451
  }
10434
10452
 
10453
+ //#endregion
10454
+ //#region src/chat/tui/screens/model-floor.ts
10455
+ /** No floor on offer: the picker behaves as it did before the floor existed. */
10456
+ const NO_FREE_FLOOR = {
10457
+ freeModels: null,
10458
+ lock: null
10459
+ };
10460
+ /**
10461
+ * Derives the picker's floor view from the org's billing summary and the
10462
+ * floor's live state, with the same verdicts as the web switcher
10463
+ * (`use-models.ts`), so the two pickers never disagree about what is free
10464
+ * or locked.
10465
+ *
10466
+ * The floor is offered to ENFORCED orgs only (with enforcement off no gate
10467
+ * runs, so nothing is free and nothing is locked), and only while
10468
+ * `available`: in the capped state the gate refuses floor runs too, so the
10469
+ * picker falls back to its plain treatment instead of tagging models free
10470
+ * that the gate would bill.
10471
+ */
10472
+ function resolveFreeFloor({ summary, floor }) {
10473
+ if (!summary.billingEnforcementEnabled || floor === null || !floor.available) return NO_FREE_FLOOR;
10474
+ const freeModels = new Set(floor.models);
10475
+ return {
10476
+ freeModels,
10477
+ lock: summary.blockReason === "insufficient_balance" ? {
10478
+ models: freeModels,
10479
+ pinnedModelId: floor.models[0] ?? null,
10480
+ onPaidPlan: summary.planKey !== null && summary.planKey !== "free"
10481
+ } : null
10482
+ };
10483
+ }
10484
+ /**
10485
+ * The model the picker marks as current. Under the lock the gate pins any
10486
+ * off-floor selection to the floor, so the marker follows the pin: the saved
10487
+ * model when it is on the floor, else the pinned floor model. Mirrors the
10488
+ * server (`resolveFreeFloor` pins off-floor requests) so the row with the
10489
+ * marker is the model the next run really uses.
10490
+ */
10491
+ function effectiveCurrentModel({ currentModel, lock }) {
10492
+ if (lock === null) return currentModel;
10493
+ return currentModel !== null && lock.models.has(currentModel) ? currentModel : lock.pinnedModelId;
10494
+ }
10495
+
10435
10496
  //#endregion
10436
10497
  //#region src/chat/tui/screens/model-picker.tsx
10437
10498
  /**
10499
+ * The free floor's view for the picker (ANY-8042 / ANY-8661), or none. Both
10500
+ * reads are best-effort and fail OPEN together: a billing read that errors
10501
+ * (an api that predates the routes, a session with no active org, a
10502
+ * transient failure) leaves the picker exactly as it was before the floor
10503
+ * existed. A fetch error must never lock a paying org's picker; the run gate
10504
+ * stays authoritative either way. The two reads run alongside each other and
10505
+ * the floor is discarded for a non-enforced org inside `resolveFreeFloor`,
10506
+ * which costs one small query on such orgs but keeps the picker's open to a
10507
+ * single round-trip.
10508
+ */
10509
+ async function loadFreeFloor(rest) {
10510
+ try {
10511
+ const [summary, floor] = await Promise.all([rest.getBillingSummary(), rest.getFreeFloorState()]);
10512
+ return resolveFreeFloor({
10513
+ summary,
10514
+ floor
10515
+ });
10516
+ } catch (_error) {
10517
+ return NO_FREE_FLOOR;
10518
+ }
10519
+ }
10520
+ /**
10438
10521
  * A 404 from the pin route means the server predates it, not that the pick
10439
10522
  * failed: a canary CLI publishes minutes after its release push while the
10440
10523
  * API deploy on that same push takes far longer (ANY-8191, ANY-8202), so a
@@ -10481,12 +10564,17 @@ function ModelPicker({ agentId, conversationId, currentModel, currentEffort, mod
10481
10564
  let cancelled = false;
10482
10565
  (async () => {
10483
10566
  try {
10484
- const [models, savedEfforts] = await Promise.all([rest.listModels(), rest.listModelEfforts().catch(() => ({}))]);
10567
+ const [models, savedEfforts, floor] = await Promise.all([
10568
+ rest.listModels(),
10569
+ rest.listModelEfforts().catch(() => ({})),
10570
+ loadFreeFloor(rest)
10571
+ ]);
10485
10572
  if (cancelled) return;
10486
10573
  setState({
10487
10574
  kind: "ready",
10488
10575
  models,
10489
- savedEfforts
10576
+ savedEfforts,
10577
+ floor
10490
10578
  });
10491
10579
  } catch (err) {
10492
10580
  if (cancelled) return;
@@ -10517,6 +10605,7 @@ function ModelPicker({ agentId, conversationId, currentModel, currentEffort, mod
10517
10605
  return /* @__PURE__ */ jsx(ModelSwitcher, {
10518
10606
  models: state.models,
10519
10607
  initialSavedEfforts: state.savedEfforts,
10608
+ floor: state.floor,
10520
10609
  currentModel,
10521
10610
  currentEffort,
10522
10611
  modelLocked,
@@ -10554,7 +10643,7 @@ function ModelPickerFrame({ hint, children }) {
10554
10643
  ]
10555
10644
  });
10556
10645
  }
10557
- function ModelSwitcher({ models, initialSavedEfforts, currentModel, currentEffort, modelLocked, initialView, agentId, conversationId, onModelSelected, onEffortSelected, onCancel }) {
10646
+ function ModelSwitcher({ models, initialSavedEfforts, floor, currentModel, currentEffort, modelLocked, initialView, agentId, conversationId, onModelSelected, onEffortSelected, onCancel }) {
10558
10647
  const rest = useStore((s) => s.rest);
10559
10648
  const [savedEfforts, setSavedEfforts] = useState(initialSavedEfforts);
10560
10649
  const [view, setView] = useState(() => {
@@ -10669,6 +10758,7 @@ function ModelSwitcher({ models, initialSavedEfforts, currentModel, currentEffor
10669
10758
  });
10670
10759
  return /* @__PURE__ */ jsx(ModelList, {
10671
10760
  models,
10761
+ floor,
10672
10762
  currentModel,
10673
10763
  modelLocked,
10674
10764
  saving,
@@ -10685,10 +10775,26 @@ function ModelSwitcher({ models, initialSavedEfforts, currentModel, currentEffor
10685
10775
  onCancel
10686
10776
  });
10687
10777
  }
10688
- function ModelList({ models, currentModel, modelLocked, saving, saveError, effortFor, onSelect, onEditEffort, onCancel }) {
10778
+ function ModelList({ models, floor, currentModel, modelLocked, saving, saveError, effortFor, onSelect, onEditEffort, onCancel }) {
10689
10779
  const { width, height } = useTerminalDimensions();
10690
10780
  const [query, setQuery] = useState("");
10691
- const [highlight, setHighlight] = useState(0);
10781
+ const { lock } = floor;
10782
+ const markedModel = effectiveCurrentModel({
10783
+ currentModel,
10784
+ lock
10785
+ });
10786
+ const [highlight, setHighlight] = useState(() => {
10787
+ if (lock === null) return 0;
10788
+ const index = models.findIndex((m) => m.id === markedModel);
10789
+ return index < 0 ? 0 : index;
10790
+ });
10791
+ /**
10792
+ * Locked rows stay visible (so the user sees what credits would unlock),
10793
+ * dimmed, navigable, and answer to no key but movement; the status line
10794
+ * says why while one is highlighted. No per-row marker: on a long catalog
10795
+ * it wraps every row for a fact the notice already states.
10796
+ */
10797
+ const isLocked = (model) => lock !== null && !lock.models.has(model.id);
10692
10798
  const hits = fuzzyFilter(query, models, modelKeys);
10693
10799
  const filtered = hits.map((h) => h.item);
10694
10800
  const clamped = Math.min(highlight, Math.max(0, filtered.length - 1));
@@ -10705,15 +10811,16 @@ function ModelList({ models, currentModel, modelLocked, saving, saveError, effor
10705
10811
  else if (key.name === "down") setHighlight(Math.min(Math.max(0, filtered.length - 1), clamped + 1));
10706
10812
  else if (key.name === "tab") {
10707
10813
  const model = filtered[clamped];
10708
- if (model && effortFor(model)) onEditEffort(model);
10814
+ if (model && !isLocked(model) && effortFor(model)) onEditEffort(model);
10709
10815
  } else if (key.name === "return") {
10710
10816
  const model = filtered[clamped];
10711
- if (model) onSelect(model);
10817
+ if (model && !isLocked(model)) onSelect(model);
10712
10818
  }
10713
10819
  });
10714
10820
  const highlighted = filtered[clamped];
10715
- const canEditEffort = !!highlighted && !!effortFor(highlighted);
10716
- const statusLine = saving ? "saving…" : saveError ? saveError : `${filtered.length}/${models.length} · ↑/↓ move · ↵ select${canEditEffort ? " · tab effort" : ""} · esc cancel`;
10821
+ const highlightedLocked = !!highlighted && isLocked(highlighted);
10822
+ const canEditEffort = !!highlighted && !highlightedLocked && !!effortFor(highlighted);
10823
+ const statusLine = saving ? "saving…" : saveError ? saveError : `${filtered.length}/${models.length} · ↑/↓ move · ${highlightedLocked ? "↵ needs credits" : "↵ select"}${canEditEffort ? " · tab effort" : ""} · esc cancel`;
10717
10824
  return /* @__PURE__ */ jsxs("box", {
10718
10825
  style: {
10719
10826
  flexDirection: "column",
@@ -10728,7 +10835,19 @@ function ModelList({ models, currentModel, modelLocked, saving, saveError, effor
10728
10835
  fg: theme.warning,
10729
10836
  children: "model choice is limited while this agent uses a Google integration. models without a zero-data-retention route will be rejected."
10730
10837
  }) : null,
10731
- /* @__PURE__ */ jsxs("box", {
10838
+ lock !== null ? /* @__PURE__ */ jsxs("box", {
10839
+ style: {
10840
+ flexDirection: "column",
10841
+ marginTop: 1
10842
+ },
10843
+ children: [/* @__PURE__ */ jsx("text", {
10844
+ fg: theme.fg,
10845
+ children: lock.onPaidPlan ? "You're out of credits" : "You're on the Free plan"
10846
+ }), /* @__PURE__ */ jsx("text", {
10847
+ fg: theme.muted,
10848
+ children: "Some models are unavailable."
10849
+ })]
10850
+ }) : /* @__PURE__ */ jsxs("box", {
10732
10851
  style: {
10733
10852
  flexDirection: "row",
10734
10853
  height: 1,
@@ -10766,10 +10885,11 @@ function ModelList({ models, currentModel, modelLocked, saving, saveError, effor
10766
10885
  }) : windowed.map((hit, i) => {
10767
10886
  const m = hit.item;
10768
10887
  const selected = start + i === clamped;
10769
- const isCurrent = m.id === currentModel;
10888
+ const isCurrent = m.id === markedModel;
10889
+ const locked = isLocked(m);
10770
10890
  const effort = effortFor(m);
10771
10891
  return /* @__PURE__ */ jsxs("text", {
10772
- fg: selected ? theme.accent : theme.fg,
10892
+ fg: selected ? theme.accent : locked ? theme.dim : theme.fg,
10773
10893
  children: [
10774
10894
  selected ? "› " : " ",
10775
10895
  isCurrent ? "● " : " ",
@@ -10789,6 +10909,10 @@ function ModelList({ models, currentModel, modelLocked, saving, saveError, effor
10789
10909
  effort ? /* @__PURE__ */ jsx("span", {
10790
10910
  fg: theme.dim,
10791
10911
  children: ` ${effortLabel(m, effort)}`
10912
+ }) : null,
10913
+ floor.freeModels?.has(m.id) ? /* @__PURE__ */ jsx("span", {
10914
+ fg: theme.success,
10915
+ children: " free"
10792
10916
  }) : null
10793
10917
  ]
10794
10918
  }, m.id);
@@ -15087,6 +15211,10 @@ function ChatScreen({ agent, conversation, attachRunId, parentConversationId })
15087
15211
  useStore.getState().showToast({ message: "agent runs can't be archived — ctrl+d in the picker deletes" });
15088
15212
  return;
15089
15213
  }
15214
+ if (!viewerIsParticipant(conversation)) {
15215
+ useStore.getState().showToast({ message: "you're not in this thread, so there's nothing to archive" });
15216
+ return;
15217
+ }
15090
15218
  if (runRef.current.kind !== "idle") {
15091
15219
  useStore.getState().showToast({ message: "finish or cancel the run first (ctrl+c)" });
15092
15220
  return;
@@ -15120,6 +15248,7 @@ function ChatScreen({ agent, conversation, attachRunId, parentConversationId })
15120
15248
  askPrompt,
15121
15249
  restartConfirm,
15122
15250
  agentRunConversation,
15251
+ conversation,
15123
15252
  conversationId,
15124
15253
  rest,
15125
15254
  goTo,
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { t as PortalClient } from "./client-pKI_QTmT.mjs";
3
+ import "./api-BFQ4PQDA.mjs";
4
+
5
+ export { PortalClient };
@@ -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 "1789516264";
163
+ return "1789594940";
164
164
  }
165
165
  /**
166
166
  * Whether a client carrying `mine` should replace a running daemon carrying
@@ -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-DJ0bPjqx.mjs";
2
+ import { i as isRecord, n as isNewerBuild, r as portalDaemonBuild, t as PortalClient } from "./client-pKI_QTmT.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-DJ0bPjqx.mjs";
2
+ import "./client-pKI_QTmT.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-ccNQRdJd.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--k2fcWhI.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-ccNQRdJd.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--k2fcWhI.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-pKI_QTmT.mjs";
3
+ import "./tls-cert-Rua2oV7n.mjs";
4
+ import "./api-BFQ4PQDA.mjs";
5
+ import "./daemon--k2fcWhI.mjs";
6
+ import { t as PortalDaemonClient } from "./daemon-client-BxNqcNzX.mjs";
7
+
8
+ export { PortalDaemonClient };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { c as version$1 } from "./rest-CQ76yHFR.mjs";
3
- import { E as getConfigPath } from "./print-CHSPHoin.mjs";
2
+ import { c as version$1 } from "./rest-DyrLMPnC.mjs";
3
+ import { E as getConfigPath } from "./print-BjWK1R7z.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-CQ76yHFR.mjs";
2
+ import { a as errorMessage, i as sendErrorMessage, o as isRecord, t as createRestClient } from "./rest-DyrLMPnC.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-CQ76yHFR.mjs";
3
- import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-CHSPHoin.mjs";
2
+ import "./rest-DyrLMPnC.mjs";
3
+ import { a as runPrint, i as resolveAgent, n as messageGet, o as toPrintError, r as readStdin, t as collectRunText } from "./print-BjWK1R7z.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-rIw2WPPz.mjs");
17
+ const { PortalClient } = await import("./client-LsfcGY1X.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-CQ76yHFR.mjs";
2
+ import { i as sendErrorMessage, n as errorDetail, r as filenameFromContentDisposition, t as createRestClient } from "./rest-DyrLMPnC.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.23";
9
+ var version = "0.6.0-beta.25";
10
10
 
11
11
  //#endregion
12
12
  //#region src/chat/util.ts
@@ -279,6 +279,14 @@ function createRestClient({ appUrl, sessionToken, workspaceId }) {
279
279
  for (const [modelId, config] of Object.entries(modelConfigOverrides ?? {})) if (config.thinkingLevel) efforts[modelId] = config.thinkingLevel;
280
280
  return efforts;
281
281
  },
282
+ getBillingSummary: async () => {
283
+ const { result } = await get("/api/v1/trpc/v2.billing.getBillingOrgSummary", billingSummaryResponseSchema);
284
+ return result.data;
285
+ },
286
+ getFreeFloorState: async () => {
287
+ const { result } = await get("/api/v1/trpc/v2.billing.freeFloorState", freeFloorStateResponseSchema);
288
+ return result.data;
289
+ },
282
290
  setModelEffort: async ({ modelId, thinkingLevel }) => {
283
291
  await post("/api/v1/models/config", {
284
292
  modelId,
@@ -546,6 +554,16 @@ const platformModelSchema = z.object({
546
554
  }).passthrough();
547
555
  const listModelsResponseSchema = z.object({ models: z.array(platformModelSchema) });
548
556
  const modelEffortPreferencesResponseSchema = z.object({ modelConfigOverrides: z.record(z.string(), z.object({ thinkingLevel: z.string().optional() })).optional() });
557
+ const billingSummaryResponseSchema = z.object({ result: z.object({ data: z.object({
558
+ planKey: z.string().nullable(),
559
+ blockReason: z.string().nullable(),
560
+ billingEnforcementEnabled: z.boolean()
561
+ }) }) });
562
+ const freeFloorStateResponseSchema = z.object({ result: z.object({ data: z.object({
563
+ available: z.boolean(),
564
+ models: z.array(z.string()),
565
+ resetsAt: z.string().nullable()
566
+ }).nullable() }) });
549
567
  const conversationModelResponseSchema = z.object({
550
568
  model: z.string().nullable(),
551
569
  thinkingLevel: z.string().nullable()
@@ -582,6 +600,7 @@ const conversationSummarySchema = z.object({
582
600
  channel: z.string().nullable(),
583
601
  channelLabel: z.string().nullable(),
584
602
  viewerArchivedAt: z.string().nullable().optional(),
603
+ viewerParticipates: z.boolean().optional(),
585
604
  unread: z.boolean().optional(),
586
605
  agent: conversationAgentSchema,
587
606
  agents: z.array(conversationAgentSchema).optional()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skydive-cli",
3
- "version": "0.6.0-beta.23",
3
+ "version": "0.6.0-beta.25",
4
4
  "description": "Skydive CLI — cloud agents from the command line",
5
5
  "homepage": "https://skydive.com",
6
6
  "license": "MIT",
@@ -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.23",
75
- "skydive-cli-darwin-x64": "0.6.0-beta.23",
76
- "skydive-cli-linux-x64": "0.6.0-beta.23",
77
- "skydive-cli-linux-arm64": "0.6.0-beta.23"
74
+ "skydive-cli-darwin-arm64": "0.6.0-beta.25",
75
+ "skydive-cli-darwin-x64": "0.6.0-beta.25",
76
+ "skydive-cli-linux-x64": "0.6.0-beta.25",
77
+ "skydive-cli-linux-arm64": "0.6.0-beta.25"
78
78
  }
79
79
  }
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as PortalClient } from "./client-DJ0bPjqx.mjs";
3
- import "./api-BFQ4PQDA.mjs";
4
-
5
- export { PortalClient };
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./client-DJ0bPjqx.mjs";
3
- import "./tls-cert-Rua2oV7n.mjs";
4
- import "./api-BFQ4PQDA.mjs";
5
- import "./daemon-ccNQRdJd.mjs";
6
- import { t as PortalDaemonClient } from "./daemon-client-1Nh5ITt9.mjs";
7
-
8
- export { PortalDaemonClient };