@cfio/cohort-sync 0.34.2 → 0.34.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -207,11 +207,11 @@ var TypeBoxError = class extends Error {
207
207
  };
208
208
 
209
209
  // ../../node_modules/.pnpm/@sinclair+typebox@0.34.48/node_modules/@sinclair/typebox/build/esm/type/symbols/symbols.mjs
210
- var TransformKind = Symbol.for("TypeBox.Transform");
211
- var ReadonlyKind = Symbol.for("TypeBox.Readonly");
212
- var OptionalKind = Symbol.for("TypeBox.Optional");
213
- var Hint = Symbol.for("TypeBox.Hint");
214
- var Kind = Symbol.for("TypeBox.Kind");
210
+ var TransformKind = /* @__PURE__ */ Symbol.for("TypeBox.Transform");
211
+ var ReadonlyKind = /* @__PURE__ */ Symbol.for("TypeBox.Readonly");
212
+ var OptionalKind = /* @__PURE__ */ Symbol.for("TypeBox.Optional");
213
+ var Hint = /* @__PURE__ */ Symbol.for("TypeBox.Hint");
214
+ var Kind = /* @__PURE__ */ Symbol.for("TypeBox.Kind");
215
215
 
216
216
  // ../../node_modules/.pnpm/@sinclair+typebox@0.34.48/node_modules/@sinclair/typebox/build/esm/type/guard/kind.mjs
