@standardagents/code 0.11.2 → 0.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -579,14 +579,24 @@ var ApiClient = class {
579
579
  // Backed by the instance's /api/users/me/kv store. This is where the machine
580
580
  // registry lives: which machines (laptops / VPS daemons) belong to this
581
581
  // account, and which projects each machine has.
582
- /** Read one value from the signed-in user's account-wide KV (null if absent). */
583
- async userKvGet(key) {
582
+ /**
583
+ * Read one value from the signed-in user's account-wide KV (null if absent).
584
+ * A missing key is a 200 `{value: null}` from the instance, so any THROWN
585
+ * error is a transport/server failure, never "not found". `strict` rethrows
586
+ * those failures — required by read-modify-write callers (the machine
587
+ * registry), where treating a failed read as "no record yet" would overwrite
588
+ * the real record with a fresh empty one (this wiped a machine's whole
589
+ * project list during a deploy window). Non-strict callers (pollers,
590
+ * display overlays) keep the lenient null.
591
+ */
592
+ async userKvGet(key, opts) {
584
593
  try {
585
594
  const res = await this.json(
586
595
  `/api/users/me/kv?key=${encodeURIComponent(key)}`
587
596
  );
588
597
  return res?.value ?? null;
589
- } catch {
598
+ } catch (error) {
599
+ if (opts?.strict) throw error;
590
600
  return null;
591
601
  }
592
602
  }
@@ -6587,8 +6597,8 @@ function parseMachineRecord(value) {
6587
6597
  updated_at: typeof r.updated_at === "number" ? r.updated_at : 0
6588
6598
  };
6589
6599
  }
6590
- async function loadRawMachine(api, machineId) {
6591
- return parseMachineRecord(await api.userKvGet(machineKey(machineId)));
6600
+ async function loadRawMachine(api, machineId, opts) {
6601
+ return parseMachineRecord(await api.userKvGet(machineKey(machineId), opts));
6592
6602
  }
6593
6603
  async function loadMachines(api) {
6594
6604
  const entries = await api.userKvList(KEY_PREFIX);
@@ -6715,7 +6725,7 @@ function newRecord(identity) {
6715
6725
  };
6716
6726
  }
6717
6727
  async function updateOwnMachineRecord(api, identity, mutate) {
6718
- const existing = await loadRawMachine(api, identity.machine_id);
6728
+ const existing = await loadRawMachine(api, identity.machine_id, { strict: true });
6719
6729
  const record2 = existing ?? newRecord(identity);
6720
6730
  record2.hostname = os8.hostname();
6721
6731
  record2.platform = process.platform;
@@ -7023,7 +7033,7 @@ async function pickDirectory(tui, backend, opts = {}) {
7023
7033
  return null;
7024
7034
  }
7025
7035
  spin?.stop();
7026
- const title = () => `${c2.bold}${gradientText(`Browse ${backend.label}`)}${c2.reset} ${c2.teal}${tail(listing.path, 48)}${c2.reset}` + (showHidden ? ` ${c2.dim}\xB7 hidden shown${c2.reset}` : "");
7036
+ const title = () => `${c2.bold}Browse ${backend.label}${c2.reset} ${c2.teal}${tail(listing.path, 48)}${c2.reset}` + (showHidden ? ` ${c2.dim}\xB7 hidden shown${c2.reset}` : "");
7027
7037
  const footer = (error) => error ? `${c2.yellow}${error}${c2.reset} ${c2.dim}${KEY_HINTS}${c2.reset}` : `${c2.dim}${KEY_HINTS}${c2.reset}`;
7028
7038
  const buildItems = () => {
7029
7039
  const pageSize = Math.max(5, Math.min(14, (process.stdout.rows || 24) - 9));
@@ -8625,7 +8635,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8625
8635
  if (result.hint) stdout.write(` ${c4.dim}${result.hint}${c4.reset}
8626
8636
  `);
8627
8637
  };
8628
- stdout.write(`${c4.bold}${gradientText("Sign in to Standard Code")}${c4.reset}
8638
+ stdout.write(`${c4.bold}Sign in to ${gradientText("Standard Code")}${c4.reset}
8629
8639
  `);
8630
8640
  if (`https://${host}` !== PRODUCTION_ENDPOINT) {
8631
8641
  stdout.write(`${c4.dim}Connecting to${c4.reset} ${c4.teal}${host}${c4.reset}
@@ -8737,7 +8747,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8737
8747
  flow: for (; ; ) {
8738
8748
  if (step === "machine") {
8739
8749
  const picked = await tui.select(
8740
- `${c4.bold}${gradientText("Where should this session run?")}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8750
+ `${c4.bold}Where should this session run?${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8741
8751
  [
8742
8752
  {
8743
8753
  label: `This machine \u2014 ${self ? machineDisplayName(self) : machine}`,
@@ -8821,7 +8831,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8821
8831
  items.push({ label: "\uFF0B Start a new session", value: null });
8822
8832
  const whereLabel = session.mode === "remote" && session.runner ? `${session.runner.name}:${shortenPath(session.remotePath ?? "")}` : shortDir;
8823
8833
  const picked = await tui.select(
8824
- `${c4.bold}${gradientText("Resume a session")}${c4.reset} ${c4.gray}${whereLabel}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8834
+ `${c4.bold}Resume a session${c4.reset} ${c4.gray}${whereLabel}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8825
8835
  items
8826
8836
  );
8827
8837
  if (picked === void 0) {
@@ -8847,7 +8857,7 @@ ${c4.dim}Press Control-C again to exit${c4.reset}
8847
8857
  } else {
8848
8858
  if (!agentOverride) {
8849
8859
  const picked = await tui.select(
8850
- `${c4.bold}${gradientText("Which agent?")}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8860
+ `${c4.bold}Which agent?${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8851
8861
  AGENT_CHOICES.map((choice) => ({
8852
8862
  label: choice.title,
8853
8863
  hint: choice.description,
@@ -8897,7 +8907,7 @@ async function ensureSamaAuth(tui, api) {
8897
8907
  checking.stop();
8898
8908
  if (already) return true;
8899
8909
  const picked = await tui.select(
8900
- `${c4.bold}${gradientText("Authenticate with ChatGPT")}${c4.reset} ${c4.dim}Sama One runs on OpenAI using your own ChatGPT Pro account${c4.reset}`,
8910
+ `${c4.bold}Authenticate with ChatGPT${c4.reset} ${c4.dim}Sama One runs on OpenAI using your own ChatGPT Pro account${c4.reset}`,
8901
8911
  [
8902
8912
  {
8903
8913
  label: "Continue with ChatGPT",
@@ -8932,7 +8942,7 @@ async function ensureSamaAuth(tui, api) {
8932
8942
  }
8933
8943
  async function runAgentSwitchMenu(tui, api, threadId) {
8934
8944
  const picked = await tui.select(
8935
- `${c4.bold}${gradientText("Switch agent")}${c4.reset} ${c4.dim}takes effect on the next message${c4.reset}`,
8945
+ `${c4.bold}Switch agent${c4.reset} ${c4.dim}takes effect on the next message${c4.reset}`,
8936
8946
  AGENT_CHOICES.map((choice) => ({
8937
8947
  label: choice.title,
8938
8948
  hint: choice.description,
@@ -8977,7 +8987,7 @@ async function pickLocalProject(tui, api, self, cwd, machineName) {
8977
8987
  { label: "\uFF0B New project", hint: "browse or create a directory", value: NEW }
8978
8988
  ];
8979
8989
  const picked = await tui.select(
8980
- `${c4.bold}${gradientText(`Project on ${label}`)}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8990
+ `${c4.bold}Project on ${label}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
8981
8991
  items,
8982
8992
  { spaced: true }
8983
8993
  );
@@ -9001,7 +9011,7 @@ async function pickRemoteProject(tui, api, runner) {
9001
9011
  );
9002
9012
  items.push({ label: "\uFF0B New project", hint: "browse or create a directory", value: ENTER_PATH });
9003
9013
  const picked = await tui.select(
9004
- `${c4.bold}${gradientText(`Project on ${runner.name}`)}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9014
+ `${c4.bold}Project on ${runner.name}${c4.reset} ${c4.dim}\u2191\u2193 \xB7 enter \xB7 esc${c4.reset}`,
9005
9015
  items,
9006
9016
  { spaced: true }
9007
9017
  );
@@ -9080,6 +9090,7 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
9080
9090
  const runnerName = session.runner?.name ?? "the remote machine";
9081
9091
  let currentOwner = null;
9082
9092
  let ownershipKnown = false;
9093
+ let ownershipWasOurs = false;
9083
9094
  const describeExecOwner = (o) => o?.client_kind === "daemon" ? "the daemon on this machine" : o?.client_name || "another client";
9084
9095
  const registry = new ProcessRegistry(api, threadId, machine);
9085
9096
  const refreshBgCount = () => {
@@ -9168,13 +9179,15 @@ async function runInteractive(tui, api, threadId, projectDir, machine, resumed,
9168
9179
  },
9169
9180
  onOwnership: (isOwner, owner2) => {
9170
9181
  const wasKnown = ownershipKnown;
9182
+ const moved = !wasKnown || ownershipWasOurs !== isOwner || (currentOwner?.client_id ?? null) !== (owner2?.client_id ?? null);
9171
9183
  ownershipKnown = true;
9184
+ ownershipWasOurs = isOwner;
9172
9185
  currentOwner = owner2;
9173
9186
  if (isOwner) {
9174
9187
  bridge?.setClaim("takeover");
9175
9188
  exec?.writeSessionInfo();
9176
- if (wasKnown) tui.print(`${c4.dim}Tool execution moved to this terminal.${c4.reset}`);
9177
- } else if (wasKnown) {
9189
+ if (wasKnown && moved) tui.print(`${c4.dim}Tool execution moved to this terminal.${c4.reset}`);
9190
+ } else if (wasKnown && moved) {
9178
9191
  tui.print(`${c4.dim}Tool execution moved to ${describeExecOwner(owner2)}.${c4.reset}`);
9179
9192
  }
9180
9193
  },
@@ -9577,7 +9590,7 @@ why: ${req.requestPermission}` : ""}`,
9577
9590
  const cost = fmtCost(q);
9578
9591
  const lines = [
9579
9592
  "",
9580
- `${c4.bold}${gradientText("\u2726 Add a parallel session")}${c4.reset}`,
9593
+ `${c4.bold}\u2726 Add a parallel session${c4.reset}`,
9581
9594
  "",
9582
9595
  `${dots.join(" ")} ${c4.dim}${q.current} of ${q.current} session${q.current === 1 ? "" : "s"} in use${c4.reset}`
9583
9596
  ];