@sailorbridge/client 0.2.2 → 0.2.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/src/cli.js CHANGED
@@ -2,10 +2,10 @@
2
2
  import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
- // .dist-releases/1784862378006-1249051/src/cli.js
5
+ // .dist-releases/1784894032002-2676534/src/cli.js
6
6
  import { hostname as hostname3 } from "node:os";
7
7
  import { writeSync } from "node:fs";
8
- // .dist-releases/1784862378006-1249051/src/auth/host-pairing.js
8
+ // .dist-releases/1784894032002-2676534/src/auth/host-pairing.js
9
9
  import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
10
10
  import { homedir, hostname, platform } from "node:os";
11
11
  import path from "node:path";
@@ -220,7 +220,7 @@ function _assertObject(value) {
220
220
  throw new Error("host pairing response must be an object");
221
221
  return value;
222
222
  }
223
- // .dist-releases/1784862378006-1249051/src/host/repo-tip-reporter.js
223
+ // .dist-releases/1784894032002-2676534/src/host/repo-tip-reporter.js
224
224
  import { createHash } from "node:crypto";
225
225
  import { existsSync, mkdirSync } from "node:fs";
226
226
  import { homedir as homedir2 } from "node:os";
@@ -402,10 +402,10 @@ ${error.message}`));
402
402
  proc.on("close", (code) => finish(code ?? 1));
403
403
  });
404
404
  }
405
- // .dist-releases/1784862378006-1249051/src/host/request-consumer.js
405
+ // .dist-releases/1784894032002-2676534/src/host/request-consumer.js
406
406
  import { hostname as hostname2 } from "node:os";
407
407
 
408
- // .dist-releases/1784862378006-1249051/src/driver/secrets.js
408
+ // .dist-releases/1784894032002-2676534/src/driver/secrets.js
409
409
  function redactSensitiveText(text, env = {}, explicitSecrets = []) {
410
410
  let redacted = text;
411
411
  for (const [key, value] of Object.entries(env)) {
@@ -424,16 +424,16 @@ function isSensitiveEnvKey(key) {
424
424
  return /(TOKEN|KEY|SECRET|AUTHORIZATION|PASSWORD)/i.test(key);
425
425
  }
426
426
 
427
- // .dist-releases/1784862378006-1249051/src/version.js
428
- var CLIENT_VERSION = "0.2.2";
429
- var CLIENT_GIT_SHA = "f943ffe8e731-dirty";
430
- var CLIENT_BUILD_DIRTY = true;
427
+ // .dist-releases/1784894032002-2676534/src/version.js
428
+ var CLIENT_VERSION = "0.2.4";
429
+ var CLIENT_GIT_SHA = "22d3b8376a97";
430
+ var CLIENT_BUILD_DIRTY = false;
431
431
  var CLIENT_VERSION_LABEL = formatClientVersion(CLIENT_VERSION, CLIENT_GIT_SHA);
432
432
  function formatClientVersion(version, gitSha) {
433
433
  return `${version}+git.${gitSha}`;
434
434
  }
435
435
 
436
- // .dist-releases/1784862378006-1249051/src/host/request-consumer.js
436
+ // .dist-releases/1784894032002-2676534/src/host/request-consumer.js
437
437
  var MAX_ERROR_LENGTH = 500;
438
438
  function defaultHostIdentity(config) {
439
439
  const captainId = config.captainId?.trim();
@@ -455,7 +455,8 @@ async function runHostRequestOnce(options) {
455
455
  hostId: options.hostId,
456
456
  displayName: options.displayName,
457
457
  clientVersion: CLIENT_VERSION,
458
- clientGitSha: CLIENT_GIT_SHA
458
+ clientGitSha: CLIENT_GIT_SHA,
459
+ ...options.updateError === undefined ? {} : { updateError: options.updateError }
459
460
  });
460
461
  if (options.workspaceInventory) {
461
462
  try {
@@ -488,7 +489,7 @@ function limitErrorMessage(error, explicitSecrets) {
488
489
  const trimmed = redactSensitiveText(message, process.env, explicitSecrets).trim() || "host request starter failed";
489
490
  return trimmed.length <= MAX_ERROR_LENGTH ? trimmed : `${trimmed.slice(0, MAX_ERROR_LENGTH - 3)}...`;
490
491
  }
491
- // .dist-releases/1784862378006-1249051/src/product/start-request.js
492
+ // .dist-releases/1784894032002-2676534/src/product/start-request.js
492
493
  async function createAgentStartRequest(options) {
493
494
  const apiUrl = _required(options.apiUrl, "product API URL").replace(/\/+$/u, "");
494
495
  const accessToken = _required(options.accessToken, "product API bearer token");
@@ -548,7 +549,7 @@ function _string(value, label) {
548
549
  throw new Error(`${label} must be a non-empty string`);
549
550
  return value;
550
551
  }
551
- // .dist-releases/1784862378006-1249051/src/product/pair.js
552
+ // .dist-releases/1784894032002-2676534/src/product/pair.js
552
553
  async function createPair(options) {
553
554
  const apiUrl = _required2(options.apiUrl, "product API URL").replace(/\/+$/u, "");
554
555
  const accessToken = _required2(options.accessToken, "product API bearer token");
@@ -625,7 +626,7 @@ function _string2(value, label) {
625
626
  throw new Error(`${label} must be a non-empty string`);
626
627
  return value;
627
628
  }
628
- // .dist-releases/1784862378006-1249051/src/product/worker.js
629
+ // .dist-releases/1784894032002-2676534/src/product/worker.js
629
630
  async function createWorker(options) {
630
631
  const apiUrl = _required3(options.apiUrl, "product API URL").replace(/\/+$/u, "");
631
632
  const accessToken = _required3(options.accessToken, "product API bearer token");
@@ -689,7 +690,7 @@ function _string3(value, label) {
689
690
  throw new Error(`${label} must be a non-empty string`);
690
691
  return value;
691
692
  }
692
- // .dist-releases/1784862378006-1249051/src/driver/events.js
693
+ // .dist-releases/1784894032002-2676534/src/driver/events.js
693
694
  class DriverEventEmitter {
694
695
  callbacks = new Map;
695
696
  on(type, callback) {
@@ -704,7 +705,7 @@ class DriverEventEmitter {
704
705
  }
705
706
  }
706
707
  }
707
- // .dist-releases/1784862378006-1249051/src/driver/async-queue.js
708
+ // .dist-releases/1784894032002-2676534/src/driver/async-queue.js
708
709
  class AsyncQueue {
709
710
  values = [];
710
711
  waiters = [];
@@ -743,7 +744,7 @@ class AsyncQueue {
743
744
  }
744
745
  }
745
746
 
746
- // .dist-releases/1784862378006-1249051/src/driver/types.js
747
+ // .dist-releases/1784894032002-2676534/src/driver/types.js
747
748
  class DriverUnsupportedError extends Error {
748
749
  constructor(message) {
749
750
  super(message);
@@ -758,7 +759,7 @@ class DriverTargetNotReadyError extends Error {
758
759
  }
759
760
  }
760
761
 
761
- // .dist-releases/1784862378006-1249051/src/driver/sdk-loader.js
762
+ // .dist-releases/1784894032002-2676534/src/driver/sdk-loader.js
762
763
  async function loadSdk(loader, packageName, env = {}, installHint = `Install the optional SDK dependency ${packageName} and its peers for this transport.`) {
763
764
  try {
764
765
  return await loader();
@@ -768,7 +769,7 @@ async function loadSdk(loader, packageName, env = {}, installHint = `Install the
768
769
  }
769
770
  }
770
771
 
