@sailorbridge/client 0.1.1 → 0.2.1

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/1784772051951-3107696/src/cli.js
5
+ // .dist-releases/1784860966076-1188772/src/cli.js
6
6
  import { hostname as hostname3 } from "node:os";
7
7
  import { writeSync } from "node:fs";
8
- // .dist-releases/1784772051951-3107696/src/auth/host-pairing.js
8
+ // .dist-releases/1784860966076-1188772/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,37 +220,82 @@ function _assertObject(value) {
220
220
  throw new Error("host pairing response must be an object");
221
221
  return value;
222
222
  }
223
- // .dist-releases/1784772051951-3107696/src/host/repo-tip-reporter.js
223
+ // .dist-releases/1784860966076-1188772/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";
227
227
  import path2 from "node:path";
228
228
  import { spawn } from "node:child_process";
229
229
  var COMMAND_TIMEOUT_MS = 120000;
230
+ var REPO_BACKOFF_INITIAL_MS = 60000;
231
+ var REPO_BACKOFF_MAX_MS = 60 * 60000;
230
232
  var SHA_RE = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
231
233
  function defaultRepoMirrorRoot() {
232
234
  return path2.join(homedir2(), ".sailorbridge", "repo-mirrors");
233
235
  }
234
- async function reportRepoTipsOnce(options, deps = {}) {
235
- const logger = options.logger ?? (() => {});
236
+ function createRepoTipReporter(deps = {}) {
237
+ const backoffs = new Map;
238
+ return async (options) => _reportRepoTipsOnce(options, deps, backoffs);
239
+ }
240
+ async function _reportRepoTipsOnce(options, deps, backoffs) {
236
241
  const wanted = await options.relay.getTaskRepoTipWanted(options.captainId);
237
242
  const mirrorRoot = options.mirrorRoot ?? defaultRepoMirrorRoot();
238
243
  let reported = 0;
239
244
  for (const entry of wanted.wanted) {
240
- try {
241
- const report = await observeRepoTips(entry, mirrorRoot, deps);
242
- await options.relay.putTaskRepoTips(options.captainId, entry.session, report);
245
+ if (await _reportRepoTip(entry, mirrorRoot, options, deps, backoffs))
243
246
  reported += 1;
244
- } catch (error) {
245
- logger("host_repo_tip_report_failed", {
246
- session: entry.session,
247
- repo: entry.repo_path,
248
- error: error instanceof Error ? error.message : String(error)
249
- });
250
- }
251
247
  }
248
+ _forgetUnwantedBackoffs(backoffs, wanted.wanted);
252
249
  return { observed: wanted.wanted.length, reported };
253
250
  }
251
+ async function _reportRepoTip(entry, mirrorRoot, options, deps, backoffs) {
252
+ const backoff = backoffs.get(entry.repo_path);
253
+ if (backoff && _now(deps) < backoff.retryAt)
254
+ return false;
255
+ try {
256
+ const report = await observeRepoTips(entry, mirrorRoot, deps);
257
+ await options.relay.putTaskRepoTips(options.captainId, entry.session, report);
258
+ } catch (error) {
259
+ _backOffRepo(entry, error, options.logger, deps, backoffs);
260
+ return false;
261
+ }
262
+ if (backoff)
263
+ _recoverRepo(entry, backoff, options.logger, backoffs);
264
+ return true;
265
+ }
266
+ function _backOffRepo(entry, error, logger, deps, backoffs) {
267
+ const previous = backoffs.get(entry.repo_path);
268
+ const retryMs = previous ? Math.min(previous.retryMs * 2, REPO_BACKOFF_MAX_MS) : REPO_BACKOFF_INITIAL_MS;
269
+ const failures = (previous?.failures ?? 0) + 1;
270
+ backoffs.set(entry.repo_path, { failures, retryAt: _now(deps) + retryMs, retryMs });
271
+ if (previous)
272
+ return;
273
+ logger?.("host_repo_tip_backoff_started", {
274
+ session: entry.session,
275
+ repo: entry.repo_path,
276
+ error: error instanceof Error ? error.message : String(error),
277
+ failures,
278
+ retry_ms: retryMs
279
+ });
280
+ }
281
+ function _recoverRepo(entry, backoff, logger, backoffs) {
282
+ backoffs.delete(entry.repo_path);
283
+ logger?.("host_repo_tip_backoff_recovered", {
284
+ session: entry.session,
285
+ repo: entry.repo_path,
286
+ failures: backoff.failures
287
+ });
288
+ }
289
+ function _forgetUnwantedBackoffs(backoffs, wanted) {
290
+ const activeRepos = new Set(wanted.map((entry) => entry.repo_path));
291
+ for (const repo of backoffs.keys()) {
292
+ if (!activeRepos.has(repo))
293
+ backoffs.delete(repo);
294
+ }
295
+ }
296
+ function _now(deps) {
297
+ return deps.now?.() ?? Date.now();
298
+ }
254
299
  async function observeRepoTips(entry, mirrorRoot, deps) {
255
300
  const exec = deps.execCommand ?? execArgv;
256
301
  const mirror = mirrorPathFor(mirrorRoot, entry.repo_path);
@@ -357,10 +402,10 @@ ${error.message}`));
357
402
  proc.on("close", (code) => finish(code ?? 1));
358
403
  });
359
404
  }
360
- // .dist-releases/1784772051951-3107696/src/host/request-consumer.js
405
+ // .dist-releases/1784860966076-1188772/src/host/request-consumer.js
361
406
  import { hostname as hostname2 } from "node:os";
362
407
 
363
- // .dist-releases/1784772051951-3107696/src/driver/secrets.js
408
+ // .dist-releases/1784860966076-1188772/src/driver/secrets.js
364
409
  function redactSensitiveText(text, env = {}, explicitSecrets = []) {
365
410
  let redacted = text;
366
411
  for (const [key, value] of Object.entries(env)) {
@@ -379,16 +424,16 @@ function isSensitiveEnvKey(key) {
379
424
  return /(TOKEN|KEY|SECRET|AUTHORIZATION|PASSWORD)/i.test(key);
380
425
  }
381
426
 
382
- // .dist-releases/1784772051951-3107696/src/version.js
383
- var CLIENT_VERSION = "0.1.1";
384
- var CLIENT_GIT_SHA = "ba1bf5ddf9ff";
385
- var CLIENT_BUILD_DIRTY = false;
427
+ // .dist-releases/1784860966076-1188772/src/version.js
428
+ var CLIENT_VERSION = "0.2.1";
429
+ var CLIENT_GIT_SHA = "89604f060de5-dirty";
430
+ var CLIENT_BUILD_DIRTY = true;
386
431
  var CLIENT_VERSION_LABEL = formatClientVersion(CLIENT_VERSION, CLIENT_GIT_SHA);
387
432
  function formatClientVersion(version, gitSha) {
388
433
  return `${version}+git.${gitSha}`;
389
434
  }
390
435
 
391
- // .dist-releases/1784772051951-3107696/src/host/request-consumer.js
436
+ // .dist-releases/1784860966076-1188772/src/host/request-consumer.js
392
437
  var MAX_ERROR_LENGTH = 500;
393
438
  function defaultHostIdentity(config) {
394
439
  const captainId = config.captainId?.trim();
@@ -443,7 +488,7 @@ function limitErrorMessage(error, explicitSecrets) {
443
488
  const trimmed = redactSensitiveText(message, process.env, explicitSecrets).trim() || "host request starter failed";
444
489
  return trimmed.length <= MAX_ERROR_LENGTH ? trimmed : `${trimmed.slice(0, MAX_ERROR_LENGTH - 3)}...`;
445
490
  }
446
- // .dist-releases/1784772051951-3107696/src/product/start-request.js
491
+ // .dist-releases/1784860966076-1188772/src/product/start-request.js
447
492
  async function createAgentStartRequest(options) {
448
493
  const apiUrl = _required(options.apiUrl, "product API URL").replace(/\/+$/u, "");
449
494
  const accessToken = _required(options.accessToken, "product API bearer token");
@@ -503,7 +548,7 @@ function _string(value, label) {
503
548
  throw new Error(`${label} must be a non-empty string`);
504
549
  return value;
505
550
  }
506
- // .dist-releases/1784772051951-3107696/src/product/pair.js
551
+ // .dist-releases/1784860966076-1188772/src/product/pair.js
507
552
  async function createPair(options) {
508
553
  const apiUrl = _required2(options.apiUrl, "product API URL").replace(/\/+$/u, "");
509
554
  const accessToken = _required2(options.accessToken, "product API bearer token");
@@ -580,7 +625,7 @@ function _string2(value, label) {
580
625
  throw new Error(`${label} must be a non-empty string`);
581
626
  return value;
582
627
  }
583
- // .dist-releases/1784772051951-3107696/src/product/worker.js
628
+ // .dist-releases/1784860966076-1188772/src/product/worker.js
584
629
  async function createWorker(options) {
585
630
  const apiUrl = _required3(options.apiUrl, "product API URL").replace(/\/+$/u, "");
586
631
  const accessToken = _required3(options.accessToken, "product API bearer token");
@@ -644,7 +689,7 @@ function _string3(value, label) {
644
689
  throw new Error(`${label} must be a non-empty string`);
645
690
  return value;
646
691
  }
647
- // .dist-releases/1784772051951-3107696/src/driver/events.js
692
+ // .dist-releases/1784860966076-1188772/src/driver/events.js
648
693
  class DriverEventEmitter {
649
694
  callbacks = new Map;
650
695
  on(type, callback) {
@@ -659,7 +704,7 @@ class DriverEventEmitter {
659
704
  }
660
705
  }
661
706
  }
662
- // .dist-releases/1784772051951-3107696/src/driver/async-queue.js
707
+ // .dist-releases/1784860966076-1188772/src/driver/async-queue.js
663
708
  class AsyncQueue {
664
709
  values = [];
665
710
  waiters = [];
@@ -698,7 +743,7 @@ class AsyncQueue {
698
743
  }
699
744
  }
700
745
 
701
- // .dist-releases/1784772051951-3107696/src/driver/types.js
746
+ // .dist-releases/1784860966076-1188772/src/driver/types.js
702
747
  class DriverUnsupportedError extends Error {
703
748
  constructor(message) {
704
749
  super(message);
@@ -713,7 +758,7 @@ class DriverTargetNotReadyError extends Error {
713
758
  }
714
759
  }
715
760
 
716
- // .dist-releases/1784772051951-3107696/src/driver/sdk-loader.js
761
+ // .dist-releases/1784860966076-1188772/src/driver/sdk-loader.js
717
762
  async function loadSdk(loader, packageName, env = {}, installHint = `Install the optional SDK dependency ${packageName} and its peers for this transport.`) {
718
763
  try {
719
764
  return await loader();
@@ -723,7 +768,7 @@ async function loadSdk(loader, packageName, env = {}, installHint = `Install the
723
768
  }
724
769
  }
725
770
 
