@rosthq/cli 0.7.68 → 0.7.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -472,13 +472,13 @@ function __disposeResources(env) {
472
472
  }
473
473
  return next();
474
474
  }
475
- function __rewriteRelativeImportExtension(path7, preserveJsx) {
476
- if (typeof path7 === "string" && /^\.\.?\//.test(path7)) {
477
- return path7.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
475
+ function __rewriteRelativeImportExtension(path8, preserveJsx) {
476
+ if (typeof path8 === "string" && /^\.\.?\//.test(path8)) {
477
+ return path8.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
478
478
  return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : d + ext + "." + cm.toLowerCase() + "js";
479
479
  });
480
480
  }
481
- return path7;
481
+ return path8;
482
482
  }
483
483
  var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
484
484
  var init_tslib_es6 = __esm({
@@ -14027,10 +14027,10 @@ function mergeDefs(...defs) {
14027
14027
  function cloneDef(schema) {
14028
14028
  return mergeDefs(schema._zod.def);
14029
14029
  }
14030
- function getElementAtPath(obj, path7) {
14031
- if (!path7)
14030
+ function getElementAtPath(obj, path8) {
14031
+ if (!path8)
14032
14032
  return obj;
14033
- return path7.reduce((acc, key) => acc?.[key], obj);
14033
+ return path8.reduce((acc, key) => acc?.[key], obj);
14034
14034
  }
14035
14035
  function promiseAllObject(promisesObj) {
14036
14036
  const keys = Object.keys(promisesObj);
@@ -14439,11 +14439,11 @@ function explicitlyAborted(x, startIndex = 0) {
14439
14439
  }
14440
14440
  return false;
14441
14441
  }
14442
- function prefixIssues(path7, issues) {
14442
+ function prefixIssues(path8, issues) {
14443
14443
  return issues.map((iss) => {
14444
14444
  var _a3;
14445
14445
  (_a3 = iss).path ?? (_a3.path = []);
14446
- iss.path.unshift(path7);
14446
+ iss.path.unshift(path8);
14447
14447
  return iss;
14448
14448
  });
14449
14449
  }
@@ -14590,16 +14590,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
14590
14590
  }
14591
14591
  function formatError(error51, mapper = (issue2) => issue2.message) {
14592
14592
  const fieldErrors = { _errors: [] };
14593
- const processError = (error52, path7 = []) => {
14593
+ const processError = (error52, path8 = []) => {
14594
14594
  for (const issue2 of error52.issues) {
14595
14595
  if (issue2.code === "invalid_union" && issue2.errors.length) {
14596
- issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
14596
+ issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
14597
14597
  } else if (issue2.code === "invalid_key") {
14598
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
14598
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
14599
14599
  } else if (issue2.code === "invalid_element") {
14600
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
14600
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
14601
14601
  } else {
14602
- const fullpath = [...path7, ...issue2.path];
14602
+ const fullpath = [...path8, ...issue2.path];
14603
14603
  if (fullpath.length === 0) {
14604
14604
  fieldErrors._errors.push(mapper(issue2));
14605
14605
  } else {
@@ -14626,17 +14626,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
14626
14626
  }
14627
14627
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
14628
14628
  const result = { errors: [] };
14629
- const processError = (error52, path7 = []) => {
14629
+ const processError = (error52, path8 = []) => {
14630
14630
  var _a3, _b;
14631
14631
  for (const issue2 of error52.issues) {
14632
14632
  if (issue2.code === "invalid_union" && issue2.errors.length) {
14633
- issue2.errors.map((issues) => processError({ issues }, [...path7, ...issue2.path]));
14633
+ issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
14634
14634
  } else if (issue2.code === "invalid_key") {
14635
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
14635
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
14636
14636
  } else if (issue2.code === "invalid_element") {
14637
- processError({ issues: issue2.issues }, [...path7, ...issue2.path]);
14637
+ processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
14638
14638
  } else {
14639
- const fullpath = [...path7, ...issue2.path];
14639
+ const fullpath = [...path8, ...issue2.path];
14640
14640
  if (fullpath.length === 0) {
14641
14641
  result.errors.push(mapper(issue2));
14642
14642
  continue;
@@ -14668,8 +14668,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
14668
14668
  }
14669
14669
  function toDotPath(_path) {
14670
14670
  const segs = [];
14671
- const path7 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
14672
- for (const seg of path7) {
14671
+ const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
14672
+ for (const seg of path8) {
14673
14673
  if (typeof seg === "number")
14674
14674
  segs.push(`[${seg}]`);
14675
14675
  else if (typeof seg === "symbol")
@@ -27361,13 +27361,13 @@ function resolveRef(ref, ctx) {
27361
27361
  if (!ref.startsWith("#")) {
27362
27362
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
27363
27363
  }
27364
- const path7 = ref.slice(1).split("/").filter(Boolean);
27365
- if (path7.length === 0) {
27364
+ const path8 = ref.slice(1).split("/").filter(Boolean);
27365
+ if (path8.length === 0) {
27366
27366
  return ctx.rootSchema;
27367
27367
  }
27368
27368
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
27369
- if (path7[0] === defsKey) {
27370
- const key = path7[1];
27369
+ if (path8[0] === defsKey) {
27370
+ const key = path8[1];
27371
27371
  if (!key || !ctx.defs[key]) {
27372
27372
  throw new Error(`Reference not found: ${ref}`);
27373
27373
  }
@@ -27829,7 +27829,7 @@ var CommandClient = class {
27829
27829
  this.fetchImpl = options.fetchImpl ?? fetch;
27830
27830
  }
27831
27831
  async execute(commandId, body = {}, options = {}) {
27832
- const path7 = `/api/commands/${encodeURIComponent(commandId)}`;
27832
+ const path8 = `/api/commands/${encodeURIComponent(commandId)}`;
27833
27833
  const headers = {
27834
27834
  authorization: `Bearer ${this.token}`,
27835
27835
  "content-type": "application/json"
@@ -27837,7 +27837,7 @@ var CommandClient = class {
27837
27837
  if (options.targetSeatId !== void 0 && options.targetSeatId.length > 0) {
27838
27838
  headers["x-rost-seat"] = options.targetSeatId;
27839
27839
  }
27840
- const response = await this.fetchImpl(`${this.appUrl}${path7}`, {
27840
+ const response = await this.fetchImpl(`${this.appUrl}${path8}`, {
27841
27841
  method: "POST",
27842
27842
  headers,
27843
27843
  body: JSON.stringify(body)
@@ -27850,7 +27850,7 @@ var CommandClient = class {
27850
27850
  const snippet = redactSecrets(text.replace(/\s+/g, " ").trim()).slice(0, 200);
27851
27851
  throw new CommandClientError(
27852
27852
  response.status,
27853
- `server returned HTTP ${response.status} (non-JSON) from ${path7}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
27853
+ `server returned HTTP ${response.status} (non-JSON) from ${path8}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
27854
27854
  );
27855
27855
  }
27856
27856
  const parsed = commandResponseSchema.parse(raw);
@@ -33181,8 +33181,8 @@ var IcebergError = class extends Error {
33181
33181
  return this.status === 419;
33182
33182
  }
33183
33183
  };
33184
- function buildUrl(baseUrl, path7, query) {
33185
- const url2 = new URL(path7, baseUrl);
33184
+ function buildUrl(baseUrl, path8, query) {
33185
+ const url2 = new URL(path8, baseUrl);
33186
33186
  if (query) {
33187
33187
  for (const [key, value] of Object.entries(query)) {
33188
33188
  if (value !== void 0) {
@@ -33212,12 +33212,12 @@ function createFetchClient(options) {
33212
33212
  return {
33213
33213
  async request({
33214
33214
  method,
33215
- path: path7,
33215
+ path: path8,
33216
33216
  query,
33217
33217
  body,
33218
33218
  headers
33219
33219
  }) {
33220
- const url2 = buildUrl(options.baseUrl, path7, query);
33220
+ const url2 = buildUrl(options.baseUrl, path8, query);
33221
33221
  const authHeaders = await buildAuthHeaders(options.auth);
33222
33222
  const res = await fetchFn(url2, {
33223
33223
  method,
@@ -34079,7 +34079,7 @@ var StorageFileApi = class extends BaseApiClient {
34079
34079
  * @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
34080
34080
  * @param fileBody The body of the file to be stored in the bucket.
34081
34081
  */
34082
- async uploadOrUpdate(method, path7, fileBody, fileOptions) {
34082
+ async uploadOrUpdate(method, path8, fileBody, fileOptions) {
34083
34083
  var _this = this;
34084
34084
  return _this.handleOperation(async () => {
34085
34085
  let body;
@@ -34103,7 +34103,7 @@ var StorageFileApi = class extends BaseApiClient {
34103
34103
  if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
34104
34104
  }
34105
34105
  if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) for (const [key, value] of Object.entries(fileOptions.headers)) headers = setHeader(headers, key, value);
34106
- const cleanPath = _this._removeEmptyFolders(path7);
34106
+ const cleanPath = _this._removeEmptyFolders(path8);
34107
34107
  const _path = _this._getFinalPath(cleanPath);
34108
34108
  const data = await (method == "PUT" ? put : post)(_this.fetch, `${_this.url}/object/${_path}`, body, _objectSpread22({ headers }, (options === null || options === void 0 ? void 0 : options.duplex) ? { duplex: options.duplex } : {}));
34109
34109
  return {
@@ -34180,8 +34180,8 @@ var StorageFileApi = class extends BaseApiClient {
34180
34180
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34181
34181
  * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Upload file using `ArrayBuffer` from base64 file data instead, see example below.
34182
34182
  */
34183
- async upload(path7, fileBody, fileOptions) {
34184
- return this.uploadOrUpdate("POST", path7, fileBody, fileOptions);
34183
+ async upload(path8, fileBody, fileOptions) {
34184
+ return this.uploadOrUpdate("POST", path8, fileBody, fileOptions);
34185
34185
  }
34186
34186
  /**
34187
34187
  * Upload a file with a token generated from `createSignedUploadUrl`.
@@ -34221,9 +34221,9 @@ var StorageFileApi = class extends BaseApiClient {
34221
34221
  * - `objects` table permissions: none
34222
34222
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34223
34223
  */
34224
- async uploadToSignedUrl(path7, token, fileBody, fileOptions) {
34224
+ async uploadToSignedUrl(path8, token, fileBody, fileOptions) {
34225
34225
  var _this3 = this;
34226
- const cleanPath = _this3._removeEmptyFolders(path7);
34226
+ const cleanPath = _this3._removeEmptyFolders(path8);
34227
34227
  const _path = _this3._getFinalPath(cleanPath);
34228
34228
  const url2 = new URL(_this3.url + `/object/upload/sign/${_path}`);
34229
34229
  url2.searchParams.set("token", token);
@@ -34292,10 +34292,10 @@ var StorageFileApi = class extends BaseApiClient {
34292
34292
  * - `objects` table permissions: `insert`
34293
34293
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34294
34294
  */
34295
- async createSignedUploadUrl(path7, options) {
34295
+ async createSignedUploadUrl(path8, options) {
34296
34296
  var _this4 = this;
34297
34297
  return _this4.handleOperation(async () => {
34298
- let _path = _this4._getFinalPath(path7);
34298
+ let _path = _this4._getFinalPath(path8);
34299
34299
  const headers = _objectSpread22({}, _this4.headers);
34300
34300
  if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
34301
34301
  const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
@@ -34304,7 +34304,7 @@ var StorageFileApi = class extends BaseApiClient {
34304
34304
  if (!token) throw new StorageError("No token returned by API");
34305
34305
  return {
34306
34306
  signedUrl: url2.toString(),
34307
- path: path7,
34307
+ path: path8,
34308
34308
  token
34309
34309
  };
34310
34310
  });
@@ -34364,8 +34364,8 @@ var StorageFileApi = class extends BaseApiClient {
34364
34364
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34365
34365
  * - For React Native, using either `Blob`, `File` or `FormData` does not work as intended. Update file using `ArrayBuffer` from base64 file data instead, see example below.
34366
34366
  */
34367
- async update(path7, fileBody, fileOptions) {
34368
- return this.uploadOrUpdate("PUT", path7, fileBody, fileOptions);
34367
+ async update(path8, fileBody, fileOptions) {
34368
+ return this.uploadOrUpdate("PUT", path8, fileBody, fileOptions);
34369
34369
  }
34370
34370
  /**
34371
34371
  * Moves an existing file to a new path in the same bucket.
@@ -34516,10 +34516,10 @@ var StorageFileApi = class extends BaseApiClient {
34516
34516
  * - `objects` table permissions: `select`
34517
34517
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34518
34518
  */
34519
- async createSignedUrl(path7, expiresIn, options) {
34519
+ async createSignedUrl(path8, expiresIn, options) {
34520
34520
  var _this8 = this;
34521
34521
  return _this8.handleOperation(async () => {
34522
- let _path = _this8._getFinalPath(path7);
34522
+ let _path = _this8._getFinalPath(path8);
34523
34523
  const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
34524
34524
  let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
34525
34525
  const query = new URLSearchParams();
@@ -34655,13 +34655,13 @@ var StorageFileApi = class extends BaseApiClient {
34655
34655
  * - `objects` table permissions: `select`
34656
34656
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34657
34657
  */
34658
- download(path7, options, parameters) {
34658
+ download(path8, options, parameters) {
34659
34659
  const renderPath = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0 ? "render/image/authenticated" : "object";
34660
34660
  const query = new URLSearchParams();
34661
34661
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
34662
34662
  if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
34663
34663
  const queryString = query.toString();
34664
- const _path = this._getFinalPath(path7);
34664
+ const _path = this._getFinalPath(path8);
34665
34665
  const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
34666
34666
  headers: this.headers,
34667
34667
  noResolveJson: true
@@ -34692,9 +34692,9 @@ var StorageFileApi = class extends BaseApiClient {
34692
34692
  * }
34693
34693
  * ```
34694
34694
  */
34695
- async info(path7) {
34695
+ async info(path8) {
34696
34696
  var _this10 = this;
34697
- const _path = _this10._getFinalPath(path7);
34697
+ const _path = _this10._getFinalPath(path8);
34698
34698
  return _this10.handleOperation(async () => {
34699
34699
  return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
34700
34700
  });
@@ -34715,9 +34715,9 @@ var StorageFileApi = class extends BaseApiClient {
34715
34715
  * .exists('folder/avatar1.png')
34716
34716
  * ```
34717
34717
  */
34718
- async exists(path7) {
34718
+ async exists(path8) {
34719
34719
  var _this11 = this;
34720
- const _path = _this11._getFinalPath(path7);
34720
+ const _path = _this11._getFinalPath(path8);
34721
34721
  try {
34722
34722
  await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
34723
34723
  return {
@@ -34796,8 +34796,8 @@ var StorageFileApi = class extends BaseApiClient {
34796
34796
  * - `objects` table permissions: none
34797
34797
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34798
34798
  */
34799
- getPublicUrl(path7, options) {
34800
- const _path = this._getFinalPath(path7);
34799
+ getPublicUrl(path8, options) {
34800
+ const _path = this._getFinalPath(path8);
34801
34801
  const query = new URLSearchParams();
34802
34802
  if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
34803
34803
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
@@ -34936,10 +34936,10 @@ var StorageFileApi = class extends BaseApiClient {
34936
34936
  * - `objects` table permissions: `select`
34937
34937
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34938
34938
  */
34939
- async list(path7, options, parameters) {
34939
+ async list(path8, options, parameters) {
34940
34940
  var _this13 = this;
34941
34941
  return _this13.handleOperation(async () => {
34942
- const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path7 || "" });
34942
+ const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path8 || "" });
34943
34943
  return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
34944
34944
  });
34945
34945
  }
@@ -35004,11 +35004,11 @@ var StorageFileApi = class extends BaseApiClient {
35004
35004
  if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
35005
35005
  return btoa(data);
35006
35006
  }
35007
- _getFinalPath(path7) {
35008
- return `${this.bucketId}/${path7.replace(/^\/+/, "")}`;
35007
+ _getFinalPath(path8) {
35008
+ return `${this.bucketId}/${path8.replace(/^\/+/, "")}`;
35009
35009
  }
35010
- _removeEmptyFolders(path7) {
35011
- return path7.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
35010
+ _removeEmptyFolders(path8) {
35011
+ return path8.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
35012
35012
  }
35013
35013
  /** Modifies the `query`, appending values the from `transform` */
35014
35014
  applyTransformOptsToQuery(query, transform2) {
@@ -40845,13 +40845,24 @@ var goalListAtRiskOutputSchema = external_exports.object({
40845
40845
  var goalSignalStateSchema = external_exports.enum(["ok", "risk", "crit", "pending"]);
40846
40846
  var goalStatusSourceSchema = external_exports.enum(["human", "agent", "rollup", "signal"]);
40847
40847
  var goalMeasurableRoleSchema = external_exports.enum(["drives_status", "informs"]);
40848
+ var goalDriverAnchorSchema = external_exports.object({
40849
+ // The closed period being anchored on (an ISO date / period label — free-form so a
40850
+ // caller can pass '2026-Q1' or '2026-06-30' as the books define the period).
40851
+ period_start: external_exports.string().min(1),
40852
+ // The value the goal's Signal should show for that closed period per the books.
40853
+ expected_value: external_exports.number()
40854
+ }).strict();
40848
40855
  var goalBindMeasurableInputSchema = external_exports.object({
40849
40856
  goal_id: uuidSchema8,
40850
40857
  measurable_id: uuidSchema8,
40851
40858
  // DER-1643: new operator bindings default to the conservative 'informs' role.
40852
40859
  // 'drives_status' is the deliberate leaf-only status driver (childless goal only,
40853
40860
  // one per goal) and stays human-confirmed.
40854
- role: goalMeasurableRoleSchema.default("informs")
40861
+ role: goalMeasurableRoleSchema.default("informs"),
40862
+ // DER-1126 (ADR-0009 R5): required when role is 'drives_status' — a binding may not
40863
+ // drive status without a second anchor. Optional here (only drives_status needs it);
40864
+ // the command rejects a driver bind that omits it, exactly like signal.bind_confirm.
40865
+ second_anchor: goalDriverAnchorSchema.optional()
40855
40866
  }).strict();
40856
40867
  var goalBindMeasurableOutputSchema = external_exports.object({
40857
40868
  goal_id: uuidSchema8,
@@ -40869,7 +40880,11 @@ var goalBindMeasurableOutputSchema = external_exports.object({
40869
40880
  // DER-1633: false for a fresh binding; true when this exact (goal, Signal) pair was
40870
40881
  // already bound — the command is an idempotent no-op (no new event, no duplicate
40871
40882
  // confirmation). Lets a caller distinguish "just bound" from "already bound".
40872
- already_bound: external_exports.boolean()
40883
+ already_bound: external_exports.boolean(),
40884
+ // DER-1126: the second anchor recorded when this binding was promoted to (or created
40885
+ // as) the status driver. Null for an 'informs' binding (no anchor) and for a legacy
40886
+ // driver bound before the anchor gate existed.
40887
+ driver_anchor: goalDriverAnchorSchema.nullable()
40873
40888
  }).strict();
40874
40889
  var goalUnbindMeasurableInputSchema = external_exports.object({
40875
40890
  goal_id: uuidSchema8,
@@ -40910,7 +40925,11 @@ var goalMeasurableBindingSchema = external_exports.object({
40910
40925
  computed_status: goalStatusSchema.nullable(),
40911
40926
  // The latest confirmed reading's value (null while awaiting one) — enough to render
40912
40927
  // "measured by <Signal>: <value> <unit>" instead of a bare "Not measured".
40913
- last_reading_value: external_exports.number().nullable()
40928
+ last_reading_value: external_exports.number().nullable(),
40929
+ // DER-1126: the second anchor recorded when this driver was confirmed (null for a
40930
+ // legacy driver bound before the anchor gate). Lets the driver view show "Anchored on
40931
+ // <period>: expected <value>".
40932
+ driver_anchor: goalDriverAnchorSchema.nullable()
40914
40933
  }).strict();
40915
40934
  var goalListMeasurablesInputSchema = external_exports.object({
40916
40935
  goal_id: uuidSchema8
@@ -40919,6 +40938,23 @@ var goalListMeasurablesOutputSchema = external_exports.object({
40919
40938
  goal_id: uuidSchema8,
40920
40939
  bindings: external_exports.array(goalMeasurableBindingSchema)
40921
40940
  }).strict();
40941
+ var goalCoreSchema = external_exports.object({
40942
+ id: uuidSchema8,
40943
+ title: external_exports.string(),
40944
+ kind: external_exports.enum(["objective", "cycle_goal", "company_objective", "seat_goal", "milestone"]),
40945
+ status: goalStatusSchema,
40946
+ status_source: goalStatusSourceSchema,
40947
+ progress_pct: external_exports.number().int().min(0).max(100).nullable(),
40948
+ seat_id: uuidSchema8.nullable(),
40949
+ parent_goal_id: uuidSchema8.nullable(),
40950
+ definition_of_done: external_exports.string()
40951
+ }).strict();
40952
+ var goalGetInputSchema = external_exports.object({
40953
+ goal_id: uuidSchema8
40954
+ }).strict();
40955
+ var goalGetOutputSchema = external_exports.object({
40956
+ goal: goalCoreSchema
40957
+ }).strict();
40922
40958
  var issueStatusSchema = external_exports.enum(["open", "diagnosing", "resolved", "dropped"]);
40923
40959
  var frictionListInputSchema = external_exports.object({
40924
40960
  status: external_exports.enum(["open", "diagnosing", "resolved", "dropped", "active", "all"]).optional()
@@ -40941,6 +40977,28 @@ var frictionIssueSummarySchema = external_exports.object({
40941
40977
  var frictionListOutputSchema = external_exports.object({
40942
40978
  issues: external_exports.array(frictionIssueSummarySchema)
40943
40979
  }).strict();
40980
+ var frictionIssueDetailSchema = frictionIssueSummarySchema.extend({
40981
+ raised_by_seat_type: external_exports.enum(["human", "agent", "hybrid"]),
40982
+ raised_by_seat_steward_name: external_exports.string().nullable(),
40983
+ origin_actor_kind: external_exports.enum(["user", "agent", "system"]).nullable(),
40984
+ origin_actor_id: uuidSchema8.nullable(),
40985
+ evidence: external_exports.unknown(),
40986
+ action_task_title: external_exports.string().nullable(),
40987
+ action_task_owner_seat_id: uuidSchema8.nullable(),
40988
+ action_task_owner_seat_name: external_exports.string().nullable(),
40989
+ action_task_due_on: external_exports.string().nullable(),
40990
+ decision_id: uuidSchema8.nullable(),
40991
+ decision_title: external_exports.string().nullable(),
40992
+ decision_summary: external_exports.string().nullable(),
40993
+ decided_by_name: external_exports.string().nullable(),
40994
+ updated_at: external_exports.string()
40995
+ }).strict();
40996
+ var frictionGetInputSchema = external_exports.object({
40997
+ issue_id: uuidSchema8
40998
+ }).strict();
40999
+ var frictionGetOutputSchema = external_exports.object({
41000
+ issue: frictionIssueDetailSchema
41001
+ }).strict();
40944
41002
  var frictionUpdateStatusInputSchema = external_exports.object({
40945
41003
  issue_id: uuidSchema8,
40946
41004
  status: external_exports.enum(["open", "diagnosing"])
@@ -40952,17 +41010,34 @@ var frictionUpdateStatusOutputSchema = external_exports.object({
40952
41010
  }).strict();
40953
41011
  var frictionResolveInputSchema = external_exports.object({
40954
41012
  issue_id: uuidSchema8,
41013
+ resolution_mode: external_exports.enum(["ids", "already_fixed"]).default("ids"),
40955
41014
  root_cause: external_exports.string().trim().min(1),
40956
- owner_seat_id: uuidSchema8,
40957
- task_title: external_exports.string().trim().min(1),
40958
- task_description: external_exports.string().trim().min(1),
40959
- done_by: dateOnlySchema,
41015
+ owner_seat_id: uuidSchema8.optional(),
41016
+ task_title: external_exports.string().trim().min(1).optional(),
41017
+ task_description: external_exports.string().trim().min(1).optional(),
41018
+ done_by: dateOnlySchema.optional(),
40960
41019
  resolving_seat_id: uuidSchema8.optional()
40961
- }).strict();
41020
+ }).strict().superRefine((value, ctx) => {
41021
+ if (value.resolution_mode !== "ids") {
41022
+ return;
41023
+ }
41024
+ if (value.owner_seat_id === void 0) {
41025
+ ctx.addIssue({ code: "custom", path: ["owner_seat_id"], message: 'Required for resolution_mode "ids".' });
41026
+ }
41027
+ if (value.task_title === void 0) {
41028
+ ctx.addIssue({ code: "custom", path: ["task_title"], message: 'Required for resolution_mode "ids".' });
41029
+ }
41030
+ if (value.task_description === void 0) {
41031
+ ctx.addIssue({ code: "custom", path: ["task_description"], message: 'Required for resolution_mode "ids".' });
41032
+ }
41033
+ if (value.done_by === void 0) {
41034
+ ctx.addIssue({ code: "custom", path: ["done_by"], message: 'Required for resolution_mode "ids".' });
41035
+ }
41036
+ });
40962
41037
  var frictionResolveOutputSchema = external_exports.object({
40963
41038
  issue_id: uuidSchema8,
40964
41039
  status: external_exports.literal("resolved"),
40965
- action_task_id: uuidSchema8,
41040
+ action_task_id: uuidSchema8.nullable(),
40966
41041
  decision_id: uuidSchema8
40967
41042
  }).strict();
40968
41043
  var frictionLinkTaskInputSchema = external_exports.object({
@@ -41565,6 +41640,18 @@ var compassShowMarkdownInputSchema = external_exports.object({}).strict();
41565
41640
  var charterShowMarkdownInputSchema = external_exports.object({
41566
41641
  seat_id: uuidSchema9
41567
41642
  }).strict();
41643
+ var charterShowMarkdownOutputSchema = external_exports.object({
41644
+ markdown: external_exports.string().min(1),
41645
+ charter_version_id: uuidSchema9.nullable(),
41646
+ seat_id: uuidSchema9,
41647
+ version: external_exports.number().int().nonnegative().nullable(),
41648
+ status: external_exports.string().nullable(),
41649
+ doc: external_exports.unknown().nullable(),
41650
+ dry_run: external_exports.object({
41651
+ status: external_exports.enum(["passed", "failed"]),
41652
+ summary: external_exports.string()
41653
+ }).strict().nullable()
41654
+ }).strict();
41568
41655
  var agentShowMarkdownInputSchema = external_exports.object({
41569
41656
  seat_id: uuidSchema9
41570
41657
  }).strict();
@@ -41799,6 +41886,27 @@ var confirmationRejectOutputSchema = external_exports.object({
41799
41886
  command_id: external_exports.string().min(1),
41800
41887
  rejected: external_exports.literal(true)
41801
41888
  }).strict();
41889
+ var confirmationListInputSchema = external_exports.object({
41890
+ limit: external_exports.number().int().min(1).max(200).optional()
41891
+ }).strict();
41892
+ var confirmationSummarySchema = external_exports.object({
41893
+ id: uuidSchema10,
41894
+ command_id: external_exports.string(),
41895
+ redacted_args: external_exports.unknown(),
41896
+ actor_kind: external_exports.enum(["user", "agent", "system"]),
41897
+ source: external_exports.string(),
41898
+ scope_kind: external_exports.enum(["tenant_admin", "tenant", "seat"]),
41899
+ seat_id: uuidSchema10.nullable(),
41900
+ seat_name: external_exports.string().nullable(),
41901
+ seat_type: external_exports.enum(["human", "agent", "hybrid"]).nullable(),
41902
+ risk_level: external_exports.enum(["normal", "sensitive", "dangerous"]),
41903
+ summary: external_exports.string(),
41904
+ expires_at: external_exports.string(),
41905
+ created_at: external_exports.string()
41906
+ }).strict();
41907
+ var confirmationListOutputSchema = external_exports.object({
41908
+ confirmations: external_exports.array(confirmationSummarySchema)
41909
+ }).strict();
41802
41910
  var credentialIngressInputSchema = external_exports.object({
41803
41911
  seat_id: uuidSchema10.optional(),
41804
41912
  provider: external_exports.string().trim().min(1),
@@ -43020,6 +43128,37 @@ var systemHealthOutputSchema = external_exports.object({
43020
43128
  findings: external_exports.array(systemHealthFindingSchema)
43021
43129
  }).strict();
43022
43130
 
43131
+ // ../../packages/protocol/src/baserow-filter-operators.ts
43132
+ var baserowFilterOperatorSchema = external_exports.enum([
43133
+ "equal",
43134
+ "not_equal",
43135
+ "higher_than",
43136
+ "higher_than_or_equal",
43137
+ "lower_than",
43138
+ "lower_than_or_equal",
43139
+ "contains",
43140
+ "contains_not",
43141
+ "empty",
43142
+ "not_empty"
43143
+ ]);
43144
+ var BASEROW_FILTER_OPERATORS = {
43145
+ equal: { requiresValue: true, label: "equals" },
43146
+ not_equal: { requiresValue: true, label: "does not equal" },
43147
+ higher_than: { requiresValue: true, label: "is greater than" },
43148
+ higher_than_or_equal: { requiresValue: true, label: "is greater than or equal to" },
43149
+ lower_than: { requiresValue: true, label: "is less than" },
43150
+ lower_than_or_equal: { requiresValue: true, label: "is less than or equal to" },
43151
+ contains: { requiresValue: true, label: "contains" },
43152
+ contains_not: { requiresValue: true, label: "does not contain" },
43153
+ empty: { requiresValue: false, label: "is empty" },
43154
+ not_empty: { requiresValue: false, label: "is not empty" }
43155
+ };
43156
+ var baserowFilterOperatorOptions = Object.keys(BASEROW_FILTER_OPERATORS).map((value) => ({
43157
+ value,
43158
+ label: BASEROW_FILTER_OPERATORS[value].label,
43159
+ requiresValue: BASEROW_FILTER_OPERATORS[value].requiresValue
43160
+ }));
43161
+
43023
43162
  // ../../packages/protocol/src/signal-source.ts
43024
43163
  var uuid9 = external_exports.string().uuid();
43025
43164
  var signalCadenceSchema = external_exports.enum(["daily", "weekly", "monthly", "quarterly", "annual"]);
@@ -43150,6 +43289,18 @@ var signalBindConfirmOutputSchema = external_exports.object({
43150
43289
  binding_status: external_exports.literal("active")
43151
43290
  }).strict();
43152
43291
 
43292
+ // ../../packages/protocol/src/baserow-filtered-count.ts
43293
+ var FIELD_KEY_PATTERN = /^field_\d+$/;
43294
+ var baserowFilteredCountInputSchema = external_exports.object({
43295
+ endpointHost: external_exports.string().min(1).max(255),
43296
+ // Accept string or number from callers (CLI/MCP/assistant), coerce to a bounded
43297
+ // positive integer before it is ever used as a URL path segment.
43298
+ tableId: external_exports.coerce.number().int().positive().max(2147483647),
43299
+ fieldKey: external_exports.string().regex(FIELD_KEY_PATTERN, "fieldKey must look like field_123"),
43300
+ operator: baserowFilterOperatorSchema,
43301
+ value: external_exports.string().max(200).optional()
43302
+ }).strict();
43303
+
43153
43304
  // ../../packages/protocol/src/signal-report.ts
43154
43305
  var uuidSchema14 = external_exports.string().uuid();
43155
43306
  var dateOnlySchema2 = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "expected YYYY-MM-DD");
@@ -44658,7 +44809,24 @@ var statusEventPayloadSchema = external_exports.object({
44658
44809
  // so every existing status writer (status.record, corrections, imports) is
44659
44810
  // unaffected.
44660
44811
  note: external_exports.string().min(1).max(2e3).optional(),
44661
- confidence: external_exports.enum(["low", "medium", "high"]).optional()
44812
+ confidence: external_exports.enum(["low", "medium", "high"]).optional(),
44813
+ // DER-1123: when this reading supersedes a materially-different same-period
44814
+ // reading (e.g. an integration pull replacing an agent-reported value), the
44815
+ // prior reading's provenance is captured here — append-only, so the audit
44816
+ // story survives even though the canonical row keeps a single value
44817
+ // (invariant #8, supersession not silent rewrite). Non-secret fields ONLY
44818
+ // (source kind, the numeric value, actor kind, a timestamp); never a vault
44819
+ // ref, token, or raw integration response.
44820
+ supersededFrom: external_exports.object({
44821
+ // The value's origin (agent report / human entry / integration pull).
44822
+ source: external_exports.enum(["agent", "human", "integration"]),
44823
+ value: external_exports.number().finite(),
44824
+ // The actor that last ESTABLISHED the superseded value — its reporter, or the
44825
+ // human who confirmed/corrected it. Optional (a reading may lack a backing
44826
+ // event). Never an actor id — only the coarse kind, so no PII.
44827
+ actorKind: external_exports.enum(["user", "agent", "system"]).optional(),
44828
+ at: external_exports.string().min(1)
44829
+ }).strict().optional()
44662
44830
  }).strict()).optional(),
44663
44831
  goals: external_exports.array(external_exports.object({
44664
44832
  id: uuidSchema16,
@@ -45445,7 +45613,7 @@ Treat AICOS as a coordinator over the operating system. It can become more usefu
45445
45613
  order: 20,
45446
45614
  title: "Responsibility Graph playbook",
45447
45615
  summary: "How to build a functions-first graph with seats, owners, Stewards, vacancies, and clean authority.",
45448
- version: "2026-07-10.2",
45616
+ version: "2026-07-10.3",
45449
45617
  public: true,
45450
45618
  audiences: ["human", "cli", "mcp", "in_app_agent"],
45451
45619
  stages: ["graph_design", "staffing"],
@@ -45518,6 +45686,8 @@ The graph is also where you land after onboarding \u2014 it is the mission contr
45518
45686
  - **Signal** \u2014 the worst measurable state per seat.
45519
45687
  - **Scoreboard** \u2014 direct work and direct cost on each seat, plus a separate team/subtree rollup for manager seats. The tenant summary counts direct agent work once, so a parent seat's team cost does not double-count the same run again. A seat whose direct cost is a clear outlier above the rest of the fleet is flagged as cost drift (labelled, not colour-only). Human seats and seats with no direct or team runs read calmly rather than showing a bare zero. For a small fleet the Scoreboard also leads with a two-tile summary \u2014 total work and total cost \u2014 framed as the single question that matters: is it earning its keep.
45520
45688
 
45689
+ Switching into Cascade never re-runs the chart layout \u2014 seat positions scale in place to fit taller goal-summary cards, and the connector layer scales to match, so nothing rearranges under you. A company-objectives card anchors above the root seat with the active cycle's objectives; selecting one traces and emphasizes its branch down through the connectors while the rest of the chart recedes. Selecting a seat's goal opens the same goal detail panel \`/cascade\` uses, as a right-hand drawer \u2014 breadcrumb ancestry and child-goal rows let you walk the branch without leaving the graph, and an **Open in Cascade** link round-trips the same cycle, branch, and goal selection to the \`/cascade\` page. Lens, objective, goal, seat, and cycle are all URL state, so a reload or a shared link reopens the same view.
45690
+
45521
45691
  The graph also carries a mission-control panel. It pulls the same operational state visible on the agent and setup-health surfaces: held actions that need steward decisions, failed runs, open Friction, pending Signal readings, stale agents, and setup recommendations. Each item can focus the affected seat on the canvas and links to the route where the operator can act.
45522
45692
 
45523
45693
  ## An agent seat's profile
@@ -46261,7 +46431,7 @@ External connectors are being rolled out provider by provider, conservatively (r
46261
46431
  order: 48,
46262
46432
  title: "CLI and MCP installation guide",
46263
46433
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
46264
- version: "2026-07-12.2",
46434
+ version: "2026-07-12.4",
46265
46435
  public: true,
46266
46436
  audiences: ["human", "cli", "mcp", "in_app_agent"],
46267
46437
  stages: ["company_setup", "staffing"],
@@ -46834,6 +47004,7 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
46834
47004
  | \`rost_skip_charter_draft\` | \`charter.skip\` | Mark a Charter draft as skipped. | Seat or tenant-admin | Call only when a human defers the seat. |
46835
47005
  | \`rost_apply_charter_seat_type_recommendation\` | \`charter.apply_seat_type_recommendation\` | Apply a Charter's seat-type recommendation. | Seat or tenant-admin | Call after reviewing the draft. |
46836
47006
  | \`rost_sign_charter_manifest\` | \`charter.sign_manifest\` | Request human confirmation for a permission manifest. | Seat or tenant-admin | Use after tool permissions are reviewed. |
47007
+ | \`rost_list_pending_confirmations\` | \`confirmation.list\` | List in-flight confirmations awaiting a human decision, scoped to the caller's authority (owner sees all; a member sees seats they occupy; an agent sees its own seat). | Seat or tenant-admin | Call with \`{}\`. |
46837
47008
  | \`rost_draft_compass\` | \`compass.draft\` | Create a Compass draft. | Tenant | Call with a concise Compass document. |
46838
47009
  | \`rost_update_compass_draft\` | \`compass.update_draft\` | Replace a Compass draft document. | Tenant | Call with the \`compass_version_id\` from \`compass.draft\` as \`draft_id\` and the updated document. |
46839
47010
  | \`rost_approve_compass_version\` | \`compass.approve_version\` | Activate a Compass version by supersession. | Tenant | Call after human review. |
@@ -46934,18 +47105,20 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
46934
47105
  | \`rost_import_rocks_from_a_ninety_rocks_export\` | \`cascade.import\` | Bulk-import Rocks as Cascade cycle goals under a company objective in the active cycle (from a ninety Rocks export). Owners resolve to seats; idempotent re-import skips a cycle goal already imported for the same objective + seat + title. | Tenant | Call with the parsed \`rocks\` array. |
46935
47106
  | \`rost_import_a_ninety_v_to_into_a_compass_draft\` | \`compass.import\` | AI-draft a Compass from a ninety V/TO (Core Values \u2192 principles, 10/3/1-Year \u2192 horizon goals, Rocks \u2192 cycle objectives), landed as a draft a human confirms by supersession \u2014 never an accepted Compass. Idempotent by document content (a re-upload of the same V/TO short-circuits without re-drafting). | Tenant | Call with the extracted V/TO \`text\` (and optional \`source_name\`). |
46936
47107
  | \`rost_list_cascade_goals\` | \`goal.list\` | List Cascade goals, optionally by cycle or seat. | Seat or tenant-admin | Call with \`{}\` or \`{"cycle_id":"<id>"}\`. |
47108
+ | \`rost_get_cascade_goal\` | \`goal.get\` | Get one goal's core detail and status by id. Seat-scoped callers may only read their own seat's goals. | Seat or tenant-admin | Call with \`{"goal_id":"<id>"}\`. |
46937
47109
  | \`rost_list_a_cascade_goals_signals\` | \`goal.list_measurables\` | List the goal's status-driving Signal binding (role drives_status; 0-or-1 per goal) with its current indicator and latest confirmed reading. Driver-only \u2014 informs bindings (direct indicators) are not returned. Read-only. | Seat or tenant-admin | Call with \`{"goal_id":"<id>"}\`. |
46938
47110
  | \`rost_create_cascade_goal\` | \`goal.create\` | Create a cycle goal under an objective. | Tenant | Call with cycle, seat, parent, title, definition of done. |
46939
47111
  | \`rost_update_cascade_goal\` | \`goal.update\` | Update a goal's title or definition of done. | Tenant | Call with \`goal_id\` and the changed fields. |
46940
47112
  | \`rost_set_cascade_goal_status\` | \`goal.set_status\` | Set a goal's status (on/off/done). | Seat or tenant-admin | Seats may set only their own goals. |
46941
47113
  | \`rost_set_cascade_goal_progress\` | \`goal.set_progress\` | Report a goal's quantified progress (0-100). An agent's progress is a proposal a human approves before the goal moves. | Seat or tenant-admin | Seats set only their own goals; expect confirmation. |
46942
- | \`rost_bind_a_signal_to_a_cascade_goal\` | \`goal.bind_measurable\` | Bind a measurable to a goal. Defaults to role \`informs\` \u2014 a conservative indicator allowed at any goal level (many per goal) that never changes status. Pass role \`drives_status\` to make it the goal's status driver (childless leaf only, one per goal). Human-gated. | Seat or tenant-admin | Call with \`{"goal_id":"<id>","measurable_id":"<id>"}\` (add \`"role":"drives_status"\` for the driver); expect confirmation. |
47114
+ | \`rost_bind_a_signal_to_a_cascade_goal\` | \`goal.bind_measurable\` | Bind a measurable to a goal. Defaults to role \`informs\` \u2014 a conservative indicator allowed at any goal level (many per goal) that never changes status. Pass role \`drives_status\` to make it the goal's status driver (childless leaf only, one per goal); a driver requires a \`second_anchor\` (a prior closed period + expected value confirming the Signal matched the books) and promotes an existing \`informs\` binding of the same Signal. Human-gated. | Seat or tenant-admin | Call with \`{"goal_id":"<id>","measurable_id":"<id>"}\` (add \`"role":"drives_status","second_anchor":{"period_start":"2026-Q1","expected_value":0}\` for the driver); expect confirmation. |
46943
47115
  | \`rost_unbind_a_signal_from_a_cascade_goal\` | \`goal.unbind_measurable\` | Remove a goal\u2194measurable binding (role defaults to \`drives_status\`; pass \`"role":"informs"\` to remove an indicator). | Seat or tenant-admin | Call with \`{"goal_id":"<id>","measurable_id":"<id>"}\`. |
46944
47116
  | \`rost_toggle_signal_driven_auto_status\` | \`goal.set_auto_status\` | Per-goal steward opt-in (default OFF) that lets the bound measurable's latest confirmed reading compute the goal's status. Enabling applies the current signal immediately. Human-gated. | Seat or tenant-admin | Call with \`{"goal_id":"<id>","enabled":true}\`; expect confirmation. |
46945
47117
  | \`rost_list_at_risk_cascade_goals\` | \`goal.list_at_risk\` | List goals projected off pace (at-risk or projected-miss) with days behind pace. Read-only; status-only goals excluded. | Tenant | Call with \`{}\`. |
46946
47118
  | \`rost_reparent_cascade_goal\` | \`goal.reparent\` | Move a goal under a new parent. | Tenant | Authority change; expect confirmation. |
46947
47119
  | \`rost_drop_cascade_goal\` | \`goal.drop\` | Drop (retire) a goal, retained for audit. | Tenant | Expect confirmation. |
46948
47120
  | \`rost_list_friction_issues\` | \`friction.list\` | List Friction issues ranked by impact and severity. | Seat or tenant-admin | Call with \`{}\` or \`{"status":"active"}\`. |
47121
+ | \`rost_get_friction_issue\` | \`friction.get\` | Get full detail for one Friction issue by id. | Seat or tenant-admin | Call with \`{"issue_id":"<id>"}\`. |
46949
47122
  | \`rost_assist_friction_issue\` | \`friction.assist\` | Advisory IDS assist: the most-similar prior resolved issues with their root causes and decisions, a recurring-pattern flag, and recommended options. Read-only \u2014 it never resolves the issue. | Tenant | Call with \`{"issue_id":"<id>"}\`. |
46950
47123
  | \`rost_update_friction_issue_status\` | \`friction.update_status\` | Move a non-terminal issue between open and diagnosing. | Seat or tenant-admin | Call with \`issue_id\` and \`status\`. |
46951
47124
  | \`rost_resolve_friction_issue\` | \`friction.resolve\` | Resolve an issue with a root cause and remediation task. | Tenant | Human decision; expect confirmation. |
@@ -47507,7 +47680,7 @@ Retrieve the narrowest relevant guide before making a setup recommendation. Pref
47507
47680
  order: 60,
47508
47681
  title: "Cascade guide",
47509
47682
  summary: "How to connect company goals to seat-level work without turning {{brand}} into a project-management tool.",
47510
- version: "2026-07-12.1",
47683
+ version: "2026-07-12.2",
47511
47684
  public: true,
47512
47685
  audiences: ["human", "cli", "mcp", "in_app_agent"],
47513
47686
  stages: ["operating_rhythm"],
@@ -47516,6 +47689,7 @@ Retrieve the narrowest relevant guide before making a setup recommendation. Pref
47516
47689
  "compass.approve_version",
47517
47690
  "onboarding.finish",
47518
47691
  "goal.list",
47692
+ "goal.get",
47519
47693
  "goal.create",
47520
47694
  "goal.update",
47521
47695
  "goal.set_status",
@@ -47585,7 +47759,7 @@ On human and function seat pages, goal visibility includes both direct goals and
47585
47759
 
47586
47760
  ## Drive a goal's status from a Signal
47587
47761
 
47588
- A Signal can be linked to a goal for visibility, or set to compute its status. Bind a Signal with \`goal.bind_measurable\`. By default the binding is **informative** (role \`informs\`): it appears as a direct indicator on the goal, is allowed at any level (company objective, parent, or leaf), can be one of many on the same goal, and never changes the goal's status. To let a Signal **drive** a leaf goal's status instead, bind it with \`--role drives_status\` (the goal must be a childless seat goal or milestone \u2014 a company objective or a goal with children rolls up from its children, so a driver there is rejected). A goal can have at most one driving Signal, and choosing a driver is a deliberate, human-gated act.
47762
+ A Signal can be linked to a goal for visibility, or set to compute its status. Bind a Signal with \`goal.bind_measurable\`. By default the binding is **informative** (role \`informs\`): it appears as a direct indicator on the goal, is allowed at any level (company objective, parent, or leaf), can be one of many on the same goal, and never changes the goal's status. To let a Signal **drive** a leaf goal's status instead, bind it with \`--role drives_status\` (the goal must be a childless seat goal or milestone \u2014 a company objective or a goal with children rolls up from its children, so a driver there is rejected). A goal can have at most one driving Signal, and choosing a driver is a deliberate, human-gated act. Promoting a Signal to the status driver requires a **second anchor**: a prior closed period plus the value that period's books show (\`second_anchor: { period_start, expected_value }\`). The instantaneous reading is not enough \u2014 the anchor proves the Signal matched reality at least once before it may automatically flip a goal, and it is recorded durably with the confirmation. Confirming a driver over an existing informative binding of the same Signal promotes that binding in place.
47589
47763
 
47590
47764
  A \`drives_status\` binding starts as an indicator only: nothing changes until a steward opts in with \`goal.set_auto_status --enabled true\`. Once opted in, the goal's status is computed from the measurable's latest confirmed reading against its target and direction \u2014 on target reads on-track, below target reads off-track. Only confirmed readings drive status; an agent's draft reading never auto-flips a goal. A human who sets the status by hand pins it, and that pin wins over the computed value until they change it or re-run \`goal.set_auto_status\`. Remove a binding with \`goal.unbind_measurable\`; the goal keeps its last status. Until a binding is opted in, an off-track Signal never raises an at-risk alert on its own.
47591
47765
 
@@ -47613,7 +47787,7 @@ Agents can suggest commitments and report progress. They should not create a new
47613
47787
  order: 61,
47614
47788
  title: "Signal guide",
47615
47789
  summary: "How to define and read measurables so the company runs on evidence instead of status theater.",
47616
- version: "2026-07-08.1",
47790
+ version: "2026-07-12.1",
47617
47791
  public: true,
47618
47792
  audiences: ["human", "cli", "mcp", "in_app_agent"],
47619
47793
  stages: ["operating_rhythm"],
@@ -47701,7 +47875,7 @@ Once a measurable's number lives in a connected system, you can teach {{brand}}
47701
47875
  - \`signal.bind\` persists the validated recipe as an inert \`draft\` binding. It requires the \`preview_token\` from a preview of the same recipe, so nothing is bound that was not first seen working.
47702
47876
  - \`signal.bind_confirm\` is human-only. It activates the binding (\`draft -> active\`); an agent that calls it produces a pending confirmation instead. Only after a human confirms does the scheduled pull begin writing confirmed readings.
47703
47877
 
47704
- Discovery always returns a \`recipe_presets\` array; it is empty when {{brand}} does not recognize a safe source type. Today the proven self-filling path is Baserow REST through \`integration.connect_rest\`: a tenant admin creates or rotates the Baserow connection through the command/vault path, then Signal runs \`integration.discover -> signal.preview -> signal.bind -> signal.bind_confirm\` against that connected source. Baserow row-field recipes and custom filtered-count recipes are supported when the human supplies the real table, field, filter, host, and value path. A first-class filtered-count preset is still a follow-up; do not imply every filtered count appears as a one-click preset. Google Sheets Signal pulls are not live yet: Google uses OAuth JSON credentials and remains withheld from REST presets until the pull executor extracts and refreshes the Google access token instead of treating the stored credential blob as a bearer token. A preset includes a label, cadence, required metadata, a draft \`rest_for_signals\` recipe, and the host allowlist to preview with. The human still fills in the real Baserow table/row/field or exact REST URL and value path before running \`signal.preview\`; presets are starter recipes, not secret stores.
47878
+ Discovery always returns a \`recipe_presets\` array; it is empty when {{brand}} does not recognize a safe source type. Today the proven self-filling path is Baserow REST through \`integration.connect_rest\`: a tenant admin creates or rotates the Baserow connection through the command/vault path, then Signal runs \`integration.discover -> signal.preview -> signal.bind -> signal.bind_confirm\` against that connected source. Baserow discovery now offers two first-class presets: \`baserow.row-field\` (read one scalar field from a row) and \`baserow.filtered-count\` (count rows matching a condition). The filtered-count builder takes a table id, field key, comparison operator, and comparison value and assembles a validated \`rest_for_signals\` recipe whose \`value_path\` is the response \`count\`; the field key and operator are allowlisted (they form the query-parameter name) and the comparison value is percent-encoded, so nothing the operator types can inject into the URL. The human still supplies the real table, field, operator, value, and host \u2014 presets and the builder are starter recipes, not secret stores. Google Sheets Signal pulls are not live yet: Google uses OAuth JSON credentials and remains withheld from REST presets until the pull executor extracts and refreshes the Google access token instead of treating the stored credential blob as a bearer token. A preset includes a label, cadence, required metadata, a draft \`rest_for_signals\` recipe, and the host allowlist to preview with. The human still fills in the real Baserow table/row/field or exact REST URL and value path before running \`signal.preview\`; presets are starter recipes, not secret stores.
47705
47879
 
47706
47880
  Recipes are validated data, never code: a read-only GET, a closed set of non-secret headers, and a restricted scalar path to the value. Every fetch passes one SSRF guard (HTTPS only, a steward-confirmed host allowlist set at bind, and a runtime check that the host does not resolve to a private address). The preset output never contains \`nango://\` handles, vault refs, tokens, or static credential headers. Deterministic pulls write \`integration\`-sourced readings on the measurable's cadence.
47707
47881
 
@@ -47735,6 +47909,7 @@ Agents may record readings when the Charter allows it. Agent-reported readings s
47735
47909
  "compass.answer_gap",
47736
47910
  "friction.file_issue",
47737
47911
  "friction.list",
47912
+ "friction.get",
47738
47913
  "friction.assist",
47739
47914
  "friction.update_status",
47740
47915
  "friction.resolve",
@@ -47881,7 +48056,7 @@ Keep agent scope tight at first. Approve more autonomy only after evidence. Use
47881
48056
  public: true,
47882
48057
  audiences: ["human", "cli", "mcp", "in_app_agent"],
47883
48058
  stages: ["graph_design", "charter_design", "staffing", "operating_rhythm"],
47884
- relatedCommandIds: ["confirmation.approve", "confirmation.reject", "charter.approve", "agent.go_live", "credential.ingress", "mcp_token.create", "staffing.assign_user", "goal.reparent", "friction.resolve"],
48059
+ relatedCommandIds: ["confirmation.approve", "confirmation.list", "confirmation.reject", "charter.approve", "agent.go_live", "credential.ingress", "mcp_token.create", "staffing.assign_user", "goal.reparent", "friction.resolve"],
47885
48060
  legal: { publicRisk: "low", notes: ["{{brand}}-native human-gate guidance."] },
47886
48061
  sources: [
47887
48062
  {
@@ -50026,6 +50201,11 @@ function helpText() {
50026
50201
  ].join("\n");
50027
50202
  }
50028
50203
 
50204
+ // src/update-check.ts
50205
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
50206
+ import { homedir as homedir2 } from "node:os";
50207
+ import path2 from "node:path";
50208
+
50029
50209
  // src/cli-version.ts
50030
50210
  import { readFile as readFile2 } from "node:fs/promises";
50031
50211
  import { dirname as dirname2, resolve } from "node:path";
@@ -50047,15 +50227,23 @@ async function readCliVersion() {
50047
50227
  var latestPackageSchema = external_exports.object({
50048
50228
  version: external_exports.string()
50049
50229
  });
50050
- async function maybePrintUpgradeNudge(io) {
50051
- if (process.env.CI || process.env.VITEST || process.env.CLI_SKIP_UPDATE_CHECK === "1") {
50230
+ var updateCheckStateSchema = external_exports.object({ lastNaggedOn: external_exports.string() }).strict();
50231
+ async function maybePrintUpgradeNudge(io, options = {}) {
50232
+ if (!options.skipEnvGuard && (process.env.CI || process.env.VITEST || process.env.CLI_SKIP_UPDATE_CHECK === "1")) {
50233
+ return;
50234
+ }
50235
+ const statePath = options.statePath ?? defaultUpdateCheckStatePath();
50236
+ const today = toUtcDateOnly(options.now ?? /* @__PURE__ */ new Date());
50237
+ const state = await readUpdateCheckState(statePath);
50238
+ if (state?.lastNaggedOn === today) {
50052
50239
  return;
50053
50240
  }
50054
50241
  const currentVersion = await readCliVersion();
50055
50242
  if (!currentVersion || currentVersion === "0.0.0") {
50056
50243
  return;
50057
50244
  }
50058
- const latestVersion = await fetchLatestVersion(cliBrand.packageName);
50245
+ const fetchLatest = options.fetchLatest ?? fetchLatestVersion;
50246
+ const latestVersion = await fetchLatest(cliBrand.packageName);
50059
50247
  if (!latestVersion || !isSemverBehind(currentVersion, latestVersion)) {
50060
50248
  return;
50061
50249
  }
@@ -50063,6 +50251,28 @@ async function maybePrintUpgradeNudge(io) {
50063
50251
  `A newer ${cliBrand.binName} CLI is available: ${currentVersion} -> ${latestVersion}. Upgrade with npm i -g ${cliBrand.packageName}@latest.
50064
50252
  `
50065
50253
  );
50254
+ await writeUpdateCheckStateBestEffort(statePath, { lastNaggedOn: today });
50255
+ }
50256
+ function defaultUpdateCheckStatePath() {
50257
+ return path2.join(homedir2(), ".config", cliBrand.binName, "update-check.json");
50258
+ }
50259
+ function toUtcDateOnly(date5) {
50260
+ return date5.toISOString().slice(0, 10);
50261
+ }
50262
+ async function readUpdateCheckState(statePath) {
50263
+ try {
50264
+ return updateCheckStateSchema.parse(JSON.parse(await readFile3(statePath, "utf8")));
50265
+ } catch {
50266
+ return null;
50267
+ }
50268
+ }
50269
+ async function writeUpdateCheckStateBestEffort(statePath, state) {
50270
+ try {
50271
+ await mkdir2(path2.dirname(statePath), { recursive: true, mode: 448 });
50272
+ await writeFile2(statePath, `${JSON.stringify(state)}
50273
+ `, { mode: 384 });
50274
+ } catch {
50275
+ }
50066
50276
  }
50067
50277
  async function fetchLatestVersion(packageName) {
50068
50278
  const controller = new AbortController();
@@ -50168,6 +50378,7 @@ var COMMAND_MANIFEST = [
50168
50378
  { "id": "compass.show_markdown", "namespace": "compass", "action": "show_markdown", "title": "Show Compass as markdown", "description": "Compose the current Compass and its open gaps into a clean, human-skimmable markdown card for review.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [], "hasComplexInput": false, "help": "Render the current Compass and its open gaps as a clean markdown card to show your human a quick review." },
50169
50379
  { "id": "compass.update_draft", "namespace": "compass", "action": "update_draft", "title": "Update Compass draft", "description": "Replace a draft Compass document.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "draft_id", "flag": "draft-id", "type": "string", "required": true }], "hasComplexInput": true, "help": "Pass the compass_version_id returned by compass.draft as draft_id; update_draft replaces only an unpublished draft in this tenant." },
50170
50380
  { "id": "confirmation.approve", "namespace": "confirmation", "action": "approve", "title": "Approve pending confirmation", "description": "Approve an in-flight confirmation and execute the original command as the approving human.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "confirmation_id", "flag": "confirmation-id", "type": "string", "required": true }, { "name": "reviewed", "flag": "reviewed", "type": "boolean", "required": false }], "hasComplexInput": false, "help": "Approve pending confirmations only when the requested durable change is clear to the human owner." },
50381
+ { "id": "confirmation.list", "namespace": "confirmation", "action": "list", "title": "List pending confirmations", "description": "List in-flight confirmations awaiting a human decision, scoped to the caller's authority (owner sees all; a member sees seats they occupy; an agent sees its own seat).", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "limit", "flag": "limit", "type": "integer", "required": false }], "hasComplexInput": false, "help": "List pending confirmations awaiting a human decision before approving or rejecting them." },
50171
50382
  { "id": "confirmation.reject", "namespace": "confirmation", "action": "reject", "title": "Reject pending confirmation", "description": "Reject an in-flight confirmation without executing the original command.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "confirmation_id", "flag": "confirmation-id", "type": "string", "required": true }, { "name": "reason", "flag": "reason", "type": "string", "required": false }], "hasComplexInput": false, "help": "Reject pending confirmations when authority, evidence, or human intent is unclear." },
50172
50383
  { "id": "credential.ingress", "namespace": "credential", "action": "ingress", "title": "Store credential", "description": "Store a secret through the vault and persist only the credential vault reference.", "requiredScope": "seat", "confirmation": "credential_flow", "exposeOverMcp": false, "fields": [{ "name": "seat_id", "flag": "seat-id", "type": "string", "required": false }, { "name": "provider", "flag": "provider", "type": "string", "required": true }, { "name": "scope_description", "flag": "scope-description", "type": "string", "required": true }, { "name": "secret_name", "flag": "secret-name", "type": "string", "required": true }], "hasComplexInput": false, "secretBlocked": true, "help": "Ingress credentials only through vault-backed flows; never place raw secrets in prompts, logs, or docs." },
50173
50384
  { "id": "deliverable.accept", "namespace": "deliverable", "action": "accept", "title": "Accept deliverable value", "description": "Record a human-confirmed accepted value (USD) on a deliverable. Humans decide; agents cannot self-accept. Re-accepting records a correction as a new event; prior events are never mutated.", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "deliverable_id", "flag": "deliverable-id", "type": "string", "required": true }, { "name": "impact_value_usd", "flag": "impact-value-usd", "type": "number", "required": true }], "hasComplexInput": false, "help": "Record a human-confirmed accepted value (USD) on a deliverable; owner-only and human-gated. Humans decide \u2014 agents cannot self-accept. Re-accepting records a correction as a new event." },
@@ -50185,14 +50396,16 @@ var COMMAND_MANIFEST = [
50185
50396
  { "id": "escalation.resolve", "namespace": "escalation", "action": "resolve", "title": "Resolve escalation", "description": "Approve an open escalation (or convert it to Friction). Writes a human decision; agents cannot resolve.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": false, "fields": [{ "name": "id", "flag": "id", "type": "string", "required": true }, { "name": "action", "flag": "action", "type": "enum", "required": false, "enumValues": ["approve", "convert_to_issue"] }, { "name": "rationale", "flag": "rationale", "type": "string", "required": false }], "hasComplexInput": false, "help": "Resolve an escalation as a human Steward; the decision records decided_by as the approving human. Agents cannot resolve." },
50186
50397
  { "id": "friction.assist", "namespace": "friction", "action": "assist", "title": "Assist Friction issue", "description": "Advisory IDS assist for an issue: the most-similar prior resolved issues with their root causes and decisions, a recurring-pattern flag, and recommended options. Read-only \u2014 it never resolves the issue.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "issue_id", "flag": "issue-id", "type": "string", "required": true }, { "name": "limit", "flag": "limit", "type": "integer", "required": false }], "hasComplexInput": false, "help": "Advisory IDS assist: the most-similar prior resolved issues with their root causes and decisions, plus a recurring-pattern flag. Read-only \u2014 it never resolves the issue." },
50187
50398
  { "id": "friction.file_issue", "namespace": "friction", "action": "file_issue", "title": "File Friction issue", "description": "Emit an issue event and open a Friction issue raised by the acting seat.", "requiredScope": "seat", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "summary", "flag": "summary", "type": "string", "required": true }, { "name": "blocking_goal_id", "flag": "blocking-goal-id", "type": "string", "required": false }, { "name": "broken_measurable_id", "flag": "broken-measurable-id", "type": "string", "required": false }, { "name": "severity", "flag": "severity", "type": "enum", "required": true, "enumValues": ["low", "med", "high", "critical"] }], "hasComplexInput": true, "help": "File a Friction issue with evidence when a goal or measurable is blocked or broken." },
50399
+ { "id": "friction.get", "namespace": "friction", "action": "get", "title": "Get Friction issue", "description": "Get full detail for one Friction issue by id.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "issue_id", "flag": "issue-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Get full detail for one Friction issue by id, including its root cause, action task, and resolution decision when present." },
50188
50400
  { "id": "friction.import", "namespace": "friction", "action": "import", "title": "Import Friction issues from a ninety Issues List", "description": "Bulk-import a ninety Issues List export into Friction. Owners are seat names resolved to seats; each issue is raised by the resolved seat and attributed to the importing human. Idempotent \u2014 re-importing the same export is a no-op, and an existing open issue with the same seat + summary is skipped.", "requiredScope": "tenant_admin", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "dry_run", "flag": "dry-run", "type": "boolean", "required": false }], "hasComplexInput": true, "help": "Import a ninety Issues List export into Friction; owners resolve to seats, idempotent by file + seat/summary." },
50189
50401
  { "id": "friction.link_task", "namespace": "friction", "action": "link_task", "title": "Link task to Friction issue", "description": "Attach an existing tenant task as the action task for a non-terminal issue. Both ids must belong to the tenant.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "issue_id", "flag": "issue-id", "type": "string", "required": true }, { "name": "task_id", "flag": "task-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Attach an existing task as the action task for a non-terminal issue." },
50190
50402
  { "id": "friction.list", "namespace": "friction", "action": "list", "title": "List Friction issues", "description": "List Friction issues ranked by status, impact, and severity. Defaults to active (open + diagnosing).", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "status", "flag": "status", "type": "enum", "required": false, "enumValues": ["open", "diagnosing", "resolved", "dropped", "active", "all"] }], "hasComplexInput": false, "help": "List Friction issues ranked by impact and severity to find the issue id before updating or resolving it." },
50191
- { "id": "friction.resolve", "namespace": "friction", "action": "resolve", "title": "Resolve Friction issue", "description": "Resolve an issue with a root cause and remediation task. This records a human decision (decided_by) and creates the follow-up task in one transaction.", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "issue_id", "flag": "issue-id", "type": "string", "required": true }, { "name": "root_cause", "flag": "root-cause", "type": "string", "required": true }, { "name": "owner_seat_id", "flag": "owner-seat-id", "type": "string", "required": true }, { "name": "task_title", "flag": "task-title", "type": "string", "required": true }, { "name": "task_description", "flag": "task-description", "type": "string", "required": true }, { "name": "done_by", "flag": "done-by", "type": "string", "required": true }, { "name": "resolving_seat_id", "flag": "resolving-seat-id", "type": "string", "required": false }], "hasComplexInput": false, "help": "Resolve an issue with a root cause and remediation task; resolution is a human decision." },
50403
+ { "id": "friction.resolve", "namespace": "friction", "action": "resolve", "title": "Resolve Friction issue", "description": 'Resolve an issue with a root cause. In the default "ids" resolution_mode this also creates a remediation follow-up task; "already_fixed" records only the root cause (no follow-up task). Always records a human decision (decided_by) in the same transaction.', "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "issue_id", "flag": "issue-id", "type": "string", "required": true }, { "name": "resolution_mode", "flag": "resolution-mode", "type": "enum", "required": false, "enumValues": ["ids", "already_fixed"] }, { "name": "root_cause", "flag": "root-cause", "type": "string", "required": true }, { "name": "owner_seat_id", "flag": "owner-seat-id", "type": "string", "required": false }, { "name": "task_title", "flag": "task-title", "type": "string", "required": false }, { "name": "task_description", "flag": "task-description", "type": "string", "required": false }, { "name": "done_by", "flag": "done-by", "type": "string", "required": false }, { "name": "resolving_seat_id", "flag": "resolving-seat-id", "type": "string", "required": false }], "hasComplexInput": false, "help": "Resolve an issue with a root cause and remediation task; resolution is a human decision." },
50192
50404
  { "id": "friction.update_status", "namespace": "friction", "action": "update_status", "title": "Update Friction issue status", "description": "Move a non-terminal issue between open and diagnosing. Terminal issues (resolved/dropped) cannot be reopened here.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "issue_id", "flag": "issue-id", "type": "string", "required": true }, { "name": "status", "flag": "status", "type": "enum", "required": true, "enumValues": ["open", "diagnosing"] }], "hasComplexInput": false, "help": "Move a non-terminal issue between open and diagnosing while it is being worked." },
50193
- { "id": "goal.bind_measurable", "namespace": "goal", "action": "bind_measurable", "title": "Bind a Signal to a Cascade goal", "description": "Bind a measurable to a goal. Role defaults to 'informs' \u2014 a conservative informative indicator allowed at any goal level (many per goal) that never changes goal status. Role 'drives_status' makes it the goal's status driver: allowed only on a childless seat goal or milestone, at most one per goal, and auto-status starts OFF until a steward opts in with goal.set_auto_status. Binding is human-gated.", "requiredScope": "seat", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }, { "name": "measurable_id", "flag": "measurable-id", "type": "string", "required": true }, { "name": "role", "flag": "role", "type": "enum", "required": false, "enumValues": ["drives_status", "informs"] }], "hasComplexInput": false, "help": "Bind a Signal to a goal. Defaults to role informs (a conservative indicator at any level that never changes status); pass role drives_status to make it a childless leaf goal's status driver (auto-status starts OFF until a steward opts in)." },
50405
+ { "id": "goal.bind_measurable", "namespace": "goal", "action": "bind_measurable", "title": "Bind a Signal to a Cascade goal", "description": "Bind a measurable to a goal. Role defaults to 'informs' \u2014 a conservative informative indicator allowed at any goal level (many per goal) that never changes goal status. Role 'drives_status' makes it the goal's status driver: allowed only on a childless seat goal or milestone, at most one per goal, requires a second_anchor (a prior closed period + expected value confirming the Signal matched the books), and auto-status starts OFF until a steward opts in with goal.set_auto_status. Binding a driver over an existing informs binding of the same Signal promotes it. Binding is human-gated.", "requiredScope": "seat", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }, { "name": "measurable_id", "flag": "measurable-id", "type": "string", "required": true }, { "name": "role", "flag": "role", "type": "enum", "required": false, "enumValues": ["drives_status", "informs"] }], "hasComplexInput": true, "help": "Bind a Signal to a goal. Defaults to role informs (a conservative indicator at any level that never changes status); pass role drives_status with a second_anchor (a prior closed period + expected value confirming the Signal matched the books) to make it a childless leaf goal's status driver \u2014 this promotes an existing informs binding (auto-status starts OFF until a steward opts in)." },
50194
50406
  { "id": "goal.create", "namespace": "goal", "action": "create", "title": "Create Cascade goal", "description": "Create a cycle goal under an existing objective. The parent chain must terminate at a Compass objective in the same cycle (enforced server-side).", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "cycle_id", "flag": "cycle-id", "type": "string", "required": true }, { "name": "seat_id", "flag": "seat-id", "type": "string", "required": true }, { "name": "parent_goal_id", "flag": "parent-goal-id", "type": "string", "required": true }, { "name": "title", "flag": "title", "type": "string", "required": true }, { "name": "definition_of_done", "flag": "definition-of-done", "type": "string", "required": true }, { "name": "status", "flag": "status", "type": "enum", "required": false, "enumValues": ["on", "off"] }], "hasComplexInput": false, "help": "Create cycle goals under an existing objective; the parent chain must terminate at a Compass objective." },
50195
50407
  { "id": "goal.drop", "namespace": "goal", "action": "drop", "title": "Drop Cascade goal", "description": "Drop (retire) a goal by setting status to dropped. Dropping retires the commitment, so it is human-gated. The row is retained for audit (never deleted).", "requiredScope": "tenant_admin", "confirmation": "human_required", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Drop a goal to retire a commitment; the goal is retained for audit and dropping is human-gated." },
50408
+ { "id": "goal.get", "namespace": "goal", "action": "get", "title": "Get Cascade goal", "description": "Get one goal's core detail and status by id. Seat-scoped callers may only read their own seat's goals.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "Get one goal's core detail and status by id; seat tokens may only read their own goals." },
50196
50409
  { "id": "goal.list", "namespace": "goal", "action": "list", "title": "List Cascade goals", "description": "List goals in the tenant, optionally filtered by cycle or seat. Seat-scoped callers see only their own seat's goals.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "cycle_id", "flag": "cycle-id", "type": "string", "required": false }, { "name": "seat_id", "flag": "seat-id", "type": "string", "required": false }], "hasComplexInput": false, "help": "List Cascade goals to find goal ids, parentage, and seat ownership before mutating the tree." },
50197
50410
  { "id": "goal.list_at_risk", "namespace": "goal", "action": "list_at_risk", "title": "List at-risk Cascade goals", "description": "List goals projected to miss their cycle pace (at-risk or projected-miss), with how many days behind pace. Read-only; status-only goals (no progress) are excluded.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [], "hasComplexInput": false, "help": "List goals projected off pace (at-risk or projected-miss), with how many days behind pace. Read-only; status-only goals are excluded." },
50198
50411
  { "id": "goal.list_measurables", "namespace": "goal", "action": "list_measurables", "title": "List a Cascade goal's Signals", "description": "List the status-driver Signal bound to a Cascade goal (role drives_status), with its current signal indicator and latest confirmed reading. Read-only. Informative (informs) bindings appear in the goal-detail panel's Direct indicators, not here.", "requiredScope": "tenant", "confirmation": "none", "exposeOverMcp": true, "fields": [{ "name": "goal_id", "flag": "goal-id", "type": "string", "required": true }], "hasComplexInput": false, "help": "List the Signals bound to a goal (role drives_status) with each one's current indicator and latest confirmed reading." },
@@ -50497,6 +50710,19 @@ function execute(context, parsed, commandId, body, format) {
50497
50710
  }
50498
50711
 
50499
50712
  // src/generator/flags.ts
50713
+ var SEAT_FLAG_FILLS_REQUIRED_SEAT_ID = /* @__PURE__ */ new Set([
50714
+ "agent.status",
50715
+ "agent.show_markdown",
50716
+ "agent.get_run",
50717
+ "agent.list_runs",
50718
+ "agent.list_tool_calls",
50719
+ "charter.show_markdown",
50720
+ "seat.get",
50721
+ "seat.decommission_preview",
50722
+ "deliverable.get",
50723
+ "deliverable.list",
50724
+ "skill.check_dependencies"
50725
+ ]);
50500
50726
  function booleanFlagsFor(entry, overrides = {}) {
50501
50727
  const booleanFields = new Set(entry.fields.filter((field3) => field3.type === "boolean").map((field3) => field3.name));
50502
50728
  const flags = /* @__PURE__ */ new Set(["yes", "y"]);
@@ -50586,6 +50812,9 @@ function buildBodyFromFlags(entry, parsed, positional, overrides = {}) {
50586
50812
  if (raw === void 0 && positional !== void 0 && overrides.primaryPositional === field3.name) {
50587
50813
  raw = positional;
50588
50814
  }
50815
+ if (raw === void 0 && field3.name === "seat_id" && (!field3.required || SEAT_FLAG_FILLS_REQUIRED_SEAT_ID.has(entry.id))) {
50816
+ raw = parsed.seat;
50817
+ }
50589
50818
  if (raw === void 0 || raw.length === 0) {
50590
50819
  continue;
50591
50820
  }
@@ -50990,6 +51219,15 @@ function renderGoalStatus(output) {
50990
51219
  const record2 = asRecord(output);
50991
51220
  return `Goal ${field(record2, "goal_id")} status set to ${field(record2, "status")}.`;
50992
51221
  }
51222
+ function renderGoalGet(output) {
51223
+ const record2 = asRecord(asRecord(output).goal);
51224
+ return [
51225
+ `${field(record2, "id")} ${field(record2, "status")} ${field(record2, "title")}`,
51226
+ `kind: ${field(record2, "kind")} status source: ${field(record2, "status_source")}`,
51227
+ `progress: ${field(record2, "progress_pct")}`,
51228
+ `definition of done: ${field(record2, "definition_of_done")}`
51229
+ ].join("\n");
51230
+ }
50993
51231
  function renderGoalReparent(output) {
50994
51232
  const record2 = asRecord(output);
50995
51233
  return `Goal ${field(record2, "goal_id")} reparented under ${field(record2, "parent_goal_id")}.`;
@@ -51020,6 +51258,27 @@ function renderFrictionLink(output) {
51020
51258
  const record2 = asRecord(output);
51021
51259
  return `Linked task ${field(record2, "action_task_id")} to issue ${field(record2, "issue_id")}.`;
51022
51260
  }
51261
+ function renderFrictionGet(output) {
51262
+ const record2 = asRecord(asRecord(output).issue);
51263
+ return [
51264
+ `${field(record2, "issue_id")} ${field(record2, "status")} ${field(record2, "severity")}`,
51265
+ `summary: ${field(record2, "summary")}`,
51266
+ `raised by: ${field(record2, "raised_by_seat_name")}`,
51267
+ `impact score: ${field(record2, "impact_score")}`,
51268
+ `root cause: ${field(record2, "root_cause")}`,
51269
+ `action task: ${field(record2, "action_task_id")}`
51270
+ ].join("\n");
51271
+ }
51272
+ function renderConfirmationList(output) {
51273
+ const confirmations = asArray(asRecord(output).confirmations);
51274
+ if (confirmations.length === 0) {
51275
+ return "No pending confirmations.";
51276
+ }
51277
+ return confirmations.map((entry) => {
51278
+ const record2 = asRecord(entry);
51279
+ return `${field(record2, "id")} ${field(record2, "risk_level")} ${field(record2, "command_id")} ${field(record2, "summary")}`;
51280
+ }).join("\n");
51281
+ }
51023
51282
  function renderEscalationList(output) {
51024
51283
  const escalations = asArray(asRecord(output).escalations);
51025
51284
  if (escalations.length === 0) {
@@ -51922,7 +52181,8 @@ var GROUP_SPECS = {
51922
52181
  list: { commandId: "goal.list", render: renderGoalList },
51923
52182
  create: { commandId: "goal.create", render: renderGoalCreate },
51924
52183
  update: { commandId: "goal.update", handler: goalUpdateHandler, render: renderGoalUpdate },
51925
- status: { commandId: "goal.set_status", render: renderGoalStatus },
52184
+ status: { commandId: "goal.get", render: renderGoalGet, primaryPositional: "goal_id" },
52185
+ "set-status": { commandId: "goal.set_status", render: renderGoalStatus },
51926
52186
  reparent: { commandId: "goal.reparent", render: renderGoalReparent },
51927
52187
  drop: { commandId: "goal.drop", render: renderGoalDrop }
51928
52188
  }
@@ -51946,6 +52206,7 @@ var GROUP_SPECS = {
51946
52206
  friction: {
51947
52207
  verbs: {
51948
52208
  list: { commandId: "friction.list", render: renderFrictionList },
52209
+ get: { commandId: "friction.get", render: renderFrictionGet, primaryPositional: "issue_id" },
51949
52210
  file: { commandId: "friction.file_issue", handler: frictionFileHandler, render: renderFrictionFile },
51950
52211
  resolve: { commandId: "friction.resolve", render: renderFrictionResolve },
51951
52212
  link: { commandId: "friction.link_task", render: renderFrictionLink },
@@ -51967,7 +52228,7 @@ var GROUP_SPECS = {
51967
52228
  verbs: {
51968
52229
  list: { commandId: "escalation.list", render: renderEscalationList },
51969
52230
  get: { commandId: "escalation.get", render: renderEscalationGet, primaryPositional: "id" },
51970
- resolve: { commandId: "escalation.resolve", render: renderEscalationResolve },
52231
+ resolve: { commandId: "escalation.resolve", render: renderEscalationResolve, primaryPositional: "id" },
51971
52232
  reject: { commandId: "escalation.reject", render: renderEscalationReject }
51972
52233
  }
51973
52234
  },
@@ -52089,6 +52350,11 @@ var GROUP_SPECS = {
52089
52350
  verbs: {
52090
52351
  health: { commandId: "system.health", handler: systemHealthHandler, render: formatSystemHealth }
52091
52352
  }
52353
+ },
52354
+ confirmation: {
52355
+ verbs: {
52356
+ list: { commandId: "confirmation.list", render: renderConfirmationList }
52357
+ }
52092
52358
  }
52093
52359
  };
52094
52360
 
@@ -52378,7 +52644,7 @@ function ensureTrailingSlash2(value) {
52378
52644
  // src/skills.ts
52379
52645
  import { existsSync, mkdirSync, readFileSync as readFileSync3, rmSync, writeFileSync } from "node:fs";
52380
52646
  import os from "node:os";
52381
- import path2 from "node:path";
52647
+ import path3 from "node:path";
52382
52648
  var UsageError2 = class extends Error {
52383
52649
  };
52384
52650
  var CLIENTS = /* @__PURE__ */ new Set(["claude-code", "codex", "cursor"]);
@@ -52573,8 +52839,8 @@ function writeInstalls(output, root) {
52573
52839
  const slug2 = safeLocalSlug(String(install.slug ?? ""));
52574
52840
  const metadata = asRecord2(install.metadata);
52575
52841
  const files = asArray2(install.files).map(asRecord2);
52576
- const dir = path2.join(root, slug2);
52577
- const metadataPath = path2.join(dir, ".rost-skill.json");
52842
+ const dir = path3.join(root, slug2);
52843
+ const metadataPath = path3.join(dir, ".rost-skill.json");
52578
52844
  const expectedHash = String(metadata.content_sha256 ?? "");
52579
52845
  if (existingHash(metadataPath) === expectedHash) {
52580
52846
  return { slug: slug2, dir, status: "unchanged", files: files.length };
@@ -52584,8 +52850,8 @@ function writeInstalls(output, root) {
52584
52850
  for (const file2 of files) {
52585
52851
  const relativePath = safeRelativePath(String(file2.path ?? ""));
52586
52852
  const content = String(file2.content ?? "");
52587
- const target = path2.join(dir, relativePath);
52588
- mkdirSync(path2.dirname(target), { recursive: true });
52853
+ const target = path3.join(dir, relativePath);
52854
+ mkdirSync(path3.dirname(target), { recursive: true });
52589
52855
  writeFileSync(target, content, "utf8");
52590
52856
  }
52591
52857
  writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}
@@ -52605,7 +52871,7 @@ function removeRevokedInstalls(output, root) {
52605
52871
  if (installs.has(slug2)) {
52606
52872
  continue;
52607
52873
  }
52608
- const dir = path2.join(root, slug2);
52874
+ const dir = path3.join(root, slug2);
52609
52875
  if (!existsSync(dir)) {
52610
52876
  continue;
52611
52877
  }
@@ -52617,15 +52883,15 @@ function removeRevokedInstalls(output, root) {
52617
52883
  function installRoot(client, env) {
52618
52884
  const override = env.ROST_SKILL_INSTALL_ROOT;
52619
52885
  if (override && override.length > 0) {
52620
- return path2.resolve(override, client);
52886
+ return path3.resolve(override, client);
52621
52887
  }
52622
52888
  if (client === "codex") {
52623
- return path2.join(env.CODEX_HOME ?? path2.join(os.homedir(), ".codex"), "skills", "rost");
52889
+ return path3.join(env.CODEX_HOME ?? path3.join(os.homedir(), ".codex"), "skills", "rost");
52624
52890
  }
52625
52891
  if (client === "claude-code") {
52626
- return path2.join(env.CLAUDE_HOME ?? path2.join(os.homedir(), ".claude"), "skills", "rost");
52892
+ return path3.join(env.CLAUDE_HOME ?? path3.join(os.homedir(), ".claude"), "skills", "rost");
52627
52893
  }
52628
- return path2.join(os.homedir(), ".cursor", "skills", "rost");
52894
+ return path3.join(os.homedir(), ".cursor", "skills", "rost");
52629
52895
  }
52630
52896
  function safeRelativePath(value) {
52631
52897
  const normalized = value.trim().replaceAll("\\", "/").replace(/^\.\/+/, "");
@@ -52834,9 +53100,9 @@ import { promisify as promisify3 } from "node:util";
52834
53100
  import { existsSync as existsSync2, realpathSync } from "node:fs";
52835
53101
  import { readdir, rm as rm2, stat as stat2 } from "node:fs/promises";
52836
53102
  import { tmpdir } from "node:os";
52837
- import path3 from "node:path";
53103
+ import path4 from "node:path";
52838
53104
  var SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
52839
- var RUNNER_ASKPASS_DIR = path3.join(tmpdir(), "rost-runner-askpass");
53105
+ var RUNNER_ASKPASS_DIR = path4.join(tmpdir(), "rost-runner-askpass");
52840
53106
  var RUNNER_TEMP_PREFIXES = [
52841
53107
  "rost-runner-mcp-",
52842
53108
  "rost-runner-sandbox-",
@@ -53006,7 +53272,7 @@ function canonical(p) {
53006
53272
  }
53007
53273
  }
53008
53274
  function canonicalFile(p) {
53009
- return path3.join(canonical(path3.dirname(p)), path3.basename(p));
53275
+ return path4.join(canonical(path4.dirname(p)), path4.basename(p));
53010
53276
  }
53011
53277
  function isSandboxExecAvailable() {
53012
53278
  try {
@@ -53089,7 +53355,7 @@ async function sweepStaleRunnerTempFiles(dir, options = {}) {
53089
53355
  if (!RUNNER_TEMP_PREFIXES.some((prefix) => name.startsWith(prefix))) {
53090
53356
  return;
53091
53357
  }
53092
- const full = path3.join(dir, name);
53358
+ const full = path4.join(dir, name);
53093
53359
  try {
53094
53360
  const info = await stat2(full);
53095
53361
  if (now - info.mtimeMs < maxAgeMs) {
@@ -53107,9 +53373,9 @@ async function sweepStaleRunnerTempFiles(dir, options = {}) {
53107
53373
  // src/runner-service.ts
53108
53374
  import { execFile as execFileCallback2 } from "node:child_process";
53109
53375
  import { realpathSync as realpathSync2 } from "node:fs";
53110
- import { access, mkdir as mkdir2, readFile as readFile3, readdir as readdir2, rm as rm3, writeFile as writeFile2 } from "node:fs/promises";
53111
- import { homedir as homedir2 } from "node:os";
53112
- import path4 from "node:path";
53376
+ import { access, mkdir as mkdir3, readFile as readFile4, readdir as readdir2, rm as rm3, writeFile as writeFile3 } from "node:fs/promises";
53377
+ import { homedir as homedir3 } from "node:os";
53378
+ import path5 from "node:path";
53113
53379
  import { promisify as promisify2 } from "node:util";
53114
53380
  var execFile2 = promisify2(execFileCallback2);
53115
53381
  var SERVICE_VERBS = ["install-service", "start", "stop", "restart", "status", "logs", "uninstall"];
@@ -53126,9 +53392,9 @@ function isRunnerServiceInvocation(args) {
53126
53392
  function runnerServicePaths(homeDir, runnerName) {
53127
53393
  const safeName = safeRunnerName(runnerName);
53128
53394
  return {
53129
- launchAgentPath: path4.join(homeDir, "Library", "LaunchAgents", `${serviceLabel(safeName)}.plist`),
53130
- stateFile: path4.join(homeDir, "Library", "Application Support", cliBrand.name, `runner-${safeName}.json`),
53131
- logDir: path4.join(homeDir, "Library", "Logs", cliBrand.name, "runner", safeName)
53395
+ launchAgentPath: path5.join(homeDir, "Library", "LaunchAgents", `${serviceLabel(safeName)}.plist`),
53396
+ stateFile: path5.join(homeDir, "Library", "Application Support", cliBrand.name, `runner-${safeName}.json`),
53397
+ logDir: path5.join(homeDir, "Library", "Logs", cliBrand.name, "runner", safeName)
53132
53398
  };
53133
53399
  }
53134
53400
  function renderLaunchdPlist(config2) {
@@ -53170,9 +53436,9 @@ ${config2.allowDevelopmentAppUrl === true ? ` <key>${allowInsecureAppUrlEnv}<
53170
53436
  <key>RunAtLoad</key>
53171
53437
  <true/>
53172
53438
  <key>StandardOutPath</key>
53173
- <string>${escapePlist(path4.join(config2.logDir, "stdout.log"))}</string>
53439
+ <string>${escapePlist(path5.join(config2.logDir, "stdout.log"))}</string>
53174
53440
  <key>StandardErrorPath</key>
53175
- <string>${escapePlist(path4.join(config2.logDir, "stderr.log"))}</string>
53441
+ <string>${escapePlist(path5.join(config2.logDir, "stderr.log"))}</string>
53176
53442
  </dict>
53177
53443
  </plist>
53178
53444
  `;
@@ -53208,7 +53474,7 @@ ${serviceUsage()}
53208
53474
  userCode: parsed.userCode,
53209
53475
  nodePath: options.nodePath ?? resolveNodePath(),
53210
53476
  binPath: options.binPath ?? resolveBinPath(),
53211
- homeDir: options.homeDir ?? homedir2(),
53477
+ homeDir: options.homeDir ?? homedir3(),
53212
53478
  platform: options.platform ?? process.platform,
53213
53479
  exec: options.exec ?? defaultExec,
53214
53480
  uid: options.uid ?? (process.getuid ? process.getuid() : 0)
@@ -53239,9 +53505,9 @@ async function installService(options) {
53239
53505
  const safeName = safeRunnerName(options.runnerName);
53240
53506
  const paths = runnerServicePaths(options.homeDir, safeName);
53241
53507
  const label = serviceLabel(safeName);
53242
- await mkdir2(paths.logDir, { recursive: true });
53243
- await mkdir2(path4.dirname(paths.stateFile), { recursive: true });
53244
- await mkdir2(path4.dirname(paths.launchAgentPath), { recursive: true });
53508
+ await mkdir3(paths.logDir, { recursive: true });
53509
+ await mkdir3(path5.dirname(paths.stateFile), { recursive: true });
53510
+ await mkdir3(path5.dirname(paths.launchAgentPath), { recursive: true });
53245
53511
  if (options.userCode !== void 0) {
53246
53512
  const paired = await pairRunnerOnce(options, safeName, paths.stateFile, options.userCode);
53247
53513
  if (!paired) {
@@ -53259,7 +53525,7 @@ async function installService(options) {
53259
53525
  servicePath: buildServicePath(options.nodePath, options.homeDir),
53260
53526
  execute: options.execute
53261
53527
  });
53262
- await writeFile2(paths.launchAgentPath, plist, { mode: 420 });
53528
+ await writeFile3(paths.launchAgentPath, plist, { mode: 420 });
53263
53529
  await bootoutQuietly(options.exec, options.uid, paths.launchAgentPath);
53264
53530
  try {
53265
53531
  const bootstrapped = await bootstrapService(options.exec, options.uid, paths.launchAgentPath);
@@ -53396,8 +53662,8 @@ async function logsService(options) {
53396
53662
  const safeName = safeRunnerName(options.runnerName);
53397
53663
  const paths = runnerServicePaths(options.homeDir, safeName);
53398
53664
  const label = serviceLabel(safeName);
53399
- const stdoutLog = path4.join(paths.logDir, "stdout.log");
53400
- const stderrLog = path4.join(paths.logDir, "stderr.log");
53665
+ const stdoutLog = path5.join(paths.logDir, "stdout.log");
53666
+ const stderrLog = path5.join(paths.logDir, "stderr.log");
53401
53667
  options.io.stdout.write(
53402
53668
  `${[
53403
53669
  `runner service logs for ${label}:`,
@@ -53500,8 +53766,8 @@ function serviceTargets(uid, label) {
53500
53766
  return launchdDomains(uid).map((domain2) => `${domain2}/${label}`);
53501
53767
  }
53502
53768
  async function inspectRunnerServiceInstallations(options = {}) {
53503
- const homeDir = options.homeDir ?? homedir2();
53504
- const launchAgentsDir = path4.join(homeDir, "Library", "LaunchAgents");
53769
+ const homeDir = options.homeDir ?? homedir3();
53770
+ const launchAgentsDir = path5.join(homeDir, "Library", "LaunchAgents");
53505
53771
  let names = [];
53506
53772
  try {
53507
53773
  const entries = await readdir2(launchAgentsDir);
@@ -53521,7 +53787,7 @@ async function inspectRunnerService(options) {
53521
53787
  const paths = runnerServicePaths(options.homeDir, safeName);
53522
53788
  const label = serviceLabel(safeName);
53523
53789
  const installed = await fileExists(paths.launchAgentPath);
53524
- const plist = installed ? await readFile3(paths.launchAgentPath, "utf8").catch(() => null) : null;
53790
+ const plist = installed ? await readFile4(paths.launchAgentPath, "utf8").catch(() => null) : null;
53525
53791
  const programArgs = plist ? extractProgramArguments(plist) : [];
53526
53792
  const appUrl2 = plist ? extractPlistEnvironmentValue(plist, `${cliBrand.envPrefix}_APP_URL`) : null;
53527
53793
  const urlPolicyEnv = extractPlistEnvironmentValue(plist ?? "", allowInsecureAppUrlEnv) === "1" ? "set" : "unset";
@@ -53630,11 +53896,11 @@ function resolveNodePath() {
53630
53896
  }
53631
53897
  function buildServicePath(nodePath, homeDir) {
53632
53898
  const entries = [
53633
- path4.dirname(nodePath),
53899
+ path5.dirname(nodePath),
53634
53900
  "/opt/homebrew/bin",
53635
53901
  "/usr/local/bin",
53636
- path4.join(homeDir, ".local", "bin"),
53637
- path4.join(homeDir, "bin"),
53902
+ path5.join(homeDir, ".local", "bin"),
53903
+ path5.join(homeDir, "bin"),
53638
53904
  "/usr/bin",
53639
53905
  "/bin",
53640
53906
  "/usr/sbin",
@@ -53789,7 +54055,7 @@ async function fileExists(file2) {
53789
54055
  }
53790
54056
  async function readTail(file2, maxBytes) {
53791
54057
  try {
53792
- const content = await readFile3(file2, "utf8");
54058
+ const content = await readFile4(file2, "utf8");
53793
54059
  return content.length > maxBytes ? content.slice(content.length - maxBytes) : content;
53794
54060
  } catch {
53795
54061
  return null;
@@ -53936,17 +54202,17 @@ function parseSemver2(value) {
53936
54202
  // src/runner-serve.ts
53937
54203
  import { execFile as execFileCallback5, spawn } from "node:child_process";
53938
54204
  import { createHash as createHash2 } from "node:crypto";
53939
- import { chmod as chmod2, mkdir as mkdir4, readFile as readFile4, rename, rm as rm5, statfs, writeFile as writeFile4 } from "node:fs/promises";
53940
- import { cpus, homedir as homedir3, hostname as hostname3, platform, totalmem, tmpdir as tmpdir2 } from "node:os";
53941
- import path6 from "node:path";
54205
+ import { chmod as chmod2, mkdir as mkdir5, readFile as readFile5, rename, rm as rm5, statfs, writeFile as writeFile5 } from "node:fs/promises";
54206
+ import { cpus, homedir as homedir4, hostname as hostname3, platform, totalmem, tmpdir as tmpdir2 } from "node:os";
54207
+ import path7 from "node:path";
53942
54208
  import { setTimeout as sleep3 } from "node:timers/promises";
53943
54209
  import { promisify as promisify5 } from "node:util";
53944
54210
 
53945
54211
  // src/forge-workspace.ts
53946
54212
  import { execFile as execFileCallback4 } from "node:child_process";
53947
54213
  import { createHash } from "node:crypto";
53948
- import { chmod, lstat, mkdir as mkdir3, mkdtemp, realpath, rm as rm4, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
53949
- import path5 from "node:path";
54214
+ import { chmod, lstat, mkdir as mkdir4, mkdtemp, realpath, rm as rm4, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
54215
+ import path6 from "node:path";
53950
54216
  import { setTimeout as sleep2 } from "node:timers/promises";
53951
54217
  import { promisify as promisify4 } from "node:util";
53952
54218
 
@@ -54083,20 +54349,20 @@ function shortRequestId(buildRequestId) {
54083
54349
  return slice.length > 0 ? slice : createHash("sha1").update(buildRequestId).digest("hex").slice(0, 8);
54084
54350
  }
54085
54351
  function requestRoot(baseDir, buildRequestId) {
54086
- return path5.join(baseDir, "work", shortRequestId(buildRequestId));
54352
+ return path6.join(baseDir, "work", shortRequestId(buildRequestId));
54087
54353
  }
54088
54354
  function baseRepoDir(baseDir, buildRequestId) {
54089
- return path5.join(requestRoot(baseDir, buildRequestId), "base");
54355
+ return path6.join(requestRoot(baseDir, buildRequestId), "base");
54090
54356
  }
54091
54357
  function branchName(buildRequestId, changeset) {
54092
54358
  const short = shortRequestId(buildRequestId);
54093
54359
  return changeset.total > 1 ? `forge/${short}-cs${changeset.ordinal}` : `forge/${short}`;
54094
54360
  }
54095
54361
  function buildWorktreeDir(baseDir, buildRequestId, ordinal) {
54096
- return path5.join(requestRoot(baseDir, buildRequestId), `cs${ordinal}`);
54362
+ return path6.join(requestRoot(baseDir, buildRequestId), `cs${ordinal}`);
54097
54363
  }
54098
54364
  function reviewWorktreeDir(baseDir, buildRequestId, ordinal) {
54099
- return path5.join(requestRoot(baseDir, buildRequestId), `review-cs${ordinal}`);
54365
+ return path6.join(requestRoot(baseDir, buildRequestId), `review-cs${ordinal}`);
54100
54366
  }
54101
54367
  function remoteUrlFor(options) {
54102
54368
  if (options.remoteUrl) {
@@ -54113,7 +54379,7 @@ async function pathExists(target) {
54113
54379
  }
54114
54380
  }
54115
54381
  async function isGitRepo(dir) {
54116
- return await pathExists(path5.join(dir, ".git")) || await pathExists(path5.join(dir, "HEAD"));
54382
+ return await pathExists(path6.join(dir, ".git")) || await pathExists(path6.join(dir, "HEAD"));
54117
54383
  }
54118
54384
  async function runGit(args, cwd, timeoutMs = LOCAL_GIT_TIMEOUT_MS) {
54119
54385
  try {
@@ -54166,10 +54432,10 @@ async function withNetworkRetries(op, fn, retryCounter) {
54166
54432
  throw new Error(`${op} exhausted retries: ${lastError instanceof Error ? redactSecrets(lastError.message) : redactSecrets(String(lastError))}`);
54167
54433
  }
54168
54434
  async function canonicalAskpassBase(baseDir) {
54169
- return path5.join(await realpath(path5.dirname(baseDir)), path5.basename(baseDir));
54435
+ return path6.join(await realpath(path6.dirname(baseDir)), path6.basename(baseDir));
54170
54436
  }
54171
54437
  async function prepareAskpassTurnDir(baseDir) {
54172
- await mkdir3(baseDir, { recursive: true, mode: 448 });
54438
+ await mkdir4(baseDir, { recursive: true, mode: 448 });
54173
54439
  const baseInfo = await lstat(baseDir);
54174
54440
  if (baseInfo.isSymbolicLink() || !baseInfo.isDirectory()) {
54175
54441
  throw new Error(`refusing to use askpass dir ${baseDir}: not a real directory (possible symlink attack)`);
@@ -54178,10 +54444,10 @@ async function prepareAskpassTurnDir(baseDir) {
54178
54444
  throw new Error(`refusing to use askpass dir ${baseDir}: not owned by this user (possible pre-planted dir)`);
54179
54445
  }
54180
54446
  await chmod(baseDir, 448);
54181
- const turnDir = await mkdtemp(path5.join(baseDir, "rost-runner-askpass-"));
54447
+ const turnDir = await mkdtemp(path6.join(baseDir, "rost-runner-askpass-"));
54182
54448
  const canonicalBase = await canonicalAskpassBase(baseDir);
54183
54449
  const realTurn = await realpath(turnDir);
54184
- if (path5.dirname(realTurn) !== canonicalBase) {
54450
+ if (path6.dirname(realTurn) !== canonicalBase) {
54185
54451
  await rm4(turnDir, { recursive: true, force: true });
54186
54452
  throw new Error(`refusing to use askpass dir ${turnDir}: resolved outside ${canonicalBase} (possible symlink attack)`);
54187
54453
  }
@@ -54189,7 +54455,7 @@ async function prepareAskpassTurnDir(baseDir) {
54189
54455
  }
54190
54456
  async function openAskpassSession(credential, baseDir = RUNNER_ASKPASS_DIR) {
54191
54457
  const turnDir = await prepareAskpassTurnDir(baseDir);
54192
- const helperPath = path5.join(turnDir, "askpass.sh");
54458
+ const helperPath = path6.join(turnDir, "askpass.sh");
54193
54459
  const helper = [
54194
54460
  "#!/bin/sh",
54195
54461
  "# One-shot Forge GIT_ASKPASS helper. Contains no secret; reads the token from env.",
@@ -54199,7 +54465,7 @@ async function openAskpassSession(credential, baseDir = RUNNER_ASKPASS_DIR) {
54199
54465
  "esac",
54200
54466
  ""
54201
54467
  ].join("\n");
54202
- await writeFile3(helperPath, helper, { mode: 448 });
54468
+ await writeFile4(helperPath, helper, { mode: 448 });
54203
54469
  await chmod(helperPath, 448);
54204
54470
  const env = {
54205
54471
  ...process.env,
@@ -54250,8 +54516,8 @@ async function localBranchExists(baseRepo, branch) {
54250
54516
  async function worktreeRegistered(baseRepo, worktreeDir) {
54251
54517
  try {
54252
54518
  const listing = await runGit(["worktree", "list", "--porcelain"], baseRepo);
54253
- const target = path5.resolve(worktreeDir);
54254
- return listing.split("\n").some((line) => line.startsWith("worktree ") && path5.resolve(line.slice("worktree ".length).trim()) === target);
54519
+ const target = path6.resolve(worktreeDir);
54520
+ return listing.split("\n").some((line) => line.startsWith("worktree ") && path6.resolve(line.slice("worktree ".length).trim()) === target);
54255
54521
  } catch {
54256
54522
  return false;
54257
54523
  }
@@ -54262,7 +54528,7 @@ async function configureForgeIdentity(baseRepo) {
54262
54528
  }
54263
54529
  async function ensureBaseClone(input) {
54264
54530
  const baseRepo = baseRepoDir(input.baseDir, input.buildRequestId);
54265
- await mkdir3(path5.dirname(baseRepo), { recursive: true, mode: 448 });
54531
+ await mkdir4(path6.dirname(baseRepo), { recursive: true, mode: 448 });
54266
54532
  await withLedgerStep(input.ledger, "clone", (retryCounter) => withAskpass(input.credential, async (netEnv) => {
54267
54533
  if (await isGitRepo(baseRepo)) {
54268
54534
  await runGitNetwork("git fetch", ["fetch", "--prune", "--quiet", input.url, "+refs/heads/*:refs/remotes/origin/*"], netEnv, baseRepo, FETCH_TIMEOUT_MS, retryCounter);
@@ -54311,7 +54577,7 @@ async function prepareMergeWorkspace(options) {
54311
54577
  ledger: options.ledger
54312
54578
  });
54313
54579
  const branch = options.headBranch;
54314
- const worktreeDir = path5.join(requestRoot(options.baseDir, options.buildRequestId), "merge");
54580
+ const worktreeDir = path6.join(requestRoot(options.baseDir, options.buildRequestId), "merge");
54315
54581
  const originDefault = `origin/${options.repo.default_branch}`;
54316
54582
  const originHead = `origin/${branch}`;
54317
54583
  await withLedgerStep(options.ledger, "branch", async () => {
@@ -54789,7 +55055,7 @@ async function runRunnerServe(options) {
54789
55055
  const env = options.env ?? process.env;
54790
55056
  const fetchImpl = options.fetchImpl ?? fetch;
54791
55057
  const cliVersion = await readCliVersion();
54792
- const homeDir = options.homeDir ?? homedir3();
55058
+ const homeDir = options.homeDir ?? homedir4();
54793
55059
  let config2;
54794
55060
  try {
54795
55061
  config2 = parseServeArgs(options.args, options.appUrl, env, homeDir);
@@ -54975,7 +55241,7 @@ function detectTelemetry(capabilities, env, activeSessions, cliVersion) {
54975
55241
  }
54976
55242
  function parseServeArgs(args, appUrl2, env, homeDir) {
54977
55243
  let name = env.RUNNER_NAME ?? `headless-${hostname3()}`;
54978
- let stateFile = env.RUNNER_STATE_FILE ?? path6.join(tmpdir2(), `rost-runner-${createHash2("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
55244
+ let stateFile = env.RUNNER_STATE_FILE ?? path7.join(tmpdir2(), `rost-runner-${createHash2("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
54979
55245
  let heartbeatMs = Number(env.RUNNER_HEARTBEAT_MS ?? 3e4);
54980
55246
  let interactiveClaimMs = Number(env.RUNNER_INTERACTIVE_CLAIM_MS ?? 1e3);
54981
55247
  let execute2 = env.RUNNER_EXECUTE === "1";
@@ -55026,7 +55292,7 @@ function parseServeArgs(args, appUrl2, env, homeDir) {
55026
55292
  tmpDir: tmpdir2(),
55027
55293
  homeDir,
55028
55294
  stateFile,
55029
- runnerBaseDir: path6.join(homeDir, ".rost"),
55295
+ runnerBaseDir: path7.join(homeDir, ".rost"),
55030
55296
  allowNetwork: network.allowNetwork
55031
55297
  });
55032
55298
  return {
@@ -55132,7 +55398,7 @@ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
55132
55398
  }
55133
55399
  async function loadState(filePath, expectedTenantId) {
55134
55400
  try {
55135
- const parsed = JSON.parse(await readFile4(filePath, "utf8"));
55401
+ const parsed = JSON.parse(await readFile5(filePath, "utf8"));
55136
55402
  if (typeof parsed.runner_id === "string" && typeof parsed.runner_secret === "string") {
55137
55403
  if (expectedTenantId && parsed.tenant_id !== expectedTenantId) {
55138
55404
  return null;
@@ -55163,10 +55429,10 @@ function isRuntimeSessionMap(value) {
55163
55429
  });
55164
55430
  }
55165
55431
  async function saveState(filePath, state) {
55166
- await mkdir4(path6.dirname(filePath), { recursive: true, mode: 448 });
55432
+ await mkdir5(path7.dirname(filePath), { recursive: true, mode: 448 });
55167
55433
  const tempPath = `${filePath}.${process.pid}.${createHash2("sha1").update(`${Date.now()}:${Math.random()}`).digest("hex").slice(0, 8)}.tmp`;
55168
55434
  try {
55169
- await writeFile4(tempPath, `${JSON.stringify(state)}
55435
+ await writeFile5(tempPath, `${JSON.stringify(state)}
55170
55436
  `, { mode: 384 });
55171
55437
  await chmod2(tempPath, 384);
55172
55438
  await rename(tempPath, filePath);
@@ -55216,8 +55482,8 @@ function resolveSandboxKind(env, homeDir) {
55216
55482
  workspaceDir: tmpdir2(),
55217
55483
  tmpDir: tmpdir2(),
55218
55484
  homeDir,
55219
- stateFile: path6.join(tmpdir2(), "runner-sandbox-probe.json"),
55220
- runnerBaseDir: path6.join(homeDir, ".rost"),
55485
+ stateFile: path7.join(tmpdir2(), "runner-sandbox-probe.json"),
55486
+ runnerBaseDir: path7.join(homeDir, ".rost"),
55221
55487
  allowNetwork: resolveSandboxNetworkFlag(env) === "allow"
55222
55488
  }).kind;
55223
55489
  }
@@ -55316,9 +55582,9 @@ async function probeGhAuth() {
55316
55582
  }
55317
55583
  }
55318
55584
  async function probeForgeDiskFreeMb(homeDir) {
55319
- const dir = path6.join(homeDir, ".rost", "forge");
55585
+ const dir = path7.join(homeDir, ".rost", "forge");
55320
55586
  try {
55321
- await mkdir4(dir, { recursive: true, mode: 448 });
55587
+ await mkdir5(dir, { recursive: true, mode: 448 });
55322
55588
  const stats = await statfs(dir);
55323
55589
  return Math.floor(stats.bavail * stats.bsize / (1024 * 1024));
55324
55590
  } catch {
@@ -55650,7 +55916,7 @@ async function prepareForgeTurn(ctx, workOrder, execution) {
55650
55916
  let workspace;
55651
55917
  try {
55652
55918
  workspace = await prepareForgeWorkspace({
55653
- baseDir: path6.join(ctx.config.homeDir, ".rost", "forge", ctx.state.runner_id),
55919
+ baseDir: path7.join(ctx.config.homeDir, ".rost", "forge", ctx.state.runner_id),
55654
55920
  tenantId,
55655
55921
  buildRequestId,
55656
55922
  changeset: { id: buildRequestId, ordinal: 1, total: 1 },
@@ -55670,7 +55936,7 @@ async function prepareForgeTurn(ctx, workOrder, execution) {
55670
55936
  tmpDir: tmpdir2(),
55671
55937
  homeDir: ctx.config.homeDir,
55672
55938
  stateFile: ctx.config.stateFile,
55673
- runnerBaseDir: path6.join(ctx.config.homeDir, ".rost"),
55939
+ runnerBaseDir: path7.join(ctx.config.homeDir, ".rost"),
55674
55940
  allowWritePaths: [workspace.baseRepo],
55675
55941
  // Re-allow the shared object store for local commits, but DENY the model writing the
55676
55942
  // repo's hook + config (and the worktree's gitdir pointer). The harness runs git
@@ -55678,9 +55944,9 @@ async function prepareForgeTurn(ctx, workOrder, execution) {
55678
55944
  // hook or `ext::`/`insteadOf` config would otherwise exfiltrate it (DER-1327 review).
55679
55945
  // `.git/{objects,refs,worktrees,logs,index,HEAD}` stay writable so the model can commit.
55680
55946
  denyWritePaths: [
55681
- `${path6.join(workspace.baseRepo, ".git", "hooks")}/`,
55682
- path6.join(workspace.baseRepo, ".git", "config"),
55683
- path6.join(workspace.worktreeDir, ".git")
55947
+ `${path7.join(workspace.baseRepo, ".git", "hooks")}/`,
55948
+ path7.join(workspace.baseRepo, ".git", "config"),
55949
+ path7.join(workspace.worktreeDir, ".git")
55684
55950
  ],
55685
55951
  allowNetwork: ctx.config.sandboxAllowNetwork
55686
55952
  });
@@ -55699,7 +55965,7 @@ async function prepareForgeTurn(ctx, workOrder, execution) {
55699
55965
  var FORGE_MAX_CHANGED_PATHS = 2e3;
55700
55966
  var FORGE_MAX_CHANGED_PATH_LENGTH = 500;
55701
55967
  function forgeChangedPathsField(changedPaths) {
55702
- const withinCaps = changedPaths.length > 0 && changedPaths.length <= FORGE_MAX_CHANGED_PATHS && changedPaths.every((path7) => path7.length >= 1 && path7.length <= FORGE_MAX_CHANGED_PATH_LENGTH);
55968
+ const withinCaps = changedPaths.length > 0 && changedPaths.length <= FORGE_MAX_CHANGED_PATHS && changedPaths.every((path8) => path8.length >= 1 && path8.length <= FORGE_MAX_CHANGED_PATH_LENGTH);
55703
55969
  return withinCaps ? { changedPaths: [...changedPaths] } : {};
55704
55970
  }
55705
55971
  async function finalizeForgeBuildPush(ctx, prep, result) {
@@ -55840,9 +56106,9 @@ async function spawnRunnerTurn(ctx, workOrder, runtime, kind) {
55840
56106
  const token = typeof mcp.token === "string" ? mcp.token : "";
55841
56107
  const url2 = typeof mcp.url === "string" ? `${ctx.appUrl.replace(/\/+$/, "")}${mcp.url}` : `${ctx.appUrl.replace(/\/+$/, "")}/mcp`;
55842
56108
  const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
55843
- const configPath = path6.join(tmpdir2(), `rost-runner-mcp-${createHash2("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
56109
+ const configPath = path7.join(tmpdir2(), `rost-runner-mcp-${createHash2("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
55844
56110
  const prompt = buildTurnPrompt({ kind, workOrder, execution, hasWorkspace: forgePrep !== null });
55845
- await writeFile4(configPath, `${mcpConfig}
56111
+ await writeFile5(configPath, `${mcpConfig}
55846
56112
  `, { mode: 384 });
55847
56113
  await chmod2(configPath, 384);
55848
56114
  const resumeSessionId = runtime === "claude" ? getClaudeResumeSessionId(ctx.state, workOrder) : void 0;
@@ -55857,11 +56123,11 @@ async function spawnRunnerTurn(ctx, workOrder, runtime, kind) {
55857
56123
  });
55858
56124
  let sandboxProfilePath = null;
55859
56125
  if (sandbox.kind === "seatbelt") {
55860
- sandboxProfilePath = path6.join(
56126
+ sandboxProfilePath = path7.join(
55861
56127
  tmpdir2(),
55862
56128
  `rost-runner-sandbox-${createHash2("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`
55863
56129
  );
55864
- await writeFile4(sandboxProfilePath, sandbox.profile, { mode: 384 });
56130
+ await writeFile5(sandboxProfilePath, sandbox.profile, { mode: 384 });
55865
56131
  await chmod2(sandboxProfilePath, 384);
55866
56132
  }
55867
56133
  const { command, args } = wrapCommandWithSandbox(sandbox, built.command, built.args, sandboxProfilePath);
@@ -55992,7 +56258,7 @@ async function resolveMergeConflictTurn(ctx, input) {
55992
56258
  let workspace;
55993
56259
  try {
55994
56260
  workspace = await prepareMergeWorkspace({
55995
- baseDir: path6.join(ctx.config.homeDir, ".rost", "forge", ctx.state.runner_id),
56261
+ baseDir: path7.join(ctx.config.homeDir, ".rost", "forge", ctx.state.runner_id),
55996
56262
  tenantId,
55997
56263
  buildRequestId: input.buildRequestId,
55998
56264
  repo: input.repo,
@@ -56010,12 +56276,12 @@ async function resolveMergeConflictTurn(ctx, input) {
56010
56276
  tmpDir: tmpdir2(),
56011
56277
  homeDir: ctx.config.homeDir,
56012
56278
  stateFile: ctx.config.stateFile,
56013
- runnerBaseDir: path6.join(ctx.config.homeDir, ".rost"),
56279
+ runnerBaseDir: path7.join(ctx.config.homeDir, ".rost"),
56014
56280
  allowWritePaths: [workspace.baseRepo],
56015
56281
  denyWritePaths: [
56016
- `${path6.join(workspace.baseRepo, ".git", "hooks")}/`,
56017
- path6.join(workspace.baseRepo, ".git", "config"),
56018
- path6.join(workspace.worktreeDir, ".git")
56282
+ `${path7.join(workspace.baseRepo, ".git", "hooks")}/`,
56283
+ path7.join(workspace.baseRepo, ".git", "config"),
56284
+ path7.join(workspace.worktreeDir, ".git")
56019
56285
  ],
56020
56286
  allowNetwork: ctx.config.sandboxAllowNetwork
56021
56287
  });
@@ -56045,8 +56311,8 @@ async function spawnMergeConflictModel(ctx, input) {
56045
56311
  const token = typeof mcp.token === "string" ? mcp.token : "";
56046
56312
  const url2 = typeof mcp.url === "string" ? `${ctx.appUrl.replace(/\/+$/, "")}${mcp.url}` : `${ctx.appUrl.replace(/\/+$/, "")}/mcp`;
56047
56313
  const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
56048
- const configPath = path6.join(tmpdir2(), `rost-runner-mcp-${createHash2("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
56049
- await writeFile4(configPath, `${mcpConfig}
56314
+ const configPath = path7.join(tmpdir2(), `rost-runner-mcp-${createHash2("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
56315
+ await writeFile5(configPath, `${mcpConfig}
56050
56316
  `, { mode: 384 });
56051
56317
  await chmod2(configPath, 384);
56052
56318
  const plan = resolveTurnPlan({ kind: "work_order", execution: input.execution, repoDir: input.workspace.worktreeDir });
@@ -56058,8 +56324,8 @@ async function spawnMergeConflictModel(ctx, input) {
56058
56324
  ].join("\n");
56059
56325
  let sandboxProfilePath = null;
56060
56326
  if (input.sandbox.kind === "seatbelt") {
56061
- sandboxProfilePath = path6.join(tmpdir2(), `rost-runner-sandbox-${createHash2("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`);
56062
- await writeFile4(sandboxProfilePath, input.sandbox.profile, { mode: 384 });
56327
+ sandboxProfilePath = path7.join(tmpdir2(), `rost-runner-sandbox-${createHash2("sha256").update(`${configPath}:${Math.random()}`).digest("hex").slice(0, 16)}.sb`);
56328
+ await writeFile5(sandboxProfilePath, input.sandbox.profile, { mode: 384 });
56063
56329
  await chmod2(sandboxProfilePath, 384);
56064
56330
  }
56065
56331
  const built = buildTurnCommand({