771
- // .dist-releases/1784862378006-1249051/src/driver/claude/driver.js
772
+ // .dist-releases/1784894032002-2676534/src/driver/claude/driver.js
772
773
  class ClaudeAgentSdkDriver {
773
774
  options;
774
775
  capabilities = { structured: true, resume: true, attach: false };
@@ -944,7 +945,7 @@ function asRecord(value) {
944
945
  function stringValue(value) {
945
946
  return typeof value === "string" ? value : undefined;
946
947
  }
947
- // .dist-releases/1784862378006-1249051/src/driver/codex/driver.js
948
+ // .dist-releases/1784894032002-2676534/src/driver/codex/driver.js
948
949
  class CodexSdkDriver {
949
950
  options;
950
951
  capabilities = { structured: true, resume: true, attach: false };
@@ -1125,7 +1126,7 @@ function asRecord2(value) {
1125
1126
  function stringValue2(value) {
1126
1127
  return typeof value === "string" ? value : undefined;
1127
1128
  }
1128
- // .dist-releases/1784862378006-1249051/src/driver/liveness-heuristics.js
1129
+ // .dist-releases/1784894032002-2676534/src/driver/liveness-heuristics.js
1129
1130
  var BUSY_MARKERS = ["Working (", "Waiting for background", "Pasted Content", "tab to queue"];
1130
1131
  var ANSI_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[PX^_][\s\S]*?\x1B\\)/g;
1131
1132
  function stripAnsi(text) {
@@ -1152,7 +1153,7 @@ function classifyInteractiveOutput(input) {
1152
1153
  function isOutputStable(now, lastOutputAt, idleStableMs) {
1153
1154
  return Boolean(lastOutputAt && now.getTime() - lastOutputAt.getTime() >= idleStableMs);
1154
1155
  }
1155
- // .dist-releases/1784862378006-1249051/src/driver/pty/loader.js
1156
+ // .dist-releases/1784894032002-2676534/src/driver/pty/loader.js
1156
1157
  async function loadNodePty(loader = defaultNodePtyLoader) {
1157
1158
  try {
1158
1159
  return await loader();
@@ -1178,7 +1179,7 @@ async function defaultNodePtyLoader() {
1178
1179
  return loaded;
1179
1180
  }
1180
1181
 
1181
- // .dist-releases/1784862378006-1249051/src/driver/pty/driver.js
1182
+ // .dist-releases/1784894032002-2676534/src/driver/pty/driver.js
1182
1183
  class PtyDriver {
1183
1184
  options;
1184
1185
  capabilities = { structured: false, resume: false, attach: false };
@@ -1330,12 +1331,12 @@ function tail(text, lines) {
1330
1331
  function sleep(ms) {
1331
1332
  return new Promise((resolve) => setTimeout(resolve, ms));
1332
1333
  }
1333
- // .dist-releases/1784862378006-1249051/src/driver/tmux/driver.js
1334
+ // .dist-releases/1784894032002-2676534/src/driver/tmux/driver.js
1334
1335
  import { mkdtemp, rm, writeFile as writeFile2 } from "node:fs/promises";
1335
1336
  import { tmpdir } from "node:os";
1336
1337
  import { join } from "node:path";
1337
1338
 
1338
- // .dist-releases/1784862378006-1249051/src/driver/tmux/launch.js
1339
+ // .dist-releases/1784894032002-2676534/src/driver/tmux/launch.js
1339
1340
  var ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
1340
1341
  var PANE_BOOTSTRAP_COMMAND = 'tmux set-option -t "$TMUX_PANE" remain-on-exit on >/dev/null 2>&1 || true; tmux wait-for -S "$1"; shift; exec "$@"';
1341
1342
  function buildTmuxNewSessionArgs(input) {
@@ -1358,7 +1359,7 @@ function validateEnv(env) {
1358
1359
  }
1359
1360
  }
1360
1361
 
1361
- // .dist-releases/1784862378006-1249051/src/driver/tmux/paste.js
1362
+ // .dist-releases/1784894032002-2676534/src/driver/tmux/paste.js
1362
1363
  function buildDispatchPlan(text, useTyping) {
1363
1364
  return {
1364
1365
  mode: useTyping ? "type-literal" : "paste-buffer",
@@ -1382,7 +1383,7 @@ function arraysEqual(left, right) {
1382
1383
  return left.length === right.length && left.every((item, index) => item === right[index]);
1383
1384
  }
1384
1385
 
1385
- // .dist-releases/1784862378006-1249051/src/driver/tmux/liveness.js
1386
+ // .dist-releases/1784894032002-2676534/src/driver/tmux/liveness.js
1386
1387
  var SHELL_COMMANDS = new Set(["", "bash", "zsh", "sh", "fish", "pwsh", "powershell"]);
1387
1388
  function classifyTmuxLiveness(input) {
1388
1389
  if (!input.hasSession) {
@@ -1402,7 +1403,7 @@ function isTmuxProcessAlive(command, runningCommands) {
1402
1403
  return !SHELL_COMMANDS.has(command);
1403
1404
  }
1404
1405
 
1405
- // .dist-releases/1784862378006-1249051/src/driver/tmux/runner.js
1406
+ // .dist-releases/1784894032002-2676534/src/driver/tmux/runner.js
1406
1407
  import { spawn as spawn2 } from "node:child_process";
1407
1408
  var DEFAULT_TIMEOUT_MS = 15000;
1408
1409
 
@@ -1527,7 +1528,7 @@ class TmuxClient {
1527
1528
  }
1528
1529
  }
1529
1530
 
1530
- // .dist-releases/1784862378006-1249051/src/driver/tmux/driver.js
1531
+ // .dist-releases/1784894032002-2676534/src/driver/tmux/driver.js
1531
1532
  var ATTACHED_STOP_GRACE_MS = 1e4;
1532
1533
  var DEFAULT_STOP_TIMEOUT_MS = 15000;
1533
1534
  var DEFAULT_READY_TIMEOUT_MS = 90000;
@@ -1922,12 +1923,12 @@ function isMissingTmuxSessionError(error) {
1922
1923
  ${error.result.stderr}`.toLowerCase();
1923
1924
  return text.includes("can't find session") || text.includes("cannot find session") || text.includes("no such session") || text.includes("session not found");
1924
1925
  }
1925
- // .dist-releases/1784862378006-1249051/src/relay/client.js
1926
+ // .dist-releases/1784894032002-2676534/src/relay/client.js
1926
1927
  import { Buffer as Buffer2 } from "node:buffer";
1927
1928
  import http from "node:http";
1928
1929
  import https from "node:https";
1929
1930
 
1930
- // .dist-releases/1784862378006-1249051/src/relay/errors.js
1931
+ // .dist-releases/1784894032002-2676534/src/relay/errors.js
1931
1932
  class RelayError extends Error {
1932
1933
  method;
1933
1934
  path;
@@ -1964,7 +1965,7 @@ class RelayProtocolError extends RelayError {
1964
1965
  }
1965
1966
  }
1966
1967
 
1967
- // .dist-releases/1784862378006-1249051/src/worker/protocol.js
1968
+ // .dist-releases/1784894032002-2676534/src/worker/protocol.js
1968
1969
  var DEFAULT_RELAY_URL = DEFAULT_PRODUCT_API_URL;
1969
1970
  var DEFAULT_WORKER = "host";
1970
1971
  var DEFAULT_WORKDIR = "~/sailorbridge-workdir";
@@ -1994,7 +1995,7 @@ function effectiveJobKind(kind, spec) {
1994
1995
  return kind === "stream" || spec.stream === true ? "stream" : kind;
1995
1996
  }
1996
1997
 
1997
- // .dist-releases/1784862378006-1249051/src/relay/client.js
1998
+ // .dist-releases/1784894032002-2676534/src/relay/client.js
1998
1999
  var JSON_CONTENT_TYPE = "application/json";
1999
2000
  var MAX_MAILBOX_LIMIT = 16;
2000
2001
  var MAX_WAIT_TIMEOUT_SEC = 120;
@@ -2161,13 +2162,15 @@ class RelayClient {
2161
2162
  }
2162
2163
  return { status: "queued", ids: response.ids, recipients: response.recipients };
2163
2164
  }
2164
- async postSessionReply(session, text, target = { kind: "captain" }, inReplyToMessageId, tag = "REPLY") {
2165
+ async postSessionReply(session, text, target = { kind: "captain" }, inReplyToMessageId, tag = "REPLY", origin) {
2165
2166
  requireSegment(session, "session");
2166
2167
  const toAgent = target.kind === "captain" ? "captain" : target.agentName;
2167
2168
  requireNonEmpty(toAgent, "reply target");
2168
2169
  requireNonEmpty(tag, "reply tag");
2169
2170
  if (inReplyToMessageId !== undefined)
2170
2171
  requireNonEmpty(inReplyToMessageId, "in_reply_to_message_id");
2172
+ if (origin !== undefined && target.kind !== "captain")
2173
+ throw new Error("origin is only valid for captain replies");
2171
2174
  const response = await this.requestJson("POST", `/api/v1/mailbox/session/${segment(session)}`, {
2172
2175
  tag,
2173
2176
  to_agents: [toAgent],
@@ -2175,13 +2178,18 @@ class RelayClient {
2175
2178
  payload_json: text,
2176
2179
  ...inReplyToMessageId === undefined ? {} : {
2177
2180
  in_reply_to_message_id: inReplyToMessageId
2178
- }
2181
+ },
2182
+ ...origin === undefined ? {} : { origin }
2179
2183
  });
2180
- const expectedStatus = target.kind === "captain" ? "recorded" : "queued";
2181
- if (response.status !== expectedStatus || !isNonEmptyStringArray(response.ids) || !isStringArray(response.recipients)) {
2182
- throw new RelayProtocolError("POST", `/api/v1/mailbox/session/${segment(session)}`, "invalid session reply response");
2184
+ const validRecipients = isStringArray(response.recipients) && response.recipients.length === 1 && response.recipients[0] === toAgent;
2185
+ if (origin !== undefined && response.status === "skipped") {
2186
+ if (!validRecipients || typeof response.reason !== "string" || !response.reason) {
2187
+ throw new RelayProtocolError("POST", `/api/v1/mailbox/session/${segment(session)}`, "invalid session reply skip response");
2188
+ }
2189
+ return { ids: [], recipients: response.recipients, skippedReason: response.reason };
2183
2190
  }
2184
- if (response.recipients.length !== 1 || response.recipients[0] !== toAgent) {
2191
+ const expectedStatus = target.kind === "captain" ? "recorded" : "queued";
2192
+ if (response.status !== expectedStatus || !isNonEmptyStringArray(response.ids) || !validRecipients) {
2185
2193
  throw new RelayProtocolError("POST", `/api/v1/mailbox/session/${segment(session)}`, "invalid session reply response");
2186
2194
  }
2187
2195
  return { ids: response.ids, recipients: response.recipients };
@@ -2370,7 +2378,8 @@ class RelayClient {
2370
2378
  host_id: body.hostId,
2371
2379
  display_name: body.displayName,
2372
2380
  ...body.clientVersion === undefined ? {} : { client_version: body.clientVersion },
2373
- ...body.clientGitSha === undefined ? {} : { client_git_sha: body.clientGitSha }
2381
+ ...body.clientGitSha === undefined ? {} : { client_git_sha: body.clientGitSha },
2382
+ ...body.updateError === undefined ? {} : { update_error: body.updateError }
2374
2383
  });
2375
2384
  return parseClientHostReport(response, "POST", path3);
2376
2385
  }
@@ -2384,7 +2393,8 @@ class RelayClient {
2384
2393
  const response = await this.requestJson("POST", path3, {
2385
2394
  ...body.displayName === undefined ? {} : { display_name: body.displayName },
2386
2395
  ...body.clientVersion === undefined ? {} : { client_version: body.clientVersion },
2387
- ...body.clientGitSha === undefined ? {} : { client_git_sha: body.clientGitSha }
2396
+ ...body.clientGitSha === undefined ? {} : { client_git_sha: body.clientGitSha },
2397
+ ...body.updateError === undefined ? {} : { update_error: body.updateError }
2388
2398
  });
2389
2399
  return parseClientHostReport(response, "POST", path3);
2390
2400
  }
@@ -3471,6 +3481,8 @@ function optionalClientHostMetadata(host, method, path3) {
3471
3481
  const minimum = optionalClientHostString(host, "minimum_client_version", method, path3);
3472
3482
  if (minimum === null)
3473
3483
  throw new RelayProtocolError(method, path3, "host.minimum_client_version must be a string");
3484
+ const latest = optionalClientHostString(host, "latest_client_version", method, path3);
3485
+ const updateError = optionalClientHostString(host, "update_error", method, path3);
3474
3486
  const compatibility = host.client_compatibility;
3475
3487
  if (compatibility !== undefined && compatibility !== "current" && compatibility !== "outdated" && compatibility !== "unknown" && compatibility !== "development") {
3476
3488
  throw new RelayProtocolError(method, path3, "host.client_compatibility is invalid");
@@ -3479,7 +3491,9 @@ function optionalClientHostMetadata(host, method, path3) {
3479
3491
  ...version === undefined ? {} : { client_version: version },
3480
3492
  ...gitSha === undefined ? {} : { client_git_sha: gitSha },
3481
3493
  ...minimum === undefined ? {} : { minimum_client_version: minimum },
3482
- ...compatibility === undefined ? {} : { client_compatibility: compatibility }
3494
+ ...latest === undefined ? {} : { latest_client_version: latest },
3495
+ ...compatibility === undefined ? {} : { client_compatibility: compatibility },
3496
+ ...updateError === undefined ? {} : { update_error: updateError }
3483
3497
  };
3484
3498
  }
3485
3499
  function optionalClientHostString(host, field, method, path3) {
@@ -4047,7 +4061,7 @@ function redact(value, token) {
4047
4061
  function bytesToBase64(bytes) {
4048
4062
  return Buffer2.from(bytes).toString("base64");
4049
4063
  }
4050
- // .dist-releases/1784862378006-1249051/src/relay/config.js
4064
+ // .dist-releases/1784894032002-2676534/src/relay/config.js
4051
4065
  import { existsSync as existsSync2, readFileSync } from "node:fs";
4052
4066
  import { homedir as homedir3 } from "node:os";
4053
4067
  import path3 from "node:path";
@@ -4065,6 +4079,7 @@ function loadRelayConfig(env = process.env) {
4065
4079
  const captainId = optionalCaptainId(env.SAILORBRIDGE_CAPTAIN_ID) ?? stored?.captain_id;
4066
4080
  const hostId = nonEmpty(env.SAILORBRIDGE_HOST_ID) ?? stored?.host_id;
4067
4081
  const hostDisplayName = nonEmpty(env.SAILORBRIDGE_HOST_DISPLAY_NAME);
4082
+ const keyScope = !envBearerToken && stored ? "host" : undefined;
4068
4083
  return {
4069
4084
  relayUrl,
4070
4085
  bearerToken,
@@ -4072,7 +4087,8 @@ function loadRelayConfig(env = process.env) {
4072
4087
  ...session ? { session } : {},
4073
4088
  ...captainId ? { captainId } : {},
4074
4089
  ...hostId ? { hostId } : {},
4075
- ...hostDisplayName ? { hostDisplayName } : {}
4090
+ ...hostDisplayName ? { hostDisplayName } : {},
4091
+ ...keyScope ? { keyScope } : {}
4076
4092
  };
4077
4093
  }
4078
4094
  function loadStoredHostCredential(env, options = {}) {
@@ -4122,7 +4138,7 @@ function storedCaptainId(value) {
4122
4138
  }
4123
4139
  return;
4124
4140
  }
4125
- // .dist-releases/1784862378006-1249051/src/supervisor/retirement.js
4141
+ // .dist-releases/1784894032002-2676534/src/supervisor/retirement.js
4126
4142
  var DEFAULT_THRESHOLD = 3;
4127
4143
  var DEFAULT_WINDOW_MS = 30000;
4128
4144
  var DEFAULT_COOLDOWN_MS = 1000;
@@ -4197,7 +4213,7 @@ function isAuthoritativeRetirementReason(reason) {
4197
4213
  return reason === "monitor_fetch" || reason === "agent_session_patch";
4198
4214
  }
4199
4215
 
4200
- // .dist-releases/1784862378006-1249051/src/supervisor/wait.js
4216
+ // .dist-releases/1784894032002-2676534/src/supervisor/wait.js
4201
4217
  async function waitForWakeableTimeout(ms, setWake, sleep3) {
4202
4218
  if (sleep3) {
4203
4219
  await Promise.race([
@@ -4228,7 +4244,7 @@ async function waitForWakeableTimeout(ms, setWake, sleep3) {
4228
4244
  setWake(null);
4229
4245
  }
4230
4246
 
4231
- // .dist-releases/1784862378006-1249051/src/supervisor/mailbox.js
4247
+ // .dist-releases/1784894032002-2676534/src/supervisor/mailbox.js
4232
4248
  var CLEAR_TAG = "CLEAR";
4233
4249
  var REFRESH_TAG = "WORKFLOW-REFRESH";
4234
4250
  var SYNC_TAG = "SYNC";
@@ -4299,7 +4315,9 @@ class MailboxSupervisor {
4299
4315
  let handled = await this.dispatchBatch(controls, true);
4300
4316
  if (this.stopped)
4301
4317
  return handled;
4302
- const messages = await this.claimOwnedMessages(true, undefined, true, this.limit, [...MAILBOX_CLAIM_EXCLUDED_TAGS]);
4318
+ const preview = await this.claimOwnedMessages(true);
4319
+ handled += await this.dispatchBatch(preview.filter(isControl));
4320
+ const messages = preview.filter((message) => !isControl(message));
4303
4321
  this.restoreDoorbellAttempts(messages);
4304
4322
  this.pruneDoorbellAttempts(messages);
4305
4323
  if (messages.length === 0)
@@ -4409,7 +4427,7 @@ class MailboxSupervisor {
4409
4427
  if (this.stopped && !deliver)
4410
4428
  return 0;
4411
4429
  try {
4412
- await this.deliverRefreshCue();
4430
+ await this.deliverRefreshCue(refreshNotices(pending));
4413
4431
  } catch (error) {
4414
4432
  await this.recordSurfaceBatch(pending, { status: "failed", error: this.redact(errorMessage2(error)) });
4415
4433
  return 0;
@@ -4421,8 +4439,8 @@ class MailboxSupervisor {
4421
4439
  await this.claimDeliveredControls(messages);
4422
4440
  return pending.length;
4423
4441
  }
4424
- async deliverRefreshCue() {
4425
- const text = refreshCue(this.options.cli);
4442
+ async deliverRefreshCue(notices = []) {
4443
+ const text = refreshCue(this.options.cli, notices);
4426
4444
  await this.options.driver.send(text, { verifyText: REFRESH_TAG });
4427
4445
  }
4428
4446
  async handleSyncMessages(messages, deliver = false) {
@@ -4706,7 +4724,7 @@ function formatWorkDoorbell(input, toDisplay) {
4706
4724
  throw new Error("cannot format an empty work doorbell");
4707
4725
  const sender = message.sender_agent_name ?? message.sender_role ?? "unknown";
4708
4726
  const tag = canonicalDoorbellTag(tagOf(message));
4709
- return `[${sender}->${toDisplay}] ${tag} ${doorbellToken(message.id)} \uD83D\uDCEC ${messages.length} new message(s). Read and claim: sail mailbox claim; complete obligation tags; direct workflows use the returned lease token.`;
4727
+ return `[${sender}->${toDisplay}] ${tag} ${doorbellToken(message.id)} \uD83D\uDCEC ${messages.length} new message(s). Read: sail mailbox claim. Conversational tags: answer with one sail reply no ack owed. Obligation tags: complete them; their claim output prints the ack line.`;
4710
4728
  }
4711
4729
  function canonicalDoorbellTag(tag) {
4712
4730
  const printable = Array.from(tag, (char) => char >= " " && char <= "~" ? char : " ").join("");
@@ -4719,10 +4737,27 @@ function unsurfaced(messages) {
4719
4737
  function doorbellToken(messageId) {
4720
4738
  return `#${messageId}#`;
4721
4739
  }
4722
- function refreshCue(_cli) {
4723
- return `${REFRESH_TAG}
4740
+ function refreshCue(_cli, notices = []) {
4741
+ return [
4742
+ REFRESH_TAG,
4743
+ ...notices,
4744
+ "Run `sail refresh` now to reload your role and instructions, then continue."
4745
+ ].join(`
4724
4746
 
4725
- Run \`sail refresh\` before handling mailbox work.`;
4747
+ `);
4748
+ }
4749
+ function refreshNotices(messages) {
4750
+ const notices = [];
4751
+ for (const message of messages) {
4752
+ const payload = message.payload_json;
4753
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
4754
+ continue;
4755
+ const notice = payload.notice;
4756
+ if (typeof notice === "string" && notice.trim() && !notices.includes(notice.trim())) {
4757
+ notices.push(notice.trim());
4758
+ }
4759
+ }
4760
+ return notices;
4726
4761
  }
4727
4762
  function saveContextCue(agentName, checkpointId) {
4728
4763
  return [
@@ -4764,19 +4799,19 @@ function ordinaryDeliveryAttempt(error) {
4764
4799
  function _isMissingModerator(error) {
4765
4800
  return error instanceof RelayHttpError && error.status === 400 && error.body.includes("no agents match");
4766
4801
  }
4767
- // .dist-releases/1784862378006-1249051/src/supervisor/runtime.js
4802
+ // .dist-releases/1784894032002-2676534/src/supervisor/runtime.js
4768
4803
  import path9 from "node:path";
4769
4804
  import { execFile } from "node:child_process";
4770
4805
  import { promisify } from "node:util";
4771
4806
 
4772
- // .dist-releases/1784862378006-1249051/src/host/managed-materialization.js
4807
+ // .dist-releases/1784894032002-2676534/src/host/managed-materialization.js
4773
4808
  import { createHash as createHash2, randomBytes } from "node:crypto";
4774
4809
  import { constants as fsConstants } from "node:fs";
4775
4810
  import { lstat, mkdir as mkdir3, open, readdir, rename, rm as rm2 } from "node:fs/promises";
4776
4811
  import { homedir as homedir5 } from "node:os";
4777
4812
  import path5 from "node:path";
4778
4813
 
4779
- // .dist-releases/1784862378006-1249051/src/host/memory-stub.js
4814
+ // .dist-releases/1784894032002-2676534/src/host/memory-stub.js
4780
4815
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile3 } from "node:fs/promises";
4781
4816
  import { homedir as homedir4 } from "node:os";
4782
4817
  import path4 from "node:path";
@@ -4923,7 +4958,7 @@ function _safePathLabel(value, field) {
4923
4958
  return trimmed;
4924
4959
  }
4925
4960
 
4926
- // .dist-releases/1784862378006-1249051/src/host/managed-materialization.js
4961
+ // .dist-releases/1784894032002-2676534/src/host/managed-materialization.js
4927
4962
  var MANIFEST_FILE = ".sailorbridge-managed-manifest.json";
4928
4963
  var BANNER_PREFIX = "# SailorBridge managed file";
4929
4964
  var HASH_RE = /^sha256:[a-f0-9]{64}$/u;
@@ -5449,7 +5484,7 @@ function _expandHome2(value) {
5449
5484
  return value;
5450
5485
  }
5451
5486
 
5452
- // .dist-releases/1784862378006-1249051/src/host/credential-guardian.js
5487
+ // .dist-releases/1784894032002-2676534/src/host/credential-guardian.js
5453
5488
  import { lstat as lstat2, readFile as readFile3, rename as rename2, symlink, unlink, writeFile as writeFile4 } from "node:fs/promises";
5454
5489
  import path6 from "node:path";
5455
5490
  var DEFAULT_CREDENTIAL_SWEEP_INTERVAL_MS = 60000;
@@ -5595,7 +5630,7 @@ function _tmpSibling(filePath) {
5595
5630
  return path6.join(path6.dirname(filePath), `.${path6.basename(filePath)}.sb-guardian.${process.pid}.${tmpSequence}.tmp`);
5596
5631
  }
5597
5632
 
5598
- // .dist-releases/1784862378006-1249051/src/supervisor/resolve.js
5633
+ // .dist-releases/1784894032002-2676534/src/supervisor/resolve.js
5599
5634
  var DEFAULT_CAPTURE_LINES = 200;
5600
5635
  var MAX_CAPTURE_LINES = 1000;
5601
5636
 
@@ -5801,7 +5836,7 @@ function defaultSleep(ms) {
5801
5836
  return new Promise((resolve) => setTimeout(resolve, ms));
5802
5837
  }
5803
5838
 
5804
- // .dist-releases/1784862378006-1249051/src/supervisor/transcript-backfill.js
5839
+ // .dist-releases/1784894032002-2676534/src/supervisor/transcript-backfill.js
5805
5840
  import { createHash as createHash3 } from "node:crypto";
5806
5841
  import {
5807
5842
  closeSync,
@@ -6220,6 +6255,17 @@ class TranscriptBackfillWorker {
6220
6255
  store.rememberPostedEvent(source, event.eventId, item.nextOffset, fingerprint);
6221
6256
  this.clearPendingIfUnchanged(pendingMessageId);
6222
6257
  this.resetRetry();
6258
+ if (result.skippedReason !== undefined) {
6259
+ this.log("transcript_backfill_relay_skipped", {
6260
+ agent: this.options.agentName,
6261
+ engine: this.options.engine,
6262
+ source,
6263
+ offset: item.startOffset,
6264
+ eventId: event.eventId,
6265
+ reason: result.skippedReason
6266
+ });
6267
+ break;
6268
+ }
6223
6269
  count += 1;
6224
6270
  this.log("transcript_backfill_posted", {
6225
6271
  agent: this.options.agentName,
@@ -6248,7 +6294,8 @@ class TranscriptBackfillWorker {
6248
6294
  throw new Error("transcript backfill requires session");
6249
6295
  const target = this.pending?.replyTarget ?? { kind: "captain" };
6250
6296
  const inReplyToMessageId = target.kind === "captain" ? this.pending?.messageId : undefined;
6251
- return this.options.relay.postSessionReply(this.options.session, event.text, target, inReplyToMessageId);
6297
+ const origin = target.kind === "captain" ? "transcript-backfill" : undefined;
6298
+ return this.options.relay.postSessionReply(this.options.session, event.text, target, inReplyToMessageId, "REPLY", origin);
6252
6299
  }
6253
6300
  clearPendingIfUnchanged(pendingMessageId) {
6254
6301
  if (this.pending && this.pending.messageId === pendingMessageId)
@@ -6458,7 +6505,7 @@ function errorMessage4(error) {
6458
6505
  return error instanceof Error ? error.message : String(error);
6459
6506
  }
6460
6507
 
6461
- // .dist-releases/1784862378006-1249051/src/supervisor/usage-collector.js
6508
+ // .dist-releases/1784894032002-2676534/src/supervisor/usage-collector.js
6462
6509
  import { statSync as statSync2 } from "node:fs";
6463
6510
  import path8 from "node:path";
6464
6511
  var SUPPORTED_USAGE_ENGINES = new Set(["claude", "codex"]);
@@ -6867,7 +6914,7 @@ function sourceUnreadableReason(source) {
6867
6914
  }
6868
6915
  }
6869
6916
 
6870
- // .dist-releases/1784862378006-1249051/src/supervisor/runtime.js
6917
+ // .dist-releases/1784894032002-2676534/src/supervisor/runtime.js
6871
6918
  var RESOLVE_CAPTURE_LINES = 200;
6872
6919
  var SHORT_LIVED_EXIT_MS = 5000;
6873
6920
  var STARTUP_OBSERVATION_MS = 1e4;
@@ -7537,7 +7584,7 @@ function errorMessage5(error) {
7537
7584
  function formatRestartCommand(command) {
7538
7585
  return command.map((part) => /^[A-Za-z0-9_./:=@%+-]+$/.test(part) ? part : `'${part.replaceAll("'", "'\\''")}'`).join(" ");
7539
7586
  }
7540
- // .dist-releases/1784862378006-1249051/src/supervisor/transport.js
7587
+ // .dist-releases/1784894032002-2676534/src/supervisor/transport.js
7541
7588
  var SUPPORTED_PHASE1_CLI_KINDS = ["claude", "codex"];
7542
7589
  var PHASE1_ENGINE_GATE_MESSAGE = "Only claude and codex engines are available in this release; opencode and custom CLI engines will open in the next release.";
7543
7590
  var PHASE1_TRANSPORT_GATE_MESSAGE = "Only tmux transport is available in this release; sdk, headless, and pty transports will open in the next release.";
@@ -7745,7 +7792,7 @@ function hasCodexSafetyDriverOptions(options) {
7745
7792
  return options?.codex?.sandboxMode !== undefined || options?.codex?.approvalPolicy !== undefined || options?.codex?.yolo !== undefined;
7746
7793
  }
7747
7794
 
7748
- // .dist-releases/1784862378006-1249051/src/supervisor/cli-options.js
7795
+ // .dist-releases/1784894032002-2676534/src/supervisor/cli-options.js
7749
7796
  function parseSuperviseArgs(argv) {
7750
7797
  const split = argv.indexOf("--");
7751
7798
  const flagArgs = split >= 0 ? argv.slice(0, split) : argv;
@@ -7897,7 +7944,7 @@ function optionalBoolean(value, label) {
7897
7944
  return false;
7898
7945
  throw new Error(`${label} must be true or false`);
7899
7946
  }
7900
- // .dist-releases/1784862378006-1249051/src/supervisor/tmux-availability.js
7947
+ // .dist-releases/1784894032002-2676534/src/supervisor/tmux-availability.js
7901
7948
  import { spawn as spawn3 } from "node:child_process";
7902
7949
  import { createInterface } from "node:readline";
7903
7950
  function isTmuxAvailable(spawnFn = spawn3) {
@@ -8014,7 +8061,7 @@ function _tmuxMissingMessage(platform2) {
8014
8061
  const how = platform2 === "darwin" ? "brew install tmux" : platform2 === "linux" ? "install tmux via your package manager (e.g. sudo apt-get install -y tmux)" : "install tmux";
8015
8062
  return `tmux is required in this release but was not found. Install tmux (${how}) and re-run.`;
8016
8063
  }
8017
- // .dist-releases/1784862378006-1249051/src/output-format.js
8064
+ // .dist-releases/1784894032002-2676534/src/output-format.js
8018
8065
  function parseOutputArgs(argv) {
8019
8066
  const separator = argv.indexOf("--");
8020
8067
  const localArgv = separator < 0 ? argv : argv.slice(0, separator);
@@ -8107,7 +8154,7 @@ ${normalized.split(`
8107
8154
  `)}`;
8108
8155
  }
8109
8156
 
8110
- // .dist-releases/1784862378006-1249051/src/plain-output-fields.js
8157
+ // .dist-releases/1784894032002-2676534/src/plain-output-fields.js
8111
8158
  var PREVIEW_LIMIT = 20;
8112
8159
  var UNSAFE_DISPLAY_CODE_POINT = /[\u007f-\u009f\u2028-\u202e\u2066-\u2069]/gu;
8113
8160
  function renderTaskPlain(command, value) {
@@ -8515,7 +8562,7 @@ function _omissionLine(total) {
8515
8562
  return total > PREVIEW_LIMIT ? `Omitted ${total - PREVIEW_LIMIT}; rerun with --format json for the complete response.` : undefined;
8516
8563
  }
8517
8564
 
8518
- // .dist-releases/1784862378006-1249051/src/host/cli.js
8565
+ // .dist-releases/1784894032002-2676534/src/host/cli.js
8519
8566
  var HOST_RELAY_SUBCOMMANDS = new Set(["consume", "heartbeat", "list", "register"]);
8520
8567
  async function runHostCommandFromCli(client, argv, config, io) {
8521
8568
  const sub = argv[0] ?? "list";
@@ -8753,7 +8800,7 @@ function optionalDisplayName(value) {
8753
8800
  const trimmed = value?.trim();
8754
8801
  return trimmed || undefined;
8755
8802
  }
8756
- // .dist-releases/1784862378006-1249051/src/host/service.js
8803
+ // .dist-releases/1784894032002-2676534/src/host/service.js
8757
8804
  import { execFile as execFileCb } from "node:child_process";
8758
8805
  import { createHash as createHash4 } from "node:crypto";
8759
8806
  import { constants as fsConstants2 } from "node:fs";
@@ -8779,6 +8826,18 @@ async function installHostService(options, deps = {}) {
8779
8826
  return installSystemd(flags, runtime, paths, command);
8780
8827
  return installLaunchd(flags, runtime, paths, command);
8781
8828
  }
8829
+ async function restartRunningHostService(deps = {}) {
8830
+ const runtime = serviceRuntime(deps);
8831
+ if (runtime.platform !== "linux" && runtime.platform !== "darwin") {
8832
+ return { restarted: false, reason: "unsupported-platform" };
8833
+ }
8834
+ const paths = servicePaths(runtime, { dryRun: false });
8835
+ if (!await _isManagedHostService(runtime, paths))
8836
+ return { restarted: false, reason: "not-managed" };
8837
+ if (runtime.platform === "linux")
8838
+ return _restartRunningSystemd(runtime, paths);
8839
+ return _restartRunningLaunchd(runtime, paths);
8840
+ }
8782
8841
  var UNSUPPORTED_SERVICE_PLATFORM = "host service supports Linux systemd and macOS launchd only";
8783
8842
  async function runHostServiceCommandFromCli(argv, io, deps = {}) {
8784
8843
  const action = argv[0] ?? "status";
@@ -8968,6 +9027,32 @@ async function statusLaunchd(runtime, paths, io) {
8968
9027
  const installed = await fileExists(runtime, paths.file);
8969
9028
  _writeServiceOutput(io, { status: active ? "active" : "inactive", installed, platform: "launchd", file: paths.file, linger: "not-applicable" });
8970
9029
  }
9030
+ async function _restartRunningSystemd(runtime, paths) {
9031
+ const active = await optionalExec(runtime, "systemctl", ["--user", "is-active", paths.serviceName]);
9032
+ if (active?.stdout.trim() !== "active")
9033
+ return { restarted: false, reason: "not-running" };
9034
+ await runtime.execFile("systemctl", ["--user", "restart", paths.serviceName]);
9035
+ return { restarted: true, platform: "systemd", target: paths.serviceName };
9036
+ }
9037
+ async function _restartRunningLaunchd(runtime, paths) {
9038
+ const target = launchdServiceTarget(runtime.uid, paths.launchdLabel);
9039
+ if (!await optionalExec(runtime, "launchctl", ["print", target])) {
9040
+ return { restarted: false, reason: "not-running" };
9041
+ }
9042
+ await runtime.execFile("launchctl", ["kickstart", "-k", target]);
9043
+ return { restarted: true, platform: "launchd", target: paths.launchdLabel };
9044
+ }
9045
+ async function _isManagedHostService(runtime, paths) {
9046
+ try {
9047
+ const definition = await _readDefinition(runtime, paths.file);
9048
+ if (!definition)
9049
+ return false;
9050
+ const manager = runtime.platform === "linux" ? "systemd" : "launchd";
9051
+ return _isLegacyHostDefinition(definition, manager);
9052
+ } catch {
9053
+ return false;
9054
+ }
9055
+ }
8971
9056
  function serviceRuntime(deps) {
8972
9057
  const info = userInfo();
8973
9058
  return {
@@ -9105,8 +9190,8 @@ function _isLegacyHostDefinition(text, manager) {
9105
9190
  function _isSailorBridgeEntry(value) {
9106
9191
  if (!value)
9107
9192
  return false;
9108
- const segments = value.replace(/\\/gu, "/").split("/").filter(Boolean);
9109
- return segments.at(-1) === "cli.js" && segments.at(-2) === "dist" && segments.at(-3) === "client" && segments.at(-4) === "@sailorbridge";
9193
+ const normalized = `/${value.replace(/\\/gu, "/").split("/").filter(Boolean).join("/")}`;
9194
+ return normalized.endsWith("/@sailorbridge/client/dist/cli.js") || normalized.endsWith("/@sailorbridge/client/dist/src/cli.js");
9110
9195
  }
9111
9196
  function _systemdExecStart(text) {
9112
9197
  const values = [...text.matchAll(/^\s*ExecStart\s*=(.*)$/gmu)].map((match) => match[1]?.trim() ?? "").filter(Boolean);
@@ -9236,7 +9321,7 @@ function writeServiceLog(io, event, fields) {
9236
9321
  io.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, ...fields ?? {} })}
9237
9322
  `);
9238
9323
  }
9239
- // .dist-releases/1784862378006-1249051/src/host/git-remote.js
9324
+ // .dist-releases/1784894032002-2676534/src/host/git-remote.js
9240
9325
  var GIT_SECRET_PATH_MARKERS = [
9241
9326
  "token",
9242
9327
  "credential",
@@ -9328,7 +9413,7 @@ function looksLikeCredential(segment2) {
9328
9413
  return value.length >= 32 && /^[A-Za-z0-9_-]+$/u.test(value);
9329
9414
  }
9330
9415
 
9331
- // .dist-releases/1784862378006-1249051/src/host/start-spec.js
9416
+ // .dist-releases/1784894032002-2676534/src/host/start-spec.js
9332
9417
  var TRANSPORTS = ["auto", "sdk", "pty", "tmux", "headless"];
9333
9418
  var SUPPORTED_PHASE1_ENGINES = ["claude", "codex"];
9334
9419
  var ENGINE_PATTERN = /^[a-z][a-z0-9._-]{0,63}$/u;
@@ -9578,7 +9663,7 @@ function _trimmed(value, label) {
9578
9663
  throw new Error(`${label} must not be empty`);
9579
9664
  return trimmed;
9580
9665
  }
9581
- // .dist-releases/1784862378006-1249051/src/host/workspace.js
9666
+ // .dist-releases/1784894032002-2676534/src/host/workspace.js
9582
9667
  import { spawn as spawn4 } from "node:child_process";
9583
9668
  import { existsSync as existsSync4 } from "node:fs";
9584
9669
  import { mkdir as mkdir5, readdir as readdir2, rename as rename4, stat } from "node:fs/promises";
@@ -9809,7 +9894,7 @@ function _safeSegment(value) {
9809
9894
  throw new Error("workspace path segment is empty after sanitizing");
9810
9895
  return segment2;
9811
9896
  }
9812
- // .dist-releases/1784862378006-1249051/src/host/workspace-inventory.js
9897
+ // .dist-releases/1784894032002-2676534/src/host/workspace-inventory.js
9813
9898
  import { access as access2 } from "node:fs/promises";
9814
9899
  import { constants } from "node:fs";
9815
9900
  import { execFile as execFile2 } from "node:child_process";
@@ -9901,25 +9986,179 @@ async function canAccess(path12, mode, deps) {
9901
9986
  return false;
9902
9987
  }
9903
9988
  }
9904
- // .dist-releases/1784862378006-1249051/src/host/host-config.js
9905
- import { chmod as chmod3, mkdir as mkdir6, readFile as readFile5, writeFile as writeFile6 } from "node:fs/promises";
9906
- import { homedir as homedir10 } from "node:os";
9989
+ // .dist-releases/1784894032002-2676534/src/upgrade.js
9990
+ import { spawn as spawn5 } from "node:child_process";
9991
+ import { execFile as execFileCb2 } from "node:child_process";
9992
+ import { readFile as readFile5 } from "node:fs/promises";
9907
9993
  import path12 from "node:path";
9994
+ import { promisify as promisify4 } from "node:util";
9995
+ var execFileDefault2 = promisify4(execFileCb2);
9996
+ var CLIENT_PACKAGE = "@sailorbridge/client";
9997
+ var LATEST_PACKAGE = `${CLIENT_PACKAGE}@latest`;
9998
+ async function runUpgradeFromCli(argv, io, deps = {}) {
9999
+ if (argv.length)
10000
+ throw new Error(`upgrade takes no arguments: ${argv.join(" ")}`);
10001
+ const before = deps.currentVersion ?? CLIENT_VERSION;
10002
+ await (deps.installLatest ?? installLatestClientPackage)();
10003
+ const after = await (deps.readInstalledVersion ?? readInstalledClientVersion)();
10004
+ if (before === after) {
10005
+ io.stdout.write(`SailorBridge client is already up to date (${after}).
10006
+ `);
10007
+ return;
10008
+ }
10009
+ const service = await (deps.restartHostService ?? restartRunningHostService)();
10010
+ _writeUpgradeResult(io.stdout, before, after, service);
10011
+ }
10012
+ function installLatestClientPackage() {
10013
+ return new Promise((resolve2, reject) => {
10014
+ const child = spawn5("npm", ["i", "-g", LATEST_PACKAGE], { stdio: "inherit" });
10015
+ child.once("error", reject);
10016
+ child.once("close", (code, signal) => {
10017
+ if (code === 0)
10018
+ resolve2();
10019
+ else
10020
+ reject(new Error(`npm i -g ${LATEST_PACKAGE} failed (${signal ?? `exit ${code ?? "unknown"}`})`));
10021
+ });
10022
+ });
10023
+ }
10024
+ async function readInstalledClientVersion() {
10025
+ const { stdout } = await execFileDefault2("npm", ["root", "-g"]);
10026
+ const root = String(stdout).trim();
10027
+ if (!root)
10028
+ throw new Error("npm root -g returned an empty path after upgrade");
10029
+ const packageJson = path12.join(root, "@sailorbridge", "client", "package.json");
10030
+ const parsed = JSON.parse(await readFile5(packageJson, "utf8"));
10031
+ const version = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "version") : undefined;
10032
+ if (typeof version !== "string" || !version.trim()) {
10033
+ throw new Error(`${packageJson} has no valid version after upgrade`);
10034
+ }
10035
+ return version.trim();
10036
+ }
10037
+ function _writeUpgradeResult(output, before, after, service) {
10038
+ if (service.restarted) {
10039
+ output.write(`Updated SailorBridge client from ${before} to ${after} and restarted ${service.target} (${service.platform}).
10040
+ `);
10041
+ return;
10042
+ }
10043
+ output.write(`Updated SailorBridge client from ${before} to ${after}. No running SailorBridge-managed host service was found; stop and rerun 'sailorbridge host run' to load ${after}.
10044
+ `);
10045
+ }
10046
+
10047
+ // .dist-releases/1784894032002-2676534/src/host/auto-update.js
10048
+ var AUTO_UPDATE_HARD_DEADLINE_MS = 24 * 60 * 60 * 1000;
10049
+ var AUTO_UPDATE_ATTEMPT_COOLDOWN_MS = 60 * 1000;
10050
+ var MAX_UPDATE_ERROR_LENGTH = 500;
10051
+ function createAutoUpdater(options) {
10052
+ const logger = options.logger ?? (() => {});
10053
+ const deps = options.deps ?? {};
10054
+ const now = deps.now ?? (() => Date.now());
10055
+ const currentVersion = deps.currentVersion ?? CLIENT_VERSION;
10056
+ const installLatest = deps.installLatest ?? installLatestClientPackage;
10057
+ const readInstalledVersion = deps.readInstalledVersion ?? readInstalledClientVersion;
10058
+ const restartHostService = deps.restartHostService ?? restartRunningHostService;
10059
+ let pendingSince;
10060
+ let lastAttemptAt;
10061
+ let updateError;
10062
+ let installedVersion;
10063
+ const _attempt = async (reason, target) => {
10064
+ if (lastAttemptAt !== undefined && now() - lastAttemptAt < AUTO_UPDATE_ATTEMPT_COOLDOWN_MS)
10065
+ return;
10066
+ lastAttemptAt = now();
10067
+ logger("host_auto_update_start", { reason, target, current: currentVersion });
10068
+ try {
10069
+ await installLatest();
10070
+ const after = await readInstalledVersion();
10071
+ updateError = undefined;
10072
+ installedVersion = after;
10073
+ if (after === currentVersion) {
10074
+ logger("host_auto_update_noop", { reason, target, installed: after });
10075
+ return;
10076
+ }
10077
+ const report = await restartHostService();
10078
+ if (report.restarted) {
10079
+ logger("host_auto_update_restarting", { from: currentVersion, to: after, platform: report.platform, target: report.target });
10080
+ } else {
10081
+ logger("host_auto_update_installed_pending_restart", { from: currentVersion, to: after, reason: report.reason });
10082
+ }
10083
+ } catch (error) {
10084
+ const message = error instanceof Error ? error.message : String(error);
10085
+ updateError = message.length <= MAX_UPDATE_ERROR_LENGTH ? message : `${message.slice(0, MAX_UPDATE_ERROR_LENGTH - 3)}...`;
10086
+ logger("host_auto_update_error", { reason, target, error: updateError });
10087
+ }
10088
+ };
10089
+ return {
10090
+ async onHostReport(host) {
10091
+ if (!options.enabled)
10092
+ return;
10093
+ const latest = host.latest_client_version;
10094
+ if (!latest || compareReleaseSemver(latest, currentVersion) !== 1) {
10095
+ pendingSince = undefined;
10096
+ return;
10097
+ }
10098
+ const installedCmp = installedVersion === undefined ? null : compareReleaseSemver(installedVersion, latest);
10099
+ if (installedCmp === 0 || installedCmp === 1)
10100
+ return;
10101
+ if (pendingSince === undefined) {
10102
+ pendingSince = now();
10103
+ logger("host_auto_update_pending", { latest, current: currentVersion });
10104
+ }
10105
+ const forced = now() - pendingSince >= AUTO_UPDATE_HARD_DEADLINE_MS;
10106
+ if (options.isActive() && !forced)
10107
+ return;
10108
+ if (options.isMidWork())
10109
+ return;
10110
+ await _attempt(forced ? "deadline-forced" : "scheduled", latest);
10111
+ },
10112
+ async onUpgradeRequired(detail) {
10113
+ if (!options.enabled) {
10114
+ logger("host_auto_update_disabled_upgrade_required", { detail });
10115
+ return;
10116
+ }
10117
+ if (options.isMidWork())
10118
+ return;
10119
+ await _attempt("relay-rejected", null);
10120
+ },
10121
+ pendingUpdateError() {
10122
+ return updateError;
10123
+ }
10124
+ };
10125
+ }
10126
+ function compareReleaseSemver(a, b) {
10127
+ const left = _releaseTuple(a);
10128
+ const right = _releaseTuple(b);
10129
+ if (left === null || right === null)
10130
+ return null;
10131
+ for (let i = 0;i < 3; i += 1) {
10132
+ if (left[i] !== right[i])
10133
+ return left[i] < right[i] ? -1 : 1;
10134
+ }
10135
+ return 0;
10136
+ }
10137
+ function _releaseTuple(value) {
10138
+ const match = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(value);
10139
+ if (!match)
10140
+ return null;
10141
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
10142
+ }
10143
+ // .dist-releases/1784894032002-2676534/src/host/host-config.js
10144
+ import { chmod as chmod3, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile6 } from "node:fs/promises";
10145
+ import { homedir as homedir10 } from "node:os";
10146
+ import path13 from "node:path";
9908
10147
  var HOST_CONFIG_KIND = "sailorbridge-host-config";
9909
10148
  function defaultHostConfigPath() {
9910
- return path12.join(homedir10(), ".sailorbridge", "host-config.json");
10149
+ return path13.join(homedir10(), ".sailorbridge", "host-config.json");
9911
10150
  }
9912
10151
  function normalizeWorkspacePath(entry) {
9913
10152
  const trimmed = entry.trim();
9914
10153
  if (!trimmed)
9915
10154
  throw new Error("workspace path must not be empty");
9916
10155
  const expanded = trimmed === "~" ? homedir10() : trimmed.startsWith("~/") ? `${homedir10()}${trimmed.slice(1)}` : trimmed;
9917
- return path12.resolve(expanded);
10156
+ return path13.resolve(expanded);
9918
10157
  }
9919
10158
  async function loadHostConfig(configPath = defaultHostConfigPath()) {
9920
10159
  let text;
9921
10160
  try {
9922
- text = await readFile5(configPath, "utf8");
10161
+ text = await readFile6(configPath, "utf8");
9923
10162
  } catch (error) {
9924
10163
  if (error.code === "ENOENT")
9925
10164
  return { kind: HOST_CONFIG_KIND, workspaces: [] };
@@ -9928,8 +10167,8 @@ async function loadHostConfig(configPath = defaultHostConfigPath()) {
9928
10167
  return _parseHostConfig(text, configPath);
9929
10168
  }
9930
10169
  async function saveHostConfig(configPath, config) {
9931
- await mkdir6(path12.dirname(configPath), { recursive: true, mode: 448 });
9932
- await chmod3(path12.dirname(configPath), 448);
10170
+ await mkdir6(path13.dirname(configPath), { recursive: true, mode: 448 });
10171
+ await chmod3(path13.dirname(configPath), 448);
9933
10172
  await writeFile6(configPath, `${JSON.stringify(config, null, 2)}
9934
10173
  `, { mode: 384 });
9935
10174
  await chmod3(configPath, 384);
@@ -9939,7 +10178,7 @@ async function addHostWorkspace(configPath, workspacePath) {
9939
10178
  const config = await loadHostConfig(configPath);
9940
10179
  if (config.workspaces.includes(normalized))
9941
10180
  return { config, path: normalized, added: false };
9942
- const next = { kind: HOST_CONFIG_KIND, workspaces: [...config.workspaces, normalized] };
10181
+ const next = { ...config, workspaces: [...config.workspaces, normalized] };
9943
10182
  await saveHostConfig(configPath, next);
9944
10183
  return { config: next, path: normalized, added: true };
9945
10184
  }
@@ -9960,7 +10199,7 @@ async function removeHostWorkspace(configPath, workspacePath) {
9960
10199
  const config = await loadHostConfig(configPath);
9961
10200
  if (!config.workspaces.includes(normalized))
9962
10201
  return { config, path: normalized, removed: false };
9963
- const next = { kind: HOST_CONFIG_KIND, workspaces: config.workspaces.filter((entry) => entry !== normalized) };
10202
+ const next = { ...config, workspaces: config.workspaces.filter((entry) => entry !== normalized) };
9964
10203
  await saveHostConfig(configPath, next);
9965
10204
  return { config: next, path: normalized, removed: true };
9966
10205
  }
@@ -9984,9 +10223,16 @@ function _parseHostConfig(text, configPath) {
9984
10223
  throw new Error(`host config workspaces[${index}] must be a non-empty string`);
9985
10224
  return value.trim();
9986
10225
  });
9987
- return { kind: HOST_CONFIG_KIND, workspaces };
10226
+ if (record.auto_update !== undefined && typeof record.auto_update !== "boolean") {
10227
+ throw new Error(`host config at ${configPath} auto_update must be a boolean`);
10228
+ }
10229
+ return {
10230
+ kind: HOST_CONFIG_KIND,
10231
+ workspaces,
10232
+ ...record.auto_update === undefined ? {} : { auto_update: record.auto_update }
10233
+ };
9988
10234
  }
9989
- // .dist-releases/1784862378006-1249051/src/host/host-workspace-cli.js
10235
+ // .dist-releases/1784894032002-2676534/src/host/host-workspace-cli.js
9990
10236
  async function runHostWorkspaceCommandFromCli(argv, io, deps = {}) {
9991
10237
  const configPath = deps.configPath ?? defaultHostConfigPath();
9992
10238
  const sub = argv[0] ?? "list";
@@ -10030,7 +10276,7 @@ function _renderAddPlain(result) {
10030
10276
  return result.note ? `${status} ${result.path}
10031
10277
  ${result.note}` : `${status} ${result.path}`;
10032
10278
  }
10033
- // .dist-releases/1784862378006-1249051/src/host/agent-env.js
10279
+ // .dist-releases/1784894032002-2676534/src/host/agent-env.js
10034
10280
  var RUNTIME_NATIVE_HOME_ENV = new Set(["CLAUDE_CONFIG_DIR", "CODEX_HOME"]);
10035
10281
  function buildAgentEnv(options) {
10036
10282
  const env = {};
@@ -10059,25 +10305,25 @@ function buildAgentEnv(options) {
10059
10305
  }
10060
10306
  return env;
10061
10307
  }
10062
- // .dist-releases/1784862378006-1249051/src/host/runtime-env.js
10063
- import path16 from "node:path";
10308
+ // .dist-releases/1784894032002-2676534/src/host/runtime-env.js
10309
+ import path17 from "node:path";
10064
10310
 
10065
- // .dist-releases/1784862378006-1249051/src/host/agent-defaults.js
10066
- import { chmod as chmod4, mkdir as mkdir7, readFile as readFile7, stat as stat2, writeFile as writeFile7 } from "node:fs/promises";
10311
+ // .dist-releases/1784894032002-2676534/src/host/agent-defaults.js
10312
+ import { chmod as chmod4, mkdir as mkdir7, readFile as readFile8, stat as stat2, writeFile as writeFile7 } from "node:fs/promises";
10067
10313
  import { homedir as homedir11 } from "node:os";
10068
- import path14 from "node:path";
10314
+ import path15 from "node:path";
10069
10315
 
10070
- // .dist-releases/1784862378006-1249051/src/host/claude-settings.js
10071
- import { readFile as readFile6 } from "node:fs/promises";
10072
- import path13 from "node:path";
10316
+ // .dist-releases/1784894032002-2676534/src/host/claude-settings.js
10317
+ import { readFile as readFile7 } from "node:fs/promises";
10318
+ import path14 from "node:path";
10073
10319
  async function readHostClaudeSettings(claudeConfigDir) {
10074
- const settings = await _readSettingsObject(path13.join(claudeConfigDir, "settings.json"));
10320
+ const settings = await _readSettingsObject(path14.join(claudeConfigDir, "settings.json"));
10075
10321
  return { env: _stringValues(settings.env), apiKeyHelper: _nonEmptyString(settings.apiKeyHelper) };
10076
10322
  }
10077
10323
  async function _readSettingsObject(filePath) {
10078
10324
  let text;
10079
10325
  try {
10080
- text = await readFile6(filePath, "utf8");
10326
+ text = await readFile7(filePath, "utf8");
10081
10327
  } catch (error) {
10082
10328
  if (error.code === "ENOENT")
10083
10329
  return {};
@@ -10108,7 +10354,7 @@ function _nonEmptyString(value) {
10108
10354
  return typeof value === "string" && value.trim() ? value : null;
10109
10355
  }
10110
10356
 
10111
- // .dist-releases/1784862378006-1249051/src/host/agent-defaults.js
10357
+ // .dist-releases/1784894032002-2676534/src/host/agent-defaults.js
10112
10358
  var CODEX_BLOCK_BEGIN = "# sailorbridge-terse-defaults:begin";
10113
10359
  var CODEX_BLOCK_END = "# sailorbridge-terse-defaults:end";
10114
10360
  var CODEX_MODEL_BLOCK_BEGIN = "# sailorbridge-host-model-settings:begin";
@@ -10760,23 +11006,23 @@ function _warnStatusLine(message) {
10760
11006
  `);
10761
11007
  }
10762
11008
  function resolveClaudeStatusLineCommand(runtime = { execPath: process.execPath, argv1: process.argv[1] }) {
10763
- const name = path14.basename(runtime.execPath);
10764
- const prefix = name === "node" || name === "bun" ? [path14.resolve(runtime.execPath), _requireEntry(runtime.argv1)] : [path14.resolve(runtime.execPath)];
11009
+ const name = path15.basename(runtime.execPath);
11010
+ const prefix = name === "node" || name === "bun" ? [path15.resolve(runtime.execPath), _requireEntry(runtime.argv1)] : [path15.resolve(runtime.execPath)];
10765
11011
  return [...prefix.map(_shellSingleQuote), "host", "statusline"].join(" ");
10766
11012
  }
10767
11013
  function _requireEntry(argv1) {
10768
11014
  const entry = argv1?.trim();
10769
11015
  if (!entry)
10770
11016
  throw new Error("cannot resolve claude statusline command: client entry point is unknown");
10771
- return path14.resolve(entry);
11017
+ return path15.resolve(entry);
10772
11018
  }
10773
11019
  async function _materializeClaudeDefaults(homeDir, workspaceDir, statusLineCommand, warn = _warnStatusLine, sourceHomeDir) {
10774
- const settingsPath = path14.join(homeDir, "settings.json");
10775
- const stylePath = path14.join(homeDir, "output-styles", "sailorbridge-terse.md");
10776
- const hooksDir = path14.join(homeDir, "hooks");
10777
- const hygienePath = path14.join(hooksDir, "tool-output-hygiene.py");
10778
- const refsPath = path14.join(hooksDir, "stale-refs-guard.py");
10779
- await mkdir7(path14.dirname(stylePath), { recursive: true });
11020
+ const settingsPath = path15.join(homeDir, "settings.json");
11021
+ const stylePath = path15.join(homeDir, "output-styles", "sailorbridge-terse.md");
11022
+ const hooksDir = path15.join(homeDir, "hooks");
11023
+ const hygienePath = path15.join(hooksDir, "tool-output-hygiene.py");
11024
+ const refsPath = path15.join(hooksDir, "stale-refs-guard.py");
11025
+ await mkdir7(path15.dirname(stylePath), { recursive: true });
10780
11026
  await _writeManagedFile(stylePath, CLAUDE_TERSE_STYLE, false);
10781
11027
  await mkdir7(hooksDir, { recursive: true });
10782
11028
  await _writeManagedFile(hygienePath, TOOL_OUTPUT_HYGIENE, true);
@@ -10791,7 +11037,7 @@ async function _materializeClaudeDefaults(homeDir, workspaceDir, statusLineComma
10791
11037
  { matcher: "Bash", command: `python3 ${_shellSingleQuote(refsPath)}` }
10792
11038
  ]);
10793
11039
  _ensureClaudeStatusLine(settings, statusLineCommand ?? resolveClaudeStatusLineCommand(), warn);
10794
- await mkdir7(path14.dirname(settingsPath), { recursive: true });
11040
+ await mkdir7(path15.dirname(settingsPath), { recursive: true });
10795
11041
  await writeFile7(settingsPath, JSON.stringify(settings, null, 2) + `
10796
11042
  `, { mode: 384 });
10797
11043
  if (workspaceDir)
@@ -10806,10 +11052,10 @@ async function _mergeHostClaudeEnv(settings, sourceHomeDir) {
10806
11052
  settings.env = { ..._jsonObject(settings.env, "Claude settings env"), ...hostEnv };
10807
11053
  }
10808
11054
  async function _trustClaudeWorkspace(homeDir, workspaceDir) {
10809
- const configPath = path14.join(homeDir, ".claude.json");
11055
+ const configPath = path15.join(homeDir, ".claude.json");
10810
11056
  const config = await _readJsonObject(configPath);
10811
11057
  const projects = _jsonObject(config.projects, "Claude config projects");
10812
- const projectPath = path14.resolve(_expandHome3(workspaceDir));
11058
+ const projectPath = path15.resolve(_expandHome3(workspaceDir));
10813
11059
  for (const trustedPath of await _claudeTrustRoots(projectPath)) {
10814
11060
  const project = _jsonObject(projects[trustedPath], `Claude config project ${trustedPath}`);
10815
11061
  project.hasTrustDialogAccepted = true;
@@ -10817,12 +11063,12 @@ async function _trustClaudeWorkspace(homeDir, workspaceDir) {
10817
11063
  }
10818
11064
  config.projects = projects;
10819
11065
  config.hasCompletedOnboarding = true;
10820
- await mkdir7(path14.dirname(configPath), { recursive: true });
11066
+ await mkdir7(path15.dirname(configPath), { recursive: true });
10821
11067
  await writeFile7(configPath, JSON.stringify(config, null, 2) + `
10822
11068
  `, { mode: 384 });
10823
11069
  }
10824
11070
  async function _claudeTrustRoots(projectPath) {
10825
- const pointer = path14.join(projectPath, ".git");
11071
+ const pointer = path15.join(projectPath, ".git");
10826
11072
  let stats;
10827
11073
  try {
10828
11074
  stats = await stat2(pointer);
@@ -10831,12 +11077,12 @@ async function _claudeTrustRoots(projectPath) {
10831
11077
  }
10832
11078
  if (!stats.isFile())
10833
11079
  return [projectPath];
10834
- const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await readFile7(pointer, "utf8"))?.[1];
11080
+ const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await readFile8(pointer, "utf8"))?.[1];
10835
11081
  if (!gitdir) {
10836
11082
  throw new Error(`workspace ${projectPath} has an unreadable .git pointer — cannot pre-trust its main repository for claude`);
10837
11083
  }
10838
- const resolved = path14.resolve(projectPath, gitdir);
10839
- const marker = `${path14.sep}.git${path14.sep}worktrees${path14.sep}`;
11084
+ const resolved = path15.resolve(projectPath, gitdir);
11085
+ const marker = `${path15.sep}.git${path15.sep}worktrees${path15.sep}`;
10840
11086
  const markerIndex = resolved.indexOf(marker);
10841
11087
  if (markerIndex === -1)
10842
11088
  return [projectPath];
@@ -10865,26 +11111,26 @@ function _claudeStatusLineShouldBeReplaced(command) {
10865
11111
  if (CLAUDE_STATUSLINE_SUBCOMMAND_RE.test(trimmed))
10866
11112
  return true;
10867
11113
  const first = trimmed.split(/\s+/u)[0] ?? "";
10868
- return SAIL_CLAUDE_STATUSLINE_BASENAMES.has(path14.basename(first));
11114
+ return SAIL_CLAUDE_STATUSLINE_BASENAMES.has(path15.basename(first));
10869
11115
  }
10870
11116
  function _claudeStatusLineConfig(command) {
10871
11117
  return { type: "command", command, refreshInterval: 20 };
10872
11118
  }
10873
11119
  async function _materializeCodexDefaults(homeDir, workspaceDir, sourceHomeDir, warn = _warnStatusLine) {
10874
- const configPath = path14.join(homeDir, "config.toml");
11120
+ const configPath = path15.join(homeDir, "config.toml");
10875
11121
  let current = await _readTextIfExists2(configPath);
10876
11122
  current = current.replace(new RegExp(`${_escapeRegExp(CODEX_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_BLOCK_END)}\\n?`, "u"), "");
10877
11123
  current = current.replace(new RegExp(`${_escapeRegExp(CODEX_TRUST_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_TRUST_BLOCK_END)}\\n?`, "u"), "");
10878
11124
  current = await _refreshCodexModelSettings(current, sourceHomeDir);
10879
11125
  current = _removeTopLevelTomlKeys(current, CODEX_UPDATE_CHECK_KEYS);
10880
- const trustedWorkspace = workspaceDir ? path14.resolve(_expandHome3(workspaceDir)) : undefined;
11126
+ const trustedWorkspace = workspaceDir ? path15.resolve(_expandHome3(workspaceDir)) : undefined;
10881
11127
  const trust = trustedWorkspace ? _setExistingCodexProjectTrust(current, trustedWorkspace) : { text: current, found: false };
10882
11128
  current = trust.text;
10883
11129
  const missing = Object.entries(CODEX_DEFAULTS).filter(([key]) => !_hasTopLevelTomlKey(current, key));
10884
11130
  const defaultsBlock = _codexManagedBlock(missing);
10885
11131
  const trustBlock = trustedWorkspace && !trust.found ? _codexTrustBlock(trustedWorkspace) : "";
10886
11132
  const next = _ensureCodexStatusLine(_appendTomlBlock(_insertBeforeFirstTomlTable(current, defaultsBlock), trustBlock), warn);
10887
- await mkdir7(path14.dirname(configPath), { recursive: true });
11133
+ await mkdir7(path15.dirname(configPath), { recursive: true });
10888
11134
  await writeFile7(configPath, next, { mode: 384 });
10889
11135
  }
10890
11136
  function _ensureCodexStatusLine(text, warn) {
@@ -10934,7 +11180,7 @@ ${CODEX_CONTEXT_STATUS_LINE}`);
10934
11180
  async function _refreshCodexModelSettings(current, sourceHomeDir) {
10935
11181
  if (!sourceHomeDir)
10936
11182
  return current;
10937
- const source = await _readTextIfExists2(path14.join(sourceHomeDir, "config.toml"));
11183
+ const source = await _readTextIfExists2(path15.join(sourceHomeDir, "config.toml"));
10938
11184
  const entries = _codexModelEntries(source);
10939
11185
  const withoutManaged = current.replace(new RegExp(`${_escapeRegExp(CODEX_MODEL_BLOCK_BEGIN)}[\\s\\S]*?${_escapeRegExp(CODEX_MODEL_BLOCK_END)}\\n?`, "u"), "");
10940
11186
  const keys = new Set(entries.map(({ key }) => key));
@@ -11201,7 +11447,7 @@ async function _writeManagedFile(filePath, content, executable) {
11201
11447
  if (existing && existing !== content && !existing.includes(MANAGED_MARKER2) && !_matchesMarkerlessManagedContent(existing, content)) {
11202
11448
  throw new Error(`refusing to overwrite unmanaged file: ${filePath}`);
11203
11449
  }
11204
- await mkdir7(path14.dirname(filePath), { recursive: true });
11450
+ await mkdir7(path15.dirname(filePath), { recursive: true });
11205
11451
  await writeFile7(filePath, content, { mode: executable ? 493 : 384 });
11206
11452
  if (executable)
11207
11453
  await chmod4(filePath, 493);
@@ -11217,13 +11463,13 @@ async function _readTextIfExists2(filePath) {
11217
11463
  return "";
11218
11464
  throw error;
11219
11465
  }
11220
- return readFile7(filePath, "utf8");
11466
+ return readFile8(filePath, "utf8");
11221
11467
  }
11222
11468
  function _expandHome3(value) {
11223
11469
  if (value === "~")
11224
11470
  return homedir11();
11225
11471
  if (value.startsWith("~/"))
11226
- return path14.join(homedir11(), value.slice(2));
11472
+ return path15.join(homedir11(), value.slice(2));
11227
11473
  return value;
11228
11474
  }
11229
11475
  function _shellSingleQuote(value) {
@@ -11238,10 +11484,10 @@ function _escapeRegExp(value) {
11238
11484
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
11239
11485
  }
11240
11486
 
11241
- // .dist-releases/1784862378006-1249051/src/host/engine-auth.js
11487
+ // .dist-releases/1784894032002-2676534/src/host/engine-auth.js
11242
11488
  import { lstat as lstat4, mkdir as mkdir8, readlink, realpath, stat as stat3, symlink as symlink2, unlink as unlink2 } from "node:fs/promises";
11243
11489
  import { homedir as homedir12 } from "node:os";
11244
- import path15 from "node:path";
11490
+ import path16 from "node:path";
11245
11491
  class EngineNotLoggedInError extends Error {
11246
11492
  constructor(engine, authPath) {
11247
11493
  super(`engine-auth-missing[${engine}]: ${_notLoggedInMessage(engine, authPath)}`);
@@ -11256,9 +11502,9 @@ async function provisionEngineAuth(options) {
11256
11502
  return null;
11257
11503
  throw new EngineNotLoggedInError(options.engine, source);
11258
11504
  }
11259
- const target = path15.join(options.homeDir, engineAuthFileName(options.engine));
11505
+ const target = path16.join(options.homeDir, engineAuthFileName(options.engine));
11260
11506
  const sharedTargetPath = await realpath(source);
11261
- await mkdir8(path15.dirname(target), { recursive: true });
11507
+ await mkdir8(path16.dirname(target), { recursive: true });
11262
11508
  await _ensureSymlink(target, source, sharedTargetPath);
11263
11509
  return { linkPath: target, sharedTargetPath };
11264
11510
  }
@@ -11267,7 +11513,7 @@ async function _hasEnvBasedAuth(engine, env) {
11267
11513
  return _nonEmpty(env.OPENAI_API_KEY);
11268
11514
  if (_nonEmpty(env.ANTHROPIC_AUTH_TOKEN) || _nonEmpty(env.ANTHROPIC_API_KEY))
11269
11515
  return true;
11270
- const settings = await readHostClaudeSettings(path15.dirname(engineAuthSourcePath("claude", env)));
11516
+ const settings = await readHostClaudeSettings(path16.dirname(engineAuthSourcePath("claude", env)));
11271
11517
  return _nonEmpty(settings.env.ANTHROPIC_AUTH_TOKEN) || _nonEmpty(settings.env.ANTHROPIC_API_KEY) || settings.apiKeyHelper !== null;
11272
11518
  }
11273
11519
  function _nonEmpty(value) {
@@ -11277,12 +11523,12 @@ function _notLoggedInMessage(engine, authPath) {
11277
11523
  if (engine === "codex") {
11278
11524
  return `engine is not authenticated: codex has no usable credential source. Provide either (1) auth.json at ${authPath} via codex login, or (2) OPENAI_API_KEY in the process environment.`;
11279
11525
  }
11280
- const settingsPath = path15.join(path15.dirname(authPath), "settings.json");
11526
+ const settingsPath = path16.join(path16.dirname(authPath), "settings.json");
11281
11527
  return `engine is not authenticated: claude has no usable credential source. Provide one of: (1) ${authPath} via claude login; ` + `(2) ANTHROPIC_AUTH_TOKEN/ANTHROPIC_API_KEY or apiKeyHelper in ${settingsPath}; ` + `(3) ANTHROPIC_AUTH_TOKEN/ANTHROPIC_API_KEY in the process environment.`;
11282
11528
  }
11283
11529
  function engineAuthSourcePath(engine, env = process.env) {
11284
11530
  const spec = _engineAuthSpec(engine, env);
11285
- return path15.join(spec.sourceDir, spec.fileName);
11531
+ return path16.join(spec.sourceDir, spec.fileName);
11286
11532
  }
11287
11533
  function engineAuthFileName(engine) {
11288
11534
  return _engineAuthSpec(engine, {}).fileName;
@@ -11290,9 +11536,9 @@ function engineAuthFileName(engine) {
11290
11536
  function _engineAuthSpec(engine, env) {
11291
11537
  const home = env.HOME?.trim() || homedir12();
11292
11538
  if (engine === "codex") {
11293
- return { sourceDir: env.CODEX_HOME?.trim() || path15.join(home, ".codex"), fileName: "auth.json" };
11539
+ return { sourceDir: env.CODEX_HOME?.trim() || path16.join(home, ".codex"), fileName: "auth.json" };
11294
11540
  }
11295
- return { sourceDir: env.CLAUDE_CONFIG_DIR?.trim() || path15.join(home, ".claude"), fileName: ".credentials.json" };
11541
+ return { sourceDir: env.CLAUDE_CONFIG_DIR?.trim() || path16.join(home, ".claude"), fileName: ".credentials.json" };
11296
11542
  }
11297
11543
  async function _isReadableAuthFile(filePath) {
11298
11544
  try {
@@ -11321,14 +11567,14 @@ async function _ensureSymlink(linkPath, targetPath, sharedTargetPath) {
11321
11567
  return;
11322
11568
  }
11323
11569
  const current = await readlink(linkPath);
11324
- const resolved = path15.resolve(path15.dirname(linkPath), current);
11325
- if (resolved === path15.resolve(targetPath))
11570
+ const resolved = path16.resolve(path16.dirname(linkPath), current);
11571
+ if (resolved === path16.resolve(targetPath))
11326
11572
  return;
11327
11573
  await unlink2(linkPath);
11328
11574
  await symlink2(targetPath, linkPath);
11329
11575
  }
11330
11576
 
11331
- // .dist-releases/1784862378006-1249051/src/host/runtime-env.js
11577
+ // .dist-releases/1784894032002-2676534/src/host/runtime-env.js
11332
11578
  async function materializeAgentRuntimeEnv(options) {
11333
11579
  const env = buildAgentEnv({
11334
11580
  hostEnv: options.hostEnv,
@@ -11347,7 +11593,7 @@ async function materializeAgentRuntimeEnv(options) {
11347
11593
  agentName: options.agentName,
11348
11594
  engine: memoryStub.engine,
11349
11595
  homeDir: memoryStub.homeDir,
11350
- sourceHomeDir: path16.dirname(engineAuthSourcePath(memoryStub.engine, options.hostEnv))
11596
+ sourceHomeDir: path17.dirname(engineAuthSourcePath(memoryStub.engine, options.hostEnv))
11351
11597
  });
11352
11598
  const credentialGuard = await provisionEngineAuth({
11353
11599
  engine: memoryStub.engine,
@@ -11368,7 +11614,7 @@ function _ensureRootSandboxEscape(env, engine, uid) {
11368
11614
  function _currentUid() {
11369
11615
  return typeof process.getuid === "function" ? process.getuid() : null;
11370
11616
  }
11371
- // .dist-releases/1784862378006-1249051/src/host/context-statusline.js
11617
+ // .dist-releases/1784894032002-2676534/src/host/context-statusline.js
11372
11618
  function renderClaudeContextStatusLine(payload) {
11373
11619
  const percent = _usedPercentage(payload);
11374
11620
  if (percent === null)
@@ -11406,12 +11652,12 @@ function _bar(percent) {
11406
11652
  const filled = Math.min(10, Math.max(0, Math.trunc(percent / 10)));
11407
11653
  return "█".repeat(filled) + "░".repeat(10 - filled);
11408
11654
  }
11409
- // .dist-releases/1784862378006-1249051/src/host/scratch-sweeper.js
11655
+ // .dist-releases/1784894032002-2676534/src/host/scratch-sweeper.js
11410
11656
  import { execFile as execFile3 } from "node:child_process";
11411
11657
  import { lstat as lstat5, readdir as readdir3, realpath as realpath2, unlink as unlink3 } from "node:fs/promises";
11412
- import path17 from "node:path";
11413
- import { promisify as promisify4 } from "node:util";
11414
- var execFileAsync3 = promisify4(execFile3);
11658
+ import path18 from "node:path";
11659
+ import { promisify as promisify5 } from "node:util";
11660
+ var execFileAsync3 = promisify5(execFile3);
11415
11661
  var SCRATCH_SUBDIR = ".agent/tmp";
11416
11662
  var SCRATCH_SWEEP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
11417
11663
  var SCRATCH_SWEEP_MAX_BYTES = 512 * 1024 * 1024;
@@ -11423,8 +11669,8 @@ function scratchSweepEnabled(env) {
11423
11669
  return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
11424
11670
  }
11425
11671
  async function sweepWorkspaceScratch(options) {
11426
- const root = path17.resolve(options.workspaceRoot);
11427
- const tmpDir = path17.join(root, SCRATCH_SUBDIR);
11672
+ const root = path18.resolve(options.workspaceRoot);
11673
+ const tmpDir = path18.join(root, SCRATCH_SUBDIR);
11428
11674
  const state = await _tmpDirState(root, tmpDir);
11429
11675
  if (state === "missing")
11430
11676
  return EMPTY;
@@ -11462,7 +11708,7 @@ async function _trackedScratchFiles(root, options) {
11462
11708
  const tracked = new Set;
11463
11709
  for (const rel of stdout.split("\x00"))
11464
11710
  if (rel)
11465
- tracked.add(path17.join(root, rel));
11711
+ tracked.add(path18.join(root, rel));
11466
11712
  return tracked;
11467
11713
  } catch (error) {
11468
11714
  options.logger?.("host_scratch_sweep_git_failed", { workspace: root, error: _errno(error) });
@@ -11472,7 +11718,7 @@ async function _trackedScratchFiles(root, options) {
11472
11718
  async function _collectScratchFiles(dir, root, logger) {
11473
11719
  const out = [];
11474
11720
  for (const entry of await readdir3(dir, { withFileTypes: true })) {
11475
- const abs = path17.join(dir, entry.name);
11721
+ const abs = path18.join(dir, entry.name);
11476
11722
  if (!_within(root, abs)) {
11477
11723
  logger?.("host_scratch_sweep_out_of_bounds", { workspace: root, path: abs });
11478
11724
  continue;
@@ -11538,8 +11784,8 @@ async function _runGit2(root, args) {
11538
11784
  return String(stdout);
11539
11785
  }
11540
11786
  function _within(root, target) {
11541
- const rel = path17.relative(root, target);
11542
- return rel === "" || !rel.startsWith("..") && !path17.isAbsolute(rel);
11787
+ const rel = path18.relative(root, target);
11788
+ return rel === "" || !rel.startsWith("..") && !path18.isAbsolute(rel);
11543
11789
  }
11544
11790
  function _isEnoent(error) {
11545
11791
  return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
@@ -11549,7 +11795,7 @@ function _errno(error) {
11549
11795
  return String(error.code);
11550
11796
  return error instanceof Error ? error.message : String(error);
11551
11797
  }
11552
- // .dist-releases/1784862378006-1249051/src/host/launcher.js
11798
+ // .dist-releases/1784894032002-2676534/src/host/launcher.js
11553
11799
  var MAX_TMUX_SESSION_SUFFIX = 100;
11554
11800
  async function launchSupervisedAgent(input, deps = {}) {
11555
11801
  const spec = parseAgentStartSpec(input.spec);
@@ -11700,10 +11946,10 @@ async function _tmuxSessionExists(session) {
11700
11946
  throw error;
11701
11947
  }
11702
11948
  }
11703
- // .dist-releases/1784862378006-1249051/src/worker/capability.js
11949
+ // .dist-releases/1784894032002-2676534/src/worker/capability.js
11704
11950
  import { accessSync, constants as constants2, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
11705
11951
  import os from "node:os";
11706
- import path18 from "node:path";
11952
+ import path19 from "node:path";
11707
11953
  import { spawnSync } from "node:child_process";
11708
11954
  var TOOLS = ["podman", "git", "kubectl", "perf", "bpftrace"];
11709
11955
  var GPU_TAGS = new Set(["intel-b60", "nvidia-h100", "none"]);
@@ -11803,10 +12049,10 @@ function parseOsReleaseLine(line) {
11803
12049
  }
11804
12050
  function isReadonly(dir) {
11805
12051
  try {
11806
- const root = path18.resolve(dir);
12052
+ const root = path19.resolve(dir);
11807
12053
  mkdirSync3(root, { recursive: true });
11808
- const temp = mkdtempSync(path18.join(root, ".sailorbridge-write-test-"));
11809
- writeFileSync2(path18.join(temp, "probe"), "");
12054
+ const temp = mkdtempSync(path19.join(root, ".sailorbridge-write-test-"));
12055
+ writeFileSync2(path19.join(temp, "probe"), "");
11810
12056
  rmSync(temp, { recursive: true, force: true });
11811
12057
  return false;
11812
12058
  } catch {
@@ -11831,10 +12077,10 @@ function commandExists(command) {
11831
12077
  return commandPath(command) !== null;
11832
12078
  }
11833
12079
  function commandPath(command) {
11834
- if (command.includes(path18.sep))
12080
+ if (command.includes(path19.sep))
11835
12081
  return command;
11836
- for (const entry of (process.env.PATH ?? "").split(path18.delimiter)) {
11837
- const candidate = path18.join(entry, command);
12082
+ for (const entry of (process.env.PATH ?? "").split(path19.delimiter)) {
12083
+ const candidate = path19.join(entry, command);
11838
12084
  try {
11839
12085
  accessSync(candidate, constants2.X_OK);
11840
12086
  return candidate;
@@ -11843,7 +12089,7 @@ function commandPath(command) {
11843
12089
  return null;
11844
12090
  }
11845
12091
 
11846
- // .dist-releases/1784862378006-1249051/src/host/worker-start-spec.js
12092
+ // .dist-releases/1784894032002-2676534/src/host/worker-start-spec.js
11847
12093
  var MAX_TAGS = 32;
11848
12094
  var TAG_PATTERN = /^[a-z0-9][a-z0-9=._:/-]{0,63}$/iu;
11849
12095
  var RESERVED_TAG_KEYS = new Set(["os", "kernel", "arch", "gpu", "mode", "role", "tool"]);
@@ -11903,16 +12149,16 @@ function _trimmed2(value, label) {
11903
12149
  return trimmed;
11904
12150
  }
11905
12151
 
11906
- // .dist-releases/1784862378006-1249051/src/host/worker-launcher.js
12152
+ // .dist-releases/1784894032002-2676534/src/host/worker-launcher.js
11907
12153
  import { statSync as statSync3 } from "node:fs";
11908
12154
  import os4 from "node:os";
11909
- import path21 from "node:path";
11910
- // .dist-releases/1784862378006-1249051/src/worker/client.js
12155
+ import path22 from "node:path";
12156
+ // .dist-releases/1784894032002-2676534/src/worker/client.js
11911
12157
  import { readFileSync as readFileSync5 } from "node:fs";
11912
12158
  import os3 from "node:os";
11913
- import path20 from "node:path";
12159
+ import path21 from "node:path";
11914
12160
 
11915
- // .dist-releases/1784862378006-1249051/src/worker/errors.js
12161
+ // .dist-releases/1784894032002-2676534/src/worker/errors.js
11916
12162
  class LeaseFencingRejected extends Error {
11917
12163
  status;
11918
12164
  constructor(status, message) {
@@ -11926,10 +12172,10 @@ class WorkerHttpError extends Error {
11926
12172
  method;
11927
12173
  path;
11928
12174
  status;
11929
- constructor(method, path19, status, message) {
12175
+ constructor(method, path20, status, message) {
11930
12176
  super(message || `HTTP ${status}`);
11931
12177
  this.method = method;
11932
- this.path = path19;
12178
+ this.path = path20;
11933
12179
  this.status = status;
11934
12180
  this.name = "WorkerHttpError";
11935
12181
  }
@@ -11938,10 +12184,10 @@ class WorkerHttpError extends Error {
11938
12184
  class WorkerNetworkError extends Error {
11939
12185
  method;
11940
12186
  path;
11941
- constructor(method, path19, message) {
12187
+ constructor(method, path20, message) {
11942
12188
  super(message);
11943
12189
  this.method = method;
11944
- this.path = path19;
12190
+ this.path = path20;
11945
12191
  this.name = "WorkerNetworkError";
11946
12192
  }
11947
12193
  }
@@ -11949,14 +12195,14 @@ var LEASE_FENCING_BAD_REQUEST_MESSAGES = [
11949
12195
  "lease_token is required",
11950
12196
  "lease_token must be a non-empty string"
11951
12197
  ];
11952
- function raiseForLeaseFencingOrStatus(method, path19, status, body) {
12198
+ function raiseForLeaseFencingOrStatus(method, path20, status, body) {
11953
12199
  if (status === 409)
11954
12200
  throw new LeaseFencingRejected(status, body);
11955
12201
  if (status === 400 && LEASE_FENCING_BAD_REQUEST_MESSAGES.some((message) => body.includes(message))) {
11956
12202
  throw new LeaseFencingRejected(status, body);
11957
12203
  }
11958
12204
  if (status >= 300)
11959
- throw new WorkerHttpError(method, path19, status, body);
12205
+ throw new WorkerHttpError(method, path20, status, body);
11960
12206
  }
11961
12207
  function isNetworkError(error) {
11962
12208
  if (error instanceof WorkerNetworkError)
@@ -11968,23 +12214,23 @@ function isNetworkError(error) {
11968
12214
  const code = error.code;
11969
12215
  return code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EPIPE" || error.name === "AbortError";
11970
12216
  }
11971
- function countsTowardWorkerSessionRebuild(path19) {
11972
- if (path19 === "/api/v1/workers/register")
12217
+ function countsTowardWorkerSessionRebuild(path20) {
12218
+ if (path20 === "/api/v1/workers/register")
11973
12219
  return false;
11974
- if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(path19))
12220
+ if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(path20))
11975
12221
  return false;
11976
12222
  return true;
11977
12223
  }
11978
12224
 
11979
- // .dist-releases/1784862378006-1249051/src/worker/executor.js
11980
- import { spawn as spawn5 } from "node:child_process";
12225
+ // .dist-releases/1784894032002-2676534/src/worker/executor.js
12226
+ import { spawn as spawn6 } from "node:child_process";
11981
12227
  import { mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, realpathSync, rmSync as rmSync2, statfsSync, writeFileSync as writeFileSync3 } from "node:fs";
11982
12228
  import { tmpdir as tmpdir2, loadavg } from "node:os";
11983
12229
  import os2 from "node:os";
11984
- import path19 from "node:path";
12230
+ import path20 from "node:path";
11985
12231
  import { TextDecoder } from "node:util";
11986
12232
 
11987
- // .dist-releases/1784862378006-1249051/src/worker/async-queue.js
12233
+ // .dist-releases/1784894032002-2676534/src/worker/async-queue.js
11988
12234
  class AsyncByteQueue {
11989
12235
  maxSize;
11990
12236
  values = [];
@@ -12044,7 +12290,7 @@ class AsyncByteQueue {
12044
12290
  }
12045
12291
  }
12046
12292
 
12047
- // .dist-releases/1784862378006-1249051/src/worker/http.js
12293
+ // .dist-releases/1784894032002-2676534/src/worker/http.js
12048
12294
  import { request as httpRequest } from "node:http";
12049
12295
  import { request as httpsRequest } from "node:https";
12050
12296
  import { URLSearchParams as URLSearchParams2 } from "node:url";
@@ -12074,27 +12320,27 @@ class WorkerHttpClient {
12074
12320
  throw new Error("token is required");
12075
12321
  this.relayUrl = relayUrl.replace(/\/+$/u, "");
12076
12322
  }
12077
- async getJson(path19, query, timeouts, options = {}) {
12323
+ async getJson(path20, query, timeouts, options = {}) {
12078
12324
  const suffix = new URLSearchParams2(query).toString();
12079
- return this.requestJson("GET", suffix ? `${path19}?${suffix}` : path19, undefined, timeouts, options);
12325
+ return this.requestJson("GET", suffix ? `${path20}?${suffix}` : path20, undefined, timeouts, options);
12080
12326
  }
12081
- async postJson(path19, value, timeouts) {
12082
- return this.requestJson("POST", path19, { type: "json", value }, timeouts, {});
12327
+ async postJson(path20, value, timeouts) {
12328
+ return this.requestJson("POST", path20, { type: "json", value }, timeouts, {});
12083
12329
  }
12084
- async postBytes(path19, body, headers, timeouts) {
12085
- const response = await this.request("POST", path19, { type: "bytes", value: body }, timeouts, headers);
12086
- raiseForLeaseFencingOrStatus("POST", path19, response.status, response.text);
12330
+ async postBytes(path20, body, headers, timeouts) {
12331
+ const response = await this.request("POST", path20, { type: "bytes", value: body }, timeouts, headers);
12332
+ raiseForLeaseFencingOrStatus("POST", path20, response.status, response.text);
12087
12333
  }
12088
- async requestJson(method, path19, body, timeouts, options) {
12089
- const response = await this.request(method, path19, body, timeouts, {});
12090
- const errorPath = pathOnly(path19);
12334
+ async requestJson(method, path20, body, timeouts, options) {
12335
+ const response = await this.request(method, path20, body, timeouts, {});
12336
+ const errorPath = pathOnly(path20);
12091
12337
  if (options.leaseFencing === false && response.status >= 300)
12092
12338
  throw new WorkerHttpError(method, errorPath, response.status, response.text);
12093
12339
  raiseForLeaseFencingOrStatus(method, errorPath, response.status, response.text);
12094
12340
  return parseJson(method, errorPath, response.text);
12095
12341
  }
12096
- async request(method, path19, body, timeouts, extraHeaders) {
12097
- const url = new URL(`${this.relayUrl}${path19}`);
12342
+ async request(method, path20, body, timeouts, extraHeaders) {
12343
+ const url = new URL(`${this.relayUrl}${path20}`);
12098
12344
  const headers = this.headers(body, extraHeaders);
12099
12345
  return sendRequest(method, url, body, headers, timeouts, this.signal);
12100
12346
  }
@@ -12221,7 +12467,7 @@ function waitForRequestDrain(req) {
12221
12467
  req.once("close", onClose);
12222
12468
  });
12223
12469
  }
12224
- function parseJson(method, path19, text) {
12470
+ function parseJson(method, path20, text) {
12225
12471
  try {
12226
12472
  const parsed = JSON.parse(text);
12227
12473
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -12230,15 +12476,15 @@ function parseJson(method, path19, text) {
12230
12476
  return parsed;
12231
12477
  } catch (error) {
12232
12478
  if (error instanceof SyntaxError)
12233
- throw new WorkerNetworkError(method, path19, "response JSON is invalid");
12479
+ throw new WorkerNetworkError(method, path20, "response JSON is invalid");
12234
12480
  throw error;
12235
12481
  }
12236
12482
  }
12237
- function pathOnly(path19) {
12238
- return path19.split("?", 1)[0] || path19;
12483
+ function pathOnly(path20) {
12484
+ return path20.split("?", 1)[0] || path20;
12239
12485
  }
12240
12486
 
12241
- // .dist-releases/1784862378006-1249051/src/worker/stream-upload.js
12487
+ // .dist-releases/1784894032002-2676534/src/worker/stream-upload.js
12242
12488
  async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId) {
12243
12489
  if (!leaseToken.trim())
12244
12490
  throw new Error("lease_token is required");
@@ -12248,7 +12494,7 @@ async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId
12248
12494
  await session.http.postBytes(`/api/v1/jobs/${encodeURIComponent(jobId)}/stream-upload?${query.toString()}`, chunks, { "X-Lease-Token": leaseToken }, UPLOAD_TIMEOUTS);
12249
12495
  }
12250
12496
 
12251
- // .dist-releases/1784862378006-1249051/src/worker/executor.js
12497
+ // .dist-releases/1784894032002-2676534/src/worker/executor.js
12252
12498
  class StreamingProcessFailure extends Error {
12253
12499
  exitCode;
12254
12500
  stdout;
@@ -12320,8 +12566,8 @@ function sandboxPath(workdir, requested) {
12320
12566
  if (requested.startsWith("/") || parts.length === 0 || parts.includes("..")) {
12321
12567
  throw new Error("path must be a relative path inside the worker sandbox");
12322
12568
  }
12323
- const root = realpathSync(path19.resolve(workdir));
12324
- const target = path19.resolve(root, ...parts);
12569
+ const root = realpathSync(path20.resolve(workdir));
12570
+ const target = path20.resolve(root, ...parts);
12325
12571
  if (!isPathInside(root, target)) {
12326
12572
  throw new Error("path escapes worker sandbox");
12327
12573
  }
@@ -12461,7 +12707,7 @@ async function collectAndUpload(session, jobId, stream, pipe, leaseToken, worker
12461
12707
  return collected.join("");
12462
12708
  }
12463
12709
  function spawnProcess(argv, cwd) {
12464
- return spawn5(argv[0], argv.slice(1), { cwd, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
12710
+ return spawn6(argv[0], argv.slice(1), { cwd, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
12465
12711
  }
12466
12712
  async function terminateProcess(proc) {
12467
12713
  if (proc.pid === undefined)
@@ -12497,9 +12743,9 @@ function writeFileJob(workdir, spec, startedAt, started) {
12497
12743
  if (typeof spec.content !== "string")
12498
12744
  throw new Error("write_file spec.content must be a string");
12499
12745
  const target = sandboxPath(workdir, String(spec.path ?? ""));
12500
- mkdirSync4(path19.dirname(target), { recursive: true });
12746
+ mkdirSync4(path20.dirname(target), { recursive: true });
12501
12747
  writeFileSync3(target, spec.content, "utf8");
12502
- return output(0, path19.relative(realpathSync(path19.resolve(workdir)), target), "", startedAt, started);
12748
+ return output(0, path20.relative(realpathSync(path20.resolve(workdir)), target), "", startedAt, started);
12503
12749
  }
12504
12750
  function readUtf8File(target) {
12505
12751
  return decodeUtf8(readFileSync4(target));
@@ -12507,8 +12753,8 @@ function readUtf8File(target) {
12507
12753
  async function applyDiff(workdir, spec, signal) {
12508
12754
  if (typeof spec.diff_unified_text !== "string")
12509
12755
  throw new Error("apply_diff spec.diff_unified_text must be a string");
12510
- const dir = mkdtempSync2(path19.join(tmpdir2(), "sailorbridge-patch-"));
12511
- const patch = path19.join(dir, "change.patch");
12756
+ const dir = mkdtempSync2(path20.join(tmpdir2(), "sailorbridge-patch-"));
12757
+ const patch = path20.join(dir, "change.patch");
12512
12758
  writeFileSync3(patch, spec.diff_unified_text, "utf8");
12513
12759
  try {
12514
12760
  return await runArgv(["patch", "-p1", "-i", patch], workdir, coerceJobTimeoutSeconds(spec) * 1000, signal);
@@ -12748,24 +12994,24 @@ function resolveExistingTarget(target) {
12748
12994
  let current = target;
12749
12995
  while (true) {
12750
12996
  try {
12751
- return path19.resolve(realpathSync(current), ...missingParts.reverse());
12997
+ return path20.resolve(realpathSync(current), ...missingParts.reverse());
12752
12998
  } catch (error) {
12753
12999
  if (error.code !== "ENOENT")
12754
13000
  throw error;
12755
- const parent = path19.dirname(current);
13001
+ const parent = path20.dirname(current);
12756
13002
  if (parent === current)
12757
13003
  throw error;
12758
- missingParts.push(path19.basename(current));
13004
+ missingParts.push(path20.basename(current));
12759
13005
  current = parent;
12760
13006
  }
12761
13007
  }
12762
13008
  }
12763
13009
  function isPathInside(root, target) {
12764
- const relative = path19.relative(root, target);
12765
- return relative === "" || !relative.startsWith("..") && !path19.isAbsolute(relative);
13010
+ const relative = path20.relative(root, target);
13011
+ return relative === "" || !relative.startsWith("..") && !path20.isAbsolute(relative);
12766
13012
  }
12767
13013
  function prepareWorkdir(workdir) {
12768
- const resolved = path19.resolve(workdir);
13014
+ const resolved = path20.resolve(workdir);
12769
13015
  mkdirSync4(resolved, { recursive: true });
12770
13016
  return resolved;
12771
13017
  }
@@ -12780,7 +13026,7 @@ function errorMessage6(error) {
12780
13026
  return error instanceof Error ? error.message : String(error);
12781
13027
  }
12782
13028
 
12783
- // .dist-releases/1784862378006-1249051/src/worker/client.js
13029
+ // .dist-releases/1784894032002-2676534/src/worker/client.js
12784
13030
  var TOKEN_FILES = [
12785
13031
  "~/.config/sailorbridge/token",
12786
13032
  "/etc/sailorbridge/token"
@@ -13119,7 +13365,7 @@ ${JSON.stringify(result)}\r
13119
13365
  `);
13120
13366
  for (const file of attachments) {
13121
13367
  yield Buffer.from(`--${multipartBoundary}\r
13122
- Content-Disposition: form-data; name="files"; filename="${path20.basename(file)}"\r
13368
+ Content-Disposition: form-data; name="files"; filename="${path21.basename(file)}"\r
13123
13369
  \r
13124
13370
  `);
13125
13371
  yield readFileSync5(file);
@@ -13153,7 +13399,7 @@ function parseFlags2(argv) {
13153
13399
  return values;
13154
13400
  }
13155
13401
  function expandHome2(value) {
13156
- return value === "~" || value.startsWith("~/") ? path20.join(os3.homedir(), value.slice(2)) : value;
13402
+ return value === "~" || value.startsWith("~/") ? path21.join(os3.homedir(), value.slice(2)) : value;
13157
13403
  }
13158
13404
  function _sleep(ms, signal) {
13159
13405
  if (signal?.aborted)
@@ -13177,7 +13423,7 @@ function errorName2(error) {
13177
13423
  function errorMessage7(error) {
13178
13424
  return error instanceof Error ? error.message : String(error);
13179
13425
  }
13180
- // .dist-releases/1784862378006-1249051/src/host/worker-launcher.js
13426
+ // .dist-releases/1784894032002-2676534/src/host/worker-launcher.js
13181
13427
  var DEFAULT_WORKER_READY_TIMEOUT_MS = 60000;
13182
13428
  async function launchSupervisedWorker(input, deps = {}) {
13183
13429
  const logger = input.logger ?? (() => {});
@@ -13260,7 +13506,7 @@ function _requireWorkerName(descriptor) {
13260
13506
  return worker;
13261
13507
  }
13262
13508
  function _resolveWorkdir(workdir, homeDir) {
13263
- return workdir === "~" || workdir.startsWith("~/") ? path21.join(homeDir, workdir.slice(2)) : workdir;
13509
+ return workdir === "~" || workdir.startsWith("~/") ? path22.join(homeDir, workdir.slice(2)) : workdir;
13264
13510
  }
13265
13511
  function _assertWorkdir(workdir) {
13266
13512
  let stat4;
@@ -13274,7 +13520,7 @@ function _assertWorkdir(workdir) {
13274
13520
  return workdir;
13275
13521
  }
13276
13522
 
13277
- // .dist-releases/1784862378006-1249051/src/host/daemon.js
13523
+ // .dist-releases/1784894032002-2676534/src/host/daemon.js
13278
13524
  async function runHostDaemon(options) {
13279
13525
  const logger = options.logger ?? (() => {});
13280
13526
  const sleep3 = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
@@ -13302,6 +13548,13 @@ async function runHostDaemon(options) {
13302
13548
  let lastRepoTipReportAt = now() - repoTipReportIntervalMs;
13303
13549
  const reconcileIntervalMs = options.reconcileIntervalMs ?? 5 * 60 * 1000;
13304
13550
  let lastReconcileAt = now() - reconcileIntervalMs;
13551
+ const autoUpdater = createAutoUpdater({
13552
+ enabled: options.autoUpdate !== false,
13553
+ isActive: () => runtimes.size > 0 || workerRuntimes.size > 0 || startLocks.size > 0 || cleanupLocks.size > 0,
13554
+ isMidWork: () => startLocks.size > 0 || cleanupLocks.size > 0,
13555
+ logger,
13556
+ deps: { now, ...options.autoUpdateDeps }
13557
+ });
13305
13558
  const _startWorker = async (request, recoveredDescriptor) => {
13306
13559
  const spec = parseWorkerStartSpec(request.spec);
13307
13560
  const descriptor = recoveredDescriptor ?? await options.resolveCredential(request);
@@ -13452,7 +13705,7 @@ async function runHostDaemon(options) {
13452
13705
  try {
13453
13706
  await _recoverDurableRuntimes();
13454
13707
  while (!options.signal?.aborted) {
13455
- const drained = await _drainOnce(options, identity, starter, logger, redactSecrets);
13708
+ const drained = await _drainOnce(options, identity, starter, logger, redactSecrets, autoUpdater);
13456
13709
  if (drained === "retired") {
13457
13710
  logger("host_daemon_retired", { host_id: options.hostId, status: 401 });
13458
13711
  break;
@@ -13588,7 +13841,7 @@ async function _loadHostConfigBestEffort(options, logger) {
13588
13841
  return { kind: "sailorbridge-host-config", workspaces: [] };
13589
13842
  }
13590
13843
  }
13591
- async function _drainOnce(options, identity, starter, logger, redactSecrets) {
13844
+ async function _drainOnce(options, identity, starter, logger, redactSecrets, autoUpdater) {
13592
13845
  try {
13593
13846
  const hostConfig = await _loadHostConfigBestEffort(options, logger);
13594
13847
  const workspaceInventory = await discoverWorkspaceInventoryBestEffort(hostWorkspacePaths(hostConfig, options.hostEnv ?? process.env), {
@@ -13597,17 +13850,20 @@ async function _drainOnce(options, identity, starter, logger, redactSecrets) {
13597
13850
  error: error instanceof Error ? error.message : String(error)
13598
13851
  })
13599
13852
  });
13853
+ const updateError = autoUpdater.pendingUpdateError();
13600
13854
  const result = await runHostRequestOnce({
13601
13855
  relay: options.relay,
13602
13856
  ...identity,
13603
13857
  starter,
13604
13858
  redactSecrets,
13605
13859
  ...workspaceInventory.length ? { workspaceInventory } : {},
13860
+ ...updateError === undefined ? {} : { updateError },
13606
13861
  onWorkspaceInventoryError: (error) => logger("host_workspace_inventory_error", {
13607
13862
  error: error instanceof Error ? error.message : String(error)
13608
13863
  }),
13609
13864
  ...options.signal ? { signal: options.signal } : {}
13610
13865
  });
13866
+ await autoUpdater.onHostReport(result.host);
13611
13867
  if (result.status === "idle")
13612
13868
  return false;
13613
13869
  logger(`host_request_${result.status}`, {
@@ -13619,11 +13875,16 @@ async function _drainOnce(options, identity, starter, logger, redactSecrets) {
13619
13875
  } catch (error) {
13620
13876
  if (error instanceof RelayHttpError && error.status === 401)
13621
13877
  return "retired";
13878
+ if (error instanceof RelayHttpError && error.status === 426) {
13879
+ logger("host_daemon_upgrade_required", { error: error.message });
13880
+ await autoUpdater.onUpgradeRequired(error.body);
13881
+ return false;
13882
+ }
13622
13883
  logger("host_daemon_poll_error", { error: error instanceof Error ? error.message : String(error) });
13623
13884
  return false;
13624
13885
  }
13625
13886
  }
13626
- // .dist-releases/1784862378006-1249051/src/host/enrollment-redeem.js
13887
+ // .dist-releases/1784894032002-2676534/src/host/enrollment-redeem.js
13627
13888
  var DEFAULT_NOT_FOUND_RETRIES = 3;
13628
13889
  var NOT_FOUND_RETRY_DELAY_MS = 500;
13629
13890
  function createEnrollmentRedeemer(options) {
@@ -13760,7 +14021,7 @@ function _role2(enrollment) {
13760
14021
  }
13761
14022
  return _string4(enrollment.roles[0], "roles[0]").trim();
13762
14023
  }
13763
- // .dist-releases/1784862378006-1249051/src/host/tutorial.js
14024
+ // .dist-releases/1784894032002-2676534/src/host/tutorial.js
13764
14025
  function buildTutorialSteps(state) {
13765
14026
  const paired = state.hasCredential;
13766
14027
  return [
@@ -13831,7 +14092,7 @@ async function runTutorial(options) {
13831
14092
  }
13832
14093
  return steps;
13833
14094
  }
13834
- // .dist-releases/1784862378006-1249051/src/events.js
14095
+ // .dist-releases/1784894032002-2676534/src/events.js
13835
14096
  var EVENT_COMMANDS = new Set(["add", "list", "enable", "disable", "rm"]);
13836
14097
  var EVENT_FLAGS = new Set(["at", "days", "message", "run-date", "session", "tag", "tz"]);
13837
14098
  var PRODUCT_DAYS_MODES = new Set(["once", "daily", "weekday"]);
@@ -13921,9 +14182,9 @@ function _assertListArgs(args) {
13921
14182
  }
13922
14183
  }
13923
14184
 
13924
- // .dist-releases/1784862378006-1249051/src/text-input.js
13925
- import { readFile as readFile8 } from "node:fs/promises";
13926
- var _readUtf8File = async (file) => await readFile8(file, "utf8");
14185
+ // .dist-releases/1784894032002-2676534/src/text-input.js
14186
+ import { readFile as readFile9 } from "node:fs/promises";
14187
+ var _readUtf8File = async (file) => await readFile9(file, "utf8");
13927
14188
  async function readTextInput(input) {
13928
14189
  let content = "";
13929
14190
  for await (const chunk of input)
@@ -13955,17 +14216,17 @@ async function rejectUnusedTextInput(input, message) {
13955
14216
  input.unref?.();
13956
14217
  }
13957
14218
  }
13958
- async function readTextFile(path22, readUtf8File2 = _readUtf8File) {
13959
- return await readUtf8File2(path22);
14219
+ async function readTextFile(path23, readUtf8File2 = _readUtf8File) {
14220
+ return await readUtf8File2(path23);
13960
14221
  }
13961
- async function readTextFileOrStdin(path22, input, unusedInputMessage, readUtf8File2 = _readUtf8File) {
13962
- if (path22 === "-")
14222
+ async function readTextFileOrStdin(path23, input, unusedInputMessage, readUtf8File2 = _readUtf8File) {
14223
+ if (path23 === "-")
13963
14224
  return await readTextInput(input);
13964
14225
  await rejectUnusedTextInput(input, unusedInputMessage);
13965
- return await readTextFile(path22, readUtf8File2);
14226
+ return await readTextFile(path23, readUtf8File2);
13966
14227
  }
13967
14228
 
13968
- // .dist-releases/1784862378006-1249051/src/ask.js
14229
+ // .dist-releases/1784894032002-2676534/src/ask.js
13969
14230
  var ASK_FLAGS = new Set(["body-file", "option", "reason", "recommend", "session", "title"]);
13970
14231
  async function runAskFromCli(client2, argv, config, input) {
13971
14232
  const args = _parseAskArgs(argv);
@@ -14080,8 +14341,8 @@ function _singleAskValue(values, name) {
14080
14341
  return entries[0];
14081
14342
  }
14082
14343
 
14083
- // .dist-releases/1784862378006-1249051/src/task.js
14084
- import { readFile as readFile9 } from "node:fs/promises";
14344
+ // .dist-releases/1784894032002-2676534/src/task.js
14345
+ import { readFile as readFile10 } from "node:fs/promises";
14085
14346
  import { basename } from "node:path";
14086
14347
  var TASK_FLAGS = new Set(["after", "all", "artifact-id", "before", "body-file", "commit", "depends", "description", "description-file", "draft", "findings", "image", "level", "limit", "owner-of-resume", "pair", "priority", "query", "reason", "resume-condition", "resume-dependency", "session", "status", "type", "version"]);
14087
14348
  var TASK_BOOLEAN_FLAGS = new Set(["all", "draft"]);
@@ -14312,7 +14573,7 @@ async function _createTaskRequest(args, input) {
14312
14573
  async function _uploadTaskImages(client2, captainId, session, imagePaths) {
14313
14574
  const artifactIds = [];
14314
14575
  for (const imagePath of imagePaths) {
14315
- const content = await readFile9(imagePath);
14576
+ const content = await readFile10(imagePath);
14316
14577
  const artifact = await client2.uploadSessionArtifact(captainId, session, {
14317
14578
  name: basename(imagePath),
14318
14579
  content_bytes_base64: bytesToBase64(content)
@@ -14570,7 +14831,7 @@ function _taskListFooter(shown, returned, total, hasMore, cursor) {
14570
14831
  return `Shown ${shown} of ${total}${local}${next}`;
14571
14832
  }
14572
14833
 
14573
- // .dist-releases/1784862378006-1249051/src/team.js
14834
+ // .dist-releases/1784894032002-2676534/src/team.js
14574
14835
  var TEAM_BOOTSTRAP_ENGINES = new Set(["claude", "codex"]);
14575
14836
  var SLOT_SPEC_RE = /^([^=\s]+)=([^:@\s]+):([^:@\s]+)$/;
14576
14837
  var TEAM_PLAIN_LIMIT = 20;
@@ -14725,7 +14986,7 @@ function _slotTable(slots) {
14725
14986
  Shown ${shown.length} of ${slots.length}${omitted ? `; ${omitted} omitted (use --format json)` : ""}`;
14726
14987
  }
14727
14988
 
14728
- // .dist-releases/1784862378006-1249051/src/memory.js
14989
+ // .dist-releases/1784894032002-2676534/src/memory.js
14729
14990
  var VALUE_FLAGS = new Set(["file", "source"]);
14730
14991
  var MEMORY_SEARCH_PLAIN_LIMIT = 20;
14731
14992
  async function runMemoryFromCli(client2, argv, config, input, format) {
@@ -14860,7 +15121,7 @@ function _summary(value) {
14860
15121
  return value.replace(/\s+/gu, " ").trim().slice(0, 180);
14861
15122
  }
14862
15123
 
14863
- // .dist-releases/1784862378006-1249051/src/mailbox-send.js
15124
+ // .dist-releases/1784894032002-2676534/src/mailbox-send.js
14864
15125
  var SEND_FLAGS = new Set(["body-file", "commit", "session", "tag", "task-id", "task-version", "to-agents", "to-roles", "to-host"]);
14865
15126
  function parseMailboxSendArgs(argv) {
14866
15127
  const parsed = argsFromValues(parseFlagValues(argv));
@@ -14959,7 +15220,7 @@ function splitList(value) {
14959
15220
  return list?.length ? list : undefined;
14960
15221
  }
14961
15222
 
14962
- // .dist-releases/1784862378006-1249051/src/reply.js
15223
+ // .dist-releases/1784894032002-2676534/src/reply.js
14963
15224
  var REPLY_FLAGS = new Set(["agent", "body-file", "in-reply-to", "session"]);
14964
15225
  async function sendReplyFromCli(client2, argv, config, io) {
14965
15226
  const args = _parseReplyArgs(argv);
@@ -15016,12 +15277,13 @@ function _requiredReplyValue(values, name) {
15016
15277
  return value;
15017
15278
  }
15018
15279
 
15019
- // .dist-releases/1784862378006-1249051/src/worker-command.js
15280
+ // .dist-releases/1784894032002-2676534/src/worker-command.js
15020
15281
  async function runWorkerCommandFromCli(client2, argv, config, io, format = "plain") {
15282
+ const command = argv[0];
15283
+ _requireWorkerCredential(config.keyScope, command);
15021
15284
  const captainId = config.captainId;
15022
15285
  if (!captainId)
15023
15286
  throw new Error("SAILORBRIDGE_CAPTAIN_ID is required for worker commands");
15024
- const command = argv[0];
15025
15287
  switch (command) {
15026
15288
  case "list":
15027
15289
  return _listWorkers(client2, captainId, _requireSession(config.session), argv.slice(1), io, format);
@@ -15033,6 +15295,14 @@ async function runWorkerCommandFromCli(client2, argv, config, io, format = "plai
15033
15295
  throw new Error("worker command must be job, list, run, or serve");
15034
15296
  }
15035
15297
  }
15298
+ function _requireWorkerCredential(keyScope, command) {
15299
+ if (keyScope !== "host")
15300
+ return;
15301
+ if (command === "list") {
15302
+ throw new Error("worker list cannot use a paired host credential. " + "Open this team's Workers page in the SailorBridge console.");
15303
+ }
15304
+ throw new Error("worker run/job requires a member-runtime credential; this command loaded a paired host credential. " + "Retry from a member workspace where SAILORBRIDGE_TOKEN is already set.");
15305
+ }
15036
15306
  function _requireSession(session) {
15037
15307
  if (!session)
15038
15308
  throw new Error("SAILORBRIDGE_SESSION is required for worker list and run");
@@ -15197,7 +15467,7 @@ function _writeSignalStatus(remoteCode, localStatus, remoteStderr, io) {
15197
15467
  `);
15198
15468
  }
15199
15469
 
15200
- // .dist-releases/1784862378006-1249051/src/create.js
15470
+ // .dist-releases/1784894032002-2676534/src/create.js
15201
15471
  var CREATE_ENGINES = new Set(["claude", "codex"]);
15202
15472
  var DEFAULT_ENGINE = "claude";
15203
15473
  var WORKSPACE_PLAIN_LIMIT = 20;
@@ -15251,9 +15521,9 @@ async function _startTeamSlots(deps, ctx, team, hostId, workspaceRoot, engine, s
15251
15521
  }
15252
15522
  return requests;
15253
15523
  }
15254
- async function _postConsoleJson(deps, ctx, path22, body) {
15524
+ async function _postConsoleJson(deps, ctx, path23, body) {
15255
15525
  const fetchImpl = deps.fetchImpl ?? fetch;
15256
- const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path22}`, {
15526
+ const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path23}`, {
15257
15527
  method: "POST",
15258
15528
  headers: { Authorization: `Bearer ${ctx.token}`, "Content-Type": "application/json" },
15259
15529
  body: JSON.stringify(body)
@@ -15266,9 +15536,9 @@ async function _postConsoleJson(deps, ctx, path22, body) {
15266
15536
  throw new Error("SailorBridge API returned a malformed object");
15267
15537
  return parsed;
15268
15538
  }
15269
- async function _getConsoleJson(deps, ctx, path22) {
15539
+ async function _getConsoleJson(deps, ctx, path23) {
15270
15540
  const fetchImpl = deps.fetchImpl ?? fetch;
15271
- const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path22}`, {
15541
+ const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path23}`, {
15272
15542
  headers: { Authorization: `Bearer ${ctx.token}` }
15273
15543
  });
15274
15544
  const text = await response.text();
@@ -15343,15 +15613,15 @@ function _rejectMemberFlags(role, flags) {
15343
15613
  throw new Error(`create ${role} does not accept ${found[0]}`);
15344
15614
  }
15345
15615
  async function _memberSlotForCreate(deps, ctx, session, role) {
15346
- const path22 = `/api/console/teams/${encodeURIComponent(session)}/agent-slots`;
15347
- const slots = _responseMemberSlots(await _getConsoleJson(deps, ctx, path22));
15616
+ const path23 = `/api/console/teams/${encodeURIComponent(session)}/agent-slots`;
15617
+ const slots = _responseMemberSlots(await _getConsoleJson(deps, ctx, path23));
15348
15618
  const retryable = slots.find((slot) => _isRetryableMemberSlot(slot, role));
15349
15619
  if (retryable)
15350
15620
  return retryable;
15351
15621
  if (role === "planner" && slots.some((slot) => slot.role === role)) {
15352
15622
  throw new Error("duplicate-singleton-role: team already has a planner slot");
15353
15623
  }
15354
- const created = _responseMemberSlot(await _postConsoleJson(deps, ctx, path22, { role }));
15624
+ const created = _responseMemberSlot(await _postConsoleJson(deps, ctx, path23, { role }));
15355
15625
  if (created.role !== role)
15356
15626
  throw new Error("SailorBridge API returned a member slot with the wrong role");
15357
15627
  return created;
@@ -15587,7 +15857,7 @@ function _workerPlain(hostId, workdir, result) {
15587
15857
  `);
15588
15858
  }
15589
15859
 
15590
- // .dist-releases/1784862378006-1249051/src/cli.js
15860
+ // .dist-releases/1784894032002-2676534/src/cli.js
15591
15861
  var FORMATTED_COMMANDS = new Set([
15592
15862
  "ask",
15593
15863
  "create",
@@ -15647,6 +15917,10 @@ async function main(argv) {
15647
15917
  });
15648
15918
  return;
15649
15919
  }
15920
+ if (command === "upgrade") {
15921
+ await runUpgradeFromCli(argv.slice(1), { stdout: process.stdout });
15922
+ return;
15923
+ }
15650
15924
  if (command === "host" && argv[1] === "start-request") {
15651
15925
  await runHostStartRequestCommandFromCli(argv.slice(2), { stdout: process.stdout, stderr: process.stderr, format: output2.format });
15652
15926
  return;
@@ -15753,6 +16027,7 @@ var KNOWN_COMMANDS = new Set([
15753
16027
  "task",
15754
16028
  "team",
15755
16029
  "tutorial",
16030
+ "upgrade",
15756
16031
  "workflow",
15757
16032
  "worker"
15758
16033
  ]);
@@ -15780,6 +16055,7 @@ var SUBCOMMANDS = {
15780
16055
  "unpark"
15781
16056
  ]),
15782
16057
  team: new Set(["bootstrap"]),
16058
+ upgrade: new Set,
15783
16059
  worker: new Set(["job", "list", "run", "serve"])
15784
16060
  };
15785
16061
  function _assertKnownCommand(command, subcommand) {
@@ -15981,8 +16257,9 @@ function _resolveOptionalApiUrl(flag, env) {
15981
16257
  }
15982
16258
  return env?.trim() || undefined;
15983
16259
  }
16260
+ var AUTO_UPDATE_SKIP_FLAG = "--disable-auto-update";
15984
16261
  async function hostRun(argv) {
15985
- const input = parseFlags3(argv);
16262
+ const input = parseFlags3(argv.filter((arg) => arg !== AUTO_UPDATE_SKIP_FLAG));
15986
16263
  const credentialsPath = input["credentials-path"] ?? process.env.SAILORBRIDGE_CREDENTIALS_FILE ?? defaultHostCredentialsPath();
15987
16264
  const credential = await loadHostCredential(credentialsPath);
15988
16265
  const relayUrl = credential.relay_url ?? process.env.SAILORBRIDGE_RELAY_URL;
@@ -16000,6 +16277,7 @@ async function hostRun(argv) {
16000
16277
  process.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, host_id: credential.host_id, ...fields })}
16001
16278
  `);
16002
16279
  };
16280
+ const autoUpdate = !argv.includes(AUTO_UPDATE_SKIP_FLAG) && await _hostConfigAllowsAutoUpdate(emit);
16003
16281
  await runHostDaemon({
16004
16282
  relay,
16005
16283
  resolveCredential,
@@ -16010,9 +16288,19 @@ async function hostRun(argv) {
16010
16288
  signal: controller.signal,
16011
16289
  redactSecrets: [credential.token],
16012
16290
  ...input["workspace-root"] ? { workspaceRoot: input["workspace-root"] } : {},
16291
+ autoUpdate,
16013
16292
  logger: emit
16014
16293
  });
16015
16294
  }
16295
+ async function _hostConfigAllowsAutoUpdate(emit) {
16296
+ try {
16297
+ const config = await loadHostConfig(process.env.SAILORBRIDGE_HOST_CONFIG_FILE ?? defaultHostConfigPath());
16298
+ return config.auto_update !== false;
16299
+ } catch (error) {
16300
+ emit("host_config_load_error", { error: error instanceof Error ? error.message : String(error) });
16301
+ return true;
16302
+ }
16303
+ }
16016
16304
  async function supervise(client2, argv, config) {
16017
16305
  const parsed = parseSuperviseArgs(argv);
16018
16306
  assertSingleOwnerConfirmed(parsed);
@@ -16234,6 +16522,8 @@ function _usageText() {
16234
16522
  sailorbridge workflow
16235
16523
  sailorbridge refresh
16236
16524
  sailorbridge --version
16525
+ sailorbridge upgrade
16526
+ installs @sailorbridge/client@latest and restarts a running SailorBridge-managed host service.
16237
16527
  sailorbridge tutorial [--check] [--api-url URL] [--credentials-path PATH] (default API: ${DEFAULT_PRODUCT_API_URL})
16238
16528
  sailorbridge login [--api-url URL] [--host-id H] [--display-name N] [--credentials-path PATH] [--binary PATH] [${SERVICE_SKIP_FLAG}] [--service-dir DIR] [--service-name NAME] (default API: ${DEFAULT_PRODUCT_API_URL})
16239
16529
  pairs this host, then installs and starts the host service so the console can start workers and agents here.
@@ -16247,7 +16537,7 @@ function _usageText() {
16247
16537
  sailorbridge event add --at HH:MM --tz IANA --days once|daily|weekday --message TEXT [--run-date YYYY-MM-DD] [--tag T] [--disabled] [--session S]
16248
16538
  sailorbridge event list [--session S]
16249
16539
  sailorbridge event enable|disable|rm EVENT_ID [--session S]
16250
- sailorbridge ask --title TITLE --option LABEL::DESCRIPTION --option LABEL::DESCRIPTION --recommend A --reason TEXT [--reason TEXT] [--body-file PATH|-] [--session S]
16540
+ sailorbridge ask --title TITLE --option LABEL::DESCRIPTION --option LABEL::DESCRIPTION --recommend A --reason TEXT [--reason TEXT] --body-file - [--session S]
16251
16541
  sailorbridge ask list [--session S]
16252
16542
  sailorbridge ask get|status DECISION_ID [--session S]
16253
16543
  sailorbridge task create [--description TEXT|-|--body-file PATH|-|--description-file PATH|-] [--pair PAIR] [--draft|--status draft|new] [--type feature|bug|docs|chore] [--priority high|normal|low] [--level d1|d2|d3|d4] [--depends TASK[,TASK]] [--artifact-id ID] [--image PATH] [--session S]
@@ -16283,7 +16573,7 @@ function _usageText() {
16283
16573
  sailorbridge host consume --once --dry-run-start [--host-id H] [--display-name N]
16284
16574
  sailorbridge host service install|uninstall|status [--binary PATH] [--credentials-path PATH] [--workspace-root DIR] [--service-dir DIR] [--service-name NAME]
16285
16575
  sailorbridge host start-request [--credentials-path PATH] [--api-url URL] [--access-token TOKEN] --team T [--host-id H] --agent A --engine codex --roles operator --workspace-mode fresh|git [--git-url URL]
16286
- sailorbridge host run [--credentials-path PATH] [--api-url URL] [--host-id H] [--display-name N] [--workspace-root DIR]
16576
+ sailorbridge host run [--credentials-path PATH] [--api-url URL] [--host-id H] [--display-name N] [--workspace-root DIR] [--disable-auto-update]
16287
16577
  sailorbridge worker list [--format plain|json]
16288
16578
  sailorbridge worker run [--detach] --worker NAME [--timeout SEC] [--format plain|json] -- ARGV...
16289
16579
  sailorbridge worker job ID [--format plain|json]
@@ -16337,5 +16627,5 @@ function _looksLikeNetworkFailure(message) {
16337
16627
  return /fetch failed|network|ECONN(?:REFUSED|RESET)|ENOTFOUND|ETIMEDOUT/iu.test(message);
16338
16628
  }
16339
16629
 
16340
- //# debugId=3B810968682A4A6F64756E2164756E21
16630
+ //# debugId=F9747B92FC45FFDB64756E2164756E21
16341
16631
  //# sourceMappingURL=cli.js.map