726
- // .dist-releases/1784772051951-3107696/src/driver/claude/driver.js
771
+ // .dist-releases/1784860966076-1188772/src/driver/claude/driver.js
727
772
  class ClaudeAgentSdkDriver {
728
773
  options;
729
774
  capabilities = { structured: true, resume: true, attach: false };
@@ -899,7 +944,7 @@ function asRecord(value) {
899
944
  function stringValue(value) {
900
945
  return typeof value === "string" ? value : undefined;
901
946
  }
902
- // .dist-releases/1784772051951-3107696/src/driver/codex/driver.js
947
+ // .dist-releases/1784860966076-1188772/src/driver/codex/driver.js
903
948
  class CodexSdkDriver {
904
949
  options;
905
950
  capabilities = { structured: true, resume: true, attach: false };
@@ -1080,7 +1125,7 @@ function asRecord2(value) {
1080
1125
  function stringValue2(value) {
1081
1126
  return typeof value === "string" ? value : undefined;
1082
1127
  }
1083
- // .dist-releases/1784772051951-3107696/src/driver/liveness-heuristics.js
1128
+ // .dist-releases/1784860966076-1188772/src/driver/liveness-heuristics.js
1084
1129
  var BUSY_MARKERS = ["Working (", "Waiting for background", "Pasted Content", "tab to queue"];
1085
1130
  var ANSI_PATTERN = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[PX^_][\s\S]*?\x1B\\)/g;
1086
1131
  function stripAnsi(text) {
@@ -1107,7 +1152,7 @@ function classifyInteractiveOutput(input) {
1107
1152
  function isOutputStable(now, lastOutputAt, idleStableMs) {
1108
1153
  return Boolean(lastOutputAt && now.getTime() - lastOutputAt.getTime() >= idleStableMs);
1109
1154
  }
1110
- // .dist-releases/1784772051951-3107696/src/driver/pty/loader.js
1155
+ // .dist-releases/1784860966076-1188772/src/driver/pty/loader.js
1111
1156
  async function loadNodePty(loader = defaultNodePtyLoader) {
1112
1157
  try {
1113
1158
  return await loader();
@@ -1133,7 +1178,7 @@ async function defaultNodePtyLoader() {
1133
1178
  return loaded;
1134
1179
  }
1135
1180
 
1136
- // .dist-releases/1784772051951-3107696/src/driver/pty/driver.js
1181
+ // .dist-releases/1784860966076-1188772/src/driver/pty/driver.js
1137
1182
  class PtyDriver {
1138
1183
  options;
1139
1184
  capabilities = { structured: false, resume: false, attach: false };
@@ -1285,12 +1330,12 @@ function tail(text, lines) {
1285
1330
  function sleep(ms) {
1286
1331
  return new Promise((resolve) => setTimeout(resolve, ms));
1287
1332
  }
1288
- // .dist-releases/1784772051951-3107696/src/driver/tmux/driver.js
1333
+ // .dist-releases/1784860966076-1188772/src/driver/tmux/driver.js
1289
1334
  import { mkdtemp, rm, writeFile as writeFile2 } from "node:fs/promises";
1290
1335
  import { tmpdir } from "node:os";
1291
1336
  import { join } from "node:path";
1292
1337
 
1293
- // .dist-releases/1784772051951-3107696/src/driver/tmux/launch.js
1338
+ // .dist-releases/1784860966076-1188772/src/driver/tmux/launch.js
1294
1339
  var ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
1295
1340
  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 "$@"';
1296
1341
  function buildTmuxNewSessionArgs(input) {
@@ -1313,7 +1358,7 @@ function validateEnv(env) {
1313
1358
  }
1314
1359
  }
1315
1360
 
1316
- // .dist-releases/1784772051951-3107696/src/driver/tmux/paste.js
1361
+ // .dist-releases/1784860966076-1188772/src/driver/tmux/paste.js
1317
1362
  function buildDispatchPlan(text, useTyping) {
1318
1363
  return {
1319
1364
  mode: useTyping ? "type-literal" : "paste-buffer",
@@ -1337,7 +1382,7 @@ function arraysEqual(left, right) {
1337
1382
  return left.length === right.length && left.every((item, index) => item === right[index]);
1338
1383
  }
1339
1384
 
1340
- // .dist-releases/1784772051951-3107696/src/driver/tmux/liveness.js
1385
+ // .dist-releases/1784860966076-1188772/src/driver/tmux/liveness.js
1341
1386
  var SHELL_COMMANDS = new Set(["", "bash", "zsh", "sh", "fish", "pwsh", "powershell"]);
1342
1387
  function classifyTmuxLiveness(input) {
1343
1388
  if (!input.hasSession) {
@@ -1357,7 +1402,7 @@ function isTmuxProcessAlive(command, runningCommands) {
1357
1402
  return !SHELL_COMMANDS.has(command);
1358
1403
  }
1359
1404
 
1360
- // .dist-releases/1784772051951-3107696/src/driver/tmux/runner.js
1405
+ // .dist-releases/1784860966076-1188772/src/driver/tmux/runner.js
1361
1406
  import { spawn as spawn2 } from "node:child_process";
1362
1407
  var DEFAULT_TIMEOUT_MS = 15000;
1363
1408
 
@@ -1482,7 +1527,7 @@ class TmuxClient {
1482
1527
  }
1483
1528
  }
1484
1529
 
1485
- // .dist-releases/1784772051951-3107696/src/driver/tmux/driver.js
1530
+ // .dist-releases/1784860966076-1188772/src/driver/tmux/driver.js
1486
1531
  var ATTACHED_STOP_GRACE_MS = 1e4;
1487
1532
  var DEFAULT_STOP_TIMEOUT_MS = 15000;
1488
1533
  var DEFAULT_READY_TIMEOUT_MS = 90000;
@@ -1877,12 +1922,12 @@ function isMissingTmuxSessionError(error) {
1877
1922
  ${error.result.stderr}`.toLowerCase();
1878
1923
  return text.includes("can't find session") || text.includes("cannot find session") || text.includes("no such session") || text.includes("session not found");
1879
1924
  }
1880
- // .dist-releases/1784772051951-3107696/src/relay/client.js
1925
+ // .dist-releases/1784860966076-1188772/src/relay/client.js
1881
1926
  import { Buffer as Buffer2 } from "node:buffer";
1882
1927
  import http from "node:http";
1883
1928
  import https from "node:https";
1884
1929
 
1885
- // .dist-releases/1784772051951-3107696/src/relay/errors.js
1930
+ // .dist-releases/1784860966076-1188772/src/relay/errors.js
1886
1931
  class RelayError extends Error {
1887
1932
  method;
1888
1933
  path;
@@ -1919,7 +1964,37 @@ class RelayProtocolError extends RelayError {
1919
1964
  }
1920
1965
  }
1921
1966
 
1922
- // .dist-releases/1784772051951-3107696/src/relay/client.js
1967
+ // .dist-releases/1784860966076-1188772/src/worker/protocol.js
1968
+ var DEFAULT_RELAY_URL = DEFAULT_PRODUCT_API_URL;
1969
+ var DEFAULT_WORKER = "host";
1970
+ var DEFAULT_WORKDIR = "~/sailorbridge-workdir";
1971
+ var DEFAULT_JOB_TIMEOUT_SECONDS = 300;
1972
+ var MAX_JOB_TIMEOUT_SECONDS = 24 * 60 * 60;
1973
+ var MIN_JOB_TIMEOUT_SECONDS = 1;
1974
+ var WORKER_JOB_RESULT_GRACE_SECONDS = 30;
1975
+ var ACTIVE_JOB_KINDS = new Set(["apply_diff", "bash", "read_file", "write_file", "compose", "stream", "status"]);
1976
+ var READONLY_ALLOWED_KINDS = new Set(["read_file", "status"]);
1977
+ var WORKER_SOCK_CONNECT_MS = 1e4;
1978
+ var WORKER_PULL_READ_IDLE_MS = 45000;
1979
+ var WORKER_QUICK_READ_IDLE_MS = 15000;
1980
+ var WORKER_UPLOAD_READ_IDLE_MS = (MAX_JOB_TIMEOUT_SECONDS + 300) * 1000;
1981
+ var DRAIN_AFTER_KILL_MS = 5000;
1982
+ var WORKER_RETRY_INITIAL_MS = 1000;
1983
+ var WORKER_RETRY_MAX_MS = 30000;
1984
+ var WORKER_SESSION_REBUILD_FAILURES = 10;
1985
+ var MAX_WORKER_RESULT_OUTPUT_BYTES = 256 * 1024;
1986
+ function coerceJobTimeoutSeconds(spec) {
1987
+ const value = spec.timeout_sec === undefined ? DEFAULT_JOB_TIMEOUT_SECONDS : spec.timeout_sec;
1988
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
1989
+ throw new Error("spec.timeout_sec must be a finite positive number");
1990
+ }
1991
+ return Math.max(MIN_JOB_TIMEOUT_SECONDS, Math.min(value, MAX_JOB_TIMEOUT_SECONDS));
1992
+ }
1993
+ function effectiveJobKind(kind, spec) {
1994
+ return kind === "stream" || spec.stream === true ? "stream" : kind;
1995
+ }
1996
+
1997
+ // .dist-releases/1784860966076-1188772/src/relay/client.js
1923
1998
  var JSON_CONTENT_TYPE = "application/json";
1924
1999
  var MAX_MAILBOX_LIMIT = 16;
1925
2000
  var MAX_WAIT_TIMEOUT_SEC = 120;
@@ -1954,6 +2029,44 @@ class RelayClient {
1954
2029
  close() {
1955
2030
  this.httpClient?.close();
1956
2031
  }
2032
+ async listWorkers(captainId, session) {
2033
+ requireCaptainId(captainId);
2034
+ if (session !== undefined)
2035
+ requireSegment(session, "session");
2036
+ const suffix = session === undefined ? "" : `?session=${segment(session)}`;
2037
+ const path3 = `/api/v1/captains/${segment(captainId)}/workers${suffix}`;
2038
+ const response = await this.requestJson("GET", path3);
2039
+ if (!Array.isArray(response.workers)) {
2040
+ throw new RelayProtocolError("GET", path3, "workers must be an array");
2041
+ }
2042
+ return { workers: response.workers.map((worker) => _parseWorkerRecord(worker, path3)) };
2043
+ }
2044
+ async submitJob(captainId, body) {
2045
+ requireCaptainId(captainId);
2046
+ _validateWorkerJobSubmit(body);
2047
+ const path3 = `/api/v1/captains/${segment(captainId)}/task`;
2048
+ const spec = {
2049
+ argv: [...body.argv],
2050
+ ...body.timeoutSec === undefined ? {} : { timeout_sec: body.timeoutSec }
2051
+ };
2052
+ const response = await this.requestJson("POST", path3, { kind: "bash", worker: body.worker, spec });
2053
+ return _parseWorkerJobSubmitResponse(response, path3);
2054
+ }
2055
+ async getJobStatus(captainId, jobId) {
2056
+ return this._getJob(captainId, jobId);
2057
+ }
2058
+ async getJobResult(captainId, jobId, timeoutSec) {
2059
+ _validateWorkerJobWaitTimeout(timeoutSec);
2060
+ return this._getJob(captainId, jobId, timeoutSec);
2061
+ }
2062
+ async _getJob(captainId, jobId, timeoutSec) {
2063
+ requireCaptainId(captainId);
2064
+ requireSegment(jobId, "jobId");
2065
+ const base = `/api/v1/captains/${segment(captainId)}/task/${segment(jobId)}`;
2066
+ const path3 = timeoutSec === undefined ? base : `${base}?wait=true&timeout_sec=${timeoutSec}`;
2067
+ const timeoutMs = timeoutSec === undefined ? this.defaultTimeoutMs : (timeoutSec + 5) * 1000;
2068
+ return _parseWorkerJobResultResponse(await this.requestJson("GET", path3, undefined, timeoutMs), path3);
2069
+ }
1957
2070
  async claimMailbox(agentName, options = {}) {
1958
2071
  requireSegment(agentName, "agentName");
1959
2072
  if (options.messageId)
@@ -2206,29 +2319,6 @@ class RelayClient {
2206
2319
  const response = await this.requestJson("POST", path3, body);
2207
2320
  return _parseTeamAgentSlot(response.agent_slot, "POST", path3);
2208
2321
  }
2209
- async getTeamRepository(captainId, session) {
2210
- const path3 = `/api/v1/captains/${segment(captainId)}/sessions`;
2211
- const response = await this.requestJson("GET", path3);
2212
- if (!Array.isArray(response.sessions))
2213
- throw new RelayProtocolError("GET", path3, "sessions must be an array");
2214
- const rows = response.sessions.map((row) => _parseTeamRepository(row, "GET", path3));
2215
- const repository = rows.find((row) => row.team_id === session);
2216
- if (!repository)
2217
- throw new RelayProtocolError("GET", path3, `team ${session} is missing`);
2218
- return repository;
2219
- }
2220
- async setTeamRepositoryState(captainId, session, body) {
2221
- const path3 = `${_captainSessionPath(captainId, session)}/repo-verification-state`;
2222
- const response = await this.requestJson("PATCH", path3, body);
2223
- return _parseTeamRepository(response.session, "PATCH", path3);
2224
- }
2225
- async createRepoVerificationRequest(captainId, session, hostId, gitRepoUrl) {
2226
- const path3 = `${_captainSessionPath(captainId, session)}/repo-verification-requests`;
2227
- const response = await this.requestJson("POST", path3, { host_id: hostId, git_repo_url: gitRepoUrl });
2228
- const request = parseAgentStartRequest(response.request, "POST", path3);
2229
- _requireRepoVerificationRequest(request, "POST", path3, session, hostId);
2230
- return request;
2231
- }
2232
2322
  async getAgentStartRequest(captainId, session, requestId, timeoutMs) {
2233
2323
  const path3 = `${_captainSessionPath(captainId, session)}/agent-start-requests/${segment(requestId)}`;
2234
2324
  const response = await this.requestJson("GET", path3, undefined, timeoutMs);
@@ -2403,6 +2493,12 @@ class RelayClient {
2403
2493
  const path3 = `/api/v1/rag/captains/${segment(captainId)}/teams/${segment(teamId)}/index`;
2404
2494
  return parseRagMemoryIndex(await this.requestJson("GET", path3), "GET", path3);
2405
2495
  }
2496
+ async getRagEntitlement(captainId, teamId) {
2497
+ requireCaptainId(captainId);
2498
+ requireSegment(teamId, "teamId");
2499
+ const path3 = `/api/v1/rag/captains/${segment(captainId)}/teams/${segment(teamId)}/entitlement`;
2500
+ return parseRagEntitlement(await this.requestJson("GET", path3), "GET", path3);
2501
+ }
2406
2502
  async setRagCardPin(captainId, teamId, key, pinned) {
2407
2503
  requireCaptainId(captainId);
2408
2504
  requireSegment(teamId, "teamId");
@@ -2889,14 +2985,139 @@ function parseAgentMonitor(value) {
2889
2985
  if (typeof value.context_checkpoint_percent !== "number" || !Number.isFinite(value.context_checkpoint_percent)) {
2890
2986
  throw new RelayProtocolError("GET", "/api/v1/agents/{name}/monitor", "context_checkpoint_percent must be a finite number");
2891
2987
  }
2988
+ if (value.idle_presave_min !== null && (typeof value.idle_presave_min !== "number" || !Number.isFinite(value.idle_presave_min))) {
2989
+ throw new RelayProtocolError("GET", "/api/v1/agents/{name}/monitor", "idle_presave_min must be a finite number or null");
2990
+ }
2991
+ const latestInbound = value.latest_message_inbound_at;
2992
+ const latestOutbound = value.latest_message_outbound_at;
2993
+ if (latestInbound !== null && typeof latestInbound !== "string") {
2994
+ throw new RelayProtocolError("GET", "/api/v1/agents/{name}/monitor", "latest_message_inbound_at must be string or null");
2995
+ }
2996
+ if (latestOutbound !== null && typeof latestOutbound !== "string") {
2997
+ throw new RelayProtocolError("GET", "/api/v1/agents/{name}/monitor", "latest_message_outbound_at must be string or null");
2998
+ }
2892
2999
  return {
2893
3000
  name: value.name,
2894
3001
  engine: value.engine,
2895
3002
  should_resume: value.should_resume,
2896
3003
  session_handle: value.session_handle,
2897
- context_checkpoint_percent: value.context_checkpoint_percent
3004
+ context_checkpoint_percent: value.context_checkpoint_percent,
3005
+ idle_presave_min: value.idle_presave_min,
3006
+ latest_message_inbound_at: latestInbound,
3007
+ latest_message_outbound_at: latestOutbound
3008
+ };
3009
+ }
3010
+ function _parseWorkerRecord(value, path3) {
3011
+ const worker = _requireObject(value, "GET", path3, "worker");
3012
+ for (const field of ["id", "session", "name", "host", "consumer_status", "last_seen"]) {
3013
+ _requireStringField(worker, field, "GET", path3, "worker");
3014
+ }
3015
+ if (!isStringArray(worker.capabilities) || !isStringArray(worker.caps)) {
3016
+ throw new RelayProtocolError("GET", path3, "worker capabilities and caps must be string arrays");
3017
+ }
3018
+ if (!Number.isSafeInteger(worker.max_concurrency) || worker.max_concurrency < 1) {
3019
+ throw new RelayProtocolError("GET", path3, "worker.max_concurrency must be a positive integer");
3020
+ }
3021
+ if (typeof worker.online !== "boolean") {
3022
+ throw new RelayProtocolError("GET", path3, "worker.online must be boolean");
3023
+ }
3024
+ return worker;
3025
+ }
3026
+ function _validateWorkerJobSubmit(body) {
3027
+ requireSegment(body.worker, "worker");
3028
+ if (body.argv.length === 0 || !body.argv[0]?.trim())
3029
+ throw new Error("job argv requires a command");
3030
+ if (body.argv.some((arg) => typeof arg !== "string"))
3031
+ throw new Error("job argv must contain strings");
3032
+ if (body.timeoutSec !== undefined)
3033
+ _validateWorkerJobTimeout(body.timeoutSec);
3034
+ }
3035
+ function _validateWorkerJobTimeout(value) {
3036
+ if (!Number.isFinite(value) || value < 1 || value > MAX_JOB_TIMEOUT_SECONDS) {
3037
+ throw new Error(`timeoutSec must be a number from 1 to ${MAX_JOB_TIMEOUT_SECONDS}`);
3038
+ }
3039
+ }
3040
+ function _validateWorkerJobWaitTimeout(value) {
3041
+ const maximum = MAX_JOB_TIMEOUT_SECONDS + WORKER_JOB_RESULT_GRACE_SECONDS;
3042
+ if (!Number.isFinite(value) || value < 1 || value > maximum) {
3043
+ throw new Error(`result wait timeoutSec must be a number from 1 to ${maximum}`);
3044
+ }
3045
+ }
3046
+ function _parseWorkerJobSubmitResponse(value, path3) {
3047
+ if (typeof value.id !== "string" || !value.id || value.status !== "queued") {
3048
+ throw new RelayProtocolError("POST", path3, "job submit response requires id and queued status");
3049
+ }
3050
+ if (value.warning !== undefined && typeof value.warning !== "string") {
3051
+ throw new RelayProtocolError("POST", path3, "job submit warning must be a string");
3052
+ }
3053
+ return {
3054
+ id: value.id,
3055
+ status: "queued",
3056
+ ...value.warning === undefined ? {} : { warning: value.warning }
2898
3057
  };
2899
3058
  }
3059
+ function _parseWorkerJobResultResponse(value, path3) {
3060
+ const status = value.status;
3061
+ if (!_isWorkerJobStatus(status))
3062
+ throw new RelayProtocolError("GET", path3, "job status is invalid");
3063
+ for (const field of ["id", "created_at"])
3064
+ _requireStringField(value, field, "GET", path3, "job");
3065
+ _requireNullableStringFields(value, ["started_at", "finished_at", "submitter_agent_name"], path3);
3066
+ _requireNullableNumberFields(value, ["duration_sec"], path3);
3067
+ if (value.exit_code !== null && !Number.isSafeInteger(value.exit_code)) {
3068
+ throw new RelayProtocolError("GET", path3, "job.exit_code must be an integer or null");
3069
+ }
3070
+ const result = value.result === undefined ? undefined : _parseWorkerJobResult(value.result, path3);
3071
+ if (_isTerminalJobStatus(status) && (result === undefined || value.exit_code !== result.exit_code)) {
3072
+ throw new RelayProtocolError("GET", path3, "terminal job response requires a matching result");
3073
+ }
3074
+ return { ...value, status, ...result ? { result } : {} };
3075
+ }
3076
+ function _parseWorkerJobResult(value, path3) {
3077
+ const result = _requireObject(value, "GET", path3, "job result");
3078
+ for (const field of ["stdout", "stderr"])
3079
+ _requireStringField(result, field, "GET", path3, "job result", true);
3080
+ _requireNullableStringFields(result, ["started_at", "ended_at"], path3, "job result");
3081
+ if (!Number.isSafeInteger(result.exit_code)) {
3082
+ throw new RelayProtocolError("GET", path3, "job result.exit_code must be an integer");
3083
+ }
3084
+ _requireNullableNumberFields(result, ["duration_sec"], path3, "job result");
3085
+ if (!isStringArray(result.attached_file_paths)) {
3086
+ throw new RelayProtocolError("GET", path3, "job result.attached_file_paths must be a string array");
3087
+ }
3088
+ return result;
3089
+ }
3090
+ function _isWorkerJobStatus(value) {
3091
+ return value === "queued" || value === "running" || value === "done" || value === "failed" || value === "cancelled";
3092
+ }
3093
+ function _isTerminalJobStatus(value) {
3094
+ return value === "done" || value === "failed" || value === "cancelled";
3095
+ }
3096
+ function _requireObject(value, method, path3, label) {
3097
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
3098
+ throw new RelayProtocolError(method, path3, `${label} must be an object`);
3099
+ }
3100
+ return value;
3101
+ }
3102
+ function _requireStringField(value, field, method, path3, label, allowEmpty = false) {
3103
+ if (typeof value[field] !== "string" || !allowEmpty && !value[field]) {
3104
+ throw new RelayProtocolError(method, path3, `${label}.${field} must be a string`);
3105
+ }
3106
+ }
3107
+ function _requireNullableStringFields(value, fields, path3, label = "job") {
3108
+ for (const field of fields) {
3109
+ if (value[field] !== null && typeof value[field] !== "string") {
3110
+ throw new RelayProtocolError("GET", path3, `${label}.${field} must be a string or null`);
3111
+ }
3112
+ }
3113
+ }
3114
+ function _requireNullableNumberFields(value, fields, path3, label = "job") {
3115
+ for (const field of fields) {
3116
+ if (value[field] !== null && (typeof value[field] !== "number" || !Number.isFinite(value[field]))) {
3117
+ throw new RelayProtocolError("GET", path3, `${label}.${field} must be a finite number or null`);
3118
+ }
3119
+ }
3120
+ }
2900
3121
  function parseScheduledEventResponse(value, method, path3) {
2901
3122
  return { event: parseScheduledEvent(value.event, method, path3) };
2902
3123
  }
@@ -2966,34 +3187,6 @@ function _parseTeamAgentSlot(value, method, path3) {
2966
3187
  template_id: slot.template_id ?? null
2967
3188
  };
2968
3189
  }
2969
- function _parseTeamRepository(value, method, path3) {
2970
- if (!value || typeof value !== "object" || Array.isArray(value))
2971
- throw new RelayProtocolError(method, path3, "session must be an object");
2972
- const session = value;
2973
- if (typeof session.id !== "string" || !session.id)
2974
- throw new RelayProtocolError(method, path3, "session.id must be a non-empty string");
2975
- if (session.git_repo_url !== null && typeof session.git_repo_url !== "string")
2976
- throw new RelayProtocolError(method, path3, "session.git_repo_url must be a string or null");
2977
- if (!_isTeamRepositoryStatus(session.git_repo_verification_status))
2978
- throw new RelayProtocolError(method, path3, "session.git_repo_verification_status is invalid");
2979
- if (session.git_repo_verification_reason !== null && typeof session.git_repo_verification_reason !== "string")
2980
- throw new RelayProtocolError(method, path3, "session.git_repo_verification_reason must be a string or null");
2981
- return {
2982
- team_id: session.id,
2983
- git_repo_url: session.git_repo_url,
2984
- git_repo_verification_status: session.git_repo_verification_status,
2985
- git_repo_verification_reason: session.git_repo_verification_reason
2986
- };
2987
- }
2988
- function _isTeamRepositoryStatus(value) {
2989
- return value === "unverified" || value === "pending" || value === "verified" || value === "failed";
2990
- }
2991
- function _requireRepoVerificationRequest(request, method, path3, session, hostId) {
2992
- if (request.request_kind !== "repo_verification")
2993
- throw new RelayProtocolError(method, path3, "request_kind must be repo_verification");
2994
- if (request.session !== session || request.host_id !== hostId)
2995
- throw new RelayProtocolError(method, path3, "request identity does not match repository verification target");
2996
- }
2997
3190
  function _parseSessionArtifact(value, method, path3) {
2998
3191
  for (const field of ["id", "name", "sha256", "content_url"]) {
2999
3192
  if (typeof value[field] !== "string" || !value[field])
@@ -3357,7 +3550,7 @@ function parseAgentStartRequest(value, method, path3) {
3357
3550
  }
3358
3551
  if (request.request_kind === undefined)
3359
3552
  request.request_kind = "agent_start";
3360
- if (request.request_kind !== "agent_start" && request.request_kind !== "repo_verification" && request.request_kind !== "worker_start") {
3553
+ if (request.request_kind !== "agent_start" && request.request_kind !== "worker_start") {
3361
3554
  throw new RelayProtocolError(method, path3, "request.request_kind is invalid");
3362
3555
  }
3363
3556
  if (request.result !== undefined && (!request.result || typeof request.result !== "object" || Array.isArray(request.result))) {
@@ -3520,6 +3713,11 @@ function parseRagMemoryIndex(value, method, path3) {
3520
3713
  }));
3521
3714
  return { enabled: value.enabled, degraded: value.degraded, text: value.text, budget_tokens: value.budget_tokens, total_cards: value.total_cards, omitted_cards: value.omitted_cards, sections };
3522
3715
  }
3716
+ function parseRagEntitlement(value, method, path3) {
3717
+ if (typeof value.enabled !== "boolean")
3718
+ throw new RelayProtocolError(method, path3, "entitlement.enabled must be a boolean");
3719
+ return { enabled: value.enabled };
3720
+ }
3523
3721
  function parseManagedMaterializationResponse(value, method, path3, cursor) {
3524
3722
  if (value === "not_modified_304") {
3525
3723
  if (!cursor?.trim())
@@ -3849,41 +4047,10 @@ function redact(value, token) {
3849
4047
  function bytesToBase64(bytes) {
3850
4048
  return Buffer2.from(bytes).toString("base64");
3851
4049
  }
3852
- // .dist-releases/1784772051951-3107696/src/relay/config.js
4050
+ // .dist-releases/1784860966076-1188772/src/relay/config.js
3853
4051
  import { existsSync as existsSync2, readFileSync } from "node:fs";
3854
4052
  import { homedir as homedir3 } from "node:os";
3855
4053
  import path3 from "node:path";
3856
-
3857
- // .dist-releases/1784772051951-3107696/src/worker/protocol.js
3858
- var DEFAULT_RELAY_URL = DEFAULT_PRODUCT_API_URL;
3859
- var DEFAULT_WORKER = "host";
3860
- var DEFAULT_WORKDIR = "~/sailorbridge-workdir";
3861
- var DEFAULT_JOB_TIMEOUT_SECONDS = 300;
3862
- var MAX_JOB_TIMEOUT_SECONDS = 24 * 60 * 60;
3863
- var MIN_JOB_TIMEOUT_SECONDS = 1;
3864
- var ACTIVE_JOB_KINDS = new Set(["apply_diff", "bash", "read_file", "write_file", "compose", "stream", "status"]);
3865
- var READONLY_ALLOWED_KINDS = new Set(["read_file", "status"]);
3866
- var WORKER_SOCK_CONNECT_MS = 1e4;
3867
- var WORKER_PULL_READ_IDLE_MS = 45000;
3868
- var WORKER_QUICK_READ_IDLE_MS = 15000;
3869
- var WORKER_UPLOAD_READ_IDLE_MS = (MAX_JOB_TIMEOUT_SECONDS + 300) * 1000;
3870
- var DRAIN_AFTER_KILL_MS = 5000;
3871
- var WORKER_RETRY_INITIAL_MS = 1000;
3872
- var WORKER_RETRY_MAX_MS = 30000;
3873
- var WORKER_SESSION_REBUILD_FAILURES = 10;
3874
- var MAX_WORKER_RESULT_OUTPUT_BYTES = 256 * 1024;
3875
- function coerceJobTimeoutSeconds(spec) {
3876
- const value = spec.timeout_sec === undefined ? DEFAULT_JOB_TIMEOUT_SECONDS : spec.timeout_sec;
3877
- if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
3878
- throw new Error("spec.timeout_sec must be a finite positive number");
3879
- }
3880
- return Math.max(MIN_JOB_TIMEOUT_SECONDS, Math.min(value, MAX_JOB_TIMEOUT_SECONDS));
3881
- }
3882
- function effectiveJobKind(kind, spec) {
3883
- return kind === "stream" || spec.stream === true ? "stream" : kind;
3884
- }
3885
-
3886
- // .dist-releases/1784772051951-3107696/src/relay/config.js
3887
4054
  function loadRelayConfig(env = process.env) {
3888
4055
  const envRelayUrl = nonEmpty(env.SAILORBRIDGE_RELAY_URL);
3889
4056
  const envBearerToken = nonEmpty(env.SAILORBRIDGE_TOKEN);
@@ -3955,7 +4122,7 @@ function storedCaptainId(value) {
3955
4122
  }
3956
4123
  return;
3957
4124
  }
3958
- // .dist-releases/1784772051951-3107696/src/supervisor/retirement.js
4125
+ // .dist-releases/1784860966076-1188772/src/supervisor/retirement.js
3959
4126
  var DEFAULT_THRESHOLD = 3;
3960
4127
  var DEFAULT_WINDOW_MS = 30000;
3961
4128
  var DEFAULT_COOLDOWN_MS = 1000;
@@ -4030,7 +4197,7 @@ function isAuthoritativeRetirementReason(reason) {
4030
4197
  return reason === "monitor_fetch" || reason === "agent_session_patch";
4031
4198
  }
4032
4199
 
4033
- // .dist-releases/1784772051951-3107696/src/supervisor/wait.js
4200
+ // .dist-releases/1784860966076-1188772/src/supervisor/wait.js
4034
4201
  async function waitForWakeableTimeout(ms, setWake, sleep3) {
4035
4202
  if (sleep3) {
4036
4203
  await Promise.race([
@@ -4061,7 +4228,7 @@ async function waitForWakeableTimeout(ms, setWake, sleep3) {
4061
4228
  setWake(null);
4062
4229
  }
4063
4230
 
4064
- // .dist-releases/1784772051951-3107696/src/supervisor/mailbox.js
4231
+ // .dist-releases/1784860966076-1188772/src/supervisor/mailbox.js
4065
4232
  var CLEAR_TAG = "CLEAR";
4066
4233
  var REFRESH_TAG = "WORKFLOW-REFRESH";
4067
4234
  var SYNC_TAG = "SYNC";
@@ -4597,38 +4764,79 @@ function ordinaryDeliveryAttempt(error) {
4597
4764
  function _isMissingModerator(error) {
4598
4765
  return error instanceof RelayHttpError && error.status === 400 && error.body.includes("no agents match");
4599
4766
  }
4600
- // .dist-releases/1784772051951-3107696/src/supervisor/runtime.js
4767
+ // .dist-releases/1784860966076-1188772/src/supervisor/runtime.js
4601
4768
  import path9 from "node:path";
4602
4769
  import { execFile } from "node:child_process";
4603
4770
  import { promisify } from "node:util";
4604
4771
 
4605
- // .dist-releases/1784772051951-3107696/src/host/managed-materialization.js
4772
+ // .dist-releases/1784860966076-1188772/src/host/managed-materialization.js
4606
4773
  import { createHash as createHash2, randomBytes } from "node:crypto";
4607
4774
  import { constants as fsConstants } from "node:fs";
4608
4775
  import { lstat, mkdir as mkdir3, open, readdir, rename, rm as rm2 } from "node:fs/promises";
4609
4776
  import { homedir as homedir5 } from "node:os";
4610
4777
  import path5 from "node:path";
4611
4778
 
4612
- // .dist-releases/1784772051951-3107696/src/host/memory-stub.js
4613
- import { mkdir as mkdir2, writeFile as writeFile3 } from "node:fs/promises";
4779
+ // .dist-releases/1784860966076-1188772/src/host/memory-stub.js
4780
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile3 } from "node:fs/promises";
4614
4781
  import { homedir as homedir4 } from "node:os";
4615
4782
  import path4 from "node:path";
4616
- var RAG_MEMORY_STUB_BEGIN = "<!-- sailorbridge-rag-memory-stub:begin -->";
4617
- var RAG_MEMORY_STUB_END = "<!-- sailorbridge-rag-memory-stub:end -->";
4618
- var RAG_MEMORY_STUB = [
4619
- "# SailorBridge RAG Memory Protocol",
4783
+ var TEAM_MEMORY_STUB_BEGIN = "<!-- sailorbridge-team-memory-stub:begin -->";
4784
+ var TEAM_MEMORY_STUB_END = "<!-- sailorbridge-team-memory-stub:end -->";
4785
+ var LEGACY_STUB_BEGIN = "<!-- sailorbridge-rag-memory-stub:begin -->";
4786
+ var LEGACY_STUB_END = "<!-- sailorbridge-rag-memory-stub:end -->";
4787
+ var STUB_LOCAL_MEMORY = [
4788
+ "- Your local memory files are yours: the product never collects, rewrites, or deletes them.",
4789
+ " Keep working notes and everything tied to your own role in local memory.",
4790
+ "- Role-bound and personal content must stay local. Never publish it to shared team stores:",
4791
+ " another member recalling your role's private memory is an identity mix-up."
4792
+ ];
4793
+ var TEAM_MEMORY_STUB_ENTITLED = [
4794
+ "# SailorBridge Team Memory Protocol",
4620
4795
  "",
4621
- "- Memory is service-only with zero synchronization: store durable facts only in the relay RAG card store, never in local memory files.",
4622
- "- Run `sail rag search` before recall; treat returned RAG cards as authoritative and never override them from local memory.",
4623
- "- Write learned durable facts back with `sail rag set <key> --stdin --source <source>` or `sail rag set <key> --file <path> --source <source>`.",
4624
- "- Keep only this protocol stub in local memory files: query RAG first, trust the cards, and write learned facts back to RAG.",
4625
- "- Never write durable facts to CLAUDE.md, AGENTS.md, MEMORY.md, or engine-native local memory directories; RAG is the only write channel.",
4626
- "- Fail fast when the service is unavailable; never present stale local content as a successful result.",
4627
- "- Optional read-only snapshots must be clearly marked as disaster recovery.",
4796
+ ...STUB_LOCAL_MEMORY,
4797
+ "- Team memory is the team's shared knowledge and experience. Before a non-trivial",
4798
+ " question, run `sail memory search <query>` someone may already know.",
4799
+ "- When you learn something the whole team benefits from, save it with",
4800
+ " `sail memory set <key> --stdin --source <source>` (or `--file <path>`).",
4801
+ "- Prefer team memory over rules when saving: rules are behavior norms; knowledge",
4802
+ " and experience belong in team memory.",
4803
+ "- If the memory service is unavailable, fail fast and say so; never present stale",
4804
+ " local content as the team's shared truth.",
4628
4805
  ""
4629
4806
  ].join(`
4630
4807
  `);
4631
- var RAG_MEMORY_STUB_MARKED = _markedStub(RAG_MEMORY_STUB);
4808
+ var TEAM_MEMORY_STUB_UNENTITLED = [
4809
+ "# SailorBridge Team Memory Protocol",
4810
+ "",
4811
+ ...STUB_LOCAL_MEMORY,
4812
+ "- Share lessons and working norms through team rules: propose a rule to your",
4813
+ " captain or moderator so the whole team inherits it.",
4814
+ "- The Team Memory add-on adds shared cross-member memory with corrections",
4815
+ " (`sail memory` commands); the captain can enable it from the console.",
4816
+ ""
4817
+ ].join(`
4818
+ `);
4819
+ function teamMemoryStub(entitled) {
4820
+ return entitled ? TEAM_MEMORY_STUB_ENTITLED : TEAM_MEMORY_STUB_UNENTITLED;
4821
+ }
4822
+ function teamMemoryStubMarked(entitled) {
4823
+ return `${TEAM_MEMORY_STUB_BEGIN}
4824
+ ${teamMemoryStub(entitled)}${TEAM_MEMORY_STUB_END}
4825
+ `;
4826
+ }
4827
+ async function resolveTeamMemoryEntitlement(relay, captainId, teamId) {
4828
+ const captain = captainId?.trim();
4829
+ const team = teamId?.trim();
4830
+ if (!captain || !team)
4831
+ throw new Error("team memory entitlement requires captain_id and session");
4832
+ try {
4833
+ return (await relay.getRagEntitlement(captain, team)).enabled;
4834
+ } catch (error) {
4835
+ if (error instanceof RelayHttpError && error.status === 402)
4836
+ return false;
4837
+ throw error;
4838
+ }
4839
+ }
4632
4840
  async function materializeAgentMemoryStub(options) {
4633
4841
  const engine = _memoryStubEngine(options.engine);
4634
4842
  const homeDir = options.homeDir ? path4.resolve(_expandHome(options.homeDir)) : agentSandboxHome(options.agentName, engine, options.env ?? process.env);
@@ -4637,7 +4845,10 @@ async function materializeAgentMemoryStub(options) {
4637
4845
  const mkdirFn = options.mkdir ?? mkdir2;
4638
4846
  const writeFileFn = options.writeFile ?? writeFile3;
4639
4847
  await mkdirFn(homeDir, { recursive: true });
4640
- await writeFileFn(filePath, RAG_MEMORY_STUB_MARKED, { mode: 384 });
4848
+ const existing = await _readIfPresent(filePath, options.readFile ?? readFile2);
4849
+ const next = _spliceStubBlock(existing, teamMemoryStubMarked(options.teamMemoryEntitled));
4850
+ if (next !== existing)
4851
+ await writeFileFn(filePath, next, { mode: 384 });
4641
4852
  return {
4642
4853
  engine,
4643
4854
  homeDir,
@@ -4652,6 +4863,38 @@ function agentSandboxHome(agentName, engine, env = process.env) {
4652
4863
  function memoryStubFileName(engine) {
4653
4864
  return engine === "claude" ? "CLAUDE.md" : "AGENTS.md";
4654
4865
  }
4866
+ function _spliceStubBlock(existing, block) {
4867
+ if (existing === null || !existing.trim())
4868
+ return block;
4869
+ const current = _replaceMarkedBlock(existing, TEAM_MEMORY_STUB_BEGIN, TEAM_MEMORY_STUB_END, block) ?? _replaceMarkedBlock(existing, LEGACY_STUB_BEGIN, LEGACY_STUB_END, block);
4870
+ if (current !== undefined)
4871
+ return current;
4872
+ const cleaned = _dropStrayMarkers(existing);
4873
+ if (!cleaned.trim())
4874
+ return block;
4875
+ return `${block}
4876
+ ${cleaned.replace(/^\n+/u, "")}`;
4877
+ }
4878
+ function _replaceMarkedBlock(content, begin, end, block) {
4879
+ const start = content.indexOf(begin);
4880
+ const stop = content.indexOf(end);
4881
+ if (start < 0 || stop < start)
4882
+ return;
4883
+ const after = content.slice(stop + end.length).replace(/^\n/u, "");
4884
+ return `${content.slice(0, start)}${block}${after}`;
4885
+ }
4886
+ function _dropStrayMarkers(content) {
4887
+ return [TEAM_MEMORY_STUB_BEGIN, TEAM_MEMORY_STUB_END, LEGACY_STUB_BEGIN, LEGACY_STUB_END].reduce((text, marker) => text.split(marker).join(""), content);
4888
+ }
4889
+ async function _readIfPresent(filePath, readFileFn) {
4890
+ try {
4891
+ return await readFileFn(filePath, "utf8");
4892
+ } catch (error) {
4893
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT")
4894
+ return null;
4895
+ throw error;
4896
+ }
4897
+ }
4655
4898
  function _memoryStubEngine(value) {
4656
4899
  if (value !== "claude" && value !== "codex") {
4657
4900
  throw new Error("memory stub engine must be claude or codex");
@@ -4679,13 +4922,8 @@ function _safePathLabel(value, field) {
4679
4922
  }
4680
4923
  return trimmed;
4681
4924
  }
4682
- function _markedStub(body) {
4683
- return `${RAG_MEMORY_STUB_BEGIN}
4684
- ${body}${RAG_MEMORY_STUB_END}
4685
- `;
4686
- }
4687
4925
 
4688
- // .dist-releases/1784772051951-3107696/src/host/managed-materialization.js
4926
+ // .dist-releases/1784860966076-1188772/src/host/managed-materialization.js
4689
4927
  var MANIFEST_FILE = ".sailorbridge-managed-manifest.json";
4690
4928
  var BANNER_PREFIX = "# SailorBridge managed file";
4691
4929
  var HASH_RE = /^sha256:[a-f0-9]{64}$/u;
@@ -5211,14 +5449,14 @@ function _expandHome2(value) {
5211
5449
  return value;
5212
5450
  }
5213
5451
 
5214
- // .dist-releases/1784772051951-3107696/src/host/credential-guardian.js
5215
- import { lstat as lstat2, readFile as readFile2, rename as rename2, symlink, unlink, writeFile as writeFile4 } from "node:fs/promises";
5452
+ // .dist-releases/1784860966076-1188772/src/host/credential-guardian.js
5453
+ import { lstat as lstat2, readFile as readFile3, rename as rename2, symlink, unlink, writeFile as writeFile4 } from "node:fs/promises";
5216
5454
  import path6 from "node:path";
5217
5455
  var DEFAULT_CREDENTIAL_SWEEP_INTERVAL_MS = 60000;
5218
5456
  var DEFAULT_FAILURE_ALERT_THRESHOLD = 3;
5219
5457
  var defaultFs = {
5220
5458
  lstat: lstat2,
5221
- readFile: (filePath) => readFile2(filePath),
5459
+ readFile: (filePath) => readFile3(filePath),
5222
5460
  writeFile: (filePath, data, options) => writeFile4(filePath, data, options),
5223
5461
  rename: rename2,
5224
5462
  symlink: (target, linkPath) => symlink(target, linkPath),
@@ -5357,7 +5595,7 @@ function _tmpSibling(filePath) {
5357
5595
  return path6.join(path6.dirname(filePath), `.${path6.basename(filePath)}.sb-guardian.${process.pid}.${tmpSequence}.tmp`);
5358
5596
  }
5359
5597
 
5360
- // .dist-releases/1784772051951-3107696/src/supervisor/resolve.js
5598
+ // .dist-releases/1784860966076-1188772/src/supervisor/resolve.js
5361
5599
  var DEFAULT_CAPTURE_LINES = 200;
5362
5600
  var MAX_CAPTURE_LINES = 1000;
5363
5601
 
@@ -5563,7 +5801,7 @@ function defaultSleep(ms) {
5563
5801
  return new Promise((resolve) => setTimeout(resolve, ms));
5564
5802
  }
5565
5803
 
5566
- // .dist-releases/1784772051951-3107696/src/supervisor/transcript-backfill.js
5804
+ // .dist-releases/1784860966076-1188772/src/supervisor/transcript-backfill.js
5567
5805
  import { createHash as createHash3 } from "node:crypto";
5568
5806
  import {
5569
5807
  closeSync,
@@ -6220,7 +6458,7 @@ function errorMessage4(error) {
6220
6458
  return error instanceof Error ? error.message : String(error);
6221
6459
  }
6222
6460
 
6223
- // .dist-releases/1784772051951-3107696/src/supervisor/usage-collector.js
6461
+ // .dist-releases/1784860966076-1188772/src/supervisor/usage-collector.js
6224
6462
  import { statSync as statSync2 } from "node:fs";
6225
6463
  import path8 from "node:path";
6226
6464
  var SUPPORTED_USAGE_ENGINES = new Set(["claude", "codex"]);
@@ -6629,7 +6867,7 @@ function sourceUnreadableReason(source) {
6629
6867
  }
6630
6868
  }
6631
6869
 
6632
- // .dist-releases/1784772051951-3107696/src/supervisor/runtime.js
6870
+ // .dist-releases/1784860966076-1188772/src/supervisor/runtime.js
6633
6871
  var RESOLVE_CAPTURE_LINES = 200;
6634
6872
  var SHORT_LIVED_EXIT_MS = 5000;
6635
6873
  var STARTUP_OBSERVATION_MS = 1e4;
@@ -7299,7 +7537,7 @@ function errorMessage5(error) {
7299
7537
  function formatRestartCommand(command) {
7300
7538
  return command.map((part) => /^[A-Za-z0-9_./:=@%+-]+$/.test(part) ? part : `'${part.replaceAll("'", "'\\''")}'`).join(" ");
7301
7539
  }
7302
- // .dist-releases/1784772051951-3107696/src/supervisor/transport.js
7540
+ // .dist-releases/1784860966076-1188772/src/supervisor/transport.js
7303
7541
  var SUPPORTED_PHASE1_CLI_KINDS = ["claude", "codex"];
7304
7542
  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.";
7305
7543
  var PHASE1_TRANSPORT_GATE_MESSAGE = "Only tmux transport is available in this release; sdk, headless, and pty transports will open in the next release.";
@@ -7507,7 +7745,7 @@ function hasCodexSafetyDriverOptions(options) {
7507
7745
  return options?.codex?.sandboxMode !== undefined || options?.codex?.approvalPolicy !== undefined || options?.codex?.yolo !== undefined;
7508
7746
  }
7509
7747
 
7510
- // .dist-releases/1784772051951-3107696/src/supervisor/cli-options.js
7748
+ // .dist-releases/1784860966076-1188772/src/supervisor/cli-options.js
7511
7749
  function parseSuperviseArgs(argv) {
7512
7750
  const split = argv.indexOf("--");
7513
7751
  const flagArgs = split >= 0 ? argv.slice(0, split) : argv;
@@ -7659,7 +7897,7 @@ function optionalBoolean(value, label) {
7659
7897
  return false;
7660
7898
  throw new Error(`${label} must be true or false`);
7661
7899
  }
7662
- // .dist-releases/1784772051951-3107696/src/supervisor/tmux-availability.js
7900
+ // .dist-releases/1784860966076-1188772/src/supervisor/tmux-availability.js
7663
7901
  import { spawn as spawn3 } from "node:child_process";
7664
7902
  import { createInterface } from "node:readline";
7665
7903
  function isTmuxAvailable(spawnFn = spawn3) {
@@ -7776,26 +8014,29 @@ function _tmuxMissingMessage(platform2) {
7776
8014
  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";
7777
8015
  return `tmux is required in this release but was not found. Install tmux (${how}) and re-run.`;
7778
8016
  }
7779
- // .dist-releases/1784772051951-3107696/src/output-format.js
8017
+ // .dist-releases/1784860966076-1188772/src/output-format.js
7780
8018
  function parseOutputArgs(argv) {
8019
+ const separator = argv.indexOf("--");
8020
+ const localArgv = separator < 0 ? argv : argv.slice(0, separator);
8021
+ const remoteArgv = separator < 0 ? [] : argv.slice(separator);
7781
8022
  const cleaned = [];
7782
8023
  let format = "plain";
7783
8024
  let seen = false;
7784
- for (let index = 0;index < argv.length; index += 1) {
7785
- const token = argv[index];
8025
+ for (let index = 0;index < localArgv.length; index += 1) {
8026
+ const token = localArgv[index];
7786
8027
  if (token !== "--format" && !token.startsWith("--format=")) {
7787
8028
  cleaned.push(token);
7788
8029
  continue;
7789
8030
  }
7790
8031
  if (seen)
7791
8032
  throw new Error("--format may only be provided once");
7792
- const value = token === "--format" ? argv[++index] : token.slice("--format=".length);
8033
+ const value = token === "--format" ? localArgv[++index] : token.slice("--format=".length);
7793
8034
  if (value !== "plain" && value !== "json")
7794
8035
  throw new Error("--format must be plain or json");
7795
8036
  format = value;
7796
8037
  seen = true;
7797
8038
  }
7798
- return { argv: cleaned, format, provided: seen };
8039
+ return { argv: [...cleaned, ...remoteArgv], format, provided: seen };
7799
8040
  }
7800
8041
  function _renderOutput(value, format, plain) {
7801
8042
  if (format === "json") {
@@ -7866,7 +8107,7 @@ ${normalized.split(`
7866
8107
  `)}`;
7867
8108
  }
7868
8109
 
7869
- // .dist-releases/1784772051951-3107696/src/plain-output-fields.js
8110
+ // .dist-releases/1784860966076-1188772/src/plain-output-fields.js
7870
8111
  var PREVIEW_LIMIT = 20;
7871
8112
  var UNSAFE_DISPLAY_CODE_POINT = /[\u007f-\u009f\u2028-\u202e\u2066-\u2069]/gu;
7872
8113
  function renderTaskPlain(command, value) {
@@ -8274,7 +8515,7 @@ function _omissionLine(total) {
8274
8515
  return total > PREVIEW_LIMIT ? `Omitted ${total - PREVIEW_LIMIT}; rerun with --format json for the complete response.` : undefined;
8275
8516
  }
8276
8517
 
8277
- // .dist-releases/1784772051951-3107696/src/host/cli.js
8518
+ // .dist-releases/1784860966076-1188772/src/host/cli.js
8278
8519
  var HOST_RELAY_SUBCOMMANDS = new Set(["consume", "heartbeat", "list", "register"]);
8279
8520
  async function runHostCommandFromCli(client, argv, config, io) {
8280
8521
  const sub = argv[0] ?? "list";
@@ -8512,11 +8753,11 @@ function optionalDisplayName(value) {
8512
8753
  const trimmed = value?.trim();
8513
8754
  return trimmed || undefined;
8514
8755
  }
8515
- // .dist-releases/1784772051951-3107696/src/host/service.js
8756
+ // .dist-releases/1784860966076-1188772/src/host/service.js
8516
8757
  import { execFile as execFileCb } from "node:child_process";
8517
8758
  import { createHash as createHash4 } from "node:crypto";
8518
8759
  import { constants as fsConstants2 } from "node:fs";
8519
- import { access, chmod as chmod2, lstat as lstat3, mkdir as mkdir4, readFile as readFile3, rename as rename3, rm as rm3, writeFile as writeFile5 } from "node:fs/promises";
8760
+ import { access, chmod as chmod2, lstat as lstat3, mkdir as mkdir4, readFile as readFile4, rename as rename3, rm as rm3, writeFile as writeFile5 } from "node:fs/promises";
8520
8761
  import { homedir as homedir7, platform as platform2, userInfo } from "node:os";
8521
8762
  import path10 from "node:path";
8522
8763
  import { promisify as promisify2 } from "node:util";
@@ -8736,7 +8977,7 @@ function serviceRuntime(deps) {
8736
8977
  username: deps.username ?? info.username,
8737
8978
  execFile: deps.execFile ?? execFileDefault,
8738
8979
  writeFile: deps.writeFile ?? writeFile5,
8739
- readFile: deps.readFile ?? readFile3,
8980
+ readFile: deps.readFile ?? readFile4,
8740
8981
  mkdir: deps.mkdir ?? mkdir4,
8741
8982
  rm: deps.rm ?? rm3,
8742
8983
  access: deps.access ?? access,
@@ -8995,7 +9236,7 @@ function writeServiceLog(io, event, fields) {
8995
9236
  io.stderr.write(`${JSON.stringify({ ts: new Date().toISOString(), event, ...fields ?? {} })}
8996
9237
  `);
8997
9238
  }
8998
- // .dist-releases/1784772051951-3107696/src/host/git-remote.js
9239
+ // .dist-releases/1784860966076-1188772/src/host/git-remote.js
8999
9240
  var GIT_SECRET_PATH_MARKERS = [
9000
9241
  "token",
9001
9242
  "credential",
@@ -9047,16 +9288,6 @@ function sanitizeGitOrigin(value) {
9047
9288
  }
9048
9289
  return hasCredentialPath(origin) ? null : origin;
9049
9290
  }
9050
- function gitRemoteRequiresGitHubAuth(value) {
9051
- const remote = value.trim();
9052
- if (remote.startsWith("git@"))
9053
- return /^git@github\.com:/iu.test(remote);
9054
- try {
9055
- return new URL(remote).hostname.toLowerCase() === "github.com";
9056
- } catch {
9057
- return false;
9058
- }
9059
- }
9060
9291
  function isAcceptedRemote(value, allowLocalPath) {
9061
9292
  const remote = value.trim();
9062
9293
  if (!remote || remote.length > 2048 || GIT_REMOTE_UNSAFE_RE.test(remote))
@@ -9097,7 +9328,7 @@ function looksLikeCredential(segment2) {
9097
9328
  return value.length >= 32 && /^[A-Za-z0-9_-]+$/u.test(value);
9098
9329
  }
9099
9330
 
9100
- // .dist-releases/1784772051951-3107696/src/host/start-spec.js
9331
+ // .dist-releases/1784860966076-1188772/src/host/start-spec.js
9101
9332
  var TRANSPORTS = ["auto", "sdk", "pty", "tmux", "headless"];
9102
9333
  var SUPPORTED_PHASE1_ENGINES = ["claude", "codex"];
9103
9334
  var ENGINE_PATTERN = /^[a-z][a-z0-9._-]{0,63}$/u;
@@ -9347,7 +9578,7 @@ function _trimmed(value, label) {
9347
9578
  throw new Error(`${label} must not be empty`);
9348
9579
  return trimmed;
9349
9580
  }
9350
- // .dist-releases/1784772051951-3107696/src/host/workspace.js
9581
+ // .dist-releases/1784860966076-1188772/src/host/workspace.js
9351
9582
  import { spawn as spawn4 } from "node:child_process";
9352
9583
  import { existsSync as existsSync4 } from "node:fs";
9353
9584
  import { mkdir as mkdir5, readdir as readdir2, rename as rename4, stat } from "node:fs/promises";
@@ -9578,7 +9809,7 @@ function _safeSegment(value) {
9578
9809
  throw new Error("workspace path segment is empty after sanitizing");
9579
9810
  return segment2;
9580
9811
  }
9581
- // .dist-releases/1784772051951-3107696/src/host/workspace-inventory.js
9812
+ // .dist-releases/1784860966076-1188772/src/host/workspace-inventory.js
9582
9813
  import { access as access2 } from "node:fs/promises";
9583
9814
  import { constants } from "node:fs";
9584
9815
  import { execFile as execFile2 } from "node:child_process";
@@ -9670,8 +9901,8 @@ async function canAccess(path12, mode, deps) {
9670
9901
  return false;
9671
9902
  }
9672
9903
  }
9673
- // .dist-releases/1784772051951-3107696/src/host/host-config.js
9674
- import { chmod as chmod3, mkdir as mkdir6, readFile as readFile4, writeFile as writeFile6 } from "node:fs/promises";
9904
+ // .dist-releases/1784860966076-1188772/src/host/host-config.js
9905
+ import { chmod as chmod3, mkdir as mkdir6, readFile as readFile5, writeFile as writeFile6 } from "node:fs/promises";
9675
9906
  import { homedir as homedir10 } from "node:os";
9676
9907
  import path12 from "node:path";
9677
9908
  var HOST_CONFIG_KIND = "sailorbridge-host-config";
@@ -9688,7 +9919,7 @@ function normalizeWorkspacePath(entry) {
9688
9919
  async function loadHostConfig(configPath = defaultHostConfigPath()) {
9689
9920
  let text;
9690
9921
  try {
9691
- text = await readFile4(configPath, "utf8");
9922
+ text = await readFile5(configPath, "utf8");
9692
9923
  } catch (error) {
9693
9924
  if (error.code === "ENOENT")
9694
9925
  return { kind: HOST_CONFIG_KIND, workspaces: [] };
@@ -9755,7 +9986,7 @@ function _parseHostConfig(text, configPath) {
9755
9986
  });
9756
9987
  return { kind: HOST_CONFIG_KIND, workspaces };
9757
9988
  }
9758
- // .dist-releases/1784772051951-3107696/src/host/host-workspace-cli.js
9989
+ // .dist-releases/1784860966076-1188772/src/host/host-workspace-cli.js
9759
9990
  async function runHostWorkspaceCommandFromCli(argv, io, deps = {}) {
9760
9991
  const configPath = deps.configPath ?? defaultHostConfigPath();
9761
9992
  const sub = argv[0] ?? "list";
@@ -9799,7 +10030,7 @@ function _renderAddPlain(result) {
9799
10030
  return result.note ? `${status} ${result.path}
9800
10031
  ${result.note}` : `${status} ${result.path}`;
9801
10032
  }
9802
- // .dist-releases/1784772051951-3107696/src/host/agent-env.js
10033
+ // .dist-releases/1784860966076-1188772/src/host/agent-env.js
9803
10034
  var RUNTIME_NATIVE_HOME_ENV = new Set(["CLAUDE_CONFIG_DIR", "CODEX_HOME"]);
9804
10035
  function buildAgentEnv(options) {
9805
10036
  const env = {};
@@ -9828,16 +10059,16 @@ function buildAgentEnv(options) {
9828
10059
  }
9829
10060
  return env;
9830
10061
  }
9831
- // .dist-releases/1784772051951-3107696/src/host/runtime-env.js
10062
+ // .dist-releases/1784860966076-1188772/src/host/runtime-env.js
9832
10063
  import path16 from "node:path";
9833
10064
 
9834
- // .dist-releases/1784772051951-3107696/src/host/agent-defaults.js
9835
- import { chmod as chmod4, mkdir as mkdir7, readFile as readFile6, stat as stat2, writeFile as writeFile7 } from "node:fs/promises";
10065
+ // .dist-releases/1784860966076-1188772/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";
9836
10067
  import { homedir as homedir11 } from "node:os";
9837
10068
  import path14 from "node:path";
9838
10069
 
9839
- // .dist-releases/1784772051951-3107696/src/host/claude-settings.js
9840
- import { readFile as readFile5 } from "node:fs/promises";
10070
+ // .dist-releases/1784860966076-1188772/src/host/claude-settings.js
10071
+ import { readFile as readFile6 } from "node:fs/promises";
9841
10072
  import path13 from "node:path";
9842
10073
  async function readHostClaudeSettings(claudeConfigDir) {
9843
10074
  const settings = await _readSettingsObject(path13.join(claudeConfigDir, "settings.json"));
@@ -9846,7 +10077,7 @@ async function readHostClaudeSettings(claudeConfigDir) {
9846
10077
  async function _readSettingsObject(filePath) {
9847
10078
  let text;
9848
10079
  try {
9849
- text = await readFile5(filePath, "utf8");
10080
+ text = await readFile6(filePath, "utf8");
9850
10081
  } catch (error) {
9851
10082
  if (error.code === "ENOENT")
9852
10083
  return {};
@@ -9877,7 +10108,7 @@ function _nonEmptyString(value) {
9877
10108
  return typeof value === "string" && value.trim() ? value : null;
9878
10109
  }
9879
10110
 
9880
- // .dist-releases/1784772051951-3107696/src/host/agent-defaults.js
10111
+ // .dist-releases/1784860966076-1188772/src/host/agent-defaults.js
9881
10112
  var CODEX_BLOCK_BEGIN = "# sailorbridge-terse-defaults:begin";
9882
10113
  var CODEX_BLOCK_END = "# sailorbridge-terse-defaults:end";
9883
10114
  var CODEX_MODEL_BLOCK_BEGIN = "# sailorbridge-host-model-settings:begin";
@@ -10600,7 +10831,7 @@ async function _claudeTrustRoots(projectPath) {
10600
10831
  }
10601
10832
  if (!stats.isFile())
10602
10833
  return [projectPath];
10603
- const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await readFile6(pointer, "utf8"))?.[1];
10834
+ const gitdir = /^gitdir:\s*(.+)\s*$/m.exec(await readFile7(pointer, "utf8"))?.[1];
10604
10835
  if (!gitdir) {
10605
10836
  throw new Error(`workspace ${projectPath} has an unreadable .git pointer — cannot pre-trust its main repository for claude`);
10606
10837
  }
@@ -10986,7 +11217,7 @@ async function _readTextIfExists2(filePath) {
10986
11217
  return "";
10987
11218
  throw error;
10988
11219
  }
10989
- return readFile6(filePath, "utf8");
11220
+ return readFile7(filePath, "utf8");
10990
11221
  }
10991
11222
  function _expandHome3(value) {
10992
11223
  if (value === "~")
@@ -11007,7 +11238,7 @@ function _escapeRegExp(value) {
11007
11238
  return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
11008
11239
  }
11009
11240
 
11010
- // .dist-releases/1784772051951-3107696/src/host/engine-auth.js
11241
+ // .dist-releases/1784860966076-1188772/src/host/engine-auth.js
11011
11242
  import { lstat as lstat4, mkdir as mkdir8, readlink, realpath, stat as stat3, symlink as symlink2, unlink as unlink2 } from "node:fs/promises";
11012
11243
  import { homedir as homedir12 } from "node:os";
11013
11244
  import path15 from "node:path";
@@ -11097,7 +11328,7 @@ async function _ensureSymlink(linkPath, targetPath, sharedTargetPath) {
11097
11328
  await symlink2(targetPath, linkPath);
11098
11329
  }
11099
11330
 
11100
- // .dist-releases/1784772051951-3107696/src/host/runtime-env.js
11331
+ // .dist-releases/1784860966076-1188772/src/host/runtime-env.js
11101
11332
  async function materializeAgentRuntimeEnv(options) {
11102
11333
  const env = buildAgentEnv({
11103
11334
  hostEnv: options.hostEnv,
@@ -11108,6 +11339,7 @@ async function materializeAgentRuntimeEnv(options) {
11108
11339
  const memoryStub = await materializeAgentMemoryStub({
11109
11340
  agentName: options.agentName,
11110
11341
  engine: options.engine,
11342
+ teamMemoryEntitled: options.teamMemoryEntitled,
11111
11343
  env: options.hostEnv,
11112
11344
  homeDir
11113
11345
  });
@@ -11136,7 +11368,7 @@ function _ensureRootSandboxEscape(env, engine, uid) {
11136
11368
  function _currentUid() {
11137
11369
  return typeof process.getuid === "function" ? process.getuid() : null;
11138
11370
  }
11139
- // .dist-releases/1784772051951-3107696/src/host/context-statusline.js
11371
+ // .dist-releases/1784860966076-1188772/src/host/context-statusline.js
11140
11372
  function renderClaudeContextStatusLine(payload) {
11141
11373
  const percent = _usedPercentage(payload);
11142
11374
  if (percent === null)
@@ -11174,179 +11406,10 @@ function _bar(percent) {
11174
11406
  const filled = Math.min(10, Math.max(0, Math.trunc(percent / 10)));
11175
11407
  return "█".repeat(filled) + "░".repeat(10 - filled);
11176
11408
  }
11177
- // .dist-releases/1784772051951-3107696/src/host/memory-sweeper.js
11178
- import { createHash as createHash5 } from "node:crypto";
11179
- import { readdir as readdir3, readFile as readFile7, stat as stat4, writeFile as writeFile8 } from "node:fs/promises";
11180
- import path17 from "node:path";
11181
- function createMemorySweepState() {
11182
- return { completedFingerprints: new Map };
11183
- }
11184
- async function sweepAgentMemory(options) {
11185
- const captainId = _captainId2(options.captainId);
11186
- const engine = _memoryEngine(options.engine);
11187
- const sandbox = agentSandboxHome(options.agentName, engine, options.env ?? process.env);
11188
- const candidates = await _memoryCandidates(sandbox, engine, options);
11189
- let ingested = 0;
11190
- let skipped = 0;
11191
- for (const candidate of candidates) {
11192
- const changed = await _sweepCandidate(candidate, { ...options, captainId, engine });
11193
- if (changed)
11194
- ingested += 1;
11195
- else
11196
- skipped += 1;
11197
- }
11198
- return { scanned: candidates.length, ingested, skipped };
11199
- }
11200
- async function _sweepCandidate(candidate, options) {
11201
- const content = await (options.readFile ?? readFile7)(candidate.filePath, "utf8");
11202
- const spill = _spillContent(content);
11203
- if (spill.malformedStub) {
11204
- options.logger?.("host_memory_spill_malformed_marker", {
11205
- agent_name: options.agentName,
11206
- engine: options.engine,
11207
- mode: options.mode ?? "runtime",
11208
- path: candidate.relativePath
11209
- });
11210
- return false;
11211
- }
11212
- if (!spill.text.trim())
11213
- return false;
11214
- const fingerprint = _fingerprint(candidate.filePath, spill.text, await (options.stat ?? stat4)(candidate.filePath));
11215
- if (options.state?.completedFingerprints.get(candidate.filePath) === fingerprint)
11216
- return false;
11217
- const response = await options.relay.ingestRagDocument(options.captainId, options.teamId, _ingestBody(candidate, options, spill.text));
11218
- await _verifyCards(options.relay, options.captainId, options.teamId, response);
11219
- const latestContent = await (options.readFile ?? readFile7)(candidate.filePath, "utf8");
11220
- if (latestContent !== content) {
11221
- options.logger?.("host_memory_spill_changed_before_slim", {
11222
- agent_name: options.agentName,
11223
- engine: options.engine,
11224
- mode: options.mode ?? "runtime",
11225
- path: candidate.relativePath,
11226
- cards: response.cards
11227
- });
11228
- return true;
11229
- }
11230
- await (options.writeFile ?? writeFile8)(candidate.filePath, RAG_MEMORY_STUB_MARKED, { mode: 384 });
11231
- options.state?.completedFingerprints.set(candidate.filePath, fingerprint);
11232
- options.logger?.("host_memory_spill_ingested", {
11233
- agent_name: options.agentName,
11234
- engine: options.engine,
11235
- mode: options.mode ?? "runtime",
11236
- path: candidate.relativePath,
11237
- cards: response.cards
11238
- });
11239
- return true;
11240
- }
11241
- async function _memoryCandidates(sandbox, engine, deps) {
11242
- const canonical = path17.join(sandbox, memoryStubFileName(engine));
11243
- const candidates = await _fileExists(canonical, deps) ? [_candidate(sandbox, canonical)] : [];
11244
- const projectRoot = path17.join(sandbox, "projects");
11245
- for (const filePath of await _walkMemoryFiles(projectRoot, deps)) {
11246
- if (filePath !== canonical)
11247
- candidates.push(_candidate(sandbox, filePath));
11248
- }
11249
- return candidates;
11250
- }
11251
- async function _walkMemoryFiles(root, deps) {
11252
- if (!await _dirExists(root, deps))
11253
- return [];
11254
- const out = [];
11255
- for (const entry of await (deps.readdir ?? readdir3)(root, { withFileTypes: true })) {
11256
- const filePath = path17.join(root, entry.name);
11257
- if (entry.isDirectory())
11258
- out.push(...await _walkMemoryFiles(filePath, deps));
11259
- if (entry.isFile() && path17.basename(filePath) === "MEMORY.md" && path17.basename(path17.dirname(filePath)) === "memory") {
11260
- out.push(filePath);
11261
- }
11262
- }
11263
- return out;
11264
- }
11265
- function _spillContent(content) {
11266
- const start = content.indexOf(RAG_MEMORY_STUB_BEGIN);
11267
- const stop = content.indexOf(RAG_MEMORY_STUB_END);
11268
- if (start < 0 && stop < 0)
11269
- return { text: content, malformedStub: false };
11270
- if (start < 0 || stop < start)
11271
- return { text: "", malformedStub: true };
11272
- return {
11273
- text: _joinSpillParts(content.slice(0, start), content.slice(stop + RAG_MEMORY_STUB_END.length)),
11274
- malformedStub: false
11275
- };
11276
- }
11277
- function _joinSpillParts(beforeStub, afterStub) {
11278
- const before = beforeStub.trim();
11279
- const after = afterStub.trim();
11280
- if (!before)
11281
- return after ? `${after}
11282
- ` : "";
11283
- if (!after)
11284
- return `${before}
11285
- `;
11286
- return `${before}
11287
- ${after}
11288
- `;
11289
- }
11290
- function _ingestBody(candidate, options, text) {
11291
- return {
11292
- name: `agent-memory/${options.agentName}/${options.engine}/${candidate.relativePath}`,
11293
- text,
11294
- type: "memory",
11295
- max_chars: 800,
11296
- citation: { agent: options.agentName, engine: options.engine, file: candidate.relativePath }
11297
- };
11298
- }
11299
- async function _verifyCards(relay, captainId, teamId, response) {
11300
- if (response.cards < 1)
11301
- throw new Error("RAG ingest returned no cards for non-empty memory spill");
11302
- const cards = [];
11303
- for (let index = 0;index < response.cards; index += 1) {
11304
- cards.push(await relay.getRagCurrentCard(captainId, teamId, `${response.doc_id}#${String(index).padStart(2, "0")}`));
11305
- }
11306
- return cards;
11307
- }
11308
- function _candidate(sandbox, filePath) {
11309
- return { filePath, relativePath: path17.relative(sandbox, filePath).split(path17.sep).join("/") };
11310
- }
11311
- function _fingerprint(filePath, content, stats) {
11312
- const digest = createHash5("sha256").update(content).digest("hex");
11313
- return `${filePath}:${stats.size}:${stats.mtimeMs}:${digest}`;
11314
- }
11315
- async function _fileExists(filePath, deps) {
11316
- try {
11317
- return (await (deps.stat ?? stat4)(filePath)).isFile();
11318
- } catch (error) {
11319
- if (_notFound(error))
11320
- return false;
11321
- throw error;
11322
- }
11323
- }
11324
- async function _dirExists(filePath, deps) {
11325
- try {
11326
- return (await (deps.stat ?? stat4)(filePath)).isDirectory();
11327
- } catch (error) {
11328
- if (_notFound(error))
11329
- return false;
11330
- throw error;
11331
- }
11332
- }
11333
- function _captainId2(value) {
11334
- if (value?.trim())
11335
- return value.trim();
11336
- throw new Error("memory sweep requires descriptor captain_id");
11337
- }
11338
- function _memoryEngine(value) {
11339
- if (value === "claude" || value === "codex")
11340
- return value;
11341
- throw new Error("memory sweep engine must be claude or codex");
11342
- }
11343
- function _notFound(error) {
11344
- return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
11345
- }
11346
- // .dist-releases/1784772051951-3107696/src/host/scratch-sweeper.js
11409
+ // .dist-releases/1784860966076-1188772/src/host/scratch-sweeper.js
11347
11410
  import { execFile as execFile3 } from "node:child_process";
11348
- import { lstat as lstat5, readdir as readdir4, realpath as realpath2, unlink as unlink3 } from "node:fs/promises";
11349
- import path18 from "node:path";
11411
+ import { lstat as lstat5, readdir as readdir3, realpath as realpath2, unlink as unlink3 } from "node:fs/promises";
11412
+ import path17 from "node:path";
11350
11413
  import { promisify as promisify4 } from "node:util";
11351
11414
  var execFileAsync3 = promisify4(execFile3);
11352
11415
  var SCRATCH_SUBDIR = ".agent/tmp";
@@ -11360,8 +11423,8 @@ function scratchSweepEnabled(env) {
11360
11423
  return raw !== "0" && raw !== "false" && raw !== "no" && raw !== "off";
11361
11424
  }
11362
11425
  async function sweepWorkspaceScratch(options) {
11363
- const root = path18.resolve(options.workspaceRoot);
11364
- const tmpDir = path18.join(root, SCRATCH_SUBDIR);
11426
+ const root = path17.resolve(options.workspaceRoot);
11427
+ const tmpDir = path17.join(root, SCRATCH_SUBDIR);
11365
11428
  const state = await _tmpDirState(root, tmpDir);
11366
11429
  if (state === "missing")
11367
11430
  return EMPTY;
@@ -11399,7 +11462,7 @@ async function _trackedScratchFiles(root, options) {
11399
11462
  const tracked = new Set;
11400
11463
  for (const rel of stdout.split("\x00"))
11401
11464
  if (rel)
11402
- tracked.add(path18.join(root, rel));
11465
+ tracked.add(path17.join(root, rel));
11403
11466
  return tracked;
11404
11467
  } catch (error) {
11405
11468
  options.logger?.("host_scratch_sweep_git_failed", { workspace: root, error: _errno(error) });
@@ -11408,8 +11471,8 @@ async function _trackedScratchFiles(root, options) {
11408
11471
  }
11409
11472
  async function _collectScratchFiles(dir, root, logger) {
11410
11473
  const out = [];
11411
- for (const entry of await readdir4(dir, { withFileTypes: true })) {
11412
- const abs = path18.join(dir, entry.name);
11474
+ for (const entry of await readdir3(dir, { withFileTypes: true })) {
11475
+ const abs = path17.join(dir, entry.name);
11413
11476
  if (!_within(root, abs)) {
11414
11477
  logger?.("host_scratch_sweep_out_of_bounds", { workspace: root, path: abs });
11415
11478
  continue;
@@ -11475,8 +11538,8 @@ async function _runGit2(root, args) {
11475
11538
  return String(stdout);
11476
11539
  }
11477
11540
  function _within(root, target) {
11478
- const rel = path18.relative(root, target);
11479
- return rel === "" || !rel.startsWith("..") && !path18.isAbsolute(rel);
11541
+ const rel = path17.relative(root, target);
11542
+ return rel === "" || !rel.startsWith("..") && !path17.isAbsolute(rel);
11480
11543
  }
11481
11544
  function _isEnoent(error) {
11482
11545
  return Boolean(error && typeof error === "object" && "code" in error && error.code === "ENOENT");
@@ -11486,16 +11549,22 @@ function _errno(error) {
11486
11549
  return String(error.code);
11487
11550
  return error instanceof Error ? error.message : String(error);
11488
11551
  }
11489
- // .dist-releases/1784772051951-3107696/src/host/launcher.js
11552
+ // .dist-releases/1784860966076-1188772/src/host/launcher.js
11490
11553
  var MAX_TMUX_SESSION_SUFFIX = 100;
11491
11554
  async function launchSupervisedAgent(input, deps = {}) {
11492
11555
  const spec = parseAgentStartSpec(input.spec);
11493
11556
  const logger = input.logger ?? (() => {});
11557
+ const relay = new RelayClient({
11558
+ relayUrl: input.descriptor.relay_url,
11559
+ bearerToken: input.descriptor.token,
11560
+ ...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
11561
+ });
11494
11562
  const { env, credentialGuard } = await materializeAgentRuntimeEnv({
11495
11563
  agentName: input.descriptor.agent_name,
11496
11564
  engine: spec.engine,
11497
11565
  hostEnv: input.hostEnv ?? process.env,
11498
11566
  descriptor: input.descriptor,
11567
+ teamMemoryEntitled: await (deps.resolveTeamMemoryEntitlementFn ?? resolveTeamMemoryEntitlement)(relay, input.captainId, input.descriptor.session),
11499
11568
  specEnv: spec.env
11500
11569
  });
11501
11570
  const cwd = await (deps.prepareWorkspaceFn ?? prepareWorkspace)({
@@ -11508,11 +11577,6 @@ async function launchSupervisedAgent(input, deps = {}) {
11508
11577
  });
11509
11578
  await _trustPreparedWorkspace(input.descriptor.agent_name, spec.engine, env, cwd);
11510
11579
  const resolved = await _resolveTransport(spec, input.descriptor, cwd, logger, deps.ensureTmuxTransportFn, deps.tmuxSessionExistsFn, input.recoveryTmuxSession);
11511
- const relay = new RelayClient({
11512
- relayUrl: input.descriptor.relay_url,
11513
- bearerToken: input.descriptor.token,
11514
- ...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
11515
- });
11516
11580
  const runtime = (deps.createRuntime ?? ((options) => new AgentSupervisorRuntime(options)))({
11517
11581
  agentName: input.descriptor.agent_name,
11518
11582
  displayName: input.descriptor.agent_name,
@@ -11636,90 +11700,10 @@ async function _tmuxSessionExists(session) {
11636
11700
  throw error;
11637
11701
  }
11638
11702
  }
11639
- // .dist-releases/1784772051951-3107696/src/host/repo-verification.js
11640
- import { mkdtempSync, rmSync } from "node:fs";
11641
- import { tmpdir as tmpdir2 } from "node:os";
11642
- import path19 from "node:path";
11643
- import { spawn as spawn5 } from "node:child_process";
11644
- var COMMAND_TIMEOUT_MS2 = 30000;
11645
- async function verifyTeamRepository(gitRepoUrl, hostId, deps = {}) {
11646
- const command = deps.runCommand ?? runCommand;
11647
- const commandOutput = deps.runCommandOutput ?? runCommandOutput;
11648
- const trimmed = gitRepoUrl.trim();
11649
- if (!isAcceptedTeamGitRemote(trimmed))
11650
- throw new Error("git repository URL is not accepted by SailorBridge hardening rules");
11651
- if (gitRemoteRequiresGitHubAuth(trimmed)) {
11652
- await command(["gh", "auth", "status"], process.cwd(), "gh auth status failed; install and authenticate GitHub CLI on the Moderator host");
11653
- }
11654
- const readProbe = await commandOutput(["git", "ls-remote", "--symref", trimmed, "HEAD"], process.cwd(), "git read probe failed");
11655
- const mainRef = _defaultBranchFromSymrefProbe(readProbe);
11656
- const temp = mkdtempSync(path19.join(tmpdir2(), "sailorbridge-repo-verify-"));
11657
- try {
11658
- await command(["git", "init"], temp, "git init failed for write probe");
11659
- await command(["git", "config", "user.email", "sailorbridge-verify@example.invalid"], temp, "git config failed for write probe");
11660
- await command(["git", "config", "user.name", "SailorBridge Verify"], temp, "git config failed for write probe");
11661
- await command(["git", "commit", "--allow-empty", "-m", "sailorbridge verify"], temp, "git commit failed for write probe");
11662
- await command(["git", "push", "--dry-run", trimmed, "HEAD:refs/heads/sailorbridge-verify"], temp, "git write probe failed");
11663
- } finally {
11664
- rmSync(temp, { recursive: true, force: true });
11665
- }
11666
- return {
11667
- verified: true,
11668
- reason: "git read and dry-run write probes succeeded",
11669
- verified_at: (deps.now ?? (() => new Date))().toISOString(),
11670
- verification_host_id: hostId,
11671
- ...mainRef ? { main_ref: mainRef } : {}
11672
- };
11673
- }
11674
- function _defaultBranchFromSymrefProbe(output) {
11675
- const match = /^ref:\s+refs\/heads\/(\S+)\s+HEAD$/mu.exec(output);
11676
- return match ? match[1] : null;
11677
- }
11678
- async function runCommand(argv, cwd, failurePrefix) {
11679
- await runCommandOutput(argv, cwd, failurePrefix);
11680
- }
11681
- async function runCommandOutput(argv, cwd, failurePrefix) {
11682
- const result = await execArgv2(argv, cwd);
11683
- if (result.exitCode === 0)
11684
- return result.stdout;
11685
- const detail = [result.stderr.trim(), result.stdout.trim()].filter(Boolean).join(`
11686
- `).slice(0, 500);
11687
- throw new Error(detail ? `${failurePrefix}: ${detail}` : failurePrefix);
11688
- }
11689
- function execArgv2(argv, cwd) {
11690
- return new Promise((resolve2) => {
11691
- const proc = spawn5(argv[0], argv.slice(1), { cwd, stdio: ["ignore", "pipe", "pipe"] });
11692
- const stdout = [];
11693
- const stderr = [];
11694
- let settled = false;
11695
- const finish = (exitCode, extraStderr = "") => {
11696
- if (settled)
11697
- return;
11698
- settled = true;
11699
- clearTimeout(timer);
11700
- resolve2({
11701
- exitCode,
11702
- stdout: Buffer.concat(stdout).toString("utf8"),
11703
- stderr: `${Buffer.concat(stderr).toString("utf8")}${extraStderr}`
11704
- });
11705
- };
11706
- const timer = setTimeout(() => {
11707
- proc.kill("SIGTERM");
11708
- finish(124, `
11709
- timeout expired`);
11710
- }, COMMAND_TIMEOUT_MS2);
11711
- proc.stdout.on("data", (chunk) => stdout.push(chunk));
11712
- proc.stderr.on("data", (chunk) => stderr.push(chunk));
11713
- proc.on("error", (error) => finish(127, `
11714
- ${error.message}`));
11715
- proc.on("close", (code) => finish(code ?? 1));
11716
- });
11717
- }
11718
-
11719
- // .dist-releases/1784772051951-3107696/src/worker/capability.js
11720
- import { accessSync, constants as constants2, mkdirSync as mkdirSync3, mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
11703
+ // .dist-releases/1784860966076-1188772/src/worker/capability.js
11704
+ import { accessSync, constants as constants2, mkdirSync as mkdirSync3, mkdtempSync, readFileSync as readFileSync3, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
11721
11705
  import os from "node:os";
11722
- import path20 from "node:path";
11706
+ import path18 from "node:path";
11723
11707
  import { spawnSync } from "node:child_process";
11724
11708
  var TOOLS = ["podman", "git", "kubectl", "perf", "bpftrace"];
11725
11709
  var GPU_TAGS = new Set(["intel-b60", "nvidia-h100", "none"]);
@@ -11819,11 +11803,11 @@ function parseOsReleaseLine(line) {
11819
11803
  }
11820
11804
  function isReadonly(dir) {
11821
11805
  try {
11822
- const root = path20.resolve(dir);
11806
+ const root = path18.resolve(dir);
11823
11807
  mkdirSync3(root, { recursive: true });
11824
- const temp = mkdtempSync2(path20.join(root, ".sailorbridge-write-test-"));
11825
- writeFileSync2(path20.join(temp, "probe"), "");
11826
- rmSync2(temp, { recursive: true, force: true });
11808
+ const temp = mkdtempSync(path18.join(root, ".sailorbridge-write-test-"));
11809
+ writeFileSync2(path18.join(temp, "probe"), "");
11810
+ rmSync(temp, { recursive: true, force: true });
11827
11811
  return false;
11828
11812
  } catch {
11829
11813
  return true;
@@ -11847,10 +11831,10 @@ function commandExists(command) {
11847
11831
  return commandPath(command) !== null;
11848
11832
  }
11849
11833
  function commandPath(command) {
11850
- if (command.includes(path20.sep))
11834
+ if (command.includes(path18.sep))
11851
11835
  return command;
11852
- for (const entry of (process.env.PATH ?? "").split(path20.delimiter)) {
11853
- const candidate = path20.join(entry, command);
11836
+ for (const entry of (process.env.PATH ?? "").split(path18.delimiter)) {
11837
+ const candidate = path18.join(entry, command);
11854
11838
  try {
11855
11839
  accessSync(candidate, constants2.X_OK);
11856
11840
  return candidate;
@@ -11859,7 +11843,7 @@ function commandPath(command) {
11859
11843
  return null;
11860
11844
  }
11861
11845
 
11862
- // .dist-releases/1784772051951-3107696/src/host/worker-start-spec.js
11846
+ // .dist-releases/1784860966076-1188772/src/host/worker-start-spec.js
11863
11847
  var MAX_TAGS = 32;
11864
11848
  var TAG_PATTERN = /^[a-z0-9][a-z0-9=._:/-]{0,63}$/iu;
11865
11849
  var RESERVED_TAG_KEYS = new Set(["os", "kernel", "arch", "gpu", "mode", "role", "tool"]);
@@ -11919,16 +11903,16 @@ function _trimmed2(value, label) {
11919
11903
  return trimmed;
11920
11904
  }
11921
11905
 
11922
- // .dist-releases/1784772051951-3107696/src/host/worker-launcher.js
11906
+ // .dist-releases/1784860966076-1188772/src/host/worker-launcher.js
11923
11907
  import { statSync as statSync3 } from "node:fs";
11924
11908
  import os4 from "node:os";
11925
- import path23 from "node:path";
11926
- // .dist-releases/1784772051951-3107696/src/worker/client.js
11909
+ import path21 from "node:path";
11910
+ // .dist-releases/1784860966076-1188772/src/worker/client.js
11927
11911
  import { readFileSync as readFileSync5 } from "node:fs";
11928
11912
  import os3 from "node:os";
11929
- import path22 from "node:path";
11913
+ import path20 from "node:path";
11930
11914
 
11931
- // .dist-releases/1784772051951-3107696/src/worker/errors.js
11915
+ // .dist-releases/1784860966076-1188772/src/worker/errors.js
11932
11916
  class LeaseFencingRejected extends Error {
11933
11917
  status;
11934
11918
  constructor(status, message) {
@@ -11942,10 +11926,10 @@ class WorkerHttpError extends Error {
11942
11926
  method;
11943
11927
  path;
11944
11928
  status;
11945
- constructor(method, path21, status, message) {
11929
+ constructor(method, path19, status, message) {
11946
11930
  super(message || `HTTP ${status}`);
11947
11931
  this.method = method;
11948
- this.path = path21;
11932
+ this.path = path19;
11949
11933
  this.status = status;
11950
11934
  this.name = "WorkerHttpError";
11951
11935
  }
@@ -11954,10 +11938,10 @@ class WorkerHttpError extends Error {
11954
11938
  class WorkerNetworkError extends Error {
11955
11939
  method;
11956
11940
  path;
11957
- constructor(method, path21, message) {
11941
+ constructor(method, path19, message) {
11958
11942
  super(message);
11959
11943
  this.method = method;
11960
- this.path = path21;
11944
+ this.path = path19;
11961
11945
  this.name = "WorkerNetworkError";
11962
11946
  }
11963
11947
  }
@@ -11965,14 +11949,14 @@ var LEASE_FENCING_BAD_REQUEST_MESSAGES = [
11965
11949
  "lease_token is required",
11966
11950
  "lease_token must be a non-empty string"
11967
11951
  ];
11968
- function raiseForLeaseFencingOrStatus(method, path21, status, body) {
11952
+ function raiseForLeaseFencingOrStatus(method, path19, status, body) {
11969
11953
  if (status === 409)
11970
11954
  throw new LeaseFencingRejected(status, body);
11971
11955
  if (status === 400 && LEASE_FENCING_BAD_REQUEST_MESSAGES.some((message) => body.includes(message))) {
11972
11956
  throw new LeaseFencingRejected(status, body);
11973
11957
  }
11974
11958
  if (status >= 300)
11975
- throw new WorkerHttpError(method, path21, status, body);
11959
+ throw new WorkerHttpError(method, path19, status, body);
11976
11960
  }
11977
11961
  function isNetworkError(error) {
11978
11962
  if (error instanceof WorkerNetworkError)
@@ -11984,23 +11968,23 @@ function isNetworkError(error) {
11984
11968
  const code = error.code;
11985
11969
  return code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EPIPE" || error.name === "AbortError";
11986
11970
  }
11987
- function countsTowardWorkerSessionRebuild(path21) {
11988
- if (path21 === "/api/v1/workers/register")
11971
+ function countsTowardWorkerSessionRebuild(path19) {
11972
+ if (path19 === "/api/v1/workers/register")
11989
11973
  return false;
11990
- if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(path21))
11974
+ if (/^\/api\/v1\/workers\/[^/]+\/heartbeat$/u.test(path19))
11991
11975
  return false;
11992
11976
  return true;
11993
11977
  }
11994
11978
 
11995
- // .dist-releases/1784772051951-3107696/src/worker/executor.js
11996
- import { spawn as spawn6 } from "node:child_process";
11997
- import { mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync3, readFileSync as readFileSync4, realpathSync, rmSync as rmSync3, statfsSync, writeFileSync as writeFileSync3 } from "node:fs";
11998
- import { tmpdir as tmpdir3, loadavg } from "node:os";
11979
+ // .dist-releases/1784860966076-1188772/src/worker/executor.js
11980
+ import { spawn as spawn5 } from "node:child_process";
11981
+ import { mkdirSync as mkdirSync4, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, realpathSync, rmSync as rmSync2, statfsSync, writeFileSync as writeFileSync3 } from "node:fs";
11982
+ import { tmpdir as tmpdir2, loadavg } from "node:os";
11999
11983
  import os2 from "node:os";
12000
- import path21 from "node:path";
11984
+ import path19 from "node:path";
12001
11985
  import { TextDecoder } from "node:util";
12002
11986
 
12003
- // .dist-releases/1784772051951-3107696/src/worker/async-queue.js
11987
+ // .dist-releases/1784860966076-1188772/src/worker/async-queue.js
12004
11988
  class AsyncByteQueue {
12005
11989
  maxSize;
12006
11990
  values = [];
@@ -12060,7 +12044,7 @@ class AsyncByteQueue {
12060
12044
  }
12061
12045
  }
12062
12046
 
12063
- // .dist-releases/1784772051951-3107696/src/worker/http.js
12047
+ // .dist-releases/1784860966076-1188772/src/worker/http.js
12064
12048
  import { request as httpRequest } from "node:http";
12065
12049
  import { request as httpsRequest } from "node:https";
12066
12050
  import { URLSearchParams as URLSearchParams2 } from "node:url";
@@ -12090,27 +12074,27 @@ class WorkerHttpClient {
12090
12074
  throw new Error("token is required");
12091
12075
  this.relayUrl = relayUrl.replace(/\/+$/u, "");
12092
12076
  }
12093
- async getJson(path21, query, timeouts, options = {}) {
12077
+ async getJson(path19, query, timeouts, options = {}) {
12094
12078
  const suffix = new URLSearchParams2(query).toString();
12095
- return this.requestJson("GET", suffix ? `${path21}?${suffix}` : path21, undefined, timeouts, options);
12079
+ return this.requestJson("GET", suffix ? `${path19}?${suffix}` : path19, undefined, timeouts, options);
12096
12080
  }
12097
- async postJson(path21, value, timeouts) {
12098
- return this.requestJson("POST", path21, { type: "json", value }, timeouts, {});
12081
+ async postJson(path19, value, timeouts) {
12082
+ return this.requestJson("POST", path19, { type: "json", value }, timeouts, {});
12099
12083
  }
12100
- async postBytes(path21, body, headers, timeouts) {
12101
- const response = await this.request("POST", path21, { type: "bytes", value: body }, timeouts, headers);
12102
- raiseForLeaseFencingOrStatus("POST", path21, response.status, response.text);
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);
12103
12087
  }
12104
- async requestJson(method, path21, body, timeouts, options) {
12105
- const response = await this.request(method, path21, body, timeouts, {});
12106
- const errorPath = pathOnly(path21);
12088
+ async requestJson(method, path19, body, timeouts, options) {
12089
+ const response = await this.request(method, path19, body, timeouts, {});
12090
+ const errorPath = pathOnly(path19);
12107
12091
  if (options.leaseFencing === false && response.status >= 300)
12108
12092
  throw new WorkerHttpError(method, errorPath, response.status, response.text);
12109
12093
  raiseForLeaseFencingOrStatus(method, errorPath, response.status, response.text);
12110
12094
  return parseJson(method, errorPath, response.text);
12111
12095
  }
12112
- async request(method, path21, body, timeouts, extraHeaders) {
12113
- const url = new URL(`${this.relayUrl}${path21}`);
12096
+ async request(method, path19, body, timeouts, extraHeaders) {
12097
+ const url = new URL(`${this.relayUrl}${path19}`);
12114
12098
  const headers = this.headers(body, extraHeaders);
12115
12099
  return sendRequest(method, url, body, headers, timeouts, this.signal);
12116
12100
  }
@@ -12237,7 +12221,7 @@ function waitForRequestDrain(req) {
12237
12221
  req.once("close", onClose);
12238
12222
  });
12239
12223
  }
12240
- function parseJson(method, path21, text) {
12224
+ function parseJson(method, path19, text) {
12241
12225
  try {
12242
12226
  const parsed = JSON.parse(text);
12243
12227
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -12246,15 +12230,15 @@ function parseJson(method, path21, text) {
12246
12230
  return parsed;
12247
12231
  } catch (error) {
12248
12232
  if (error instanceof SyntaxError)
12249
- throw new WorkerNetworkError(method, path21, "response JSON is invalid");
12233
+ throw new WorkerNetworkError(method, path19, "response JSON is invalid");
12250
12234
  throw error;
12251
12235
  }
12252
12236
  }
12253
- function pathOnly(path21) {
12254
- return path21.split("?", 1)[0] || path21;
12237
+ function pathOnly(path19) {
12238
+ return path19.split("?", 1)[0] || path19;
12255
12239
  }
12256
12240
 
12257
- // .dist-releases/1784772051951-3107696/src/worker/stream-upload.js
12241
+ // .dist-releases/1784860966076-1188772/src/worker/stream-upload.js
12258
12242
  async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId) {
12259
12243
  if (!leaseToken.trim())
12260
12244
  throw new Error("lease_token is required");
@@ -12264,7 +12248,7 @@ async function uploadStream(session, jobId, stream, leaseToken, chunks, workerId
12264
12248
  await session.http.postBytes(`/api/v1/jobs/${encodeURIComponent(jobId)}/stream-upload?${query.toString()}`, chunks, { "X-Lease-Token": leaseToken }, UPLOAD_TIMEOUTS);
12265
12249
  }
12266
12250
 
12267
- // .dist-releases/1784772051951-3107696/src/worker/executor.js
12251
+ // .dist-releases/1784860966076-1188772/src/worker/executor.js
12268
12252
  class StreamingProcessFailure extends Error {
12269
12253
  exitCode;
12270
12254
  stdout;
@@ -12336,8 +12320,8 @@ function sandboxPath(workdir, requested) {
12336
12320
  if (requested.startsWith("/") || parts.length === 0 || parts.includes("..")) {
12337
12321
  throw new Error("path must be a relative path inside the worker sandbox");
12338
12322
  }
12339
- const root = realpathSync(path21.resolve(workdir));
12340
- const target = path21.resolve(root, ...parts);
12323
+ const root = realpathSync(path19.resolve(workdir));
12324
+ const target = path19.resolve(root, ...parts);
12341
12325
  if (!isPathInside(root, target)) {
12342
12326
  throw new Error("path escapes worker sandbox");
12343
12327
  }
@@ -12477,7 +12461,7 @@ async function collectAndUpload(session, jobId, stream, pipe, leaseToken, worker
12477
12461
  return collected.join("");
12478
12462
  }
12479
12463
  function spawnProcess(argv, cwd) {
12480
- return spawn6(argv[0], argv.slice(1), { cwd, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
12464
+ return spawn5(argv[0], argv.slice(1), { cwd, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "pipe"] });
12481
12465
  }
12482
12466
  async function terminateProcess(proc) {
12483
12467
  if (proc.pid === undefined)
@@ -12513,9 +12497,9 @@ function writeFileJob(workdir, spec, startedAt, started) {
12513
12497
  if (typeof spec.content !== "string")
12514
12498
  throw new Error("write_file spec.content must be a string");
12515
12499
  const target = sandboxPath(workdir, String(spec.path ?? ""));
12516
- mkdirSync4(path21.dirname(target), { recursive: true });
12500
+ mkdirSync4(path19.dirname(target), { recursive: true });
12517
12501
  writeFileSync3(target, spec.content, "utf8");
12518
- return output(0, path21.relative(realpathSync(path21.resolve(workdir)), target), "", startedAt, started);
12502
+ return output(0, path19.relative(realpathSync(path19.resolve(workdir)), target), "", startedAt, started);
12519
12503
  }
12520
12504
  function readUtf8File(target) {
12521
12505
  return decodeUtf8(readFileSync4(target));
@@ -12523,13 +12507,13 @@ function readUtf8File(target) {
12523
12507
  async function applyDiff(workdir, spec, signal) {
12524
12508
  if (typeof spec.diff_unified_text !== "string")
12525
12509
  throw new Error("apply_diff spec.diff_unified_text must be a string");
12526
- const dir = mkdtempSync3(path21.join(tmpdir3(), "sailorbridge-patch-"));
12527
- const patch = path21.join(dir, "change.patch");
12510
+ const dir = mkdtempSync2(path19.join(tmpdir2(), "sailorbridge-patch-"));
12511
+ const patch = path19.join(dir, "change.patch");
12528
12512
  writeFileSync3(patch, spec.diff_unified_text, "utf8");
12529
12513
  try {
12530
12514
  return await runArgv(["patch", "-p1", "-i", patch], workdir, coerceJobTimeoutSeconds(spec) * 1000, signal);
12531
12515
  } finally {
12532
- rmSync3(dir, { recursive: true, force: true });
12516
+ rmSync2(dir, { recursive: true, force: true });
12533
12517
  }
12534
12518
  }
12535
12519
  async function executeCompose(spec, workdir, startedAt, started, signal) {
@@ -12764,24 +12748,24 @@ function resolveExistingTarget(target) {
12764
12748
  let current = target;
12765
12749
  while (true) {
12766
12750
  try {
12767
- return path21.resolve(realpathSync(current), ...missingParts.reverse());
12751
+ return path19.resolve(realpathSync(current), ...missingParts.reverse());
12768
12752
  } catch (error) {
12769
12753
  if (error.code !== "ENOENT")
12770
12754
  throw error;
12771
- const parent = path21.dirname(current);
12755
+ const parent = path19.dirname(current);
12772
12756
  if (parent === current)
12773
12757
  throw error;
12774
- missingParts.push(path21.basename(current));
12758
+ missingParts.push(path19.basename(current));
12775
12759
  current = parent;
12776
12760
  }
12777
12761
  }
12778
12762
  }
12779
12763
  function isPathInside(root, target) {
12780
- const relative = path21.relative(root, target);
12781
- return relative === "" || !relative.startsWith("..") && !path21.isAbsolute(relative);
12764
+ const relative = path19.relative(root, target);
12765
+ return relative === "" || !relative.startsWith("..") && !path19.isAbsolute(relative);
12782
12766
  }
12783
12767
  function prepareWorkdir(workdir) {
12784
- const resolved = path21.resolve(workdir);
12768
+ const resolved = path19.resolve(workdir);
12785
12769
  mkdirSync4(resolved, { recursive: true });
12786
12770
  return resolved;
12787
12771
  }
@@ -12796,7 +12780,7 @@ function errorMessage6(error) {
12796
12780
  return error instanceof Error ? error.message : String(error);
12797
12781
  }
12798
12782
 
12799
- // .dist-releases/1784772051951-3107696/src/worker/client.js
12783
+ // .dist-releases/1784860966076-1188772/src/worker/client.js
12800
12784
  var TOKEN_FILES = [
12801
12785
  "~/.config/sailorbridge/token",
12802
12786
  "/etc/sailorbridge/token"
@@ -13135,7 +13119,7 @@ ${JSON.stringify(result)}\r
13135
13119
  `);
13136
13120
  for (const file of attachments) {
13137
13121
  yield Buffer.from(`--${multipartBoundary}\r
13138
- Content-Disposition: form-data; name="files"; filename="${path22.basename(file)}"\r
13122
+ Content-Disposition: form-data; name="files"; filename="${path20.basename(file)}"\r
13139
13123
  \r
13140
13124
  `);
13141
13125
  yield readFileSync5(file);
@@ -13169,7 +13153,7 @@ function parseFlags2(argv) {
13169
13153
  return values;
13170
13154
  }
13171
13155
  function expandHome2(value) {
13172
- return value === "~" || value.startsWith("~/") ? path22.join(os3.homedir(), value.slice(2)) : value;
13156
+ return value === "~" || value.startsWith("~/") ? path20.join(os3.homedir(), value.slice(2)) : value;
13173
13157
  }
13174
13158
  function _sleep(ms, signal) {
13175
13159
  if (signal?.aborted)
@@ -13193,7 +13177,7 @@ function errorName2(error) {
13193
13177
  function errorMessage7(error) {
13194
13178
  return error instanceof Error ? error.message : String(error);
13195
13179
  }
13196
- // .dist-releases/1784772051951-3107696/src/host/worker-launcher.js
13180
+ // .dist-releases/1784860966076-1188772/src/host/worker-launcher.js
13197
13181
  var DEFAULT_WORKER_READY_TIMEOUT_MS = 60000;
13198
13182
  async function launchSupervisedWorker(input, deps = {}) {
13199
13183
  const logger = input.logger ?? (() => {});
@@ -13276,21 +13260,21 @@ function _requireWorkerName(descriptor) {
13276
13260
  return worker;
13277
13261
  }
13278
13262
  function _resolveWorkdir(workdir, homeDir) {
13279
- return workdir === "~" || workdir.startsWith("~/") ? path23.join(homeDir, workdir.slice(2)) : workdir;
13263
+ return workdir === "~" || workdir.startsWith("~/") ? path21.join(homeDir, workdir.slice(2)) : workdir;
13280
13264
  }
13281
13265
  function _assertWorkdir(workdir) {
13282
- let stat5;
13266
+ let stat4;
13283
13267
  try {
13284
- stat5 = statSync3(workdir);
13268
+ stat4 = statSync3(workdir);
13285
13269
  } catch {
13286
13270
  throw new Error(`worker workdir does not exist: ${workdir}`);
13287
13271
  }
13288
- if (!stat5.isDirectory())
13272
+ if (!stat4.isDirectory())
13289
13273
  throw new Error(`worker workdir is not a directory: ${workdir}`);
13290
13274
  return workdir;
13291
13275
  }
13292
13276
 
13293
- // .dist-releases/1784772051951-3107696/src/host/daemon.js
13277
+ // .dist-releases/1784860966076-1188772/src/host/daemon.js
13294
13278
  async function runHostDaemon(options) {
13295
13279
  const logger = options.logger ?? (() => {});
13296
13280
  const sleep3 = options.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
@@ -13299,7 +13283,6 @@ async function runHostDaemon(options) {
13299
13283
  const workerRuntimes = new Map;
13300
13284
  const cleanupLocks = new Map;
13301
13285
  const startLocks = new Map;
13302
- const memorySweepTargets = new Map;
13303
13286
  const managedTargets = new Map;
13304
13287
  const launch = options.launch ?? launchSupervisedAgent;
13305
13288
  const launchWorker = options.launchWorker ?? launchSupervisedWorker;
@@ -13307,15 +13290,14 @@ async function runHostDaemon(options) {
13307
13290
  const reclaim = options.reclaimOrphanWorkspaces ?? reclaimOrphanWorkspaces;
13308
13291
  const identity = { captainId: options.captainId, hostId: options.hostId, displayName: options.displayName };
13309
13292
  const redactSecrets = [...options.redactSecrets ?? []];
13310
- const sweep = options.sweepAgentMemory ?? sweepAgentMemory;
13311
13293
  const materializeManaged = options.materializeManagedFiles ?? materializeManagedFiles;
13312
13294
  const now = options.now ?? (() => Date.now());
13313
- const memorySweepIntervalMs = options.memorySweepIntervalMs ?? 6 * 60 * 60 * 1000;
13314
- let lastRuntimeMemorySweepAt = now();
13295
+ const managedSyncIntervalMs = options.managedSyncIntervalMs ?? 6 * 60 * 60 * 1000;
13296
+ let lastManagedSyncAt = now();
13315
13297
  const scratchSweep = options.sweepWorkspaceScratch ?? sweepWorkspaceScratch;
13316
13298
  const scratchSweepIntervalMs = options.scratchSweepIntervalMs ?? 6 * 60 * 60 * 1000;
13317
13299
  let lastScratchSweepAt = now();
13318
- const reportRepoTips = options.reportRepoTips ?? reportRepoTipsOnce;
13300
+ const reportRepoTips = options.reportRepoTips ?? createRepoTipReporter();
13319
13301
  const repoTipReportIntervalMs = options.repoTipReportIntervalMs ?? 60 * 1000;
13320
13302
  let lastRepoTipReportAt = now() - repoTipReportIntervalMs;
13321
13303
  const reconcileIntervalMs = options.reconcileIntervalMs ?? 5 * 60 * 1000;
@@ -13360,9 +13342,7 @@ async function runHostDaemon(options) {
13360
13342
  await waitForAgentCleanup(cleanupLocks, request.agent_name);
13361
13343
  const spec = parseAgentStartSpec(request.spec);
13362
13344
  const agentRelay = agentRelayClient(descriptor, options);
13363
- const memoryTarget = memorySweepTarget(descriptor, spec.engine, options, agentRelay);
13364
13345
  const managedTarget = managedMaterializationTarget(descriptor, spec.engine, options, agentRelay);
13365
- await runMemorySweep(sweep, memoryTarget, "pre-start", logger, true);
13366
13346
  await runManagedMaterialization(materializeManaged, managedTarget, "pre-start", logger, true);
13367
13347
  await waitForAgentCleanup(cleanupLocks, request.agent_name);
13368
13348
  let runtime = null;
@@ -13379,10 +13359,8 @@ async function runHostDaemon(options) {
13379
13359
  return;
13380
13360
  }
13381
13361
  const cleanupTask = Promise.resolve().then(async () => {
13382
- await runMemorySweep(sweep, memoryTarget, "runtime", logger, false);
13383
13362
  await runManagedMaterialization(materializeManaged, managedTarget, "runtime", logger, false);
13384
13363
  runtimes.delete(request.agent_name);
13385
- memorySweepTargets.delete(request.agent_name);
13386
13364
  managedTargets.delete(request.agent_name);
13387
13365
  try {
13388
13366
  await cleanup({
@@ -13426,7 +13404,6 @@ async function runHostDaemon(options) {
13426
13404
  }, options.launchDeps);
13427
13405
  previous = runtimes.get(request.agent_name);
13428
13406
  runtimes.set(request.agent_name, runtime);
13429
- memorySweepTargets.set(request.agent_name, memoryTarget);
13430
13407
  managedTargets.set(request.agent_name, managedTarget);
13431
13408
  } finally {
13432
13409
  finishStart();
@@ -13438,14 +13415,6 @@ async function runHostDaemon(options) {
13438
13415
  await previous.stop().catch(() => {});
13439
13416
  };
13440
13417
  const starter = async (request) => {
13441
- if (request.request_kind === "repo_verification") {
13442
- const gitRepoUrl = request.spec.git_repo_url;
13443
- if (typeof gitRepoUrl !== "string" || !gitRepoUrl.trim()) {
13444
- throw new Error("repo verification request requires spec.git_repo_url");
13445
- }
13446
- const verifyRepository = options.verifyRepository ?? verifyTeamRepository;
13447
- return { ...await verifyRepository(gitRepoUrl, options.hostId) };
13448
- }
13449
13418
  if (request.request_kind === "worker_start")
13450
13419
  return _startWorker(request);
13451
13420
  await startAgent(request, await options.resolveCredential(request));
@@ -13462,7 +13431,9 @@ async function runHostDaemon(options) {
13462
13431
  const _recoverDurableRuntimes = async () => {
13463
13432
  if (!options.recoverRuntimes)
13464
13433
  return;
13465
- for (const recovery of await options.recoverRuntimes()) {
13434
+ const recoveries = await options.recoverRuntimes();
13435
+ const workers = recoveries.filter(({ request }) => request.request_kind === "worker_start").length;
13436
+ for (const recovery of recoveries) {
13466
13437
  if (options.signal?.aborted)
13467
13438
  return;
13468
13439
  try {
@@ -13475,6 +13446,7 @@ async function runHostDaemon(options) {
13475
13446
  });
13476
13447
  }
13477
13448
  }
13449
+ logger("host_runtime_recovery_completed", { agents: recoveries.length - workers, workers });
13478
13450
  };
13479
13451
  logger("host_daemon_start", { host_id: options.hostId, captain_id: options.captainId });
13480
13452
  try {
@@ -13500,9 +13472,8 @@ async function runHostDaemon(options) {
13500
13472
  logger("host_workspace_reconcile_failed", { error: errorMessage8(error) });
13501
13473
  }
13502
13474
  }
13503
- if (now() - lastRuntimeMemorySweepAt >= memorySweepIntervalMs) {
13504
- lastRuntimeMemorySweepAt = now();
13505
- await sweepRuntimeMemory(sweep, memorySweepTargets, logger);
13475
+ if (now() - lastManagedSyncAt >= managedSyncIntervalMs) {
13476
+ lastManagedSyncAt = now();
13506
13477
  await syncRuntimeManagedMaterialization(materializeManaged, managedTargets, logger);
13507
13478
  }
13508
13479
  if (now() - lastScratchSweepAt >= scratchSweepIntervalMs) {
@@ -13530,7 +13501,6 @@ async function runHostDaemon(options) {
13530
13501
  } finally {
13531
13502
  logger("host_daemon_stopping", { agents: runtimes.size, workers: workerRuntimes.size });
13532
13503
  try {
13533
- await sweepRuntimeMemory(sweep, memorySweepTargets, logger);
13534
13504
  await syncRuntimeManagedMaterialization(materializeManaged, managedTargets, logger);
13535
13505
  } finally {
13536
13506
  await Promise.allSettled([
@@ -13540,17 +13510,6 @@ async function runHostDaemon(options) {
13540
13510
  }
13541
13511
  }
13542
13512
  }
13543
- function memorySweepTarget(descriptor, engine, options, relay) {
13544
- return {
13545
- agentName: descriptor.agent_name,
13546
- engine,
13547
- captainId: descriptor.captain_id,
13548
- teamId: descriptor.session,
13549
- env: options.hostEnv ?? process.env,
13550
- relay,
13551
- state: createMemorySweepState()
13552
- };
13553
- }
13554
13513
  function managedMaterializationTarget(descriptor, engine, options, relay) {
13555
13514
  return {
13556
13515
  agentName: descriptor.agent_name,
@@ -13569,11 +13528,6 @@ function agentRelayClient(descriptor, options) {
13569
13528
  ...options.launchDeps?.fetchImpl ? { fetchImpl: options.launchDeps.fetchImpl } : {}
13570
13529
  });
13571
13530
  }
13572
- async function sweepRuntimeMemory(sweep, targets, logger) {
13573
- for (const target of targets.values()) {
13574
- await runMemorySweep(sweep, target, "runtime", logger, false);
13575
- }
13576
- }
13577
13531
  async function sweepWorkspaceScratchRoots(scratchSweep, env, logger) {
13578
13532
  if (!scratchSweepEnabled(env))
13579
13533
  return;
@@ -13590,15 +13544,6 @@ async function syncRuntimeManagedMaterialization(materialize, targets, logger) {
13590
13544
  await runManagedMaterialization(materialize, target, "runtime", logger, false);
13591
13545
  }
13592
13546
  }
13593
- async function runMemorySweep(sweep, target, mode, logger, failFast) {
13594
- try {
13595
- await sweep({ ...target, mode, logger });
13596
- } catch (error) {
13597
- logger("host_memory_sweep_failed", { agent_name: target.agentName, mode, error: errorMessage8(error) });
13598
- if (failFast)
13599
- throw error;
13600
- }
13601
- }
13602
13547
  async function runManagedMaterialization(materialize, target, mode, logger, failFast) {
13603
13548
  try {
13604
13549
  await materialize({ ...target, logger });
@@ -13678,7 +13623,7 @@ async function _drainOnce(options, identity, starter, logger, redactSecrets) {
13678
13623
  return false;
13679
13624
  }
13680
13625
  }
13681
- // .dist-releases/1784772051951-3107696/src/host/enrollment-redeem.js
13626
+ // .dist-releases/1784860966076-1188772/src/host/enrollment-redeem.js
13682
13627
  var DEFAULT_NOT_FOUND_RETRIES = 3;
13683
13628
  var NOT_FOUND_RETRY_DELAY_MS = 500;
13684
13629
  function createEnrollmentRedeemer(options) {
@@ -13815,7 +13760,7 @@ function _role2(enrollment) {
13815
13760
  }
13816
13761
  return _string4(enrollment.roles[0], "roles[0]").trim();
13817
13762
  }
13818
- // .dist-releases/1784772051951-3107696/src/host/tutorial.js
13763
+ // .dist-releases/1784860966076-1188772/src/host/tutorial.js
13819
13764
  function buildTutorialSteps(state) {
13820
13765
  const paired = state.hasCredential;
13821
13766
  return [
@@ -13886,7 +13831,7 @@ async function runTutorial(options) {
13886
13831
  }
13887
13832
  return steps;
13888
13833
  }
13889
- // .dist-releases/1784772051951-3107696/src/events.js
13834
+ // .dist-releases/1784860966076-1188772/src/events.js
13890
13835
  var EVENT_COMMANDS = new Set(["add", "list", "enable", "disable", "rm"]);
13891
13836
  var EVENT_FLAGS = new Set(["at", "days", "message", "run-date", "session", "tag", "tz"]);
13892
13837
  var PRODUCT_DAYS_MODES = new Set(["once", "daily", "weekday"]);
@@ -13976,7 +13921,7 @@ function _assertListArgs(args) {
13976
13921
  }
13977
13922
  }
13978
13923
 
13979
- // .dist-releases/1784772051951-3107696/src/text-input.js
13924
+ // .dist-releases/1784860966076-1188772/src/text-input.js
13980
13925
  import { readFile as readFile8 } from "node:fs/promises";
13981
13926
  var _readUtf8File = async (file) => await readFile8(file, "utf8");
13982
13927
  async function readTextInput(input) {
@@ -14010,17 +13955,17 @@ async function rejectUnusedTextInput(input, message) {
14010
13955
  input.unref?.();
14011
13956
  }
14012
13957
  }
14013
- async function readTextFile(path24, readUtf8File2 = _readUtf8File) {
14014
- return await readUtf8File2(path24);
13958
+ async function readTextFile(path22, readUtf8File2 = _readUtf8File) {
13959
+ return await readUtf8File2(path22);
14015
13960
  }
14016
- async function readTextFileOrStdin(path24, input, unusedInputMessage, readUtf8File2 = _readUtf8File) {
14017
- if (path24 === "-")
13961
+ async function readTextFileOrStdin(path22, input, unusedInputMessage, readUtf8File2 = _readUtf8File) {
13962
+ if (path22 === "-")
14018
13963
  return await readTextInput(input);
14019
13964
  await rejectUnusedTextInput(input, unusedInputMessage);
14020
- return await readTextFile(path24, readUtf8File2);
13965
+ return await readTextFile(path22, readUtf8File2);
14021
13966
  }
14022
13967
 
14023
- // .dist-releases/1784772051951-3107696/src/ask.js
13968
+ // .dist-releases/1784860966076-1188772/src/ask.js
14024
13969
  var ASK_FLAGS = new Set(["body-file", "option", "reason", "recommend", "session", "title"]);
14025
13970
  async function runAskFromCli(client2, argv, config, input) {
14026
13971
  const args = _parseAskArgs(argv);
@@ -14135,7 +14080,7 @@ function _singleAskValue(values, name) {
14135
14080
  return entries[0];
14136
14081
  }
14137
14082
 
14138
- // .dist-releases/1784772051951-3107696/src/task.js
14083
+ // .dist-releases/1784860966076-1188772/src/task.js
14139
14084
  import { readFile as readFile9 } from "node:fs/promises";
14140
14085
  import { basename } from "node:path";
14141
14086
  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"]);
@@ -14625,13 +14570,9 @@ function _taskListFooter(shown, returned, total, hasMore, cursor) {
14625
14570
  return `Shown ${shown} of ${total}${local}${next}`;
14626
14571
  }
14627
14572
 
14628
- // .dist-releases/1784772051951-3107696/src/team.js
14629
- import { setTimeout as sleep3 } from "node:timers/promises";
14573
+ // .dist-releases/1784860966076-1188772/src/team.js
14630
14574
  var TEAM_BOOTSTRAP_ENGINES = new Set(["claude", "codex"]);
14631
14575
  var SLOT_SPEC_RE = /^([^=\s]+)=([^:@\s]+):([^:@\s]+)$/;
14632
- var REPO_WAIT_DEFAULT_SECONDS = 60;
14633
- var REPO_WAIT_MAX_SECONDS = 300;
14634
- var REPO_POLL_INTERVAL_MS = 250;
14635
14576
  var TEAM_PLAIN_LIMIT = 20;
14636
14577
 
14637
14578
  class TeamBootstrapError extends Error {
@@ -14642,20 +14583,9 @@ class TeamBootstrapError extends Error {
14642
14583
  this.kind = kind;
14643
14584
  }
14644
14585
  }
14645
-
14646
- class TeamRepoError extends Error {
14647
- kind;
14648
- constructor(kind, message) {
14649
- super(message);
14650
- this.name = "TeamRepoError";
14651
- this.kind = kind;
14652
- }
14653
- }
14654
14586
  async function runTeamFromCli(client2, argv, config, format) {
14655
- if (argv[0] === "repo")
14656
- return await _runTeamRepoFromCli(client2, argv.slice(1), config, format);
14657
14587
  if (argv[0] !== "bootstrap")
14658
- throw new TeamBootstrapError("usage", "team command must be bootstrap or repo");
14588
+ throw new TeamBootstrapError("usage", "team command must be bootstrap");
14659
14589
  const captainId = config.captainId;
14660
14590
  if (!captainId)
14661
14591
  throw new TeamBootstrapError("usage", "team bootstrap requires SAILORBRIDGE_CAPTAIN_ID");
@@ -14668,173 +14598,6 @@ async function runTeamFromCli(client2, argv, config, format) {
14668
14598
  const finalSlots = (await client2.getTeamAgentSlots(captainId, args.session)).agent_slots;
14669
14599
  return format === "json" ? { agent_slots: finalSlots } : _auditFinalState(finalSlots);
14670
14600
  }
14671
- async function _runTeamRepoFromCli(client2, argv, config, format) {
14672
- const captainId = config.captainId;
14673
- if (!captainId)
14674
- throw new TeamRepoError("usage", "team repo requires SAILORBRIDGE_CAPTAIN_ID");
14675
- if (argv[0] === "status") {
14676
- const repository = await client2.getTeamRepository(captainId, _parseRepoStatusArgs(argv.slice(1), config));
14677
- return format === "json" ? repository : _formatRepository(repository);
14678
- }
14679
- if (argv[0] !== "set")
14680
- throw new TeamRepoError("usage", "team repo command must be set or status");
14681
- return await _setTeamRepository(client2, captainId, _parseRepoSetArgs(argv.slice(1), config), format);
14682
- }
14683
- function _parseRepoStatusArgs(tokens, config) {
14684
- if (!tokens.length)
14685
- return _requiredRepoSession(undefined, config);
14686
- if (tokens.length !== 2 || tokens[0] !== "--session")
14687
- throw new TeamRepoError("usage", "team repo status accepts only --session TEAM");
14688
- return _requiredRepoSession(tokens[1], config);
14689
- }
14690
- function _parseRepoSetArgs(tokens, config) {
14691
- const gitRepoUrl = tokens[0]?.trim();
14692
- if (!gitRepoUrl || gitRepoUrl.startsWith("--"))
14693
- throw new TeamRepoError("usage", "team repo set requires GIT_URL");
14694
- if (!isAcceptedTeamGitRemote(gitRepoUrl))
14695
- throw new TeamRepoError("usage", "GIT_URL must be an accepted git remote without embedded credentials");
14696
- const flags = _parseRepoSetFlags(tokens.slice(1));
14697
- return {
14698
- gitRepoUrl,
14699
- session: _requiredRepoSession(flags.session, config),
14700
- wait: flags.wait,
14701
- timeoutSeconds: flags.timeoutSeconds
14702
- };
14703
- }
14704
- function _parseRepoSetFlags(tokens) {
14705
- const flags = { wait: false, timeoutSet: false, timeoutSeconds: REPO_WAIT_DEFAULT_SECONDS };
14706
- for (let index = 0;index < tokens.length; index += 1) {
14707
- index += _applyRepoSetFlag(tokens, index, flags);
14708
- }
14709
- if (flags.timeoutSet && !flags.wait)
14710
- throw new TeamRepoError("usage", "--timeout-sec requires --wait");
14711
- return flags;
14712
- }
14713
- function _applyRepoSetFlag(tokens, index, flags) {
14714
- const token = tokens[index];
14715
- if (token === "--wait") {
14716
- if (flags.wait)
14717
- throw new TeamRepoError("usage", "--wait may only be provided once");
14718
- flags.wait = true;
14719
- return 0;
14720
- }
14721
- if (token === "--session") {
14722
- flags.session = _singleRepoFlagValue(tokens, index + 1, token, flags.session);
14723
- return 1;
14724
- }
14725
- if (token !== "--timeout-sec")
14726
- throw new TeamRepoError("usage", `unknown team repo set flag: ${token}`);
14727
- if (flags.timeoutSet)
14728
- throw new TeamRepoError("usage", "--timeout-sec may only be provided once");
14729
- flags.timeoutSet = true;
14730
- flags.timeoutSeconds = _repoTimeoutSeconds(_singleRepoFlagValue(tokens, index + 1, token));
14731
- return 1;
14732
- }
14733
- function _singleRepoFlagValue(tokens, index, flag, previous) {
14734
- if (previous !== undefined)
14735
- throw new TeamRepoError("usage", `${flag} may only be provided once`);
14736
- const value = tokens[index]?.trim();
14737
- if (!value || value.startsWith("--"))
14738
- throw new TeamRepoError("usage", `${flag} requires a value`);
14739
- return value;
14740
- }
14741
- function _repoTimeoutSeconds(value) {
14742
- const seconds = Number(value);
14743
- if (!Number.isInteger(seconds) || seconds < 1 || seconds > REPO_WAIT_MAX_SECONDS) {
14744
- throw new TeamRepoError("usage", `--timeout-sec must be an integer from 1 to ${REPO_WAIT_MAX_SECONDS}`);
14745
- }
14746
- return seconds;
14747
- }
14748
- function _requiredRepoSession(value, config) {
14749
- const session = (value ?? config.session)?.trim();
14750
- if (!session)
14751
- throw new TeamRepoError("usage", "team repo requires --session or SAILORBRIDGE_SESSION");
14752
- return session;
14753
- }
14754
- async function _setTeamRepository(client2, captainId, args, format) {
14755
- const hostId = _moderatorHostId((await client2.getTeamAgentSlots(captainId, args.session)).agent_slots);
14756
- const pending = await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "pending", null));
14757
- const request = await _queueRepoVerification(client2, captainId, args, hostId);
14758
- if (!args.wait) {
14759
- if (format === "json")
14760
- return { repository: pending, verification_request: request };
14761
- return `${_formatRepository(pending)}
14762
- Verification request: ${request.id}`;
14763
- }
14764
- const terminal = await _waitForRepoVerification(client2, captainId, args, request.id);
14765
- const repository = await _persistRepoTerminal(client2, captainId, args, terminal);
14766
- return format === "json" ? { repository, verification_request: terminal } : _formatRepository(repository);
14767
- }
14768
- function _moderatorHostId(slots) {
14769
- const moderator = slots.find((slot) => slot.slot_id === "moderator" && slot.status === "materialized");
14770
- if (!moderator?.host_id) {
14771
- throw new TeamRepoError("moderator-unavailable", "team repo verification requires a materialized Moderator slot with a host");
14772
- }
14773
- return moderator.host_id;
14774
- }
14775
- async function _queueRepoVerification(client2, captainId, args, hostId) {
14776
- try {
14777
- return await client2.createRepoVerificationRequest(captainId, args.session, hostId, args.gitRepoUrl);
14778
- } catch (error) {
14779
- await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "failed", "Repository verification could not be queued on the Moderator host"));
14780
- throw error;
14781
- }
14782
- }
14783
- async function _waitForRepoVerification(client2, captainId, args, requestId) {
14784
- const deadline = Date.now() + args.timeoutSeconds * 1000;
14785
- while (Date.now() < deadline) {
14786
- let request;
14787
- try {
14788
- request = await client2.getAgentStartRequest(captainId, args.session, requestId, Math.max(1, deadline - Date.now()));
14789
- } catch (error) {
14790
- if (Date.now() < deadline)
14791
- throw error;
14792
- break;
14793
- }
14794
- if (request.status !== "queued" && request.status !== "claimed")
14795
- return request;
14796
- await sleep3(Math.min(REPO_POLL_INTERVAL_MS, Math.max(1, deadline - Date.now())));
14797
- }
14798
- throw new TeamRepoError("verification-timeout", `repository verification timed out after ${args.timeoutSeconds} second${args.timeoutSeconds === 1 ? "" : "s"}`);
14799
- }
14800
- async function _persistRepoTerminal(client2, captainId, args, request) {
14801
- if (request.status === "started") {
14802
- try {
14803
- const reason2 = _verifiedRepoReason(request);
14804
- return await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "verified", reason2));
14805
- } catch (error) {
14806
- const reason2 = error instanceof Error ? error.message : "Moderator host returned an invalid repository verification result";
14807
- await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "failed", reason2));
14808
- throw error;
14809
- }
14810
- }
14811
- const reason = request.error?.trim() || `Repository verification request ended as ${request.status}`;
14812
- await client2.setTeamRepositoryState(captainId, args.session, _repoState(args.gitRepoUrl, "failed", reason));
14813
- throw new TeamRepoError("verification-failed", reason);
14814
- }
14815
- function _verifiedRepoReason(request) {
14816
- const result = request.result;
14817
- if (result?.verified !== true || typeof result.reason !== "string" || !result.reason.trim()) {
14818
- throw new TeamRepoError("verification-failed", "Moderator host returned an invalid repository verification result");
14819
- }
14820
- if (typeof result.verified_at !== "string" || !result.verified_at || typeof result.verification_host_id !== "string" || !result.verification_host_id) {
14821
- throw new TeamRepoError("verification-failed", "Moderator host returned an incomplete repository verification result");
14822
- }
14823
- if (!Number.isFinite(Date.parse(result.verified_at)) || result.verification_host_id !== request.host_id) {
14824
- throw new TeamRepoError("verification-failed", "Moderator host returned an inconsistent repository verification result");
14825
- }
14826
- return result.reason.trim();
14827
- }
14828
- function _repoState(gitRepoUrl, status, reason) {
14829
- return { git_repo_url: gitRepoUrl, git_repo_verification_status: status, git_repo_verification_reason: reason };
14830
- }
14831
- function _formatRepository(repository) {
14832
- const url = repository.git_repo_url ?? "not configured";
14833
- const reason = repository.git_repo_verification_reason ?? "-";
14834
- return `Repository: ${url}
14835
- Status: ${repository.git_repo_verification_status}
14836
- Reason: ${reason}`;
14837
- }
14838
14601
  function _parseBootstrapArgs(tokens, config) {
14839
14602
  const values = {};
14840
14603
  for (let index = 0;index < tokens.length; index += 1) {
@@ -14962,11 +14725,11 @@ function _slotTable(slots) {
14962
14725
  Shown ${shown.length} of ${slots.length}${omitted ? `; ${omitted} omitted (use --format json)` : ""}`;
14963
14726
  }
14964
14727
 
14965
- // .dist-releases/1784772051951-3107696/src/rag.js
14728
+ // .dist-releases/1784860966076-1188772/src/memory.js
14966
14729
  var VALUE_FLAGS = new Set(["file", "source"]);
14967
- var RAG_SEARCH_PLAIN_LIMIT = 20;
14968
- async function runRagFromCli(client2, argv, config, input, format) {
14969
- const args = _parseRagArgs(argv);
14730
+ var MEMORY_SEARCH_PLAIN_LIMIT = 20;
14731
+ async function runMemoryFromCli(client2, argv, config, input, format) {
14732
+ const args = _parseMemoryCliArgs(argv);
14970
14733
  const captainId = _requireConfig(config.captainId, "SAILORBRIDGE_CAPTAIN_ID");
14971
14734
  const teamId = _requireConfig(config.session, "SAILORBRIDGE_SESSION");
14972
14735
  if (args.command === "set")
@@ -14977,14 +14740,14 @@ async function runRagFromCli(client2, argv, config, input, format) {
14977
14740
  return await _currentMemory(client2, captainId, teamId, args, format);
14978
14741
  if (args.command === "pin" || args.command === "unpin")
14979
14742
  return await _pinMemory(client2, captainId, teamId, args, format);
14980
- _assertRagShape(args, "rag index", 0, []);
14743
+ _assertMemoryShape(args, "memory index", 0, []);
14981
14744
  const result = await client2.getRagMemoryIndex(captainId, teamId);
14982
14745
  return format === "json" ? result : result.text;
14983
14746
  }
14984
- function _parseRagArgs(argv) {
14747
+ function _parseMemoryCliArgs(argv) {
14985
14748
  const command = argv[0];
14986
14749
  if (command !== "set" && command !== "search" && command !== "current" && command !== "index" && command !== "pin" && command !== "unpin") {
14987
- throw new Error("rag command must be set, search, current, index, pin, or unpin");
14750
+ throw new Error("memory command must be set, search, current, index, pin, or unpin");
14988
14751
  }
14989
14752
  const positionals = [];
14990
14753
  const flags = {};
@@ -14993,14 +14756,14 @@ function _parseRagArgs(argv) {
14993
14756
  if (!token.startsWith("--"))
14994
14757
  positionals.push(token);
14995
14758
  else
14996
- _addRagFlag(flags, token, VALUE_FLAGS.has(token.slice(2)) ? argv[++index] : undefined);
14759
+ _addMemoryFlag(flags, token, VALUE_FLAGS.has(token.slice(2)) ? argv[++index] : undefined);
14997
14760
  }
14998
14761
  return { command, positionals, flags };
14999
14762
  }
15000
- function _addRagFlag(flags, token, value) {
14763
+ function _addMemoryFlag(flags, token, value) {
15001
14764
  const name = token.slice(2);
15002
14765
  if (name !== "stdin" && !VALUE_FLAGS.has(name))
15003
- throw new Error(`unknown rag flag: ${token}`);
14766
+ throw new Error(`unknown memory flag: ${token}`);
15004
14767
  if (flags[name] !== undefined)
15005
14768
  throw new Error(`${token} may only be provided once`);
15006
14769
  if (name === "stdin")
@@ -15011,7 +14774,7 @@ function _addRagFlag(flags, token, value) {
15011
14774
  flags[name] = value;
15012
14775
  }
15013
14776
  async function _setMemory(client2, captainId, teamId, args, input, format) {
15014
- _assertRagShape(args, "rag set", 1, ["stdin", "file", "source"]);
14777
+ _assertMemoryShape(args, "memory set", 1, ["stdin", "file", "source"]);
15015
14778
  const key = args.positionals[0];
15016
14779
  const source = _requiredFlag(args, "source");
15017
14780
  const value = await _payload(args, input);
@@ -15024,26 +14787,26 @@ async function _setMemory(client2, captainId, teamId, args, input, format) {
15024
14787
  return `${result.card.key} v${result.card.version ?? 1} ${result.card.status ?? "active"}${suffix}`;
15025
14788
  }
15026
14789
  async function _searchMemory(client2, captainId, teamId, args, input, format) {
15027
- _assertRagShape(args, "rag search", 1, []);
14790
+ _assertMemoryShape(args, "memory search", 1, []);
15028
14791
  if (args.positionals[0] !== undefined)
15029
- await rejectUnusedTextInput(input, "rag search received stdin that its query argument does not consume; omit the query argument to read stdin");
14792
+ await rejectUnusedTextInput(input, "memory search received stdin that its query argument does not consume; omit the query argument to read stdin");
15030
14793
  const text = args.positionals[0] ?? await readTextInput(input);
15031
14794
  if (!text.trim())
15032
- throw new Error("rag search requires a query argument or stdin");
14795
+ throw new Error("memory search requires a query argument or stdin");
15033
14796
  const result = await client2.searchRag(captainId, teamId, text);
15034
14797
  if (format === "json")
15035
14798
  return result;
15036
14799
  const { cards } = result;
15037
14800
  if (!cards.length)
15038
14801
  return "No matching memory cards.";
15039
- const shown = cards.slice(0, RAG_SEARCH_PLAIN_LIMIT);
14802
+ const shown = cards.slice(0, MEMORY_SEARCH_PLAIN_LIMIT);
15040
14803
  const lines = shown.map((card) => `${card.key} ${card.score} ${_summary(card.value)}`);
15041
14804
  const omitted = cards.length - shown.length;
15042
14805
  return [...lines, `Shown ${shown.length} of ${cards.length}${omitted ? `; ${omitted} omitted (use --format json)` : ""}`].join(`
15043
14806
  `);
15044
14807
  }
15045
14808
  async function _currentMemory(client2, captainId, teamId, args, format) {
15046
- _assertRagShape(args, "rag current", 1, []);
14809
+ _assertMemoryShape(args, "memory current", 1, []);
15047
14810
  const card = await client2.getRagCurrentCard(captainId, teamId, args.positionals[0]);
15048
14811
  if (format === "json")
15049
14812
  return card;
@@ -15053,14 +14816,14 @@ ${card.value}`;
15053
14816
  }
15054
14817
  async function _pinMemory(client2, captainId, teamId, args, format) {
15055
14818
  const pinned = args.command === "pin";
15056
- _assertRagShape(args, `rag ${args.command}`, 1, []);
14819
+ _assertMemoryShape(args, `memory ${args.command}`, 1, []);
15057
14820
  const card = await client2.setRagCardPin(captainId, teamId, args.positionals[0], pinned);
15058
14821
  if (format === "json")
15059
14822
  return card;
15060
14823
  return `${card.key} v${card.version ?? 1} ${pinned ? "pinned" : "unpinned"}`;
15061
14824
  }
15062
- function _assertRagShape(args, name, maxPositionals, allowedFlags) {
15063
- const requiresKey = name !== "rag search" && name !== "rag index";
14825
+ function _assertMemoryShape(args, name, maxPositionals, allowedFlags) {
14826
+ const requiresKey = name !== "memory search" && name !== "memory index";
15064
14827
  if (args.positionals.length > maxPositionals) {
15065
14828
  const requirement = maxPositionals ? requiresKey ? "requires one key" : "accepts at most one positional argument" : "requires no positional arguments";
15066
14829
  throw new Error(`${name} ${requirement}`);
@@ -15074,12 +14837,12 @@ async function _payload(args, input) {
15074
14837
  const fromStdin = args.flags.stdin === true;
15075
14838
  const file = args.flags.file;
15076
14839
  if (fromStdin === (typeof file === "string"))
15077
- throw new Error("rag set requires exactly one of --stdin or --file");
14840
+ throw new Error("memory set requires exactly one of --stdin or --file");
15078
14841
  if (!fromStdin)
15079
- await rejectUnusedTextInput(input, "rag set received stdin that --file does not consume; use --stdin");
14842
+ await rejectUnusedTextInput(input, "memory set received stdin that --file does not consume; use --stdin");
15080
14843
  const value = fromStdin ? await readTextInput(input) : await readTextFile(file);
15081
14844
  if (!value.trim())
15082
- throw new Error("rag set payload must not be empty");
14845
+ throw new Error("memory set payload must not be empty");
15083
14846
  return value;
15084
14847
  }
15085
14848
  function _requiredFlag(args, name) {
@@ -15090,14 +14853,14 @@ function _requiredFlag(args, name) {
15090
14853
  }
15091
14854
  function _requireConfig(value, name) {
15092
14855
  if (!value?.trim())
15093
- throw new Error(`rag requires ${name}`);
14856
+ throw new Error(`memory requires ${name}`);
15094
14857
  return value;
15095
14858
  }
15096
14859
  function _summary(value) {
15097
14860
  return value.replace(/\s+/gu, " ").trim().slice(0, 180);
15098
14861
  }
15099
14862
 
15100
- // .dist-releases/1784772051951-3107696/src/mailbox-send.js
14863
+ // .dist-releases/1784860966076-1188772/src/mailbox-send.js
15101
14864
  var SEND_FLAGS = new Set(["body-file", "commit", "session", "tag", "task-id", "task-version", "to-agents", "to-roles", "to-host"]);
15102
14865
  function parseMailboxSendArgs(argv) {
15103
14866
  const parsed = argsFromValues(parseFlagValues(argv));
@@ -15196,7 +14959,7 @@ function splitList(value) {
15196
14959
  return list?.length ? list : undefined;
15197
14960
  }
15198
14961
 
15199
- // .dist-releases/1784772051951-3107696/src/reply.js
14962
+ // .dist-releases/1784860966076-1188772/src/reply.js
15200
14963
  var REPLY_FLAGS = new Set(["agent", "body-file", "in-reply-to", "session"]);
15201
14964
  async function sendReplyFromCli(client2, argv, config, io) {
15202
14965
  const args = _parseReplyArgs(argv);
@@ -15253,11 +15016,192 @@ function _requiredReplyValue(values, name) {
15253
15016
  return value;
15254
15017
  }
15255
15018
 
15256
- // .dist-releases/1784772051951-3107696/src/create.js
15019
+ // .dist-releases/1784860966076-1188772/src/worker-command.js
15020
+ async function runWorkerCommandFromCli(client2, argv, config, io, format = "plain") {
15021
+ const captainId = config.captainId;
15022
+ if (!captainId)
15023
+ throw new Error("SAILORBRIDGE_CAPTAIN_ID is required for worker commands");
15024
+ const command = argv[0];
15025
+ switch (command) {
15026
+ case "list":
15027
+ return _listWorkers(client2, captainId, _requireSession(config.session), argv.slice(1), io, format);
15028
+ case "run":
15029
+ return _runWorker(client2, captainId, _requireSession(config.session), argv.slice(1), io, format);
15030
+ case "job":
15031
+ return _showJob(client2, captainId, argv.slice(1), io, format);
15032
+ default:
15033
+ throw new Error("worker command must be job, list, run, or serve");
15034
+ }
15035
+ }
15036
+ function _requireSession(session) {
15037
+ if (!session)
15038
+ throw new Error("SAILORBRIDGE_SESSION is required for worker list and run");
15039
+ return session;
15040
+ }
15041
+ async function _listWorkers(client2, captainId, session, argv, io, format) {
15042
+ if (argv.length > 0)
15043
+ throw new Error(`unexpected argument: ${argv[0]}`);
15044
+ const response = await client2.listWorkers(captainId, session);
15045
+ if (format === "json") {
15046
+ writeOutput(io.stdout, response, format);
15047
+ return;
15048
+ }
15049
+ const { workers } = response;
15050
+ if (workers.length === 0) {
15051
+ io.stdout.write(`No workers registered.
15052
+ `);
15053
+ return;
15054
+ }
15055
+ for (const worker of workers.slice(0, 20)) {
15056
+ io.stdout.write(`${worker.name} ${worker.online ? "online" : "offline"} ${worker.host}
15057
+ `);
15058
+ }
15059
+ const omitted = workers.length - 20;
15060
+ if (omitted > 0)
15061
+ io.stdout.write(`${omitted} omitted; run sail worker list --format json for all.
15062
+ `);
15063
+ }
15064
+ async function _runWorker(client2, captainId, session, argv, io, format) {
15065
+ const input = _parseRunArgs(argv);
15066
+ const workers = (await client2.listWorkers(captainId, session)).workers;
15067
+ const worker = _requireRunnableWorker(workers, input.worker);
15068
+ const submitted = await _submitWorkerJob(client2, captainId, input);
15069
+ if (!input.detach || format === "plain" || !submitted.warning) {
15070
+ _writeWarning(submitted.warning, worker, io);
15071
+ }
15072
+ if (input.detach) {
15073
+ if (format === "json")
15074
+ writeOutput(io.stdout, submitted, format);
15075
+ else
15076
+ io.stdout.write(`${submitted.id}
15077
+ `);
15078
+ return;
15079
+ }
15080
+ const remoteBudget = input.timeoutSec ?? DEFAULT_JOB_TIMEOUT_SECONDS;
15081
+ const result = await client2.getJobResult(captainId, submitted.id, remoteBudget + WORKER_JOB_RESULT_GRACE_SECONDS);
15082
+ _writeResult(result, io, format);
15083
+ }
15084
+ function _requireRunnableWorker(workers, name) {
15085
+ const worker = workers.find((item) => item.name === name);
15086
+ if (!worker)
15087
+ throw new Error(`worker '${name}' is not registered in this team`);
15088
+ if (worker.capabilities.includes("mode=readonly")) {
15089
+ throw new Error(`worker '${name}' does not support bash jobs in readonly mode`);
15090
+ }
15091
+ return worker;
15092
+ }
15093
+ function _submitWorkerJob(client2, captainId, input) {
15094
+ return client2.submitJob(captainId, {
15095
+ worker: input.worker,
15096
+ argv: input.argv,
15097
+ ...input.timeoutSec === undefined ? {} : { timeoutSec: input.timeoutSec }
15098
+ });
15099
+ }
15100
+ async function _showJob(client2, captainId, argv, io, format) {
15101
+ if (argv.length !== 1 || !argv[0])
15102
+ throw new Error("worker job requires exactly one job id");
15103
+ const result = await client2.getJobStatus(captainId, argv[0]);
15104
+ if (format === "json") {
15105
+ writeOutput(io.stdout, result, format);
15106
+ if (result.exit_code !== null)
15107
+ io.setExitCode(_localExitStatus(result.exit_code));
15108
+ return;
15109
+ }
15110
+ if (result.status === "queued" || result.status === "running") {
15111
+ io.stdout.write(`${result.id} ${result.status}
15112
+ `);
15113
+ return;
15114
+ }
15115
+ _writeResult(result, io, format);
15116
+ }
15117
+ function _parseRunArgs(argv) {
15118
+ const separator = argv.indexOf("--");
15119
+ if (separator < 0)
15120
+ throw new Error("worker run requires '--' before argv");
15121
+ const remoteArgv = argv.slice(separator + 1);
15122
+ if (remoteArgv.length === 0 || !remoteArgv[0]?.trim())
15123
+ throw new Error("worker run requires a command after '--'");
15124
+ const { worker, timeoutSec, detach } = _parseRunFlags(argv.slice(0, separator));
15125
+ if (!worker)
15126
+ throw new Error("--worker is required");
15127
+ return { worker, argv: remoteArgv, detach, ...timeoutSec === undefined ? {} : { timeoutSec } };
15128
+ }
15129
+ function _parseRunFlags(flags) {
15130
+ let worker = "";
15131
+ let timeoutSec;
15132
+ let detach = false;
15133
+ for (let index = 0;index < flags.length; index += 1) {
15134
+ const flag = flags[index];
15135
+ if (flag === "--detach") {
15136
+ detach = true;
15137
+ continue;
15138
+ }
15139
+ const value = flags[index + 1];
15140
+ if (!value)
15141
+ throw new Error(`${flag} requires a value`);
15142
+ if (flag === "--worker")
15143
+ worker = value.trim();
15144
+ else if (flag === "--timeout")
15145
+ timeoutSec = _parseTimeout(value);
15146
+ else
15147
+ throw new Error(`unexpected argument: ${flag}`);
15148
+ index += 1;
15149
+ }
15150
+ return { worker, detach, ...timeoutSec === undefined ? {} : { timeoutSec } };
15151
+ }
15152
+ function _parseTimeout(value) {
15153
+ if (!/^(?:0|[1-9]\d*)(?:\.\d+)?$/u.test(value)) {
15154
+ throw new Error(`timeout must be a number from 1 to ${MAX_JOB_TIMEOUT_SECONDS} seconds`);
15155
+ }
15156
+ const timeout = Number(value);
15157
+ if (!Number.isFinite(timeout) || timeout < 1 || timeout > MAX_JOB_TIMEOUT_SECONDS) {
15158
+ throw new Error(`timeout must be a number from 1 to ${MAX_JOB_TIMEOUT_SECONDS} seconds`);
15159
+ }
15160
+ return timeout;
15161
+ }
15162
+ function _writeWarning(warning, worker, io) {
15163
+ if (warning) {
15164
+ io.stderr.write(`Warning: ${warning}
15165
+ `);
15166
+ return;
15167
+ }
15168
+ if (!worker.online)
15169
+ io.stderr.write(`Warning: worker '${worker.name}' is registered but offline.
15170
+ `);
15171
+ }
15172
+ function _writeResult(response, io, format) {
15173
+ if (!response.result || response.exit_code === null) {
15174
+ throw new Error(`job ${response.id} did not finish before the timeout (status=${response.status})`);
15175
+ }
15176
+ const localStatus = _localExitStatus(response.exit_code);
15177
+ if (format === "json") {
15178
+ writeOutput(io.stdout, response, format);
15179
+ io.setExitCode(localStatus);
15180
+ return;
15181
+ }
15182
+ io.stdout.write(response.result.stdout);
15183
+ io.stderr.write(response.result.stderr);
15184
+ if (response.exit_code < 0)
15185
+ _writeSignalStatus(response.exit_code, localStatus, response.result.stderr, io);
15186
+ io.setExitCode(localStatus);
15187
+ }
15188
+ function _localExitStatus(remoteCode) {
15189
+ return remoteCode < 0 ? 128 + Math.abs(remoteCode) : remoteCode;
15190
+ }
15191
+ function _writeSignalStatus(remoteCode, localStatus, remoteStderr, io) {
15192
+ if (remoteStderr && !remoteStderr.endsWith(`
15193
+ `))
15194
+ io.stderr.write(`
15195
+ `);
15196
+ io.stderr.write(`Remote exit code ${remoteCode}; local exit status ${localStatus}.
15197
+ `);
15198
+ }
15199
+
15200
+ // .dist-releases/1784860966076-1188772/src/create.js
15257
15201
  var CREATE_ENGINES = new Set(["claude", "codex"]);
15258
15202
  var DEFAULT_ENGINE = "claude";
15259
15203
  var WORKSPACE_PLAIN_LIMIT = 20;
15260
- var CREATE_SUBCOMMANDS = new Set(["team", "pair", "worker"]);
15204
+ var CREATE_SUBCOMMANDS = new Set(["team", "pair", "planner", "tester", "worker"]);
15261
15205
  async function runCreateFromCli(argv, io, deps = {}) {
15262
15206
  const sub = argv[0];
15263
15207
  if (!sub || sub.startsWith("--"))
@@ -15269,6 +15213,8 @@ async function runCreateFromCli(argv, io, deps = {}) {
15269
15213
  return await _createTeam(flags, io, deps);
15270
15214
  if (sub === "pair")
15271
15215
  return await _createPair(flags, io, deps);
15216
+ if (sub === "planner" || sub === "tester")
15217
+ return await _createMember(sub, flags, io, deps);
15272
15218
  return await _createWorker(flags, io, deps);
15273
15219
  }
15274
15220
  async function _createTeam(flags, io, deps) {
@@ -15277,7 +15223,7 @@ async function _createTeam(flags, io, deps) {
15277
15223
  const displayName = _required4(flags.name, "--name");
15278
15224
  const template = _required4(flags.template, "--template");
15279
15225
  const engine = _engine3(flags.engine);
15280
- const ctx = await _teamConsoleContext(flags, deps);
15226
+ const ctx = await _teamConsoleContext("team", flags, deps);
15281
15227
  const host = await _resolveOnlineHost(ctx.relay, ctx.captainId, flags.host);
15282
15228
  const workspace = await _resolveHostWorkspace(ctx.relay, ctx.captainId, host.host_id, flags.workspace);
15283
15229
  const { team, slots } = await _createAndApplyTeam(deps, ctx, displayName, template);
@@ -15305,9 +15251,9 @@ async function _startTeamSlots(deps, ctx, team, hostId, workspaceRoot, engine, s
15305
15251
  }
15306
15252
  return requests;
15307
15253
  }
15308
- async function _postConsoleJson(deps, ctx, path24, body) {
15254
+ async function _postConsoleJson(deps, ctx, path22, body) {
15309
15255
  const fetchImpl = deps.fetchImpl ?? fetch;
15310
- const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path24}`, {
15256
+ const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path22}`, {
15311
15257
  method: "POST",
15312
15258
  headers: { Authorization: `Bearer ${ctx.token}`, "Content-Type": "application/json" },
15313
15259
  body: JSON.stringify(body)
@@ -15320,6 +15266,19 @@ async function _postConsoleJson(deps, ctx, path24, body) {
15320
15266
  throw new Error("SailorBridge API returned a malformed object");
15321
15267
  return parsed;
15322
15268
  }
15269
+ async function _getConsoleJson(deps, ctx, path22) {
15270
+ const fetchImpl = deps.fetchImpl ?? fetch;
15271
+ const response = await fetchImpl(`${ctx.apiUrl.replace(/\/$/, "")}${path22}`, {
15272
+ headers: { Authorization: `Bearer ${ctx.token}` }
15273
+ });
15274
+ const text = await response.text();
15275
+ if (!response.ok)
15276
+ throw new Error(`SailorBridge API ${response.status}: ${text}`);
15277
+ const parsed = JSON.parse(text);
15278
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
15279
+ throw new Error("SailorBridge API returned a malformed object");
15280
+ return parsed;
15281
+ }
15323
15282
  function _responseString(body, field) {
15324
15283
  const value = body[field];
15325
15284
  if (typeof value !== "string" || !value)
@@ -15361,6 +15320,77 @@ async function _createPair(flags, io, deps) {
15361
15320
  });
15362
15321
  writeOutput(io.stdout, result, io.format, () => _pairPlain(host.host_id, workspace, engine, result));
15363
15322
  }
15323
+ async function _createMember(role, flags, io, deps) {
15324
+ _rejectMemberFlags(role, flags);
15325
+ const engine = _engine3(flags.engine);
15326
+ const session = _requiredSession(flags.session, _envSession(deps));
15327
+ const ctx = await _teamConsoleContext(role, flags, deps);
15328
+ const host = await _resolveOnlineHost(ctx.relay, ctx.captainId, flags.host);
15329
+ const workspace = await _resolveHostWorkspace(ctx.relay, ctx.captainId, host.host_id, flags.workspace);
15330
+ const slot = await _memberSlotForCreate(deps, ctx, session, role);
15331
+ const request = await _startMember(deps, ctx, session, host.host_id, workspace.path, engine, role, slot);
15332
+ const result = { role, host: host.host_id, workspace: workspace.path, engine, slot, request };
15333
+ writeOutput(io.stdout, result, io.format, () => _memberPlain(result));
15334
+ }
15335
+ function _rejectMemberFlags(role, flags) {
15336
+ const unsupported = [
15337
+ ["--name", flags.name],
15338
+ ["--template", flags.template],
15339
+ ["--concurrency", flags.concurrency]
15340
+ ];
15341
+ const found = unsupported.find(([, value]) => value !== undefined);
15342
+ if (found)
15343
+ throw new Error(`create ${role} does not accept ${found[0]}`);
15344
+ }
15345
+ 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));
15348
+ const retryable = slots.find((slot) => _isRetryableMemberSlot(slot, role));
15349
+ if (retryable)
15350
+ return retryable;
15351
+ if (role === "planner" && slots.some((slot) => slot.role === role)) {
15352
+ throw new Error("duplicate-singleton-role: team already has a planner slot");
15353
+ }
15354
+ const created = _responseMemberSlot(await _postConsoleJson(deps, ctx, path22, { role }));
15355
+ if (created.role !== role)
15356
+ throw new Error("SailorBridge API returned a member slot with the wrong role");
15357
+ return created;
15358
+ }
15359
+ function _isRetryableMemberSlot(slot, role) {
15360
+ return slot.role === role && slot.status === "planned" && slot.template_origin === "single-add";
15361
+ }
15362
+ async function _startMember(deps, ctx, session, hostId, workspaceRoot, engine, role, slot) {
15363
+ const response = await _postConsoleJson(deps, ctx, `/api/console/teams/${encodeURIComponent(session)}/agent-start-requests`, { host_id: hostId, slot_id: slot.slot_id, spec: { engine, role, workspace: { mode: "worktree", root: workspaceRoot } } });
15364
+ const request = _responseStartRequest(response);
15365
+ if (request.host_id !== hostId || request.agent_name !== slot.agent_name) {
15366
+ throw new Error("SailorBridge API returned a start request with mismatched identity");
15367
+ }
15368
+ return request;
15369
+ }
15370
+ function _responseMemberSlots(body) {
15371
+ if (!Array.isArray(body.agent_slots))
15372
+ throw new Error("SailorBridge API response is missing agent_slots");
15373
+ return body.agent_slots.map(_responseMemberSlotValue);
15374
+ }
15375
+ function _responseMemberSlot(body) {
15376
+ if (!body.agent_slot || typeof body.agent_slot !== "object" || Array.isArray(body.agent_slot)) {
15377
+ throw new Error("SailorBridge API response is missing agent_slot");
15378
+ }
15379
+ return _responseMemberSlotValue(body.agent_slot);
15380
+ }
15381
+ function _responseMemberSlotValue(raw) {
15382
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
15383
+ throw new Error("SailorBridge API returned a malformed agent slot");
15384
+ }
15385
+ const slot = raw;
15386
+ return {
15387
+ slot_id: _responseString(slot, "slot_id"),
15388
+ agent_name: _responseString(slot, "agent_name"),
15389
+ role: _responseString(slot, "role"),
15390
+ status: _responseString(slot, "status"),
15391
+ template_origin: _responseString(slot, "template_origin")
15392
+ };
15393
+ }
15364
15394
  async function _createWorker(flags, io, deps) {
15365
15395
  const workerName = _required4(flags.name, "--name");
15366
15396
  const maxConcurrency = _concurrency(flags.concurrency);
@@ -15397,11 +15427,11 @@ async function _consoleContext(flags, deps) {
15397
15427
  token: credential.token
15398
15428
  };
15399
15429
  }
15400
- async function _teamConsoleContext(flags, deps) {
15430
+ async function _teamConsoleContext(command, flags, deps) {
15401
15431
  const env = deps.env ?? process.env;
15402
15432
  const accessToken = env.SAILORBRIDGE_CAPTAIN_ACCESS_TOKEN?.trim();
15403
15433
  if (!accessToken) {
15404
- throw new Error("create team requires SAILORBRIDGE_CAPTAIN_ACCESS_TOKEN for captain-authorized console operations");
15434
+ throw new Error(`create ${command} requires SAILORBRIDGE_CAPTAIN_ACCESS_TOKEN for captain-authorized console operations`);
15405
15435
  }
15406
15436
  return { ...await _consoleContext(flags, deps), token: accessToken };
15407
15437
  }
@@ -15534,6 +15564,18 @@ function _pairPlain(hostId, workspace, engine, result) {
15534
15564
  ].join(`
15535
15565
  `);
15536
15566
  }
15567
+ function _memberPlain(result) {
15568
+ return [
15569
+ `${result.role}: ${result.slot.agent_name}`,
15570
+ `slot: ${result.slot.slot_id}`,
15571
+ `host: ${result.host}`,
15572
+ `workspace: ${result.workspace}`,
15573
+ `engine: ${result.engine}`,
15574
+ `request: ${result.request.id}`,
15575
+ `status: ${result.request.status}`
15576
+ ].join(`
15577
+ `);
15578
+ }
15537
15579
  function _workerPlain(hostId, workdir, result) {
15538
15580
  return [
15539
15581
  `worker: ${result.worker.name}`,
@@ -15545,7 +15587,7 @@ function _workerPlain(hostId, workdir, result) {
15545
15587
  `);
15546
15588
  }
15547
15589
 
15548
- // .dist-releases/1784772051951-3107696/src/cli.js
15590
+ // .dist-releases/1784860966076-1188772/src/cli.js
15549
15591
  var FORMATTED_COMMANDS = new Set([
15550
15592
  "ask",
15551
15593
  "create",
@@ -15554,11 +15596,12 @@ var FORMATTED_COMMANDS = new Set([
15554
15596
  "host",
15555
15597
  "login",
15556
15598
  "mailbox",
15557
- "rag",
15599
+ "memory",
15558
15600
  "reply",
15559
15601
  "task",
15560
15602
  "team",
15561
- "tutorial"
15603
+ "tutorial",
15604
+ "worker"
15562
15605
  ]);
15563
15606
  var SERVICE_INSTALL_COMMAND = "sailorbridge host service install";
15564
15607
  var SERVICE_UNINSTALL_COMMAND = "sailorbridge host service uninstall";
@@ -15585,6 +15628,9 @@ async function main(argv) {
15585
15628
  if (command === "host" && argv[1] === "run" && output2.provided) {
15586
15629
  throw new Error("host run does not produce a finite result; remove --format");
15587
15630
  }
15631
+ if (command === "worker" && argv[1] === "serve" && output2.provided) {
15632
+ throw new Error("worker serve does not produce a finite result; remove --format");
15633
+ }
15588
15634
  await _rejectUnsupportedCliInput(command, argv.slice(1), process.stdin);
15589
15635
  if (command === "login") {
15590
15636
  await _hostLogin(argv.slice(1), output2.format);
@@ -15621,7 +15667,7 @@ async function main(argv) {
15621
15667
  await runHostWorkspaceCommandFromCli(argv.slice(2), { stdout: process.stdout, format: output2.format });
15622
15668
  return;
15623
15669
  }
15624
- if (command === "worker") {
15670
+ if (command === "worker" && argv[1] === "serve") {
15625
15671
  await runWorkerFromCli(argv.slice(2));
15626
15672
  return;
15627
15673
  }
@@ -15664,8 +15710,18 @@ async function main(argv) {
15664
15710
  writeOutput(process.stdout, await runTeamFromCli(client2, argv.slice(1), config, output2.format), output2.format);
15665
15711
  return;
15666
15712
  }
15667
- if (command === "rag") {
15668
- writeOutput(process.stdout, await runRagFromCli(client2, argv.slice(1), config, process.stdin, output2.format), output2.format);
15713
+ if (command === "memory") {
15714
+ writeOutput(process.stdout, await runMemoryFromCli(client2, argv.slice(1), config, process.stdin, output2.format), output2.format);
15715
+ return;
15716
+ }
15717
+ if (command === "worker") {
15718
+ await runWorkerCommandFromCli(client2, argv.slice(1), config, {
15719
+ stdout: process.stdout,
15720
+ stderr: process.stderr,
15721
+ setExitCode: (code) => {
15722
+ process.exitCode = code;
15723
+ }
15724
+ }, output2.format);
15669
15725
  return;
15670
15726
  }
15671
15727
  if (command === "enrollment") {
@@ -15690,7 +15746,7 @@ var KNOWN_COMMANDS = new Set([
15690
15746
  "host",
15691
15747
  "login",
15692
15748
  "mailbox",
15693
- "rag",
15749
+ "memory",
15694
15750
  "refresh",
15695
15751
  "reply",
15696
15752
  "supervise",
@@ -15707,7 +15763,7 @@ var SUBCOMMANDS = {
15707
15763
  event: new Set(["add", "disable", "enable", "list", "rm"]),
15708
15764
  host: new Set([...HOST_RELAY_SUBCOMMANDS, "run", "service", "start-request", "statusline", "workspace"]),
15709
15765
  mailbox: new Set(["ack", "claim", "peek", "send"]),
15710
- rag: new Set(["current", "index", "pin", "search", "set", "unpin"]),
15766
+ memory: new Set(["current", "index", "pin", "search", "set", "unpin"]),
15711
15767
  supervise: new Set,
15712
15768
  task: new Set([
15713
15769
  "activate",
@@ -15723,8 +15779,8 @@ var SUBCOMMANDS = {
15723
15779
  "ready",
15724
15780
  "unpark"
15725
15781
  ]),
15726
- team: new Set(["bootstrap", "repo"]),
15727
- worker: new Set(["serve"])
15782
+ team: new Set(["bootstrap"]),
15783
+ worker: new Set(["job", "list", "run", "serve"])
15728
15784
  };
15729
15785
  function _assertKnownCommand(command, subcommand) {
15730
15786
  if (!KNOWN_COMMANDS.has(command))
@@ -15740,7 +15796,7 @@ function _assertKnownCommand(command, subcommand) {
15740
15796
  }
15741
15797
  async function _rejectUnsupportedCliInput(command, args, input) {
15742
15798
  const subcommand = args[0];
15743
- const acceptsInput = command === "reply" || command === "host" && subcommand === "statusline" || command === "mailbox" && subcommand === "send" || command === "task" && subcommand === "create" || command === "ask" && subcommand !== "list" && subcommand !== "get" && subcommand !== "status" || command === "rag" && (subcommand === "set" || subcommand === "search");
15799
+ const acceptsInput = command === "reply" || command === "host" && subcommand === "statusline" || command === "mailbox" && subcommand === "send" || command === "task" && subcommand === "create" || command === "ask" && subcommand !== "list" && subcommand !== "get" && subcommand !== "status" || command === "memory" && (subcommand === "set" || subcommand === "search");
15744
15800
  if (!acceptsInput)
15745
15801
  await rejectUnusedTextInput(input, `${command}${subcommand ? ` ${subcommand}` : ""} does not accept stdin`);
15746
15802
  }
@@ -15986,6 +16042,7 @@ async function supervise(client2, argv, config) {
15986
16042
  agentName: parsed.agentName,
15987
16043
  engine: parsed.cli,
15988
16044
  hostEnv: process.env,
16045
+ teamMemoryEntitled: await resolveTeamMemoryEntitlement(client2, config.captainId, config.session),
15989
16046
  descriptor: {
15990
16047
  key_id: "direct-supervise",
15991
16048
  relay_url: config.relayUrl,
@@ -16208,30 +16265,38 @@ function _usageText() {
16208
16265
  creates an auto-numbered team and starts every template slot on one paired online host.
16209
16266
  sailorbridge create pair [--host H] [--workspace PATH|GIT_URL] [--engine claude|codex] [--name N] [--session S]
16210
16267
  builds a writer+reviewer dev pair on a paired online host; workspace defaults to the host's reported one.
16268
+ sailorbridge create planner [--host H] [--workspace PATH] [--engine claude|codex] [--session S]
16269
+ adds the team's single planner through the console slot + start flow.
16270
+ sailorbridge create tester [--host H] [--workspace PATH] [--engine claude|codex] [--session S]
16271
+ adds one tester through the console slot + start flow.
16211
16272
  sailorbridge create worker --name N --concurrency N [--host H] [--workspace PATH] [--session S]
16212
16273
  starts a worker on a paired online host. All create commands: [--api-url URL] [--credentials-path PATH] [--format plain|json]
16213
16274
  sailorbridge team bootstrap --template classic3 --slot SLOT=HOST:claude|codex [--slot ...] [--session S]
16214
- sailorbridge team repo set GIT_URL [--wait] [--timeout-sec 1..300] [--session S]
16215
- sailorbridge team repo status [--session S]
16216
- sail rag set KEY (--stdin | --file PATH) --source SOURCE
16217
- sail rag search [QUERY]
16218
- sail rag current KEY
16219
- sail rag pin KEY (moderator/captain only)
16220
- sail rag unpin KEY (moderator/captain only)
16221
- sail rag index
16275
+ sail memory set KEY (--stdin | --file PATH) --source SOURCE
16276
+ sail memory search [QUERY]
16277
+ sail memory current KEY
16278
+ sail memory pin KEY (moderator/captain only)
16279
+ sail memory unpin KEY (moderator/captain only)
16280
+ sail memory index
16222
16281
  sailorbridge enrollment --session S --agent A --roles operator [--host H] [--name N]
16223
16282
  sailorbridge host list|register|heartbeat [--host-id H] [--display-name N]
16224
16283
  sailorbridge host consume --once --dry-run-start [--host-id H] [--display-name N]
16225
16284
  sailorbridge host service install|uninstall|status [--binary PATH] [--credentials-path PATH] [--workspace-root DIR] [--service-dir DIR] [--service-name NAME]
16226
16285
  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]
16227
16286
  sailorbridge host run [--credentials-path PATH] [--api-url URL] [--host-id H] [--display-name N] [--workspace-root DIR]
16287
+ sailorbridge worker list [--format plain|json]
16288
+ sailorbridge worker run [--detach] --worker NAME [--timeout SEC] [--format plain|json] -- ARGV...
16289
+ sailorbridge worker job ID [--format plain|json]
16290
+ list/run/job require SAILORBRIDGE_CAPTAIN_ID; list/run also require SAILORBRIDGE_SESSION.
16228
16291
  sailorbridge worker serve [--relay-url URL] [--worker NAME] [--workdir DIR] [--max-concurrency N]
16229
16292
  sailorbridge supervise --agent A --cli claude|codex --cwd DIR --i-own-this-agent [--transport tmux] [--tmux-session S] [-- COMMAND...]
16230
16293
 
16231
16294
  Environment:
16232
16295
  SAILORBRIDGE_RELAY_URL / SAILORBRIDGE_TOKEN
16233
16296
  SAILORBRIDGE_API_URL overrides the default product API for login/tutorial (${DEFAULT_PRODUCT_API_URL}); --api-url wins
16234
- SAILORBRIDGE_AGENT_NAME / SAILORBRIDGE_SESSION / SAILORBRIDGE_CAPTAIN_ID
16297
+ SAILORBRIDGE_AGENT_NAME
16298
+ SAILORBRIDGE_CAPTAIN_ID is required for worker list/run/job.
16299
+ SAILORBRIDGE_SESSION is also required for worker list/run.
16235
16300
  SAILORBRIDGE_HOST_ID / SAILORBRIDGE_HOST_DISPLAY_NAME
16236
16301
  SAILORBRIDGE_NO_SERVICE=1 makes login skip the host service install
16237
16302
  SAILORBRIDGE_DEBUG=1 prints the internal pairing id during login (diagnostics only)
@@ -16272,5 +16337,5 @@ function _looksLikeNetworkFailure(message) {
16272
16337
  return /fetch failed|network|ECONN(?:REFUSED|RESET)|ENOTFOUND|ETIMEDOUT/iu.test(message);
16273
16338
  }
16274
16339
 
16275
- //# debugId=97C757320E29169264756E2164756E21
16340
+ //# debugId=9889EE8093CEB27064756E2164756E21
16276
16341
  //# sourceMappingURL=cli.js.map