217
217
  function IsReadonly(value) {
@@ -449,8 +449,8 @@ function IsPattern(value) {
449
449
  function IsControlCharacterFree(value) {
450
450
  if (!IsString(value))
451
451
  return false;
452
- for (let i = 0; i < value.length; i++) {
453
- const code2 = value.charCodeAt(i);
452
+ for (let i2 = 0; i2 < value.length; i2++) {
453
+ const code2 = value.charCodeAt(i2);
454
454
  if (code2 >= 7 && code2 <= 13 || code2 === 27 || code2 === 127) {
455
455
  return false;
456
456
  }
@@ -977,20 +977,20 @@ function* FromTerminal(syntax) {
977
977
  const R = FromSyntax(syntax.slice(1));
978
978
  return yield* [L, ...R];
979
979
  }
980
- for (let i = 2; i < syntax.length; i++) {
981
- if (syntax[i] === "}") {
982
- const L = FromUnion(syntax.slice(2, i));
983
- const R = FromSyntax(syntax.slice(i + 1));
980
+ for (let i2 = 2; i2 < syntax.length; i2++) {
981
+ if (syntax[i2] === "}") {
982
+ const L = FromUnion(syntax.slice(2, i2));
983
+ const R = FromSyntax(syntax.slice(i2 + 1));
984
984
  return yield* [...L, ...R];
985
985
  }
986
986
  }
987
987
  yield Literal(syntax);
988
988
  }
989
989
  function* FromSyntax(syntax) {
990
- for (let i = 0; i < syntax.length; i++) {
991
- if (syntax[i] === "$") {
992
- const L = Literal(syntax.slice(0, i));
993
- const R = FromTerminal(syntax.slice(i));
990
+ for (let i2 = 0; i2 < syntax.length; i2++) {
991
+ if (syntax[i2] === "$") {
992
+ const L = Literal(syntax.slice(0, i2));
993
+ const R = FromTerminal(syntax.slice(i2));
994
994
  return yield* [L, ...R];
995
995
  }
996
996
  }
@@ -2619,11 +2619,13 @@ var Type = type_exports2;
2619
2619
  import { Buffer as Buffer2 } from "node:buffer";
2620
2620
 
2621
2621
  // src/hooks.ts
2622
- import fs2 from "node:fs";
2622
+ import fs3 from "node:fs";
2623
2623
  import os2 from "node:os";
2624
- import path2 from "node:path";
2624
+ import path3 from "node:path";
2625
2625
 
2626
2626
  // src/sync.ts
2627
+ import fs from "node:fs";
2628
+ import path from "node:path";
2627
2629
  var VALID_STATUSES = /* @__PURE__ */ new Set(["idle", "working", "waiting"]);
2628
2630
  function normalizeStatus(status) {
2629
2631
  return VALID_STATUSES.has(status) ? status : "idle";
@@ -2632,39 +2634,39 @@ function trimTrailingSlashes(url) {
2632
2634
  while (url.endsWith("/")) url = url.slice(0, -1);
2633
2635
  return url;
2634
2636
  }
2635
- async function v1Get(apiUrl2, apiKey2, path3) {
2636
- const res = await fetch(`${trimTrailingSlashes(apiUrl2)}${path3}`, {
2637
+ async function v1Get(apiUrl2, apiKey2, path4) {
2638
+ const res = await fetch(`${trimTrailingSlashes(apiUrl2)}${path4}`, {
2637
2639
  headers: { Authorization: `Bearer ${apiKey2}` },
2638
2640
  signal: AbortSignal.timeout(1e4)
2639
2641
  });
2640
- if (!res.ok) throw new Error(`GET ${path3} \u2192 ${res.status}`);
2642
+ if (!res.ok) throw new Error(`GET ${path4} \u2192 ${res.status}`);
2641
2643
  return res.json();
2642
2644
  }
2643
- async function v1Patch(apiUrl2, apiKey2, path3, body) {
2644
- const res = await fetch(`${trimTrailingSlashes(apiUrl2)}${path3}`, {
2645
+ async function v1Patch(apiUrl2, apiKey2, path4, body) {
2646
+ const res = await fetch(`${trimTrailingSlashes(apiUrl2)}${path4}`, {
2645
2647
  method: "PATCH",
2646
2648
  headers: { Authorization: `Bearer ${apiKey2}`, "Content-Type": "application/json" },
2647
2649
  body: JSON.stringify(body),
2648
2650
  signal: AbortSignal.timeout(1e4)
2649
2651
  });
2650
- if (!res.ok) throw new Error(`PATCH ${path3} \u2192 ${res.status}`);
2652
+ if (!res.ok) throw new Error(`PATCH ${path4} \u2192 ${res.status}`);
2651
2653
  }
2652
- async function v1Post(apiUrl2, apiKey2, path3, body) {
2653
- const res = await fetch(`${trimTrailingSlashes(apiUrl2)}${path3}`, {
2654
+ async function v1Post(apiUrl2, apiKey2, path4, body) {
2655
+ const res = await fetch(`${trimTrailingSlashes(apiUrl2)}${path4}`, {
2654
2656
  method: "POST",
2655
2657
  headers: { Authorization: `Bearer ${apiKey2}`, "Content-Type": "application/json" },
2656
2658
  body: JSON.stringify(body),
2657
2659
  signal: AbortSignal.timeout(1e4)
2658
2660
  });
2659
- if (!res.ok) throw new Error(`POST ${path3} \u2192 ${res.status}`);
2661
+ if (!res.ok) throw new Error(`POST ${path4} \u2192 ${res.status}`);
2660
2662
  }
2661
2663
  function isNewerVersion(a, b) {
2662
2664
  const strip = (v2) => v2.replace(/-.*$/, "");
2663
2665
  const pa = strip(a).split(".").map(Number);
2664
2666
  const pb = strip(b).split(".").map(Number);
2665
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
2666
- const na = pa[i] ?? 0;
2667
- const nb = pb[i] ?? 0;
2667
+ for (let i2 = 0; i2 < Math.max(pa.length, pb.length); i2++) {
2668
+ const na = pa[i2] ?? 0;
2669
+ const nb = pb[i2] ?? 0;
2668
2670
  if (isNaN(na) || isNaN(nb)) return false;
2669
2671
  if (na > nb) return true;
2670
2672
  if (na < nb) return false;
@@ -2753,7 +2755,12 @@ async function reconcileRoster(openClawAgents, cfg, logger) {
2753
2755
  displayName: oc.identity?.name ?? agentName,
2754
2756
  emoji: oc.identity?.emoji ?? "\u{1F916}",
2755
2757
  model: oc.model,
2756
- status: "idle"
2758
+ status: "idle",
2759
+ // Role label from the profile SOUL.md, set ONLY at first registration so
2760
+ // agents get a default title. Omitted when absent (the backend skips an
2761
+ // undefined title). Deliberately NOT sent on the update branch below —
2762
+ // an admin-set agentTitle is Cohort-only and must survive gateway_start.
2763
+ title: oc.identity?.title
2757
2764
  });
2758
2765
  logger.info(`cohort-sync: provisioned new agent "${agentName}"`);
2759
2766
  } catch (err) {
@@ -2819,7 +2826,94 @@ async function markAllUnreachable(cfg, logger) {
2819
2826
  }
2820
2827
  logger.info("cohort-sync: all agents marked unreachable");
2821
2828
  }
2822
- async function fullSync(agentName, model, cfg, logger, openClawAgents) {
2829
+ var MAX_SKILL_BYTES = 64 * 1024;
2830
+ var MAX_SYNC_SKILLS = 500;
2831
+ function parseSkillFrontmatter(text) {
2832
+ if (!text) return null;
2833
+ const lines = text.split(/\r?\n/);
2834
+ if (lines.length === 0 || lines[0].trim() !== "---") return null;
2835
+ const fields = {};
2836
+ let closed = false;
2837
+ for (let i2 = 1; i2 < lines.length; i2++) {
2838
+ const line = lines[i2];
2839
+ if (line.trim() === "---") {
2840
+ closed = true;
2841
+ break;
2842
+ }
2843
+ const first = line.charAt(0);
2844
+ if (line.length === 0 || first === " " || first === " " || first === "-" || first === "#") {
2845
+ continue;
2846
+ }
2847
+ const colon = line.indexOf(":");
2848
+ if (colon === -1) continue;
2849
+ const key = line.slice(0, colon).trim();
2850
+ if (key !== "name" && key !== "description") continue;
2851
+ fields[key] = stripScalar(line.slice(colon + 1));
2852
+ }
2853
+ if (!closed) return null;
2854
+ const name = fields.name;
2855
+ if (!name) return null;
2856
+ return { name, description: fields.description ?? "" };
2857
+ }
2858
+ function stripScalar(value) {
2859
+ const v2 = value.trim();
2860
+ if (v2.length >= 2 && v2[0] === v2[v2.length - 1] && (v2[0] === "'" || v2[0] === '"')) {
2861
+ return v2.slice(1, -1);
2862
+ }
2863
+ return v2;
2864
+ }
2865
+ function enumerateSkills(skillsDir, source = "hermes") {
2866
+ const byName = /* @__PURE__ */ new Map();
2867
+ const visit = (dir) => {
2868
+ let entries;
2869
+ try {
2870
+ entries = fs.readdirSync(dir);
2871
+ } catch {
2872
+ return;
2873
+ }
2874
+ for (const entry of entries) {
2875
+ const full = path.join(dir, entry);
2876
+ let isDir = false;
2877
+ try {
2878
+ isDir = fs.statSync(full).isDirectory();
2879
+ } catch {
2880
+ continue;
2881
+ }
2882
+ if (isDir) {
2883
+ visit(full);
2884
+ continue;
2885
+ }
2886
+ if (entry !== "SKILL.md") continue;
2887
+ let text;
2888
+ try {
2889
+ text = fs["read"+"FileSync"](full, "utf-8").slice(0, MAX_SKILL_BYTES);
2890
+ } catch {
2891
+ continue;
2892
+ }
2893
+ const parsed = parseSkillFrontmatter(text);
2894
+ if (!parsed) continue;
2895
+ if (byName.has(parsed.name)) continue;
2896
+ byName.set(parsed.name, { name: parsed.name, description: parsed.description, source });
2897
+ }
2898
+ };
2899
+ visit(skillsDir);
2900
+ return [...byName.values()];
2901
+ }
2902
+ function skillsDirExists(skillsDir) {
2903
+ try {
2904
+ return fs.statSync(skillsDir).isDirectory();
2905
+ } catch {
2906
+ return false;
2907
+ }
2908
+ }
2909
+ async function syncSkills(agentName, skills, cfg, logger) {
2910
+ const capped = skills.slice(0, MAX_SYNC_SKILLS);
2911
+ if (capped.length < skills.length) {
2912
+ logger.warn(`cohort-sync: capping skill sync at ${MAX_SYNC_SKILLS} (had ${skills.length})`);
2913
+ }
2914
+ await v1Post(cfg.apiUrl, cfg.apiKey, "/api/v1/skills/sync", { agentName, skills: capped });
2915
+ }
2916
+ async function fullSync(agentName, model, cfg, logger, openClawAgents, skillsDir) {
2823
2917
  logger.info("cohort-sync: full sync starting");
2824
2918
  if (openClawAgents && openClawAgents.length > 0) {
2825
2919
  try {
@@ -2830,7 +2924,21 @@ async function fullSync(agentName, model, cfg, logger, openClawAgents) {
2830
2924
  } else {
2831
2925
  await syncAgentStatus(agentName, "working", model, cfg, logger);
2832
2926
  }
2833
- logger.info("cohort-sync: skill sync not available in this version");
2927
+ if (skillsDir) {
2928
+ try {
2929
+ if (!skillsDirExists(skillsDir)) {
2930
+ logger.info(`cohort-sync: skill sync skipped (skills dir missing or unreadable: ${skillsDir})`);
2931
+ } else {
2932
+ const skills = enumerateSkills(skillsDir, "openclaw");
2933
+ await syncSkills(agentName, skills, cfg, logger);
2934
+ logger.info(`cohort-sync: synced ${skills.length} skill(s) from ${skillsDir}`);
2935
+ }
2936
+ } catch (err) {
2937
+ logger.warn(`cohort-sync: skill sync failed (non-fatal): ${String(err)}`);
2938
+ }
2939
+ } else {
2940
+ logger.info("cohort-sync: skill sync skipped (no skills dir resolved)");
2941
+ }
2834
2942
  logger.info("cohort-sync: full sync complete");
2835
2943
  }
2836
2944
 
@@ -2861,13 +2969,13 @@ var len;
2861
2969
  revLookup["-".charCodeAt(0)] = 62;
2862
2970
  revLookup["_".charCodeAt(0)] = 63;
2863
2971
  function getLens(b64) {
2864
- var len = b64.length;
2865
- if (len % 4 > 0) {
2972
+ var len2 = b64.length;
2973
+ if (len2 % 4 > 0) {
2866
2974
  throw new Error("Invalid string. Length must be a multiple of 4");
2867
2975
  }
2868
2976
  var validLen = b64.indexOf("=");
2869
- if (validLen === -1) validLen = len;
2870
- var placeHoldersLen = validLen === len ? 0 : 4 - validLen % 4;
2977
+ if (validLen === -1) validLen = len2;
2978
+ var placeHoldersLen = validLen === len2 ? 0 : 4 - validLen % 4;
2871
2979
  return [validLen, placeHoldersLen];
2872
2980
  }
2873
2981
  function byteLength(b64) {
@@ -2886,20 +2994,20 @@ function toByteArray(b64) {
2886
2994
  var placeHoldersLen = lens[1];
2887
2995
  var arr2 = new Arr(_byteLength(b64, validLen, placeHoldersLen));
2888
2996
  var curByte = 0;
2889
- var len = placeHoldersLen > 0 ? validLen - 4 : validLen;
2890
- var i;
2891
- for (i = 0; i < len; i += 4) {
2892
- tmp = revLookup[b64.charCodeAt(i)] << 18 | revLookup[b64.charCodeAt(i + 1)] << 12 | revLookup[b64.charCodeAt(i + 2)] << 6 | revLookup[b64.charCodeAt(i + 3)];
2997
+ var len2 = placeHoldersLen > 0 ? validLen - 4 : validLen;
2998
+ var i2;
2999
+ for (i2 = 0; i2 < len2; i2 += 4) {
3000
+ tmp = revLookup[b64.charCodeAt(i2)] << 18 | revLookup[b64.charCodeAt(i2 + 1)] << 12 | revLookup[b64.charCodeAt(i2 + 2)] << 6 | revLookup[b64.charCodeAt(i2 + 3)];
2893
3001
  arr2[curByte++] = tmp >> 16 & 255;
2894
3002
  arr2[curByte++] = tmp >> 8 & 255;
2895
3003
  arr2[curByte++] = tmp & 255;
2896
3004
  }
2897
3005
  if (placeHoldersLen === 2) {
2898
- tmp = revLookup[b64.charCodeAt(i)] << 2 | revLookup[b64.charCodeAt(i + 1)] >> 4;
3006
+ tmp = revLookup[b64.charCodeAt(i2)] << 2 | revLookup[b64.charCodeAt(i2 + 1)] >> 4;
2899
3007
  arr2[curByte++] = tmp & 255;
2900
3008
  }
2901
3009
  if (placeHoldersLen === 1) {
2902
- tmp = revLookup[b64.charCodeAt(i)] << 10 | revLookup[b64.charCodeAt(i + 1)] << 4 | revLookup[b64.charCodeAt(i + 2)] >> 2;
3010
+ tmp = revLookup[b64.charCodeAt(i2)] << 10 | revLookup[b64.charCodeAt(i2 + 1)] << 4 | revLookup[b64.charCodeAt(i2 + 2)] >> 2;
2903
3011
  arr2[curByte++] = tmp >> 8 & 255;
2904
3012
  arr2[curByte++] = tmp & 255;
2905
3013
  }
@@ -2911,32 +3019,32 @@ function tripletToBase64(num) {
2911
3019
  function encodeChunk(uint8, start, end) {
2912
3020
  var tmp;
2913
3021
  var output = [];
2914
- for (var i = start; i < end; i += 3) {
2915
- tmp = (uint8[i] << 16 & 16711680) + (uint8[i + 1] << 8 & 65280) + (uint8[i + 2] & 255);
3022
+ for (var i2 = start; i2 < end; i2 += 3) {
3023
+ tmp = (uint8[i2] << 16 & 16711680) + (uint8[i2 + 1] << 8 & 65280) + (uint8[i2 + 2] & 255);
2916
3024
  output.push(tripletToBase64(tmp));
2917
3025
  }
2918
3026
  return output.join("");
2919
3027
  }
2920
3028
  function fromByteArray(uint8) {
2921
3029
  var tmp;
2922
- var len = uint8.length;
2923
- var extraBytes = len % 3;
3030
+ var len2 = uint8.length;
3031
+ var extraBytes = len2 % 3;
2924
3032
  var parts = [];
2925
3033
  var maxChunkLength = 16383;
2926
- for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
3034
+ for (var i2 = 0, len22 = len2 - extraBytes; i2 < len22; i2 += maxChunkLength) {
2927
3035
  parts.push(
2928
3036
  encodeChunk(
2929
3037
  uint8,
2930
- i,
2931
- i + maxChunkLength > len2 ? len2 : i + maxChunkLength
3038
+ i2,
3039
+ i2 + maxChunkLength > len22 ? len22 : i2 + maxChunkLength
2932
3040
  )
2933
3041
  );
2934
3042
  }
2935
3043
  if (extraBytes === 1) {
2936
- tmp = uint8[len - 1];
3044
+ tmp = uint8[len2 - 1];
2937
3045
  parts.push(lookup[tmp >> 2] + lookup[tmp << 4 & 63] + "==");
2938
3046
  } else if (extraBytes === 2) {
2939
- tmp = (uint8[len - 2] << 8) + uint8[len - 1];
3047
+ tmp = (uint8[len2 - 2] << 8) + uint8[len2 - 1];
2940
3048
  parts.push(
2941
3049
  lookup[tmp >> 10] + lookup[tmp >> 4 & 63] + lookup[tmp << 2 & 63] + "="
2942
3050
  );
@@ -3014,9 +3122,9 @@ function slowBigIntToBase64(value) {
3014
3122
  let hex = value.toString(16);
3015
3123
  if (hex.length % 2 === 1) hex = "0" + hex;
3016
3124
  const bytes = new Uint8Array(new ArrayBuffer(8));
3017
- let i = 0;
3125
+ let i2 = 0;
3018
3126
  for (const hexByte of hex.match(/.{2}/g).reverse()) {
3019
- bytes.set([parseInt(hexByte, 16)], i++);
3127
+ bytes.set([parseInt(hexByte, 16)], i2++);
3020
3128
  value >>= EIGHT;
3021
3129
  }
3022
3130
  return fromByteArray(bytes);
@@ -3071,11 +3179,11 @@ function validateObjectField(k) {
3071
3179
  if (k.startsWith("$")) {
3072
3180
  throw new Error(`Field name ${k} starts with a '$', which is reserved.`);
3073
3181
  }
3074
- for (let i = 0; i < k.length; i += 1) {
3075
- const charCode = k.charCodeAt(i);
3182
+ for (let i2 = 0; i2 < k.length; i2 += 1) {
3183
+ const charCode = k.charCodeAt(i2);
3076
3184
  if (charCode < 32 || charCode >= 127) {
3077
3185
  throw new Error(
3078
- `Field name ${k} has invalid character '${k[i]}': Field names can only contain non-control ASCII characters`
3186
+ `Field name ${k} has invalid character '${k[i2]}': Field names can only contain non-control ASCII characters`
3079
3187
  );
3080
3188
  }
3081
3189
  }
@@ -3211,7 +3319,7 @@ function convexToJsonInternal(value, originalValue, context, includeTopLevelUnde
3211
3319
  }
3212
3320
  if (Array.isArray(value)) {
3213
3321
  return value.map(
3214
- (value2, i) => convexToJsonInternal(value2, originalValue, context + `[${i}]`, false)
3322
+ (value2, i2) => convexToJsonInternal(value2, originalValue, context + `[${i2}]`, false)
3215
3323
  );
3216
3324
  }
3217
3325
  if (value instanceof Set) {
@@ -3947,7 +4055,7 @@ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp3(obj, key, {
3947
4055
  var __publicField2 = (obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
3948
4056
  var _a;
3949
4057
  var _b;
3950
- var IDENTIFYING_FIELD = Symbol.for("ConvexError");
4058
+ var IDENTIFYING_FIELD = /* @__PURE__ */ Symbol.for("ConvexError");
3951
4059
  var ConvexError = class extends (_b = Error, _a = IDENTIFYING_FIELD, _b) {
3952
4060
  constructor(data) {
3953
4061
  super(typeof data === "string" ? data : stringifyValueForError(data));
@@ -3989,7 +4097,7 @@ var DefaultLogger = class {
3989
4097
  }
3990
4098
  addLogLineListener(func) {
3991
4099
  let id = Math.random().toString(36).substring(2, 15);
3992
- for (let i = 0; i < 10; i++) {
4100
+ for (let i2 = 0; i2 < 10; i2++) {
3993
4101
  if (this._onLogLineFuncs[id] === void 0) {
3994
4102
  break;
3995
4103
  }
@@ -4621,10 +4729,10 @@ var RequestManager = class {
4621
4729
  };
4622
4730
 
4623
4731
  // ../../node_modules/.pnpm/convex@1.33.0_patch_hash=l43bztwr6e2lbmpd6ao6hmcg24_react@19.2.1/node_modules/convex/dist/esm/server/functionName.js
4624
- var functionName = Symbol.for("functionName");
4732
+ var functionName = /* @__PURE__ */ Symbol.for("functionName");
4625
4733
 
4626
4734
  // ../../node_modules/.pnpm/convex@1.33.0_patch_hash=l43bztwr6e2lbmpd6ao6hmcg24_react@19.2.1/node_modules/convex/dist/esm/server/components/paths.js
4627
- var toReferencePath = Symbol.for("toReferencePath");
4735
+ var toReferencePath = /* @__PURE__ */ Symbol.for("toReferencePath");
4628
4736
  function extractReferencePath(reference) {
4629
4737
  return reference[toReferencePath] ?? null;
4630
4738
  }
@@ -4691,12 +4799,12 @@ function createApi(pathParts = []) {
4691
4799
  `API path is expected to be of the form \`api.moduleName.functionName\`. Found: \`${found}\``
4692
4800
  );
4693
4801
  }
4694
- const path3 = pathParts.slice(0, -1).join("/");
4802
+ const path4 = pathParts.slice(0, -1).join("/");
4695
4803
  const exportName = pathParts[pathParts.length - 1];
4696
4804
  if (exportName === "default") {
4697
- return path3;
4805
+ return path4;
4698
4806
  } else {
4699
- return path3 + ":" + exportName;
4807
+ return path4 + ":" + exportName;
4700
4808
  }
4701
4809
  } else if (prop === Symbol.toStringTag) {
4702
4810
  return "FunctionReference";
@@ -6213,12 +6321,12 @@ var BaseConvexClient = class {
6213
6321
  this.debug = options.reportDebugInfoToConvex ?? false;
6214
6322
  this.address = address;
6215
6323
  this.logger = options.logger === false ? instantiateNoopLogger({ verbose: options.verbose ?? false }) : options.logger !== true && options.logger ? options.logger : instantiateDefaultLogger({ verbose: options.verbose ?? false });
6216
- const i = address.search("://");
6217
- if (i === -1) {
6324
+ const i2 = address.search("://");
6325
+ if (i2 === -1) {
6218
6326
  throw new Error("Provided address was not an absolute URL.");
6219
6327
  }
6220
- const origin = address.substring(i + 3);
6221
- const protocol = address.substring(0, i);
6328
+ const origin = address.substring(i2 + 3);
6329
+ const protocol = address.substring(0, i2);
6222
6330
  let wsProtocol;
6223
6331
  if (protocol === "http") {
6224
6332
  wsProtocol = "ws";
@@ -7754,10 +7862,10 @@ var require_constants = __commonJS({
7754
7862
  EMPTY_BUFFER: Buffer.alloc(0),
7755
7863
  GUID: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
7756
7864
  hasBlob,
7757
- kForOnEventAttribute: Symbol("kIsForOnEventAttribute"),
7758
- kListener: Symbol("kListener"),
7759
- kStatusCode: Symbol("status-code"),
7760
- kWebSocket: Symbol("websocket"),
7865
+ kForOnEventAttribute: /* @__PURE__ */ Symbol("kIsForOnEventAttribute"),
7866
+ kListener: /* @__PURE__ */ Symbol("kListener"),
7867
+ kStatusCode: /* @__PURE__ */ Symbol("status-code"),
7868
+ kWebSocket: /* @__PURE__ */ Symbol("websocket"),
7761
7869
  NOOP: () => {
7762
7870
  }
7763
7871
  };
@@ -7765,8 +7873,8 @@ var require_constants = __commonJS({
7765
7873
  });
7766
7874
  var require_node_gyp_build = __commonJS({
7767
7875
  "../common/temp/node_modules/.pnpm/node-gyp-build@4.8.4/node_modules/node-gyp-build/node-gyp-build.js"(exports, module) {
7768
- var fs3 = __require("fs");
7769
- var path3 = __require("path");
7876
+ var fs4 = __require("fs");
7877
+ var path4 = __require("path");
7770
7878
  var os3 = __require("os");
7771
7879
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
7772
7880
  var vars = process.config && process.config.variables || {};
@@ -7783,21 +7891,21 @@ var require_node_gyp_build = __commonJS({
7783
7891
  return runtimeRequire(load.resolve(dir));
7784
7892
  }
7785
7893
  load.resolve = load.path = function(dir) {
7786
- dir = path3.resolve(dir || ".");
7894
+ dir = path4.resolve(dir || ".");
7787
7895
  try {
7788
- var name = runtimeRequire(path3.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
7896
+ var name = runtimeRequire(path4.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
7789
7897
  if (define_process_env_default[name + "_PREBUILD"]) dir = define_process_env_default[name + "_PREBUILD"];
7790
7898
  } catch (err) {
7791
7899
  }
7792
7900
  if (!prebuildsOnly) {
7793
- var release = getFirst(path3.join(dir, "build/Release"), matchBuild);
7901
+ var release = getFirst(path4.join(dir, "build/Release"), matchBuild);
7794
7902
  if (release) return release;
7795
- var debug = getFirst(path3.join(dir, "build/Debug"), matchBuild);
7903
+ var debug = getFirst(path4.join(dir, "build/Debug"), matchBuild);
7796
7904
  if (debug) return debug;
7797
7905
  }
7798
7906
  var prebuild = resolve(dir);
7799
7907
  if (prebuild) return prebuild;
7800
- var nearby = resolve(path3.dirname(process.execPath));
7908
+ var nearby = resolve(path4.dirname(process.execPath));
7801
7909
  if (nearby) return nearby;
7802
7910
  var target = [
7803
7911
  "platform=" + platform,
@@ -7814,26 +7922,26 @@ var require_node_gyp_build = __commonJS({
7814
7922
  ].filter(Boolean).join(" ");
7815
7923
  throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
7816
7924
  function resolve(dir2) {
7817
- var tuples = readdirSync(path3.join(dir2, "prebuilds")).map(parseTuple);
7925
+ var tuples = readdirSync(path4.join(dir2, "prebuilds")).map(parseTuple);
7818
7926
  var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0];
7819
7927
  if (!tuple) return;
7820
- var prebuilds = path3.join(dir2, "prebuilds", tuple.name);
7928
+ var prebuilds = path4.join(dir2, "prebuilds", tuple.name);
7821
7929
  var parsed = readdirSync(prebuilds).map(parseTags);
7822
7930
  var candidates = parsed.filter(matchTags(runtime, abi));
7823
7931
  var winner = candidates.sort(compareTags(runtime))[0];
7824
- if (winner) return path3.join(prebuilds, winner.file);
7932
+ if (winner) return path4.join(prebuilds, winner.file);
7825
7933
  }
7826
7934
  };
7827
7935
  function readdirSync(dir) {
7828
7936
  try {
7829
- return fs3.readdirSync(dir);
7937
+ return fs4.readdirSync(dir);
7830
7938
  } catch (err) {
7831
7939
  return [];
7832
7940
  }
7833
7941
  }
7834
7942
  function getFirst(dir, filter) {
7835
7943
  var files = readdirSync(dir).filter(filter);
7836
- return files[0] && path3.join(dir, files[0]);
7944
+ return files[0] && path4.join(dir, files[0]);
7837
7945
  }
7838
7946
  function matchBuild(name) {
7839
7947
  return /\.node$/.test(name);
@@ -7863,8 +7971,8 @@ var require_node_gyp_build = __commonJS({
7863
7971
  var extension = arr2.pop();
7864
7972
  var tags = { file, specificity: 0 };
7865
7973
  if (extension !== "node") return;
7866
- for (var i = 0; i < arr2.length; i++) {
7867
- var tag = arr2[i];
7974
+ for (var i2 = 0; i2 < arr2.length; i2++) {
7975
+ var tag = arr2[i2];
7868
7976
  if (tag === "node" || tag === "electron" || tag === "node-webkit") {
7869
7977
  tags.runtime = tag;
7870
7978
  } else if (tag === "napi") {
@@ -7920,7 +8028,7 @@ var require_node_gyp_build = __commonJS({
7920
8028
  return typeof window !== "undefined" && window.process && window.process.type === "renderer";
7921
8029
  }
7922
8030
  function isAlpine(platform2) {
7923
- return platform2 === "linux" && fs3.existsSync("/etc/alpine-release");
8031
+ return platform2 === "linux" && fs4.existsSync("/etc/alpine-release");
7924
8032
  }
7925
8033
  load.parseTags = parseTags;
7926
8034
  load.matchTags = matchTags;
@@ -7944,14 +8052,14 @@ var require_fallback = __commonJS({
7944
8052
  "../common/temp/node_modules/.pnpm/bufferutil@4.0.9/node_modules/bufferutil/fallback.js"(exports, module) {
7945
8053
  "use strict";
7946
8054
  var mask = (source, mask2, output, offset, length) => {
7947
- for (var i = 0; i < length; i++) {
7948
- output[offset + i] = source[i] ^ mask2[i & 3];
8055
+ for (var i2 = 0; i2 < length; i2++) {
8056
+ output[offset + i2] = source[i2] ^ mask2[i2 & 3];
7949
8057
  }
7950
8058
  };
7951
8059
  var unmask = (buffer, mask2) => {
7952
8060
  const length = buffer.length;
7953
- for (var i = 0; i < length; i++) {
7954
- buffer[i] ^= mask2[i & 3];
8061
+ for (var i2 = 0; i2 < length; i2++) {
8062
+ buffer[i2] ^= mask2[i2 & 3];
7955
8063
  }
7956
8064
  };
7957
8065
  module.exports = { mask, unmask };
@@ -7977,8 +8085,8 @@ var require_buffer_util = __commonJS({
7977
8085
  if (list.length === 1) return list[0];
7978
8086
  const target = Buffer.allocUnsafe(totalLength);
7979
8087
  let offset = 0;
7980
- for (let i = 0; i < list.length; i++) {
7981
- const buf = list[i];
8088
+ for (let i2 = 0; i2 < list.length; i2++) {
8089
+ const buf = list[i2];
7982
8090
  target.set(buf, offset);
7983
8091
  offset += buf.length;
7984
8092
  }
@@ -7988,13 +8096,13 @@ var require_buffer_util = __commonJS({
7988
8096
  return target;
7989
8097
  }
7990
8098
  function _mask(source, mask, output, offset, length) {
7991
- for (let i = 0; i < length; i++) {
7992
- output[offset + i] = source[i] ^ mask[i & 3];
8099
+ for (let i2 = 0; i2 < length; i2++) {
8100
+ output[offset + i2] = source[i2] ^ mask[i2 & 3];
7993
8101
  }
7994
8102
  }
7995
8103
  function _unmask(buffer, mask) {
7996
- for (let i = 0; i < buffer.length; i++) {
7997
- buffer[i] ^= mask[i & 3];
8104
+ for (let i2 = 0; i2 < buffer.length; i2++) {
8105
+ buffer[i2] ^= mask[i2 & 3];
7998
8106
  }
7999
8107
  }
8000
8108
  function toArrayBuffer(buf) {
@@ -8043,8 +8151,8 @@ var require_buffer_util = __commonJS({
8043
8151
  var require_limiter = __commonJS({
8044
8152
  "../common/temp/node_modules/.pnpm/ws@8.18.0_bufferutil@4.0.9/node_modules/ws/lib/limiter.js"(exports, module) {
8045
8153
  "use strict";
8046
- var kDone = Symbol("kDone");
8047
- var kRun = Symbol("kRun");
8154
+ var kDone = /* @__PURE__ */ Symbol("kDone");
8155
+ var kRun = /* @__PURE__ */ Symbol("kRun");
8048
8156
  var Limiter = class {
8049
8157
  /**
8050
8158
  * Creates a new `Limiter`.
@@ -8097,11 +8205,11 @@ var require_permessage_deflate = __commonJS({
8097
8205
  var { kStatusCode } = require_constants();
8098
8206
  var FastBuffer = Buffer[Symbol.species];
8099
8207
  var TRAILER = Buffer.from([0, 0, 255, 255]);
8100
- var kPerMessageDeflate = Symbol("permessage-deflate");
8101
- var kTotalLength = Symbol("total-length");
8102
- var kCallback = Symbol("callback");
8103
- var kBuffers = Symbol("buffers");
8104
- var kError = Symbol("error");
8208
+ var kPerMessageDeflate = /* @__PURE__ */ Symbol("permessage-deflate");
8209
+ var kTotalLength = /* @__PURE__ */ Symbol("total-length");
8210
+ var kCallback = /* @__PURE__ */ Symbol("callback");
8211
+ var kBuffers = /* @__PURE__ */ Symbol("buffers");
8212
+ var kError = /* @__PURE__ */ Symbol("error");
8105
8213
  var zlibLimiter;
8106
8214
  var PerMessageDeflate = class {
8107
8215
  /**
@@ -8612,28 +8720,28 @@ var require_validation = __commonJS({
8612
8720
  return code2 >= 1e3 && code2 <= 1014 && code2 !== 1004 && code2 !== 1005 && code2 !== 1006 || code2 >= 3e3 && code2 <= 4999;
8613
8721
  }
8614
8722
  function _isValidUTF8(buf) {
8615
- const len = buf.length;
8616
- let i = 0;
8617
- while (i < len) {
8618
- if ((buf[i] & 128) === 0) {
8619
- i++;
8620
- } else if ((buf[i] & 224) === 192) {
8621
- if (i + 1 === len || (buf[i + 1] & 192) !== 128 || (buf[i] & 254) === 192) {
8723
+ const len2 = buf.length;
8724
+ let i2 = 0;
8725
+ while (i2 < len2) {
8726
+ if ((buf[i2] & 128) === 0) {
8727
+ i2++;
8728
+ } else if ((buf[i2] & 224) === 192) {
8729
+ if (i2 + 1 === len2 || (buf[i2 + 1] & 192) !== 128 || (buf[i2] & 254) === 192) {
8622
8730
  return false;
8623
8731
  }
8624
- i += 2;
8625
- } else if ((buf[i] & 240) === 224) {
8626
- if (i + 2 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || buf[i] === 224 && (buf[i + 1] & 224) === 128 || // Overlong
8627
- buf[i] === 237 && (buf[i + 1] & 224) === 160) {
8732
+ i2 += 2;
8733
+ } else if ((buf[i2] & 240) === 224) {
8734
+ if (i2 + 2 >= len2 || (buf[i2 + 1] & 192) !== 128 || (buf[i2 + 2] & 192) !== 128 || buf[i2] === 224 && (buf[i2 + 1] & 224) === 128 || // Overlong
8735
+ buf[i2] === 237 && (buf[i2 + 1] & 224) === 160) {
8628
8736
  return false;
8629
8737
  }
8630
- i += 3;
8631
- } else if ((buf[i] & 248) === 240) {
8632
- if (i + 3 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 || buf[i] === 240 && (buf[i + 1] & 240) === 128 || // Overlong
8633
- buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) {
8738
+ i2 += 3;
8739
+ } else if ((buf[i2] & 248) === 240) {
8740
+ if (i2 + 3 >= len2 || (buf[i2 + 1] & 192) !== 128 || (buf[i2 + 2] & 192) !== 128 || (buf[i2 + 3] & 192) !== 128 || buf[i2] === 240 && (buf[i2 + 1] & 240) === 128 || // Overlong
8741
+ buf[i2] === 244 && buf[i2 + 1] > 143 || buf[i2] > 244) {
8634
8742
  return false;
8635
8743
  }
8636
- i += 4;
8744
+ i2 += 4;
8637
8745
  } else {
8638
8746
  return false;
8639
8747
  }
@@ -9263,7 +9371,7 @@ var require_sender = __commonJS({
9263
9371
  var { EMPTY_BUFFER, kWebSocket, NOOP } = require_constants();
9264
9372
  var { isBlob, isValidStatusCode } = require_validation();
9265
9373
  var { mask: applyMask, toBuffer } = require_buffer_util();
9266
- var kByteLength = Symbol("kByteLength");
9374
+ var kByteLength = /* @__PURE__ */ Symbol("kByteLength");
9267
9375
  var maskBuffer = Buffer.alloc(4);
9268
9376
  var RANDOM_POOL_SIZE = 8 * 1024;
9269
9377
  var randomPool;
@@ -9728,8 +9836,8 @@ var require_sender = __commonJS({
9728
9836
  module.exports = Sender2;
9729
9837
  function callCallbacks(sender, err, cb) {
9730
9838
  if (typeof cb === "function") cb(err);
9731
- for (let i = 0; i < sender._queue.length; i++) {
9732
- const params = sender._queue[i];
9839
+ for (let i2 = 0; i2 < sender._queue.length; i2++) {
9840
+ const params = sender._queue[i2];
9733
9841
  const callback = params[params.length - 1];
9734
9842
  if (typeof callback === "function") callback(err);
9735
9843
  }
@@ -9744,14 +9852,14 @@ var require_event_target = __commonJS({
9744
9852
  "../common/temp/node_modules/.pnpm/ws@8.18.0_bufferutil@4.0.9/node_modules/ws/lib/event-target.js"(exports, module) {
9745
9853
  "use strict";
9746
9854
  var { kForOnEventAttribute, kListener } = require_constants();
9747
- var kCode = Symbol("kCode");
9748
- var kData = Symbol("kData");
9749
- var kError = Symbol("kError");
9750
- var kMessage = Symbol("kMessage");
9751
- var kReason = Symbol("kReason");
9752
- var kTarget = Symbol("kTarget");
9753
- var kType = Symbol("kType");
9754
- var kWasClean = Symbol("kWasClean");
9855
+ var kCode = /* @__PURE__ */ Symbol("kCode");
9856
+ var kData = /* @__PURE__ */ Symbol("kData");
9857
+ var kError = /* @__PURE__ */ Symbol("kError");
9858
+ var kMessage = /* @__PURE__ */ Symbol("kMessage");
9859
+ var kReason = /* @__PURE__ */ Symbol("kReason");
9860
+ var kTarget = /* @__PURE__ */ Symbol("kTarget");
9861
+ var kType = /* @__PURE__ */ Symbol("kType");
9862
+ var kWasClean = /* @__PURE__ */ Symbol("kWasClean");
9755
9863
  var Event = class {
9756
9864
  /**
9757
9865
  * Create a new `Event`.
@@ -9986,19 +10094,19 @@ var require_extension = __commonJS({
9986
10094
  let start = -1;
9987
10095
  let code2 = -1;
9988
10096
  let end = -1;
9989
- let i = 0;
9990
- for (; i < header.length; i++) {
9991
- code2 = header.charCodeAt(i);
10097
+ let i2 = 0;
10098
+ for (; i2 < header.length; i2++) {
10099
+ code2 = header.charCodeAt(i2);
9992
10100
  if (extensionName === void 0) {
9993
10101
  if (end === -1 && tokenChars[code2] === 1) {
9994
- if (start === -1) start = i;
9995
- } else if (i !== 0 && (code2 === 32 || code2 === 9)) {
9996
- if (end === -1 && start !== -1) end = i;
10102
+ if (start === -1) start = i2;
10103
+ } else if (i2 !== 0 && (code2 === 32 || code2 === 9)) {
10104
+ if (end === -1 && start !== -1) end = i2;
9997
10105
  } else if (code2 === 59 || code2 === 44) {
9998
10106
  if (start === -1) {
9999
- throw new SyntaxError(`Unexpected character at index ${i}`);
10107
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
10000
10108
  }
10001
- if (end === -1) end = i;
10109
+ if (end === -1) end = i2;
10002
10110
  const name = header.slice(start, end);
10003
10111
  if (code2 === 44) {
10004
10112
  push(offers, name, params);
@@ -10008,18 +10116,18 @@ var require_extension = __commonJS({
10008
10116
  }
10009
10117
  start = end = -1;
10010
10118
  } else {
10011
- throw new SyntaxError(`Unexpected character at index ${i}`);
10119
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
10012
10120
  }
10013
10121
  } else if (paramName === void 0) {
10014
10122
  if (end === -1 && tokenChars[code2] === 1) {
10015
- if (start === -1) start = i;
10123
+ if (start === -1) start = i2;
10016
10124
  } else if (code2 === 32 || code2 === 9) {
10017
- if (end === -1 && start !== -1) end = i;
10125
+ if (end === -1 && start !== -1) end = i2;
10018
10126
  } else if (code2 === 59 || code2 === 44) {
10019
10127
  if (start === -1) {
10020
- throw new SyntaxError(`Unexpected character at index ${i}`);
10128
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
10021
10129
  }
10022
- if (end === -1) end = i;
10130
+ if (end === -1) end = i2;
10023
10131
  push(params, header.slice(start, end), true);
10024
10132
  if (code2 === 44) {
10025
10133
  push(offers, extensionName, params);
@@ -10028,41 +10136,41 @@ var require_extension = __commonJS({
10028
10136
  }
10029
10137
  start = end = -1;
10030
10138
  } else if (code2 === 61 && start !== -1 && end === -1) {
10031
- paramName = header.slice(start, i);
10139
+ paramName = header.slice(start, i2);
10032
10140
  start = end = -1;
10033
10141
  } else {
10034
- throw new SyntaxError(`Unexpected character at index ${i}`);
10142
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
10035
10143
  }
10036
10144
  } else {
10037
10145
  if (isEscaping) {
10038
10146
  if (tokenChars[code2] !== 1) {
10039
- throw new SyntaxError(`Unexpected character at index ${i}`);
10147
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
10040
10148
  }
10041
- if (start === -1) start = i;
10149
+ if (start === -1) start = i2;
10042
10150
  else if (!mustUnescape) mustUnescape = true;
10043
10151
  isEscaping = false;
10044
10152
  } else if (inQuotes) {
10045
10153
  if (tokenChars[code2] === 1) {
10046
- if (start === -1) start = i;
10154
+ if (start === -1) start = i2;
10047
10155
  } else if (code2 === 34 && start !== -1) {
10048
10156
  inQuotes = false;
10049
- end = i;
10157
+ end = i2;
10050
10158
  } else if (code2 === 92) {
10051
10159
  isEscaping = true;
10052
10160
  } else {
10053
- throw new SyntaxError(`Unexpected character at index ${i}`);
10161
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
10054
10162
  }
10055
- } else if (code2 === 34 && header.charCodeAt(i - 1) === 61) {
10163
+ } else if (code2 === 34 && header.charCodeAt(i2 - 1) === 61) {
10056
10164
  inQuotes = true;
10057
10165
  } else if (end === -1 && tokenChars[code2] === 1) {
10058
- if (start === -1) start = i;
10166
+ if (start === -1) start = i2;
10059
10167
  } else if (start !== -1 && (code2 === 32 || code2 === 9)) {
10060
- if (end === -1) end = i;
10168
+ if (end === -1) end = i2;
10061
10169
  } else if (code2 === 59 || code2 === 44) {
10062
10170
  if (start === -1) {
10063
- throw new SyntaxError(`Unexpected character at index ${i}`);
10171
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
10064
10172
  }
10065
- if (end === -1) end = i;
10173
+ if (end === -1) end = i2;
10066
10174
  let value = header.slice(start, end);
10067
10175
  if (mustUnescape) {
10068
10176
  value = value.replace(/\\/g, "");
@@ -10077,14 +10185,14 @@ var require_extension = __commonJS({
10077
10185
  paramName = void 0;
10078
10186
  start = end = -1;
10079
10187
  } else {
10080
- throw new SyntaxError(`Unexpected character at index ${i}`);
10188
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
10081
10189
  }
10082
10190
  }
10083
10191
  }
10084
10192
  if (start === -1 || inQuotes || code2 === 32 || code2 === 9) {
10085
10193
  throw new SyntaxError("Unexpected end of input");
10086
10194
  }
10087
- if (end === -1) end = i;
10195
+ if (end === -1) end = i2;
10088
10196
  const token2 = header.slice(start, end);
10089
10197
  if (extensionName === void 0) {
10090
10198
  push(offers, token2, params);
@@ -10149,7 +10257,7 @@ var require_websocket = __commonJS({
10149
10257
  var { format, parse } = require_extension();
10150
10258
  var { toBuffer } = require_buffer_util();
10151
10259
  var closeTimeout = 30 * 1e3;
10152
- var kAborted = Symbol("kAborted");
10260
+ var kAborted = /* @__PURE__ */ Symbol("kAborted");
10153
10261
  var protocolVersions = [8, 13];
10154
10262
  var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
10155
10263
  var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
@@ -11007,18 +11115,18 @@ var require_subprotocol = __commonJS({
11007
11115
  const protocols = /* @__PURE__ */ new Set();
11008
11116
  let start = -1;
11009
11117
  let end = -1;
11010
- let i = 0;
11011
- for (i; i < header.length; i++) {
11012
- const code2 = header.charCodeAt(i);
11118
+ let i2 = 0;
11119
+ for (i2; i2 < header.length; i2++) {
11120
+ const code2 = header.charCodeAt(i2);
11013
11121
  if (end === -1 && tokenChars[code2] === 1) {
11014
- if (start === -1) start = i;
11015
- } else if (i !== 0 && (code2 === 32 || code2 === 9)) {
11016
- if (end === -1 && start !== -1) end = i;
11122
+ if (start === -1) start = i2;
11123
+ } else if (i2 !== 0 && (code2 === 32 || code2 === 9)) {
11124
+ if (end === -1 && start !== -1) end = i2;
11017
11125
  } else if (code2 === 44) {
11018
11126
  if (start === -1) {
11019
- throw new SyntaxError(`Unexpected character at index ${i}`);
11127
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
11020
11128
  }
11021
- if (end === -1) end = i;
11129
+ if (end === -1) end = i2;
11022
11130
  const protocol2 = header.slice(start, end);
11023
11131
  if (protocols.has(protocol2)) {
11024
11132
  throw new SyntaxError(`The "${protocol2}" subprotocol is duplicated`);
@@ -11026,13 +11134,13 @@ var require_subprotocol = __commonJS({
11026
11134
  protocols.add(protocol2);
11027
11135
  start = end = -1;
11028
11136
  } else {
11029
- throw new SyntaxError(`Unexpected character at index ${i}`);
11137
+ throw new SyntaxError(`Unexpected character at index ${i2}`);
11030
11138
  }
11031
11139
  }
11032
11140
  if (start === -1 || end !== -1) {
11033
11141
  throw new SyntaxError("Unexpected end of input");
11034
11142
  }
11035
- const protocol = header.slice(start, i);
11143
+ const protocol = header.slice(start, i2);
11036
11144
  if (protocols.has(protocol)) {
11037
11145
  throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
11038
11146
  }
@@ -11994,6 +12102,7 @@ var upsertTelemetryFromPlugin = makeFunctionReference("telemetryPlugin:upsertTel
11994
12102
  var upsertSessionsFromPlugin = makeFunctionReference("telemetryPlugin:upsertSessionsFromPlugin");
11995
12103
  var pushActivityFromPluginRef = makeFunctionReference("activityFeed:pushActivityFromPlugin");
11996
12104
  var upsertCronSnapshotFromPluginRef = makeFunctionReference("telemetryPlugin:upsertCronSnapshotFromPlugin");
12105
+ var recordGatewayPresenceRef = makeFunctionReference("gatewayPresence:recordGatewayPresence");
11997
12106
  var recordCronRunFromPluginRef = makeFunctionReference("cronRunHistory:recordFromPlugin");
11998
12107
  var getUndeliveredForPlugin = makeFunctionReference("notifications:getUndeliveredForPlugin");
11999
12108
  var markDeliveredByPlugin = makeFunctionReference("notifications:markDeliveredByPlugin");
@@ -12015,7 +12124,7 @@ async function pushTelemetry(apiKey2, data) {
12015
12124
  const c = getClient();
12016
12125
  if (!c) return;
12017
12126
  try {
12018
- await c.mutation(upsertTelemetryFromPlugin, { apiKeyHash: hashApiKey(apiKey2), ...data });
12127
+ await c.mutation(upsertTelemetryFromPlugin, { apiKeyHash: hashApiKey(apiKey2), runtime: "openclaw", ...data });
12019
12128
  } catch (err) {
12020
12129
  if (isUnauthorizedError(err)) {
12021
12130
  tripAuthCircuit();
@@ -12068,6 +12177,22 @@ async function pushCronSnapshot(apiKey2, jobs) {
12068
12177
  return false;
12069
12178
  }
12070
12179
  }
12180
+ async function pushPresence(apiKey2, runtime) {
12181
+ if (authCircuitOpen) return false;
12182
+ const c = getClient();
12183
+ if (!c) return false;
12184
+ try {
12185
+ await c.mutation(recordGatewayPresenceRef, { apiKeyHash: hashApiKey(apiKey2), runtime });
12186
+ return true;
12187
+ } catch (err) {
12188
+ if (isUnauthorizedError(err)) {
12189
+ tripAuthCircuit();
12190
+ return false;
12191
+ }
12192
+ getLogger().error(`cohort-sync: pushPresence failed: ${err}`);
12193
+ return false;
12194
+ }
12195
+ }
12071
12196
  async function recordCronRun(apiKey2, run) {
12072
12197
  if (authCircuitOpen) return false;
12073
12198
  const c = getClient();
@@ -12686,7 +12811,7 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12686
12811
  processing = true;
12687
12812
  try {
12688
12813
  for (const n of notifications) {
12689
- const failCount = deliveryFailures.get(n._id) ?? 0;
12814
+ const failCount = deliveryFailures.get(n.id) ?? 0;
12690
12815
  if (failCount >= MAX_DELIVERY_ATTEMPTS) {
12691
12816
  continue;
12692
12817
  }
@@ -12697,34 +12822,34 @@ async function startNotificationSubscription(port, cfg, hooksToken, logger, gwCl
12697
12822
  logger.info(`cohort-sync: injected notification for ${targetLabel} (${agentName})`);
12698
12823
  } else {
12699
12824
  throw new Error(
12700
- `no transport available for notification ${n._id} (gwClient alive: ${gwClient?.isAlive() ?? "null"}, hooksToken: ${!!hooksToken})`
12825
+ `no transport available for notification ${n.id} (gwClient alive: ${gwClient?.isAlive() ?? "null"}, hooksToken: ${!!hooksToken})`
12701
12826
  );
12702
12827
  }
12703
12828
  await c.mutation(markDeliveredByPlugin, {
12704
- notificationId: n._id,
12829
+ notificationId: n.id,
12705
12830
  apiKeyHash
12706
12831
  });
12707
- deliveryFailures.delete(n._id);
12832
+ deliveryFailures.delete(n.id);
12708
12833
  } catch (err) {
12709
12834
  const newFailCount = failCount + 1;
12710
- deliveryFailures.set(n._id, newFailCount);
12835
+ deliveryFailures.set(n.id, newFailCount);
12711
12836
  if (newFailCount >= MAX_DELIVERY_ATTEMPTS) {
12712
12837
  const targetLabel = n.type === "room_message" ? `room ${n.roomId ?? n.roomName ?? "unknown"}` : `task #${n.taskNumber}`;
12713
12838
  logger.error(
12714
- `cohort-sync: dead-letter notification ${n._id} for ${targetLabel} (${n.type} from ${n.actorName}) after ${MAX_DELIVERY_ATTEMPTS} failed delivery attempts: ${String(err)}`
12839
+ `cohort-sync: dead-letter notification ${n.id} for ${targetLabel} (${n.type} from ${n.actorName}) after ${MAX_DELIVERY_ATTEMPTS} failed delivery attempts: ${String(err)}`
12715
12840
  );
12716
12841
  try {
12717
12842
  await c.mutation(markDeliveredByPlugin, {
12718
- notificationId: n._id,
12843
+ notificationId: n.id,
12719
12844
  apiKeyHash
12720
12845
  });
12721
- deliveryFailures.delete(n._id);
12846
+ deliveryFailures.delete(n.id);
12722
12847
  } catch (markErr) {
12723
- logger.error(`cohort-sync: failed to dead-letter ${n._id}: ${String(markErr)}`);
12848
+ logger.error(`cohort-sync: failed to dead-letter ${n.id}: ${String(markErr)}`);
12724
12849
  }
12725
12850
  } else {
12726
12851
  logger.warn(
12727
- `cohort-sync: failed to inject notification ${n._id} (attempt ${newFailCount}/${MAX_DELIVERY_ATTEMPTS}): ${String(err)}`
12852
+ `cohort-sync: failed to inject notification ${n.id} (attempt ${newFailCount}/${MAX_DELIVERY_ATTEMPTS}): ${String(err)}`
12728
12853
  );
12729
12854
  }
12730
12855
  }
@@ -12855,15 +12980,15 @@ import crypto2 from "node:crypto";
12855
12980
 
12856
12981
  // src/device-identity-crypto.ts
12857
12982
  import crypto from "node:crypto";
12858
- import fs from "node:fs";
12859
- import path from "node:path";
12983
+ import fs2 from "node:fs";
12984
+ import path2 from "node:path";
12860
12985
  import os from "node:os";
12861
- var DATA_DIR = path.join(os.homedir(), ".openclaw", "data", "cohort-sync");
12862
- var IDENTITY_PATH = path.join(DATA_DIR, ".device-identity.json");
12863
- var LEGACY_IDENTITY_PATH = path.join(os.homedir(), ".openclaw", "extensions", "cohort-sync", ".device-identity.json");
12986
+ var DATA_DIR = path2.join(os.homedir(), ".openclaw", "data", "cohort-sync");
12987
+ var IDENTITY_PATH = path2.join(DATA_DIR, ".device-identity.json");
12988
+ var LEGACY_IDENTITY_PATH = path2.join(os.homedir(), ".openclaw", "extensions", "cohort-sync", ".device-identity.json");
12864
12989
  function tryLoadIdentity(filePath) {
12865
12990
  try {
12866
- const data = JSON.parse(fs["read"+"FileSync"](filePath, "utf-8"));
12991
+ const data = JSON.parse(fs2["read"+"FileSync"](filePath, "utf-8"));
12867
12992
  if (data.deviceId && data.publicKeyPem && data.privateKeyPem) {
12868
12993
  return data;
12869
12994
  }
@@ -12896,8 +13021,8 @@ function loadOrCreateDeviceIdentity() {
12896
13021
  }
12897
13022
  function persistIdentity(identity) {
12898
13023
  try {
12899
- fs.mkdirSync(DATA_DIR, { recursive: true, mode: 448 });
12900
- fs.writeFileSync(IDENTITY_PATH, JSON.stringify(identity, null, 2), { mode: 384 });
13024
+ fs2.mkdirSync(DATA_DIR, { recursive: true, mode: 448 });
13025
+ fs2.writeFileSync(IDENTITY_PATH, JSON.stringify(identity, null, 2), { mode: 384 });
12901
13026
  } catch (err) {
12902
13027
  console.debug("cohort-sync: device identity write failed", { error: String(err) });
12903
13028
  }
@@ -14009,6 +14134,28 @@ var AgentStateTracker = class {
14009
14134
 
14010
14135
  // src/types.ts
14011
14136
  var DEFAULT_API_URL = "https://api.cohort.bot";
14137
+ function normalizeTriageBrief(raw) {
14138
+ if (raw === null || typeof raw !== "object") {
14139
+ return { error: "brief must be an object with problem, solution (and optional risks)." };
14140
+ }
14141
+ const obj = raw;
14142
+ const requireField = (name) => {
14143
+ const value = obj[name];
14144
+ if (typeof value !== "string" || value.trim().length === 0) {
14145
+ return { error: `brief.${name} is required and must be a non-empty string.` };
14146
+ }
14147
+ return value.trim();
14148
+ };
14149
+ const problem = requireField("problem");
14150
+ if (typeof problem !== "string") return problem;
14151
+ const solution = requireField("solution");
14152
+ if (typeof solution !== "string") return solution;
14153
+ const brief = { problem, solution };
14154
+ if (typeof obj.risks === "string" && obj.risks.trim().length > 0) {
14155
+ brief.risks = obj.risks.trim();
14156
+ }
14157
+ return { brief };
14158
+ }
14012
14159
 
14013
14160
  // src/tool-runtime.ts
14014
14161
  var apiKey = null;
@@ -14119,7 +14266,8 @@ function dumpEvent(event) {
14119
14266
  function positiveNumber(value) {
14120
14267
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
14121
14268
  }
14122
- var PLUGIN_VERSION = true ? "0.34.2" : "unknown";
14269
+ var PLUGIN_VERSION = true ? "0.34.4" : "unknown";
14270
+ var PRESENCE_PING_INTERVAL_MS = 12e4;
14123
14271
  function resolveGatewayToken(api) {
14124
14272
  const token2 = api.config?.gateway?.auth?.token;
14125
14273
  return typeof token2 === "string" ? token2 : null;
@@ -14149,8 +14297,8 @@ function registerCronEventHandlers(client2, cfg, resolveAgentName, cronTimestamp
14149
14297
  }
14150
14298
  function parseIdentityFile(workspaceDir) {
14151
14299
  try {
14152
- const filePath = path2.join(workspaceDir, "IDENTITY.md");
14153
- const content = fs2["read"+"FileSync"](filePath, "utf-8");
14300
+ const filePath = path3.join(workspaceDir, "IDENTITY.md");
14301
+ const content = fs3["read"+"FileSync"](filePath, "utf-8");
14154
14302
  const identity = {};
14155
14303
  for (const line of content.split(/\r?\n/)) {
14156
14304
  const cleaned = line.trim().replace(/^\s*-\s*/, "");
@@ -14169,14 +14317,41 @@ function parseIdentityFile(workspaceDir) {
14169
14317
  return null;
14170
14318
  }
14171
14319
  }
14320
+ function parseRoleFromSoul(content) {
14321
+ const MAX_LEN = 80;
14322
+ for (const line of content.split(/\r?\n/)) {
14323
+ const cleaned = line.trim().replace(/^\s*-\s*/, "");
14324
+ const colonIndex = cleaned.indexOf(":");
14325
+ if (colonIndex === -1) continue;
14326
+ const label = cleaned.slice(0, colonIndex).replace(/[*_]/g, "").trim().toLowerCase();
14327
+ if (label !== "role" && label !== "current role") continue;
14328
+ let value = cleaned.slice(colonIndex + 1).replace(/^[*_]+|[*_]+$/g, "").trim();
14329
+ if (!value) continue;
14330
+ const firstSentence = value.split(/\.\s/)[0].trim();
14331
+ if (firstSentence) value = firstSentence;
14332
+ if (value.length > MAX_LEN) value = value.slice(0, MAX_LEN).trim();
14333
+ return value;
14334
+ }
14335
+ return void 0;
14336
+ }
14337
+ function parseSoulRole(workspaceDir) {
14338
+ try {
14339
+ const content = fs3["read"+"FileSync"](path3.join(workspaceDir, "SOUL.md"), "utf-8");
14340
+ return parseRoleFromSoul(content);
14341
+ } catch {
14342
+ return void 0;
14343
+ }
14344
+ }
14172
14345
  function resolveIdentity(configIdentity, workspaceDir) {
14173
14346
  const fileIdentity = workspaceDir ? parseIdentityFile(workspaceDir) : null;
14174
- if (!configIdentity && !fileIdentity) return void 0;
14347
+ const soulRole = workspaceDir ? parseSoulRole(workspaceDir) : void 0;
14348
+ if (!configIdentity && !fileIdentity && !soulRole) return void 0;
14175
14349
  return {
14176
14350
  name: configIdentity?.name ?? fileIdentity?.name,
14177
14351
  emoji: configIdentity?.emoji ?? fileIdentity?.emoji,
14178
14352
  theme: configIdentity?.theme ?? fileIdentity?.theme,
14179
- avatar: configIdentity?.avatar ?? fileIdentity?.avatar
14353
+ avatar: configIdentity?.avatar ?? fileIdentity?.avatar,
14354
+ title: configIdentity?.title ?? soulRole
14180
14355
  };
14181
14356
  }
14182
14357
  function saveSessionsToDisk(tracker, stateFilePath) {
@@ -14193,14 +14368,14 @@ function saveSessionsToDisk(tracker, stateFilePath) {
14193
14368
  data.sessions.push({ agentName: name, key });
14194
14369
  }
14195
14370
  }
14196
- fs2.writeFileSync(stateFilePath, JSON.stringify(data), { mode: 384 });
14371
+ fs3.writeFileSync(stateFilePath, JSON.stringify(data), { mode: 384 });
14197
14372
  } catch {
14198
14373
  }
14199
14374
  }
14200
14375
  function loadSessionsFromDisk(tracker, stateFilePath, logger) {
14201
14376
  try {
14202
- if (!fs2.existsSync(stateFilePath)) return;
14203
- const data = JSON.parse(fs2["read"+"FileSync"](stateFilePath, "utf8"));
14377
+ if (!fs3.existsSync(stateFilePath)) return;
14378
+ const data = JSON.parse(fs3["read"+"FileSync"](stateFilePath, "utf8"));
14204
14379
  if (Date.now() - new Date(data.savedAt).getTime() > 864e5) {
14205
14380
  logger.info("cohort-sync: disk session state too old (>24h), skipping");
14206
14381
  return;
@@ -14296,7 +14471,15 @@ async function handleGatewayStart(event, state) {
14296
14471
  model: state.resolveModel(a.id),
14297
14472
  identity: resolveIdentity(a.identity, a.workspace)
14298
14473
  }));
14299
- await fullSync(state.resolveAgentName("main"), state.resolveModel("main"), cfg, logger, agentList);
14474
+ const skillsDir = api.config?.skills?.dir ?? path3.join(os2.homedir(), ".openclaw", "skills");
14475
+ await fullSync(
14476
+ state.resolveAgentName("main"),
14477
+ state.resolveModel("main"),
14478
+ cfg,
14479
+ logger,
14480
+ agentList,
14481
+ skillsDir
14482
+ );
14300
14483
  } catch (err) {
14301
14484
  logger.error(`cohort-sync: gateway_start sync failed: ${String(err)}`);
14302
14485
  }
@@ -14421,6 +14604,17 @@ async function handleGatewayStart(event, state) {
14421
14604
  saveSessionsToDisk(tracker, state.stateFilePath);
14422
14605
  }, 15e4);
14423
14606
  logger.info("cohort-sync: keepalive interval started (150s)");
14607
+ void pushPresence(cfg.apiKey, "openclaw").catch(() => {
14608
+ });
14609
+ if (state.presenceInterval) clearInterval(state.presenceInterval);
14610
+ state.presenceInterval = setInterval(() => {
14611
+ void pushPresence(cfg.apiKey, "openclaw").catch(() => {
14612
+ });
14613
+ }, PRESENCE_PING_INTERVAL_MS);
14614
+ if (typeof state.presenceInterval.unref === "function") {
14615
+ state.presenceInterval.unref();
14616
+ }
14617
+ logger.info(`cohort-sync: liveness ping interval started (${PRESENCE_PING_INTERVAL_MS / 1e3}s)`);
14424
14618
  }
14425
14619
  function registerHookHandlers(api, logger, getState) {
14426
14620
  function resolveAgentFromContext(state, ctx) {
@@ -14497,7 +14691,7 @@ function registerHookHandlers(api, logger, getState) {
14497
14691
  const parsed = parseSessionKey(sessionKey);
14498
14692
  const routineId = parsed.kind === "cron" ? parsed.identifier : void 0;
14499
14693
  try {
14500
- const raw = fs2["read"+"FileSync"](state.cronStorePath, "utf8");
14694
+ const raw = fs3["read"+"FileSync"](state.cronStorePath, "utf8");
14501
14695
  const store = JSON.parse(raw);
14502
14696
  const jobs = store.jobs ?? [];
14503
14697
  const mapped = jobs.map((j) => mapCronJob(j, state.resolveAgentName, state.cronTimestampTracker));
@@ -14881,6 +15075,10 @@ function registerHookHandlers(api, logger, getState) {
14881
15075
  clearInterval(state.keepaliveInterval);
14882
15076
  state.keepaliveInterval = null;
14883
15077
  }
15078
+ if (state.presenceInterval) {
15079
+ clearInterval(state.presenceInterval);
15080
+ state.presenceInterval = null;
15081
+ }
14884
15082
  if (state.updateCheckInterval) {
14885
15083
  clearInterval(state.updateCheckInterval);
14886
15084
  state.updateCheckInterval = null;
@@ -14931,7 +15129,7 @@ function registerHookHandlers(api, logger, getState) {
14931
15129
  }
14932
15130
  function initializeHookState(api, cfg) {
14933
15131
  const { logger, config } = api;
14934
- const stateFilePath = path2.join(cfg.stateDir, "session-state.json");
15132
+ const stateFilePath = path3.join(cfg.stateDir, "session-state.json");
14935
15133
  const nameMap = cfg.agentNameMap;
14936
15134
  const tracker = new AgentStateTracker();
14937
15135
  const convexUrl = cfg.convexUrl ?? deriveConvexUrl(cfg.apiUrl);
@@ -14965,7 +15163,7 @@ function initializeHookState(api, cfg) {
14965
15163
  function getModelContextLimit2(model) {
14966
15164
  return getModelContextLimit(model);
14967
15165
  }
14968
- const cronStorePath = api.config?.cron?.store ?? path2.join(os2.homedir(), ".openclaw", "cron", "jobs.json");
15166
+ const cronStorePath = api.config?.cron?.store ?? path3.join(os2.homedir(), ".openclaw", "cron", "jobs.json");
14969
15167
  const activityBatch = new MicroBatch({
14970
15168
  maxSize: 10,
14971
15169
  maxDelayMs: 1e3,
@@ -15041,6 +15239,7 @@ function initializeHookState(api, cfg) {
15041
15239
  persistentGwClient,
15042
15240
  gwClientInitialized,
15043
15241
  keepaliveInterval: null,
15242
+ presenceInterval: null,
15044
15243
  commandUnsubscriber: commandUnsub,
15045
15244
  channelsUnsubscriber: null,
15046
15245
  api,
@@ -15084,6 +15283,65 @@ function registerGatewayMethods(api, getGatewayClient) {
15084
15283
  );
15085
15284
  }
15086
15285
 
15286
+ // src/triage-pr-files.ts
15287
+ import { execFile } from "node:child_process";
15288
+ import { readFile } from "node:fs/promises";
15289
+ import { join } from "node:path";
15290
+ import { promisify } from "node:util";
15291
+ var execFileAsync = promisify(execFile);
15292
+ async function collectChangedFiles(args) {
15293
+ const cwd = args.repoDir || process.cwd();
15294
+ const git = async (gitArgs) => {
15295
+ const { stdout } = await execFileAsync("git", gitArgs, {
15296
+ cwd,
15297
+ maxBuffer: 64 * 1024 * 1024
15298
+ });
15299
+ return stdout;
15300
+ };
15301
+ const files = [];
15302
+ const seen = /* @__PURE__ */ new Set();
15303
+ const nameStatus = await git(["diff", "--name-status", "-z", args.baseSha]);
15304
+ const tokens = nameStatus.split("\0");
15305
+ let i2 = 0;
15306
+ while (i2 < tokens.length) {
15307
+ const status = tokens[i2];
15308
+ if (!status) {
15309
+ i2 += 1;
15310
+ continue;
15311
+ }
15312
+ let path4;
15313
+ if (status[0] === "R" || status[0] === "C") {
15314
+ const oldPath = tokens[i2 + 1] ?? "";
15315
+ path4 = tokens[i2 + 2] ?? "";
15316
+ i2 += 3;
15317
+ if (oldPath && !seen.has(oldPath)) {
15318
+ seen.add(oldPath);
15319
+ files.push({ path: oldPath, deleted: true });
15320
+ }
15321
+ } else {
15322
+ path4 = tokens[i2 + 1] ?? "";
15323
+ i2 += 2;
15324
+ }
15325
+ if (!path4 || seen.has(path4)) continue;
15326
+ seen.add(path4);
15327
+ if (status[0] === "D") {
15328
+ files.push({ path: path4, deleted: true });
15329
+ } else {
15330
+ files.push({ path: path4, content: await readRepoFile(cwd, path4) });
15331
+ }
15332
+ }
15333
+ const others = await git(["ls-files", "--others", "--exclude-standard", "-z"]);
15334
+ for (const path4 of others.split("\0")) {
15335
+ if (!path4 || seen.has(path4)) continue;
15336
+ seen.add(path4);
15337
+ files.push({ path: path4, content: await readRepoFile(cwd, path4) });
15338
+ }
15339
+ return files;
15340
+ }
15341
+ async function readRepoFile(cwd, path4) {
15342
+ return readFile(join(cwd, path4), "utf8");
15343
+ }
15344
+
15087
15345
  // src/pocket-guide.ts
15088
15346
  var POCKET_GUIDE = `# Cohort Agent Guide (Pocket Version)
15089
15347
 
@@ -15104,6 +15362,13 @@ var POCKET_GUIDE = `# Cohort Agent Guide (Pocket Version)
15104
15362
  - For code changes, work is not complete when it is implemented locally; push your branch, open a PR, enable auto-merge, watch CI until green or red, confirm the PR merged, verify the production deploy, then comment with the PR link, deploy result, and verification evidence.
15105
15363
  - Use GitHub for deployment: after opening the PR, run gh pr merge --auto --rebase --delete-branch, gh pr checks <PR> --watch, confirm the merge, then run gh run watch <deploy-run-id> for the Deploy Production workflow. Do not stop because Vercel CLI is not authenticated; Cohort deploys through GitHub Actions after merge.
15106
15364
 
15365
+ ## Working as a Team
15366
+ - Two modes: orchestrator and worker. As an orchestrator, break the goal into a task graph (create tasks, assign owners, relate dependencies), then report the graph and leave implementation to the assigned workers. As a worker, take one task, do it, and hand it back cleanly.
15367
+ - Relate dependent tasks with blockers so ordering is explicit. A blocked task stays gated until its blocker clears.
15368
+ - Before assigning, confirm the assignee is a real agent in the workspace, because a task assigned to a name that resolves to no one strands in its column.
15369
+ - The closing comment is the handoff: what you did, the evidence (links, PR, test output, artifact), and what is now unblocked or what the human must decide. Agents cannot set "done", so move to "waiting" and let a human close it.
15370
+ - Create a task when work spans multiple agents, needs to survive a restart, wants a human in the loop, or can run in parallel. For something you can finish right now, just do it.
15371
+
15107
15372
  ## Comments
15108
15373
  - Comment before every status transition explaining what happened.
15109
15374
  - Post progress updates every 15-30 minutes on long-running work.
@@ -15306,6 +15571,31 @@ async function safeHttpError(response) {
15306
15571
  }
15307
15572
  return "";
15308
15573
  }
15574
+ var PR_MAX_FILES = 50;
15575
+ var PR_MAX_FILE_BYTES = 1e6;
15576
+ var PR_MAX_TOTAL_BYTES = 5e6;
15577
+ var PR_BASE_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
15578
+ function checkPrEnvelope(files) {
15579
+ if (files.length === 0) {
15580
+ return "No changed files to report \u2014 make a change first, then call cohort_triage_pr.";
15581
+ }
15582
+ if (files.length > PR_MAX_FILES) {
15583
+ return `Change too large: ${files.length} files (max ${PR_MAX_FILES}).`;
15584
+ }
15585
+ let total = 0;
15586
+ for (const entry of files) {
15587
+ if ("deleted" in entry) continue;
15588
+ const size = Buffer2.byteLength(entry.content, "utf8");
15589
+ if (size > PR_MAX_FILE_BYTES) {
15590
+ return `File too large: ${entry.path} (max ${PR_MAX_FILE_BYTES} bytes).`;
15591
+ }
15592
+ total += size;
15593
+ }
15594
+ if (total > PR_MAX_TOTAL_BYTES) {
15595
+ return "Change too large (total content size exceeds the limit).";
15596
+ }
15597
+ return null;
15598
+ }
15309
15599
  function renderTaskContext(context) {
15310
15600
  if (!context) return "";
15311
15601
  const lines = [];
@@ -15472,7 +15762,7 @@ Do not attempt more comments until tomorrow.`);
15472
15762
  label: "cohort_room_message",
15473
15763
  description: "Post a message in a Cohort Room. Use this to reply when you are participating in a Room chat.",
15474
15764
  parameters: Type.Object({
15475
- room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
15765
+ room_id: Type.String({ description: 'The Room to post in. Prefer the exact room id from your prompt/notification (e.g. xs773mgqe3qd9h41pjwxndd755887b1x). The room name (e.g. "All Hands") is also accepted.' }),
15476
15766
  message: Type.String({ description: "Message text to post in the Room" }),
15477
15767
  turn_id: Type.Optional(Type.String({ description: "Moderation turn id to attribute this reply to (echo from your prompt)" }))
15478
15768
  }),
@@ -15499,7 +15789,7 @@ Do not attempt more comments until tomorrow.`);
15499
15789
  label: "cohort_room_prompt_agent",
15500
15790
  description: "Ask one agent in a Cohort Room to respond. Use this when you are the Room moderator and need controlled turn-taking.",
15501
15791
  parameters: Type.Object({
15502
- room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
15792
+ room_id: Type.String({ description: 'Room ID supplied by Cohort, e.g. xs773mgqe3qd9h41pjwxndd755887b1x (NOT prefixed with "rooms:").' }),
15503
15793
  agent_name: Type.String({ description: "Cohort agent name to prompt, e.g. iris" }),
15504
15794
  prompt: Type.String({ description: "Prompt to send to the target agent" })
15505
15795
  }),
@@ -15527,7 +15817,7 @@ Do not attempt more comments until tomorrow.`);
15527
15817
  label: "cohort_room_prompt_agents",
15528
15818
  description: "Ask multiple agents in a Cohort Room to respond in one controlled moderator action.",
15529
15819
  parameters: Type.Object({
15530
- room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
15820
+ room_id: Type.String({ description: 'Room ID supplied by Cohort, e.g. xs773mgqe3qd9h41pjwxndd755887b1x (NOT prefixed with "rooms:").' }),
15531
15821
  agent_names: Type.Array(Type.String({ description: "Cohort agent name to prompt" })),
15532
15822
  prompt: Type.String({ description: "Prompt to send to the target agents" })
15533
15823
  }),
@@ -15671,7 +15961,7 @@ Wire item: ${result.wireItemId}`, result);
15671
15961
  label: "cohort_room_start_moderation_session",
15672
15962
  description: 'Start a managed moderation session in a Cohort Room. Use round_robin = ask every participant to report in; use moderated_qna = route a question to one selected agent, wait for the answer, ask the asker whether it resolves the question, then complete. For multiple experts in Q&A, start with one best agent and use action: "followup" for additional experts one at a time. Returns the session state block. The returned block includes the session_id \u2014 pass it to cohort_room_advance_moderation_session for every subsequent action.',
15673
15963
  parameters: Type.Object({
15674
- room_id: Type.String({ description: "Room ID supplied by Cohort, e.g. rooms:abc123" }),
15964
+ room_id: Type.String({ description: 'Room ID supplied by Cohort, e.g. xs773mgqe3qd9h41pjwxndd755887b1x (NOT prefixed with "rooms:").' }),
15675
15965
  mode: Type.Union([
15676
15966
  Type.Literal("round_robin"),
15677
15967
  Type.Literal("moderated_qna")
@@ -16056,6 +16346,127 @@ ${renderGoal(goal)}`, goal);
16056
16346
  }
16057
16347
  };
16058
16348
  });
16349
+ api.registerTool(() => {
16350
+ return {
16351
+ name: "cohort_triage",
16352
+ label: "cohort_triage",
16353
+ description: "Report your triage decision on a Cohort triage item back to Cohort. Pass the Triage item ID from your wake task as triage_item_id, the tier (trivial | judgment | feature | not_actionable), whether it is actionable, and your reasoning. Also pass a short `title` \u2014 a brief (\u226480 char) issue summary in your own words (a task-name headline of what you read), NOT a paste of the feedback; Cohort uses it as the Build-it task title. SHADOW MODE: do NOT write code or open PRs.",
16354
+ parameters: Type.Object({
16355
+ triage_item_id: Type.String({ description: "Triage item ID from your wake task (e.g. triageItems:abc123)." }),
16356
+ tier: Type.Union([
16357
+ Type.Literal("trivial"),
16358
+ Type.Literal("judgment"),
16359
+ Type.Literal("feature"),
16360
+ Type.Literal("not_actionable")
16361
+ ], { description: "Triage tier for this feedback." }),
16362
+ actionable: Type.Boolean({ description: "Whether this feedback is actionable." }),
16363
+ reasoning: Type.String({ description: "Why you reached this decision." }),
16364
+ confidence: Type.Optional(Type.Number({ description: "Optional confidence in the decision, 0 to 1." })),
16365
+ title: Type.Optional(Type.String({ description: "Optional short (\u226480 char) issue summary in your own words \u2014 a task-name headline of the problem, NOT a paste of the feedback. Used as the Build-it task title." }))
16366
+ }),
16367
+ async execute(_toolCallId, params) {
16368
+ const rt = getToolRuntime();
16369
+ if (!rt.isReady) {
16370
+ return textResult("cohort_triage is not ready yet \u2014 the plugin is still starting up.");
16371
+ }
16372
+ try {
16373
+ const response = await fetch(
16374
+ `${rt.apiUrl}/api/v1/plugin/triage/${encodeURIComponent(params.triage_item_id)}/decision`,
16375
+ {
16376
+ method: "POST",
16377
+ headers: {
16378
+ "Authorization": `Bearer ${rt.apiKey}`,
16379
+ "Content-Type": "application/json"
16380
+ },
16381
+ body: JSON.stringify({
16382
+ tier: params.tier,
16383
+ actionable: params.actionable,
16384
+ reasoning: params.reasoning,
16385
+ ...params.confidence !== void 0 ? { confidence: params.confidence } : {},
16386
+ ...params.title !== void 0 ? { title: params.title } : {}
16387
+ }),
16388
+ signal: AbortSignal.timeout(1e4)
16389
+ }
16390
+ );
16391
+ if (!response.ok) {
16392
+ const message = await safeHttpError(response);
16393
+ return textResult(`Failed to record triage decision for ${params.triage_item_id}: ${response.status}${message}`);
16394
+ }
16395
+ const result = await response.json();
16396
+ const verdict = params.actionable ? "actionable" : "not actionable";
16397
+ return textResult(`Recorded triage decision for ${params.triage_item_id}: ${params.tier} (${verdict}).`, result);
16398
+ } catch (err) {
16399
+ return textResult(`Failed to record triage decision for ${params.triage_item_id}: ${err instanceof Error ? redactSecrets(err.message) : "Unknown error"}`);
16400
+ }
16401
+ }
16402
+ };
16403
+ });
16404
+ api.registerTool(() => {
16405
+ return {
16406
+ name: "cohort_triage_pr",
16407
+ label: "cohort_triage_pr",
16408
+ description: "Report a Build-it fix you produced for a Cohort triage item. Pass the Triage item ID, the base commit SHA you branched from (base_sha), and a structured brief (problem, solution, optional risks). The tool collects your changed files from the current worktree and sends them to Cohort; the Cohort spine opens the PR and it auto-merges once CI is green. Do NOT open a PR or merge yourself \u2014 you hold no merge credential.",
16409
+ parameters: Type.Object({
16410
+ triage_item_id: Type.String({ description: "Triage item ID from your wake task (e.g. triageItems:abc123)." }),
16411
+ base_sha: Type.String({ description: "The 40- or 64-char hex commit SHA you branched from." }),
16412
+ brief: Type.Object({
16413
+ problem: Type.String({ description: "What's wrong (the issue being fixed)." }),
16414
+ solution: Type.String({ description: "How the PR fixes it (rendered under the 'Fix' label)." }),
16415
+ risks: Type.Optional(Type.String({ description: "Optional risks/blast radius of the change." }))
16416
+ }),
16417
+ repo_dir: Type.Optional(Type.String({ description: "Optional worktree path to diff (defaults to the current directory)." }))
16418
+ }),
16419
+ async execute(_toolCallId, params) {
16420
+ const rt = getToolRuntime();
16421
+ if (!rt.isReady) {
16422
+ return textResult("cohort_triage_pr is not ready yet \u2014 the plugin is still starting up.");
16423
+ }
16424
+ if (!PR_BASE_SHA_RE.test(params.base_sha)) {
16425
+ return textResult("Invalid base_sha: must be a 40- or 64-char hex commit SHA.");
16426
+ }
16427
+ const normalized = normalizeTriageBrief(params.brief);
16428
+ if ("error" in normalized) {
16429
+ return textResult(`Invalid brief: ${normalized.error}`);
16430
+ }
16431
+ let files;
16432
+ try {
16433
+ files = await collectChangedFiles({ baseSha: params.base_sha, repoDir: params.repo_dir });
16434
+ } catch (err) {
16435
+ return textResult(`Failed to collect changed files for ${params.triage_item_id}: ${err instanceof Error ? redactSecrets(err.message) : "Unknown error"}`);
16436
+ }
16437
+ const envelopeError = checkPrEnvelope(files);
16438
+ if (envelopeError) {
16439
+ return textResult(envelopeError);
16440
+ }
16441
+ try {
16442
+ const response = await fetch(
16443
+ `${rt.apiUrl}/api/v1/plugin/triage/${encodeURIComponent(params.triage_item_id)}/pr`,
16444
+ {
16445
+ method: "POST",
16446
+ headers: {
16447
+ "Authorization": `Bearer ${rt.apiKey}`,
16448
+ "Content-Type": "application/json"
16449
+ },
16450
+ body: JSON.stringify({
16451
+ brief: normalized.brief,
16452
+ baseSha: params.base_sha,
16453
+ files
16454
+ }),
16455
+ signal: AbortSignal.timeout(3e4)
16456
+ }
16457
+ );
16458
+ if (!response.ok) {
16459
+ const message = await safeHttpError(response);
16460
+ return textResult(`Failed to report PR for ${params.triage_item_id}: ${response.status}${message}`);
16461
+ }
16462
+ const result = await response.json();
16463
+ return textResult(`Reported ${files.length} changed file(s) for ${params.triage_item_id}; the spine will open the PR.`, result);
16464
+ } catch (err) {
16465
+ return textResult(`Failed to report PR for ${params.triage_item_id}: ${err instanceof Error ? redactSecrets(err.message) : "Unknown error"}`);
16466
+ }
16467
+ }
16468
+ };
16469
+ });
16059
16470
  api.registerTool(() => {
16060
16471
  return {
16061
16472
  name: "cohort_relate",