@rosthq/cli 0.5.26 → 0.5.28

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(path3, preserveJsx) {
476
- if (typeof path3 === "string" && /^\.\.?\//.test(path3)) {
477
- return path3.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function(m, tsx, d, ext, cm) {
475
+ function __rewriteRelativeImportExtension(path4, preserveJsx) {
476
+ if (typeof path4 === "string" && /^\.\.?\//.test(path4)) {
477
+ return path4.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 path3;
481
+ return path4;
482
482
  }
483
483
  var extendStatics, __assign, __createBinding, __setModuleDefault, ownKeys, _SuppressedError, tslib_es6_default;
484
484
  var init_tslib_es6 = __esm({
@@ -5471,7 +5471,7 @@ var require_helpers = __commonJS({
5471
5471
  exports.generateCallbackId = generateCallbackId;
5472
5472
  exports.parseParametersFromURL = parseParametersFromURL;
5473
5473
  exports.decodeJWT = decodeJWT;
5474
- exports.sleep = sleep2;
5474
+ exports.sleep = sleep3;
5475
5475
  exports.retryable = retryable;
5476
5476
  exports.generatePKCEVerifier = generatePKCEVerifier;
5477
5477
  exports.generatePKCEChallenge = generatePKCEChallenge;
@@ -5609,7 +5609,7 @@ var require_helpers = __commonJS({
5609
5609
  };
5610
5610
  return data;
5611
5611
  }
5612
- async function sleep2(time3) {
5612
+ async function sleep3(time3) {
5613
5613
  return await new Promise((accept) => {
5614
5614
  setTimeout(() => accept(null), time3);
5615
5615
  });
@@ -7718,10 +7718,10 @@ var require_webauthn = __commonJS({
7718
7718
  authenticatorAttachment: (_a3 = credentialWithAttachment.authenticatorAttachment) !== null && _a3 !== void 0 ? _a3 : void 0
7719
7719
  };
7720
7720
  }
7721
- function isValidDomain(hostname3) {
7721
+ function isValidDomain(hostname4) {
7722
7722
  return (
7723
7723
  // Consider localhost valid as well since it's okay wrt Secure Contexts
7724
- hostname3 === "localhost" || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname3)
7724
+ hostname4 === "localhost" || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname4)
7725
7725
  );
7726
7726
  }
7727
7727
  function browserSupportsWebAuthn() {
@@ -14026,10 +14026,10 @@ function mergeDefs(...defs) {
14026
14026
  function cloneDef(schema) {
14027
14027
  return mergeDefs(schema._zod.def);
14028
14028
  }
14029
- function getElementAtPath(obj, path3) {
14030
- if (!path3)
14029
+ function getElementAtPath(obj, path4) {
14030
+ if (!path4)
14031
14031
  return obj;
14032
- return path3.reduce((acc, key) => acc?.[key], obj);
14032
+ return path4.reduce((acc, key) => acc?.[key], obj);
14033
14033
  }
14034
14034
  function promiseAllObject(promisesObj) {
14035
14035
  const keys = Object.keys(promisesObj);
@@ -14438,11 +14438,11 @@ function explicitlyAborted(x, startIndex = 0) {
14438
14438
  }
14439
14439
  return false;
14440
14440
  }
14441
- function prefixIssues(path3, issues) {
14441
+ function prefixIssues(path4, issues) {
14442
14442
  return issues.map((iss) => {
14443
14443
  var _a3;
14444
14444
  (_a3 = iss).path ?? (_a3.path = []);
14445
- iss.path.unshift(path3);
14445
+ iss.path.unshift(path4);
14446
14446
  return iss;
14447
14447
  });
14448
14448
  }
@@ -14589,16 +14589,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
14589
14589
  }
14590
14590
  function formatError(error51, mapper = (issue2) => issue2.message) {
14591
14591
  const fieldErrors = { _errors: [] };
14592
- const processError = (error52, path3 = []) => {
14592
+ const processError = (error52, path4 = []) => {
14593
14593
  for (const issue2 of error52.issues) {
14594
14594
  if (issue2.code === "invalid_union" && issue2.errors.length) {
14595
- issue2.errors.map((issues) => processError({ issues }, [...path3, ...issue2.path]));
14595
+ issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
14596
14596
  } else if (issue2.code === "invalid_key") {
14597
- processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
14597
+ processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
14598
14598
  } else if (issue2.code === "invalid_element") {
14599
- processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
14599
+ processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
14600
14600
  } else {
14601
- const fullpath = [...path3, ...issue2.path];
14601
+ const fullpath = [...path4, ...issue2.path];
14602
14602
  if (fullpath.length === 0) {
14603
14603
  fieldErrors._errors.push(mapper(issue2));
14604
14604
  } else {
@@ -14625,17 +14625,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
14625
14625
  }
14626
14626
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
14627
14627
  const result = { errors: [] };
14628
- const processError = (error52, path3 = []) => {
14628
+ const processError = (error52, path4 = []) => {
14629
14629
  var _a3, _b;
14630
14630
  for (const issue2 of error52.issues) {
14631
14631
  if (issue2.code === "invalid_union" && issue2.errors.length) {
14632
- issue2.errors.map((issues) => processError({ issues }, [...path3, ...issue2.path]));
14632
+ issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
14633
14633
  } else if (issue2.code === "invalid_key") {
14634
- processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
14634
+ processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
14635
14635
  } else if (issue2.code === "invalid_element") {
14636
- processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
14636
+ processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
14637
14637
  } else {
14638
- const fullpath = [...path3, ...issue2.path];
14638
+ const fullpath = [...path4, ...issue2.path];
14639
14639
  if (fullpath.length === 0) {
14640
14640
  result.errors.push(mapper(issue2));
14641
14641
  continue;
@@ -14667,8 +14667,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
14667
14667
  }
14668
14668
  function toDotPath(_path) {
14669
14669
  const segs = [];
14670
- const path3 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
14671
- for (const seg of path3) {
14670
+ const path4 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
14671
+ for (const seg of path4) {
14672
14672
  if (typeof seg === "number")
14673
14673
  segs.push(`[${seg}]`);
14674
14674
  else if (typeof seg === "symbol")
@@ -14874,8 +14874,8 @@ function emoji() {
14874
14874
  }
14875
14875
  var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
14876
14876
  var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
14877
- var mac = (delimiter) => {
14878
- const escapedDelim = escapeRegex(delimiter ?? ":");
14877
+ var mac = (delimiter2) => {
14878
+ const escapedDelim = escapeRegex(delimiter2 ?? ":");
14879
14879
  return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
14880
14880
  };
14881
14881
  var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
@@ -27360,13 +27360,13 @@ function resolveRef(ref, ctx) {
27360
27360
  if (!ref.startsWith("#")) {
27361
27361
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
27362
27362
  }
27363
- const path3 = ref.slice(1).split("/").filter(Boolean);
27364
- if (path3.length === 0) {
27363
+ const path4 = ref.slice(1).split("/").filter(Boolean);
27364
+ if (path4.length === 0) {
27365
27365
  return ctx.rootSchema;
27366
27366
  }
27367
27367
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
27368
- if (path3[0] === defsKey) {
27369
- const key = path3[1];
27368
+ if (path4[0] === defsKey) {
27369
+ const key = path4[1];
27370
27370
  if (!key || !ctx.defs[key]) {
27371
27371
  throw new Error(`Reference not found: ${ref}`);
27372
27372
  }
@@ -27828,7 +27828,7 @@ var CommandClient = class {
27828
27828
  this.fetchImpl = options.fetchImpl ?? fetch;
27829
27829
  }
27830
27830
  async execute(commandId, body = {}, options = {}) {
27831
- const path3 = `/api/commands/${encodeURIComponent(commandId)}`;
27831
+ const path4 = `/api/commands/${encodeURIComponent(commandId)}`;
27832
27832
  const headers = {
27833
27833
  authorization: `Bearer ${this.token}`,
27834
27834
  "content-type": "application/json"
@@ -27836,7 +27836,7 @@ var CommandClient = class {
27836
27836
  if (options.targetSeatId !== void 0 && options.targetSeatId.length > 0) {
27837
27837
  headers["x-rost-seat"] = options.targetSeatId;
27838
27838
  }
27839
- const response = await this.fetchImpl(`${this.appUrl}${path3}`, {
27839
+ const response = await this.fetchImpl(`${this.appUrl}${path4}`, {
27840
27840
  method: "POST",
27841
27841
  headers,
27842
27842
  body: JSON.stringify(body)
@@ -27849,7 +27849,7 @@ var CommandClient = class {
27849
27849
  const snippet = redactSecrets(text.replace(/\s+/g, " ").trim()).slice(0, 200);
27850
27850
  throw new CommandClientError(
27851
27851
  response.status,
27852
- `server returned HTTP ${response.status} (non-JSON) from ${path3}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
27852
+ `server returned HTTP ${response.status} (non-JSON) from ${path4}` + (snippet ? ` \u2014 ${snippet}` : "") + " \u2014 the deployment may be unhealthy"
27853
27853
  );
27854
27854
  }
27855
27855
  const parsed = commandResponseSchema.parse(raw);
@@ -33056,8 +33056,8 @@ var IcebergError = class extends Error {
33056
33056
  return this.status === 419;
33057
33057
  }
33058
33058
  };
33059
- function buildUrl(baseUrl, path3, query) {
33060
- const url2 = new URL(path3, baseUrl);
33059
+ function buildUrl(baseUrl, path4, query) {
33060
+ const url2 = new URL(path4, baseUrl);
33061
33061
  if (query) {
33062
33062
  for (const [key, value] of Object.entries(query)) {
33063
33063
  if (value !== void 0) {
@@ -33087,12 +33087,12 @@ function createFetchClient(options) {
33087
33087
  return {
33088
33088
  async request({
33089
33089
  method,
33090
- path: path3,
33090
+ path: path4,
33091
33091
  query,
33092
33092
  body,
33093
33093
  headers
33094
33094
  }) {
33095
- const url2 = buildUrl(options.baseUrl, path3, query);
33095
+ const url2 = buildUrl(options.baseUrl, path4, query);
33096
33096
  const authHeaders = await buildAuthHeaders(options.auth);
33097
33097
  const res = await fetchFn(url2, {
33098
33098
  method,
@@ -33954,7 +33954,7 @@ var StorageFileApi = class extends BaseApiClient {
33954
33954
  * @param path The relative file path. Should be of the format `folder/subfolder/filename.png`. The bucket must already exist before attempting to upload.
33955
33955
  * @param fileBody The body of the file to be stored in the bucket.
33956
33956
  */
33957
- async uploadOrUpdate(method, path3, fileBody, fileOptions) {
33957
+ async uploadOrUpdate(method, path4, fileBody, fileOptions) {
33958
33958
  var _this = this;
33959
33959
  return _this.handleOperation(async () => {
33960
33960
  let body;
@@ -33978,7 +33978,7 @@ var StorageFileApi = class extends BaseApiClient {
33978
33978
  if ((typeof ReadableStream !== "undefined" && body instanceof ReadableStream || body && typeof body === "object" && "pipe" in body && typeof body.pipe === "function") && !options.duplex) options.duplex = "half";
33979
33979
  }
33980
33980
  if (fileOptions === null || fileOptions === void 0 ? void 0 : fileOptions.headers) for (const [key, value] of Object.entries(fileOptions.headers)) headers = setHeader(headers, key, value);
33981
- const cleanPath = _this._removeEmptyFolders(path3);
33981
+ const cleanPath = _this._removeEmptyFolders(path4);
33982
33982
  const _path = _this._getFinalPath(cleanPath);
33983
33983
  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 } : {}));
33984
33984
  return {
@@ -34055,8 +34055,8 @@ var StorageFileApi = class extends BaseApiClient {
34055
34055
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34056
34056
  * - 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.
34057
34057
  */
34058
- async upload(path3, fileBody, fileOptions) {
34059
- return this.uploadOrUpdate("POST", path3, fileBody, fileOptions);
34058
+ async upload(path4, fileBody, fileOptions) {
34059
+ return this.uploadOrUpdate("POST", path4, fileBody, fileOptions);
34060
34060
  }
34061
34061
  /**
34062
34062
  * Upload a file with a token generated from `createSignedUploadUrl`.
@@ -34096,9 +34096,9 @@ var StorageFileApi = class extends BaseApiClient {
34096
34096
  * - `objects` table permissions: none
34097
34097
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34098
34098
  */
34099
- async uploadToSignedUrl(path3, token, fileBody, fileOptions) {
34099
+ async uploadToSignedUrl(path4, token, fileBody, fileOptions) {
34100
34100
  var _this3 = this;
34101
- const cleanPath = _this3._removeEmptyFolders(path3);
34101
+ const cleanPath = _this3._removeEmptyFolders(path4);
34102
34102
  const _path = _this3._getFinalPath(cleanPath);
34103
34103
  const url2 = new URL(_this3.url + `/object/upload/sign/${_path}`);
34104
34104
  url2.searchParams.set("token", token);
@@ -34167,10 +34167,10 @@ var StorageFileApi = class extends BaseApiClient {
34167
34167
  * - `objects` table permissions: `insert`
34168
34168
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34169
34169
  */
34170
- async createSignedUploadUrl(path3, options) {
34170
+ async createSignedUploadUrl(path4, options) {
34171
34171
  var _this4 = this;
34172
34172
  return _this4.handleOperation(async () => {
34173
- let _path = _this4._getFinalPath(path3);
34173
+ let _path = _this4._getFinalPath(path4);
34174
34174
  const headers = _objectSpread22({}, _this4.headers);
34175
34175
  if (options === null || options === void 0 ? void 0 : options.upsert) headers["x-upsert"] = "true";
34176
34176
  const data = await post(_this4.fetch, `${_this4.url}/object/upload/sign/${_path}`, {}, { headers });
@@ -34179,7 +34179,7 @@ var StorageFileApi = class extends BaseApiClient {
34179
34179
  if (!token) throw new StorageError("No token returned by API");
34180
34180
  return {
34181
34181
  signedUrl: url2.toString(),
34182
- path: path3,
34182
+ path: path4,
34183
34183
  token
34184
34184
  };
34185
34185
  });
@@ -34239,8 +34239,8 @@ var StorageFileApi = class extends BaseApiClient {
34239
34239
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34240
34240
  * - 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.
34241
34241
  */
34242
- async update(path3, fileBody, fileOptions) {
34243
- return this.uploadOrUpdate("PUT", path3, fileBody, fileOptions);
34242
+ async update(path4, fileBody, fileOptions) {
34243
+ return this.uploadOrUpdate("PUT", path4, fileBody, fileOptions);
34244
34244
  }
34245
34245
  /**
34246
34246
  * Moves an existing file to a new path in the same bucket.
@@ -34391,10 +34391,10 @@ var StorageFileApi = class extends BaseApiClient {
34391
34391
  * - `objects` table permissions: `select`
34392
34392
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34393
34393
  */
34394
- async createSignedUrl(path3, expiresIn, options) {
34394
+ async createSignedUrl(path4, expiresIn, options) {
34395
34395
  var _this8 = this;
34396
34396
  return _this8.handleOperation(async () => {
34397
- let _path = _this8._getFinalPath(path3);
34397
+ let _path = _this8._getFinalPath(path4);
34398
34398
  const hasTransform = typeof (options === null || options === void 0 ? void 0 : options.transform) === "object" && options.transform !== null && Object.keys(options.transform).length > 0;
34399
34399
  let data = await post(_this8.fetch, `${_this8.url}/object/sign/${_path}`, _objectSpread22({ expiresIn }, hasTransform ? { transform: options.transform } : {}), { headers: _this8.headers });
34400
34400
  const query = new URLSearchParams();
@@ -34530,13 +34530,13 @@ var StorageFileApi = class extends BaseApiClient {
34530
34530
  * - `objects` table permissions: `select`
34531
34531
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34532
34532
  */
34533
- download(path3, options, parameters) {
34533
+ download(path4, options, parameters) {
34534
34534
  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";
34535
34535
  const query = new URLSearchParams();
34536
34536
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
34537
34537
  if ((options === null || options === void 0 ? void 0 : options.cacheNonce) != null) query.set("cacheNonce", String(options.cacheNonce));
34538
34538
  const queryString = query.toString();
34539
- const _path = this._getFinalPath(path3);
34539
+ const _path = this._getFinalPath(path4);
34540
34540
  const downloadFn = () => get(this.fetch, `${this.url}/${renderPath}/${_path}${queryString ? `?${queryString}` : ""}`, {
34541
34541
  headers: this.headers,
34542
34542
  noResolveJson: true
@@ -34567,9 +34567,9 @@ var StorageFileApi = class extends BaseApiClient {
34567
34567
  * }
34568
34568
  * ```
34569
34569
  */
34570
- async info(path3) {
34570
+ async info(path4) {
34571
34571
  var _this10 = this;
34572
- const _path = _this10._getFinalPath(path3);
34572
+ const _path = _this10._getFinalPath(path4);
34573
34573
  return _this10.handleOperation(async () => {
34574
34574
  return recursiveToCamel(await get(_this10.fetch, `${_this10.url}/object/info/${_path}`, { headers: _this10.headers }));
34575
34575
  });
@@ -34590,9 +34590,9 @@ var StorageFileApi = class extends BaseApiClient {
34590
34590
  * .exists('folder/avatar1.png')
34591
34591
  * ```
34592
34592
  */
34593
- async exists(path3) {
34593
+ async exists(path4) {
34594
34594
  var _this11 = this;
34595
- const _path = _this11._getFinalPath(path3);
34595
+ const _path = _this11._getFinalPath(path4);
34596
34596
  try {
34597
34597
  await head(_this11.fetch, `${_this11.url}/object/${_path}`, { headers: _this11.headers });
34598
34598
  return {
@@ -34671,8 +34671,8 @@ var StorageFileApi = class extends BaseApiClient {
34671
34671
  * - `objects` table permissions: none
34672
34672
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34673
34673
  */
34674
- getPublicUrl(path3, options) {
34675
- const _path = this._getFinalPath(path3);
34674
+ getPublicUrl(path4, options) {
34675
+ const _path = this._getFinalPath(path4);
34676
34676
  const query = new URLSearchParams();
34677
34677
  if (options === null || options === void 0 ? void 0 : options.download) query.set("download", options.download === true ? "" : options.download);
34678
34678
  if (options === null || options === void 0 ? void 0 : options.transform) this.applyTransformOptsToQuery(query, options.transform);
@@ -34811,10 +34811,10 @@ var StorageFileApi = class extends BaseApiClient {
34811
34811
  * - `objects` table permissions: `select`
34812
34812
  * - Refer to the [Storage guide](/docs/guides/storage/security/access-control) on how access control works
34813
34813
  */
34814
- async list(path3, options, parameters) {
34814
+ async list(path4, options, parameters) {
34815
34815
  var _this13 = this;
34816
34816
  return _this13.handleOperation(async () => {
34817
- const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path3 || "" });
34817
+ const body = _objectSpread22(_objectSpread22(_objectSpread22({}, DEFAULT_SEARCH_OPTIONS), options), {}, { prefix: path4 || "" });
34818
34818
  return await post(_this13.fetch, `${_this13.url}/object/list/${_this13.bucketId}`, body, { headers: _this13.headers }, parameters);
34819
34819
  });
34820
34820
  }
@@ -34879,11 +34879,11 @@ var StorageFileApi = class extends BaseApiClient {
34879
34879
  if (typeof Buffer !== "undefined") return Buffer.from(data).toString("base64");
34880
34880
  return btoa(data);
34881
34881
  }
34882
- _getFinalPath(path3) {
34883
- return `${this.bucketId}/${path3.replace(/^\/+/, "")}`;
34882
+ _getFinalPath(path4) {
34883
+ return `${this.bucketId}/${path4.replace(/^\/+/, "")}`;
34884
34884
  }
34885
- _removeEmptyFolders(path3) {
34886
- return path3.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
34885
+ _removeEmptyFolders(path4) {
34886
+ return path4.replace(/^\/|\/$/g, "").replace(/\/+/g, "/");
34887
34887
  }
34888
34888
  /** Modifies the `query`, appending values the from `transform` */
34889
34889
  applyTransformOptsToQuery(query, transform2) {
@@ -36381,12 +36381,12 @@ function shouldPropagateToTarget(targetUrl, targets) {
36381
36381
  }
36382
36382
  return false;
36383
36383
  }
36384
- function matchStringTarget(hostname3, target) {
36385
- if (target === hostname3) return true;
36384
+ function matchStringTarget(hostname4, target) {
36385
+ if (target === hostname4) return true;
36386
36386
  if (target.startsWith("*.")) {
36387
36387
  const domain2 = target.slice(2);
36388
- if (hostname3.endsWith(domain2)) {
36389
- if (hostname3 === domain2 || hostname3.endsWith("." + domain2)) return true;
36388
+ if (hostname4.endsWith(domain2)) {
36389
+ if (hostname4 === domain2 || hostname4.endsWith("." + domain2)) return true;
36390
36390
  }
36391
36391
  }
36392
36392
  return false;
@@ -37172,7 +37172,7 @@ async function defaultVerifyOtp(config2, input) {
37172
37172
  }
37173
37173
  async function loginWithDeviceCode(config2, io, deps = {}) {
37174
37174
  const fetchImpl = deps.fetchImpl ?? fetch;
37175
- const sleep2 = deps.sleep ?? defaultSleep;
37175
+ const sleep3 = deps.sleep ?? defaultSleep;
37176
37176
  const openBrowser2 = deps.openBrowser ?? tryOpenBrowser;
37177
37177
  const verifyOtp = deps.verifyOtp ?? defaultVerifyOtp;
37178
37178
  const now = deps.now ?? Date.now;
@@ -37206,7 +37206,7 @@ async function loginWithDeviceCode(config2, io, deps = {}) {
37206
37206
  `The code expired before it was approved. Run '${cliBrand.binName} login --device' again.`
37207
37207
  );
37208
37208
  }
37209
- await sleep2(intervalMs);
37209
+ await sleep3(intervalMs);
37210
37210
  const poll = await postJson(fetchImpl, `${baseUrl}/api/auth/device/token`, {
37211
37211
  device_code: session.device_code
37212
37212
  });
@@ -37359,8 +37359,8 @@ var FileTokenStore = class {
37359
37359
  return `plain file fallback at ${this.filePath}`;
37360
37360
  }
37361
37361
  async chmodOwnerOnly() {
37362
- const { chmod } = await import("node:fs/promises");
37363
- await chmod(this.filePath, 384);
37362
+ const { chmod: chmod2 } = await import("node:fs/promises");
37363
+ await chmod2(this.filePath, 384);
37364
37364
  }
37365
37365
  };
37366
37366
  function createTokenStore(options = {}) {
@@ -37920,8 +37920,8 @@ function getErrorMap2() {
37920
37920
 
37921
37921
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
37922
37922
  var makeIssue = (params) => {
37923
- const { data, path: path3, errorMaps, issueData } = params;
37924
- const fullPath = [...path3, ...issueData.path || []];
37923
+ const { data, path: path4, errorMaps, issueData } = params;
37924
+ const fullPath = [...path4, ...issueData.path || []];
37925
37925
  const fullIssue = {
37926
37926
  ...issueData,
37927
37927
  path: fullPath
@@ -38037,11 +38037,11 @@ var errorUtil;
38037
38037
 
38038
38038
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
38039
38039
  var ParseInputLazyPath = class {
38040
- constructor(parent, value, path3, key) {
38040
+ constructor(parent, value, path4, key) {
38041
38041
  this._cachedPath = [];
38042
38042
  this.parent = parent;
38043
38043
  this.data = value;
38044
- this._path = path3;
38044
+ this._path = path4;
38045
38045
  this._key = key;
38046
38046
  }
38047
38047
  get path() {
@@ -42085,6 +42085,7 @@ The dry run is a real sandbox rehearsal, not a stamp. It executes a mock-provide
42085
42085
  "sync.run.start",
42086
42086
  "sync.run.complete",
42087
42087
  "sync.item.assign",
42088
+ "coach.draft_guide",
42088
42089
  "confirmation.approve"
42089
42090
  ],
42090
42091
  legal: {
@@ -42145,7 +42146,7 @@ Decisions should be recorded as human decisions. Handoffs should attach to seats
42145
42146
  order: 45,
42146
42147
  title: "How agents work",
42147
42148
  summary: "How {{brand}} agents operate inside seats, use Charters, report work, and escalate beyond authority.",
42148
- version: "2026-06-21.6",
42149
+ version: "2026-06-27.1",
42149
42150
  public: true,
42150
42151
  audiences: ["human", "cli", "mcp", "in_app_agent"],
42151
42152
  stages: ["staffing", "operating_rhythm"],
@@ -42155,6 +42156,8 @@ Decisions should be recorded as human decisions. Handoffs should attach to seats
42155
42156
  "agent.status",
42156
42157
  "agent.list_fleet",
42157
42158
  "agent.fleet_digest",
42159
+ "system.health",
42160
+ "team.health_score",
42158
42161
  "agent_setup.get",
42159
42162
  "agent_setup.update",
42160
42163
  "agent.decommission",
@@ -42235,18 +42238,26 @@ Provider requests carry only the model payload the runtime needs; run attributio
42235
42238
 
42236
42239
  Before an agent goes live, the sandbox dry run rehearses this against fake data and returns a per-tool preview: for each tool the agent would touch, whether it would run it, would be blocked, or would escalate \u2014 no external side effect. Review that preview before you approve go-live.
42237
42240
 
42241
+ ## Company guardrails
42242
+
42243
+ Beyond each seat's own manifest, the company can set one ceiling on how much an agent may do *autonomously*. Every tool's scope carries an autonomous-risk level \u2014 reading and drafting are low, writing-with-approval is high, and unrestricted access is critical; an unrecognized tool is treated as critical, never as safe. The guardrail caps that risk for the whole organization through a named profile (locked down, balanced, high autonomy, or a custom ceiling), with balanced as the default. An autonomous call that stays within the ceiling runs as usual; an autonomous call above it is treated as needing a human instead of acting on its own. Calls that are already human-gated or being escalated are never affected \u2014 the ceiling only ever narrows autonomous action, it never blocks the human-approval path. New organizations start balanced and in a watch-only mode, so the guardrail first reports where it would intervene before it begins enforcing.
42244
+
42238
42245
  ## What humans should review
42239
42246
 
42240
42247
  The Agents page shows fleet health first: live state, recent runs, failed runs, open held actions, last activity, cost, and the next operator action. It also shows configuration completeness for each staffed agent seat: Steward chain, lane or runtime substrate, vaulted credentials, granted tools, model config, sandbox dry run, and go-live state, each with a remediation link when incomplete. A seat's Trust Card drills into the same facts: run history, one-run detail, tool-call outcomes, product-visible errors, transcript references, loaded Skill versions, and held confirmations or escalations. Open confirmations stay visible until they are decided or expire, even when they are older than the default activity window. Approving or rejecting a held confirmation from the seat page is scoped to that seat; rejection can include a short reason.
42241
42248
 
42242
- Review the first dry runs, fleet overview, tool-call audit rows, escalations, deliverables, and Signal impact. A seat's completed-work area combines explicit deliverables, work-log evidence, and successful run summaries into one operator-facing trail with safe links labelled as internal, Linear, GitHub, or external. The fleet view at \`/agents\` shows every staffed agent seat at a glance; the agent-native equivalents are \`{{cli}} command agent.list_fleet --json '{}'\` / \`rost_list_agent_fleet\` for the compact overview, and \`{{cli}} agent fleet-digest --json\` / \`agent.fleet_digest\` / \`rost_get_dogfood_fleet_health_digest\` for the daily dogfood evidence bundle. The digest returns live/idle state, 24h/7d turns, 7d/30d spend, recent failed runs, unresolved product errors, failed notifications, and a next action per seat. The \`/health\` dashboard and \`{{cli}} system health --json\` / \`system.health\` / \`rost_system_health\` share the same command-backed system-health model, with server-derived tenant, member, requested-seat, or seat-token scope. Owners see tenant health; members see occupied-seat subtrees; seat pages link to \`/health?seat_id=<seat>\` for scoped follow-up. Every health finding includes a remediation action with a human label/link plus command id, schema reference, CLI command, help text, and human-gated marker for agents. Open Friction without an action task links to the Friction detail and gives agents the \`task.create\` then \`friction.link_task\` next step; the final issue resolution still requires the human \`friction.resolve\` decision. Fleet real-turn counts use the same seat-run association as \`agent.list_runs\`, filtered to real runs. Scheduled agents are checked in rounded five-minute buckets, so a minute-level cron inside the bucket queues one work order for that bucket rather than one order per minute. Sandbox dry runs do not count as real turns. If the agent is repeatedly blocked, revise the Charter or split the seat. If the agent is taking too much judgment, narrow its autonomous scope.`
42249
+ Review the first dry runs, fleet overview, tool-call audit rows, escalations, deliverables, and Signal impact. A seat's completed-work area combines explicit deliverables, work-log evidence, and successful run summaries into one operator-facing trail with safe links labelled as internal, Linear, GitHub, or external. The fleet view at \`/agents\` shows every staffed agent seat at a glance; the agent-native equivalents are \`{{cli}} command agent.list_fleet --json '{}'\` / \`rost_list_agent_fleet\` for the compact overview, and \`{{cli}} agent fleet-digest --json\` / \`agent.fleet_digest\` / \`rost_get_dogfood_fleet_health_digest\` for the daily dogfood evidence bundle. The digest returns live/idle state, 24h/7d turns, 7d/30d spend, recent failed runs, unresolved product errors, failed notifications, and a next action per seat.
42250
+
42251
+ For an operational audit, start with the \`/health\` dashboard or \`{{cli}} system health --json\` / \`system.health\` / \`rost_system_health\` before manually chaining fleet, run, Signal, Friction, task, integration, runner, and Sync commands. The dashboard, CLI, and MCP tool share the same command-backed system-health model, with server-derived tenant, member, requested-seat, or seat-token scope. Owners see tenant health; members see occupied-seat subtrees; seat-scoped tokens see their own seat plus authorized downstream seats, and optional \`seat_id\` narrowing can only reduce that server-derived scope. Seat pages link to \`/health?seat_id=<seat>\` for scoped follow-up. Every health finding includes a remediation action with a human label/link plus command id, schema reference, CLI command, help text, and human-gated marker for agents. Open Friction without an action task links to the Friction detail and gives agents the \`task.create\` then \`friction.link_task\` next step; the final issue resolution still requires the human \`friction.resolve\` decision.
42252
+
42253
+ Use the lower-level commands after health names a finding: \`agent.get_run\` for a specific failed run, \`signal.list\` for off-track readings, \`task.list\` for the owning seat's queue, \`integration.status\` or \`integration.test\` for connector evidence, \`runner.list\` / \`runner.diagnose\` for local-runner repair, and \`sync.brief.compile\` for missing Sync Briefs. Fleet real-turn counts use the same seat-run association as \`agent.list_runs\`, filtered to real runs. Scheduled agents are checked in rounded five-minute buckets, so a minute-level cron inside the bucket queues one work order for that bucket rather than one order per minute. Sandbox dry runs do not count as real turns. If the agent is repeatedly blocked, revise the Charter or split the seat. If the agent is taking too much judgment, narrow its autonomous scope.`
42243
42254
  },
42244
42255
  {
42245
42256
  slug: "tool-access-and-vault",
42246
42257
  order: 46,
42247
42258
  title: "Tool access and vault",
42248
42259
  summary: "How to give agents access to tools without exposing raw credentials or expanding authority by accident.",
42249
- version: "2026-06-20.5",
42260
+ version: "2026-06-27.6",
42250
42261
  public: true,
42251
42262
  audiences: ["human", "cli", "mcp", "in_app_agent"],
42252
42263
  stages: ["staffing"],
@@ -42294,9 +42305,13 @@ For an API with no dedicated connector, the generic REST tool lets a seat call a
42294
42305
 
42295
42306
  \`slack.post_message\` reuses the connected Slack workspace credential and the seat's Slack channel binding. A live call posts only to that bound channel, through the server-side guard and vault-bound bot token. If the bound channel is marked sensitive, the handler escalates for human approval instead of posting. A sandbox dry run makes no Slack request and does not open the vault.
42296
42307
 
42308
+ ## Gmail connector
42309
+
42310
+ The native Gmail handlers run inside the same broker boundary as the REST and Slack connectors: every call passes the server guard and writes an audit row, and the vaulted Google OAuth token is bound only inside a credential closure \u2014 the agent never sees it, and both the access and refresh tokens are redacted from anything a handler returns. \`gmail.read\` (read scoped inbox) and \`gmail.draft\` (compose a draft, never send) are autonomous-eligible. \`gmail.send\` is approval-gated: it never sends autonomously \u2014 it holds the action for steward approval. A sandbox dry run previews the intended read/draft/held-send with no Gmail request and without opening the vault. Live external Gmail send stays gated on Google verification and domain cutover (DER-880 / DER-881); the draft and send-with-approval paths are live now.
42311
+
42297
42312
  ## Google connector status
42298
42313
 
42299
- Google can be connected from Settings once the workspace OAuth app is configured. The connection flow requests offline access for Gmail read/compose and Sheets, stores the returned credential in the vault, and records only account and scope metadata in the integration row. The test action refreshes the vaulted credential and performs a minimal Gmail profile read. Gmail and Sheets agent tools remain configuration-only until their guarded handlers ship; connecting Google does not by itself let an agent read mail or edit a sheet.
42314
+ Google can be connected from Settings once the workspace OAuth app is configured. The connection flow requests offline access for Gmail read/compose and Sheets, stores the returned credential in the vault, and records only account and scope metadata in the integration row. The test action refreshes the vaulted credential and performs a minimal Gmail profile read. Sheets agent tools remain configuration-only until their guarded handlers ship; connecting Google does not by itself let an agent edit a sheet.
42300
42315
 
42301
42316
  CLI and MCP can inspect connector readiness without seeing secrets: \`integration.list\` / \`rost_list_integrations\` lists connected providers and health metadata, \`integration.status\` / \`rost_get_integration_status\` reads one provider by id or name, and \`integration.test\` / \`rost_test_integration_connection\` runs the installed provider-specific health check. For Google, the test refreshes the vaulted OAuth credential and reads the Gmail profile, then records only account, scope, and health metadata.
42302
42317
 
@@ -42331,7 +42346,7 @@ There is exactly one way to give a connected tool its credential, and it is the
42331
42346
  order: 47,
42332
42347
  title: "Available tools guide",
42333
42348
  summary: "How to think about tool categories available to seats and what each category should be used for.",
42334
- version: "2026-06-21.1",
42349
+ version: "2026-06-27.2",
42335
42350
  public: true,
42336
42351
  audiences: ["human", "cli", "mcp", "in_app_agent"],
42337
42352
  stages: ["staffing"],
@@ -42377,14 +42392,14 @@ Agents should explain the job, the required tool category, the minimum permissio
42377
42392
 
42378
42393
  Every tool call passes the server-side guard first: the guard checks the call against the seat's signed permission manifest and records a tool-call audit row for **every** call \u2014 allowed, denied, or escalated. Tool selection is never authorization. Only an allowed call reaches its handler. A connected credential is bound into the handler for the duration of the call only; the secret never appears in the result, the audit summary, logs, or the model's context.
42379
42394
 
42380
- External connectors are being rolled out provider by provider, conservatively (read and draft before send; write behind approval). A selected tool is only a permission until a live handler exists and the seat has the required credential or binding. Today the built-in execution path supports internal status reporting, the generic REST connector when a signed host/method/path allowlist and credential exist, and \`slack.post_message\` for a bound Slack channel. Other provider entries remain configuration-only until their connector ships, so nothing runs silently.`
42395
+ External connectors are being rolled out provider by provider, conservatively (read and draft before send; write behind approval). A selected tool is only a permission until a live handler exists and the seat has the required credential or binding. Today the built-in execution path supports internal status reporting, the generic REST connector when a signed host/method/path allowlist and credential exist, \`slack.post_message\` for a bound Slack channel, and the Gmail handlers (\`gmail.read\`/\`gmail.draft\` autonomous-eligible, \`gmail.send\` held for approval) when Google is connected. Other provider entries remain configuration-only until their connector ships, so nothing runs silently.`
42381
42396
  },
42382
42397
  {
42383
42398
  slug: "mcp-and-cli-guide",
42384
42399
  order: 48,
42385
42400
  title: "CLI and MCP installation guide",
42386
42401
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
42387
- version: "2026-06-21.15",
42402
+ version: "2026-06-28.3",
42388
42403
  public: true,
42389
42404
  audiences: ["human", "cli", "mcp", "in_app_agent"],
42390
42405
  stages: ["company_setup", "staffing"],
@@ -42450,6 +42465,9 @@ External connectors are being rolled out provider by provider, conservatively (r
42450
42465
  "deliverable.get",
42451
42466
  "deliverable.create",
42452
42467
  "deliverable.attach",
42468
+ "deliverable.accept",
42469
+ "usage.report",
42470
+ "usage.snapshot",
42453
42471
  "error_log.list",
42454
42472
  "error_log.resolve",
42455
42473
  "error_log.supersede",
@@ -42780,6 +42798,7 @@ These are the security posture rules for operating after install \u2014 a checkl
42780
42798
  | Command | Purpose | Scope | Safe example |
42781
42799
  |---|---|---|---|
42782
42800
  | \`{{cli}} docs\` | Print the agent-facing how-to summary. | Public reference | \`{{cli}} docs\` |
42801
+ | \`{{cli}} doctor\` | Diagnose local CLI configuration: app URL, Supabase URL, installed version, PATH, session store, local Claude/Codex availability, and update status without printing tokens. | Local environment | \`{{cli}} doctor\` |
42783
42802
  | \`{{cli}} help [group]\` | Print top-level usage, or one operation group's usage; \`{{cli}} <group> --help\` works too. | Public reference | \`{{cli}} help agent\` |
42784
42803
  | \`{{cli}} reference <list|search|get> [options]\` | Help syntax for public reference commands. \`--audience <human|cli|mcp|in_app_agent>\` filters to one audience (omit for all). | Public reference | \`{{cli}} reference search "onboarding" --audience cli\` |
42785
42804
  | \`{{cli}} reference list\` | List public reference guides. | Public reference | \`{{cli}} reference list --audience cli\` |
@@ -42829,11 +42848,12 @@ The signed-in app exposes the same Skill command surface under **Skills** in the
42829
42848
  | \`{{cli}} escalation list|get|resolve|reject\` | \`escalation.list\`, \`escalation.get\`, \`escalation.resolve\`, \`escalation.reject\` | Work the steward escalation queue. | Steward | \`{{cli}} escalation list --json\` |
42830
42849
  | \`{{cli}} error list|resolve|supersede\` | \`error_log.list\`, \`error_log.resolve\`, \`error_log.supersede\` | List active, acknowledged, resolved, or superseded product error logs and let a human acknowledge, resolve, or supersede stale errors with evidence links. | Tenant | \`{{cli}} error list --resolved active --json\`; \`{{cli}} error resolve --error-log-id <id> --reason "Fixed in PR #123"\` |
42831
42850
  | \`{{cli}} sync brief|compile|complete\` | \`sync.brief.get\`, \`sync.brief.compile\`, \`sync.run.complete\` | Compile, read, and complete a weekly Sync. | Tenant | \`{{cli}} sync brief --json\` |
42832
- | \`{{cli}} runner list|status|diagnose|repair|work-orders|revoke\` | \`runner.list\`, \`runner.status\`, \`runner.diagnose\`, \`runner.repair\`, \`work_order.list\`, \`runner.revoke\` | Inspect runners and work orders, diagnose offline runners, get repair guidance, and revoke a runner. | Tenant | \`{{cli}} runner diagnose --runner-id <id> --json\` |
42851
+ | \`{{cli}} runner list|status|diagnose|repair|work-orders|revoke|serve\` | \`runner.list\`, \`runner.status\`, \`runner.diagnose\`, \`runner.repair\`, \`work_order.list\`, \`runner.revoke\`, runner endpoint APIs | Inspect runners and work orders, diagnose offline runners, get repair guidance, revoke a runner, or run the headless local runner loop. | Tenant | \`{{cli}} runner diagnose --runner-id <id> --json\`; \`{{cli}} runner serve --once\` |
42833
42852
  | \`{{cli}} notification settings|test|errors\` | \`notification.settings.get\`, \`notification.test\`, \`notification.list_errors\` | Read notification settings, send a test, and list failed deliveries with linked product error source, seat id, and run id when available. | Tenant | \`{{cli}} notification errors --limit 10 --json\` |
42834
42853
  | \`{{cli}} integration list|readiness|status|test\` | \`integration.list\`, \`integration.readiness\`, \`integration.status\`, \`integration.test\` | List connector metadata, read the setup-readiness checklist, read one connector's health, and run the provider-specific connection test without exposing credentials. | Tenant | \`{{cli}} integration readiness --provider google --json\` |
42835
42854
  | \`{{cli}} system health\` | \`system.health\` | Read the scoped system-health snapshot across agent runs, unresolved errors, Signals, Cascade setup, work loop, integrations, runners, notifications, and Sync Brief readiness. | Tenant, member, or seat token | \`{{cli}} system health --json\`; \`{{cli}} system health --seat <id> --json\` |
42836
- | \`{{cli}} settings get|update|product-learning|rename\` | \`settings.get\`, \`settings.update\`, \`settings.product_learning.get\`, \`settings.product_learning.update\`, \`tenant.rename\` | Read tenant settings, update budget caps, read or update product-learning participation, and rename the company. Budget and product-learning updates stop at human confirmation in non-interactive CLI/MCP sessions; company rename is owner-only and human-gated. | Tenant / Tenant-admin for updates | \`{{cli}} settings product-learning get --json\`; \`{{cli}} settings product-learning update --mode disabled --json\`; \`{{cli}} settings rename --company "Acme Operations"\` |
42855
+ | \`{{cli}} command usage.report|usage.snapshot\` | \`usage.report\`, \`usage.snapshot\` | Read the AI-workforce ROI report (per-period agent turns, token/run cost, human-accepted value, exceptions, approvals) from immutable usage snapshots, and freeze a period's totals into a new snapshot (insert-only, idempotent). | Tenant | \`{{cli}} command usage.report --json\`; \`{{cli}} command usage.snapshot --json\` |
42856
+ | \`{{cli}} settings get|update|product-learning|agent-policy|rename\` | \`settings.get\`, \`settings.update\`, \`settings.product_learning.get\`, \`settings.product_learning.update\`, \`settings.agent_policy.get\`, \`settings.agent_policy.update\`, \`tenant.rename\` | Read tenant settings, update budget caps, read or update product-learning participation, read or set the company autonomy ceiling, and rename the company. Budget, product-learning, and policy updates stop at human confirmation in non-interactive CLI/MCP sessions; company rename is owner-only and human-gated. | Tenant / Tenant-admin for updates | \`{{cli}} settings product-learning get --json\`; \`{{cli}} settings agent-policy get --json\`; \`{{cli}} settings rename --company "Acme Operations"\` |
42837
42857
  | \`{{cli}} member invite|update|remove\` | \`member.invite\`, \`member.update\`, \`member.remove\` | Manage tenant members. | Tenant | \`{{cli}} member invite --email ops@example.com --role member\` |
42838
42858
  | \`{{cli}} agent templates|create|setup|tools|dry-run|go-live|status|run-now|fleet-digest|get-run|show\` | \`agent_template.list\`, \`agent.create_from_template\`, \`agent.create_custom\`, \`agent_setup.get\`, \`agent_setup.update\`, \`agent.configure_tools\`, \`agent.run_dry_run\`, \`agent.go_live\`, \`agent.status\`, \`agent.run_now\`, \`agent.fleet_digest\`, \`agent.get_run\`, \`agent.show_markdown\` | Run the full agent setup and operation flow: list templates, create a draft from a template or guided custom answers (with \`--model\` and \`--effort\`), read or answer setup state, connect or decline tools, dry-run, go live, run on demand, capture the fleet-health digest, read one run's transcript/error diagnostics, and show a markdown readout. Human CLI dry-runs print the rehearsal transcript and, when present, the per-tool preview labels (\`Would run\`, \`Blocked\`, \`Escalated\`) before go-live. Create and go-live stop at human gates; the dry-run is ungated by human approval but requires a signed manifest first. | Tenant and seat | \`{{cli}} agent fleet-digest --json\` |
42839
42859
  | \`{{cli}} tools list\` | \`tool.catalog\` | List the discoverable tool catalog the builder reads (id, scope tiers, credential requirement, access policy, and execution-boundary guidance). | Tenant | \`{{cli}} tools list --json\` |
@@ -42844,6 +42864,8 @@ The signed-in app exposes the same Skill command surface under **Skills** in the
42844
42864
  | \`{{cli}} compass show\` | \`compass.show_markdown\` | Render the current Compass as a clean markdown card for review. | Tenant | \`{{cli}} compass show --markdown\` |
42845
42865
  | \`{{cli}} charter show\` | \`charter.show_markdown\` | Render a seat's Charter as a clean markdown card for review. | Tenant | \`{{cli}} charter show --seat-id <id> --markdown\` |
42846
42866
  | \`{{cli}} deliverable list|get|create|attach\` | \`deliverable.list\`, \`deliverable.get\`, \`deliverable.create\`, \`deliverable.attach\` | List, get, create, or attach agent deliverables for a seat. Deliverables are durable work outputs visible across UI, CLI, and MCP. | Tenant (list/get), Seat (create/attach) | \`{{cli}} deliverable list --seat-id <id> --json\`; \`{{cli}} deliverable create --title "Brief" --kind brief\` |
42867
+ | \`{{cli}} graph show\` | \`graph.get\` | Print a table view of the Responsibility Graph with explicit \`seat_id\` and \`parent_seat_id\` columns. | Tenant | \`{{cli}} graph show\` |
42868
+ | \`{{cli}} seat list|get|create|rename|reparent|decommission\` | \`graph.get\`, \`seat.get\`, \`seat.create\`, \`seat.rename\`, \`seat.reparent\`, \`seat.decommission\`, \`seat.decommission_preview\` | Work with seats as first-class CLI primitives. \`seat decommission --dry-run\` previews affected occupancies, agents, Charters, tokens, credentials, work orders, and schedules before the human-gated teardown. | Tenant / Seat for targeted mutations | \`{{cli}} seat list\`; \`{{cli}} seat decommission --seat-id <id> --dry-run\` |
42847
42869
 
42848
42870
  Skills wrapper help:
42849
42871
 
@@ -42926,6 +42948,7 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
42926
42948
  | \`rost_rename_seat\` | \`seat.rename\` | Rename a seat. | Seat or tenant-admin | Call with \`seat_id\` and \`name\`. |
42927
42949
  | \`rost_reparent_seat\` | \`seat.reparent\` | Move a seat under a new parent. | Seat or tenant-admin | Call with \`seat_id\` and \`parent_seat_id\`. |
42928
42950
  | \`rost_set_seat_type\` | \`seat.set_type\` | Set a seat type. | Seat or tenant-admin | Call with \`seat_id\` and \`seat_type\`. |
42951
+ | \`rost_preview_seat_decommission\` | \`seat.decommission_preview\` | Preview retiring a seat: affected occupancies, agents, Charters, MCP tokens, credentials, work orders, and schedules. No mutation. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>"}\`; no confirmation. |
42929
42952
  | \`rost_decommission_seat\` | \`seat.decommission\` | Retire a seat: end occupancies, archive the Charter, revoke tokens/credentials, cancel schedules, tombstone event. No-orphan guarded. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>"}\`; expect confirmation. |
42930
42953
  | \`rost_draft_charter\` | \`charter.draft\` | Create or fetch a draft Charter. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>"}\`. |
42931
42954
  | \`rost_draft_all_charters\` | \`charter.draft_all\` | Draft Charters for every eligible seat. | Tenant | Call after initial graph creation. |
@@ -42968,6 +42991,9 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
42968
42991
  | \`rost_get_agent_deliverable\` | \`deliverable.get\` | Read one durable agent deliverable by id for a seat. | Seat or tenant-admin | Call with \`{"seat_id":"<seat-id>","deliverable_id":"<id>"}\`. |
42969
42992
  | \`rost_create_agent_deliverable\` | \`deliverable.create\` | Create a scrubbed durable work output for the acting seat. | Seat | Call with title, kind, summary/content, and optional safe links. |
42970
42993
  | \`rost_attach_agent_deliverable\` | \`deliverable.attach\` | Attach a scrubbed deliverable to a source run, task, or work order after server-side seat validation. | Seat | Call with title, kind, and the source ids that belong to the acting seat. |
42994
+ | \`rost_accept_deliverable_value\` | \`deliverable.accept\` | 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. | Tenant-admin | Call with \`{"deliverable_id":"<id>","impact_value_usd":<number>}\`; expect human confirmation. |
42995
+ | \`rost_ai_workforce_roi_report\` | \`usage.report\` | Read the AI-workforce ROI report \u2014 per-period agent turns, token/run cost, human-accepted value, exceptions, and approvals \u2014 from immutable usage snapshots. | Tenant | Call with \`{}\` or \`{"limit":<n>}\`. |
42996
+ | \`rost_capture_usage_period_snapshot\` | \`usage.snapshot\` | Freeze the current (or a given) period's usage and accepted-value totals into an immutable snapshot; insert-only and idempotent. | Tenant | Call with \`{}\` or \`{"period":"YYYY-MM-DD"}\`. |
42971
42997
  | \`rost_list_agent_templates\` | \`agent_template.list\` | List stock agent templates and metadata. | Tenant | Call with \`{}\`. |
42972
42998
  | \`rost_create_agent_from_template\` | \`agent.create_from_template\` | Create a draft stock agent and draft Charter from a template (draft-only; occupancy needs a human steward). | Tenant | Call with \`seat_id\` and \`template_slug\`; expect human confirmation. |
42973
42999
  | \`rost_start_agent_setup\` | \`agent_setup.start\` | Start an agent setup draft for template, custom, or existing mode. | Tenant | Call with \`mode\` and seat placement. |
@@ -43011,6 +43037,8 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
43011
43037
  | \`rost_update_sync_brief_scope\` | \`settings.sync_brief_scope.update\` | Set the tenant's Sync Brief scope (company_wide or per_cluster). | Tenant-admin | Owner-only; call with \`{"sync_brief_scope":"company_wide"}\` or \`{"sync_brief_scope":"per_cluster"}\`. |
43012
43038
  | \`rost_get_product_learning_policy\` | \`settings.product_learning.get\` | Read whether product/page/recommendation analytics are allowed. | Tenant | Call with \`{}\`; audit/security logs are always enabled. |
43013
43039
  | \`rost_update_product_learning_policy\` | \`settings.product_learning.update\` | Set product-learning mode. | Tenant-admin | Human-gated; call with \`{"mode":"enabled"}\`, \`{"mode":"disabled"}\`, or \`{"mode":"enterprise_contract"}\`. |
43040
+ | \`rost_get_company_autonomy_ceiling\` | \`settings.agent_policy.get\` | Read the company autonomy ceiling (Company Guardrails): profile, enforcement, and max_autonomous_risk. | Tenant | Call with \`{}\`; metadata only. |
43041
+ | \`rost_set_company_autonomy_ceiling\` | \`settings.agent_policy.update\` | Set the company autonomy ceiling. | Tenant-admin | Owner-only, human-gated; call with \`{"profile":"locked_down"}\` (or \`balanced\`/\`high_autonomy\`, or \`{"profile":"custom","max_autonomous_risk":"high"}\`). Enforced by default. |
43014
43042
  | \`rost_list_signals\` | \`signal.list\` | List measurables with their latest reading and on/off-track state. | Seat or tenant-admin | Call with \`{}\` to find measurable ids. |
43015
43043
  | \`rost_get_signal\` | \`signal.get\` | Read a measurable with its full reading history. | Seat or tenant-admin | Call with \`{"measurable_id":"<id>"}\`. |
43016
43044
  | \`rost_confirm_signal_reading\` | \`signal.confirm_reading\` | Confirm an unconfirmed reading as human-verified. | Seat or tenant-admin | Humans confirm; call with \`{"reading_id":"<id>"}\`. |
@@ -43020,9 +43048,12 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
43020
43048
  | \`rost_create_cascade_goal\` | \`goal.create\` | Create a cycle goal under an objective. | Tenant | Call with cycle, seat, parent, title, definition of done. |
43021
43049
  | \`rost_update_cascade_goal\` | \`goal.update\` | Update a goal's title or definition of done. | Tenant | Call with \`goal_id\` and the changed fields. |
43022
43050
  | \`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. |
43051
+ | \`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. |
43052
+ | \`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 \`{}\`. |
43023
43053
  | \`rost_reparent_cascade_goal\` | \`goal.reparent\` | Move a goal under a new parent. | Tenant | Authority change; expect confirmation. |
43024
43054
  | \`rost_drop_cascade_goal\` | \`goal.drop\` | Drop (retire) a goal, retained for audit. | Tenant | Expect confirmation. |
43025
43055
  | \`rost_list_friction_issues\` | \`friction.list\` | List Friction issues ranked by impact and severity. | Seat or tenant-admin | Call with \`{}\` or \`{"status":"active"}\`. |
43056
+ | \`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>"}\`. |
43026
43057
  | \`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\`. |
43027
43058
  | \`rost_resolve_friction_issue\` | \`friction.resolve\` | Resolve an issue with a root cause and remediation task. | Tenant | Human decision; expect confirmation. |
43028
43059
  | \`rost_link_task_to_friction_issue\` | \`friction.link_task\` | Attach an existing task as an issue's action task. | Seat or tenant-admin | Call with \`issue_id\` and \`task_id\`. |
@@ -43034,6 +43065,8 @@ Several rows here are seat-operating commands (\`task.create\`, the \`signal.*\`
43034
43065
  | \`rost_assign_sync_follow_up\` | \`sync.item.assign\` | Create a follow-up task from a Sync agenda item. | Tenant | Call with brief, owner seat, title, description. |
43035
43066
  | \`rost_list_escalations\` | \`escalation.list\` | List Steward queue escalations (own steward chain for humans, own seat for agents). | Seat or tenant-admin | Call with \`{}\` or \`{"include_decided":true}\`. |
43036
43067
  | \`rost_get_escalation\` | \`escalation.get\` | Read one escalation's evidence, recommendation, and decision state. | Seat or tenant-admin | Call with \`{"id":"<escalation-id>"}\`. Resolve/reject are human-only and not exposed over MCP. |
43068
+ | \`rost_check_compass_alignment\` | \`compass.alignment\` | Advisory drift check: scores whether cycle goals still ladder to the active vision and flags orphan/contradictory goals. Read-only \u2014 it never edits the Compass or any goal. | Tenant | Call with \`{}\`. |
43069
+ | \`rost_team_health_score\` | \`team.health_score\` | Read the single rolled-up 0-100 team-health score (latest month) with its band and label. | Tenant | Call with \`{}\`. |
43037
43070
  | \`rost_show_compass_as_markdown\` | \`compass.show_markdown\` | Render the current Compass and its open gaps as a clean markdown card for review. | Tenant | Call with \`{}\`. |
43038
43071
  | \`rost_show_charter_as_markdown\` | \`charter.show_markdown\` | Render a seat's active or latest Charter as a clean markdown card for review. | Tenant | Call with \`{"seat_id":"<seat-id>"}\`. |
43039
43072
  | \`rost_show_agent_setup_as_markdown\` | \`agent.show_markdown\` | Render a seat's agent setup, model, steward, tools, and Charter as a clean markdown card for review. | Tenant | Call with \`{"seat_id":"<seat-id>"}\`. |
@@ -43521,7 +43554,7 @@ Retrieve the narrowest relevant guide before making a setup recommendation. Pref
43521
43554
  order: 60,
43522
43555
  title: "Cascade guide",
43523
43556
  summary: "How to connect company goals to seat-level work without turning {{brand}} into a project-management tool.",
43524
- version: "2026-06-20.2",
43557
+ version: "2026-06-28.1",
43525
43558
  public: true,
43526
43559
  audiences: ["human", "cli", "mcp", "in_app_agent"],
43527
43560
  stages: ["operating_rhythm"],
@@ -43533,8 +43566,11 @@ Retrieve the narrowest relevant guide before making a setup recommendation. Pref
43533
43566
  "goal.create",
43534
43567
  "goal.update",
43535
43568
  "goal.set_status",
43569
+ "goal.set_progress",
43570
+ "goal.list_at_risk",
43536
43571
  "goal.reparent",
43537
43572
  "goal.drop",
43573
+ "compass.alignment",
43538
43574
  "confirmation.approve"
43539
43575
  ],
43540
43576
  legal: {
@@ -43576,6 +43612,10 @@ Cascade warnings are advisory, not blockers. They point to setup that should be
43576
43612
 
43577
43613
  On \`/cascade\`, the screen renders the full goal depth, not just one or two levels. Off-track descendant branches stay open so the operator can see why a parent rolled up to risk.
43578
43614
 
43615
+ ## Goal progress and at-risk goals
43616
+
43617
+ A goal can carry a quantified progress (0-100) distinct from its on/off/done status. Report it with \`goal.set_progress\`; when an agent reports progress it lands as a proposal a human approves before the goal moves. \`goal.list_at_risk\` projects which goals are off pace by comparing progress against the elapsed fraction of the cycle window (deterministic date math, no forecast model). Off-pace goals surface on \`/cascade\`, on the owner home, in the weekly Sync Brief, and as a weekly owner alert. Status-only goals (no progress recorded) are simply excluded from the projection.
43618
+
43579
43619
  On human and function seat pages, goal visibility includes both direct goals and descendant seat goals from the Responsibility Graph subtree, so a manager can review team progress without double-owning the agent's work.
43580
43620
 
43581
43621
  ## What does not belong
@@ -43678,6 +43718,7 @@ Agents may record readings when the Charter allows it. Agent-reported readings s
43678
43718
  "compass.answer_gap",
43679
43719
  "friction.file_issue",
43680
43720
  "friction.list",
43721
+ "friction.assist",
43681
43722
  "friction.update_status",
43682
43723
  "friction.resolve",
43683
43724
  "friction.link_task",
@@ -43741,7 +43782,7 @@ Resolving Friction should produce one of four outcomes: a decision, a task, a Ch
43741
43782
  order: 70,
43742
43783
  title: "Steward queue guide",
43743
43784
  summary: "How Stewards review escalations, approve agent boundaries, and keep agents accountable.",
43744
- version: "2026-06-20.1",
43785
+ version: "2026-06-27.2",
43745
43786
  public: true,
43746
43787
  audiences: ["human", "cli", "mcp", "in_app_agent"],
43747
43788
  stages: ["staffing", "operating_rhythm"],
@@ -43780,6 +43821,14 @@ In the app, the evidence is shown as a legible card, not raw data: a proposed to
43780
43821
 
43781
43822
  When nothing is waiting, the Steward page still proves the governance loop. It shows which agent or hybrid seats the human stewards, how many escalations were raised or decided recently, and whether any are open. That empty state is an operating summary, not a dead end.
43782
43823
 
43824
+ ## How escalations reach you
43825
+
43826
+ An escalation reaches you the same way no matter how the agent raised it \u2014 in a chat or CLI session, or on its own scheduled cloud run. When the agent stops at a boundary it opens the queue item and sends a notification to the accountable human along the steward chain, preferring a connected Slack channel and falling back to email. If no human occupies the agent's seat, the chain walks up to the next Steward. Delivery is idempotent: a retried run never notifies you twice for the same escalation, and the notification carries only summarized, redacted evidence \u2014 never secret material.
43827
+
43828
+ ## Where notifications land
43829
+
43830
+ Every notification sent to you \u2014 escalations, approval requests, sync briefs, and your daily digest \u2014 also appears in your in-app Inbox, with unread state you can clear. A Slack or email delivery that fails is retried automatically on a bounded schedule; once it can no longer be delivered it stays visible in the Inbox rather than being lost. A once-a-day digest rolls up the day's deliverables, open approvals, and exceptions into a single message, so a quiet day stays quiet and a busy one is summarized in one place.
43831
+
43783
43832
  ## Work the queue from CLI or MCP
43784
43833
 
43785
43834
  The Steward reads the queue from any surface but decides as a human.
@@ -43801,7 +43850,7 @@ Keep agent scope tight at first. Approve more autonomy only after evidence. Use
43801
43850
  order: 71,
43802
43851
  title: "Confirmations and human gates guide",
43803
43852
  summary: "How {{brand}} routes authority-changing work through human confirmation, and why agents never approve their own requests.",
43804
- version: "2026-06-20.2",
43853
+ version: "2026-06-27.2",
43805
43854
  public: true,
43806
43855
  audiences: ["human", "cli", "mcp", "in_app_agent"],
43807
43856
  stages: ["graph_design", "charter_design", "staffing", "operating_rhythm"],
@@ -43836,6 +43885,8 @@ The \`dangerous\` confirmation **level** is rare and is not the same as the **ri
43836
43885
 
43837
43886
  A gated command does not mutate when an agent calls it. It returns a pending confirmation with the command id, a risk level, and an \`approveVia\` block containing a web URL and the exact \`{{cli}} command confirmation.approve --json ...\` line. The human approves through the web link or the CLI; the agent surfaces the link and stops. A logged-in human at an interactive CLI can run their own gated \`{{cli}} command <id>\` without copy-pasting the approval command because the CLI completes the returned confirmation inline.
43838
43887
 
43888
+ When an **agent** triggers a gate, {{brand}} also notifies the seat's steward (Slack with an email fallback) so the held action does not sit silent \u2014 a human is told a decision is waiting and given the approve link. A human pausing their own gated command does not self-notify. See the notifications guide.
43889
+
43839
43890
  \`\`\`bash
43840
43891
  # Human approves a pending confirmation
43841
43892
  {{cli}} command confirmation.approve --json '{"confirmation_id":"<confirmation-id>"}'
@@ -43843,6 +43894,10 @@ A gated command does not mutate when an agent calls it. It returns a pending con
43843
43894
  {{cli}} command confirmation.reject --json '{"confirmation_id":"<confirmation-id>"}'
43844
43895
  \`\`\`
43845
43896
 
43897
+ ## The approvals queue
43898
+
43899
+ The \`/approvals\` page is one place a human clears every pending decision across the company. It unions still-pending confirmations with open steward escalations into a single queue, each item shown with its risk, the source seat, and redacted arguments \u2014 secret material is never displayed. You see only items for seats you have authority over: an owner sees all of them; a member sees confirmations for the seats they occupy and escalations routed to their steward chain, and nothing else. Approve and reject route through the same \`confirmation.approve\` / \`confirmation.reject\` and escalation decision commands the CLI and per-seat surfaces use, so the audit trail and the agents-recommend-humans-decide rule are identical wherever you act.
43900
+
43846
43901
  ## The rule for agents
43847
43902
 
43848
43903
  An agent never approves or rejects its own request. \`decisions.decided_by\` is always a human. The steward decision commands (\`escalation.resolve\`, \`escalation.reject\`) are not even exposed over MCP. When an agent hits a gate, it prepares the evidence and the recommended action, returns the approve link, and waits for a human.
@@ -43856,11 +43911,11 @@ Stop before: approving a Charter, signing a manifest, connecting a tool or crede
43856
43911
  order: 72,
43857
43912
  title: "Settings guide",
43858
43913
  summary: "How to use Settings as the control plane for company access, channels, providers, tokens, and operating defaults.",
43859
- version: "2026-06-21.4",
43914
+ version: "2026-06-27.1",
43860
43915
  public: true,
43861
43916
  audiences: ["human", "cli", "mcp", "in_app_agent"],
43862
43917
  stages: ["company_setup", "staffing"],
43863
- relatedCommandIds: ["onboarding.create_invite", "mcp_token.create", "mcp_token.revoke", "mcp_token.list", "integration.list", "integration.readiness", "integration.status", "integration.test", "settings.get", "settings.update", "tenant.rename", "settings.sync_brief_scope.get", "settings.sync_brief_scope.update", "settings.product_learning.get", "settings.product_learning.update"],
43918
+ relatedCommandIds: ["onboarding.create_invite", "mcp_token.create", "mcp_token.revoke", "mcp_token.list", "integration.list", "integration.readiness", "integration.status", "integration.test", "settings.get", "settings.update", "tenant.rename", "settings.sync_brief_scope.get", "settings.sync_brief_scope.update", "settings.product_learning.get", "settings.product_learning.update", "settings.agent_policy.get", "settings.agent_policy.update"],
43864
43919
  legal: { publicRisk: "low", notes: ["{{brand}}-native settings guidance."] },
43865
43920
  sources: [
43866
43921
  {
@@ -43913,7 +43968,13 @@ Product-learning participation controls whether {{brand}} may record product/pag
43913
43968
 
43914
43969
  Read the policy with \`settings.product_learning.get\` (CLI: \`{{cli}} settings product-learning get\`). Change it with \`settings.product_learning.update\` (CLI: \`{{cli}} settings product-learning update --mode disabled\`). Analytics ingestion code should call the shared product-learning policy helper instead of reading \`tenants.settings\` directly.
43915
43970
 
43916
- The product analytics foundation stores only bounded product telemetry: product events, page views by route template, command invocation metadata, onboarding milestones, and recommendation outcomes. Authenticated users can read analytics rows for their tenant, but writes are server-owned through the trusted DB helpers. The helpers and database constraints reject or redact obvious raw URLs, filesystem paths, secrets, prompt/content fields, command argument/output fields, direct contact/payment identifiers, and long prose-shaped values. These analytics rows are not audit records; operational logs and security evidence stay in their dedicated append-only or product-visible tables.
43971
+ The product analytics foundation stores only bounded product telemetry: product events, page views by route template, command invocation metadata, onboarding milestones, and recommendation outcomes. Recommendation outcomes use bounded statuses such as \`shown\`, \`accepted\`, \`edited\`, \`rejected\`, \`dismissed\`, and \`reversed\`; current instrumented paths include model-tier recommendations from \`agent.create_custom\` and Charter seat-type recommendations from the Charter lifecycle. These rows store safe enums/categories and fingerprints, not raw responsibility answers, Charter prose, prompt text, tool arguments, or customer content. Authenticated users can read analytics rows for their tenant, but writes are server-owned through the trusted DB helpers. The helpers and database constraints reject or redact obvious raw URLs, filesystem paths, secrets, prompt/content fields, command argument/output fields, direct contact/payment identifiers, and long prose-shaped values. These analytics rows are not audit records; operational logs and security evidence stay in their dedicated append-only or product-visible tables.
43972
+
43973
+ The authenticated web app records first-party page views and product events through route templates, not raw full URLs or query strings. The ingestion route resolves tenant and user server-side, caps client batches, validates enriched payloads with the shared protocol schemas, and treats product-learning opt-out as a skipped analytics write rather than a product error.
43974
+
43975
+ Onboarding milestones are recorded from durable server-side transitions such as company setup, graph review, first Charter draft, first agent setup/dry run/pass/live, first Signal, first goal, Sync rhythm start, and onboarding finish. They are idempotent per tenant and milestone: repeated UI, CLI, MCP, or system calls keep the earliest product-learning timestamp instead of creating duplicate funnel steps. The append-only event log remains the audit timeline; onboarding milestone rows are a bounded analytics projection and are skipped when product learning is disabled.
43976
+
43977
+ Command invocation analytics are emitted from the central command executor for UI, CLI, and MCP calls. They store command id, source, actor/scope kind, outcome class, duration, confirmation flag, guard result, request id when UUID-shaped, safe error code, and bounded changed ids. They do not store raw command arguments or outputs. The command outcome classes are \`ok\`, \`denied\`, \`pending_confirmation\`, \`validation_error\`, \`precondition_error\`, \`authorization_error\`, and \`internal_error\`. Product analytics write failures are ignored so they cannot mask the command result or replace audit/security records.
43917
43978
 
43918
43979
  ## Inference budget
43919
43980
 
@@ -43923,6 +43984,16 @@ Agents that run on {{brand}}-managed inference draw against a tenant inference b
43923
43984
  - The sandbox dry run is free and is never blocked by the cap, so a fresh company can charter, dry-run, and take an agent live before setting a budget. The cap applies only to real managed-inference runs.
43924
43985
  - A company that brings its own provider key (BYOK) is metered on that key and is not subject to the {{brand}}-managed hard cap. BYOK changes the provider account used for eligible cloud calls, not the Charter, tool guard, human gate, or data-retention posture; see the ai-model-data-handling-guide before making provider-handling claims.
43925
43986
 
43987
+ ## Company autonomy ceiling (Company Guardrails)
43988
+
43989
+ The company autonomy ceiling is a single tenant-wide dial that caps how much ANY agent may do **without a human in the loop**. It is the fourth governance gate, after the signed manifest, the budget, and before escalation \u2014 it never replaces them. It only ever constrains AUTONOMOUS (always_allow) actions; a tool already routed to a human (always_ask), an explicit escalation, a connector that already escalated, or secret-shaped input is never blocked by the ceiling.
43990
+
43991
+ - Read it with \`settings.agent_policy.get\` (CLI: \`{{cli}} settings agent-policy get\`).
43992
+ - Set it with \`settings.agent_policy.update\` (CLI: \`{{cli}} settings agent-policy set --profile locked_down\`). Owner-only and human-gated.
43993
+ - Profiles are named ceilings: \`locked_down\` (agents autonomously read/draft only; every write/send escalates), \`balanced\` (the default; read/draft autonomous, writes escalate), \`high_autonomy\` (approval-marked write tools may run autonomously; only full-access escalates), and \`custom\` + \`max_autonomous_risk\` (\`low\`, \`medium\`, \`high\`, \`critical\`).
43994
+ - An explicitly set ceiling is ENFORCED by default. Enforcement has two modes: \`enforce\` blocks an over-ceiling autonomous call and routes it to the steward (it is never a dead-end deny \u2014 a human can still approve), and \`observe\` allows the call but logs it. The platform default for a company that never set a policy is balanced + observe, so existing live agents are never retro-bricked.
43995
+ - When enforcement is on, signing a manifest or taking an agent live is REFUSED if the manifest grants always_allow to a tool whose risk exceeds the ceiling; the error names the exact ceiling, tool, and risk in its \`details\`. Lower that tool to always_ask, or raise the ceiling, then retry.
43996
+
43926
43997
  ## Agent guidance
43927
43998
 
43928
43999
  Agents may explain which setting is needed and why. They should not ask users to paste secrets into chat or tool arguments. When credentials are required, route the user to the vault-backed setup flow.`
@@ -43966,7 +44037,7 @@ When a user asks to add a person, clarify whether they mean app access, seat occ
43966
44037
  order: 74,
43967
44038
  title: "Notifications guide",
43968
44039
  summary: "How {{brand}} should notify humans about decisions, escalations, stale work, and agent boundaries.",
43969
- version: "2026-06-19.1",
44040
+ version: "2026-06-27.1",
43970
44041
  public: true,
43971
44042
  audiences: ["human", "cli", "mcp", "in_app_agent"],
43972
44043
  stages: ["operating_rhythm"],
@@ -43987,6 +44058,7 @@ Notifications should move decisions to the right human without turning {{brand}}
43987
44058
 
43988
44059
  - An agent hits a must-escalate condition.
43989
44060
  - A tool or credential approval is needed.
44061
+ - An agent holds a durable command behind a pending confirmation \u2014 the seat's steward is notified so the held action does not sit silent. A human pausing their own gated command does not self-notify.
43990
44062
  - A Signal is broken or stale.
43991
44063
  - A Friction item needs a decision.
43992
44064
  - A Sync decision creates a handoff.
@@ -44244,7 +44316,7 @@ Name the failing surface, collect evidence, recommend the smallest correction, a
44244
44316
  order: 78,
44245
44317
  title: "AI model data handling guide",
44246
44318
  summary: "How {{brand}} cloud agents, local MCP sessions, runners, BYOK, and connected tools handle model-bound data.",
44247
- version: "2026-06-21.4",
44319
+ version: "2026-06-22.1",
44248
44320
  public: true,
44249
44321
  audiences: ["human", "cli", "mcp", "in_app_agent"],
44250
44322
  stages: ["company_setup", "staffing", "operating_rhythm"],
@@ -44269,27 +44341,27 @@ Name the failing surface, collect evidence, recommend the smallest correction, a
44269
44341
  {
44270
44342
  label: "OpenAI API data controls",
44271
44343
  url: "https://developers.openai.com/api/docs/guides/your-data",
44272
- note: "Official API data-use and retention controls verified 2026-06-21."
44344
+ note: "Official API data-use and retention controls verified 2026-06-22."
44273
44345
  },
44274
44346
  {
44275
44347
  label: "OpenAI enterprise privacy",
44276
44348
  url: "https://openai.com/enterprise-privacy/",
44277
- note: "Official enterprise privacy and API retention summary verified 2026-06-21."
44349
+ note: "Official enterprise privacy and API retention summary verified 2026-06-22."
44278
44350
  },
44279
44351
  {
44280
44352
  label: "OpenAI Codex with ChatGPT plan",
44281
44353
  url: "https://help.openai.com/en/articles/11369540-using-codex-with-your-chatgpt-plan",
44282
- note: "Official Codex plan data-control scope verified 2026-06-21."
44354
+ note: "Official Codex plan data-control scope verified 2026-06-22."
44283
44355
  },
44284
44356
  {
44285
44357
  label: "Anthropic API and data retention",
44286
44358
  url: "https://platform.claude.com/docs/en/manage-claude/api-and-data-retention",
44287
- note: "Official Claude API ZDR, HIPAA, and retention scope verified 2026-06-21."
44359
+ note: "Official Claude API ZDR, HIPAA, and retention scope verified 2026-06-22."
44288
44360
  },
44289
44361
  {
44290
44362
  label: "Claude Code data usage",
44291
44363
  url: "https://code.claude.com/docs/en/data-usage",
44292
- note: "Official local Claude Code data-flow, training, and retention summary verified 2026-06-21."
44364
+ note: "Official local Claude Code data-flow, training, and retention summary verified 2026-06-22."
44293
44365
  },
44294
44366
  {
44295
44367
  label: "Reference corpus and docs spec",
@@ -44392,15 +44464,15 @@ If sensitive data appears by accident, stop, do not repeat it, and route cleanup
44392
44464
 
44393
44465
  ## Provider policy checkpoints
44394
44466
 
44395
- For platform-managed \`cloud\` runs and BYOK Anthropic \`cloud\` runs, check the Anthropic account, workspace, model, and contract. As of 2026-06-21, official Anthropic Claude API documentation says retained API data is not used for model training without express permission, that some API features are ZDR-eligible while others are not, and that ZDR, HIPAA readiness, model-specific retention requirements, consumer products, Claude Code, and third-party integrations each have their own scope.
44467
+ For platform-managed \`cloud\` runs and BYOK Anthropic \`cloud\` runs, check the Anthropic account, workspace, model, and contract. As of 2026-06-22, official Anthropic Claude API documentation says retained API data is not used for model training without express permission, that some API features are ZDR-eligible while others are not, and that ZDR, HIPAA readiness, model-specific retention requirements, consumer products, Claude Code, and third-party integrations each have their own scope.
44396
44468
 
44397
- For local OpenAI API-key clients running through \`mcp_session\` or \`runner\`, check the customer's OpenAI organization, project, endpoint, and API data controls. As of 2026-06-21, official OpenAI API documentation says API data is not used to train or improve OpenAI models unless the customer explicitly opts in, and default abuse-monitoring logs may retain customer content for up to 30 days unless approved controls apply. OpenAI says zero data retention is available only for eligible customers, endpoints, and use cases.
44469
+ For local OpenAI API-key clients running through \`mcp_session\` or \`runner\`, check the customer's OpenAI organization, project, endpoint, and API data controls. As of 2026-06-22, official OpenAI API documentation says API data is not used to train or improve OpenAI models unless the customer explicitly opts in, and default abuse-monitoring logs may retain customer content for up to 30 days unless approved controls apply. OpenAI says zero data retention is available only for eligible customers, endpoints, and use cases.
44398
44470
 
44399
44471
  For Codex specifically, first identify whether the operator is using Codex through a ChatGPT Free/Plus/Pro, Business, Enterprise, Edu, API, or other workspace path. Official OpenAI Codex help says ChatGPT training data controls apply to whether content processed through Codex may be used to improve OpenAI models when Codex is used with a ChatGPT plan, and that Pro and Plus conversations may be used unless training is disabled. Do not give a Codex user the OpenAI API answer unless they are actually using an API-governed Codex path.
44400
44472
 
44401
44473
  For local Claude Code clients running through \`mcp_session\` or \`runner\`, check whether the customer is using a consumer, team, enterprise, API, or third-party-platform path. Official Claude Code documentation separately describes consumer versus commercial handling, local transcript storage, telemetry, feedback, and provider-specific defaults.
44402
44474
 
44403
- Use those statements only as current provider-doc summaries. Do not convert them into a blanket {{brand}} claim. The correct customer-facing answer is: "Here is the lane, here is what {{brand}} sends, here is which provider account or local client handles it, and here is what to verify in that account before treating the workflow as no-training, zero-retention, HIPAA-ready, or region-bound."
44475
+ Use those statements only as current provider-doc summaries. Do not convert them into a blanket {{brand}} claim. The correct customer-facing answer is: "Here is the lane, here is what {{brand}} sends, here is which provider account or local client handles it, and here is what to verify in that account before treating the workflow as no-training, zero-retention, HIPAA-ready, or region-bound." If a customer or prospect asks for a DPA, BAA, SOC 2 report, regional processing commitment, customer-managed retention window, staff-access approval workflow, or customer-visible staff-access log, escalate instead of answering from this guide.
44404
44476
 
44405
44477
  ## Agent response checklist
44406
44478
 
@@ -44410,7 +44482,8 @@ When asked about AI data handling:
44410
44482
  2. Name the specific data classes that can enter the prompt or tool result.
44411
44483
  3. Name whether the provider path is platform-managed, tenant BYOK Anthropic, local client/account, or runner-controlled.
44412
44484
  4. Warn if the answer depends on provider settings, contract terms, ZDR approval, HIPAA readiness, model choice, endpoint, or third-party tool policy.
44413
- 5. Recommend the smallest safe next step: narrow the tool scope, use a vault ref, redact the input, switch to the approved provider account, or escalate for human/legal review.`
44485
+ 5. Escalate contractual, compliance, regional-processing, customer-managed retention, or staff-access-commitment questions instead of answering from this guide.
44486
+ 6. Recommend the smallest safe next step: narrow the tool scope, use a vault ref, redact the input, switch to the approved provider account, or escalate for human/legal review.`
44414
44487
  },
44415
44488
  {
44416
44489
  slug: "security-model-guide",
@@ -45908,12 +45981,12 @@ function resolveModelConfig(model, effort) {
45908
45981
  ...resolvedEffort ? { effort: resolvedEffort } : {}
45909
45982
  };
45910
45983
  }
45911
- function usageWithSchemaFooter(usage, bin) {
45912
- return `${usage}
45984
+ function usageWithSchemaFooter(usage2, bin) {
45985
+ return `${usage2}
45913
45986
  discover a command's exact input with: ${bin} command schema <id>`;
45914
45987
  }
45915
- async function dispatch(context, group, args, actions, usage) {
45916
- const usageText = usageWithSchemaFooter(usage, context.binName);
45988
+ async function dispatch(context, group, args, actions, usage2) {
45989
+ const usageText = usageWithSchemaFooter(usage2, context.binName);
45917
45990
  const action = args[0];
45918
45991
  if (action === "--help" || action === "-h" || action === "help") {
45919
45992
  context.io.stdout.write(`${usageText}
@@ -45939,12 +46012,137 @@ ${usageText}
45939
46012
  }
45940
46013
  }
45941
46014
  function execute(context, parsed, commandId, body, format) {
45942
- const options = parsed.seat === void 0 ? void 0 : { targetSeatId: parsed.seat };
46015
+ const autoConfirm = parsed.flags.has("yes") || parsed.flags.has("y");
46016
+ const options = {
46017
+ ...parsed.seat === void 0 ? {} : { targetSeatId: parsed.seat },
46018
+ ...autoConfirm ? { autoConfirm: true } : {}
46019
+ };
45943
46020
  if (parsed.json) {
45944
46021
  return context.run(context.client, commandId, body, void 0, options);
45945
46022
  }
45946
46023
  return context.run(context.client, commandId, body, format, options);
45947
46024
  }
46025
+ var graphWrapper = (context, args) => dispatch(context, "graph", args, {
46026
+ show: (ctx, rest) => {
46027
+ const parsed = parseFlags(rest);
46028
+ return execute(ctx, parsed, "graph.get", {}, formatGraph);
46029
+ },
46030
+ get: (ctx, rest) => {
46031
+ const parsed = parseFlags(rest);
46032
+ return execute(ctx, parsed, "graph.get", {}, formatGraph);
46033
+ }
46034
+ }, graphUsage(context.binName));
46035
+ function formatGraph(output) {
46036
+ const record2 = asRecord(output);
46037
+ const nodes = asArray(record2.nodes);
46038
+ if (nodes.length === 0) {
46039
+ return "No seats in the Responsibility Graph.";
46040
+ }
46041
+ const lines = [
46042
+ `root_seat_id=${field(record2, "root_seat_id")}`,
46043
+ "seat_id parent_seat_id type status name"
46044
+ ];
46045
+ for (const node of nodes) {
46046
+ const row = asRecord(node);
46047
+ lines.push(`${field(row, "seat_id")} ${field(row, "parent_seat_id")} ${field(row, "seat_type")} ${field(row, "status")} ${field(row, "name")}`);
46048
+ }
46049
+ return lines.join("\n");
46050
+ }
46051
+ function graphUsage(bin) {
46052
+ return `Usage: ${bin} graph show|get [--json]
46053
+ ${bin} graph show`;
46054
+ }
46055
+ var seatWrapper = (context, args) => dispatch(context, "seat", args, {
46056
+ list: (ctx, rest) => {
46057
+ const parsed = parseFlags(rest);
46058
+ return execute(ctx, parsed, "graph.get", {}, formatSeatList);
46059
+ },
46060
+ get: (ctx, rest) => {
46061
+ const parsed = parseFlags(rest);
46062
+ const seatId = requireValue2(parsed, "seat-id");
46063
+ return execute(ctx, parsed, "seat.get", { seat_id: seatId }, formatSeatDetail);
46064
+ },
46065
+ create: (ctx, rest) => {
46066
+ const parsed = parseFlags(rest);
46067
+ const body = withOptional({
46068
+ name: requireValue2(parsed, "name"),
46069
+ seat_type: requireValue2(parsed, "type")
46070
+ }, {
46071
+ parent_seat_id: optionalValue(parsed, "parent-seat-id")
46072
+ });
46073
+ return execute(ctx, parsed, "seat.create", body, formatSeatMutation);
46074
+ },
46075
+ rename: (ctx, rest) => {
46076
+ const parsed = parseFlags(rest);
46077
+ return execute(ctx, parsed, "seat.rename", {
46078
+ seat_id: requireValue2(parsed, "seat-id"),
46079
+ name: requireValue2(parsed, "name")
46080
+ }, formatSeatMutation);
46081
+ },
46082
+ reparent: (ctx, rest) => {
46083
+ const parsed = parseFlags(rest);
46084
+ return execute(ctx, parsed, "seat.reparent", {
46085
+ seat_id: requireValue2(parsed, "seat-id"),
46086
+ new_parent_seat_id: requireValue2(parsed, "parent-seat-id")
46087
+ }, formatSeatMutation);
46088
+ },
46089
+ decommission: (ctx, rest) => {
46090
+ const parsed = parseFlags(rest, /* @__PURE__ */ new Set(["dry-run", "preview"]));
46091
+ if (parsed.flags.has("dry-run") || parsed.flags.has("preview")) {
46092
+ return execute(ctx, parsed, "seat.decommission_preview", {
46093
+ seat_id: requireValue2(parsed, "seat-id")
46094
+ }, formatSeatDecommissionPreview);
46095
+ }
46096
+ return execute(ctx, parsed, "seat.decommission", {
46097
+ seat_id: requireValue2(parsed, "seat-id")
46098
+ }, formatSeatMutation);
46099
+ }
46100
+ }, seatUsage(context.binName));
46101
+ function formatSeatList(output) {
46102
+ const nodes = asArray(asRecord(output).nodes);
46103
+ if (nodes.length === 0) {
46104
+ return "No seats.";
46105
+ }
46106
+ return nodes.map((node) => {
46107
+ const row = asRecord(node);
46108
+ return `seat_id=${field(row, "seat_id")} parent_seat_id=${field(row, "parent_seat_id")} type=${field(row, "seat_type")} status=${field(row, "status")} name=${field(row, "name")}`;
46109
+ }).join("\n");
46110
+ }
46111
+ function formatSeatDetail(output) {
46112
+ const record2 = asRecord(output);
46113
+ const counts = asRecord(record2.counts);
46114
+ return [
46115
+ `seat_id=${field(record2, "seat_id")} name=${field(record2, "name")} type=${field(record2, "seat_type")} status=${field(record2, "status")}`,
46116
+ `parent_seat_id=${field(record2, "parent_seat_id")} active_charter_version_id=${field(record2, "active_charter_version_id")} draft_charter_version_id=${field(record2, "draft_charter_version_id")}`,
46117
+ `agent_status=${field(record2, "agent_status")} agent_lane=${field(record2, "agent_lane")}`,
46118
+ `counts: open_tasks=${field(counts, "open_tasks")} owned_goals=${field(counts, "owned_goals")} active_measurables=${field(counts, "active_measurables")} open_issues=${field(counts, "open_issues")}`
46119
+ ].join("\n");
46120
+ }
46121
+ function formatSeatMutation(output) {
46122
+ const record2 = asRecord(output);
46123
+ return `seat_id=${field(record2, "seat_id")}`;
46124
+ }
46125
+ function formatSeatDecommissionPreview(output) {
46126
+ const record2 = asRecord(output);
46127
+ const effects = asArray(record2.effects).map((effect) => `- ${String(effect)}`);
46128
+ return [
46129
+ `seat_id=${field(record2, "seat_id")} name=${field(record2, "seat_name")} status=${field(record2, "previous_status")}`,
46130
+ `active_occupancies=${field(record2, "active_occupancy_count")} active_agents=${field(record2, "active_agent_ids")}`,
46131
+ `active_charters=${field(record2, "active_charter_count")} mcp_tokens=${field(record2, "active_mcp_token_count")} credentials=${field(record2, "active_credential_count")}`,
46132
+ `open_work_orders=${field(record2, "open_work_order_count")} schedules=${field(record2, "schedule_count")} no_orphan_steward_guard=${field(record2, "no_orphan_steward_guard")}`,
46133
+ "effects:",
46134
+ ...effects
46135
+ ].join("\n");
46136
+ }
46137
+ function seatUsage(bin) {
46138
+ return `Usage: ${bin} seat list|get|create|rename|reparent|decommission [--json] [--yes]
46139
+ ${bin} seat list
46140
+ ${bin} seat get --seat-id <id>
46141
+ ${bin} seat create --name <text> --type human|agent|hybrid [--parent-seat-id <id>]
46142
+ ${bin} seat rename --seat-id <id> --name <text>
46143
+ ${bin} seat reparent --seat-id <id> --parent-seat-id <id> [--yes]
46144
+ ${bin} seat decommission --seat-id <id> [--dry-run] [--yes]`;
46145
+ }
45948
46146
  var taskWrapper = (context, args) => dispatch(context, "task", args, {
45949
46147
  list: (ctx, rest) => {
45950
46148
  const parsed = parseFlags(rest);
@@ -46457,13 +46655,14 @@ var runnerWrapper = (context, args) => dispatch(context, "runner", args, {
46457
46655
  }
46458
46656
  }, runnerUsage(context.binName));
46459
46657
  function runnerUsage(bin) {
46460
- return `Usage: ${bin} runner list|status|diagnose|repair|work-orders|revoke [--json]
46658
+ return `Usage: ${bin} runner list|status|diagnose|repair|work-orders|revoke|serve [--json]
46461
46659
  ${bin} runner list [--include-revoked]
46462
46660
  ${bin} runner status --runner-id <id>
46463
46661
  ${bin} runner diagnose --runner-id <id>
46464
46662
  ${bin} runner repair --runner-id <id> [--issue restart|re_pair|revoke_stale|missing_cli_runtime]
46465
46663
  ${bin} runner work-orders [--status <s>] [--agent-id <id>] [--runner-id <id>] [--limit <n>]
46466
- ${bin} runner revoke --runner-id <id>`;
46664
+ ${bin} runner revoke --runner-id <id>
46665
+ ${bin} runner serve [--name <text>] [--state-file <path>] [--heartbeat-ms <n>] [--once] [--execute]`;
46467
46666
  }
46468
46667
  var notificationWrapper = (context, args) => dispatch(context, "notification", args, {
46469
46668
  settings: (ctx, rest) => {
@@ -46599,6 +46798,30 @@ var settingsWrapper = (context, args) => dispatch(context, "settings", args, {
46599
46798
  });
46600
46799
  }
46601
46800
  }, settingsUsage(ctx.binName)),
46801
+ "agent-policy": (ctx, rest) => dispatch(ctx, "settings agent-policy", rest, {
46802
+ get: (innerCtx, innerRest) => {
46803
+ const parsed = parseFlags(innerRest);
46804
+ return execute(innerCtx, parsed, "settings.agent_policy.get", {}, (output) => {
46805
+ const policy = asRecord(asRecord(output).agent_policy);
46806
+ return `agent_policy profile=${field(policy, "profile")} enforcement=${field(policy, "enforcement")} max_autonomous_risk=${field(policy, "max_autonomous_risk")} explicit=${field(policy, "explicit")}`;
46807
+ });
46808
+ },
46809
+ set: (innerCtx, innerRest) => {
46810
+ const parsed = parseFlags(innerRest);
46811
+ const profile = optionalValue(parsed, "profile");
46812
+ if (profile !== "locked_down" && profile !== "balanced" && profile !== "high_autonomy" && profile !== "custom") {
46813
+ throw new UsageError("Provide --profile locked_down|balanced|high_autonomy|custom.");
46814
+ }
46815
+ const body = withOptional({ profile }, {
46816
+ max_autonomous_risk: optionalValue(parsed, "max-autonomous-risk"),
46817
+ enforcement: optionalValue(parsed, "enforcement")
46818
+ });
46819
+ return execute(innerCtx, parsed, "settings.agent_policy.update", body, (output) => {
46820
+ const policy = asRecord(asRecord(output).agent_policy);
46821
+ return `Set company autonomy ceiling to ${field(policy, "profile")} (max_autonomous_risk=${field(policy, "max_autonomous_risk")}, enforcement=${field(policy, "enforcement")}).`;
46822
+ });
46823
+ }
46824
+ }, settingsUsage(ctx.binName)),
46602
46825
  get: (ctx, rest) => {
46603
46826
  const parsed = parseFlags(rest);
46604
46827
  return execute(ctx, parsed, "settings.get", {}, (output) => {
@@ -46635,11 +46858,13 @@ var settingsWrapper = (context, args) => dispatch(context, "settings", args, {
46635
46858
  }
46636
46859
  }, settingsUsage(context.binName));
46637
46860
  function settingsUsage(bin) {
46638
- return `Usage: ${bin} settings get|update|product-learning|rename [--json]
46861
+ return `Usage: ${bin} settings get|update|product-learning|agent-policy|rename [--json]
46639
46862
  ${bin} settings get
46640
46863
  ${bin} settings update [--soft-cap-usd <n>] [--hard-cap-usd <n>]
46641
46864
  ${bin} settings product-learning get
46642
46865
  ${bin} settings product-learning update --mode enabled|disabled|enterprise_contract
46866
+ ${bin} settings agent-policy get
46867
+ ${bin} settings agent-policy set --profile locked_down|balanced|high_autonomy|custom [--max-autonomous-risk low|medium|high|critical] [--enforcement enforce|observe]
46643
46868
  ${bin} settings rename --company <name>`;
46644
46869
  }
46645
46870
  var memberWrapper = (context, args) => dispatch(context, "member", args, {
@@ -47422,7 +47647,9 @@ var OPERATION_GROUPS = [
47422
47647
  "compass",
47423
47648
  "charter",
47424
47649
  "deliverable",
47425
- "system"
47650
+ "system",
47651
+ "graph",
47652
+ "seat"
47426
47653
  ];
47427
47654
  var wrappers = {
47428
47655
  task: taskWrapper,
@@ -47445,7 +47672,9 @@ var wrappers = {
47445
47672
  compass: compassWrapper,
47446
47673
  charter: charterWrapper,
47447
47674
  deliverable: deliverableWrapper,
47448
- system: systemWrapper
47675
+ system: systemWrapper,
47676
+ graph: graphWrapper,
47677
+ seat: seatWrapper
47449
47678
  };
47450
47679
  function isOperationGroup(value) {
47451
47680
  return value !== void 0 && OPERATION_GROUPS.includes(value);
@@ -47471,7 +47700,9 @@ var groupUsageBuilders = {
47471
47700
  compass: compassUsage,
47472
47701
  charter: charterUsage,
47473
47702
  deliverable: deliverableUsage,
47474
- system: systemUsage
47703
+ system: systemUsage,
47704
+ graph: graphUsage,
47705
+ seat: seatUsage
47475
47706
  };
47476
47707
  function operationGroupUsage(group, bin) {
47477
47708
  return usageWithSchemaFooter(groupUsageBuilders[group](bin), bin);
@@ -47494,9 +47725,9 @@ function operationUsageLines(bin) {
47494
47725
  `${bin} escalation list|get|resolve|reject`,
47495
47726
  `${bin} error list|resolve|supersede`,
47496
47727
  `${bin} sync brief|compile|complete`,
47497
- `${bin} runner list|status|diagnose|repair|work-orders|revoke`,
47728
+ `${bin} runner list|status|diagnose|repair|work-orders|revoke|serve`,
47498
47729
  `${bin} notification settings|test|errors`,
47499
- `${bin} settings get|update|product-learning|rename`,
47730
+ `${bin} settings get|update|product-learning|agent-policy|rename`,
47500
47731
  `${bin} member invite|update|remove`,
47501
47732
  `${bin} agent templates|create|setup|tools|dry-run|go-live|status|run-now|fleet-digest|get-run|show`,
47502
47733
  `${bin} tools list`,
@@ -47504,7 +47735,9 @@ function operationUsageLines(bin) {
47504
47735
  `${bin} model list`,
47505
47736
  `${bin} compass show`,
47506
47737
  `${bin} charter show`,
47507
- `${bin} deliverable list|get|create|attach`
47738
+ `${bin} deliverable list|get|create|attach`,
47739
+ `${bin} graph show`,
47740
+ `${bin} seat list|get|create|rename|reparent|decommission`
47508
47741
  ];
47509
47742
  }
47510
47743
 
@@ -47999,6 +48232,424 @@ function pendingConfirmationFromError(error51) {
47999
48232
  return { confirmationId, commandId };
48000
48233
  }
48001
48234
 
48235
+ // src/doctor.ts
48236
+ import { execFile as execFileCallback2 } from "node:child_process";
48237
+ import { access, readFile as readFile3 } from "node:fs/promises";
48238
+ import { delimiter } from "node:path";
48239
+ import { promisify as promisify3 } from "node:util";
48240
+ var execFile3 = promisify3(execFileCallback2);
48241
+ async function runDoctor(options) {
48242
+ const env = options.env ?? process.env;
48243
+ const store = options.store ?? createTokenStore();
48244
+ const lines = [`${cliBrand.name} CLI doctor`];
48245
+ const config2 = loadConfig(env);
48246
+ lines.push(`app_url: ${config2.appUrl}`);
48247
+ lines.push(`supabase_url: ${config2.supabaseUrl}`);
48248
+ const currentVersion = await readCurrentVersion2();
48249
+ lines.push(`version: ${currentVersion ?? "unknown"}`);
48250
+ const pathStatus = await checkPath(env);
48251
+ lines.push(pathStatus);
48252
+ try {
48253
+ const session = await store.read();
48254
+ lines.push(`session: ${session ? `present (${store.describe()})` : `missing - run ${cliBrand.binName} login --device`}`);
48255
+ } catch (error51) {
48256
+ lines.push(`session: unreadable - ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}`);
48257
+ }
48258
+ const [claude, codex] = await Promise.all([probeCli("claude"), probeCli("codex")]);
48259
+ lines.push(`claude: ${claude}`);
48260
+ lines.push(`codex: ${codex}`);
48261
+ const latest = await fetchLatestVersion2(cliBrand.packageName, env);
48262
+ if (currentVersion && latest && isSemverBehind2(currentVersion, latest)) {
48263
+ lines.push(`update: ${currentVersion} -> ${latest}; run npm i -g ${cliBrand.packageName}@latest`);
48264
+ } else if (latest) {
48265
+ lines.push(`update: current (${latest})`);
48266
+ } else {
48267
+ lines.push("update: skipped or unavailable");
48268
+ }
48269
+ options.io.stdout.write(`${lines.join("\n")}
48270
+ `);
48271
+ return 0;
48272
+ }
48273
+ async function checkPath(env) {
48274
+ const pathValue = env.PATH ?? "";
48275
+ const candidates = pathValue.split(delimiter).filter(Boolean).map((entry) => `${entry}/${cliBrand.binName}`);
48276
+ for (const candidate of candidates) {
48277
+ try {
48278
+ await access(candidate);
48279
+ return `path: found ${candidate}`;
48280
+ } catch {
48281
+ continue;
48282
+ }
48283
+ }
48284
+ return `path: ${cliBrand.binName} not found on PATH - use npx ${cliBrand.packageName}@latest or add your npm global bin directory`;
48285
+ }
48286
+ async function probeCli(command) {
48287
+ try {
48288
+ const { stdout, stderr } = await execFile3(command, ["--version"], { timeout: 1500 });
48289
+ const version4 = (stdout || stderr).trim().split("\n")[0]?.trim();
48290
+ return version4 ? `installed (${version4})` : "installed";
48291
+ } catch {
48292
+ return "not found";
48293
+ }
48294
+ }
48295
+ async function readCurrentVersion2() {
48296
+ try {
48297
+ const url2 = new URL("../package.json", import.meta.url);
48298
+ const parsed = JSON.parse(await readFile3(url2, "utf8"));
48299
+ return typeof parsed.version === "string" ? parsed.version : null;
48300
+ } catch {
48301
+ return null;
48302
+ }
48303
+ }
48304
+ async function fetchLatestVersion2(packageName, env) {
48305
+ if (env.CI || env.VITEST || env.CLI_SKIP_UPDATE_CHECK === "1" || env[`${cliBrand.envPrefix}_CLI_NO_UPDATE_CHECK`] === "1") {
48306
+ return null;
48307
+ }
48308
+ const controller = new AbortController();
48309
+ const timeout = setTimeout(() => controller.abort(), 750);
48310
+ try {
48311
+ const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
48312
+ headers: { accept: "application/json" },
48313
+ signal: controller.signal
48314
+ });
48315
+ if (!response.ok) {
48316
+ return null;
48317
+ }
48318
+ const body = await response.json();
48319
+ return typeof body.version === "string" ? body.version : null;
48320
+ } catch {
48321
+ return null;
48322
+ } finally {
48323
+ clearTimeout(timeout);
48324
+ }
48325
+ }
48326
+ function isSemverBehind2(currentVersion, latestVersion) {
48327
+ const current = parseSemver2(currentVersion);
48328
+ const latest = parseSemver2(latestVersion);
48329
+ if (!current || !latest) {
48330
+ return currentVersion !== latestVersion;
48331
+ }
48332
+ if (current.major !== latest.major) {
48333
+ return current.major < latest.major;
48334
+ }
48335
+ if (current.minor !== latest.minor) {
48336
+ return current.minor < latest.minor;
48337
+ }
48338
+ return current.patch < latest.patch;
48339
+ }
48340
+ function parseSemver2(value) {
48341
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(value);
48342
+ if (!match) {
48343
+ return null;
48344
+ }
48345
+ const [, major, minor, patch] = match;
48346
+ if (major === void 0 || minor === void 0 || patch === void 0) {
48347
+ return null;
48348
+ }
48349
+ return {
48350
+ major: Number.parseInt(major, 10),
48351
+ minor: Number.parseInt(minor, 10),
48352
+ patch: Number.parseInt(patch, 10)
48353
+ };
48354
+ }
48355
+
48356
+ // src/runner-serve.ts
48357
+ import { execFile as execFileCallback3, spawn } from "node:child_process";
48358
+ import { createHash } from "node:crypto";
48359
+ import { chmod, mkdir as mkdir2, readFile as readFile4, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
48360
+ import { hostname as hostname3, tmpdir } from "node:os";
48361
+ import path3 from "node:path";
48362
+ import { setTimeout as sleep2 } from "node:timers/promises";
48363
+ import { promisify as promisify4 } from "node:util";
48364
+ var execFile4 = promisify4(execFileCallback3);
48365
+ async function runRunnerServe(options) {
48366
+ const env = options.env ?? process.env;
48367
+ const fetchImpl = options.fetchImpl ?? fetch;
48368
+ let config2;
48369
+ try {
48370
+ config2 = parseServeArgs(options.args, options.appUrl, env);
48371
+ } catch (error51) {
48372
+ options.io.stderr.write(`${error51 instanceof Error ? error51.message : String(error51)}
48373
+ ${usage()}
48374
+ `);
48375
+ return 1;
48376
+ }
48377
+ options.io.stdout.write(`runner serve: ${options.appUrl} name=${config2.name} execute=${config2.execute}
48378
+ `);
48379
+ let state;
48380
+ try {
48381
+ state = await pairIfNeeded(options.client ?? null, fetchImpl, options.appUrl, config2, options.io);
48382
+ } catch (error51) {
48383
+ options.io.stderr.write(`${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
48384
+ `);
48385
+ return 1;
48386
+ }
48387
+ let beat = 0;
48388
+ let heartbeatSucceeded = false;
48389
+ let failed = false;
48390
+ do {
48391
+ const capabilities = await detectCapabilities();
48392
+ try {
48393
+ const heartbeat = await post2(fetchImpl, options.appUrl, "/api/runner/heartbeat", { capabilities }, state.runner_secret);
48394
+ if (heartbeat.status !== 200) {
48395
+ options.io.stderr.write(`heartbeat ${heartbeat.status}: ${redactForLog(JSON.stringify(heartbeat.json))}
48396
+ `);
48397
+ failed = true;
48398
+ } else {
48399
+ heartbeatSucceeded = true;
48400
+ options.io.stdout.write(`heartbeat ok #${beat} runner_id=${state.runner_id} claude=${capabilities.claude.installed} codex=${capabilities.codex.installed}
48401
+ `);
48402
+ }
48403
+ if (config2.execute && capabilities.claude.installed) {
48404
+ await claimAndExecute(fetchImpl, options.appUrl, state, options.io);
48405
+ }
48406
+ } catch (error51) {
48407
+ failed = true;
48408
+ options.io.stderr.write(`transient runner error (continuing): ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
48409
+ `);
48410
+ }
48411
+ beat += 1;
48412
+ if (!config2.once) {
48413
+ await sleep2(config2.heartbeatMs);
48414
+ }
48415
+ } while (!config2.once);
48416
+ return heartbeatSucceeded && !failed ? 0 : 1;
48417
+ }
48418
+ function parseServeArgs(args, appUrl2, env) {
48419
+ let name = env.RUNNER_NAME ?? `headless-${hostname3()}`;
48420
+ let stateFile = env.RUNNER_STATE_FILE ?? path3.join(tmpdir(), `rost-runner-${createHash("sha1").update(appUrl2).digest("hex").slice(0, 8)}.json`);
48421
+ let heartbeatMs = Number(env.RUNNER_HEARTBEAT_MS ?? 3e4);
48422
+ let execute2 = env.RUNNER_EXECUTE === "1";
48423
+ let once = false;
48424
+ let userCode = env.RUNNER_USER_CODE;
48425
+ for (let index = 0; index < args.length; index += 1) {
48426
+ const token = args[index];
48427
+ if (token === "--name") {
48428
+ name = requiredValue(args, index);
48429
+ index += 1;
48430
+ } else if (token === "--state-file") {
48431
+ stateFile = requiredValue(args, index);
48432
+ index += 1;
48433
+ } else if (token === "--heartbeat-ms") {
48434
+ heartbeatMs = Number(requiredValue(args, index));
48435
+ index += 1;
48436
+ } else if (token === "--user-code") {
48437
+ userCode = requiredValue(args, index);
48438
+ index += 1;
48439
+ } else if (token === "--execute") {
48440
+ execute2 = true;
48441
+ } else if (token === "--once") {
48442
+ once = true;
48443
+ } else {
48444
+ throw new Error(`Unexpected runner serve argument: ${token ?? ""}`);
48445
+ }
48446
+ }
48447
+ if (!Number.isSafeInteger(heartbeatMs) || heartbeatMs < 1e3) {
48448
+ throw new Error("--heartbeat-ms must be an integer >= 1000.");
48449
+ }
48450
+ return { name, stateFile, heartbeatMs, execute: execute2, once, ...userCode ? { userCode } : {} };
48451
+ }
48452
+ function requiredValue(args, index) {
48453
+ const value = args[index + 1];
48454
+ if (value === void 0 || value.startsWith("--")) {
48455
+ throw new Error(`${args[index] ?? "flag"} requires a value.`);
48456
+ }
48457
+ return value;
48458
+ }
48459
+ async function resolveRunnerServeTenant(client) {
48460
+ try {
48461
+ const response = await client.execute("user.whoami", {});
48462
+ const output = response.output && typeof response.output === "object" && !Array.isArray(response.output) ? response.output : {};
48463
+ const activeTenantId = output.active_tenant_id ?? output.current_tenant_id ?? output.tenant_id;
48464
+ return typeof activeTenantId === "string" && activeTenantId.length > 0 ? activeTenantId : null;
48465
+ } catch {
48466
+ return null;
48467
+ }
48468
+ }
48469
+ async function pairIfNeeded(client, fetchImpl, appUrl2, config2, io) {
48470
+ if (client) {
48471
+ const expectedTenantId = await resolveRunnerServeTenant(client);
48472
+ if (expectedTenantId) {
48473
+ config2.expectedTenantId = expectedTenantId;
48474
+ }
48475
+ }
48476
+ const existing = await loadState(config2.stateFile, config2.expectedTenantId);
48477
+ if (existing) {
48478
+ io.stdout.write(`reusing paired runner ${existing.runner_id}
48479
+ `);
48480
+ return existing;
48481
+ }
48482
+ if (!client) {
48483
+ throw new Error(`No paired runner state at ${config2.stateFile}. Run with a CLI login once to pair this runner before starting without a CLI session.`);
48484
+ }
48485
+ const userCode = config2.userCode ?? await startPairing(client, config2);
48486
+ const claim = await post2(fetchImpl, appUrl2, "/api/runner/pairing/claim", { user_code: userCode });
48487
+ const runnerSecret = claim.json.runner_secret;
48488
+ const runnerId = claim.json.runner_id;
48489
+ const tenantId = claim.json.tenant_id;
48490
+ if (claim.status !== 200 || typeof runnerSecret !== "string" || typeof runnerId !== "string") {
48491
+ throw new Error(`pairing claim failed ${claim.status}: ${redactForLog(JSON.stringify(claim.json))}`);
48492
+ }
48493
+ const state = {
48494
+ runner_id: runnerId,
48495
+ runner_secret: runnerSecret,
48496
+ ...typeof tenantId === "string" ? { tenant_id: tenantId } : config2.expectedTenantId ? { tenant_id: config2.expectedTenantId } : {},
48497
+ name: typeof claim.json.name === "string" ? claim.json.name : config2.name
48498
+ };
48499
+ await mkdir2(path3.dirname(config2.stateFile), { recursive: true, mode: 448 });
48500
+ await writeFile2(config2.stateFile, `${JSON.stringify(state)}
48501
+ `, { mode: 384 });
48502
+ await chmod(config2.stateFile, 384);
48503
+ io.stdout.write(`paired runner_id=${runnerId} state_file=${config2.stateFile}
48504
+ `);
48505
+ return state;
48506
+ }
48507
+ async function loadState(filePath, expectedTenantId) {
48508
+ try {
48509
+ const parsed = JSON.parse(await readFile4(filePath, "utf8"));
48510
+ if (typeof parsed.runner_id === "string" && typeof parsed.runner_secret === "string") {
48511
+ if (expectedTenantId && parsed.tenant_id !== expectedTenantId) {
48512
+ return null;
48513
+ }
48514
+ return {
48515
+ runner_id: parsed.runner_id,
48516
+ runner_secret: parsed.runner_secret,
48517
+ ...typeof parsed.tenant_id === "string" ? { tenant_id: parsed.tenant_id } : {},
48518
+ ...typeof parsed.name === "string" ? { name: parsed.name } : {}
48519
+ };
48520
+ }
48521
+ } catch {
48522
+ return null;
48523
+ }
48524
+ return null;
48525
+ }
48526
+ async function startPairing(client, config2) {
48527
+ const response = await client.execute("runner.pairing.start", { name: config2.name, platform: process.platform });
48528
+ const userCode = response.output && typeof response.output === "object" ? response.output.user_code : void 0;
48529
+ if (typeof userCode !== "string" || userCode.length === 0) {
48530
+ throw new Error("runner.pairing.start did not return a user_code.");
48531
+ }
48532
+ return userCode;
48533
+ }
48534
+ async function detectCapabilities() {
48535
+ const [claude, codex] = await Promise.all([probe("claude"), probe("codex")]);
48536
+ return { claude, codex };
48537
+ }
48538
+ async function probe(command) {
48539
+ try {
48540
+ const { stdout, stderr } = await execFile4(command, ["--version"], { timeout: 1500 });
48541
+ return { installed: true, version: (stdout || stderr).trim() || null, auth_state: "ready" };
48542
+ } catch {
48543
+ return { installed: false, version: null, auth_state: "unknown" };
48544
+ }
48545
+ }
48546
+ async function post2(fetchImpl, appUrl2, pathName, body, token) {
48547
+ const headers = { "content-type": "application/json" };
48548
+ if (token) {
48549
+ headers.authorization = `Bearer ${token}`;
48550
+ }
48551
+ const response = await fetchImpl(`${appUrl2.replace(/\/+$/, "")}${pathName}`, {
48552
+ method: "POST",
48553
+ headers,
48554
+ body: JSON.stringify(body)
48555
+ });
48556
+ const text = await response.text();
48557
+ let json2;
48558
+ try {
48559
+ json2 = JSON.parse(text);
48560
+ } catch {
48561
+ json2 = { raw: text.slice(0, 300) };
48562
+ }
48563
+ return { status: response.status, json: json2 };
48564
+ }
48565
+ async function claimAndExecute(fetchImpl, appUrl2, state, io) {
48566
+ const claimed = await post2(fetchImpl, appUrl2, "/api/runner/work-orders/claim", {}, state.runner_secret);
48567
+ const workOrder = claimed.json.work_order;
48568
+ if (claimed.status !== 200 || !workOrder || typeof workOrder !== "object") {
48569
+ return;
48570
+ }
48571
+ const workOrderId = workOrder.id;
48572
+ if (typeof workOrderId !== "string") {
48573
+ return;
48574
+ }
48575
+ const started = await post2(fetchImpl, appUrl2, `/api/runner/work-orders/${encodeURIComponent(workOrderId)}/start`, {}, state.runner_secret);
48576
+ if (started.status !== 200) {
48577
+ throw new Error(`work order start failed ${started.status}: ${redactForLog(JSON.stringify(started.json))}`);
48578
+ }
48579
+ const result = await runClaudeTurn(appUrl2, workOrder);
48580
+ const reported = await post2(fetchImpl, appUrl2, `/api/runner/work-orders/${encodeURIComponent(workOrderId)}/result`, {
48581
+ status: result.ok ? "succeeded" : "failed",
48582
+ summary: result.summary,
48583
+ log_ref: `runner-local:${workOrderId}`,
48584
+ model: "claude-cli"
48585
+ }, state.runner_secret);
48586
+ if (reported.status !== 200) {
48587
+ throw new Error(`work order result failed ${reported.status}: ${redactForLog(JSON.stringify(reported.json))}`);
48588
+ }
48589
+ io.stdout.write(`work_order_id=${workOrderId} status=${result.ok ? "succeeded" : "failed"}
48590
+ `);
48591
+ }
48592
+ async function runClaudeTurn(appUrl2, workOrder) {
48593
+ const mcp = typeof workOrder.mcp === "object" && workOrder.mcp !== null ? workOrder.mcp : {};
48594
+ const token = typeof mcp.token === "string" ? mcp.token : "";
48595
+ const url2 = typeof mcp.url === "string" ? `${appUrl2.replace(/\/+$/, "")}${mcp.url}` : `${appUrl2.replace(/\/+$/, "")}/mcp`;
48596
+ const mcpConfig = JSON.stringify({ mcpServers: { rost: { type: "http", url: url2, headers: { Authorization: `Bearer ${token}` } } } });
48597
+ const configPath = path3.join(tmpdir(), `rost-runner-mcp-${createHash("sha256").update(`${url2}:${Date.now()}:${Math.random()}`).digest("hex").slice(0, 16)}.json`);
48598
+ const context = {
48599
+ work_order_id: workOrder.id,
48600
+ task_id: workOrder.task_id ?? null,
48601
+ seat: workOrder.seat ?? null,
48602
+ charter: workOrder.charter ?? null,
48603
+ permission_manifest: workOrder.permission_manifest ?? null
48604
+ };
48605
+ const prompt = [
48606
+ `Load your ${cliBrand.name} context, report one read-only status, and stop.`,
48607
+ "Do not call external systems or mutating tools.",
48608
+ `Claimed work order context: ${JSON.stringify(context).slice(0, 8e3)}`
48609
+ ].join("\n");
48610
+ await writeFile2(configPath, `${mcpConfig}
48611
+ `, { mode: 384 });
48612
+ await chmod(configPath, 384);
48613
+ return new Promise((resolve2) => {
48614
+ let settled = false;
48615
+ const finish = (result) => {
48616
+ if (settled) {
48617
+ return;
48618
+ }
48619
+ settled = true;
48620
+ void rm2(configPath, { force: true });
48621
+ resolve2(result);
48622
+ };
48623
+ const child = spawn("claude", ["-p", prompt, "--mcp-config", configPath, "--strict-mcp-config", "--output-format", "text"], {
48624
+ cwd: tmpdir(),
48625
+ stdio: ["ignore", "pipe", "pipe"]
48626
+ });
48627
+ let out = "";
48628
+ let err = "";
48629
+ const timer = setTimeout(() => {
48630
+ child.kill("SIGKILL");
48631
+ finish({ ok: false, summary: "turn timed out after 180s" });
48632
+ }, 18e4);
48633
+ child.stdout.on("data", (data) => {
48634
+ out += data.toString("utf8");
48635
+ });
48636
+ child.stderr.on("data", (data) => {
48637
+ err += data.toString("utf8");
48638
+ });
48639
+ child.on("close", (code) => {
48640
+ clearTimeout(timer);
48641
+ finish({ ok: code === 0, summary: redactForLog((out || err || "completed").trim()).slice(0, 600) });
48642
+ });
48643
+ child.on("error", (error51) => {
48644
+ clearTimeout(timer);
48645
+ finish({ ok: false, summary: `spawn error: ${redactForLog(error51.message)}` });
48646
+ });
48647
+ });
48648
+ }
48649
+ function usage() {
48650
+ return "Usage: rost runner serve [--name <text>] [--state-file <path>] [--heartbeat-ms <n>] [--user-code <code>] [--once] [--execute]";
48651
+ }
48652
+
48002
48653
  // src/index.ts
48003
48654
  async function main(argv = process.argv.slice(2), options = {}) {
48004
48655
  const io = options.io ?? {
@@ -48052,6 +48703,13 @@ async function main(argv = process.argv.slice(2), options = {}) {
48052
48703
  }
48053
48704
  return result.exitCode;
48054
48705
  }
48706
+ if (command === "doctor") {
48707
+ return runDoctor({
48708
+ io,
48709
+ env: process.env,
48710
+ ...options.store ? { store: options.store } : {}
48711
+ });
48712
+ }
48055
48713
  if (command === "onboard" && args[0] === "run") {
48056
48714
  io.stdout.write(`${renderOnboardRunPrompt()}
48057
48715
  `);
@@ -48107,12 +48765,35 @@ async function main(argv = process.argv.slice(2), options = {}) {
48107
48765
  `);
48108
48766
  justLoggedIn = true;
48109
48767
  }
48768
+ const config2 = loadConfig();
48769
+ if (command === "runner" && args[0] === "serve") {
48770
+ let runnerClient = null;
48771
+ if (session) {
48772
+ try {
48773
+ const refreshed2 = await refreshSession(config2, session);
48774
+ if (refreshed2 !== session) {
48775
+ session = refreshed2;
48776
+ await store.write(session);
48777
+ }
48778
+ runnerClient = new CommandClient({ appUrl: config2.appUrl, token: session.accessToken });
48779
+ } catch (error51) {
48780
+ io.stderr.write(`CLI session unavailable; trying paired runner state only: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
48781
+ `);
48782
+ }
48783
+ }
48784
+ return runRunnerServe({
48785
+ io,
48786
+ client: runnerClient,
48787
+ appUrl: config2.appUrl,
48788
+ args: args.slice(1),
48789
+ env: process.env
48790
+ });
48791
+ }
48110
48792
  if (!session) {
48111
48793
  io.stderr.write(`Not logged in. Run ${cliBrand.binName} login first.
48112
48794
  `);
48113
48795
  return 1;
48114
48796
  }
48115
- const config2 = loadConfig();
48116
48797
  let refreshed;
48117
48798
  try {
48118
48799
  refreshed = await refreshSession(config2, session);
@@ -48229,9 +48910,10 @@ async function executeDirectCommand(io, client, args) {
48229
48910
  }
48230
48911
  const commandId = first;
48231
48912
  let seat;
48913
+ let autoConfirm = false;
48232
48914
  let rest;
48233
48915
  try {
48234
- ({ seat, rest } = extractSeatFlag(args.slice(1)));
48916
+ ({ seat, autoConfirm, rest } = extractCommandMetaFlags(args.slice(1)));
48235
48917
  } catch (error51) {
48236
48918
  io.stderr.write(`${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
48237
48919
  `);
@@ -48252,11 +48934,15 @@ async function executeDirectCommand(io, client, args) {
48252
48934
  `);
48253
48935
  return 1;
48254
48936
  }
48255
- return printCommandOutput(io, client, commandId, body, void 0, seat === void 0 ? {} : { targetSeatId: seat });
48937
+ return printCommandOutput(io, client, commandId, body, void 0, {
48938
+ ...seat === void 0 ? {} : { targetSeatId: seat },
48939
+ autoConfirm
48940
+ });
48256
48941
  }
48257
- function extractSeatFlag(args) {
48942
+ function extractCommandMetaFlags(args) {
48258
48943
  const rest = [];
48259
48944
  let seat;
48945
+ let autoConfirm = false;
48260
48946
  for (let index = 0; index < args.length; index += 1) {
48261
48947
  const token = args[index];
48262
48948
  if (token === "--seat") {
@@ -48268,11 +48954,15 @@ function extractSeatFlag(args) {
48268
48954
  index += 1;
48269
48955
  continue;
48270
48956
  }
48957
+ if (token === "--yes" || token === "-y") {
48958
+ autoConfirm = true;
48959
+ continue;
48960
+ }
48271
48961
  if (token !== void 0) {
48272
48962
  rest.push(token);
48273
48963
  }
48274
48964
  }
48275
- return { ...seat === void 0 ? {} : { seat }, rest };
48965
+ return { ...seat === void 0 ? {} : { seat }, autoConfirm, rest };
48276
48966
  }
48277
48967
  var SeatFlagError = class extends Error {
48278
48968
  };
@@ -48287,7 +48977,9 @@ function formatCommandSchema(output) {
48287
48977
  if (typeof help === "string" && help.length > 0) {
48288
48978
  lines.push(`help: ${help}`);
48289
48979
  }
48290
- lines.push("", "input_schema:", JSON.stringify(record2.input_schema, null, 2));
48980
+ const schemaSummary = formatInputSchemaSummary(record2.input_schema);
48981
+ lines.push("", "input:");
48982
+ lines.push(schemaSummary || " no input fields");
48291
48983
  if (record2.example !== void 0) {
48292
48984
  lines.push("", "example input:", JSON.stringify(record2.example, null, 2));
48293
48985
  }
@@ -48301,6 +48993,23 @@ function formatCommandSchema(output) {
48301
48993
  }
48302
48994
  return lines.join("\n");
48303
48995
  }
48996
+ function formatInputSchemaSummary(schema) {
48997
+ const root = asRecord3(schema);
48998
+ const properties = asRecord3(root.properties);
48999
+ const required2 = new Set(asStringArray(root.required));
49000
+ const entries = Object.entries(properties);
49001
+ if (entries.length === 0) {
49002
+ return "";
49003
+ }
49004
+ return entries.map(([key, value]) => {
49005
+ const prop = asRecord3(value);
49006
+ const type = Array.isArray(prop.type) ? prop.type.join("|") : field2(prop, "type");
49007
+ const enumValues = asStringArray(prop.enum);
49008
+ const description = typeof prop.description === "string" ? ` \u2014 ${prop.description}` : "";
49009
+ const enumText = enumValues.length > 0 ? ` enum=${enumValues.join("|")}` : "";
49010
+ return ` ${key}${required2.has(key) ? " (required)" : " (optional)"}: ${type}${enumText}${description}`;
49011
+ }).join("\n");
49012
+ }
48304
49013
  function formatCommandList(output) {
48305
49014
  const commands = Array.isArray(asRecord3(output).commands) ? asRecord3(output).commands : [];
48306
49015
  if (commands.length === 0) {
@@ -48427,10 +49136,12 @@ async function printCommandOutput(io, client, commandId, body = {}, format, opti
48427
49136
  `);
48428
49137
  return 0;
48429
49138
  } catch (error51) {
48430
- if (io.interactive === true) {
49139
+ const envAutoConfirm = process.env[`${cliBrand.envPrefix}_CLI_AUTO_CONFIRM`] === "1";
49140
+ if (io.interactive === true || options.autoConfirm === true || envAutoConfirm) {
48431
49141
  const pending = pendingConfirmationFromError2(error51);
48432
49142
  if (pending && pending.commandId === commandId) {
48433
- io.stderr.write(`Approving ${pending.commandId} confirmation ${pending.confirmationId} as the interactive CLI user.
49143
+ const reason = io.interactive === true ? "interactive CLI user" : options.autoConfirm === true ? "explicit CLI auto-confirm" : `${cliBrand.envPrefix}_CLI_AUTO_CONFIRM environment auto-confirm`;
49144
+ io.stderr.write(`Approving ${pending.commandId} confirmation ${pending.confirmationId} as the ${reason}.
48434
49145
  `);
48435
49146
  try {
48436
49147
  const approved = await client.execute("confirmation.approve", { confirmation_id: pending.confirmationId });
@@ -48475,6 +49186,14 @@ function printCommandError2(io, error51) {
48475
49186
  io.stderr.write(`\u2192 try: ${redactForLog(help)}
48476
49187
  `);
48477
49188
  }
49189
+ if (error51.response.error.code === "COMMAND_CONFIRMATION_REQUIRED") {
49190
+ const details = asRecord3(error51.response.error.details);
49191
+ const expiresAt = details.expiresAt;
49192
+ if (typeof expiresAt === "string" && expiresAt.length > 0) {
49193
+ io.stderr.write(`confirmation expires at: ${redactForLog(expiresAt)}
49194
+ `);
49195
+ }
49196
+ }
48478
49197
  if (error51.status === 409) {
48479
49198
  return 3;
48480
49199
  }
@@ -48496,7 +49215,7 @@ function parseJsonBody(args) {
48496
49215
  return JSON.parse(body);
48497
49216
  }
48498
49217
  function isCommand(value) {
48499
- return value === "login" || value === "logout" || value === "signup" || value === "whoami" || value === "tenants" || value === "use" || value === "command" || value === "onboard" || value === "mcp" || value === "skills" || value === "init" || value === "docs" || value === "reference" || value === "help";
49218
+ return value === "login" || value === "logout" || value === "signup" || value === "whoami" || value === "tenants" || value === "use" || value === "command" || value === "onboard" || value === "mcp" || value === "skills" || value === "init" || value === "docs" || value === "reference" || value === "doctor" || value === "help";
48500
49219
  }
48501
49220
  function printUsage(io) {
48502
49221
  io.stdout.write([
@@ -48510,6 +49229,7 @@ function printUsage(io) {
48510
49229
  `${cliBrand.binName} command <id> [--seat <seat-id>] --json [json-body]`,
48511
49230
  `${cliBrand.binName} command schema <id> [--json]`,
48512
49231
  `${cliBrand.binName} command list [--json]`,
49232
+ `${cliBrand.binName} doctor`,
48513
49233
  ...operationUsageLines(cliBrand.binName),
48514
49234
  `${cliBrand.binName} onboard status`,
48515
49235
  `${cliBrand.binName} onboard run`,