@sw4rm/js-sdk 0.4.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/README.md +178 -1
- package/dist/cjs/index.cjs +2819 -292
- package/dist/esm/index.js +2736 -284
- package/dist/types/agentConfig.d.ts +245 -0
- package/dist/types/audit.d.ts +214 -0
- package/dist/types/clients/handoff.d.ts +44 -1
- package/dist/types/clients/negotiationRoom.d.ts +81 -5
- package/dist/types/clients/negotiationRoomStore.d.ts +155 -0
- package/dist/types/clients/workflow.d.ts +15 -0
- package/dist/types/constants/index.d.ts +100 -0
- package/dist/types/index.d.ts +14 -5
- package/dist/types/internal/baseClient.d.ts +6 -0
- package/dist/types/internal/envelope.d.ts +16 -0
- package/dist/types/internal/errorMapping.d.ts +116 -0
- package/dist/types/internal/worktreeState.d.ts +60 -0
- package/dist/types/llm/anthropic.d.ts +83 -0
- package/dist/types/llm/client.d.ts +107 -0
- package/dist/types/llm/factory.d.ts +69 -0
- package/dist/types/llm/groq.d.ts +79 -0
- package/dist/types/llm/index.d.ts +45 -0
- package/dist/types/llm/mock.d.ts +89 -0
- package/dist/types/llm/rateLimiter.d.ts +101 -0
- package/dist/types/persistentActivityBuffer.d.ts +94 -0
- package/dist/types/runtime/cancellation.d.ts +41 -0
- package/dist/types/runtime/delegation.d.ts +20 -0
- package/dist/types/runtime/gateway.d.ts +80 -0
- package/package.json +4 -2
- package/protos/activity.proto +24 -0
- package/protos/common.proto +141 -0
- package/protos/connector.proto +29 -0
- package/protos/handoff.proto +105 -0
- package/protos/hitl.proto +23 -0
- package/protos/logging.proto +20 -0
- package/protos/negotiation.proto +57 -0
- package/protos/negotiation_room.proto +220 -0
- package/protos/policy.proto +55 -0
- package/protos/reasoning.proto +41 -0
- package/protos/registry.proto +47 -0
- package/protos/router.proto +16 -0
- package/protos/scheduler.proto +52 -0
- package/protos/scheduler_policy.proto +36 -0
- package/protos/tool.proto +47 -0
- package/protos/workflow.proto +116 -0
- package/protos/worktree.proto +33 -0
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 =
|
|
1860
|
+
module2.exports = fetch2;
|
|
1861
1861
|
var asPromise = require_aspromise();
|
|
1862
1862
|
var inquire2 = require_inquire();
|
|
1863
|
-
var
|
|
1864
|
-
function
|
|
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(
|
|
1872
|
-
if (!options.xhr &&
|
|
1873
|
-
return
|
|
1874
|
-
return err && typeof XMLHttpRequest !== "undefined" ?
|
|
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
|
|
1876
|
+
return fetch2.xhr(filename, options, callback);
|
|
1877
1877
|
}
|
|
1878
|
-
|
|
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
|
|
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
|
-
|
|
1919
|
-
return /^(?:\/|\w+:)/.test(
|
|
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
|
-
|
|
1929
|
-
|
|
1930
|
-
var parts =
|
|
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
|
-
|
|
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(
|
|
2101
|
-
if (util.isString(
|
|
2102
|
-
|
|
2103
|
-
else if (!Array.isArray(
|
|
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 (
|
|
2105
|
+
if (path7 && path7.length && path7[0] === "")
|
|
2106
2106
|
throw Error("path must be relative");
|
|
2107
2107
|
var ptr = this;
|
|
2108
|
-
while (
|
|
2109
|
-
var part =
|
|
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(
|
|
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(
|
|
2153
|
-
if (
|
|
2152
|
+
if (util.isString(path7) && path7.length) {
|
|
2153
|
+
if (path7 === ".")
|
|
2154
2154
|
return this.root;
|
|
2155
|
-
|
|
2156
|
-
} else if (!
|
|
2155
|
+
path7 = path7.split(".");
|
|
2156
|
+
} else if (!path7.length)
|
|
2157
2157
|
return this;
|
|
2158
|
-
var flatPath =
|
|
2159
|
-
if (
|
|
2160
|
-
return this.root.lookup(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
2185
|
+
var found = this.get(path7[0]);
|
|
2186
2186
|
var exact = null;
|
|
2187
2187
|
if (found) {
|
|
2188
|
-
if (
|
|
2188
|
+
if (path7.length === 1) {
|
|
2189
2189
|
exact = found;
|
|
2190
2190
|
} else if (found instanceof Namespace) {
|
|
2191
|
-
|
|
2192
|
-
exact = found._lookupImpl(
|
|
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(
|
|
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(
|
|
2203
|
-
var found = this.lookup(
|
|
2202
|
+
Namespace.prototype.lookupType = function lookupType(path7) {
|
|
2203
|
+
var found = this.lookup(path7, [Type]);
|
|
2204
2204
|
if (!found)
|
|
2205
|
-
throw Error("no such type: " +
|
|
2205
|
+
throw Error("no such type: " + path7);
|
|
2206
2206
|
return found;
|
|
2207
2207
|
};
|
|
2208
|
-
Namespace.prototype.lookupEnum = function lookupEnum(
|
|
2209
|
-
var found = this.lookup(
|
|
2208
|
+
Namespace.prototype.lookupEnum = function lookupEnum(path7) {
|
|
2209
|
+
var found = this.lookup(path7, [Enum]);
|
|
2210
2210
|
if (!found)
|
|
2211
|
-
throw Error("no such Enum '" +
|
|
2211
|
+
throw Error("no such Enum '" + path7 + "' in " + this);
|
|
2212
2212
|
return found;
|
|
2213
2213
|
};
|
|
2214
|
-
Namespace.prototype.lookupTypeOrEnum = function lookupTypeOrEnum(
|
|
2215
|
-
var found = this.lookup(
|
|
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 '" +
|
|
2217
|
+
throw Error("no such Type or Enum '" + path7 + "' in " + this);
|
|
2218
2218
|
return found;
|
|
2219
2219
|
};
|
|
2220
|
-
Namespace.prototype.lookupService = function lookupService(
|
|
2221
|
-
var found = this.lookup(
|
|
2220
|
+
Namespace.prototype.lookupService = function lookupService(path7) {
|
|
2221
|
+
var found = this.lookup(path7, [Service]);
|
|
2222
2222
|
if (!found)
|
|
2223
|
-
throw Error("no such Service '" +
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|
|
3625
|
-
function setProp(dst2,
|
|
3626
|
-
var part =
|
|
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 (
|
|
3631
|
-
dst2[part] = setProp(dst2[part] || {},
|
|
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 (!
|
|
3644
|
+
if (!path7)
|
|
3645
3645
|
throw TypeError("path must be specified");
|
|
3646
|
-
|
|
3647
|
-
return setProp(dst,
|
|
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
|
|
4194
|
+
var path7 = [this.name], ptr = this.parent;
|
|
4195
4195
|
while (ptr) {
|
|
4196
|
-
|
|
4196
|
+
path7.unshift(ptr.name);
|
|
4197
4197
|
ptr = ptr.parent;
|
|
4198
4198
|
}
|
|
4199
|
-
return
|
|
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
|
|
8182
|
-
var
|
|
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 (
|
|
8187
|
+
if (path7.isAbsolute(target)) {
|
|
8188
8188
|
return target;
|
|
8189
8189
|
}
|
|
8190
8190
|
for (const directory of includePaths) {
|
|
8191
|
-
const fullPath =
|
|
8191
|
+
const fullPath = path7.join(directory, target);
|
|
8192
8192
|
try {
|
|
8193
|
-
|
|
8193
|
+
fs5.accessSync(fullPath, fs5.constants.R_OK);
|
|
8194
8194
|
return fullPath;
|
|
8195
8195
|
} catch (err) {
|
|
8196
8196
|
continue;
|
|
@@ -9482,6 +9482,7 @@ var require_src2 = __commonJS({
|
|
|
9482
9482
|
|
|
9483
9483
|
// src/internal/baseClient.ts
|
|
9484
9484
|
var protoLoader = __toESM(require_src2(), 1);
|
|
9485
|
+
import fs from "node:fs";
|
|
9485
9486
|
import path from "node:path";
|
|
9486
9487
|
import { fileURLToPath } from "node:url";
|
|
9487
9488
|
import * as grpc from "@grpc/grpc-js";
|
|
@@ -9502,6 +9503,10 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
9502
9503
|
ErrorCode2[ErrorCode2["PARTIAL_DELIVERY"] = 11] = "PARTIAL_DELIVERY";
|
|
9503
9504
|
ErrorCode2[ErrorCode2["FORCED_PREEMPTION"] = 12] = "FORCED_PREEMPTION";
|
|
9504
9505
|
ErrorCode2[ErrorCode2["TTL_EXPIRED"] = 13] = "TTL_EXPIRED";
|
|
9506
|
+
ErrorCode2[ErrorCode2["DUPLICATE_DETECTED"] = 14] = "DUPLICATE_DETECTED";
|
|
9507
|
+
ErrorCode2[ErrorCode2["ALREADY_IN_PROGRESS"] = 15] = "ALREADY_IN_PROGRESS";
|
|
9508
|
+
ErrorCode2[ErrorCode2["OVERLOADED"] = 16] = "OVERLOADED";
|
|
9509
|
+
ErrorCode2[ErrorCode2["REDIRECT"] = 20] = "REDIRECT";
|
|
9505
9510
|
ErrorCode2[ErrorCode2["INTERNAL_ERROR"] = 99] = "INTERNAL_ERROR";
|
|
9506
9511
|
return ErrorCode2;
|
|
9507
9512
|
})(ErrorCode || {});
|
|
@@ -9517,6 +9522,54 @@ var Sw4rmError = class extends Error {
|
|
|
9517
9522
|
this.details = opts?.details;
|
|
9518
9523
|
}
|
|
9519
9524
|
};
|
|
9525
|
+
var ValidationError = class extends Sw4rmError {
|
|
9526
|
+
constructor(message, code = 6 /* VALIDATION_ERROR */, opts) {
|
|
9527
|
+
super(message, code, opts);
|
|
9528
|
+
this.name = "ValidationError";
|
|
9529
|
+
}
|
|
9530
|
+
};
|
|
9531
|
+
var TimeoutError = class extends Sw4rmError {
|
|
9532
|
+
constructor(message, code = 3 /* ACK_TIMEOUT */, opts) {
|
|
9533
|
+
super(message, code, opts);
|
|
9534
|
+
this.name = "TimeoutError";
|
|
9535
|
+
}
|
|
9536
|
+
};
|
|
9537
|
+
var StateTransitionError = class extends Sw4rmError {
|
|
9538
|
+
constructor(message, code = 99 /* INTERNAL_ERROR */, opts) {
|
|
9539
|
+
super(message, code, opts);
|
|
9540
|
+
this.name = "StateTransitionError";
|
|
9541
|
+
}
|
|
9542
|
+
};
|
|
9543
|
+
var NegotiationError = class extends Sw4rmError {
|
|
9544
|
+
constructor(message, code = 99 /* INTERNAL_ERROR */, opts) {
|
|
9545
|
+
super(message, code, opts);
|
|
9546
|
+
this.name = "NegotiationError";
|
|
9547
|
+
}
|
|
9548
|
+
};
|
|
9549
|
+
var BufferFullError = class extends Sw4rmError {
|
|
9550
|
+
constructor(message, code = 1 /* BUFFER_FULL */, opts) {
|
|
9551
|
+
super(message, code, opts);
|
|
9552
|
+
this.name = "BufferFullError";
|
|
9553
|
+
}
|
|
9554
|
+
};
|
|
9555
|
+
var RouteError = class extends Sw4rmError {
|
|
9556
|
+
constructor(message, code = 2 /* NO_ROUTE */, opts) {
|
|
9557
|
+
super(message, code, opts);
|
|
9558
|
+
this.name = "RouteError";
|
|
9559
|
+
}
|
|
9560
|
+
};
|
|
9561
|
+
var PermissionError = class extends Sw4rmError {
|
|
9562
|
+
constructor(message, code = 7 /* PERMISSION_DENIED */, opts) {
|
|
9563
|
+
super(message, code, opts);
|
|
9564
|
+
this.name = "PermissionError";
|
|
9565
|
+
}
|
|
9566
|
+
};
|
|
9567
|
+
var DuplicateDetectedError = class extends Sw4rmError {
|
|
9568
|
+
constructor(message, code = 14 /* DUPLICATE_DETECTED */, opts) {
|
|
9569
|
+
super(message, code, opts);
|
|
9570
|
+
this.name = "DuplicateDetectedError";
|
|
9571
|
+
}
|
|
9572
|
+
};
|
|
9520
9573
|
var GrpcStatus = {
|
|
9521
9574
|
OK: 0,
|
|
9522
9575
|
CANCELLED: 1,
|
|
@@ -9536,6 +9589,28 @@ var GrpcStatus = {
|
|
|
9536
9589
|
DATA_LOSS: 15,
|
|
9537
9590
|
UNAUTHENTICATED: 16
|
|
9538
9591
|
};
|
|
9592
|
+
var PreemptionError = class extends Sw4rmError {
|
|
9593
|
+
agentId;
|
|
9594
|
+
reason;
|
|
9595
|
+
constructor(agentId, reason, code = 12 /* FORCED_PREEMPTION */, opts) {
|
|
9596
|
+
super(`Agent ${agentId} preempted: ${reason}`, code, opts);
|
|
9597
|
+
this.name = "PreemptionError";
|
|
9598
|
+
this.agentId = agentId;
|
|
9599
|
+
this.reason = reason;
|
|
9600
|
+
}
|
|
9601
|
+
};
|
|
9602
|
+
var WorktreeError = class extends Sw4rmError {
|
|
9603
|
+
constructor(message, code = 99 /* INTERNAL_ERROR */, opts) {
|
|
9604
|
+
super(message, code, opts);
|
|
9605
|
+
this.name = "WorktreeError";
|
|
9606
|
+
}
|
|
9607
|
+
};
|
|
9608
|
+
var PolicyViolationError = class extends Sw4rmError {
|
|
9609
|
+
constructor(message, code = 7 /* PERMISSION_DENIED */, opts) {
|
|
9610
|
+
super(message, code, opts);
|
|
9611
|
+
this.name = "PolicyViolationError";
|
|
9612
|
+
}
|
|
9613
|
+
};
|
|
9539
9614
|
function mapGrpcStatusToErrorCode(status) {
|
|
9540
9615
|
switch (status) {
|
|
9541
9616
|
case GrpcStatus.OK:
|
|
@@ -9649,12 +9724,12 @@ var BaseClient = class {
|
|
|
9649
9724
|
errorMapper = new ErrorCodeMapper();
|
|
9650
9725
|
constructor(opts) {
|
|
9651
9726
|
this.address = opts.address;
|
|
9652
|
-
this.deadlineMs = opts.deadlineMs ??
|
|
9727
|
+
this.deadlineMs = opts.deadlineMs ?? 3e4;
|
|
9653
9728
|
this.retry = opts.retry ?? { maxAttempts: 3, initialBackoffMs: 200, maxBackoffMs: 2e3, multiplier: 2 };
|
|
9654
9729
|
this.userAgent = opts.userAgent ?? "sw4rm-js-sdk/0.1";
|
|
9655
9730
|
const __filename = fileURLToPath(import.meta.url);
|
|
9656
9731
|
const __dirname = path.dirname(__filename);
|
|
9657
|
-
const protosDir =
|
|
9732
|
+
const protosDir = this.resolveProtosDir(__dirname);
|
|
9658
9733
|
const loaderOpts = {
|
|
9659
9734
|
keepCase: true,
|
|
9660
9735
|
longs: String,
|
|
@@ -9674,10 +9749,13 @@ var BaseClient = class {
|
|
|
9674
9749
|
"tool.proto",
|
|
9675
9750
|
"connector.proto",
|
|
9676
9751
|
"negotiation.proto",
|
|
9752
|
+
"negotiation_room.proto",
|
|
9677
9753
|
"reasoning.proto",
|
|
9678
9754
|
"logging.proto",
|
|
9679
9755
|
"policy.proto",
|
|
9680
|
-
"activity.proto"
|
|
9756
|
+
"activity.proto",
|
|
9757
|
+
"handoff.proto",
|
|
9758
|
+
"workflow.proto"
|
|
9681
9759
|
], loaderOpts);
|
|
9682
9760
|
this.root = grpc.loadPackageDefinition(this.pkgDef);
|
|
9683
9761
|
this.interceptors.use(timingInterceptor());
|
|
@@ -9687,6 +9765,27 @@ var BaseClient = class {
|
|
|
9687
9765
|
if (opts.errorMapper)
|
|
9688
9766
|
opts.errorMapper(this.errorMapper);
|
|
9689
9767
|
}
|
|
9768
|
+
/**
|
|
9769
|
+
* Resolves the protos directory, checking multiple locations:
|
|
9770
|
+
* 1. npm package: ./protos relative to package root
|
|
9771
|
+
* 2. Monorepo dev: ../../../../protos relative to this file
|
|
9772
|
+
*/
|
|
9773
|
+
resolveProtosDir(dirname3) {
|
|
9774
|
+
const candidates = [
|
|
9775
|
+
// npm package structure: node_modules/@sw4rm/js-sdk/protos
|
|
9776
|
+
path.resolve(dirname3, "../../protos"),
|
|
9777
|
+
// Alternative npm structure: dist/esm/internal -> protos
|
|
9778
|
+
path.resolve(dirname3, "../../../protos"),
|
|
9779
|
+
// Monorepo development: sdks/js_sdk/src/internal -> protos
|
|
9780
|
+
path.resolve(dirname3, "../../../../protos")
|
|
9781
|
+
];
|
|
9782
|
+
for (const candidate of candidates) {
|
|
9783
|
+
if (fs.existsSync(candidate) && fs.existsSync(path.join(candidate, "common.proto"))) {
|
|
9784
|
+
return candidate;
|
|
9785
|
+
}
|
|
9786
|
+
}
|
|
9787
|
+
return candidates[candidates.length - 1];
|
|
9788
|
+
}
|
|
9690
9789
|
channelCredentials() {
|
|
9691
9790
|
return grpc.credentials.createInsecure();
|
|
9692
9791
|
}
|
|
@@ -10274,6 +10373,95 @@ var RegistryClient = class extends BaseClient {
|
|
|
10274
10373
|
}
|
|
10275
10374
|
};
|
|
10276
10375
|
|
|
10376
|
+
// src/clients/negotiationRoomStore.ts
|
|
10377
|
+
var NegotiationRoomStoreError = class extends Error {
|
|
10378
|
+
constructor(message) {
|
|
10379
|
+
super(message);
|
|
10380
|
+
this.name = "NegotiationRoomStoreError";
|
|
10381
|
+
}
|
|
10382
|
+
};
|
|
10383
|
+
var InMemoryNegotiationRoomStore = class {
|
|
10384
|
+
proposals = /* @__PURE__ */ new Map();
|
|
10385
|
+
votes = /* @__PURE__ */ new Map();
|
|
10386
|
+
decisions = /* @__PURE__ */ new Map();
|
|
10387
|
+
async hasProposal(artifactId) {
|
|
10388
|
+
return this.proposals.has(artifactId);
|
|
10389
|
+
}
|
|
10390
|
+
async getProposal(artifactId) {
|
|
10391
|
+
return this.proposals.get(artifactId) || null;
|
|
10392
|
+
}
|
|
10393
|
+
async saveProposal(proposal) {
|
|
10394
|
+
const artifactId = proposal.artifactId;
|
|
10395
|
+
if (this.proposals.has(artifactId)) {
|
|
10396
|
+
throw new NegotiationRoomStoreError(
|
|
10397
|
+
`Proposal with artifact_id '${artifactId}' already exists`
|
|
10398
|
+
);
|
|
10399
|
+
}
|
|
10400
|
+
const proposalWithTimestamp = {
|
|
10401
|
+
...proposal,
|
|
10402
|
+
createdAt: proposal.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
10403
|
+
};
|
|
10404
|
+
this.proposals.set(artifactId, proposalWithTimestamp);
|
|
10405
|
+
this.votes.set(artifactId, []);
|
|
10406
|
+
}
|
|
10407
|
+
async listProposals(negotiationRoomId) {
|
|
10408
|
+
const allProposals = Array.from(this.proposals.values());
|
|
10409
|
+
if (negotiationRoomId === void 0) {
|
|
10410
|
+
return allProposals;
|
|
10411
|
+
}
|
|
10412
|
+
return allProposals.filter((p) => p.negotiationRoomId === negotiationRoomId);
|
|
10413
|
+
}
|
|
10414
|
+
async getVotes(artifactId) {
|
|
10415
|
+
return [...this.votes.get(artifactId) || []];
|
|
10416
|
+
}
|
|
10417
|
+
async addVote(vote) {
|
|
10418
|
+
const artifactId = vote.artifactId;
|
|
10419
|
+
const criticId = vote.criticId;
|
|
10420
|
+
const existingVotes = this.votes.get(artifactId) || [];
|
|
10421
|
+
for (const existingVote of existingVotes) {
|
|
10422
|
+
if (existingVote.criticId === criticId) {
|
|
10423
|
+
throw new NegotiationRoomStoreError(
|
|
10424
|
+
`Critic '${criticId}' has already voted for artifact '${artifactId}'`
|
|
10425
|
+
);
|
|
10426
|
+
}
|
|
10427
|
+
}
|
|
10428
|
+
const voteWithTimestamp = {
|
|
10429
|
+
...vote,
|
|
10430
|
+
votedAt: vote.votedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
10431
|
+
};
|
|
10432
|
+
if (!this.votes.has(artifactId)) {
|
|
10433
|
+
this.votes.set(artifactId, []);
|
|
10434
|
+
}
|
|
10435
|
+
this.votes.get(artifactId).push(voteWithTimestamp);
|
|
10436
|
+
}
|
|
10437
|
+
async getDecision(artifactId) {
|
|
10438
|
+
return this.decisions.get(artifactId) || null;
|
|
10439
|
+
}
|
|
10440
|
+
async saveDecision(decision) {
|
|
10441
|
+
const artifactId = decision.artifactId;
|
|
10442
|
+
if (this.decisions.has(artifactId)) {
|
|
10443
|
+
throw new NegotiationRoomStoreError(
|
|
10444
|
+
`Decision already exists for artifact_id '${artifactId}'`
|
|
10445
|
+
);
|
|
10446
|
+
}
|
|
10447
|
+
const decisionWithTimestamp = {
|
|
10448
|
+
...decision,
|
|
10449
|
+
decidedAt: decision.decidedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
10450
|
+
};
|
|
10451
|
+
this.decisions.set(artifactId, decisionWithTimestamp);
|
|
10452
|
+
}
|
|
10453
|
+
};
|
|
10454
|
+
var defaultStore = null;
|
|
10455
|
+
function getDefaultStore() {
|
|
10456
|
+
if (defaultStore === null) {
|
|
10457
|
+
defaultStore = new InMemoryNegotiationRoomStore();
|
|
10458
|
+
}
|
|
10459
|
+
return defaultStore;
|
|
10460
|
+
}
|
|
10461
|
+
function resetDefaultStore() {
|
|
10462
|
+
defaultStore = null;
|
|
10463
|
+
}
|
|
10464
|
+
|
|
10277
10465
|
// src/clients/negotiationRoom.ts
|
|
10278
10466
|
var ArtifactType = /* @__PURE__ */ ((ArtifactType2) => {
|
|
10279
10467
|
ArtifactType2[ArtifactType2["ARTIFACT_TYPE_UNSPECIFIED"] = 0] = "ARTIFACT_TYPE_UNSPECIFIED";
|
|
@@ -10315,9 +10503,16 @@ function validateVote(vote) {
|
|
|
10315
10503
|
}
|
|
10316
10504
|
}
|
|
10317
10505
|
var NegotiationRoomClient = class {
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10506
|
+
store;
|
|
10507
|
+
/**
|
|
10508
|
+
* Create a new negotiation room client.
|
|
10509
|
+
*
|
|
10510
|
+
* @param options - Configuration options. If no store is provided,
|
|
10511
|
+
* uses the shared default in-memory store.
|
|
10512
|
+
*/
|
|
10513
|
+
constructor(options = {}) {
|
|
10514
|
+
this.store = options.store || getDefaultStore();
|
|
10515
|
+
}
|
|
10321
10516
|
/**
|
|
10322
10517
|
* Submit an artifact proposal for multi-agent review.
|
|
10323
10518
|
*
|
|
@@ -10345,19 +10540,8 @@ var NegotiationRoomClient = class {
|
|
|
10345
10540
|
* ```
|
|
10346
10541
|
*/
|
|
10347
10542
|
async submitProposal(proposal) {
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
throw new NegotiationValidationError(
|
|
10351
|
-
`Proposal with artifact_id '${artifactId}' already exists`
|
|
10352
|
-
);
|
|
10353
|
-
}
|
|
10354
|
-
const proposalWithTimestamp = {
|
|
10355
|
-
...proposal,
|
|
10356
|
-
createdAt: proposal.createdAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
10357
|
-
};
|
|
10358
|
-
this.proposals.set(artifactId, proposalWithTimestamp);
|
|
10359
|
-
this.votes.set(artifactId, []);
|
|
10360
|
-
return artifactId;
|
|
10543
|
+
await this.store.saveProposal(proposal);
|
|
10544
|
+
return proposal.artifactId;
|
|
10361
10545
|
}
|
|
10362
10546
|
/**
|
|
10363
10547
|
* Submit a critic's vote for an artifact.
|
|
@@ -10389,26 +10573,13 @@ var NegotiationRoomClient = class {
|
|
|
10389
10573
|
*/
|
|
10390
10574
|
async submitVote(vote) {
|
|
10391
10575
|
const artifactId = vote.artifactId;
|
|
10392
|
-
if (!this.
|
|
10576
|
+
if (!await this.store.hasProposal(artifactId)) {
|
|
10393
10577
|
throw new NegotiationValidationError(
|
|
10394
10578
|
`No proposal found for artifact_id '${artifactId}'`
|
|
10395
10579
|
);
|
|
10396
10580
|
}
|
|
10397
10581
|
validateVote(vote);
|
|
10398
|
-
|
|
10399
|
-
for (const existingVote of existingVotes) {
|
|
10400
|
-
if (existingVote.criticId === vote.criticId) {
|
|
10401
|
-
throw new NegotiationValidationError(
|
|
10402
|
-
`Critic '${vote.criticId}' has already voted for artifact '${artifactId}'`
|
|
10403
|
-
);
|
|
10404
|
-
}
|
|
10405
|
-
}
|
|
10406
|
-
const voteWithTimestamp = {
|
|
10407
|
-
...vote,
|
|
10408
|
-
votedAt: vote.votedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
10409
|
-
};
|
|
10410
|
-
existingVotes.push(voteWithTimestamp);
|
|
10411
|
-
this.votes.set(artifactId, existingVotes);
|
|
10582
|
+
await this.store.addVote(vote);
|
|
10412
10583
|
}
|
|
10413
10584
|
/**
|
|
10414
10585
|
* Retrieve all votes for a specific artifact.
|
|
@@ -10428,12 +10599,12 @@ var NegotiationRoomClient = class {
|
|
|
10428
10599
|
* ```
|
|
10429
10600
|
*/
|
|
10430
10601
|
async getVotes(artifactId) {
|
|
10431
|
-
if (!this.
|
|
10602
|
+
if (!await this.store.hasProposal(artifactId)) {
|
|
10432
10603
|
throw new NegotiationValidationError(
|
|
10433
10604
|
`No proposal found for artifact_id '${artifactId}'`
|
|
10434
10605
|
);
|
|
10435
10606
|
}
|
|
10436
|
-
return this.
|
|
10607
|
+
return this.store.getVotes(artifactId);
|
|
10437
10608
|
}
|
|
10438
10609
|
/**
|
|
10439
10610
|
* Retrieve the decision for a specific artifact if available.
|
|
@@ -10454,12 +10625,12 @@ var NegotiationRoomClient = class {
|
|
|
10454
10625
|
* ```
|
|
10455
10626
|
*/
|
|
10456
10627
|
async getDecision(artifactId) {
|
|
10457
|
-
if (!this.
|
|
10628
|
+
if (!await this.store.hasProposal(artifactId)) {
|
|
10458
10629
|
throw new NegotiationValidationError(
|
|
10459
10630
|
`No proposal found for artifact_id '${artifactId}'`
|
|
10460
10631
|
);
|
|
10461
10632
|
}
|
|
10462
|
-
return this.
|
|
10633
|
+
return this.store.getDecision(artifactId);
|
|
10463
10634
|
}
|
|
10464
10635
|
/**
|
|
10465
10636
|
* Store a decision for an artifact.
|
|
@@ -10490,21 +10661,12 @@ var NegotiationRoomClient = class {
|
|
|
10490
10661
|
*/
|
|
10491
10662
|
async storeDecision(decision) {
|
|
10492
10663
|
const artifactId = decision.artifactId;
|
|
10493
|
-
if (!this.
|
|
10664
|
+
if (!await this.store.hasProposal(artifactId)) {
|
|
10494
10665
|
throw new NegotiationValidationError(
|
|
10495
10666
|
`No proposal found for artifact_id '${artifactId}'`
|
|
10496
10667
|
);
|
|
10497
10668
|
}
|
|
10498
|
-
|
|
10499
|
-
throw new NegotiationValidationError(
|
|
10500
|
-
`Decision already exists for artifact_id '${artifactId}'`
|
|
10501
|
-
);
|
|
10502
|
-
}
|
|
10503
|
-
const decisionWithTimestamp = {
|
|
10504
|
-
...decision,
|
|
10505
|
-
decidedAt: decision.decidedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
10506
|
-
};
|
|
10507
|
-
this.decisions.set(artifactId, decisionWithTimestamp);
|
|
10669
|
+
await this.store.saveDecision(decision);
|
|
10508
10670
|
}
|
|
10509
10671
|
/**
|
|
10510
10672
|
* Wait for a decision to be made on an artifact.
|
|
@@ -10528,14 +10690,14 @@ var NegotiationRoomClient = class {
|
|
|
10528
10690
|
* ```
|
|
10529
10691
|
*/
|
|
10530
10692
|
async waitForDecision(artifactId, timeoutMs = 3e4, pollIntervalMs = 100) {
|
|
10531
|
-
if (!this.
|
|
10693
|
+
if (!await this.store.hasProposal(artifactId)) {
|
|
10532
10694
|
throw new NegotiationValidationError(
|
|
10533
10695
|
`No proposal found for artifact_id '${artifactId}'`
|
|
10534
10696
|
);
|
|
10535
10697
|
}
|
|
10536
10698
|
const startTime = Date.now();
|
|
10537
10699
|
while (Date.now() - startTime < timeoutMs) {
|
|
10538
|
-
const decision = await this.getDecision(artifactId);
|
|
10700
|
+
const decision = await this.store.getDecision(artifactId);
|
|
10539
10701
|
if (decision !== null) {
|
|
10540
10702
|
return decision;
|
|
10541
10703
|
}
|
|
@@ -10561,7 +10723,7 @@ var NegotiationRoomClient = class {
|
|
|
10561
10723
|
* ```
|
|
10562
10724
|
*/
|
|
10563
10725
|
async getProposal(artifactId) {
|
|
10564
|
-
return this.
|
|
10726
|
+
return this.store.getProposal(artifactId);
|
|
10565
10727
|
}
|
|
10566
10728
|
/**
|
|
10567
10729
|
* List all proposals, optionally filtered by negotiation room.
|
|
@@ -10576,13 +10738,7 @@ var NegotiationRoomClient = class {
|
|
|
10576
10738
|
* ```
|
|
10577
10739
|
*/
|
|
10578
10740
|
async listProposals(negotiationRoomId) {
|
|
10579
|
-
|
|
10580
|
-
if (negotiationRoomId === void 0) {
|
|
10581
|
-
return allProposals;
|
|
10582
|
-
}
|
|
10583
|
-
return allProposals.filter(
|
|
10584
|
-
(p) => p.negotiationRoomId === negotiationRoomId
|
|
10585
|
-
);
|
|
10741
|
+
return this.store.listProposals(negotiationRoomId);
|
|
10586
10742
|
}
|
|
10587
10743
|
};
|
|
10588
10744
|
function aggregateVotes(votes) {
|
|
@@ -10625,6 +10781,38 @@ var HandoffStatus = /* @__PURE__ */ ((HandoffStatus2) => {
|
|
|
10625
10781
|
HandoffStatus2[HandoffStatus2["EXPIRED"] = 5] = "EXPIRED";
|
|
10626
10782
|
return HandoffStatus2;
|
|
10627
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
|
+
}
|
|
10628
10816
|
var HandoffValidationError = class extends Error {
|
|
10629
10817
|
constructor(message) {
|
|
10630
10818
|
super(message);
|
|
@@ -10673,9 +10861,15 @@ var HandoffClient = class {
|
|
|
10673
10861
|
`Handoff request with ID '${request.requestId}' already exists`
|
|
10674
10862
|
);
|
|
10675
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
|
+
}
|
|
10676
10869
|
const requestWithTimestamp = {
|
|
10677
10870
|
...request,
|
|
10678
|
-
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
|
|
10679
10873
|
};
|
|
10680
10874
|
this.requests.set(request.requestId, requestWithTimestamp);
|
|
10681
10875
|
if (request.toAgent) {
|
|
@@ -10697,7 +10891,8 @@ var HandoffClient = class {
|
|
|
10697
10891
|
const timeoutResponse = {
|
|
10698
10892
|
requestId: request.requestId,
|
|
10699
10893
|
accepted: false,
|
|
10700
|
-
rejectionReason: `Handoff request timed out after ${timeoutMs}ms
|
|
10894
|
+
rejectionReason: `Handoff request timed out after ${timeoutMs}ms`,
|
|
10895
|
+
rejectionCode: 3 /* ACK_TIMEOUT */
|
|
10701
10896
|
};
|
|
10702
10897
|
this.responses.set(request.requestId, timeoutResponse);
|
|
10703
10898
|
if (request.toAgent && this.pendingByAgent.has(request.toAgent)) {
|
|
@@ -10762,7 +10957,7 @@ var HandoffClient = class {
|
|
|
10762
10957
|
* await client.rejectHandoff("handoff-123", "Agent at capacity");
|
|
10763
10958
|
* ```
|
|
10764
10959
|
*/
|
|
10765
|
-
async rejectHandoff(handoffId, reason) {
|
|
10960
|
+
async rejectHandoff(handoffId, reason, options) {
|
|
10766
10961
|
const request = this.requests.get(handoffId);
|
|
10767
10962
|
if (!request) {
|
|
10768
10963
|
throw new HandoffValidationError(
|
|
@@ -10777,7 +10972,10 @@ var HandoffClient = class {
|
|
|
10777
10972
|
const response = {
|
|
10778
10973
|
requestId: handoffId,
|
|
10779
10974
|
accepted: false,
|
|
10780
|
-
rejectionReason: reason
|
|
10975
|
+
rejectionReason: reason,
|
|
10976
|
+
rejectionCode: options?.rejectionCode ?? 0 /* ERROR_CODE_UNSPECIFIED */,
|
|
10977
|
+
retryAfterMs: options?.retryAfterMs,
|
|
10978
|
+
redirectToAgentId: options?.redirectToAgentId
|
|
10781
10979
|
};
|
|
10782
10980
|
this.responses.set(handoffId, response);
|
|
10783
10981
|
if (request.toAgent && this.pendingByAgent.has(request.toAgent)) {
|
|
@@ -11210,6 +11408,31 @@ var WorkflowClient = class {
|
|
|
11210
11408
|
instance.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11211
11409
|
}
|
|
11212
11410
|
}
|
|
11411
|
+
/**
|
|
11412
|
+
* Resume a workflow by marking a node as completed and advancing dependents.
|
|
11413
|
+
*
|
|
11414
|
+
* Implements the ResumeWorkflow RPC (spec §17.7 MUST). Marks the specified
|
|
11415
|
+
* node as COMPLETED, updates workflow data if provided, then transitions
|
|
11416
|
+
* dependent nodes from PENDING to READY when all their dependencies are met.
|
|
11417
|
+
*
|
|
11418
|
+
* @param instanceId - The ID of the workflow instance
|
|
11419
|
+
* @param nodeId - The ID of the node to mark as completed
|
|
11420
|
+
* @param workflowData - Optional updated workflow data (JSON string)
|
|
11421
|
+
* @param metadata - Optional metadata for the resume operation
|
|
11422
|
+
* @returns The updated workflow instance
|
|
11423
|
+
* @throws WorkflowValidationError if the instance or node does not exist
|
|
11424
|
+
*/
|
|
11425
|
+
async resumeWorkflow(instanceId, nodeId, workflowData, metadata) {
|
|
11426
|
+
await this.updateNodeState(instanceId, nodeId, 4 /* COMPLETED */);
|
|
11427
|
+
if (workflowData !== void 0) {
|
|
11428
|
+
await this.updateWorkflowData(instanceId, workflowData);
|
|
11429
|
+
}
|
|
11430
|
+
if (metadata) {
|
|
11431
|
+
const instance = this.instances.get(instanceId);
|
|
11432
|
+
Object.assign(instance.metadata, metadata);
|
|
11433
|
+
}
|
|
11434
|
+
return this.getWorkflowStatus(instanceId);
|
|
11435
|
+
}
|
|
11213
11436
|
/**
|
|
11214
11437
|
* Update the shared workflow data.
|
|
11215
11438
|
*
|
|
@@ -11231,6 +11454,21 @@ var WorkflowClient = class {
|
|
|
11231
11454
|
|
|
11232
11455
|
// src/internal/envelope.ts
|
|
11233
11456
|
import crypto2 from "node:crypto";
|
|
11457
|
+
var MessageType = /* @__PURE__ */ ((MessageType2) => {
|
|
11458
|
+
MessageType2[MessageType2["MESSAGE_TYPE_UNSPECIFIED"] = 0] = "MESSAGE_TYPE_UNSPECIFIED";
|
|
11459
|
+
MessageType2[MessageType2["CONTROL"] = 1] = "CONTROL";
|
|
11460
|
+
MessageType2[MessageType2["DATA"] = 2] = "DATA";
|
|
11461
|
+
MessageType2[MessageType2["HEARTBEAT"] = 3] = "HEARTBEAT";
|
|
11462
|
+
MessageType2[MessageType2["NOTIFICATION"] = 4] = "NOTIFICATION";
|
|
11463
|
+
MessageType2[MessageType2["ACKNOWLEDGEMENT"] = 5] = "ACKNOWLEDGEMENT";
|
|
11464
|
+
MessageType2[MessageType2["HITL_INVOCATION"] = 6] = "HITL_INVOCATION";
|
|
11465
|
+
MessageType2[MessageType2["WORKTREE_CONTROL"] = 7] = "WORKTREE_CONTROL";
|
|
11466
|
+
MessageType2[MessageType2["NEGOTIATION"] = 8] = "NEGOTIATION";
|
|
11467
|
+
MessageType2[MessageType2["TOOL_CALL"] = 9] = "TOOL_CALL";
|
|
11468
|
+
MessageType2[MessageType2["TOOL_RESULT"] = 10] = "TOOL_RESULT";
|
|
11469
|
+
MessageType2[MessageType2["TOOL_ERROR"] = 11] = "TOOL_ERROR";
|
|
11470
|
+
return MessageType2;
|
|
11471
|
+
})(MessageType || {});
|
|
11234
11472
|
function nowTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
11235
11473
|
const ms = date.getTime();
|
|
11236
11474
|
const seconds = Math.floor(ms / 1e3);
|
|
@@ -11238,7 +11476,15 @@ function nowTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
|
11238
11476
|
return { seconds, nanos };
|
|
11239
11477
|
}
|
|
11240
11478
|
function nowHlcStub(date = /* @__PURE__ */ new Date()) {
|
|
11241
|
-
|
|
11479
|
+
const wallUs = date.getTime() * 1e3;
|
|
11480
|
+
let node = "unknown";
|
|
11481
|
+
try {
|
|
11482
|
+
const os3 = __require("node:os");
|
|
11483
|
+
node = os3.hostname() || "unknown";
|
|
11484
|
+
} catch {
|
|
11485
|
+
node = "browser";
|
|
11486
|
+
}
|
|
11487
|
+
return `HLC:${wallUs}:0:${node}`;
|
|
11242
11488
|
}
|
|
11243
11489
|
function uuidv42() {
|
|
11244
11490
|
if (typeof crypto2.randomUUID === "function") {
|
|
@@ -11274,7 +11520,12 @@ function buildEnvelope(input) {
|
|
|
11274
11520
|
hlc_timestamp,
|
|
11275
11521
|
// Always set hlc_timestamp (aligns with Python/Rust)
|
|
11276
11522
|
ttl_ms: input.ttl_ms,
|
|
11277
|
-
timestamp: ts
|
|
11523
|
+
timestamp: ts,
|
|
11524
|
+
state: input.state ?? 1,
|
|
11525
|
+
// EnvelopeState.CREATED
|
|
11526
|
+
effective_policy_id: input.effective_policy_id ?? "",
|
|
11527
|
+
audit_proof: input.audit_proof ?? new Uint8Array(0),
|
|
11528
|
+
audit_policy_id: input.audit_policy_id ?? ""
|
|
11278
11529
|
};
|
|
11279
11530
|
if (hasPayload) {
|
|
11280
11531
|
const payload = input.payload instanceof Uint8Array ? input.payload : new Uint8Array(input.payload);
|
|
@@ -11288,25 +11539,141 @@ function buildEnvelope(input) {
|
|
|
11288
11539
|
}
|
|
11289
11540
|
|
|
11290
11541
|
// src/internal/worktreeState.ts
|
|
11542
|
+
var VALID_WORKTREE_TRANSITIONS = {
|
|
11543
|
+
UNBOUND: ["BOUND_HOME", "BIND_FAILED"],
|
|
11544
|
+
BOUND_HOME: ["SWITCH_PENDING", "UNBOUND"],
|
|
11545
|
+
SWITCH_PENDING: ["BOUND_NON_HOME", "BOUND_HOME"],
|
|
11546
|
+
// approve or reject
|
|
11547
|
+
BOUND_NON_HOME: ["BOUND_HOME", "UNBOUND"],
|
|
11548
|
+
// TTL expired/revoke, or unbind
|
|
11549
|
+
BIND_FAILED: ["UNBOUND", "BOUND_HOME"]
|
|
11550
|
+
// retry reset or successful retry
|
|
11551
|
+
};
|
|
11552
|
+
function isValidWorktreeTransition(from, to) {
|
|
11553
|
+
return VALID_WORKTREE_TRANSITIONS[from]?.includes(to) ?? false;
|
|
11554
|
+
}
|
|
11555
|
+
var WorktreeTransitionError = class extends Error {
|
|
11556
|
+
fromState;
|
|
11557
|
+
toState;
|
|
11558
|
+
constructor(from, to) {
|
|
11559
|
+
super(`Invalid worktree state transition from ${from} to ${to}`);
|
|
11560
|
+
this.name = "WorktreeTransitionError";
|
|
11561
|
+
this.fromState = from;
|
|
11562
|
+
this.toState = to;
|
|
11563
|
+
}
|
|
11564
|
+
};
|
|
11291
11565
|
var PersistentWorktreeState = class {
|
|
11292
11566
|
state = "UNBOUND";
|
|
11293
11567
|
repoId;
|
|
11294
11568
|
worktreeId;
|
|
11569
|
+
homeRepoId;
|
|
11570
|
+
homeWorktreeId;
|
|
11571
|
+
switchTtlMs;
|
|
11572
|
+
switchTimer;
|
|
11573
|
+
/**
|
|
11574
|
+
* Transition to a new state with validation.
|
|
11575
|
+
* @throws WorktreeTransitionError if transition is invalid
|
|
11576
|
+
*/
|
|
11577
|
+
transitionTo(newState) {
|
|
11578
|
+
if (!isValidWorktreeTransition(this.state, newState)) {
|
|
11579
|
+
throw new WorktreeTransitionError(this.state, newState);
|
|
11580
|
+
}
|
|
11581
|
+
this.state = newState;
|
|
11582
|
+
}
|
|
11583
|
+
/**
|
|
11584
|
+
* Bind to a home worktree (UNBOUND -> BOUND_HOME or BIND_FAILED -> BOUND_HOME).
|
|
11585
|
+
*/
|
|
11295
11586
|
bind(repoId, worktreeId) {
|
|
11587
|
+
this.transitionTo("BOUND_HOME");
|
|
11296
11588
|
this.repoId = repoId;
|
|
11297
11589
|
this.worktreeId = worktreeId;
|
|
11298
|
-
this.
|
|
11590
|
+
this.homeRepoId = repoId;
|
|
11591
|
+
this.homeWorktreeId = worktreeId;
|
|
11592
|
+
}
|
|
11593
|
+
/**
|
|
11594
|
+
* Handle bind failure (UNBOUND -> BIND_FAILED).
|
|
11595
|
+
*/
|
|
11596
|
+
bindFailed() {
|
|
11597
|
+
this.transitionTo("BIND_FAILED");
|
|
11299
11598
|
}
|
|
11599
|
+
/**
|
|
11600
|
+
* Unbind from current worktree.
|
|
11601
|
+
*/
|
|
11300
11602
|
unbind() {
|
|
11603
|
+
this.clearSwitchTimer();
|
|
11604
|
+
this.transitionTo("UNBOUND");
|
|
11301
11605
|
this.repoId = void 0;
|
|
11302
11606
|
this.worktreeId = void 0;
|
|
11303
|
-
|
|
11607
|
+
}
|
|
11608
|
+
/**
|
|
11609
|
+
* Request switch to a non-home worktree (BOUND_HOME -> SWITCH_PENDING).
|
|
11610
|
+
* Requires approval or rejection before taking effect.
|
|
11611
|
+
*/
|
|
11612
|
+
requestSwitch(targetRepoId, targetWorktreeId) {
|
|
11613
|
+
this.transitionTo("SWITCH_PENDING");
|
|
11614
|
+
this._pendingRepoId = targetRepoId;
|
|
11615
|
+
this._pendingWorktreeId = targetWorktreeId;
|
|
11616
|
+
}
|
|
11617
|
+
/**
|
|
11618
|
+
* Approve a pending switch (SWITCH_PENDING -> BOUND_NON_HOME).
|
|
11619
|
+
* @param ttlMs - Time-to-live in milliseconds before auto-reverting to BOUND_HOME
|
|
11620
|
+
*/
|
|
11621
|
+
approveSwitch(ttlMs = 3e5) {
|
|
11622
|
+
this.transitionTo("BOUND_NON_HOME");
|
|
11623
|
+
this.repoId = this._pendingRepoId;
|
|
11624
|
+
this.worktreeId = this._pendingWorktreeId;
|
|
11625
|
+
this.switchTtlMs = ttlMs;
|
|
11626
|
+
this.clearSwitchTimer();
|
|
11627
|
+
this.switchTimer = setTimeout(() => {
|
|
11628
|
+
this.revertToHome();
|
|
11629
|
+
}, ttlMs);
|
|
11630
|
+
}
|
|
11631
|
+
/**
|
|
11632
|
+
* Reject a pending switch (SWITCH_PENDING -> BOUND_HOME).
|
|
11633
|
+
*/
|
|
11634
|
+
rejectSwitch() {
|
|
11635
|
+
this.transitionTo("BOUND_HOME");
|
|
11636
|
+
this.repoId = this.homeRepoId;
|
|
11637
|
+
this.worktreeId = this.homeWorktreeId;
|
|
11638
|
+
delete this._pendingRepoId;
|
|
11639
|
+
delete this._pendingWorktreeId;
|
|
11640
|
+
}
|
|
11641
|
+
/**
|
|
11642
|
+
* Revert from non-home to home worktree (BOUND_NON_HOME -> BOUND_HOME).
|
|
11643
|
+
* Called on TTL expiry or explicit revoke.
|
|
11644
|
+
*/
|
|
11645
|
+
revertToHome() {
|
|
11646
|
+
this.clearSwitchTimer();
|
|
11647
|
+
if (this.state === "BOUND_NON_HOME") {
|
|
11648
|
+
this.transitionTo("BOUND_HOME");
|
|
11649
|
+
this.repoId = this.homeRepoId;
|
|
11650
|
+
this.worktreeId = this.homeWorktreeId;
|
|
11651
|
+
}
|
|
11652
|
+
}
|
|
11653
|
+
/**
|
|
11654
|
+
* Reset from BIND_FAILED to UNBOUND for retry.
|
|
11655
|
+
*/
|
|
11656
|
+
resetFromFailed() {
|
|
11657
|
+
this.transitionTo("UNBOUND");
|
|
11658
|
+
}
|
|
11659
|
+
clearSwitchTimer() {
|
|
11660
|
+
if (this.switchTimer) {
|
|
11661
|
+
clearTimeout(this.switchTimer);
|
|
11662
|
+
this.switchTimer = void 0;
|
|
11663
|
+
}
|
|
11304
11664
|
}
|
|
11305
11665
|
setState(state) {
|
|
11306
11666
|
this.state = state;
|
|
11307
11667
|
}
|
|
11308
11668
|
current() {
|
|
11309
|
-
return {
|
|
11669
|
+
return {
|
|
11670
|
+
state: this.state,
|
|
11671
|
+
repo_id: this.repoId,
|
|
11672
|
+
worktree_id: this.worktreeId,
|
|
11673
|
+
home_repo_id: this.homeRepoId,
|
|
11674
|
+
home_worktree_id: this.homeWorktreeId,
|
|
11675
|
+
switch_ttl_ms: this.switchTtlMs
|
|
11676
|
+
};
|
|
11310
11677
|
}
|
|
11311
11678
|
};
|
|
11312
11679
|
|
|
@@ -11368,16 +11735,6 @@ var ActivityBuffer = class {
|
|
|
11368
11735
|
};
|
|
11369
11736
|
|
|
11370
11737
|
// src/internal/runtime/ackLifecycle.ts
|
|
11371
|
-
var AckStage = /* @__PURE__ */ ((AckStage2) => {
|
|
11372
|
-
AckStage2[AckStage2["ACK_STAGE_UNSPECIFIED"] = 0] = "ACK_STAGE_UNSPECIFIED";
|
|
11373
|
-
AckStage2[AckStage2["RECEIVED"] = 1] = "RECEIVED";
|
|
11374
|
-
AckStage2[AckStage2["READ"] = 2] = "READ";
|
|
11375
|
-
AckStage2[AckStage2["FULFILLED"] = 3] = "FULFILLED";
|
|
11376
|
-
AckStage2[AckStage2["REJECTED"] = 4] = "REJECTED";
|
|
11377
|
-
AckStage2[AckStage2["FAILED"] = 5] = "FAILED";
|
|
11378
|
-
AckStage2[AckStage2["TIMED_OUT"] = 6] = "TIMED_OUT";
|
|
11379
|
-
return AckStage2;
|
|
11380
|
-
})(AckStage || {});
|
|
11381
11738
|
var ACKLifecycleManager = class {
|
|
11382
11739
|
acks = /* @__PURE__ */ new Map();
|
|
11383
11740
|
mark(messageId, stage, note, errorCode) {
|
|
@@ -11414,21 +11771,6 @@ var ACKLifecycleManager = class {
|
|
|
11414
11771
|
};
|
|
11415
11772
|
|
|
11416
11773
|
// src/internal/runtime/messageProcessor.ts
|
|
11417
|
-
var MessageType = /* @__PURE__ */ ((MessageType3) => {
|
|
11418
|
-
MessageType3[MessageType3["MESSAGE_TYPE_UNSPECIFIED"] = 0] = "MESSAGE_TYPE_UNSPECIFIED";
|
|
11419
|
-
MessageType3[MessageType3["CONTROL"] = 1] = "CONTROL";
|
|
11420
|
-
MessageType3[MessageType3["DATA"] = 2] = "DATA";
|
|
11421
|
-
MessageType3[MessageType3["HEARTBEAT"] = 3] = "HEARTBEAT";
|
|
11422
|
-
MessageType3[MessageType3["NOTIFICATION"] = 4] = "NOTIFICATION";
|
|
11423
|
-
MessageType3[MessageType3["ACKNOWLEDGEMENT"] = 5] = "ACKNOWLEDGEMENT";
|
|
11424
|
-
MessageType3[MessageType3["HITL_INVOCATION"] = 6] = "HITL_INVOCATION";
|
|
11425
|
-
MessageType3[MessageType3["WORKTREE_CONTROL"] = 7] = "WORKTREE_CONTROL";
|
|
11426
|
-
MessageType3[MessageType3["NEGOTIATION"] = 8] = "NEGOTIATION";
|
|
11427
|
-
MessageType3[MessageType3["TOOL_CALL"] = 9] = "TOOL_CALL";
|
|
11428
|
-
MessageType3[MessageType3["TOOL_RESULT"] = 10] = "TOOL_RESULT";
|
|
11429
|
-
MessageType3[MessageType3["TOOL_ERROR"] = 11] = "TOOL_ERROR";
|
|
11430
|
-
return MessageType3;
|
|
11431
|
-
})(MessageType || {});
|
|
11432
11774
|
var MessageProcessor = class {
|
|
11433
11775
|
handlers = /* @__PURE__ */ new Map();
|
|
11434
11776
|
defaultHandler;
|
|
@@ -11595,35 +11937,571 @@ function decodeBase64(b64) {
|
|
|
11595
11937
|
return new Uint8Array(Buffer.from(b64, "base64"));
|
|
11596
11938
|
}
|
|
11597
11939
|
|
|
11598
|
-
// src/
|
|
11599
|
-
import
|
|
11600
|
-
|
|
11601
|
-
var
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
|
|
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;
|
|
11606
11953
|
}
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
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
|
+
);
|
|
11614
12005
|
}
|
|
11615
|
-
|
|
11616
|
-
|
|
11617
|
-
|
|
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
|
+
);
|
|
11618
12015
|
}
|
|
11619
|
-
|
|
11620
|
-
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
|
|
11624
|
-
|
|
11625
|
-
|
|
11626
|
-
|
|
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
|
+
|
|
12476
|
+
// src/persistence/persistence.ts
|
|
12477
|
+
import fsp from "node:fs/promises";
|
|
12478
|
+
import path2 from "node:path";
|
|
12479
|
+
var JSONFilePersistence = class {
|
|
12480
|
+
constructor(baseDir) {
|
|
12481
|
+
this.baseDir = baseDir;
|
|
12482
|
+
this.activityFile = path2.join(baseDir, "activity.json");
|
|
12483
|
+
this.acksFile = path2.join(baseDir, "acks.json");
|
|
12484
|
+
}
|
|
12485
|
+
activityFile;
|
|
12486
|
+
acksFile;
|
|
12487
|
+
async safeWrite(file, data) {
|
|
12488
|
+
await fsp.mkdir(path2.dirname(file), { recursive: true });
|
|
12489
|
+
const tmp = `${file}.tmp-${Date.now()}`;
|
|
12490
|
+
await fsp.writeFile(tmp, data, "utf8");
|
|
12491
|
+
await fsp.rename(tmp, file);
|
|
12492
|
+
}
|
|
12493
|
+
async saveActivity(records) {
|
|
12494
|
+
const payload = JSON.stringify(records, null, 2);
|
|
12495
|
+
await this.safeWrite(this.activityFile, payload);
|
|
12496
|
+
}
|
|
12497
|
+
async loadActivity() {
|
|
12498
|
+
try {
|
|
12499
|
+
const buf = await fsp.readFile(this.activityFile, "utf8");
|
|
12500
|
+
return JSON.parse(buf);
|
|
12501
|
+
} catch (e) {
|
|
12502
|
+
if (e?.code === "ENOENT")
|
|
12503
|
+
return [];
|
|
12504
|
+
throw e;
|
|
11627
12505
|
}
|
|
11628
12506
|
}
|
|
11629
12507
|
async saveAcks(states) {
|
|
@@ -12548,7 +13426,7 @@ var AgentState = /* @__PURE__ */ ((AgentState2) => {
|
|
|
12548
13426
|
AgentState2[AgentState2["RECOVERING"] = 12] = "RECOVERING";
|
|
12549
13427
|
return AgentState2;
|
|
12550
13428
|
})(AgentState || {});
|
|
12551
|
-
var
|
|
13429
|
+
var StateTransitionError2 = class extends Error {
|
|
12552
13430
|
fromState;
|
|
12553
13431
|
toState;
|
|
12554
13432
|
constructor(fromState, toState) {
|
|
@@ -12561,8 +13439,8 @@ var StateTransitionError = class extends Error {
|
|
|
12561
13439
|
}
|
|
12562
13440
|
};
|
|
12563
13441
|
var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
12564
|
-
// INITIALIZING -> RUNNABLE
|
|
12565
|
-
[1 /* INITIALIZING */, /* @__PURE__ */ new Set([2 /* RUNNABLE */])],
|
|
13442
|
+
// INITIALIZING -> RUNNABLE, FAILED (init timeout per spec s8.2)
|
|
13443
|
+
[1 /* INITIALIZING */, /* @__PURE__ */ new Set([2 /* RUNNABLE */, 10 /* FAILED */])],
|
|
12566
13444
|
// RUNNABLE -> SCHEDULED
|
|
12567
13445
|
[2 /* RUNNABLE */, /* @__PURE__ */ new Set([3 /* SCHEDULED */])],
|
|
12568
13446
|
// SCHEDULED -> RUNNING
|
|
@@ -12586,8 +13464,8 @@ var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
|
12586
13464
|
6 /* WAITING_RESOURCES */,
|
|
12587
13465
|
/* @__PURE__ */ new Set([4 /* RUNNING */, 10 /* FAILED */])
|
|
12588
13466
|
],
|
|
12589
|
-
// SUSPENDED -> RESUMED
|
|
12590
|
-
[7 /* SUSPENDED */, /* @__PURE__ */ new Set([8 /* RESUMED */])],
|
|
13467
|
+
// SUSPENDED -> RESUMED, FAILED (suspension timeout per spec s8.3)
|
|
13468
|
+
[7 /* SUSPENDED */, /* @__PURE__ */ new Set([8 /* RESUMED */, 10 /* FAILED */])],
|
|
12591
13469
|
// RESUMED -> RUNNING
|
|
12592
13470
|
[8 /* RESUMED */, /* @__PURE__ */ new Set([4 /* RUNNING */])],
|
|
12593
13471
|
// COMPLETED -> RUNNABLE
|
|
@@ -12596,10 +13474,10 @@ var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
|
|
|
12596
13474
|
[10 /* FAILED */, /* @__PURE__ */ new Set([12 /* RECOVERING */])],
|
|
12597
13475
|
// SHUTTING_DOWN -> FAILED (agent_shutdown_timeout)
|
|
12598
13476
|
[11 /* SHUTTING_DOWN */, /* @__PURE__ */ new Set([10 /* FAILED */])],
|
|
12599
|
-
// RECOVERING -> RUNNABLE, SHUTTING_DOWN (recovery abort)
|
|
13477
|
+
// RECOVERING -> RUNNABLE, SHUTTING_DOWN (recovery abort), FAILED (recovery timeout per spec s8.3)
|
|
12600
13478
|
[
|
|
12601
13479
|
12 /* RECOVERING */,
|
|
12602
|
-
/* @__PURE__ */ new Set([2 /* RUNNABLE */, 11 /* SHUTTING_DOWN */])
|
|
13480
|
+
/* @__PURE__ */ new Set([2 /* RUNNABLE */, 11 /* SHUTTING_DOWN */, 10 /* FAILED */])
|
|
12603
13481
|
]
|
|
12604
13482
|
]);
|
|
12605
13483
|
function isValidTransition(fromState, toState) {
|
|
@@ -12651,7 +13529,7 @@ var AgentStateMachine = class {
|
|
|
12651
13529
|
*/
|
|
12652
13530
|
async initialize() {
|
|
12653
13531
|
if (this.state !== 0 /* AGENT_STATE_UNSPECIFIED */) {
|
|
12654
|
-
throw new
|
|
13532
|
+
throw new StateTransitionError2(this.state, 1 /* INITIALIZING */);
|
|
12655
13533
|
}
|
|
12656
13534
|
await this.transitionTo(1 /* INITIALIZING */);
|
|
12657
13535
|
}
|
|
@@ -12668,7 +13546,7 @@ var AgentStateMachine = class {
|
|
|
12668
13546
|
return;
|
|
12669
13547
|
}
|
|
12670
13548
|
if (!isValidTransition(this.state, toState)) {
|
|
12671
|
-
throw new
|
|
13549
|
+
throw new StateTransitionError2(this.state, toState);
|
|
12672
13550
|
}
|
|
12673
13551
|
await this.doTransition(toState, context);
|
|
12674
13552
|
}
|
|
@@ -12832,7 +13710,8 @@ function computeIdempotencyToken(input) {
|
|
|
12832
13710
|
h.update(input.operation);
|
|
12833
13711
|
h.update("\n");
|
|
12834
13712
|
h.update(Buffer.from(input.canonical_bytes));
|
|
12835
|
-
|
|
13713
|
+
const hash16 = h.digest("hex").slice(0, 16);
|
|
13714
|
+
return `${input.producer_id}:${input.operation}:${hash16}`;
|
|
12836
13715
|
}
|
|
12837
13716
|
|
|
12838
13717
|
// src/runtime/persistenceAdapter.ts
|
|
@@ -12949,89 +13828,752 @@ function normalizeReportPaths(report) {
|
|
|
12949
13828
|
return report;
|
|
12950
13829
|
}
|
|
12951
13830
|
|
|
12952
|
-
// src/
|
|
12953
|
-
var
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
return
|
|
12959
|
-
})(
|
|
12960
|
-
var
|
|
12961
|
-
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
13831
|
+
// src/constants/index.ts
|
|
13832
|
+
var CommunicationClass = /* @__PURE__ */ ((CommunicationClass2) => {
|
|
13833
|
+
CommunicationClass2[CommunicationClass2["COMM_CLASS_UNSPECIFIED"] = 0] = "COMM_CLASS_UNSPECIFIED";
|
|
13834
|
+
CommunicationClass2[CommunicationClass2["PRIVILEGED"] = 1] = "PRIVILEGED";
|
|
13835
|
+
CommunicationClass2[CommunicationClass2["STANDARD"] = 2] = "STANDARD";
|
|
13836
|
+
CommunicationClass2[CommunicationClass2["BULK"] = 3] = "BULK";
|
|
13837
|
+
return CommunicationClass2;
|
|
13838
|
+
})(CommunicationClass || {});
|
|
13839
|
+
var DebateIntensity = /* @__PURE__ */ ((DebateIntensity2) => {
|
|
13840
|
+
DebateIntensity2[DebateIntensity2["DEBATE_INTENSITY_UNSPECIFIED"] = 0] = "DEBATE_INTENSITY_UNSPECIFIED";
|
|
13841
|
+
DebateIntensity2[DebateIntensity2["LOWEST"] = 1] = "LOWEST";
|
|
13842
|
+
DebateIntensity2[DebateIntensity2["LOW"] = 2] = "LOW";
|
|
13843
|
+
DebateIntensity2[DebateIntensity2["MEDIUM"] = 3] = "MEDIUM";
|
|
13844
|
+
DebateIntensity2[DebateIntensity2["HIGH"] = 4] = "HIGH";
|
|
13845
|
+
DebateIntensity2[DebateIntensity2["HIGHEST"] = 5] = "HIGHEST";
|
|
13846
|
+
return DebateIntensity2;
|
|
13847
|
+
})(DebateIntensity || {});
|
|
13848
|
+
var HitlReasonType = /* @__PURE__ */ ((HitlReasonType2) => {
|
|
13849
|
+
HitlReasonType2[HitlReasonType2["HITL_REASON_UNSPECIFIED"] = 0] = "HITL_REASON_UNSPECIFIED";
|
|
13850
|
+
HitlReasonType2[HitlReasonType2["CONFLICT"] = 1] = "CONFLICT";
|
|
13851
|
+
HitlReasonType2[HitlReasonType2["SECURITY_APPROVAL"] = 2] = "SECURITY_APPROVAL";
|
|
13852
|
+
HitlReasonType2[HitlReasonType2["TASK_ESCALATION"] = 3] = "TASK_ESCALATION";
|
|
13853
|
+
HitlReasonType2[HitlReasonType2["MANUAL_OVERRIDE"] = 4] = "MANUAL_OVERRIDE";
|
|
13854
|
+
HitlReasonType2[HitlReasonType2["WORKTREE_OVERRIDE"] = 5] = "WORKTREE_OVERRIDE";
|
|
13855
|
+
HitlReasonType2[HitlReasonType2["DEBATE_DEADLOCK"] = 6] = "DEBATE_DEADLOCK";
|
|
13856
|
+
HitlReasonType2[HitlReasonType2["TOOL_PRIVILEGE_ESCALATION"] = 7] = "TOOL_PRIVILEGE_ESCALATION";
|
|
13857
|
+
HitlReasonType2[HitlReasonType2["CONNECTOR_APPROVAL"] = 8] = "CONNECTOR_APPROVAL";
|
|
13858
|
+
return HitlReasonType2;
|
|
13859
|
+
})(HitlReasonType || {});
|
|
13860
|
+
var AckStage2 = /* @__PURE__ */ ((AckStage3) => {
|
|
13861
|
+
AckStage3[AckStage3["ACK_STAGE_UNSPECIFIED"] = 0] = "ACK_STAGE_UNSPECIFIED";
|
|
13862
|
+
AckStage3[AckStage3["RECEIVED"] = 1] = "RECEIVED";
|
|
13863
|
+
AckStage3[AckStage3["READ"] = 2] = "READ";
|
|
13864
|
+
AckStage3[AckStage3["FULFILLED"] = 3] = "FULFILLED";
|
|
13865
|
+
AckStage3[AckStage3["REJECTED"] = 4] = "REJECTED";
|
|
13866
|
+
AckStage3[AckStage3["FAILED"] = 5] = "FAILED";
|
|
13867
|
+
AckStage3[AckStage3["TIMED_OUT"] = 6] = "TIMED_OUT";
|
|
13868
|
+
return AckStage3;
|
|
13869
|
+
})(AckStage2 || {});
|
|
13870
|
+
var EnvelopeState = /* @__PURE__ */ ((EnvelopeState3) => {
|
|
13871
|
+
EnvelopeState3[EnvelopeState3["ENVELOPE_STATE_UNSPECIFIED"] = 0] = "ENVELOPE_STATE_UNSPECIFIED";
|
|
13872
|
+
EnvelopeState3[EnvelopeState3["SENT"] = 1] = "SENT";
|
|
13873
|
+
EnvelopeState3[EnvelopeState3["RECEIVED"] = 2] = "RECEIVED";
|
|
13874
|
+
EnvelopeState3[EnvelopeState3["READ"] = 3] = "READ";
|
|
13875
|
+
EnvelopeState3[EnvelopeState3["FULFILLED"] = 4] = "FULFILLED";
|
|
13876
|
+
EnvelopeState3[EnvelopeState3["REJECTED"] = 5] = "REJECTED";
|
|
13877
|
+
EnvelopeState3[EnvelopeState3["FAILED"] = 6] = "FAILED";
|
|
13878
|
+
EnvelopeState3[EnvelopeState3["TIMED_OUT"] = 7] = "TIMED_OUT";
|
|
13879
|
+
return EnvelopeState3;
|
|
13880
|
+
})(EnvelopeState || {});
|
|
13881
|
+
var WorktreeStateEnum = /* @__PURE__ */ ((WorktreeStateEnum2) => {
|
|
13882
|
+
WorktreeStateEnum2[WorktreeStateEnum2["WORKTREE_STATE_UNSPECIFIED"] = 0] = "WORKTREE_STATE_UNSPECIFIED";
|
|
13883
|
+
WorktreeStateEnum2[WorktreeStateEnum2["UNBOUND"] = 1] = "UNBOUND";
|
|
13884
|
+
WorktreeStateEnum2[WorktreeStateEnum2["BOUND_HOME"] = 2] = "BOUND_HOME";
|
|
13885
|
+
WorktreeStateEnum2[WorktreeStateEnum2["SWITCH_PENDING"] = 3] = "SWITCH_PENDING";
|
|
13886
|
+
WorktreeStateEnum2[WorktreeStateEnum2["BOUND_NON_HOME"] = 4] = "BOUND_NON_HOME";
|
|
13887
|
+
WorktreeStateEnum2[WorktreeStateEnum2["BIND_FAILED"] = 5] = "BIND_FAILED";
|
|
13888
|
+
return WorktreeStateEnum2;
|
|
13889
|
+
})(WorktreeStateEnum || {});
|
|
13890
|
+
var DEFAULT_ACK_TIMEOUT_MS = 1e4;
|
|
13891
|
+
var DEDUP_WINDOW_S = 3600;
|
|
13892
|
+
function isTerminalEnvelopeState(state) {
|
|
13893
|
+
return state === 4 /* FULFILLED */ || state === 5 /* REJECTED */ || state === 6 /* FAILED */ || state === 7 /* TIMED_OUT */;
|
|
13894
|
+
}
|
|
13895
|
+
function updateEnvelopeState(envelope, newState) {
|
|
13896
|
+
envelope.state = newState;
|
|
13897
|
+
return envelope;
|
|
13898
|
+
}
|
|
13899
|
+
|
|
13900
|
+
// src/audit.ts
|
|
13901
|
+
import crypto4 from "node:crypto";
|
|
13902
|
+
function uuidv43() {
|
|
13903
|
+
if (typeof crypto4.randomUUID === "function") {
|
|
13904
|
+
return crypto4.randomUUID();
|
|
12966
13905
|
}
|
|
12967
|
-
|
|
12968
|
-
|
|
13906
|
+
const buf = crypto4.randomBytes(16);
|
|
13907
|
+
buf[6] = buf[6] & 15 | 64;
|
|
13908
|
+
buf[8] = buf[8] & 63 | 128;
|
|
13909
|
+
const hex = [...buf].map((b) => b.toString(16).padStart(2, "0"));
|
|
13910
|
+
return hex.slice(0, 4).join("") + "-" + hex.slice(4, 6).join("") + "-" + hex.slice(6, 8).join("") + "-" + hex.slice(8, 10).join("") + "-" + hex.slice(10, 16).join("");
|
|
13911
|
+
}
|
|
13912
|
+
function computeEnvelopeHash(envelope) {
|
|
13913
|
+
const envelopeCopy = { ...envelope };
|
|
13914
|
+
const payload = envelopeCopy.payload;
|
|
13915
|
+
const audit_proof = envelopeCopy.audit_proof;
|
|
13916
|
+
delete envelopeCopy.payload;
|
|
13917
|
+
delete envelopeCopy.audit_proof;
|
|
13918
|
+
const envelopeJson = JSON.stringify(envelopeCopy, Object.keys(envelopeCopy).sort());
|
|
13919
|
+
const hasher = crypto4.createHash("sha256");
|
|
13920
|
+
hasher.update(envelopeJson, "utf-8");
|
|
13921
|
+
if (payload) {
|
|
13922
|
+
if (payload instanceof Uint8Array) {
|
|
13923
|
+
hasher.update(payload);
|
|
13924
|
+
} else if (typeof payload === "string") {
|
|
13925
|
+
hasher.update(payload, "utf-8");
|
|
13926
|
+
}
|
|
13927
|
+
}
|
|
13928
|
+
if (audit_proof) {
|
|
13929
|
+
if (audit_proof instanceof Uint8Array) {
|
|
13930
|
+
hasher.update(audit_proof);
|
|
13931
|
+
} else if (typeof audit_proof === "string") {
|
|
13932
|
+
hasher.update(audit_proof, "utf-8");
|
|
13933
|
+
}
|
|
13934
|
+
}
|
|
13935
|
+
return hasher.digest("hex");
|
|
13936
|
+
}
|
|
13937
|
+
function createSimpleProof(envelope, actor_id) {
|
|
13938
|
+
const envelopeHash = computeEnvelopeHash(envelope);
|
|
13939
|
+
const timestamp = nowHlcStub();
|
|
13940
|
+
const proofInput = `${envelopeHash}:${actor_id}:${timestamp}`;
|
|
13941
|
+
const proofHash = crypto4.createHash("sha256").update(proofInput, "utf-8").digest();
|
|
13942
|
+
return {
|
|
13943
|
+
proof_id: uuidv43(),
|
|
13944
|
+
proof_type: "simple_hash",
|
|
13945
|
+
proof_data: new Uint8Array(proofHash),
|
|
13946
|
+
created_at: timestamp,
|
|
13947
|
+
verified: false
|
|
13948
|
+
};
|
|
13949
|
+
}
|
|
13950
|
+
function verifyAuditProof(envelope, proof) {
|
|
13951
|
+
if (proof.proof_type === "noop") {
|
|
13952
|
+
return true;
|
|
12969
13953
|
}
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
this.key = key;
|
|
12974
|
-
if (!key || key.split(".").some((p) => p.trim() === "")) {
|
|
12975
|
-
throw new Error("secret key must be dotted identifiers without empty segments");
|
|
13954
|
+
if (proof.proof_type === "simple_hash") {
|
|
13955
|
+
if (proof.proof_data.length === 0) {
|
|
13956
|
+
return false;
|
|
12976
13957
|
}
|
|
12977
|
-
|
|
12978
|
-
|
|
12979
|
-
|
|
12980
|
-
constructor(value) {
|
|
12981
|
-
this.value = value;
|
|
12982
|
-
if (value.length > _SecretValue.MAX_LEN) {
|
|
12983
|
-
throw new Error("secret value exceeds maximum allowed length (8KB)");
|
|
13958
|
+
const envelopeHash = computeEnvelopeHash(envelope);
|
|
13959
|
+
if (proof.proof_data.length !== 32) {
|
|
13960
|
+
return false;
|
|
12984
13961
|
}
|
|
13962
|
+
return true;
|
|
12985
13963
|
}
|
|
12986
|
-
|
|
12987
|
-
}
|
|
12988
|
-
|
|
12989
|
-
|
|
12990
|
-
|
|
12991
|
-
|
|
12992
|
-
|
|
12993
|
-
|
|
12994
|
-
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
|
|
12998
|
-
|
|
12999
|
-
|
|
13000
|
-
|
|
13001
|
-
|
|
13002
|
-
|
|
13003
|
-
|
|
13004
|
-
};
|
|
13005
|
-
|
|
13006
|
-
// src/secrets/backend.ts
|
|
13007
|
-
var Secrets = class {
|
|
13008
|
-
constructor(backend) {
|
|
13009
|
-
this.backend = backend;
|
|
13964
|
+
return false;
|
|
13965
|
+
}
|
|
13966
|
+
var NoOpAuditor = class {
|
|
13967
|
+
/**
|
|
13968
|
+
* Create a minimal no-op proof.
|
|
13969
|
+
*
|
|
13970
|
+
* @param envelope - The envelope (ignored)
|
|
13971
|
+
* @param action - The action (ignored)
|
|
13972
|
+
* @returns A minimal no-op proof
|
|
13973
|
+
*/
|
|
13974
|
+
createProof(envelope, action) {
|
|
13975
|
+
return {
|
|
13976
|
+
proof_id: uuidv43(),
|
|
13977
|
+
proof_type: "noop",
|
|
13978
|
+
proof_data: new Uint8Array(0),
|
|
13979
|
+
created_at: nowHlcStub(),
|
|
13980
|
+
verified: true
|
|
13981
|
+
};
|
|
13010
13982
|
}
|
|
13011
|
-
|
|
13012
|
-
|
|
13983
|
+
/**
|
|
13984
|
+
* Always returns true for no-op proofs.
|
|
13985
|
+
*
|
|
13986
|
+
* @param proof - The proof (ignored)
|
|
13987
|
+
* @returns Always true
|
|
13988
|
+
*/
|
|
13989
|
+
verifyProof(proof) {
|
|
13990
|
+
return true;
|
|
13013
13991
|
}
|
|
13014
|
-
|
|
13015
|
-
|
|
13992
|
+
/**
|
|
13993
|
+
* Create a minimal audit record without storing it.
|
|
13994
|
+
*
|
|
13995
|
+
* @param envelope - The envelope being audited
|
|
13996
|
+
* @param action - The action being performed
|
|
13997
|
+
* @param proof - Optional proof to attach
|
|
13998
|
+
* @returns A minimal audit record
|
|
13999
|
+
*/
|
|
14000
|
+
record(envelope, action, proof) {
|
|
14001
|
+
return {
|
|
14002
|
+
record_id: uuidv43(),
|
|
14003
|
+
envelope_id: envelope.message_id ?? "unknown",
|
|
14004
|
+
action,
|
|
14005
|
+
actor_id: envelope.producer_id ?? "unknown",
|
|
14006
|
+
timestamp: nowHlcStub(),
|
|
14007
|
+
proof
|
|
14008
|
+
};
|
|
13016
14009
|
}
|
|
13017
|
-
|
|
13018
|
-
|
|
14010
|
+
/**
|
|
14011
|
+
* Always returns empty array.
|
|
14012
|
+
*
|
|
14013
|
+
* @param envelope_id - The envelope ID (ignored)
|
|
14014
|
+
* @returns Empty array
|
|
14015
|
+
*/
|
|
14016
|
+
query(envelope_id) {
|
|
14017
|
+
return [];
|
|
13019
14018
|
}
|
|
13020
14019
|
};
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
14020
|
+
var InMemoryAuditor = class {
|
|
14021
|
+
_records;
|
|
14022
|
+
/**
|
|
14023
|
+
* Initialize with empty record storage.
|
|
14024
|
+
*/
|
|
14025
|
+
constructor() {
|
|
14026
|
+
this._records = /* @__PURE__ */ new Map();
|
|
13027
14027
|
}
|
|
13028
|
-
|
|
13029
|
-
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
14028
|
+
/**
|
|
14029
|
+
* Create a simple hash-based proof.
|
|
14030
|
+
*
|
|
14031
|
+
* @param envelope - The envelope to create proof for
|
|
14032
|
+
* @param action - The action being performed
|
|
14033
|
+
* @returns A simple hash-based proof
|
|
14034
|
+
*/
|
|
14035
|
+
createProof(envelope, action) {
|
|
14036
|
+
return createSimpleProof(envelope, envelope.producer_id ?? "unknown");
|
|
14037
|
+
}
|
|
14038
|
+
/**
|
|
14039
|
+
* Verify a proof using the verification module.
|
|
14040
|
+
*
|
|
14041
|
+
* @param proof - The proof to verify
|
|
14042
|
+
* @returns True if proof is valid, false otherwise
|
|
14043
|
+
*/
|
|
14044
|
+
verifyProof(proof) {
|
|
14045
|
+
if (proof.proof_type === "noop") {
|
|
14046
|
+
return true;
|
|
14047
|
+
}
|
|
14048
|
+
if (proof.proof_type === "simple_hash") {
|
|
14049
|
+
return proof.proof_data.length > 0;
|
|
14050
|
+
}
|
|
14051
|
+
return false;
|
|
14052
|
+
}
|
|
14053
|
+
/**
|
|
14054
|
+
* Create and store an audit record.
|
|
14055
|
+
*
|
|
14056
|
+
* @param envelope - The envelope being audited
|
|
14057
|
+
* @param action - The action being performed
|
|
14058
|
+
* @param proof - Optional proof to attach
|
|
14059
|
+
* @returns The created audit record
|
|
14060
|
+
*/
|
|
14061
|
+
record(envelope, action, proof) {
|
|
14062
|
+
const record = {
|
|
14063
|
+
record_id: uuidv43(),
|
|
14064
|
+
envelope_id: envelope.message_id ?? "unknown",
|
|
14065
|
+
action,
|
|
14066
|
+
actor_id: envelope.producer_id ?? "unknown",
|
|
14067
|
+
timestamp: nowHlcStub(),
|
|
14068
|
+
proof
|
|
14069
|
+
};
|
|
14070
|
+
const envelope_id = record.envelope_id;
|
|
14071
|
+
if (!this._records.has(envelope_id)) {
|
|
14072
|
+
this._records.set(envelope_id, []);
|
|
14073
|
+
}
|
|
14074
|
+
this._records.get(envelope_id).push(record);
|
|
14075
|
+
return record;
|
|
14076
|
+
}
|
|
14077
|
+
/**
|
|
14078
|
+
* Query all audit records for a specific envelope.
|
|
14079
|
+
*
|
|
14080
|
+
* @param envelope_id - The message_id of the envelope to query
|
|
14081
|
+
* @returns List of audit records for this envelope
|
|
14082
|
+
*/
|
|
14083
|
+
query(envelope_id) {
|
|
14084
|
+
return this._records.get(envelope_id) ?? [];
|
|
14085
|
+
}
|
|
14086
|
+
/**
|
|
14087
|
+
* Get all audit records (useful for testing).
|
|
14088
|
+
*
|
|
14089
|
+
* @returns All audit records across all envelopes
|
|
14090
|
+
*/
|
|
14091
|
+
getAllRecords() {
|
|
14092
|
+
const allRecords = [];
|
|
14093
|
+
for (const records of this._records.values()) {
|
|
14094
|
+
allRecords.push(...records);
|
|
14095
|
+
}
|
|
14096
|
+
return allRecords;
|
|
14097
|
+
}
|
|
14098
|
+
/**
|
|
14099
|
+
* Clear all stored records (useful for testing).
|
|
14100
|
+
*/
|
|
14101
|
+
clear() {
|
|
14102
|
+
this._records.clear();
|
|
14103
|
+
}
|
|
14104
|
+
};
|
|
14105
|
+
|
|
14106
|
+
// src/agentConfig.ts
|
|
14107
|
+
import * as fs2 from "fs";
|
|
14108
|
+
import * as path4 from "path";
|
|
14109
|
+
function defaultEndpoints() {
|
|
14110
|
+
return {
|
|
14111
|
+
router: process.env.SW4RM_ROUTER_ADDR || "http://localhost:50051",
|
|
14112
|
+
registry: process.env.SW4RM_REGISTRY_ADDR || "http://localhost:50052",
|
|
14113
|
+
scheduler: process.env.SW4RM_SCHEDULER_ADDR || "http://localhost:50053",
|
|
14114
|
+
hitl: process.env.SW4RM_HITL_ADDR || "http://localhost:50054",
|
|
14115
|
+
worktree: process.env.SW4RM_WORKTREE_ADDR || "http://localhost:50055",
|
|
14116
|
+
tool: process.env.SW4RM_TOOL_ADDR || "http://localhost:50056",
|
|
14117
|
+
connector: process.env.SW4RM_CONNECTOR_ADDR || "http://localhost:50057",
|
|
14118
|
+
negotiation: process.env.SW4RM_NEGOTIATION_ADDR || "http://localhost:50058",
|
|
14119
|
+
reasoning: process.env.SW4RM_REASONING_ADDR || "http://localhost:50059",
|
|
14120
|
+
logging: process.env.SW4RM_LOGGING_ADDR || "http://localhost:50060"
|
|
14121
|
+
};
|
|
14122
|
+
}
|
|
14123
|
+
function defaultRetryPolicy() {
|
|
14124
|
+
return {
|
|
14125
|
+
maxAttempts: 3,
|
|
14126
|
+
initialBackoffMs: 200,
|
|
14127
|
+
maxBackoffMs: 2e3,
|
|
14128
|
+
multiplier: 2
|
|
14129
|
+
};
|
|
14130
|
+
}
|
|
14131
|
+
function defaultAgentConfig(agentId = "agent-1", name = "Agent") {
|
|
14132
|
+
return {
|
|
14133
|
+
agentId,
|
|
14134
|
+
name,
|
|
14135
|
+
description: void 0,
|
|
14136
|
+
version: "0.1.0",
|
|
14137
|
+
capabilities: [],
|
|
14138
|
+
endpoints: defaultEndpoints(),
|
|
14139
|
+
timeoutMs: 3e4,
|
|
14140
|
+
streamKeepaliveMs: 6e4,
|
|
14141
|
+
retry: defaultRetryPolicy(),
|
|
14142
|
+
metadata: {},
|
|
14143
|
+
communicationClass: 2,
|
|
14144
|
+
// STANDARD
|
|
14145
|
+
modalitiesSupported: ["application/json"],
|
|
14146
|
+
reasoningConnectors: [],
|
|
14147
|
+
publicKey: void 0
|
|
14148
|
+
};
|
|
14149
|
+
}
|
|
14150
|
+
function loadConfigFromEnv() {
|
|
14151
|
+
const config = defaultAgentConfig();
|
|
14152
|
+
if (process.env.AGENT_ID) {
|
|
14153
|
+
config.agentId = process.env.AGENT_ID;
|
|
14154
|
+
}
|
|
14155
|
+
if (process.env.AGENT_NAME) {
|
|
14156
|
+
config.name = process.env.AGENT_NAME;
|
|
14157
|
+
}
|
|
14158
|
+
if (process.env.AGENT_DESCRIPTION) {
|
|
14159
|
+
config.description = process.env.AGENT_DESCRIPTION;
|
|
14160
|
+
}
|
|
14161
|
+
if (process.env.AGENT_VERSION) {
|
|
14162
|
+
config.version = process.env.AGENT_VERSION;
|
|
14163
|
+
}
|
|
14164
|
+
if (process.env.AGENT_CAPABILITIES) {
|
|
14165
|
+
config.capabilities = process.env.AGENT_CAPABILITIES.split(",").map((s) => s.trim());
|
|
14166
|
+
}
|
|
14167
|
+
if (process.env.SW4RM_TIMEOUT_MS) {
|
|
14168
|
+
const timeout = parseInt(process.env.SW4RM_TIMEOUT_MS, 10);
|
|
14169
|
+
if (!isNaN(timeout)) {
|
|
14170
|
+
config.timeoutMs = timeout;
|
|
14171
|
+
}
|
|
14172
|
+
}
|
|
14173
|
+
if (process.env.SW4RM_STREAM_KEEPALIVE_MS) {
|
|
14174
|
+
const keepalive = parseInt(process.env.SW4RM_STREAM_KEEPALIVE_MS, 10);
|
|
14175
|
+
if (!isNaN(keepalive)) {
|
|
14176
|
+
config.streamKeepaliveMs = keepalive;
|
|
14177
|
+
}
|
|
14178
|
+
}
|
|
14179
|
+
if (process.env.SW4RM_RETRY_MAX_ATTEMPTS) {
|
|
14180
|
+
const attempts = parseInt(process.env.SW4RM_RETRY_MAX_ATTEMPTS, 10);
|
|
14181
|
+
if (!isNaN(attempts)) {
|
|
14182
|
+
config.retry.maxAttempts = attempts;
|
|
14183
|
+
}
|
|
14184
|
+
}
|
|
14185
|
+
if (process.env.SW4RM_COMMUNICATION_CLASS) {
|
|
14186
|
+
const cls = parseInt(process.env.SW4RM_COMMUNICATION_CLASS, 10);
|
|
14187
|
+
if (!isNaN(cls)) {
|
|
14188
|
+
config.communicationClass = cls;
|
|
14189
|
+
}
|
|
14190
|
+
}
|
|
14191
|
+
config.endpoints = defaultEndpoints();
|
|
14192
|
+
return config;
|
|
14193
|
+
}
|
|
14194
|
+
function defaultSW4RMConfig() {
|
|
14195
|
+
return {
|
|
14196
|
+
routerAddr: process.env.SW4RM_ROUTER_ADDR || "http://localhost:50051",
|
|
14197
|
+
registryAddr: process.env.SW4RM_REGISTRY_ADDR || "http://localhost:50052",
|
|
14198
|
+
defaultTimeoutMs: parseInt(process.env.SW4RM_DEFAULT_TIMEOUT_MS || "30000", 10),
|
|
14199
|
+
maxRetries: parseInt(process.env.SW4RM_MAX_RETRIES || "3", 10),
|
|
14200
|
+
enableMetrics: parseBool(process.env.SW4RM_ENABLE_METRICS, true),
|
|
14201
|
+
enableTracing: parseBool(process.env.SW4RM_ENABLE_TRACING, true),
|
|
14202
|
+
logLevel: process.env.SW4RM_LOG_LEVEL || "INFO",
|
|
14203
|
+
featureFlags: {}
|
|
14204
|
+
};
|
|
14205
|
+
}
|
|
14206
|
+
function parseBool(value, defaultValue) {
|
|
14207
|
+
if (value === void 0) {
|
|
14208
|
+
return defaultValue;
|
|
14209
|
+
}
|
|
14210
|
+
return ["true", "1", "yes", "on"].includes(value.toLowerCase());
|
|
14211
|
+
}
|
|
14212
|
+
function loadConfig(configPath) {
|
|
14213
|
+
let config = defaultSW4RMConfig();
|
|
14214
|
+
if (configPath) {
|
|
14215
|
+
if (!fs2.existsSync(configPath)) {
|
|
14216
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
14217
|
+
}
|
|
14218
|
+
const ext = path4.extname(configPath).toLowerCase();
|
|
14219
|
+
if (ext !== ".json") {
|
|
14220
|
+
throw new Error(
|
|
14221
|
+
`Unsupported configuration file format: ${ext}. Supported formats: .json`
|
|
14222
|
+
);
|
|
14223
|
+
}
|
|
14224
|
+
const fileContent = fs2.readFileSync(configPath, "utf8");
|
|
14225
|
+
const fileConfig = JSON.parse(fileContent);
|
|
14226
|
+
config = { ...config, ...fileConfig };
|
|
14227
|
+
}
|
|
14228
|
+
const envConfig = loadFromEnv();
|
|
14229
|
+
config = { ...config, ...envConfig };
|
|
14230
|
+
return config;
|
|
14231
|
+
}
|
|
14232
|
+
function loadFromEnv() {
|
|
14233
|
+
const envConfig = {};
|
|
14234
|
+
if (process.env.SW4RM_ROUTER_ADDR) {
|
|
14235
|
+
envConfig.routerAddr = process.env.SW4RM_ROUTER_ADDR;
|
|
14236
|
+
}
|
|
14237
|
+
if (process.env.SW4RM_REGISTRY_ADDR) {
|
|
14238
|
+
envConfig.registryAddr = process.env.SW4RM_REGISTRY_ADDR;
|
|
14239
|
+
}
|
|
14240
|
+
if (process.env.SW4RM_DEFAULT_TIMEOUT_MS) {
|
|
14241
|
+
const timeout = parseInt(process.env.SW4RM_DEFAULT_TIMEOUT_MS, 10);
|
|
14242
|
+
if (!isNaN(timeout)) {
|
|
14243
|
+
envConfig.defaultTimeoutMs = timeout;
|
|
14244
|
+
}
|
|
14245
|
+
}
|
|
14246
|
+
if (process.env.SW4RM_MAX_RETRIES) {
|
|
14247
|
+
const retries = parseInt(process.env.SW4RM_MAX_RETRIES, 10);
|
|
14248
|
+
if (!isNaN(retries)) {
|
|
14249
|
+
envConfig.maxRetries = retries;
|
|
14250
|
+
}
|
|
14251
|
+
}
|
|
14252
|
+
if (process.env.SW4RM_ENABLE_METRICS !== void 0) {
|
|
14253
|
+
envConfig.enableMetrics = parseBool(process.env.SW4RM_ENABLE_METRICS, true);
|
|
14254
|
+
}
|
|
14255
|
+
if (process.env.SW4RM_ENABLE_TRACING !== void 0) {
|
|
14256
|
+
envConfig.enableTracing = parseBool(process.env.SW4RM_ENABLE_TRACING, true);
|
|
14257
|
+
}
|
|
14258
|
+
if (process.env.SW4RM_LOG_LEVEL) {
|
|
14259
|
+
envConfig.logLevel = process.env.SW4RM_LOG_LEVEL.toUpperCase();
|
|
14260
|
+
}
|
|
14261
|
+
return envConfig;
|
|
14262
|
+
}
|
|
14263
|
+
var _globalConfig = null;
|
|
14264
|
+
function getConfig() {
|
|
14265
|
+
if (_globalConfig === null) {
|
|
14266
|
+
_globalConfig = loadConfig();
|
|
14267
|
+
}
|
|
14268
|
+
return _globalConfig;
|
|
14269
|
+
}
|
|
14270
|
+
function setConfig(config) {
|
|
14271
|
+
_globalConfig = config;
|
|
14272
|
+
}
|
|
14273
|
+
function resetConfig() {
|
|
14274
|
+
_globalConfig = null;
|
|
14275
|
+
}
|
|
14276
|
+
|
|
14277
|
+
// src/persistentActivityBuffer.ts
|
|
14278
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "node:fs";
|
|
14279
|
+
import { dirname } from "node:path";
|
|
14280
|
+
var JSONFilePersistence2 = class {
|
|
14281
|
+
constructor(filePath = "sw4rm_activity.json") {
|
|
14282
|
+
this.filePath = filePath;
|
|
14283
|
+
}
|
|
14284
|
+
load() {
|
|
14285
|
+
if (!existsSync2(this.filePath)) {
|
|
14286
|
+
return { records: {}, order: [] };
|
|
14287
|
+
}
|
|
14288
|
+
const raw = readFileSync2(this.filePath, "utf-8");
|
|
14289
|
+
const data = JSON.parse(raw);
|
|
14290
|
+
return {
|
|
14291
|
+
records: data.records ?? {},
|
|
14292
|
+
order: data.order ?? []
|
|
14293
|
+
};
|
|
14294
|
+
}
|
|
14295
|
+
save(records, order) {
|
|
14296
|
+
const dir = dirname(this.filePath);
|
|
14297
|
+
if (dir && !existsSync2(dir)) {
|
|
14298
|
+
mkdirSync(dir, { recursive: true });
|
|
14299
|
+
}
|
|
14300
|
+
writeFileSync(this.filePath, JSON.stringify({ records, order, version: "1.0" }, null, 2));
|
|
14301
|
+
}
|
|
14302
|
+
clear() {
|
|
14303
|
+
if (existsSync2(this.filePath)) {
|
|
14304
|
+
writeFileSync(this.filePath, JSON.stringify({ records: {}, order: [], version: "1.0" }));
|
|
14305
|
+
}
|
|
14306
|
+
}
|
|
14307
|
+
};
|
|
14308
|
+
var PersistentActivityBuffer = class {
|
|
14309
|
+
byId = /* @__PURE__ */ new Map();
|
|
14310
|
+
byIdempotencyToken = /* @__PURE__ */ new Map();
|
|
14311
|
+
// token -> message_id
|
|
14312
|
+
order = [];
|
|
14313
|
+
maxItems;
|
|
14314
|
+
persistence;
|
|
14315
|
+
dedupWindowS;
|
|
14316
|
+
dirty = false;
|
|
14317
|
+
constructor(opts) {
|
|
14318
|
+
this.maxItems = opts?.maxItems ?? 1e4;
|
|
14319
|
+
this.persistence = opts?.persistence ?? new JSONFilePersistence2();
|
|
14320
|
+
this.dedupWindowS = opts?.dedupWindowS ?? 3600;
|
|
14321
|
+
this.loadFromPersistence();
|
|
14322
|
+
}
|
|
14323
|
+
loadFromPersistence() {
|
|
14324
|
+
try {
|
|
14325
|
+
const { records, order } = this.persistence.load();
|
|
14326
|
+
this.byId = new Map(Object.entries(records));
|
|
14327
|
+
this.order = order;
|
|
14328
|
+
for (const [mid, rec] of this.byId) {
|
|
14329
|
+
const token = rec.envelope?.idempotency_token;
|
|
14330
|
+
if (token) {
|
|
14331
|
+
this.byIdempotencyToken.set(token, mid);
|
|
14332
|
+
}
|
|
14333
|
+
}
|
|
14334
|
+
} catch {
|
|
14335
|
+
this.byId = /* @__PURE__ */ new Map();
|
|
14336
|
+
this.byIdempotencyToken = /* @__PURE__ */ new Map();
|
|
14337
|
+
this.order = [];
|
|
14338
|
+
}
|
|
14339
|
+
}
|
|
14340
|
+
saveToPersistence() {
|
|
14341
|
+
if (!this.dirty)
|
|
14342
|
+
return;
|
|
14343
|
+
try {
|
|
14344
|
+
const records = {};
|
|
14345
|
+
for (const [k, v] of this.byId) {
|
|
14346
|
+
records[k] = v;
|
|
14347
|
+
}
|
|
14348
|
+
this.persistence.save(records, this.order);
|
|
14349
|
+
this.dirty = false;
|
|
14350
|
+
} catch {
|
|
14351
|
+
}
|
|
14352
|
+
}
|
|
14353
|
+
checkCapacity() {
|
|
14354
|
+
if (this.byId.size >= this.maxItems) {
|
|
14355
|
+
throw new BufferFullError(
|
|
14356
|
+
`Activity buffer is full (max ${this.maxItems} items). Reject per spec.`,
|
|
14357
|
+
1 /* BUFFER_FULL */
|
|
14358
|
+
);
|
|
14359
|
+
}
|
|
14360
|
+
}
|
|
14361
|
+
cleanupExpiredDedupEntries() {
|
|
14362
|
+
const nowMs = Date.now();
|
|
14363
|
+
const windowMs = this.dedupWindowS * 1e3;
|
|
14364
|
+
const expired = [];
|
|
14365
|
+
for (const [token, mid] of this.byIdempotencyToken) {
|
|
14366
|
+
const rec = this.byId.get(mid);
|
|
14367
|
+
if (rec && nowMs - rec.ts_ms > windowMs) {
|
|
14368
|
+
expired.push(token);
|
|
14369
|
+
}
|
|
14370
|
+
}
|
|
14371
|
+
for (const token of expired) {
|
|
14372
|
+
this.byIdempotencyToken.delete(token);
|
|
14373
|
+
}
|
|
14374
|
+
}
|
|
14375
|
+
/**
|
|
14376
|
+
* Record an incoming envelope. Throws BufferFullError if buffer is at capacity.
|
|
14377
|
+
*/
|
|
14378
|
+
recordIncoming(envelope) {
|
|
14379
|
+
this.checkCapacity();
|
|
14380
|
+
const mid = String(envelope.message_id ?? "");
|
|
14381
|
+
const rec = {
|
|
14382
|
+
message_id: mid,
|
|
14383
|
+
direction: "in",
|
|
14384
|
+
envelope,
|
|
14385
|
+
ts_ms: Date.now(),
|
|
14386
|
+
ack_stage: 0 /* ACK_STAGE_UNSPECIFIED */,
|
|
14387
|
+
error_code: 0 /* ERROR_CODE_UNSPECIFIED */,
|
|
14388
|
+
ack_note: ""
|
|
14389
|
+
};
|
|
14390
|
+
this.byId.set(mid, rec);
|
|
14391
|
+
this.order.push(mid);
|
|
14392
|
+
const token = envelope.idempotency_token;
|
|
14393
|
+
if (token) {
|
|
14394
|
+
this.byIdempotencyToken.set(token, mid);
|
|
14395
|
+
}
|
|
14396
|
+
this.cleanupExpiredDedupEntries();
|
|
14397
|
+
this.dirty = true;
|
|
14398
|
+
return rec;
|
|
14399
|
+
}
|
|
14400
|
+
/**
|
|
14401
|
+
* Record an outgoing envelope. Throws BufferFullError if buffer is at capacity.
|
|
14402
|
+
*/
|
|
14403
|
+
recordOutgoing(envelope) {
|
|
14404
|
+
this.checkCapacity();
|
|
14405
|
+
const mid = String(envelope.message_id ?? "");
|
|
14406
|
+
const rec = {
|
|
14407
|
+
message_id: mid,
|
|
14408
|
+
direction: "out",
|
|
14409
|
+
envelope,
|
|
14410
|
+
ts_ms: Date.now(),
|
|
14411
|
+
ack_stage: 0 /* ACK_STAGE_UNSPECIFIED */,
|
|
14412
|
+
error_code: 0 /* ERROR_CODE_UNSPECIFIED */,
|
|
14413
|
+
ack_note: ""
|
|
14414
|
+
};
|
|
14415
|
+
this.byId.set(mid, rec);
|
|
14416
|
+
this.order.push(mid);
|
|
14417
|
+
const token = envelope.idempotency_token;
|
|
14418
|
+
if (token) {
|
|
14419
|
+
this.byIdempotencyToken.set(token, mid);
|
|
14420
|
+
}
|
|
14421
|
+
this.cleanupExpiredDedupEntries();
|
|
14422
|
+
this.dirty = true;
|
|
14423
|
+
return rec;
|
|
14424
|
+
}
|
|
14425
|
+
/**
|
|
14426
|
+
* Process an ACK for a previously recorded message.
|
|
14427
|
+
*/
|
|
14428
|
+
ack(ackMsg) {
|
|
14429
|
+
const target = String(ackMsg.ack_for_message_id);
|
|
14430
|
+
const rec = this.byId.get(target);
|
|
14431
|
+
if (rec) {
|
|
14432
|
+
rec.ack_stage = ackMsg.ack_stage ?? 0 /* ACK_STAGE_UNSPECIFIED */;
|
|
14433
|
+
rec.error_code = ackMsg.error_code ?? 0 /* ERROR_CODE_UNSPECIFIED */;
|
|
14434
|
+
rec.ack_note = ackMsg.note ?? "";
|
|
14435
|
+
this.dirty = true;
|
|
14436
|
+
}
|
|
14437
|
+
return rec;
|
|
14438
|
+
}
|
|
14439
|
+
/** Get record by message ID. */
|
|
14440
|
+
get(messageId) {
|
|
14441
|
+
return this.byId.get(messageId);
|
|
14442
|
+
}
|
|
14443
|
+
/** Get record by idempotency token (for deduplication). */
|
|
14444
|
+
getByIdempotencyToken(token) {
|
|
14445
|
+
this.cleanupExpiredDedupEntries();
|
|
14446
|
+
const mid = this.byIdempotencyToken.get(token);
|
|
14447
|
+
if (mid)
|
|
14448
|
+
return this.byId.get(mid);
|
|
14449
|
+
return void 0;
|
|
14450
|
+
}
|
|
14451
|
+
/** Get all un-ACKed records. */
|
|
14452
|
+
unacked() {
|
|
14453
|
+
return [...this.byId.values()].filter(
|
|
14454
|
+
(r) => r.ack_stage === 0 /* ACK_STAGE_UNSPECIFIED */ || r.ack_stage === 1 /* RECEIVED */ || r.ack_stage === 2 /* READ */
|
|
14455
|
+
);
|
|
14456
|
+
}
|
|
14457
|
+
/** Get N most recent records. */
|
|
14458
|
+
recent(n = 50) {
|
|
14459
|
+
const ids = this.order.slice(-n);
|
|
14460
|
+
return ids.map((id) => this.byId.get(id)).filter(Boolean);
|
|
14461
|
+
}
|
|
14462
|
+
/** Update envelope state for a message. */
|
|
14463
|
+
updateState(messageId, newState) {
|
|
14464
|
+
const rec = this.byId.get(messageId);
|
|
14465
|
+
if (rec) {
|
|
14466
|
+
rec.envelope.state = newState;
|
|
14467
|
+
this.dirty = true;
|
|
14468
|
+
}
|
|
14469
|
+
return rec;
|
|
14470
|
+
}
|
|
14471
|
+
/** Return unacked outgoing messages for reconciliation. */
|
|
14472
|
+
reconcile() {
|
|
14473
|
+
return this.unacked().filter((r) => r.direction === "out");
|
|
14474
|
+
}
|
|
14475
|
+
/** Force save to persistence. */
|
|
14476
|
+
flush() {
|
|
14477
|
+
this.dirty = true;
|
|
14478
|
+
this.saveToPersistence();
|
|
14479
|
+
}
|
|
14480
|
+
/** Clear all records. */
|
|
14481
|
+
clear() {
|
|
14482
|
+
this.byId.clear();
|
|
14483
|
+
this.byIdempotencyToken.clear();
|
|
14484
|
+
this.order = [];
|
|
14485
|
+
this.persistence.clear();
|
|
14486
|
+
this.dirty = false;
|
|
14487
|
+
}
|
|
14488
|
+
/** Get the count of records. */
|
|
14489
|
+
get size() {
|
|
14490
|
+
return this.byId.size;
|
|
14491
|
+
}
|
|
14492
|
+
};
|
|
14493
|
+
|
|
14494
|
+
// src/secrets/types.ts
|
|
14495
|
+
var SecretSource = /* @__PURE__ */ ((SecretSource2) => {
|
|
14496
|
+
SecretSource2["CLI"] = "cli";
|
|
14497
|
+
SecretSource2["ENV"] = "env";
|
|
14498
|
+
SecretSource2["SCOPED"] = "scoped";
|
|
14499
|
+
SecretSource2["GLOBAL"] = "global";
|
|
14500
|
+
return SecretSource2;
|
|
14501
|
+
})(SecretSource || {});
|
|
14502
|
+
var Scope = class {
|
|
14503
|
+
constructor(name = null) {
|
|
14504
|
+
this.name = name;
|
|
14505
|
+
}
|
|
14506
|
+
get isGlobal() {
|
|
14507
|
+
return this.name === null;
|
|
14508
|
+
}
|
|
14509
|
+
label() {
|
|
14510
|
+
return this.name ?? "global";
|
|
14511
|
+
}
|
|
14512
|
+
};
|
|
14513
|
+
var SecretKey = class {
|
|
14514
|
+
constructor(key) {
|
|
14515
|
+
this.key = key;
|
|
14516
|
+
if (!key || key.split(".").some((p) => p.trim() === "")) {
|
|
14517
|
+
throw new Error("secret key must be dotted identifiers without empty segments");
|
|
14518
|
+
}
|
|
14519
|
+
}
|
|
14520
|
+
};
|
|
14521
|
+
var SecretValue = class _SecretValue {
|
|
14522
|
+
constructor(value) {
|
|
14523
|
+
this.value = value;
|
|
14524
|
+
if (value.length > _SecretValue.MAX_LEN) {
|
|
14525
|
+
throw new Error("secret value exceeds maximum allowed length (8KB)");
|
|
14526
|
+
}
|
|
14527
|
+
}
|
|
14528
|
+
static MAX_LEN = 8 * 1024;
|
|
14529
|
+
};
|
|
14530
|
+
|
|
14531
|
+
// src/secrets/errors.ts
|
|
14532
|
+
var SecretError = class extends Error {
|
|
14533
|
+
};
|
|
14534
|
+
var SecretNotFound = class extends SecretError {
|
|
14535
|
+
constructor(scope, key) {
|
|
14536
|
+
super(`secret not found: scope=${scope} key=${key}`);
|
|
14537
|
+
this.scope = scope;
|
|
14538
|
+
this.key = key;
|
|
14539
|
+
}
|
|
14540
|
+
};
|
|
14541
|
+
var SecretBackendError = class extends SecretError {
|
|
14542
|
+
};
|
|
14543
|
+
var SecretPermissionError = class extends SecretError {
|
|
14544
|
+
};
|
|
14545
|
+
var SecretValidationError = class extends SecretError {
|
|
14546
|
+
};
|
|
14547
|
+
|
|
14548
|
+
// src/secrets/backend.ts
|
|
14549
|
+
var Secrets = class {
|
|
14550
|
+
constructor(backend) {
|
|
14551
|
+
this.backend = backend;
|
|
14552
|
+
}
|
|
14553
|
+
set(scope, key, value) {
|
|
14554
|
+
return this.backend.set(scope, key, value);
|
|
14555
|
+
}
|
|
14556
|
+
get(scope, key) {
|
|
14557
|
+
return this.backend.get(scope, key);
|
|
14558
|
+
}
|
|
14559
|
+
list(scope) {
|
|
14560
|
+
return this.backend.list(scope);
|
|
14561
|
+
}
|
|
14562
|
+
};
|
|
14563
|
+
|
|
14564
|
+
// src/secrets/resolver.ts
|
|
14565
|
+
var Resolver = class {
|
|
14566
|
+
constructor(backend, env = process.env) {
|
|
14567
|
+
this.backend = backend;
|
|
14568
|
+
this.env = env;
|
|
14569
|
+
}
|
|
14570
|
+
async resolve(key, scope, opts) {
|
|
14571
|
+
const explicit = opts?.explicit ?? null;
|
|
14572
|
+
const envVar = opts?.envVar ?? null;
|
|
14573
|
+
if (explicit !== null)
|
|
14574
|
+
return [explicit, "cli" /* CLI */];
|
|
14575
|
+
if (envVar && this.env[envVar] && this.env[envVar] !== "")
|
|
14576
|
+
return [this.env[envVar], "env" /* ENV */];
|
|
13035
14577
|
try {
|
|
13036
14578
|
const v = await this.backend.get(scope, key);
|
|
13037
14579
|
return [v, scope.isGlobal ? "global" /* GLOBAL */ : "scoped" /* SCOPED */];
|
|
@@ -13046,8 +14588,8 @@ var Resolver = class {
|
|
|
13046
14588
|
};
|
|
13047
14589
|
|
|
13048
14590
|
// src/secrets/backends/file.ts
|
|
13049
|
-
import { mkdirSync, readFileSync, renameSync, writeFileSync, existsSync, chmodSync } from "node:fs";
|
|
13050
|
-
import { join, dirname } from "node:path";
|
|
14591
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync2, existsSync as existsSync3, chmodSync } from "node:fs";
|
|
14592
|
+
import { join, dirname as dirname2 } from "node:path";
|
|
13051
14593
|
function defaultPath() {
|
|
13052
14594
|
const isWin = process.platform === "win32";
|
|
13053
14595
|
if (isWin) {
|
|
@@ -13060,14 +14602,14 @@ function defaultPath() {
|
|
|
13060
14602
|
}
|
|
13061
14603
|
var FileBackend = class {
|
|
13062
14604
|
path;
|
|
13063
|
-
constructor(
|
|
13064
|
-
this.path =
|
|
13065
|
-
|
|
14605
|
+
constructor(path7) {
|
|
14606
|
+
this.path = path7 ?? defaultPath();
|
|
14607
|
+
mkdirSync2(dirname2(this.path), { recursive: true });
|
|
13066
14608
|
try {
|
|
13067
|
-
chmodSync(
|
|
14609
|
+
chmodSync(dirname2(this.path), 448);
|
|
13068
14610
|
} catch {
|
|
13069
14611
|
}
|
|
13070
|
-
if (!
|
|
14612
|
+
if (!existsSync3(this.path)) {
|
|
13071
14613
|
this.safeWrite({});
|
|
13072
14614
|
this.enforceFilePerms();
|
|
13073
14615
|
} else {
|
|
@@ -13084,9 +14626,9 @@ var FileBackend = class {
|
|
|
13084
14626
|
}
|
|
13085
14627
|
}
|
|
13086
14628
|
safeWrite(obj) {
|
|
13087
|
-
const tmp = join(
|
|
14629
|
+
const tmp = join(dirname2(this.path), `.secrets.${Date.now()}.${Math.random().toString(16).slice(2)}`);
|
|
13088
14630
|
try {
|
|
13089
|
-
|
|
14631
|
+
writeFileSync2(tmp, JSON.stringify(obj, null, 2), { encoding: "utf8", mode: 384 });
|
|
13090
14632
|
renameSync(tmp, this.path);
|
|
13091
14633
|
this.enforceFilePerms();
|
|
13092
14634
|
} catch (e) {
|
|
@@ -13095,7 +14637,7 @@ var FileBackend = class {
|
|
|
13095
14637
|
}
|
|
13096
14638
|
load() {
|
|
13097
14639
|
try {
|
|
13098
|
-
const s =
|
|
14640
|
+
const s = readFileSync3(this.path, { encoding: "utf8" });
|
|
13099
14641
|
return JSON.parse(s);
|
|
13100
14642
|
} catch (e) {
|
|
13101
14643
|
if (e.code === "ENOENT")
|
|
@@ -13191,57 +14733,939 @@ async function selectBackend(mode) {
|
|
|
13191
14733
|
}
|
|
13192
14734
|
}
|
|
13193
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
|
+
|
|
13194
15571
|
// src/index.ts
|
|
13195
|
-
var version = "0.
|
|
15572
|
+
var version = "0.6.0";
|
|
13196
15573
|
export {
|
|
13197
15574
|
ACKLifecycleManager,
|
|
13198
|
-
|
|
15575
|
+
AGENT_STATE_FAILED,
|
|
15576
|
+
AGENT_STATE_INITIALIZING,
|
|
15577
|
+
AGENT_STATE_RUNNING,
|
|
15578
|
+
AGENT_STATE_SHUTTING_DOWN,
|
|
15579
|
+
AckStage2 as AckStage,
|
|
13199
15580
|
ActivityBuffer,
|
|
13200
15581
|
ActivityBufferSync,
|
|
13201
15582
|
ActivityClient,
|
|
13202
15583
|
AgentState,
|
|
13203
15584
|
AgentStateMachine,
|
|
15585
|
+
AnthropicClient,
|
|
13204
15586
|
ArtifactType,
|
|
13205
15587
|
BaseClient,
|
|
13206
15588
|
BordaCountAggregator,
|
|
15589
|
+
BufferFullError,
|
|
13207
15590
|
CT_AGENT_REPORT_V1,
|
|
13208
15591
|
CT_SCHEDULER_COMMAND_V1,
|
|
15592
|
+
CancellationManager,
|
|
15593
|
+
CancellationValidationError,
|
|
15594
|
+
CommunicationClass,
|
|
13209
15595
|
ConfidenceWeightedAggregator,
|
|
13210
15596
|
ConnectorClient,
|
|
15597
|
+
DEDUP_WINDOW_S,
|
|
15598
|
+
DEFAULT_ACK_TIMEOUT_MS,
|
|
15599
|
+
DEFAULT_BACKOFF_MULTIPLIER,
|
|
15600
|
+
DEFAULT_EFFECTIVE_MAX_REDIRECTS,
|
|
13211
15601
|
DEFAULT_ESCALATION_POLICY,
|
|
13212
15602
|
DEFAULT_EXECUTION_POLICY,
|
|
15603
|
+
DEFAULT_INITIAL_BACKOFF_MS,
|
|
15604
|
+
DEFAULT_MAX_BACKOFF_MS,
|
|
15605
|
+
DEFAULT_MAX_REDIRECTS,
|
|
15606
|
+
DEFAULT_MAX_RETRIES_ON_OVERLOADED,
|
|
13213
15607
|
DEFAULT_NEGOTIATION_POLICY,
|
|
15608
|
+
DEFAULT_PEER_LIVENESS_THRESHOLD_MS,
|
|
15609
|
+
DebateIntensity,
|
|
13214
15610
|
DecisionOutcome,
|
|
13215
15611
|
DefaultWorktreePolicy,
|
|
15612
|
+
DuplicateDetectedError,
|
|
15613
|
+
EnvelopeState,
|
|
13216
15614
|
ErrorCode,
|
|
13217
15615
|
ErrorCodeMapper,
|
|
13218
15616
|
FileBackend,
|
|
15617
|
+
GatewayRedirectEmitter,
|
|
15618
|
+
GatewayValidationError,
|
|
15619
|
+
GroqClient,
|
|
13219
15620
|
HITLClient,
|
|
13220
15621
|
HandoffClient,
|
|
13221
15622
|
HandoffStatus,
|
|
13222
15623
|
HandoffTimeoutError,
|
|
13223
15624
|
HandoffValidationError,
|
|
13224
15625
|
HitlMode,
|
|
15626
|
+
HitlReasonType,
|
|
15627
|
+
InMemoryAuditor,
|
|
15628
|
+
InMemoryNegotiationRoomStore,
|
|
13225
15629
|
InMemoryPolicyStore,
|
|
13226
15630
|
InterceptorChain,
|
|
13227
15631
|
JSONFilePersistence,
|
|
13228
15632
|
JsonFilePolicyStore,
|
|
13229
15633
|
KeyringBackend,
|
|
15634
|
+
LlmAuthenticationError,
|
|
15635
|
+
LlmContextLengthError,
|
|
15636
|
+
LlmError,
|
|
15637
|
+
LlmRateLimitError,
|
|
15638
|
+
LlmTimeoutError,
|
|
13230
15639
|
LoggingClient,
|
|
15640
|
+
MIN_GRACE_PERIOD_MS,
|
|
13231
15641
|
MajorityVoteAggregator,
|
|
13232
15642
|
MaxEntriesStrategy,
|
|
13233
15643
|
MessageProcessor,
|
|
13234
15644
|
MessageType,
|
|
15645
|
+
MockLlmClient,
|
|
15646
|
+
NON_SERVING_AGENT_STATES,
|
|
13235
15647
|
NegotiationClient,
|
|
15648
|
+
NegotiationError,
|
|
13236
15649
|
NegotiationRoomClient,
|
|
15650
|
+
NegotiationRoomStoreError,
|
|
13237
15651
|
NegotiationTimeoutError,
|
|
13238
15652
|
NegotiationValidationError,
|
|
15653
|
+
NoOpAuditor,
|
|
13239
15654
|
NodeStatus,
|
|
15655
|
+
PeerSelector,
|
|
15656
|
+
PermissionError,
|
|
15657
|
+
PersistentActivityBuffer,
|
|
13240
15658
|
PersistentWorktreeState,
|
|
13241
15659
|
PolicyStoreError,
|
|
15660
|
+
PolicyViolationError,
|
|
15661
|
+
PreemptionError,
|
|
15662
|
+
REGISTRATION_TYPE_STANDARD_AGENT,
|
|
15663
|
+
REGISTRATION_TYPE_SWARM_GATEWAY,
|
|
15664
|
+
RETRY_AFTER_JITTER_RATIO,
|
|
13242
15665
|
ReasoningClient,
|
|
13243
15666
|
RegistryClient,
|
|
13244
15667
|
Resolver,
|
|
15668
|
+
RouteError,
|
|
13245
15669
|
RouterClient,
|
|
13246
15670
|
RuntimePersistence,
|
|
13247
15671
|
SchedulerClient,
|
|
@@ -13259,35 +15683,63 @@ export {
|
|
|
13259
15683
|
SimpleAverageAggregator,
|
|
13260
15684
|
StateTransitionError,
|
|
13261
15685
|
Sw4rmError,
|
|
15686
|
+
TimeoutError,
|
|
15687
|
+
TokenBucket,
|
|
13262
15688
|
ToolClient,
|
|
13263
15689
|
TriggerType,
|
|
15690
|
+
ValidationError,
|
|
13264
15691
|
VotingAnalyzer,
|
|
13265
15692
|
WorkflowClient,
|
|
13266
15693
|
WorkflowCycleError,
|
|
13267
15694
|
WorkflowStatus,
|
|
13268
15695
|
WorkflowValidationError,
|
|
13269
15696
|
WorktreeClient,
|
|
15697
|
+
WorktreeError,
|
|
15698
|
+
WorktreeStateEnum,
|
|
15699
|
+
WorktreeTransitionError,
|
|
13270
15700
|
aggregateVotes,
|
|
13271
15701
|
buildAckEnvelope,
|
|
13272
15702
|
buildEnvelope,
|
|
15703
|
+
buildRateLimiterConfig,
|
|
15704
|
+
computeEnvelopeHash,
|
|
13273
15705
|
computeIdempotencyToken,
|
|
15706
|
+
createLlmClient,
|
|
13274
15707
|
createResilientIncomingStream,
|
|
15708
|
+
createSimpleProof,
|
|
13275
15709
|
decodeAgentReportV1,
|
|
13276
15710
|
decodeBase64,
|
|
15711
|
+
defaultAgentConfig,
|
|
15712
|
+
defaultEndpoints,
|
|
15713
|
+
defaultRetryPolicy,
|
|
15714
|
+
defaultSW4RMConfig,
|
|
15715
|
+
delegateToSwarm,
|
|
13277
15716
|
durationToMs,
|
|
13278
15717
|
encodeSchedulerCommandV1,
|
|
15718
|
+
getConfig,
|
|
15719
|
+
getDefaultStore,
|
|
15720
|
+
getGlobalRateLimiter,
|
|
13279
15721
|
getValidTransitions,
|
|
15722
|
+
isTerminalEnvelopeState,
|
|
13280
15723
|
isValidTransition,
|
|
15724
|
+
isValidWorktreeTransition,
|
|
15725
|
+
loadConfig,
|
|
15726
|
+
loadConfigFromEnv,
|
|
13281
15727
|
loggingInterceptor,
|
|
13282
15728
|
mapGrpcStatusToErrorCode,
|
|
13283
15729
|
msToDuration,
|
|
13284
15730
|
normalizeReportPaths,
|
|
13285
15731
|
nowTimestamp,
|
|
13286
15732
|
parseNegotiationEvent,
|
|
15733
|
+
resetConfig,
|
|
15734
|
+
resetDefaultStore,
|
|
15735
|
+
resetGlobalRateLimiter,
|
|
13287
15736
|
selectBackend,
|
|
13288
15737
|
sendMessageWithAck,
|
|
15738
|
+
setConfig,
|
|
13289
15739
|
timingInterceptor,
|
|
15740
|
+
updateEnvelopeState,
|
|
13290
15741
|
uuidv4,
|
|
15742
|
+
verifyAuditProof,
|
|
13291
15743
|
version
|
|
13292
15744
|
};
|
|
13293
15745
|
/*! Bundled license information:
|