@sw4rm/js-sdk 0.5.0 → 0.6.0

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/esm/index.js CHANGED
@@ -1857,25 +1857,25 @@ var require_codegen = __commonJS({
1857
1857
  var require_fetch = __commonJS({
1858
1858
  "node_modules/@protobufjs/fetch/index.js"(exports2, module2) {
1859
1859
  "use strict";
1860
- module2.exports = fetch;
1860
+ module2.exports = fetch2;
1861
1861
  var asPromise = require_aspromise();
1862
1862
  var inquire2 = require_inquire();
1863
- var fs3 = inquire2("fs");
1864
- function fetch(filename, options, callback) {
1863
+ var fs5 = inquire2("fs");
1864
+ function fetch2(filename, options, callback) {
1865
1865
  if (typeof options === "function") {
1866
1866
  callback = options;
1867
1867
  options = {};
1868
1868
  } else if (!options)
1869
1869
  options = {};
1870
1870
  if (!callback)
1871
- return asPromise(fetch, this, filename, options);
1872
- if (!options.xhr && fs3 && fs3.readFile)
1873
- return fs3.readFile(filename, function fetchReadFileCallback(err, contents) {
1874
- return err && typeof XMLHttpRequest !== "undefined" ? fetch.xhr(filename, options, callback) : err ? callback(err) : callback(null, options.binary ? contents : contents.toString("utf8"));
1871
+ return asPromise(fetch2, this, filename, options);
1872
+ if (!options.xhr && fs5 && fs5.readFile)
1873
+ return fs5.readFile(filename, function fetchReadFileCallback(err, contents) {
1874
+ return err && typeof XMLHttpRequest !== "undefined" ? fetch2.xhr(filename, options, callback) : err ? callback(err) : callback(null, options.binary ? contents : contents.toString("utf8"));
1875
1875
  });
1876
- return fetch.xhr(filename, options, callback);
1876
+ return fetch2.xhr(filename, options, callback);
1877
1877
  }
1878
- fetch.xhr = function fetch_xhr(filename, options, callback) {
1878
+ fetch2.xhr = function fetch_xhr(filename, options, callback) {
1879
1879
  var xhr = new XMLHttpRequest();
1880
1880
  xhr.onreadystatechange = function fetchOnReadyStateChange() {
1881
1881
  if (xhr.readyState !== 4)
@@ -1908,15 +1908,15 @@ var require_fetch = __commonJS({
1908
1908
  var require_path = __commonJS({
1909
1909
  "node_modules/@protobufjs/path/index.js"(exports2) {
1910
1910
  "use strict";
1911
- var path5 = exports2;
1911
+ var path7 = exports2;
1912
1912
  var isAbsolute = (
1913
1913
  /**
1914
1914
  * Tests if the specified path is absolute.
1915
1915
  * @param {string} path Path to test
1916
1916
  * @returns {boolean} `true` if path is absolute
1917
1917
  */
1918
- path5.isAbsolute = function isAbsolute2(path6) {
1919
- return /^(?:\/|\w+:)/.test(path6);
1918
+ path7.isAbsolute = function isAbsolute2(path8) {
1919
+ return /^(?:\/|\w+:)/.test(path8);
1920
1920
  }
1921
1921
  );
1922
1922
  var normalize = (
@@ -1925,9 +1925,9 @@ var require_path = __commonJS({
1925
1925
  * @param {string} path Path to normalize
1926
1926
  * @returns {string} Normalized path
1927
1927
  */
1928
- path5.normalize = function normalize2(path6) {
1929
- path6 = path6.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
1930
- var parts = path6.split("/"), absolute = isAbsolute(path6), prefix = "";
1928
+ path7.normalize = function normalize2(path8) {
1929
+ path8 = path8.replace(/\\/g, "/").replace(/\/{2,}/g, "/");
1930
+ var parts = path8.split("/"), absolute = isAbsolute(path8), prefix = "";
1931
1931
  if (absolute)
1932
1932
  prefix = parts.shift() + "/";
1933
1933
  for (var i = 0; i < parts.length; ) {
@@ -1946,7 +1946,7 @@ var require_path = __commonJS({
1946
1946
  return prefix + parts.join("/");
1947
1947
  }
1948
1948
  );
1949
- path5.resolve = function resolve(originPath, includePath, alreadyNormalized) {
1949
+ path7.resolve = function resolve(originPath, includePath, alreadyNormalized) {
1950
1950
  if (!alreadyNormalized)
1951
1951
  includePath = normalize(includePath);
1952
1952
  if (isAbsolute(includePath))
@@ -2097,16 +2097,16 @@ var require_namespace = __commonJS({
2097
2097
  object.onRemove(this);
2098
2098
  return clearCache(this);
2099
2099
  };
2100
- Namespace.prototype.define = function define2(path5, json) {
2101
- if (util.isString(path5))
2102
- path5 = path5.split(".");
2103
- else if (!Array.isArray(path5))
2100
+ Namespace.prototype.define = function define2(path7, json) {
2101
+ if (util.isString(path7))
2102
+ path7 = path7.split(".");
2103
+ else if (!Array.isArray(path7))
2104
2104
  throw TypeError("illegal path");
2105
- if (path5 && path5.length && path5[0] === "")
2105
+ if (path7 && path7.length && path7[0] === "")
2106
2106
  throw Error("path must be relative");
2107
2107
  var ptr = this;
2108
- while (path5.length > 0) {
2109
- var part = path5.shift();
2108
+ while (path7.length > 0) {
2109
+ var part = path7.shift();
2110
2110
  if (ptr.nested && ptr.nested[part]) {
2111
2111
  ptr = ptr.nested[part];
2112
2112
  if (!(ptr instanceof Namespace))
@@ -2143,26 +2143,26 @@ var require_namespace = __commonJS({
2143
2143
  });
2144
2144
  return this;
2145
2145
  };
2146
- Namespace.prototype.lookup = function lookup(path5, filterTypes, parentAlreadyChecked) {
2146
+ Namespace.prototype.lookup = function lookup(path7, filterTypes, parentAlreadyChecked) {
2147
2147
  if (typeof filterTypes === "boolean") {
2148
2148
  parentAlreadyChecked = filterTypes;
2149
2149
  filterTypes = void 0;
2150
2150
  } else if (filterTypes && !Array.isArray(filterTypes))
2151
2151
  filterTypes = [filterTypes];
2152
- if (util.isString(path5) && path5.length) {
2153
- if (path5 === ".")
2152
+ if (util.isString(path7) && path7.length) {
2153
+ if (path7 === ".")
2154
2154
  return this.root;
2155
- path5 = path5.split(".");
2156
- } else if (!path5.length)
2155
+ path7 = path7.split(".");
2156
+ } else if (!path7.length)
2157
2157
  return this;
2158
- var flatPath = path5.join(".");
2159
- if (path5[0] === "")
2160
- return this.root.lookup(path5.slice(1), filterTypes);
2158
+ var flatPath = path7.join(".");
2159
+ if (path7[0] === "")
2160
+ return this.root.lookup(path7.slice(1), filterTypes);
2161
2161
  var found = this.root._fullyQualifiedObjects && this.root._fullyQualifiedObjects["." + flatPath];
2162
2162
  if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
2163
2163
  return found;
2164
2164
  }
2165
- found = this._lookupImpl(path5, flatPath);
2165
+ found = this._lookupImpl(path7, flatPath);
2166
2166
  if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
2167
2167
  return found;
2168
2168
  }
@@ -2170,7 +2170,7 @@ var require_namespace = __commonJS({
2170
2170
  return null;
2171
2171
  var current = this;
2172
2172
  while (current.parent) {
2173
- found = current.parent._lookupImpl(path5, flatPath);
2173
+ found = current.parent._lookupImpl(path7, flatPath);
2174
2174
  if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
2175
2175
  return found;
2176
2176
  }
@@ -2178,49 +2178,49 @@ var require_namespace = __commonJS({
2178
2178
  }
2179
2179
  return null;
2180
2180
  };
2181
- Namespace.prototype._lookupImpl = function lookup(path5, flatPath) {
2181
+ Namespace.prototype._lookupImpl = function lookup(path7, flatPath) {
2182
2182
  if (Object.prototype.hasOwnProperty.call(this._lookupCache, flatPath)) {
2183
2183
  return this._lookupCache[flatPath];
2184
2184
  }
2185
- var found = this.get(path5[0]);
2185
+ var found = this.get(path7[0]);
2186
2186
  var exact = null;
2187
2187
  if (found) {
2188
- if (path5.length === 1) {
2188
+ if (path7.length === 1) {
2189
2189
  exact = found;
2190
2190
  } else if (found instanceof Namespace) {
2191
- path5 = path5.slice(1);
2192
- exact = found._lookupImpl(path5, path5.join("."));
2191
+ path7 = path7.slice(1);
2192
+ exact = found._lookupImpl(path7, path7.join("."));
2193
2193
  }
2194
2194
  } else {
2195
2195
  for (var i = 0; i < this.nestedArray.length; ++i)
2196
- if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i]._lookupImpl(path5, flatPath)))
2196
+ if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i]._lookupImpl(path7, flatPath)))
2197
2197
  exact = found;
2198
2198
  }
2199
2199
  this._lookupCache[flatPath] = exact;
2200
2200
  return exact;
2201
2201
  };
2202
- Namespace.prototype.lookupType = function lookupType(path5) {
2203
- var found = this.lookup(path5, [Type]);
2202
+ Namespace.prototype.lookupType = function lookupType(path7) {
2203
+ var found = this.lookup(path7, [Type]);
2204
2204
  if (!found)
2205
- throw Error("no such type: " + path5);
2205
+ throw Error("no such type: " + path7);
2206
2206
  return found;
2207
2207
  };
2208
- Namespace.prototype.lookupEnum = function lookupEnum(path5) {
2209
- var found = this.lookup(path5, [Enum]);
2208
+ Namespace.prototype.lookupEnum = function lookupEnum(path7) {
2209
+ var found = this.lookup(path7, [Enum]);
2210
2210
  if (!found)
2211
- throw Error("no such Enum '" + path5 + "' in " + this);
2211
+ throw Error("no such Enum '" + path7 + "' in " + this);
2212
2212
  return found;
2213
2213
  };
2214
- Namespace.prototype.lookupTypeOrEnum = function lookupTypeOrEnum(path5) {
2215
- var found = this.lookup(path5, [Type, Enum]);
2214
+ Namespace.prototype.lookupTypeOrEnum = function lookupTypeOrEnum(path7) {
2215
+ var found = this.lookup(path7, [Type, Enum]);
2216
2216
  if (!found)
2217
- throw Error("no such Type or Enum '" + path5 + "' in " + this);
2217
+ throw Error("no such Type or Enum '" + path7 + "' in " + this);
2218
2218
  return found;
2219
2219
  };
2220
- Namespace.prototype.lookupService = function lookupService(path5) {
2221
- var found = this.lookup(path5, [Service]);
2220
+ Namespace.prototype.lookupService = function lookupService(path7) {
2221
+ var found = this.lookup(path7, [Service]);
2222
2222
  if (!found)
2223
- throw Error("no such Service '" + path5 + "' in " + this);
2223
+ throw Error("no such Service '" + path7 + "' in " + this);
2224
2224
  return found;
2225
2225
  };
2226
2226
  Namespace._configure = function(Type_, Service_, Enum_) {
@@ -3366,12 +3366,12 @@ var require_root = __commonJS({
3366
3366
  if (parsed.imports) {
3367
3367
  for (; i2 < parsed.imports.length; ++i2)
3368
3368
  if (resolved2 = getBundledFileName(parsed.imports[i2]) || self2.resolvePath(filename2, parsed.imports[i2]))
3369
- fetch(resolved2);
3369
+ fetch2(resolved2);
3370
3370
  }
3371
3371
  if (parsed.weakImports) {
3372
3372
  for (i2 = 0; i2 < parsed.weakImports.length; ++i2)
3373
3373
  if (resolved2 = getBundledFileName(parsed.weakImports[i2]) || self2.resolvePath(filename2, parsed.weakImports[i2]))
3374
- fetch(resolved2, true);
3374
+ fetch2(resolved2, true);
3375
3375
  }
3376
3376
  }
3377
3377
  } catch (err) {
@@ -3381,7 +3381,7 @@ var require_root = __commonJS({
3381
3381
  finish(null, self2);
3382
3382
  }
3383
3383
  }
3384
- function fetch(filename2, weak) {
3384
+ function fetch2(filename2, weak) {
3385
3385
  filename2 = getBundledFileName(filename2) || filename2;
3386
3386
  if (self2.files.indexOf(filename2) > -1) {
3387
3387
  return;
@@ -3433,7 +3433,7 @@ var require_root = __commonJS({
3433
3433
  }
3434
3434
  for (var i = 0, resolved; i < filename.length; ++i)
3435
3435
  if (resolved = self2.resolvePath("", filename[i]))
3436
- fetch(resolved);
3436
+ fetch2(resolved);
3437
3437
  if (sync) {
3438
3438
  self2.resolveAll();
3439
3439
  return self2;
@@ -3621,14 +3621,14 @@ var require_util = __commonJS({
3621
3621
  Object.defineProperty(object, "$type", { value: enm, enumerable: false });
3622
3622
  return enm;
3623
3623
  };
3624
- util.setProperty = function setProperty(dst, path5, value, ifNotSet) {
3625
- function setProp(dst2, path6, value2) {
3626
- var part = path6.shift();
3624
+ util.setProperty = function setProperty(dst, path7, value, ifNotSet) {
3625
+ function setProp(dst2, path8, value2) {
3626
+ var part = path8.shift();
3627
3627
  if (part === "__proto__" || part === "prototype") {
3628
3628
  return dst2;
3629
3629
  }
3630
- if (path6.length > 0) {
3631
- dst2[part] = setProp(dst2[part] || {}, path6, value2);
3630
+ if (path8.length > 0) {
3631
+ dst2[part] = setProp(dst2[part] || {}, path8, value2);
3632
3632
  } else {
3633
3633
  var prevValue = dst2[part];
3634
3634
  if (prevValue && ifNotSet)
@@ -3641,10 +3641,10 @@ var require_util = __commonJS({
3641
3641
  }
3642
3642
  if (typeof dst !== "object")
3643
3643
  throw TypeError("dst must be an object");
3644
- if (!path5)
3644
+ if (!path7)
3645
3645
  throw TypeError("path must be specified");
3646
- path5 = path5.split(".");
3647
- return setProp(dst, path5, value);
3646
+ path7 = path7.split(".");
3647
+ return setProp(dst, path7, value);
3648
3648
  };
3649
3649
  Object.defineProperty(util, "decorateRoot", {
3650
3650
  get: function() {
@@ -4191,12 +4191,12 @@ var require_object = __commonJS({
4191
4191
  */
4192
4192
  fullName: {
4193
4193
  get: function() {
4194
- var path5 = [this.name], ptr = this.parent;
4194
+ var path7 = [this.name], ptr = this.parent;
4195
4195
  while (ptr) {
4196
- path5.unshift(ptr.name);
4196
+ path7.unshift(ptr.name);
4197
4197
  ptr = ptr.parent;
4198
4198
  }
4199
- return path5.join(".");
4199
+ return path7.join(".");
4200
4200
  }
4201
4201
  }
4202
4202
  });
@@ -8178,19 +8178,19 @@ var require_util2 = __commonJS({
8178
8178
  "use strict";
8179
8179
  Object.defineProperty(exports2, "__esModule", { value: true });
8180
8180
  exports2.addCommonProtos = exports2.loadProtosWithOptionsSync = exports2.loadProtosWithOptions = void 0;
8181
- var fs3 = __require("fs");
8182
- var path5 = __require("path");
8181
+ var fs5 = __require("fs");
8182
+ var path7 = __require("path");
8183
8183
  var Protobuf = require_protobufjs();
8184
8184
  function addIncludePathResolver(root, includePaths) {
8185
8185
  const originalResolvePath = root.resolvePath;
8186
8186
  root.resolvePath = (origin, target) => {
8187
- if (path5.isAbsolute(target)) {
8187
+ if (path7.isAbsolute(target)) {
8188
8188
  return target;
8189
8189
  }
8190
8190
  for (const directory of includePaths) {
8191
- const fullPath = path5.join(directory, target);
8191
+ const fullPath = path7.join(directory, target);
8192
8192
  try {
8193
- fs3.accessSync(fullPath, fs3.constants.R_OK);
8193
+ fs5.accessSync(fullPath, fs5.constants.R_OK);
8194
8194
  return fullPath;
8195
8195
  } catch (err) {
8196
8196
  continue;
@@ -9505,6 +9505,8 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
9505
9505
  ErrorCode2[ErrorCode2["TTL_EXPIRED"] = 13] = "TTL_EXPIRED";
9506
9506
  ErrorCode2[ErrorCode2["DUPLICATE_DETECTED"] = 14] = "DUPLICATE_DETECTED";
9507
9507
  ErrorCode2[ErrorCode2["ALREADY_IN_PROGRESS"] = 15] = "ALREADY_IN_PROGRESS";
9508
+ ErrorCode2[ErrorCode2["OVERLOADED"] = 16] = "OVERLOADED";
9509
+ ErrorCode2[ErrorCode2["REDIRECT"] = 20] = "REDIRECT";
9508
9510
  ErrorCode2[ErrorCode2["INTERNAL_ERROR"] = 99] = "INTERNAL_ERROR";
9509
9511
  return ErrorCode2;
9510
9512
  })(ErrorCode || {});
@@ -10779,6 +10781,38 @@ var HandoffStatus = /* @__PURE__ */ ((HandoffStatus2) => {
10779
10781
  HandoffStatus2[HandoffStatus2["EXPIRED"] = 5] = "EXPIRED";
10780
10782
  return HandoffStatus2;
10781
10783
  })(HandoffStatus || {});
10784
+ var DEFAULT_MAX_RETRIES_ON_OVERLOADED = 2;
10785
+ var DEFAULT_INITIAL_BACKOFF_MS = 250;
10786
+ var DEFAULT_BACKOFF_MULTIPLIER = 2;
10787
+ var DEFAULT_MAX_BACKOFF_MS = 2e3;
10788
+ var DEFAULT_MAX_REDIRECTS = 0;
10789
+ function defaultDelegationPolicy() {
10790
+ return {
10791
+ maxRetriesOnOverloaded: DEFAULT_MAX_RETRIES_ON_OVERLOADED,
10792
+ initialBackoffMs: DEFAULT_INITIAL_BACKOFF_MS,
10793
+ backoffMultiplier: DEFAULT_BACKOFF_MULTIPLIER,
10794
+ maxBackoffMs: DEFAULT_MAX_BACKOFF_MS,
10795
+ allowSpilloverRouting: false,
10796
+ maxRedirects: DEFAULT_MAX_REDIRECTS
10797
+ };
10798
+ }
10799
+ function normalizeDelegationPolicy(policy) {
10800
+ const defaults = defaultDelegationPolicy();
10801
+ const maxRetriesOnOverloaded = policy?.maxRetriesOnOverloaded;
10802
+ const initialBackoffMs = policy?.initialBackoffMs;
10803
+ const backoffMultiplier = policy?.backoffMultiplier;
10804
+ const maxBackoffMs = policy?.maxBackoffMs;
10805
+ const allowSpilloverRouting = policy?.allowSpilloverRouting;
10806
+ const maxRedirects = policy?.maxRedirects;
10807
+ return {
10808
+ maxRetriesOnOverloaded: maxRetriesOnOverloaded === void 0 ? defaults.maxRetriesOnOverloaded : maxRetriesOnOverloaded,
10809
+ initialBackoffMs: initialBackoffMs !== void 0 && initialBackoffMs > 0 ? initialBackoffMs : defaults.initialBackoffMs,
10810
+ backoffMultiplier: backoffMultiplier !== void 0 && backoffMultiplier > 0 ? backoffMultiplier : defaults.backoffMultiplier,
10811
+ maxBackoffMs: maxBackoffMs !== void 0 && maxBackoffMs > 0 ? maxBackoffMs : defaults.maxBackoffMs,
10812
+ allowSpilloverRouting: allowSpilloverRouting ?? defaults.allowSpilloverRouting,
10813
+ maxRedirects: maxRedirects === void 0 ? defaults.maxRedirects : maxRedirects
10814
+ };
10815
+ }
10782
10816
  var HandoffValidationError = class extends Error {
10783
10817
  constructor(message) {
10784
10818
  super(message);
@@ -10827,9 +10861,15 @@ var HandoffClient = class {
10827
10861
  `Handoff request with ID '${request.requestId}' already exists`
10828
10862
  );
10829
10863
  }
10864
+ if (request.budget !== void 0 && (!request.budget.deadlineEpochMs || request.budget.deadlineEpochMs <= 0)) {
10865
+ throw new HandoffValidationError(
10866
+ "budget.deadlineEpochMs is required for cross-swarm delegation"
10867
+ );
10868
+ }
10830
10869
  const requestWithTimestamp = {
10831
10870
  ...request,
10832
- createdAt: request.createdAt || (/* @__PURE__ */ new Date()).toISOString()
10871
+ createdAt: request.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
10872
+ delegationPolicy: request.budget !== void 0 || request.delegationPolicy !== void 0 ? normalizeDelegationPolicy(request.delegationPolicy) : void 0
10833
10873
  };
10834
10874
  this.requests.set(request.requestId, requestWithTimestamp);
10835
10875
  if (request.toAgent) {
@@ -10851,7 +10891,8 @@ var HandoffClient = class {
10851
10891
  const timeoutResponse = {
10852
10892
  requestId: request.requestId,
10853
10893
  accepted: false,
10854
- rejectionReason: `Handoff request timed out after ${timeoutMs}ms`
10894
+ rejectionReason: `Handoff request timed out after ${timeoutMs}ms`,
10895
+ rejectionCode: 3 /* ACK_TIMEOUT */
10855
10896
  };
10856
10897
  this.responses.set(request.requestId, timeoutResponse);
10857
10898
  if (request.toAgent && this.pendingByAgent.has(request.toAgent)) {
@@ -10916,7 +10957,7 @@ var HandoffClient = class {
10916
10957
  * await client.rejectHandoff("handoff-123", "Agent at capacity");
10917
10958
  * ```
10918
10959
  */
10919
- async rejectHandoff(handoffId, reason) {
10960
+ async rejectHandoff(handoffId, reason, options) {
10920
10961
  const request = this.requests.get(handoffId);
10921
10962
  if (!request) {
10922
10963
  throw new HandoffValidationError(
@@ -10931,7 +10972,10 @@ var HandoffClient = class {
10931
10972
  const response = {
10932
10973
  requestId: handoffId,
10933
10974
  accepted: false,
10934
- rejectionReason: reason
10975
+ rejectionReason: reason,
10976
+ rejectionCode: options?.rejectionCode ?? 0 /* ERROR_CODE_UNSPECIFIED */,
10977
+ retryAfterMs: options?.retryAfterMs,
10978
+ redirectToAgentId: options?.redirectToAgentId
10935
10979
  };
10936
10980
  this.responses.set(handoffId, response);
10937
10981
  if (request.toAgent && this.pendingByAgent.has(request.toAgent)) {
@@ -11435,8 +11479,8 @@ function nowHlcStub(date = /* @__PURE__ */ new Date()) {
11435
11479
  const wallUs = date.getTime() * 1e3;
11436
11480
  let node = "unknown";
11437
11481
  try {
11438
- const os = __require("node:os");
11439
- node = os.hostname() || "unknown";
11482
+ const os3 = __require("node:os");
11483
+ node = os3.hostname() || "unknown";
11440
11484
  } catch {
11441
11485
  node = "browser";
11442
11486
  }
@@ -11893,6 +11937,542 @@ function decodeBase64(b64) {
11893
11937
  return new Uint8Array(Buffer.from(b64, "base64"));
11894
11938
  }
11895
11939
 
11940
+ // src/runtime/delegation.ts
11941
+ import { randomUUID } from "node:crypto";
11942
+ var RETRY_AFTER_JITTER_RATIO = 0.2;
11943
+ var DEFAULT_EFFECTIVE_MAX_REDIRECTS = 2;
11944
+ function defaultNowMs() {
11945
+ return Date.now();
11946
+ }
11947
+ function defaultSleepMs(milliseconds) {
11948
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
11949
+ }
11950
+ function defaultRandUniform(low, high) {
11951
+ if (high <= low) {
11952
+ return low;
11953
+ }
11954
+ return low + Math.random() * (high - low);
11955
+ }
11956
+ function normalizePolicy(policy) {
11957
+ return {
11958
+ maxRetriesOnOverloaded: policy?.maxRetriesOnOverloaded ?? DEFAULT_MAX_RETRIES_ON_OVERLOADED,
11959
+ initialBackoffMs: policy?.initialBackoffMs !== void 0 && policy.initialBackoffMs > 0 ? policy.initialBackoffMs : DEFAULT_INITIAL_BACKOFF_MS,
11960
+ backoffMultiplier: policy?.backoffMultiplier !== void 0 && policy.backoffMultiplier > 0 ? policy.backoffMultiplier : DEFAULT_BACKOFF_MULTIPLIER,
11961
+ maxBackoffMs: policy?.maxBackoffMs !== void 0 && policy.maxBackoffMs > 0 ? policy.maxBackoffMs : DEFAULT_MAX_BACKOFF_MS,
11962
+ allowSpilloverRouting: policy?.allowSpilloverRouting ?? false,
11963
+ maxRedirects: policy?.maxRedirects ?? DEFAULT_MAX_REDIRECTS
11964
+ };
11965
+ }
11966
+ function deadlineExhaustedResponse(requestId) {
11967
+ return {
11968
+ requestId,
11969
+ accepted: false,
11970
+ rejectionReason: "Delegation deadline exhausted before handoff acceptance",
11971
+ rejectionCode: 3 /* ACK_TIMEOUT */
11972
+ };
11973
+ }
11974
+ function invalidRedirectResponse(requestId, reason) {
11975
+ return {
11976
+ requestId,
11977
+ accepted: false,
11978
+ rejectionReason: reason,
11979
+ rejectionCode: 6 /* VALIDATION_ERROR */
11980
+ };
11981
+ }
11982
+ function effectiveMaxRedirects(policy) {
11983
+ const configured = policy.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
11984
+ return configured > 0 ? configured : DEFAULT_EFFECTIVE_MAX_REDIRECTS;
11985
+ }
11986
+ function nextRetryWaitMs(response, retryIndex, policy, randUniformFn) {
11987
+ if (response.retryAfterMs !== void 0 && response.retryAfterMs > 0) {
11988
+ const retryAfter = response.retryAfterMs;
11989
+ const jitter = randUniformFn(0, retryAfter * RETRY_AFTER_JITTER_RATIO);
11990
+ return Math.floor(retryAfter + jitter);
11991
+ }
11992
+ const initialBackoffMs = policy.initialBackoffMs ?? DEFAULT_INITIAL_BACKOFF_MS;
11993
+ const backoffMultiplier = policy.backoffMultiplier ?? DEFAULT_BACKOFF_MULTIPLIER;
11994
+ const maxBackoffMs = policy.maxBackoffMs ?? DEFAULT_MAX_BACKOFF_MS;
11995
+ const exponential = initialBackoffMs * backoffMultiplier ** retryIndex;
11996
+ const bounded = Math.min(exponential, maxBackoffMs);
11997
+ return Math.floor(randUniformFn(0, bounded));
11998
+ }
11999
+ function deductWallTime(request, elapsedMs) {
12000
+ if (request.budget?.wallTimeRemainingMs !== void 0) {
12001
+ request.budget.wallTimeRemainingMs = Math.max(
12002
+ request.budget.wallTimeRemainingMs - elapsedMs,
12003
+ 0
12004
+ );
12005
+ }
12006
+ }
12007
+ function budgetExhausted(budget, nowMs) {
12008
+ return budget.wallTimeRemainingMs !== void 0 && budget.wallTimeRemainingMs <= 0 || nowMs > budget.deadlineEpochMs;
12009
+ }
12010
+ async function delegateToSwarm(options) {
12011
+ if (!options.budget.deadlineEpochMs || options.budget.deadlineEpochMs <= 0) {
12012
+ throw new HandoffValidationError(
12013
+ "budget.deadlineEpochMs is required for cross-swarm delegation"
12014
+ );
12015
+ }
12016
+ if (options.timeoutMs !== void 0 && options.timeoutMs < 0) {
12017
+ throw new HandoffValidationError("timeoutMs must be >= 0");
12018
+ }
12019
+ const nowMs = options.nowMsFn ?? defaultNowMs;
12020
+ const sleepMs = options.sleepMsFn ?? defaultSleepMs;
12021
+ const randUniform = options.randUniformFn ?? defaultRandUniform;
12022
+ const policy = normalizePolicy(options.delegationPolicy);
12023
+ const requestBudget = { ...options.budget };
12024
+ const request = {
12025
+ requestId: options.requestId ?? randomUUID(),
12026
+ fromAgent: options.fromAgent,
12027
+ toAgent: options.toAgent,
12028
+ reason: options.reason,
12029
+ contextSnapshot: options.contextSnapshot ?? new Uint8Array(),
12030
+ capabilitiesRequired: [...options.capabilitiesRequired ?? []],
12031
+ priority: options.priority ?? 0,
12032
+ timeoutMs: options.timeoutMs,
12033
+ budget: requestBudget,
12034
+ delegationPolicy: policy
12035
+ };
12036
+ const maxRetriesOnOverloaded = policy.maxRetriesOnOverloaded ?? DEFAULT_MAX_RETRIES_ON_OVERLOADED;
12037
+ let retryIndex = 0;
12038
+ let redirectHops = 0;
12039
+ const redirectBound = effectiveMaxRedirects(policy);
12040
+ const visitedAgents = /* @__PURE__ */ new Set([request.toAgent]);
12041
+ while (true) {
12042
+ const startMs = nowMs();
12043
+ if (budgetExhausted(request.budget, startMs)) {
12044
+ return deadlineExhaustedResponse(request.requestId);
12045
+ }
12046
+ const response = await options.sendHandoffFn(request);
12047
+ const endMs = nowMs();
12048
+ const elapsedMs = Math.max(endMs - startMs, 0);
12049
+ deductWallTime(request, elapsedMs);
12050
+ if (response.accepted) {
12051
+ return response;
12052
+ }
12053
+ if (budgetExhausted(request.budget, endMs)) {
12054
+ return deadlineExhaustedResponse(request.requestId);
12055
+ }
12056
+ if (response.rejectionCode !== 16 /* OVERLOADED */) {
12057
+ if (response.rejectionCode !== 20 /* REDIRECT */) {
12058
+ return response;
12059
+ }
12060
+ if (!policy.allowSpilloverRouting) {
12061
+ return response;
12062
+ }
12063
+ const targetAgent = response.redirectToAgentId?.trim();
12064
+ if (!targetAgent) {
12065
+ return invalidRedirectResponse(
12066
+ request.requestId,
12067
+ "Redirect response missing non-empty redirectToAgentId"
12068
+ );
12069
+ }
12070
+ if (visitedAgents.has(targetAgent)) {
12071
+ return invalidRedirectResponse(
12072
+ request.requestId,
12073
+ `Redirect loop detected for agent '${targetAgent}'`
12074
+ );
12075
+ }
12076
+ if (redirectHops >= redirectBound) {
12077
+ return response;
12078
+ }
12079
+ request.toAgent = targetAgent;
12080
+ visitedAgents.add(targetAgent);
12081
+ redirectHops += 1;
12082
+ continue;
12083
+ }
12084
+ if (retryIndex >= maxRetriesOnOverloaded) {
12085
+ return response;
12086
+ }
12087
+ const waitMs = nextRetryWaitMs(response, retryIndex, policy, randUniform);
12088
+ retryIndex += 1;
12089
+ const remainingDeadlineMs = request.budget.deadlineEpochMs - endMs;
12090
+ const remainingWallTimeMs = request.budget?.wallTimeRemainingMs;
12091
+ if (waitMs <= 0 || remainingWallTimeMs !== void 0 && waitMs > remainingWallTimeMs || waitMs > remainingDeadlineMs) {
12092
+ return response;
12093
+ }
12094
+ const beforeSleepMs = nowMs();
12095
+ await sleepMs(waitMs);
12096
+ const afterSleepMs = nowMs();
12097
+ deductWallTime(request, Math.max(afterSleepMs - beforeSleepMs, 0));
12098
+ }
12099
+ }
12100
+
12101
+ // src/runtime/cancellation.ts
12102
+ var MIN_GRACE_PERIOD_MS = 5e3;
12103
+ var CancellationValidationError = class extends Error {
12104
+ constructor(message) {
12105
+ super(message);
12106
+ this.name = "CancellationValidationError";
12107
+ }
12108
+ };
12109
+ function defaultNowMs2() {
12110
+ return Date.now();
12111
+ }
12112
+ function normalizeCorrelationId(correlationId) {
12113
+ const normalized = correlationId.trim();
12114
+ if (!normalized) {
12115
+ throw new CancellationValidationError(
12116
+ "cancel.correlationId is required and must be a non-empty string"
12117
+ );
12118
+ }
12119
+ return normalized;
12120
+ }
12121
+ function normalizeReason(reason) {
12122
+ if (reason === void 0) {
12123
+ return "";
12124
+ }
12125
+ return reason.trim();
12126
+ }
12127
+ function normalizeGracePeriodMs(gracePeriodMs) {
12128
+ const requested = gracePeriodMs !== void 0 && Number.isFinite(gracePeriodMs) && gracePeriodMs > 0 ? Math.floor(gracePeriodMs) : 0;
12129
+ return Math.max(requested, MIN_GRACE_PERIOD_MS);
12130
+ }
12131
+ function normalizeMetadataValue(value) {
12132
+ if (value === null) {
12133
+ return null;
12134
+ }
12135
+ if (typeof value === "string") {
12136
+ return value.trim();
12137
+ }
12138
+ if (typeof value === "number") {
12139
+ return Number.isFinite(value) ? value : void 0;
12140
+ }
12141
+ if (typeof value === "boolean") {
12142
+ return value;
12143
+ }
12144
+ if (value instanceof Date) {
12145
+ return value.toISOString();
12146
+ }
12147
+ if (typeof value === "bigint") {
12148
+ return value.toString();
12149
+ }
12150
+ if (typeof value === "object") {
12151
+ try {
12152
+ return JSON.stringify(value);
12153
+ } catch {
12154
+ return void 0;
12155
+ }
12156
+ }
12157
+ return void 0;
12158
+ }
12159
+ function normalizeMetadata(metadata, options) {
12160
+ const { reason, gracePeriodMs } = options;
12161
+ const normalized = {};
12162
+ if (metadata !== void 0) {
12163
+ for (const [rawKey, rawValue] of Object.entries(metadata)) {
12164
+ const key = rawKey.trim();
12165
+ if (!key) {
12166
+ continue;
12167
+ }
12168
+ const value = normalizeMetadataValue(rawValue);
12169
+ if (value !== void 0) {
12170
+ normalized[key] = value;
12171
+ }
12172
+ }
12173
+ }
12174
+ normalized.reason = reason;
12175
+ normalized.grace_period_ms = gracePeriodMs;
12176
+ return normalized;
12177
+ }
12178
+ function cloneMetadata(metadata) {
12179
+ return { ...metadata };
12180
+ }
12181
+ function cloneFlag(flag) {
12182
+ return {
12183
+ cancelled: flag.cancelled,
12184
+ gracePeriodMs: flag.gracePeriodMs,
12185
+ cancelTimeMs: flag.cancelTimeMs,
12186
+ metadata: cloneMetadata(flag.metadata)
12187
+ };
12188
+ }
12189
+ var CancellationManager = class {
12190
+ childDelegations = /* @__PURE__ */ new Map();
12191
+ cancellationFlags = /* @__PURE__ */ new Map();
12192
+ nowMs;
12193
+ constructor(options = {}) {
12194
+ this.nowMs = options.nowMsFn ?? defaultNowMs2;
12195
+ }
12196
+ registerChildDelegation(parentCorrelationId, childCorrelationId) {
12197
+ const parent = normalizeCorrelationId(parentCorrelationId);
12198
+ const child = normalizeCorrelationId(childCorrelationId);
12199
+ if (!this.childDelegations.has(parent)) {
12200
+ this.childDelegations.set(parent, /* @__PURE__ */ new Set());
12201
+ }
12202
+ this.childDelegations.get(parent).add(child);
12203
+ }
12204
+ handleCancelDelegation(cancel) {
12205
+ const correlationId = normalizeCorrelationId(cancel.correlationId);
12206
+ const gracePeriodMs = normalizeGracePeriodMs(cancel.gracePeriodMs);
12207
+ const reason = normalizeReason(cancel.reason);
12208
+ const cancelTimeMs = this.nowMs();
12209
+ const metadata = normalizeMetadata(cancel.metadata, {
12210
+ reason,
12211
+ gracePeriodMs
12212
+ });
12213
+ const flag = {
12214
+ cancelled: true,
12215
+ gracePeriodMs,
12216
+ cancelTimeMs,
12217
+ metadata
12218
+ };
12219
+ this.cancellationFlags.set(correlationId, cloneFlag(flag));
12220
+ for (const childCorrelationId of this.childDelegations.get(correlationId) ?? []) {
12221
+ this.cancellationFlags.set(childCorrelationId, cloneFlag(flag));
12222
+ }
12223
+ return {
12224
+ acknowledged: true,
12225
+ correlationId,
12226
+ gracePeriodMs,
12227
+ message: "Cancellation recorded",
12228
+ metadata: cloneMetadata(metadata)
12229
+ };
12230
+ }
12231
+ isCancelled(correlationId) {
12232
+ const normalized = correlationId.trim();
12233
+ if (!normalized) {
12234
+ return false;
12235
+ }
12236
+ const entry = this.cancellationFlags.get(normalized);
12237
+ return Boolean(entry?.cancelled);
12238
+ }
12239
+ isGraceExpired(correlationId, nowMs) {
12240
+ const normalized = correlationId.trim();
12241
+ if (!normalized) {
12242
+ return false;
12243
+ }
12244
+ const entry = this.cancellationFlags.get(normalized);
12245
+ if (entry === void 0 || !entry.cancelled) {
12246
+ return false;
12247
+ }
12248
+ const currentMs = nowMs ?? this.nowMs();
12249
+ return currentMs - entry.cancelTimeMs >= entry.gracePeriodMs;
12250
+ }
12251
+ forcedPreemptionErrorCode(correlationId, nowMs) {
12252
+ if (this.isGraceExpired(correlationId, nowMs)) {
12253
+ return 12 /* FORCED_PREEMPTION */;
12254
+ }
12255
+ return 0 /* ERROR_CODE_UNSPECIFIED */;
12256
+ }
12257
+ collectForcedPreemptions(correlationIds, nowMs) {
12258
+ const currentMs = nowMs ?? this.nowMs();
12259
+ const forced = /* @__PURE__ */ new Set();
12260
+ for (const correlationId of correlationIds) {
12261
+ if (this.isGraceExpired(correlationId, currentMs)) {
12262
+ forced.add(correlationId);
12263
+ }
12264
+ }
12265
+ return forced;
12266
+ }
12267
+ };
12268
+
12269
+ // src/runtime/gateway.ts
12270
+ var REGISTRATION_TYPE_STANDARD_AGENT = 1;
12271
+ var REGISTRATION_TYPE_SWARM_GATEWAY = 2;
12272
+ var AGENT_STATE_INITIALIZING = 1;
12273
+ var AGENT_STATE_RUNNING = 4;
12274
+ var AGENT_STATE_FAILED = 10;
12275
+ var AGENT_STATE_SHUTTING_DOWN = 11;
12276
+ var DEFAULT_PEER_LIVENESS_THRESHOLD_MS = 3e4;
12277
+ var NON_SERVING_AGENT_STATES = /* @__PURE__ */ new Set([
12278
+ AGENT_STATE_INITIALIZING,
12279
+ AGENT_STATE_FAILED,
12280
+ AGENT_STATE_SHUTTING_DOWN
12281
+ ]);
12282
+ var GatewayValidationError = class extends Error {
12283
+ constructor(message) {
12284
+ super(message);
12285
+ this.name = "GatewayValidationError";
12286
+ }
12287
+ };
12288
+ function defaultNowMs3() {
12289
+ return Date.now();
12290
+ }
12291
+ function sameCapabilities(peerCapabilities, localCapabilities) {
12292
+ const peerSet = new Set(peerCapabilities);
12293
+ if (peerSet.size !== localCapabilities.size) {
12294
+ return false;
12295
+ }
12296
+ for (const localCapability of localCapabilities) {
12297
+ if (!peerSet.has(localCapability)) {
12298
+ return false;
12299
+ }
12300
+ }
12301
+ return true;
12302
+ }
12303
+ var PeerSelector = class {
12304
+ localAgentId;
12305
+ localCapabilities;
12306
+ nowMs;
12307
+ peerHealthFn;
12308
+ livenessThresholdMs;
12309
+ peers = [];
12310
+ runtime = /* @__PURE__ */ new Map();
12311
+ rrCursor = 0;
12312
+ constructor(options) {
12313
+ const threshold = options.livenessThresholdMs ?? DEFAULT_PEER_LIVENESS_THRESHOLD_MS;
12314
+ if (threshold < 0) {
12315
+ throw new GatewayValidationError("livenessThresholdMs must be >= 0");
12316
+ }
12317
+ this.localAgentId = options.localAgentId;
12318
+ this.localCapabilities = new Set(options.localCapabilities ?? []);
12319
+ this.nowMs = options.nowMsFn ?? defaultNowMs3;
12320
+ this.peerHealthFn = options.peerHealthFn ?? (() => true);
12321
+ this.livenessThresholdMs = threshold;
12322
+ }
12323
+ setPeers(peers) {
12324
+ this.peers = [...peers];
12325
+ const activeIds = new Set(this.peers.map((peer) => peer.agentId));
12326
+ for (const agentId of [...this.runtime.keys()]) {
12327
+ if (!activeIds.has(agentId)) {
12328
+ this.runtime.delete(agentId);
12329
+ }
12330
+ }
12331
+ const nowMs = this.nowMs();
12332
+ for (const peer of this.peers) {
12333
+ if (!this.runtime.has(peer.agentId)) {
12334
+ this.runtime.set(peer.agentId, {
12335
+ state: AGENT_STATE_RUNNING,
12336
+ lastHeartbeatMs: nowMs,
12337
+ cooldownUntilMs: 0
12338
+ });
12339
+ }
12340
+ }
12341
+ }
12342
+ updatePeerRuntimeState(agentId, options = {}) {
12343
+ const runtime = this.ensureRuntime(agentId);
12344
+ if (options.state !== void 0) {
12345
+ runtime.state = options.state;
12346
+ }
12347
+ if (options.lastHeartbeatMs !== void 0) {
12348
+ runtime.lastHeartbeatMs = Math.max(Math.floor(options.lastHeartbeatMs), 0);
12349
+ }
12350
+ if (options.cooldownUntilMs !== void 0) {
12351
+ runtime.cooldownUntilMs = Math.max(Math.floor(options.cooldownUntilMs), 0);
12352
+ }
12353
+ this.runtime.set(agentId, runtime);
12354
+ }
12355
+ touchPeerHeartbeat(agentId, options = {}) {
12356
+ this.updatePeerRuntimeState(agentId, {
12357
+ state: options.state,
12358
+ lastHeartbeatMs: options.nowMs ?? this.nowMs()
12359
+ });
12360
+ }
12361
+ recordPeerOverloaded(agentId, options = {}) {
12362
+ const runtime = this.ensureRuntime(agentId);
12363
+ const nowMs = this.nowMs();
12364
+ const retryAfterMs = Math.max(Math.floor(options.retryAfterMs ?? 0), 0);
12365
+ const localCooldownMs = Math.max(Math.floor(options.localCooldownMs ?? 0), 0);
12366
+ const cooldownMs = Math.max(retryAfterMs, localCooldownMs);
12367
+ runtime.cooldownUntilMs = Math.max(runtime.cooldownUntilMs, nowMs + cooldownMs);
12368
+ this.runtime.set(agentId, runtime);
12369
+ }
12370
+ isEligible(peer) {
12371
+ if (peer.registrationType !== REGISTRATION_TYPE_SWARM_GATEWAY) {
12372
+ return false;
12373
+ }
12374
+ if (peer.agentId === this.localAgentId) {
12375
+ return false;
12376
+ }
12377
+ return sameCapabilities(peer.capabilities, this.localCapabilities);
12378
+ }
12379
+ selectPeer() {
12380
+ const healthy = this.peers.filter((peer) => this.isHealthy(peer));
12381
+ if (healthy.length === 0) {
12382
+ return "";
12383
+ }
12384
+ const idx = this.rrCursor % healthy.length;
12385
+ this.rrCursor += 1;
12386
+ return healthy[idx].agentId;
12387
+ }
12388
+ ensureRuntime(agentId) {
12389
+ const existing = this.runtime.get(agentId);
12390
+ if (existing !== void 0) {
12391
+ return existing;
12392
+ }
12393
+ return {
12394
+ state: AGENT_STATE_RUNNING,
12395
+ lastHeartbeatMs: this.nowMs(),
12396
+ cooldownUntilMs: 0
12397
+ };
12398
+ }
12399
+ isHealthy(peer) {
12400
+ if (!this.isEligible(peer)) {
12401
+ return false;
12402
+ }
12403
+ if (!this.peerHealthFn(peer)) {
12404
+ return false;
12405
+ }
12406
+ const runtime = this.runtime.get(peer.agentId);
12407
+ if (runtime === void 0) {
12408
+ return false;
12409
+ }
12410
+ const nowMs = this.nowMs();
12411
+ if (runtime.cooldownUntilMs > nowMs) {
12412
+ return false;
12413
+ }
12414
+ if (nowMs - runtime.lastHeartbeatMs > this.livenessThresholdMs) {
12415
+ return false;
12416
+ }
12417
+ if (NON_SERVING_AGENT_STATES.has(runtime.state)) {
12418
+ return false;
12419
+ }
12420
+ return true;
12421
+ }
12422
+ };
12423
+ var GatewayRedirectEmitter = class {
12424
+ retryAfterMs;
12425
+ peerSelector;
12426
+ constructor(options) {
12427
+ if ((options.retryAfterMs ?? 0) < 0) {
12428
+ throw new GatewayValidationError("retryAfterMs must be >= 0");
12429
+ }
12430
+ this.retryAfterMs = options.retryAfterMs ?? 1e3;
12431
+ this.peerSelector = new PeerSelector({
12432
+ localAgentId: options.agentId,
12433
+ localCapabilities: options.capabilities ?? [],
12434
+ nowMsFn: options.nowMsFn,
12435
+ peerHealthFn: options.peerHealthFn,
12436
+ livenessThresholdMs: options.peerLivenessThresholdMs
12437
+ });
12438
+ this.peerSelector.setPeers(options.peerDescriptors ?? []);
12439
+ }
12440
+ setPeerDescriptors(peers) {
12441
+ this.peerSelector.setPeers(peers);
12442
+ }
12443
+ updatePeerRuntimeState(agentId, options = {}) {
12444
+ this.peerSelector.updatePeerRuntimeState(agentId, options);
12445
+ }
12446
+ touchPeerHeartbeat(agentId, options = {}) {
12447
+ this.peerSelector.touchPeerHeartbeat(agentId, options);
12448
+ }
12449
+ recordPeerOverloaded(agentId, options = {}) {
12450
+ this.peerSelector.recordPeerOverloaded(agentId, options);
12451
+ }
12452
+ emitOverloadedResponse(request) {
12453
+ if (request.delegationPolicy?.allowSpilloverRouting) {
12454
+ const redirectToAgentId = this.peerSelector.selectPeer();
12455
+ if (redirectToAgentId) {
12456
+ return {
12457
+ requestId: request.requestId,
12458
+ accepted: false,
12459
+ rejectionReason: "Gateway at capacity; redirect to peer gateway",
12460
+ rejectionCode: 20 /* REDIRECT */,
12461
+ retryAfterMs: 0,
12462
+ redirectToAgentId
12463
+ };
12464
+ }
12465
+ }
12466
+ return {
12467
+ requestId: request.requestId,
12468
+ accepted: false,
12469
+ rejectionReason: "Gateway at capacity",
12470
+ rejectionCode: 16 /* OVERLOADED */,
12471
+ retryAfterMs: this.retryAfterMs
12472
+ };
12473
+ }
12474
+ };
12475
+
11896
12476
  // src/persistence/persistence.ts
11897
12477
  import fsp from "node:fs/promises";
11898
12478
  import path2 from "node:path";
@@ -14022,8 +14602,8 @@ function defaultPath() {
14022
14602
  }
14023
14603
  var FileBackend = class {
14024
14604
  path;
14025
- constructor(path5) {
14026
- this.path = path5 ?? defaultPath();
14605
+ constructor(path7) {
14606
+ this.path = path7 ?? defaultPath();
14027
14607
  mkdirSync2(dirname2(this.path), { recursive: true });
14028
14608
  try {
14029
14609
  chmodSync(dirname2(this.path), 448);
@@ -14153,30 +14733,879 @@ async function selectBackend(mode) {
14153
14733
  }
14154
14734
  }
14155
14735
 
14736
+ // src/llm/client.ts
14737
+ var LlmError = class extends Error {
14738
+ constructor(message) {
14739
+ super(message);
14740
+ this.name = "LlmError";
14741
+ Object.setPrototypeOf(this, new.target.prototype);
14742
+ }
14743
+ };
14744
+ var LlmAuthenticationError = class extends LlmError {
14745
+ constructor(message) {
14746
+ super(message);
14747
+ this.name = "LlmAuthenticationError";
14748
+ }
14749
+ };
14750
+ var LlmRateLimitError = class extends LlmError {
14751
+ constructor(message) {
14752
+ super(message);
14753
+ this.name = "LlmRateLimitError";
14754
+ }
14755
+ };
14756
+ var LlmTimeoutError = class extends LlmError {
14757
+ constructor(message) {
14758
+ super(message);
14759
+ this.name = "LlmTimeoutError";
14760
+ }
14761
+ };
14762
+ var LlmContextLengthError = class extends LlmError {
14763
+ constructor(message) {
14764
+ super(message);
14765
+ this.name = "LlmContextLengthError";
14766
+ }
14767
+ };
14768
+
14769
+ // src/llm/rateLimiter.ts
14770
+ function envBool(name, defaultVal) {
14771
+ const raw = (process.env[name] ?? defaultVal).toLowerCase();
14772
+ return !["0", "false", "no"].includes(raw);
14773
+ }
14774
+ function buildRateLimiterConfig(overrides) {
14775
+ return {
14776
+ tokensPerMinute: overrides?.tokensPerMinute ?? parseInt(process.env["LLM_RATE_LIMIT_TOKENS_PER_MIN"] ?? "250000", 10),
14777
+ burstAllowance: overrides?.burstAllowance ?? 1,
14778
+ minTokensPerRequest: overrides?.minTokensPerRequest ?? 100,
14779
+ maxWaitSeconds: overrides?.maxWaitSeconds ?? 120,
14780
+ enabled: overrides?.enabled ?? envBool("LLM_RATE_LIMIT_ENABLED", "1"),
14781
+ adaptiveEnabled: overrides?.adaptiveEnabled ?? envBool("LLM_RATE_LIMIT_ADAPTIVE", "1"),
14782
+ reductionFactor: overrides?.reductionFactor ?? parseFloat(process.env["LLM_RATE_LIMIT_REDUCTION_FACTOR"] ?? "0.7"),
14783
+ recoveryFactor: overrides?.recoveryFactor ?? parseFloat(process.env["LLM_RATE_LIMIT_RECOVERY_FACTOR"] ?? "1.1"),
14784
+ cooldownSeconds: overrides?.cooldownSeconds ?? parseFloat(process.env["LLM_RATE_LIMIT_COOLDOWN_SECONDS"] ?? "30"),
14785
+ successesForRecovery: overrides?.successesForRecovery ?? parseInt(
14786
+ process.env["LLM_RATE_LIMIT_RECOVERY_SUCCESS_THRESHOLD"] ?? "20",
14787
+ 10
14788
+ )
14789
+ };
14790
+ }
14791
+ var TokenBucket = class {
14792
+ config;
14793
+ baseTpm;
14794
+ currentTpm;
14795
+ minTpm;
14796
+ tokens;
14797
+ lastRefill;
14798
+ lastRateLimitTime = null;
14799
+ successesSinceLimit = 0;
14800
+ constructor(config) {
14801
+ this.config = buildRateLimiterConfig(config);
14802
+ this.baseTpm = this.config.tokensPerMinute;
14803
+ this.currentTpm = this.config.tokensPerMinute;
14804
+ this.minTpm = Math.max(1e3, this.baseTpm * 0.25);
14805
+ this.tokens = this.currentTpm;
14806
+ this.lastRefill = performance.now();
14807
+ }
14808
+ // -- Internal helpers ----------------------------------------------------
14809
+ refill() {
14810
+ const now = performance.now();
14811
+ const elapsedSeconds = (now - this.lastRefill) / 1e3;
14812
+ const refill = elapsedSeconds * (this.currentTpm / 60);
14813
+ this.tokens = Math.min(
14814
+ this.tokens + refill,
14815
+ this.currentTpm * this.config.burstAllowance
14816
+ );
14817
+ this.lastRefill = now;
14818
+ this.maybeRecover(now);
14819
+ }
14820
+ maybeRecover(now) {
14821
+ if (!this.config.adaptiveEnabled)
14822
+ return;
14823
+ if (this.currentTpm >= this.baseTpm)
14824
+ return;
14825
+ if (this.lastRateLimitTime === null)
14826
+ return;
14827
+ if ((now - this.lastRateLimitTime) / 1e3 < this.config.cooldownSeconds)
14828
+ return;
14829
+ if (this.successesSinceLimit < this.config.successesForRecovery)
14830
+ return;
14831
+ const newLimit = Math.min(
14832
+ this.baseTpm,
14833
+ this.currentTpm * this.config.recoveryFactor
14834
+ );
14835
+ if (newLimit > this.currentTpm) {
14836
+ this.currentTpm = newLimit;
14837
+ this.successesSinceLimit = 0;
14838
+ }
14839
+ }
14840
+ // -- Public API ----------------------------------------------------------
14841
+ /**
14842
+ * Acquire tokens, waiting if necessary.
14843
+ *
14844
+ * @param estimatedTokens - Estimated token count for the request.
14845
+ * @returns Time spent waiting in milliseconds (0 if immediate).
14846
+ * @throws Error if waiting exceeds {@link RateLimiterConfig.maxWaitSeconds}.
14847
+ */
14848
+ async acquire(estimatedTokens) {
14849
+ if (!this.config.enabled)
14850
+ return 0;
14851
+ estimatedTokens = Math.max(estimatedTokens, this.config.minTokensPerRequest);
14852
+ const waitStart = performance.now();
14853
+ for (; ; ) {
14854
+ this.refill();
14855
+ if (this.tokens >= estimatedTokens) {
14856
+ this.tokens -= estimatedTokens;
14857
+ return performance.now() - waitStart;
14858
+ }
14859
+ const elapsed = (performance.now() - waitStart) / 1e3;
14860
+ if (elapsed >= this.config.maxWaitSeconds) {
14861
+ throw new Error(
14862
+ `Rate limiter: waited ${elapsed.toFixed(1)}s for ${estimatedTokens} tokens`
14863
+ );
14864
+ }
14865
+ await new Promise((resolve) => setTimeout(resolve, 250));
14866
+ }
14867
+ }
14868
+ /**
14869
+ * Record a 429 event -- adaptively reduce budget.
14870
+ *
14871
+ * Call this when the upstream API returns HTTP 429 or an equivalent
14872
+ * rate-limit error.
14873
+ */
14874
+ recordRateLimit() {
14875
+ if (!(this.config.enabled && this.config.adaptiveEnabled))
14876
+ return;
14877
+ this.lastRateLimitTime = performance.now();
14878
+ this.successesSinceLimit = 0;
14879
+ const newLimit = Math.max(
14880
+ this.minTpm,
14881
+ this.currentTpm * this.config.reductionFactor
14882
+ );
14883
+ if (newLimit < this.currentTpm) {
14884
+ this.currentTpm = newLimit;
14885
+ this.tokens = Math.min(this.tokens, this.currentTpm);
14886
+ }
14887
+ }
14888
+ /**
14889
+ * Record a successful request for adaptive recovery.
14890
+ *
14891
+ * Call this after each successful API response.
14892
+ */
14893
+ recordSuccess() {
14894
+ if (!(this.config.enabled && this.config.adaptiveEnabled))
14895
+ return;
14896
+ this.successesSinceLimit += 1;
14897
+ }
14898
+ /** Current available token count. */
14899
+ get availableTokens() {
14900
+ return this.tokens;
14901
+ }
14902
+ /** Current tokens-per-minute budget (may be reduced after 429). */
14903
+ get currentTokensPerMinute() {
14904
+ return this.currentTpm;
14905
+ }
14906
+ };
14907
+ var globalBucket = null;
14908
+ function getGlobalRateLimiter(config) {
14909
+ if (globalBucket === null) {
14910
+ globalBucket = new TokenBucket(config);
14911
+ }
14912
+ return globalBucket;
14913
+ }
14914
+ function resetGlobalRateLimiter() {
14915
+ globalBucket = null;
14916
+ }
14917
+
14918
+ // src/llm/mock.ts
14919
+ var MockLlmClient = class {
14920
+ /** The model name returned in responses. */
14921
+ defaultModel;
14922
+ responses;
14923
+ responseIndex = 0;
14924
+ responseGenerator;
14925
+ _callCount = 0;
14926
+ _callHistory = [];
14927
+ constructor(opts) {
14928
+ this.defaultModel = opts?.defaultModel ?? "mock-model";
14929
+ this.responses = opts?.responses ?? [];
14930
+ this.responseGenerator = opts?.responseGenerator;
14931
+ }
14932
+ /** Number of queries made to this client. */
14933
+ get callCount() {
14934
+ return this._callCount;
14935
+ }
14936
+ /** History of all calls made to this client. */
14937
+ get callHistory() {
14938
+ return this._callHistory;
14939
+ }
14940
+ /** Reset call count, history, and response index. */
14941
+ reset() {
14942
+ this._callCount = 0;
14943
+ this._callHistory.length = 0;
14944
+ this.responseIndex = 0;
14945
+ }
14946
+ /**
14947
+ * Return a mock response.
14948
+ *
14949
+ * Priority for determining response content:
14950
+ * 1. `responseGenerator` function (if provided)
14951
+ * 2. `responses` array (cycles through entries)
14952
+ * 3. Default echo: "Mock response to: <prompt>"
14953
+ *
14954
+ * @param prompt - The prompt (recorded in history).
14955
+ * @param opts - Optional query configuration (recorded in history).
14956
+ * @returns LlmResponse with mock content.
14957
+ */
14958
+ async query(prompt, opts) {
14959
+ const model = opts?.model ?? this.defaultModel;
14960
+ const maxTokens = opts?.maxTokens ?? 4096;
14961
+ const temperature = opts?.temperature ?? 1;
14962
+ this._callCount += 1;
14963
+ this._callHistory.push({
14964
+ prompt,
14965
+ systemPrompt: opts?.systemPrompt,
14966
+ maxTokens,
14967
+ temperature,
14968
+ model
14969
+ });
14970
+ let content;
14971
+ if (this.responseGenerator) {
14972
+ content = this.responseGenerator(prompt);
14973
+ } else if (this.responses.length > 0) {
14974
+ content = this.responses[this.responseIndex % this.responses.length];
14975
+ this.responseIndex += 1;
14976
+ } else {
14977
+ content = `Mock response to: ${prompt.slice(0, 100)}`;
14978
+ }
14979
+ return {
14980
+ content,
14981
+ model,
14982
+ usage: {
14983
+ input_tokens: Math.max(Math.floor(prompt.length / 4), 1),
14984
+ output_tokens: Math.max(Math.floor(content.length / 4), 1)
14985
+ },
14986
+ metadata: { mock: true, call_count: this._callCount }
14987
+ };
14988
+ }
14989
+ /**
14990
+ * Stream a mock response.
14991
+ *
14992
+ * Yields the response word by word to simulate streaming.
14993
+ *
14994
+ * @param prompt - The prompt (recorded in history).
14995
+ * @param opts - Optional query configuration.
14996
+ * @yields Words from the mock response.
14997
+ */
14998
+ async *streamQuery(prompt, opts) {
14999
+ const response = await this.query(prompt, opts);
15000
+ const words = response.content.split(" ");
15001
+ for (let i = 0; i < words.length; i++) {
15002
+ yield words[i] + (i < words.length - 1 ? " " : "");
15003
+ }
15004
+ }
15005
+ };
15006
+
15007
+ // src/llm/groq.ts
15008
+ import fs3 from "node:fs";
15009
+ import os from "node:os";
15010
+ import path5 from "node:path";
15011
+ var DEFAULT_MODEL = "llama-3.3-70b-versatile";
15012
+ var BASE_URL = "https://api.groq.com/openai/v1/chat/completions";
15013
+ var GroqClient = class _GroqClient {
15014
+ /** The resolved API key. */
15015
+ apiKey;
15016
+ /** The default model used when none is specified per-call. */
15017
+ defaultModel;
15018
+ timeoutMs;
15019
+ rateLimiter;
15020
+ constructor(opts) {
15021
+ this.defaultModel = opts?.defaultModel ?? process.env["GROQ_DEFAULT_MODEL"] ?? DEFAULT_MODEL;
15022
+ const envKey = process.env["GROQ_API_KEY"]?.trim();
15023
+ const resolved = opts?.apiKey ?? envKey ?? _GroqClient.loadKeyFile();
15024
+ if (!resolved) {
15025
+ throw new LlmAuthenticationError(
15026
+ "No Groq API key. Set GROQ_API_KEY, pass apiKey, or create ~/.groq"
15027
+ );
15028
+ }
15029
+ this.apiKey = resolved;
15030
+ this.timeoutMs = opts?.timeoutMs ?? 12e4;
15031
+ this.rateLimiter = getGlobalRateLimiter();
15032
+ }
15033
+ /**
15034
+ * Load API key from ~/.groq file.
15035
+ *
15036
+ * @returns The key string, or null if the file does not exist.
15037
+ */
15038
+ static loadKeyFile() {
15039
+ try {
15040
+ const keyPath = path5.join(os.homedir(), ".groq");
15041
+ if (fs3.existsSync(keyPath)) {
15042
+ return fs3.readFileSync(keyPath, "utf-8").trim();
15043
+ }
15044
+ } catch {
15045
+ }
15046
+ return null;
15047
+ }
15048
+ // -- Helpers -------------------------------------------------------------
15049
+ estimateTokens(prompt, systemPrompt) {
15050
+ let total = prompt.length;
15051
+ if (systemPrompt)
15052
+ total += systemPrompt.length;
15053
+ return Math.max(Math.floor(total / 4), 100);
15054
+ }
15055
+ buildMessages(prompt, systemPrompt) {
15056
+ const messages = [];
15057
+ if (systemPrompt) {
15058
+ messages.push({ role: "system", content: systemPrompt });
15059
+ }
15060
+ messages.push({ role: "user", content: prompt });
15061
+ return messages;
15062
+ }
15063
+ /**
15064
+ * Map an HTTP error response to the appropriate LlmError subclass.
15065
+ *
15066
+ * @param status - HTTP status code.
15067
+ * @param body - Parsed response body (if available).
15068
+ * @param message - Raw error message.
15069
+ */
15070
+ mapError(status, body, message) {
15071
+ if (status === 401 || status === 403) {
15072
+ return new LlmAuthenticationError(`Groq auth failed (${status}): ${message}`);
15073
+ }
15074
+ if (status === 429) {
15075
+ this.rateLimiter.recordRateLimit();
15076
+ return new LlmRateLimitError(`Groq rate limit exceeded: ${message}`);
15077
+ }
15078
+ if (status === 408 || status === 504) {
15079
+ return new LlmTimeoutError(`Groq request timed out (${status}): ${message}`);
15080
+ }
15081
+ const errorObj = body?.["error"];
15082
+ const code = errorObj?.["code"];
15083
+ if (code === "context_length_exceeded") {
15084
+ return new LlmContextLengthError(`Groq context length exceeded: ${message}`);
15085
+ }
15086
+ return new LlmError(`Groq API error (${status}): ${message}`);
15087
+ }
15088
+ // -- Public API ----------------------------------------------------------
15089
+ /**
15090
+ * Send a query to Groq and get a complete response.
15091
+ *
15092
+ * @param prompt - The user prompt/query.
15093
+ * @param opts - Optional query configuration.
15094
+ * @returns LlmResponse with generated content and metadata.
15095
+ */
15096
+ async query(prompt, opts) {
15097
+ const useModel = opts?.model ?? this.defaultModel;
15098
+ const maxTokens = opts?.maxTokens ?? 4096;
15099
+ const temperature = opts?.temperature ?? 1;
15100
+ const systemPrompt = opts?.systemPrompt;
15101
+ const estimated = this.estimateTokens(prompt, systemPrompt);
15102
+ await this.rateLimiter.acquire(estimated);
15103
+ const body = {
15104
+ model: useModel,
15105
+ messages: this.buildMessages(prompt, systemPrompt),
15106
+ max_tokens: maxTokens,
15107
+ temperature
15108
+ };
15109
+ let response;
15110
+ try {
15111
+ const controller = new AbortController();
15112
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
15113
+ response = await fetch(BASE_URL, {
15114
+ method: "POST",
15115
+ headers: {
15116
+ "Content-Type": "application/json",
15117
+ "Authorization": `Bearer ${this.apiKey}`
15118
+ },
15119
+ body: JSON.stringify(body),
15120
+ signal: controller.signal
15121
+ });
15122
+ clearTimeout(timer);
15123
+ } catch (err) {
15124
+ if (err instanceof Error && err.name === "AbortError") {
15125
+ throw new LlmTimeoutError(`Groq request timed out after ${this.timeoutMs}ms`);
15126
+ }
15127
+ throw new LlmError(`Groq network error: ${String(err)}`);
15128
+ }
15129
+ if (!response.ok) {
15130
+ let errorBody = null;
15131
+ let errorMessage = response.statusText;
15132
+ try {
15133
+ errorBody = await response.json();
15134
+ const errorObj = errorBody?.["error"];
15135
+ errorMessage = errorObj?.["message"] ?? errorMessage;
15136
+ } catch {
15137
+ }
15138
+ throw this.mapError(response.status, errorBody, errorMessage);
15139
+ }
15140
+ const data = await response.json();
15141
+ this.rateLimiter.recordSuccess();
15142
+ const choices = data["choices"];
15143
+ const firstChoice = choices?.[0];
15144
+ const messageObj = firstChoice?.["message"];
15145
+ const content = messageObj?.["content"] ?? "";
15146
+ const usageObj = data["usage"];
15147
+ let usage;
15148
+ if (usageObj) {
15149
+ usage = {
15150
+ input_tokens: usageObj["prompt_tokens"] ?? 0,
15151
+ output_tokens: usageObj["completion_tokens"] ?? 0
15152
+ };
15153
+ }
15154
+ return {
15155
+ content,
15156
+ model: data["model"] ?? useModel,
15157
+ usage
15158
+ };
15159
+ }
15160
+ /**
15161
+ * Stream a query response chunk by chunk.
15162
+ *
15163
+ * Uses Server-Sent Events (SSE) streaming from the Groq API.
15164
+ *
15165
+ * @param prompt - The user prompt/query.
15166
+ * @param opts - Optional query configuration.
15167
+ * @yields Text chunks as they arrive from the API.
15168
+ */
15169
+ async *streamQuery(prompt, opts) {
15170
+ const useModel = opts?.model ?? this.defaultModel;
15171
+ const maxTokens = opts?.maxTokens ?? 4096;
15172
+ const temperature = opts?.temperature ?? 1;
15173
+ const systemPrompt = opts?.systemPrompt;
15174
+ const estimated = this.estimateTokens(prompt, systemPrompt);
15175
+ await this.rateLimiter.acquire(estimated);
15176
+ const body = {
15177
+ model: useModel,
15178
+ messages: this.buildMessages(prompt, systemPrompt),
15179
+ max_tokens: maxTokens,
15180
+ temperature,
15181
+ stream: true
15182
+ };
15183
+ let response;
15184
+ try {
15185
+ const controller = new AbortController();
15186
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
15187
+ response = await fetch(BASE_URL, {
15188
+ method: "POST",
15189
+ headers: {
15190
+ "Content-Type": "application/json",
15191
+ "Authorization": `Bearer ${this.apiKey}`
15192
+ },
15193
+ body: JSON.stringify(body),
15194
+ signal: controller.signal
15195
+ });
15196
+ clearTimeout(timer);
15197
+ } catch (err) {
15198
+ if (err instanceof Error && err.name === "AbortError") {
15199
+ throw new LlmTimeoutError(`Groq request timed out after ${this.timeoutMs}ms`);
15200
+ }
15201
+ throw new LlmError(`Groq network error: ${String(err)}`);
15202
+ }
15203
+ if (!response.ok) {
15204
+ let errorBody = null;
15205
+ let errorMessage = response.statusText;
15206
+ try {
15207
+ errorBody = await response.json();
15208
+ const errorObj = errorBody?.["error"];
15209
+ errorMessage = errorObj?.["message"] ?? errorMessage;
15210
+ } catch {
15211
+ }
15212
+ throw this.mapError(response.status, errorBody, errorMessage);
15213
+ }
15214
+ if (!response.body) {
15215
+ throw new LlmError("Groq streaming response has no body");
15216
+ }
15217
+ const reader = response.body.getReader();
15218
+ const decoder = new TextDecoder();
15219
+ let buffer = "";
15220
+ let streamCompleted = false;
15221
+ try {
15222
+ for (; ; ) {
15223
+ const { done, value } = await reader.read();
15224
+ if (done)
15225
+ break;
15226
+ buffer += decoder.decode(value, { stream: true });
15227
+ const lines = buffer.split("\n");
15228
+ buffer = lines.pop() ?? "";
15229
+ for (const line of lines) {
15230
+ const trimmed = line.trim();
15231
+ if (!trimmed || !trimmed.startsWith("data: "))
15232
+ continue;
15233
+ const payload = trimmed.slice(6);
15234
+ if (payload === "[DONE]") {
15235
+ streamCompleted = true;
15236
+ return;
15237
+ }
15238
+ try {
15239
+ const chunk = JSON.parse(payload);
15240
+ const choices = chunk["choices"];
15241
+ const delta = choices?.[0]?.["delta"];
15242
+ const content = delta?.["content"];
15243
+ if (content)
15244
+ yield content;
15245
+ } catch {
15246
+ }
15247
+ }
15248
+ }
15249
+ streamCompleted = true;
15250
+ } finally {
15251
+ reader.releaseLock();
15252
+ if (streamCompleted)
15253
+ this.rateLimiter.recordSuccess();
15254
+ }
15255
+ }
15256
+ };
15257
+
15258
+ // src/llm/anthropic.ts
15259
+ import fs4 from "node:fs";
15260
+ import os2 from "node:os";
15261
+ import path6 from "node:path";
15262
+ var DEFAULT_MODEL2 = "claude-sonnet-4-20250514";
15263
+ var BASE_URL2 = "https://api.anthropic.com/v1/messages";
15264
+ var ANTHROPIC_VERSION = "2023-06-01";
15265
+ var AnthropicClient = class _AnthropicClient {
15266
+ /** The resolved API key. */
15267
+ apiKey;
15268
+ /** The default model used when none is specified per-call. */
15269
+ defaultModel;
15270
+ timeoutMs;
15271
+ rateLimiter;
15272
+ constructor(opts) {
15273
+ this.defaultModel = opts?.defaultModel ?? process.env["ANTHROPIC_DEFAULT_MODEL"] ?? DEFAULT_MODEL2;
15274
+ const envKey = process.env["ANTHROPIC_API_KEY"]?.trim();
15275
+ const resolved = opts?.apiKey ?? envKey ?? _AnthropicClient.loadKeyFile();
15276
+ if (!resolved) {
15277
+ throw new LlmAuthenticationError(
15278
+ "No Anthropic API key. Set ANTHROPIC_API_KEY, pass apiKey, or create ~/.anthropic"
15279
+ );
15280
+ }
15281
+ this.apiKey = resolved;
15282
+ this.timeoutMs = opts?.timeoutMs ?? 3e5;
15283
+ this.rateLimiter = getGlobalRateLimiter();
15284
+ }
15285
+ /**
15286
+ * Load API key from ~/.anthropic file.
15287
+ *
15288
+ * @returns The key string, or null if the file does not exist.
15289
+ */
15290
+ static loadKeyFile() {
15291
+ try {
15292
+ const keyPath = path6.join(os2.homedir(), ".anthropic");
15293
+ if (fs4.existsSync(keyPath)) {
15294
+ return fs4.readFileSync(keyPath, "utf-8").trim();
15295
+ }
15296
+ } catch {
15297
+ }
15298
+ return null;
15299
+ }
15300
+ // -- Helpers -------------------------------------------------------------
15301
+ estimateTokens(prompt, systemPrompt) {
15302
+ let total = prompt.length;
15303
+ if (systemPrompt)
15304
+ total += systemPrompt.length;
15305
+ return Math.max(Math.floor(total / 4), 100);
15306
+ }
15307
+ /**
15308
+ * Map an HTTP error response to the appropriate LlmError subclass.
15309
+ *
15310
+ * @param status - HTTP status code.
15311
+ * @param body - Parsed response body (if available).
15312
+ * @param message - Raw error message.
15313
+ */
15314
+ mapError(status, body, message) {
15315
+ if (status === 401 || status === 403) {
15316
+ return new LlmAuthenticationError(
15317
+ `Anthropic auth failed (${status}): ${message}`
15318
+ );
15319
+ }
15320
+ if (status === 429) {
15321
+ this.rateLimiter.recordRateLimit();
15322
+ return new LlmRateLimitError(
15323
+ `Anthropic rate limit exceeded: ${message}`
15324
+ );
15325
+ }
15326
+ if (status === 408 || status === 504) {
15327
+ return new LlmTimeoutError(
15328
+ `Anthropic request timed out (${status}): ${message}`
15329
+ );
15330
+ }
15331
+ const errorType = body?.["error"];
15332
+ const errType = errorType?.["type"];
15333
+ if (errType === "invalid_request_error") {
15334
+ const msg = message.toLowerCase();
15335
+ if (msg.includes("context") || msg.includes("token")) {
15336
+ return new LlmContextLengthError(
15337
+ `Anthropic context length exceeded: ${message}`
15338
+ );
15339
+ }
15340
+ }
15341
+ const lowerMsg = message.toLowerCase();
15342
+ if (lowerMsg.includes("credit balance") || lowerMsg.includes("billing")) {
15343
+ return new LlmAuthenticationError(
15344
+ `Anthropic billing error: ${message}`
15345
+ );
15346
+ }
15347
+ return new LlmError(`Anthropic API error (${status}): ${message}`);
15348
+ }
15349
+ // -- Public API ----------------------------------------------------------
15350
+ /**
15351
+ * Send a query to Anthropic and get a complete response.
15352
+ *
15353
+ * System prompts are sent via the top-level `system` parameter rather
15354
+ * than as a system message in the messages array, per the Anthropic API
15355
+ * specification.
15356
+ *
15357
+ * @param prompt - The user prompt/query.
15358
+ * @param opts - Optional query configuration.
15359
+ * @returns LlmResponse with generated content and metadata.
15360
+ */
15361
+ async query(prompt, opts) {
15362
+ const useModel = opts?.model ?? this.defaultModel;
15363
+ const maxTokens = opts?.maxTokens ?? 4096;
15364
+ const temperature = opts?.temperature ?? 1;
15365
+ const systemPrompt = opts?.systemPrompt;
15366
+ const estimated = this.estimateTokens(prompt, systemPrompt);
15367
+ await this.rateLimiter.acquire(estimated);
15368
+ const requestBody = {
15369
+ model: useModel,
15370
+ messages: [{ role: "user", content: prompt }],
15371
+ max_tokens: maxTokens,
15372
+ temperature
15373
+ };
15374
+ if (systemPrompt) {
15375
+ requestBody["system"] = systemPrompt;
15376
+ }
15377
+ let response;
15378
+ try {
15379
+ const controller = new AbortController();
15380
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
15381
+ response = await fetch(BASE_URL2, {
15382
+ method: "POST",
15383
+ headers: {
15384
+ "Content-Type": "application/json",
15385
+ "x-api-key": this.apiKey,
15386
+ "anthropic-version": ANTHROPIC_VERSION
15387
+ },
15388
+ body: JSON.stringify(requestBody),
15389
+ signal: controller.signal
15390
+ });
15391
+ clearTimeout(timer);
15392
+ } catch (err) {
15393
+ if (err instanceof Error && err.name === "AbortError") {
15394
+ throw new LlmTimeoutError(
15395
+ `Anthropic request timed out after ${this.timeoutMs}ms`
15396
+ );
15397
+ }
15398
+ throw new LlmError(`Anthropic network error: ${String(err)}`);
15399
+ }
15400
+ if (!response.ok) {
15401
+ let errorBody = null;
15402
+ let errorMessage = response.statusText;
15403
+ try {
15404
+ errorBody = await response.json();
15405
+ const errorObj = errorBody?.["error"];
15406
+ errorMessage = errorObj?.["message"] ?? errorMessage;
15407
+ } catch {
15408
+ }
15409
+ throw this.mapError(response.status, errorBody, errorMessage);
15410
+ }
15411
+ const data = await response.json();
15412
+ this.rateLimiter.recordSuccess();
15413
+ const contentBlocks = data["content"];
15414
+ const textParts = [];
15415
+ if (contentBlocks) {
15416
+ for (const block of contentBlocks) {
15417
+ if (block["type"] === "text" && typeof block["text"] === "string") {
15418
+ textParts.push(block["text"]);
15419
+ }
15420
+ }
15421
+ }
15422
+ const content = textParts.join("").trim();
15423
+ const usageObj = data["usage"];
15424
+ const inputTokens = usageObj?.["input_tokens"] ?? 0;
15425
+ const outputTokens = usageObj?.["output_tokens"] ?? 0;
15426
+ return {
15427
+ content,
15428
+ model: data["model"] ?? useModel,
15429
+ usage: {
15430
+ input_tokens: inputTokens,
15431
+ output_tokens: outputTokens
15432
+ }
15433
+ };
15434
+ }
15435
+ /**
15436
+ * Stream a query response chunk by chunk.
15437
+ *
15438
+ * Uses Server-Sent Events (SSE) streaming from the Anthropic API.
15439
+ * System prompts are sent via the top-level `system` parameter.
15440
+ *
15441
+ * @param prompt - The user prompt/query.
15442
+ * @param opts - Optional query configuration.
15443
+ * @yields Text chunks as they arrive from the API.
15444
+ */
15445
+ async *streamQuery(prompt, opts) {
15446
+ const useModel = opts?.model ?? this.defaultModel;
15447
+ const maxTokens = opts?.maxTokens ?? 4096;
15448
+ const temperature = opts?.temperature ?? 1;
15449
+ const systemPrompt = opts?.systemPrompt;
15450
+ const estimated = this.estimateTokens(prompt, systemPrompt);
15451
+ await this.rateLimiter.acquire(estimated);
15452
+ const requestBody = {
15453
+ model: useModel,
15454
+ messages: [{ role: "user", content: prompt }],
15455
+ max_tokens: maxTokens,
15456
+ temperature,
15457
+ stream: true
15458
+ };
15459
+ if (systemPrompt) {
15460
+ requestBody["system"] = systemPrompt;
15461
+ }
15462
+ let response;
15463
+ try {
15464
+ const controller = new AbortController();
15465
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
15466
+ response = await fetch(BASE_URL2, {
15467
+ method: "POST",
15468
+ headers: {
15469
+ "Content-Type": "application/json",
15470
+ "x-api-key": this.apiKey,
15471
+ "anthropic-version": ANTHROPIC_VERSION
15472
+ },
15473
+ body: JSON.stringify(requestBody),
15474
+ signal: controller.signal
15475
+ });
15476
+ clearTimeout(timer);
15477
+ } catch (err) {
15478
+ if (err instanceof Error && err.name === "AbortError") {
15479
+ throw new LlmTimeoutError(
15480
+ `Anthropic request timed out after ${this.timeoutMs}ms`
15481
+ );
15482
+ }
15483
+ throw new LlmError(`Anthropic network error: ${String(err)}`);
15484
+ }
15485
+ if (!response.ok) {
15486
+ let errorBody = null;
15487
+ let errorMessage = response.statusText;
15488
+ try {
15489
+ errorBody = await response.json();
15490
+ const errorObj = errorBody?.["error"];
15491
+ errorMessage = errorObj?.["message"] ?? errorMessage;
15492
+ } catch {
15493
+ }
15494
+ throw this.mapError(response.status, errorBody, errorMessage);
15495
+ }
15496
+ if (!response.body) {
15497
+ throw new LlmError("Anthropic streaming response has no body");
15498
+ }
15499
+ const reader = response.body.getReader();
15500
+ const decoder = new TextDecoder();
15501
+ let buffer = "";
15502
+ let streamCompleted = false;
15503
+ try {
15504
+ for (; ; ) {
15505
+ const { done, value } = await reader.read();
15506
+ if (done)
15507
+ break;
15508
+ buffer += decoder.decode(value, { stream: true });
15509
+ const lines = buffer.split("\n");
15510
+ buffer = lines.pop() ?? "";
15511
+ for (const line of lines) {
15512
+ const trimmed = line.trim();
15513
+ if (!trimmed || !trimmed.startsWith("data: "))
15514
+ continue;
15515
+ const payload = trimmed.slice(6);
15516
+ try {
15517
+ const event = JSON.parse(payload);
15518
+ const eventType = event["type"];
15519
+ if (eventType === "content_block_delta") {
15520
+ const delta = event["delta"];
15521
+ if (delta?.["type"] === "text_delta") {
15522
+ const text = delta["text"];
15523
+ if (text)
15524
+ yield text;
15525
+ }
15526
+ } else if (eventType === "message_stop") {
15527
+ streamCompleted = true;
15528
+ return;
15529
+ }
15530
+ } catch {
15531
+ }
15532
+ }
15533
+ }
15534
+ streamCompleted = true;
15535
+ } finally {
15536
+ reader.releaseLock();
15537
+ if (streamCompleted)
15538
+ this.rateLimiter.recordSuccess();
15539
+ }
15540
+ }
15541
+ };
15542
+
15543
+ // src/llm/factory.ts
15544
+ function createLlmClient(opts) {
15545
+ const clientType = (opts?.clientType ?? process.env["LLM_CLIENT_TYPE"] ?? "mock").toLowerCase();
15546
+ const model = opts?.model ?? process.env["LLM_DEFAULT_MODEL"];
15547
+ if (clientType === "mock") {
15548
+ return new MockLlmClient({
15549
+ defaultModel: model ?? "mock-model"
15550
+ });
15551
+ }
15552
+ if (clientType === "groq") {
15553
+ return new GroqClient({
15554
+ apiKey: opts?.apiKey,
15555
+ defaultModel: model,
15556
+ timeoutMs: opts?.timeoutMs
15557
+ });
15558
+ }
15559
+ if (clientType === "anthropic") {
15560
+ return new AnthropicClient({
15561
+ apiKey: opts?.apiKey,
15562
+ defaultModel: model,
15563
+ timeoutMs: opts?.timeoutMs
15564
+ });
15565
+ }
15566
+ throw new Error(
15567
+ `Unknown LLM client type: "${clientType}". Valid types: "groq", "anthropic", "mock"`
15568
+ );
15569
+ }
15570
+
14156
15571
  // src/index.ts
14157
- var version = "0.5.0";
15572
+ var version = "0.6.0";
14158
15573
  export {
14159
15574
  ACKLifecycleManager,
15575
+ AGENT_STATE_FAILED,
15576
+ AGENT_STATE_INITIALIZING,
15577
+ AGENT_STATE_RUNNING,
15578
+ AGENT_STATE_SHUTTING_DOWN,
14160
15579
  AckStage2 as AckStage,
14161
15580
  ActivityBuffer,
14162
15581
  ActivityBufferSync,
14163
15582
  ActivityClient,
14164
15583
  AgentState,
14165
15584
  AgentStateMachine,
15585
+ AnthropicClient,
14166
15586
  ArtifactType,
14167
15587
  BaseClient,
14168
15588
  BordaCountAggregator,
14169
15589
  BufferFullError,
14170
15590
  CT_AGENT_REPORT_V1,
14171
15591
  CT_SCHEDULER_COMMAND_V1,
15592
+ CancellationManager,
15593
+ CancellationValidationError,
14172
15594
  CommunicationClass,
14173
15595
  ConfidenceWeightedAggregator,
14174
15596
  ConnectorClient,
14175
15597
  DEDUP_WINDOW_S,
14176
15598
  DEFAULT_ACK_TIMEOUT_MS,
15599
+ DEFAULT_BACKOFF_MULTIPLIER,
15600
+ DEFAULT_EFFECTIVE_MAX_REDIRECTS,
14177
15601
  DEFAULT_ESCALATION_POLICY,
14178
15602
  DEFAULT_EXECUTION_POLICY,
15603
+ DEFAULT_INITIAL_BACKOFF_MS,
15604
+ DEFAULT_MAX_BACKOFF_MS,
15605
+ DEFAULT_MAX_REDIRECTS,
15606
+ DEFAULT_MAX_RETRIES_ON_OVERLOADED,
14179
15607
  DEFAULT_NEGOTIATION_POLICY,
15608
+ DEFAULT_PEER_LIVENESS_THRESHOLD_MS,
14180
15609
  DebateIntensity,
14181
15610
  DecisionOutcome,
14182
15611
  DefaultWorktreePolicy,
@@ -14185,6 +15614,9 @@ export {
14185
15614
  ErrorCode,
14186
15615
  ErrorCodeMapper,
14187
15616
  FileBackend,
15617
+ GatewayRedirectEmitter,
15618
+ GatewayValidationError,
15619
+ GroqClient,
14188
15620
  HITLClient,
14189
15621
  HandoffClient,
14190
15622
  HandoffStatus,
@@ -14199,11 +15631,19 @@ export {
14199
15631
  JSONFilePersistence,
14200
15632
  JsonFilePolicyStore,
14201
15633
  KeyringBackend,
15634
+ LlmAuthenticationError,
15635
+ LlmContextLengthError,
15636
+ LlmError,
15637
+ LlmRateLimitError,
15638
+ LlmTimeoutError,
14202
15639
  LoggingClient,
15640
+ MIN_GRACE_PERIOD_MS,
14203
15641
  MajorityVoteAggregator,
14204
15642
  MaxEntriesStrategy,
14205
15643
  MessageProcessor,
14206
15644
  MessageType,
15645
+ MockLlmClient,
15646
+ NON_SERVING_AGENT_STATES,
14207
15647
  NegotiationClient,
14208
15648
  NegotiationError,
14209
15649
  NegotiationRoomClient,
@@ -14212,12 +15652,16 @@ export {
14212
15652
  NegotiationValidationError,
14213
15653
  NoOpAuditor,
14214
15654
  NodeStatus,
15655
+ PeerSelector,
14215
15656
  PermissionError,
14216
15657
  PersistentActivityBuffer,
14217
15658
  PersistentWorktreeState,
14218
15659
  PolicyStoreError,
14219
15660
  PolicyViolationError,
14220
15661
  PreemptionError,
15662
+ REGISTRATION_TYPE_STANDARD_AGENT,
15663
+ REGISTRATION_TYPE_SWARM_GATEWAY,
15664
+ RETRY_AFTER_JITTER_RATIO,
14221
15665
  ReasoningClient,
14222
15666
  RegistryClient,
14223
15667
  Resolver,
@@ -14240,6 +15684,7 @@ export {
14240
15684
  StateTransitionError,
14241
15685
  Sw4rmError,
14242
15686
  TimeoutError,
15687
+ TokenBucket,
14243
15688
  ToolClient,
14244
15689
  TriggerType,
14245
15690
  ValidationError,
@@ -14255,8 +15700,10 @@ export {
14255
15700
  aggregateVotes,
14256
15701
  buildAckEnvelope,
14257
15702
  buildEnvelope,
15703
+ buildRateLimiterConfig,
14258
15704
  computeEnvelopeHash,
14259
15705
  computeIdempotencyToken,
15706
+ createLlmClient,
14260
15707
  createResilientIncomingStream,
14261
15708
  createSimpleProof,
14262
15709
  decodeAgentReportV1,
@@ -14265,10 +15712,12 @@ export {
14265
15712
  defaultEndpoints,
14266
15713
  defaultRetryPolicy,
14267
15714
  defaultSW4RMConfig,
15715
+ delegateToSwarm,
14268
15716
  durationToMs,
14269
15717
  encodeSchedulerCommandV1,
14270
15718
  getConfig,
14271
15719
  getDefaultStore,
15720
+ getGlobalRateLimiter,
14272
15721
  getValidTransitions,
14273
15722
  isTerminalEnvelopeState,
14274
15723
  isValidTransition,
@@ -14283,6 +15732,7 @@ export {
14283
15732
  parseNegotiationEvent,
14284
15733
  resetConfig,
14285
15734
  resetDefaultStore,
15735
+ resetGlobalRateLimiter,
14286
15736
  selectBackend,
14287
15737
  sendMessageWithAck,
14288
15738
  setConfig,