nestor-sh 2.7.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nestor.mjs +2373 -662
- package/dist/studio/assets/index-BFlEVRQQ.js +596 -0
- package/dist/studio/assets/index-CqlwLAqD.js +596 -0
- package/dist/studio/assets/index-Cw42VG9E.js +596 -0
- package/dist/studio/index.html +1 -1
- package/package.json +1 -1
package/dist/nestor.mjs
CHANGED
|
@@ -6567,18 +6567,51 @@ var init_store = __esm({
|
|
|
6567
6567
|
}
|
|
6568
6568
|
/**
|
|
6569
6569
|
* Search missions by objective text (LIKE query).
|
|
6570
|
+
* Supports optional filters for type, status, date range.
|
|
6571
|
+
* Returns results sorted by relevance (title matches ranked higher).
|
|
6570
6572
|
*/
|
|
6571
|
-
searchMissions(query,
|
|
6572
|
-
|
|
6573
|
-
const
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6573
|
+
searchMissions(query, opts) {
|
|
6574
|
+
let sql = "SELECT * FROM missions WHERE 1=1";
|
|
6575
|
+
const params = [];
|
|
6576
|
+
if (query) {
|
|
6577
|
+
sql += " AND (title LIKE ? OR objective LIKE ? OR findings LIKE ?)";
|
|
6578
|
+
const q = `%${query}%`;
|
|
6579
|
+
params.push(q, q, q);
|
|
6580
|
+
}
|
|
6581
|
+
if (opts?.type) {
|
|
6582
|
+
sql += " AND type = ?";
|
|
6583
|
+
params.push(opts.type);
|
|
6584
|
+
}
|
|
6585
|
+
if (opts?.status) {
|
|
6586
|
+
sql += " AND status = ?";
|
|
6587
|
+
params.push(opts.status);
|
|
6588
|
+
}
|
|
6589
|
+
if (opts?.from) {
|
|
6590
|
+
sql += " AND created_at >= ?";
|
|
6591
|
+
params.push(new Date(opts.from).getTime());
|
|
6592
|
+
}
|
|
6593
|
+
if (opts?.to) {
|
|
6594
|
+
sql += " AND created_at <= ?";
|
|
6595
|
+
params.push(new Date(opts.to).getTime());
|
|
6596
|
+
}
|
|
6597
|
+
sql += " ORDER BY created_at DESC";
|
|
6598
|
+
sql += ` LIMIT ${opts?.limit || 20}`;
|
|
6599
|
+
const rows = queryAll(this.db, sql, params);
|
|
6600
|
+
const missions = rows.map((row) => this.rowToMission(row));
|
|
6601
|
+
if (query) {
|
|
6602
|
+
const lowerQ = query.toLowerCase();
|
|
6603
|
+
missions.sort((a, b) => {
|
|
6604
|
+
const aInTitle = a.title.toLowerCase().includes(lowerQ) ? 1 : 0;
|
|
6605
|
+
const bInTitle = b.title.toLowerCase().includes(lowerQ) ? 1 : 0;
|
|
6606
|
+
if (aInTitle !== bInTitle) return bInTitle - aInTitle;
|
|
6607
|
+
return b.createdAt - a.createdAt;
|
|
6608
|
+
});
|
|
6609
|
+
}
|
|
6610
|
+
return missions.map((m) => ({
|
|
6611
|
+
...m,
|
|
6612
|
+
findingCount: Array.isArray(m.findings) ? m.findings.length : 0,
|
|
6613
|
+
qualityScore: m.evaluation && typeof m.evaluation === "object" && "overall" in m.evaluation ? m.evaluation.overall : null
|
|
6614
|
+
}));
|
|
6582
6615
|
}
|
|
6583
6616
|
/**
|
|
6584
6617
|
* List all missions, optionally filtered by tenant.
|
|
@@ -6589,6 +6622,44 @@ var init_store = __esm({
|
|
|
6589
6622
|
const rows = queryAll(this.db, sql, params);
|
|
6590
6623
|
return rows.map((row) => this.rowToMission(row));
|
|
6591
6624
|
}
|
|
6625
|
+
/**
|
|
6626
|
+
* Get aggregate mission statistics.
|
|
6627
|
+
*/
|
|
6628
|
+
getMissionStats(tenantId) {
|
|
6629
|
+
const baseSql = tenantId ? "SELECT * FROM missions WHERE tenant_id = ?" : "SELECT * FROM missions";
|
|
6630
|
+
const params = tenantId ? [tenantId] : [];
|
|
6631
|
+
const rows = queryAll(this.db, baseSql, params);
|
|
6632
|
+
const missions = rows.map((row) => this.rowToMission(row));
|
|
6633
|
+
const byType = {};
|
|
6634
|
+
const byStatus = {};
|
|
6635
|
+
let totalCost = 0;
|
|
6636
|
+
let totalFindings = 0;
|
|
6637
|
+
let qualitySum = 0;
|
|
6638
|
+
let qualityCount = 0;
|
|
6639
|
+
for (const m of missions) {
|
|
6640
|
+
byType[m.type] = (byType[m.type] || 0) + 1;
|
|
6641
|
+
byStatus[m.status] = (byStatus[m.status] || 0) + 1;
|
|
6642
|
+
totalCost += m.spentUsd || 0;
|
|
6643
|
+
totalFindings += Array.isArray(m.findings) ? m.findings.length : 0;
|
|
6644
|
+
if (m.evaluation && typeof m.evaluation === "object" && "overall" in m.evaluation) {
|
|
6645
|
+
const overall = m.evaluation.overall;
|
|
6646
|
+
if (typeof overall === "number") {
|
|
6647
|
+
qualitySum += overall;
|
|
6648
|
+
qualityCount++;
|
|
6649
|
+
}
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6652
|
+
const topCategories = Object.entries(byType).sort((a, b) => b[1] - a[1]).map(([cat]) => cat);
|
|
6653
|
+
return {
|
|
6654
|
+
total: missions.length,
|
|
6655
|
+
byType,
|
|
6656
|
+
byStatus,
|
|
6657
|
+
totalCost: Math.round(totalCost * 100) / 100,
|
|
6658
|
+
totalFindings,
|
|
6659
|
+
avgQuality: qualityCount > 0 ? Math.round(qualitySum / qualityCount * 100) / 100 : 0,
|
|
6660
|
+
topCategories
|
|
6661
|
+
};
|
|
6662
|
+
}
|
|
6592
6663
|
/**
|
|
6593
6664
|
* Search findings across all missions (LIKE query).
|
|
6594
6665
|
*/
|
|
@@ -7738,9 +7809,9 @@ var init_request_validator = __esm({
|
|
|
7738
7809
|
});
|
|
7739
7810
|
|
|
7740
7811
|
// ../server/src/security/input-sanitizer.ts
|
|
7741
|
-
function resolvePath(base,
|
|
7742
|
-
if (
|
|
7743
|
-
const parts = (base + "/" +
|
|
7812
|
+
function resolvePath(base, relative7) {
|
|
7813
|
+
if (relative7.startsWith("/")) return relative7;
|
|
7814
|
+
const parts = (base + "/" + relative7).split("/");
|
|
7744
7815
|
const resolved = [];
|
|
7745
7816
|
for (const part of parts) {
|
|
7746
7817
|
if (part === "..") {
|
|
@@ -9279,7 +9350,7 @@ import path from "node:path";
|
|
|
9279
9350
|
import fs2 from "node:fs";
|
|
9280
9351
|
function validateSkillFile(filePath, content) {
|
|
9281
9352
|
const ext = path.extname(filePath).toLowerCase();
|
|
9282
|
-
const
|
|
9353
|
+
const basename6 = path.basename(filePath);
|
|
9283
9354
|
const extensionlessAllowed = /* @__PURE__ */ new Set([
|
|
9284
9355
|
"LICENSE",
|
|
9285
9356
|
"LICENCE",
|
|
@@ -9295,14 +9366,14 @@ function validateSkillFile(filePath, content) {
|
|
|
9295
9366
|
".prettierrc",
|
|
9296
9367
|
".eslintrc"
|
|
9297
9368
|
]);
|
|
9298
|
-
if (!ext && !extensionlessAllowed.has(
|
|
9369
|
+
if (!ext && !extensionlessAllowed.has(basename6)) {
|
|
9299
9370
|
return {
|
|
9300
9371
|
valid: false,
|
|
9301
|
-
reason: `File without extension is not in the allowlist: ${
|
|
9372
|
+
reason: `File without extension is not in the allowlist: ${basename6}`,
|
|
9302
9373
|
extension: ""
|
|
9303
9374
|
};
|
|
9304
9375
|
}
|
|
9305
|
-
if (!ext && extensionlessAllowed.has(
|
|
9376
|
+
if (!ext && extensionlessAllowed.has(basename6)) {
|
|
9306
9377
|
if (content && content.length >= 4) {
|
|
9307
9378
|
const binaryCheck = checkForBinaryContent(content, "");
|
|
9308
9379
|
if (binaryCheck) {
|
|
@@ -11430,7 +11501,7 @@ var SERVER_VERSION, startTime;
|
|
|
11430
11501
|
var init_health = __esm({
|
|
11431
11502
|
"../server/src/routes/health.ts"() {
|
|
11432
11503
|
"use strict";
|
|
11433
|
-
SERVER_VERSION = "
|
|
11504
|
+
SERVER_VERSION = "3.0.0";
|
|
11434
11505
|
startTime = Date.now();
|
|
11435
11506
|
}
|
|
11436
11507
|
});
|
|
@@ -12843,7 +12914,7 @@ var init_system = __esm({
|
|
|
12843
12914
|
init_error_handler();
|
|
12844
12915
|
init_broadcaster();
|
|
12845
12916
|
init_approval_service();
|
|
12846
|
-
SERVER_VERSION2 = "
|
|
12917
|
+
SERVER_VERSION2 = "3.0.0";
|
|
12847
12918
|
startTime2 = Date.now();
|
|
12848
12919
|
UpdateConfigSchema = z9.object({
|
|
12849
12920
|
server: z9.object({
|
|
@@ -20273,7 +20344,7 @@ function isNativeAvailable() {
|
|
|
20273
20344
|
return nativeModule !== null;
|
|
20274
20345
|
}
|
|
20275
20346
|
function getNativeVersion() {
|
|
20276
|
-
return nativeModule ? "
|
|
20347
|
+
return nativeModule ? "3.0.0" : null;
|
|
20277
20348
|
}
|
|
20278
20349
|
function validateSsrf(url, allowPrivate = false) {
|
|
20279
20350
|
if (nativeModule) {
|
|
@@ -71039,7 +71110,7 @@ var require_util2 = __commonJS({
|
|
|
71039
71110
|
return path30;
|
|
71040
71111
|
}
|
|
71041
71112
|
exports2.normalize = normalize4;
|
|
71042
|
-
function
|
|
71113
|
+
function join31(aRoot, aPath) {
|
|
71043
71114
|
if (aRoot === "") {
|
|
71044
71115
|
aRoot = ".";
|
|
71045
71116
|
}
|
|
@@ -71071,11 +71142,11 @@ var require_util2 = __commonJS({
|
|
|
71071
71142
|
}
|
|
71072
71143
|
return joined;
|
|
71073
71144
|
}
|
|
71074
|
-
exports2.join =
|
|
71145
|
+
exports2.join = join31;
|
|
71075
71146
|
exports2.isAbsolute = function(aPath) {
|
|
71076
71147
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
71077
71148
|
};
|
|
71078
|
-
function
|
|
71149
|
+
function relative7(aRoot, aPath) {
|
|
71079
71150
|
if (aRoot === "") {
|
|
71080
71151
|
aRoot = ".";
|
|
71081
71152
|
}
|
|
@@ -71094,7 +71165,7 @@ var require_util2 = __commonJS({
|
|
|
71094
71165
|
}
|
|
71095
71166
|
return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
|
|
71096
71167
|
}
|
|
71097
|
-
exports2.relative =
|
|
71168
|
+
exports2.relative = relative7;
|
|
71098
71169
|
var supportsNullProto = function() {
|
|
71099
71170
|
var obj = /* @__PURE__ */ Object.create(null);
|
|
71100
71171
|
return !("__proto__" in obj);
|
|
@@ -71244,7 +71315,7 @@ var require_util2 = __commonJS({
|
|
|
71244
71315
|
parsed.path = parsed.path.substring(0, index + 1);
|
|
71245
71316
|
}
|
|
71246
71317
|
}
|
|
71247
|
-
sourceURL =
|
|
71318
|
+
sourceURL = join31(urlGenerate(parsed), sourceURL);
|
|
71248
71319
|
}
|
|
71249
71320
|
return normalize4(sourceURL);
|
|
71250
71321
|
}
|
|
@@ -73046,7 +73117,7 @@ var require_escodegen = __commonJS({
|
|
|
73046
73117
|
function noEmptySpace() {
|
|
73047
73118
|
return space ? space : " ";
|
|
73048
73119
|
}
|
|
73049
|
-
function
|
|
73120
|
+
function join31(left2, right2) {
|
|
73050
73121
|
var leftSource, rightSource, leftCharCode, rightCharCode;
|
|
73051
73122
|
leftSource = toSourceNodeWhenNeeded(left2).toString();
|
|
73052
73123
|
if (leftSource.length === 0) {
|
|
@@ -73377,8 +73448,8 @@ var require_escodegen = __commonJS({
|
|
|
73377
73448
|
} else {
|
|
73378
73449
|
result.push(that.generateExpression(stmt.left, Precedence.Call, E_TTT));
|
|
73379
73450
|
}
|
|
73380
|
-
result =
|
|
73381
|
-
result = [
|
|
73451
|
+
result = join31(result, operator);
|
|
73452
|
+
result = [join31(
|
|
73382
73453
|
result,
|
|
73383
73454
|
that.generateExpression(stmt.right, Precedence.Assignment, E_TTT)
|
|
73384
73455
|
), ")"];
|
|
@@ -73521,11 +73592,11 @@ var require_escodegen = __commonJS({
|
|
|
73521
73592
|
var result, fragment;
|
|
73522
73593
|
result = ["class"];
|
|
73523
73594
|
if (stmt.id) {
|
|
73524
|
-
result =
|
|
73595
|
+
result = join31(result, this.generateExpression(stmt.id, Precedence.Sequence, E_TTT));
|
|
73525
73596
|
}
|
|
73526
73597
|
if (stmt.superClass) {
|
|
73527
|
-
fragment =
|
|
73528
|
-
result =
|
|
73598
|
+
fragment = join31("extends", this.generateExpression(stmt.superClass, Precedence.Unary, E_TTT));
|
|
73599
|
+
result = join31(result, fragment);
|
|
73529
73600
|
}
|
|
73530
73601
|
result.push(space);
|
|
73531
73602
|
result.push(this.generateStatement(stmt.body, S_TFFT));
|
|
@@ -73538,9 +73609,9 @@ var require_escodegen = __commonJS({
|
|
|
73538
73609
|
return escapeDirective(stmt.directive) + this.semicolon(flags);
|
|
73539
73610
|
},
|
|
73540
73611
|
DoWhileStatement: function(stmt, flags) {
|
|
73541
|
-
var result =
|
|
73612
|
+
var result = join31("do", this.maybeBlock(stmt.body, S_TFFF));
|
|
73542
73613
|
result = this.maybeBlockSuffix(stmt.body, result);
|
|
73543
|
-
return
|
|
73614
|
+
return join31(result, [
|
|
73544
73615
|
"while" + space + "(",
|
|
73545
73616
|
this.generateExpression(stmt.test, Precedence.Sequence, E_TTT),
|
|
73546
73617
|
")" + this.semicolon(flags)
|
|
@@ -73576,11 +73647,11 @@ var require_escodegen = __commonJS({
|
|
|
73576
73647
|
ExportDefaultDeclaration: function(stmt, flags) {
|
|
73577
73648
|
var result = ["export"], bodyFlags;
|
|
73578
73649
|
bodyFlags = flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF;
|
|
73579
|
-
result =
|
|
73650
|
+
result = join31(result, "default");
|
|
73580
73651
|
if (isStatement(stmt.declaration)) {
|
|
73581
|
-
result =
|
|
73652
|
+
result = join31(result, this.generateStatement(stmt.declaration, bodyFlags));
|
|
73582
73653
|
} else {
|
|
73583
|
-
result =
|
|
73654
|
+
result = join31(result, this.generateExpression(stmt.declaration, Precedence.Assignment, E_TTT) + this.semicolon(flags));
|
|
73584
73655
|
}
|
|
73585
73656
|
return result;
|
|
73586
73657
|
},
|
|
@@ -73588,15 +73659,15 @@ var require_escodegen = __commonJS({
|
|
|
73588
73659
|
var result = ["export"], bodyFlags, that = this;
|
|
73589
73660
|
bodyFlags = flags & F_SEMICOLON_OPT ? S_TFFT : S_TFFF;
|
|
73590
73661
|
if (stmt.declaration) {
|
|
73591
|
-
return
|
|
73662
|
+
return join31(result, this.generateStatement(stmt.declaration, bodyFlags));
|
|
73592
73663
|
}
|
|
73593
73664
|
if (stmt.specifiers) {
|
|
73594
73665
|
if (stmt.specifiers.length === 0) {
|
|
73595
|
-
result =
|
|
73666
|
+
result = join31(result, "{" + space + "}");
|
|
73596
73667
|
} else if (stmt.specifiers[0].type === Syntax.ExportBatchSpecifier) {
|
|
73597
|
-
result =
|
|
73668
|
+
result = join31(result, this.generateExpression(stmt.specifiers[0], Precedence.Sequence, E_TTT));
|
|
73598
73669
|
} else {
|
|
73599
|
-
result =
|
|
73670
|
+
result = join31(result, "{");
|
|
73600
73671
|
withIndent(function(indent2) {
|
|
73601
73672
|
var i, iz;
|
|
73602
73673
|
result.push(newline);
|
|
@@ -73614,7 +73685,7 @@ var require_escodegen = __commonJS({
|
|
|
73614
73685
|
result.push(base + "}");
|
|
73615
73686
|
}
|
|
73616
73687
|
if (stmt.source) {
|
|
73617
|
-
result =
|
|
73688
|
+
result = join31(result, [
|
|
73618
73689
|
"from" + space,
|
|
73619
73690
|
// ModuleSpecifier
|
|
73620
73691
|
this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
|
|
@@ -73702,7 +73773,7 @@ var require_escodegen = __commonJS({
|
|
|
73702
73773
|
];
|
|
73703
73774
|
cursor = 0;
|
|
73704
73775
|
if (stmt.specifiers[cursor].type === Syntax.ImportDefaultSpecifier) {
|
|
73705
|
-
result =
|
|
73776
|
+
result = join31(result, [
|
|
73706
73777
|
this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
|
|
73707
73778
|
]);
|
|
73708
73779
|
++cursor;
|
|
@@ -73712,7 +73783,7 @@ var require_escodegen = __commonJS({
|
|
|
73712
73783
|
result.push(",");
|
|
73713
73784
|
}
|
|
73714
73785
|
if (stmt.specifiers[cursor].type === Syntax.ImportNamespaceSpecifier) {
|
|
73715
|
-
result =
|
|
73786
|
+
result = join31(result, [
|
|
73716
73787
|
space,
|
|
73717
73788
|
this.generateExpression(stmt.specifiers[cursor], Precedence.Sequence, E_TTT)
|
|
73718
73789
|
]);
|
|
@@ -73741,7 +73812,7 @@ var require_escodegen = __commonJS({
|
|
|
73741
73812
|
}
|
|
73742
73813
|
}
|
|
73743
73814
|
}
|
|
73744
|
-
result =
|
|
73815
|
+
result = join31(result, [
|
|
73745
73816
|
"from" + space,
|
|
73746
73817
|
// ModuleSpecifier
|
|
73747
73818
|
this.generateExpression(stmt.source, Precedence.Sequence, E_TTT),
|
|
@@ -73795,7 +73866,7 @@ var require_escodegen = __commonJS({
|
|
|
73795
73866
|
return result;
|
|
73796
73867
|
},
|
|
73797
73868
|
ThrowStatement: function(stmt, flags) {
|
|
73798
|
-
return [
|
|
73869
|
+
return [join31(
|
|
73799
73870
|
"throw",
|
|
73800
73871
|
this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
|
|
73801
73872
|
), this.semicolon(flags)];
|
|
@@ -73806,7 +73877,7 @@ var require_escodegen = __commonJS({
|
|
|
73806
73877
|
result = this.maybeBlockSuffix(stmt.block, result);
|
|
73807
73878
|
if (stmt.handlers) {
|
|
73808
73879
|
for (i = 0, iz = stmt.handlers.length; i < iz; ++i) {
|
|
73809
|
-
result =
|
|
73880
|
+
result = join31(result, this.generateStatement(stmt.handlers[i], S_TFFF));
|
|
73810
73881
|
if (stmt.finalizer || i + 1 !== iz) {
|
|
73811
73882
|
result = this.maybeBlockSuffix(stmt.handlers[i].body, result);
|
|
73812
73883
|
}
|
|
@@ -73814,7 +73885,7 @@ var require_escodegen = __commonJS({
|
|
|
73814
73885
|
} else {
|
|
73815
73886
|
guardedHandlers = stmt.guardedHandlers || [];
|
|
73816
73887
|
for (i = 0, iz = guardedHandlers.length; i < iz; ++i) {
|
|
73817
|
-
result =
|
|
73888
|
+
result = join31(result, this.generateStatement(guardedHandlers[i], S_TFFF));
|
|
73818
73889
|
if (stmt.finalizer || i + 1 !== iz) {
|
|
73819
73890
|
result = this.maybeBlockSuffix(guardedHandlers[i].body, result);
|
|
73820
73891
|
}
|
|
@@ -73822,13 +73893,13 @@ var require_escodegen = __commonJS({
|
|
|
73822
73893
|
if (stmt.handler) {
|
|
73823
73894
|
if (Array.isArray(stmt.handler)) {
|
|
73824
73895
|
for (i = 0, iz = stmt.handler.length; i < iz; ++i) {
|
|
73825
|
-
result =
|
|
73896
|
+
result = join31(result, this.generateStatement(stmt.handler[i], S_TFFF));
|
|
73826
73897
|
if (stmt.finalizer || i + 1 !== iz) {
|
|
73827
73898
|
result = this.maybeBlockSuffix(stmt.handler[i].body, result);
|
|
73828
73899
|
}
|
|
73829
73900
|
}
|
|
73830
73901
|
} else {
|
|
73831
|
-
result =
|
|
73902
|
+
result = join31(result, this.generateStatement(stmt.handler, S_TFFF));
|
|
73832
73903
|
if (stmt.finalizer) {
|
|
73833
73904
|
result = this.maybeBlockSuffix(stmt.handler.body, result);
|
|
73834
73905
|
}
|
|
@@ -73836,7 +73907,7 @@ var require_escodegen = __commonJS({
|
|
|
73836
73907
|
}
|
|
73837
73908
|
}
|
|
73838
73909
|
if (stmt.finalizer) {
|
|
73839
|
-
result =
|
|
73910
|
+
result = join31(result, ["finally", this.maybeBlock(stmt.finalizer, S_TFFF)]);
|
|
73840
73911
|
}
|
|
73841
73912
|
return result;
|
|
73842
73913
|
},
|
|
@@ -73870,7 +73941,7 @@ var require_escodegen = __commonJS({
|
|
|
73870
73941
|
withIndent(function() {
|
|
73871
73942
|
if (stmt.test) {
|
|
73872
73943
|
result = [
|
|
73873
|
-
|
|
73944
|
+
join31("case", that.generateExpression(stmt.test, Precedence.Sequence, E_TTT)),
|
|
73874
73945
|
":"
|
|
73875
73946
|
];
|
|
73876
73947
|
} else {
|
|
@@ -73918,9 +73989,9 @@ var require_escodegen = __commonJS({
|
|
|
73918
73989
|
result.push(this.maybeBlock(stmt.consequent, S_TFFF));
|
|
73919
73990
|
result = this.maybeBlockSuffix(stmt.consequent, result);
|
|
73920
73991
|
if (stmt.alternate.type === Syntax.IfStatement) {
|
|
73921
|
-
result =
|
|
73992
|
+
result = join31(result, ["else ", this.generateStatement(stmt.alternate, bodyFlags)]);
|
|
73922
73993
|
} else {
|
|
73923
|
-
result =
|
|
73994
|
+
result = join31(result, join31("else", this.maybeBlock(stmt.alternate, bodyFlags)));
|
|
73924
73995
|
}
|
|
73925
73996
|
} else {
|
|
73926
73997
|
result.push(this.maybeBlock(stmt.consequent, bodyFlags));
|
|
@@ -74021,7 +74092,7 @@ var require_escodegen = __commonJS({
|
|
|
74021
74092
|
},
|
|
74022
74093
|
ReturnStatement: function(stmt, flags) {
|
|
74023
74094
|
if (stmt.argument) {
|
|
74024
|
-
return [
|
|
74095
|
+
return [join31(
|
|
74025
74096
|
"return",
|
|
74026
74097
|
this.generateExpression(stmt.argument, Precedence.Sequence, E_TTT)
|
|
74027
74098
|
), this.semicolon(flags)];
|
|
@@ -74110,14 +74181,14 @@ var require_escodegen = __commonJS({
|
|
|
74110
74181
|
if (leftSource.charCodeAt(leftSource.length - 1) === 47 && esutils.code.isIdentifierPartES5(expr.operator.charCodeAt(0))) {
|
|
74111
74182
|
result = [fragment, noEmptySpace(), expr.operator];
|
|
74112
74183
|
} else {
|
|
74113
|
-
result =
|
|
74184
|
+
result = join31(fragment, expr.operator);
|
|
74114
74185
|
}
|
|
74115
74186
|
fragment = this.generateExpression(expr.right, rightPrecedence, flags);
|
|
74116
74187
|
if (expr.operator === "/" && fragment.toString().charAt(0) === "/" || expr.operator.slice(-1) === "<" && fragment.toString().slice(0, 3) === "!--") {
|
|
74117
74188
|
result.push(noEmptySpace());
|
|
74118
74189
|
result.push(fragment);
|
|
74119
74190
|
} else {
|
|
74120
|
-
result =
|
|
74191
|
+
result = join31(result, fragment);
|
|
74121
74192
|
}
|
|
74122
74193
|
if (expr.operator === "in" && !(flags & F_ALLOW_IN)) {
|
|
74123
74194
|
return ["(", result, ")"];
|
|
@@ -74157,7 +74228,7 @@ var require_escodegen = __commonJS({
|
|
|
74157
74228
|
var result, length, i, iz, itemFlags;
|
|
74158
74229
|
length = expr["arguments"].length;
|
|
74159
74230
|
itemFlags = flags & F_ALLOW_UNPARATH_NEW && !parentheses && length === 0 ? E_TFT : E_TFF;
|
|
74160
|
-
result =
|
|
74231
|
+
result = join31(
|
|
74161
74232
|
"new",
|
|
74162
74233
|
this.generateExpression(expr.callee, Precedence.New, itemFlags)
|
|
74163
74234
|
);
|
|
@@ -74207,11 +74278,11 @@ var require_escodegen = __commonJS({
|
|
|
74207
74278
|
var result, fragment, rightCharCode, leftSource, leftCharCode;
|
|
74208
74279
|
fragment = this.generateExpression(expr.argument, Precedence.Unary, E_TTT);
|
|
74209
74280
|
if (space === "") {
|
|
74210
|
-
result =
|
|
74281
|
+
result = join31(expr.operator, fragment);
|
|
74211
74282
|
} else {
|
|
74212
74283
|
result = [expr.operator];
|
|
74213
74284
|
if (expr.operator.length > 2) {
|
|
74214
|
-
result =
|
|
74285
|
+
result = join31(result, fragment);
|
|
74215
74286
|
} else {
|
|
74216
74287
|
leftSource = toSourceNodeWhenNeeded(result).toString();
|
|
74217
74288
|
leftCharCode = leftSource.charCodeAt(leftSource.length - 1);
|
|
@@ -74234,7 +74305,7 @@ var require_escodegen = __commonJS({
|
|
|
74234
74305
|
result = "yield";
|
|
74235
74306
|
}
|
|
74236
74307
|
if (expr.argument) {
|
|
74237
|
-
result =
|
|
74308
|
+
result = join31(
|
|
74238
74309
|
result,
|
|
74239
74310
|
this.generateExpression(expr.argument, Precedence.Yield, E_TTT)
|
|
74240
74311
|
);
|
|
@@ -74242,7 +74313,7 @@ var require_escodegen = __commonJS({
|
|
|
74242
74313
|
return parenthesize(result, Precedence.Yield, precedence);
|
|
74243
74314
|
},
|
|
74244
74315
|
AwaitExpression: function(expr, precedence, flags) {
|
|
74245
|
-
var result =
|
|
74316
|
+
var result = join31(
|
|
74246
74317
|
expr.all ? "await*" : "await",
|
|
74247
74318
|
this.generateExpression(expr.argument, Precedence.Await, E_TTT)
|
|
74248
74319
|
);
|
|
@@ -74325,11 +74396,11 @@ var require_escodegen = __commonJS({
|
|
|
74325
74396
|
var result, fragment;
|
|
74326
74397
|
result = ["class"];
|
|
74327
74398
|
if (expr.id) {
|
|
74328
|
-
result =
|
|
74399
|
+
result = join31(result, this.generateExpression(expr.id, Precedence.Sequence, E_TTT));
|
|
74329
74400
|
}
|
|
74330
74401
|
if (expr.superClass) {
|
|
74331
|
-
fragment =
|
|
74332
|
-
result =
|
|
74402
|
+
fragment = join31("extends", this.generateExpression(expr.superClass, Precedence.Unary, E_TTT));
|
|
74403
|
+
result = join31(result, fragment);
|
|
74333
74404
|
}
|
|
74334
74405
|
result.push(space);
|
|
74335
74406
|
result.push(this.generateStatement(expr.body, S_TFFT));
|
|
@@ -74344,7 +74415,7 @@ var require_escodegen = __commonJS({
|
|
|
74344
74415
|
}
|
|
74345
74416
|
if (expr.kind === "get" || expr.kind === "set") {
|
|
74346
74417
|
fragment = [
|
|
74347
|
-
|
|
74418
|
+
join31(expr.kind, this.generatePropertyKey(expr.key, expr.computed)),
|
|
74348
74419
|
this.generateFunctionBody(expr.value)
|
|
74349
74420
|
];
|
|
74350
74421
|
} else {
|
|
@@ -74354,7 +74425,7 @@ var require_escodegen = __commonJS({
|
|
|
74354
74425
|
this.generateFunctionBody(expr.value)
|
|
74355
74426
|
];
|
|
74356
74427
|
}
|
|
74357
|
-
return
|
|
74428
|
+
return join31(result, fragment);
|
|
74358
74429
|
},
|
|
74359
74430
|
Property: function(expr, precedence, flags) {
|
|
74360
74431
|
if (expr.kind === "get" || expr.kind === "set") {
|
|
@@ -74549,7 +74620,7 @@ var require_escodegen = __commonJS({
|
|
|
74549
74620
|
for (i = 0, iz = expr.blocks.length; i < iz; ++i) {
|
|
74550
74621
|
fragment = that.generateExpression(expr.blocks[i], Precedence.Sequence, E_TTT);
|
|
74551
74622
|
if (i > 0 || extra.moz.comprehensionExpressionStartsWithAssignment) {
|
|
74552
|
-
result =
|
|
74623
|
+
result = join31(result, fragment);
|
|
74553
74624
|
} else {
|
|
74554
74625
|
result.push(fragment);
|
|
74555
74626
|
}
|
|
@@ -74557,13 +74628,13 @@ var require_escodegen = __commonJS({
|
|
|
74557
74628
|
});
|
|
74558
74629
|
}
|
|
74559
74630
|
if (expr.filter) {
|
|
74560
|
-
result =
|
|
74631
|
+
result = join31(result, "if" + space);
|
|
74561
74632
|
fragment = this.generateExpression(expr.filter, Precedence.Sequence, E_TTT);
|
|
74562
|
-
result =
|
|
74633
|
+
result = join31(result, ["(", fragment, ")"]);
|
|
74563
74634
|
}
|
|
74564
74635
|
if (!extra.moz.comprehensionExpressionStartsWithAssignment) {
|
|
74565
74636
|
fragment = this.generateExpression(expr.body, Precedence.Assignment, E_TTT);
|
|
74566
|
-
result =
|
|
74637
|
+
result = join31(result, fragment);
|
|
74567
74638
|
}
|
|
74568
74639
|
result.push(expr.type === Syntax.GeneratorExpression ? ")" : "]");
|
|
74569
74640
|
return result;
|
|
@@ -74579,8 +74650,8 @@ var require_escodegen = __commonJS({
|
|
|
74579
74650
|
} else {
|
|
74580
74651
|
fragment = this.generateExpression(expr.left, Precedence.Call, E_TTT);
|
|
74581
74652
|
}
|
|
74582
|
-
fragment =
|
|
74583
|
-
fragment =
|
|
74653
|
+
fragment = join31(fragment, expr.of ? "of" : "in");
|
|
74654
|
+
fragment = join31(fragment, this.generateExpression(expr.right, Precedence.Sequence, E_TTT));
|
|
74584
74655
|
return ["for" + space + "(", fragment, ")"];
|
|
74585
74656
|
},
|
|
74586
74657
|
SpreadElement: function(expr, precedence, flags) {
|
|
@@ -102139,8 +102210,8 @@ function findChromePath() {
|
|
|
102139
102210
|
for (const p8 of paths) {
|
|
102140
102211
|
if (p8) {
|
|
102141
102212
|
try {
|
|
102142
|
-
const { existsSync:
|
|
102143
|
-
if (
|
|
102213
|
+
const { existsSync: existsSync28 } = __require("node:fs");
|
|
102214
|
+
if (existsSync28(p8)) return p8;
|
|
102144
102215
|
} catch {
|
|
102145
102216
|
}
|
|
102146
102217
|
}
|
|
@@ -102155,8 +102226,8 @@ function findChromePath() {
|
|
|
102155
102226
|
];
|
|
102156
102227
|
for (const p8 of paths) {
|
|
102157
102228
|
try {
|
|
102158
|
-
const { existsSync:
|
|
102159
|
-
if (
|
|
102229
|
+
const { existsSync: existsSync28 } = __require("node:fs");
|
|
102230
|
+
if (existsSync28(p8)) return p8;
|
|
102160
102231
|
} catch {
|
|
102161
102232
|
}
|
|
102162
102233
|
}
|
|
@@ -102171,8 +102242,8 @@ function findChromePath() {
|
|
|
102171
102242
|
];
|
|
102172
102243
|
for (const p8 of linuxPaths) {
|
|
102173
102244
|
try {
|
|
102174
|
-
const { existsSync:
|
|
102175
|
-
if (
|
|
102245
|
+
const { existsSync: existsSync28 } = __require("node:fs");
|
|
102246
|
+
if (existsSync28(p8)) return p8;
|
|
102176
102247
|
} catch {
|
|
102177
102248
|
}
|
|
102178
102249
|
}
|
|
@@ -103436,11 +103507,305 @@ var init_meta_tool_factory = __esm({
|
|
|
103436
103507
|
}
|
|
103437
103508
|
});
|
|
103438
103509
|
|
|
103510
|
+
// ../agent/src/memory/layered-memory.ts
|
|
103511
|
+
var LayeredMemory, EpisodicLayer, SemanticLayer;
|
|
103512
|
+
var init_layered_memory = __esm({
|
|
103513
|
+
"../agent/src/memory/layered-memory.ts"() {
|
|
103514
|
+
"use strict";
|
|
103515
|
+
LayeredMemory = class {
|
|
103516
|
+
constructor(store) {
|
|
103517
|
+
this.store = store;
|
|
103518
|
+
this.layers = [
|
|
103519
|
+
new EpisodicLayer(store),
|
|
103520
|
+
new SemanticLayer(store)
|
|
103521
|
+
];
|
|
103522
|
+
}
|
|
103523
|
+
layers = [];
|
|
103524
|
+
/** Query all layers and merge results by relevance */
|
|
103525
|
+
async query(input, limit = 10) {
|
|
103526
|
+
const allResults = [];
|
|
103527
|
+
for (const layer of this.layers) {
|
|
103528
|
+
try {
|
|
103529
|
+
const results = await layer.query(input, limit);
|
|
103530
|
+
allResults.push(...results);
|
|
103531
|
+
} catch {
|
|
103532
|
+
}
|
|
103533
|
+
}
|
|
103534
|
+
return allResults.sort((a, b) => b.relevance - a.relevance).slice(0, limit);
|
|
103535
|
+
}
|
|
103536
|
+
/** Get stats about each memory layer */
|
|
103537
|
+
stats() {
|
|
103538
|
+
return [
|
|
103539
|
+
{ name: "Working", type: "working", count: 0 },
|
|
103540
|
+
// managed externally
|
|
103541
|
+
{ name: "Episodic", type: "episodic", count: this.getEpisodicCount() },
|
|
103542
|
+
{ name: "Semantic", type: "semantic", count: this.getSemanticCount() }
|
|
103543
|
+
];
|
|
103544
|
+
}
|
|
103545
|
+
getEpisodicCount() {
|
|
103546
|
+
try {
|
|
103547
|
+
const missions = this.store.listMissions("default");
|
|
103548
|
+
return missions.length;
|
|
103549
|
+
} catch {
|
|
103550
|
+
return 0;
|
|
103551
|
+
}
|
|
103552
|
+
}
|
|
103553
|
+
getSemanticCount() {
|
|
103554
|
+
try {
|
|
103555
|
+
const stats = this.store.getKgStats();
|
|
103556
|
+
return stats.totalEntities + stats.totalFacts;
|
|
103557
|
+
} catch {
|
|
103558
|
+
return 0;
|
|
103559
|
+
}
|
|
103560
|
+
}
|
|
103561
|
+
};
|
|
103562
|
+
EpisodicLayer = class {
|
|
103563
|
+
constructor(store) {
|
|
103564
|
+
this.store = store;
|
|
103565
|
+
}
|
|
103566
|
+
name = "Episodic";
|
|
103567
|
+
type = "episodic";
|
|
103568
|
+
async query(input, limit = 5) {
|
|
103569
|
+
try {
|
|
103570
|
+
const missions = this.store.searchMissions(input, { limit });
|
|
103571
|
+
return missions.map((m) => ({
|
|
103572
|
+
content: `Mission "${m.title}" (${m.type}, ${m.status}): ${(m.objective ?? "").substring(0, 200)}. ${m.findingCount} findings, score: ${m.qualityScore ?? "N/A"}`,
|
|
103573
|
+
source: `mission:${m.id}`,
|
|
103574
|
+
layer: "episodic",
|
|
103575
|
+
relevance: 0.6,
|
|
103576
|
+
timestamp: m.createdAt
|
|
103577
|
+
}));
|
|
103578
|
+
} catch {
|
|
103579
|
+
return [];
|
|
103580
|
+
}
|
|
103581
|
+
}
|
|
103582
|
+
};
|
|
103583
|
+
SemanticLayer = class {
|
|
103584
|
+
constructor(store) {
|
|
103585
|
+
this.store = store;
|
|
103586
|
+
}
|
|
103587
|
+
name = "Semantic";
|
|
103588
|
+
type = "semantic";
|
|
103589
|
+
async query(input, limit = 5) {
|
|
103590
|
+
const results = [];
|
|
103591
|
+
try {
|
|
103592
|
+
const entities = this.store.searchKgEntities(input);
|
|
103593
|
+
for (const e of entities.slice(0, limit)) {
|
|
103594
|
+
results.push({
|
|
103595
|
+
content: `Entity: ${e.canonicalName} (${e.type})${e.aliases?.length ? ` \u2014 aliases: ${e.aliases.join(", ")}` : ""}`,
|
|
103596
|
+
source: `kg:entity:${e.id}`,
|
|
103597
|
+
layer: "semantic",
|
|
103598
|
+
relevance: 0.7
|
|
103599
|
+
});
|
|
103600
|
+
}
|
|
103601
|
+
const facts = this.store.getKgFacts(input.substring(0, 50));
|
|
103602
|
+
for (const f of facts.slice(0, limit)) {
|
|
103603
|
+
results.push({
|
|
103604
|
+
content: `Fact: ${f.predicate} \u2192 ${f.object.substring(0, 150)} (confidence: ${f.confidence})`,
|
|
103605
|
+
source: `kg:fact:${f.sourceMissionId ?? "unknown"}`,
|
|
103606
|
+
layer: "semantic",
|
|
103607
|
+
relevance: 0.8 * (f.confidence || 0.5)
|
|
103608
|
+
});
|
|
103609
|
+
}
|
|
103610
|
+
} catch {
|
|
103611
|
+
}
|
|
103612
|
+
return results;
|
|
103613
|
+
}
|
|
103614
|
+
};
|
|
103615
|
+
}
|
|
103616
|
+
});
|
|
103617
|
+
|
|
103618
|
+
// ../agent/src/integrations/obsidian.ts
|
|
103619
|
+
import { existsSync as existsSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
|
|
103620
|
+
import { join as join8, basename as basename3, relative as relative3 } from "node:path";
|
|
103621
|
+
var ObsidianIntegration;
|
|
103622
|
+
var init_obsidian = __esm({
|
|
103623
|
+
"../agent/src/integrations/obsidian.ts"() {
|
|
103624
|
+
"use strict";
|
|
103625
|
+
ObsidianIntegration = class {
|
|
103626
|
+
constructor(config2) {
|
|
103627
|
+
this.config = config2;
|
|
103628
|
+
this.nestorDir = join8(config2.vaultPath, config2.nestorFolder || "Nestor");
|
|
103629
|
+
if (!existsSync5(this.nestorDir)) {
|
|
103630
|
+
mkdirSync3(this.nestorDir, { recursive: true });
|
|
103631
|
+
}
|
|
103632
|
+
}
|
|
103633
|
+
nestorDir;
|
|
103634
|
+
/** Check if vault exists and is accessible */
|
|
103635
|
+
isAvailable() {
|
|
103636
|
+
return existsSync5(this.config.vaultPath);
|
|
103637
|
+
}
|
|
103638
|
+
/** Write a note to the vault */
|
|
103639
|
+
writeNote(opts) {
|
|
103640
|
+
const folder5 = opts.folder ? join8(this.nestorDir, opts.folder) : this.nestorDir;
|
|
103641
|
+
if (!existsSync5(folder5)) mkdirSync3(folder5, { recursive: true });
|
|
103642
|
+
const filename = this.sanitizeFilename(opts.title) + ".md";
|
|
103643
|
+
const filepath = join8(folder5, filename);
|
|
103644
|
+
const fm = {
|
|
103645
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
103646
|
+
source: "nestor",
|
|
103647
|
+
tags: [...this.config.defaultTags || [], ...opts.tags || []],
|
|
103648
|
+
...opts.frontmatter
|
|
103649
|
+
};
|
|
103650
|
+
const frontmatter = `---
|
|
103651
|
+
${Object.entries(fm).map(
|
|
103652
|
+
([k, v]) => `${k}: ${typeof v === "object" ? JSON.stringify(v) : v}`
|
|
103653
|
+
).join("\n")}
|
|
103654
|
+
---
|
|
103655
|
+
|
|
103656
|
+
`;
|
|
103657
|
+
writeFileSync3(filepath, frontmatter + opts.content);
|
|
103658
|
+
return filepath;
|
|
103659
|
+
}
|
|
103660
|
+
/** Read a note from the vault */
|
|
103661
|
+
readNote(path30) {
|
|
103662
|
+
const fullPath = path30.startsWith("/") || path30.includes(":") ? path30 : join8(this.nestorDir, path30);
|
|
103663
|
+
if (!existsSync5(fullPath)) return null;
|
|
103664
|
+
const raw = readFileSync8(fullPath, "utf-8");
|
|
103665
|
+
const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n\n?([\s\S]*)$/);
|
|
103666
|
+
if (fmMatch) {
|
|
103667
|
+
const frontmatter = {};
|
|
103668
|
+
for (const line of fmMatch[1].split("\n")) {
|
|
103669
|
+
const [key, ...rest] = line.split(": ");
|
|
103670
|
+
if (key) frontmatter[key.trim()] = rest.join(": ").trim();
|
|
103671
|
+
}
|
|
103672
|
+
return { frontmatter, content: fmMatch[2] };
|
|
103673
|
+
}
|
|
103674
|
+
return { frontmatter: {}, content: raw };
|
|
103675
|
+
}
|
|
103676
|
+
/** Search notes in the vault */
|
|
103677
|
+
searchNotes(query, limit = 10) {
|
|
103678
|
+
const results = [];
|
|
103679
|
+
const q = query.toLowerCase();
|
|
103680
|
+
this.walkDir(this.nestorDir, (filepath) => {
|
|
103681
|
+
if (!filepath.endsWith(".md") || results.length >= limit) return;
|
|
103682
|
+
try {
|
|
103683
|
+
const content = readFileSync8(filepath, "utf-8");
|
|
103684
|
+
if (content.toLowerCase().includes(q)) {
|
|
103685
|
+
const relPath = relative3(this.nestorDir, filepath);
|
|
103686
|
+
const title = basename3(filepath, ".md");
|
|
103687
|
+
const idx = content.toLowerCase().indexOf(q);
|
|
103688
|
+
const snippet = content.substring(Math.max(0, idx - 50), idx + 100).replace(/\n/g, " ");
|
|
103689
|
+
results.push({ path: relPath, title, snippet });
|
|
103690
|
+
}
|
|
103691
|
+
} catch {
|
|
103692
|
+
}
|
|
103693
|
+
});
|
|
103694
|
+
return results;
|
|
103695
|
+
}
|
|
103696
|
+
/** List all notes in a folder */
|
|
103697
|
+
listNotes(folder5) {
|
|
103698
|
+
const dir = folder5 ? join8(this.nestorDir, folder5) : this.nestorDir;
|
|
103699
|
+
if (!existsSync5(dir)) return [];
|
|
103700
|
+
const notes = [];
|
|
103701
|
+
this.walkDir(dir, (filepath) => {
|
|
103702
|
+
if (!filepath.endsWith(".md")) return;
|
|
103703
|
+
const stat = statSync3(filepath);
|
|
103704
|
+
notes.push({
|
|
103705
|
+
path: relative3(this.nestorDir, filepath),
|
|
103706
|
+
title: basename3(filepath, ".md"),
|
|
103707
|
+
modified: stat.mtime
|
|
103708
|
+
});
|
|
103709
|
+
});
|
|
103710
|
+
return notes.sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
103711
|
+
}
|
|
103712
|
+
/** Write a mission report to the vault */
|
|
103713
|
+
writeMissionReport(mission) {
|
|
103714
|
+
return this.writeNote({
|
|
103715
|
+
title: mission.title,
|
|
103716
|
+
content: mission.report,
|
|
103717
|
+
folder: `missions/${mission.type}`,
|
|
103718
|
+
tags: ["mission", mission.type],
|
|
103719
|
+
frontmatter: {
|
|
103720
|
+
type: mission.type,
|
|
103721
|
+
quality: mission.evaluation?.overall
|
|
103722
|
+
}
|
|
103723
|
+
});
|
|
103724
|
+
}
|
|
103725
|
+
sanitizeFilename(name) {
|
|
103726
|
+
return name.replace(/[<>:"/\\|?*]/g, "-").substring(0, 100);
|
|
103727
|
+
}
|
|
103728
|
+
walkDir(dir, callback) {
|
|
103729
|
+
if (!existsSync5(dir)) return;
|
|
103730
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
103731
|
+
const fullPath = join8(dir, entry.name);
|
|
103732
|
+
if (entry.isDirectory()) {
|
|
103733
|
+
this.walkDir(fullPath, callback);
|
|
103734
|
+
} else {
|
|
103735
|
+
callback(fullPath);
|
|
103736
|
+
}
|
|
103737
|
+
}
|
|
103738
|
+
}
|
|
103739
|
+
};
|
|
103740
|
+
}
|
|
103741
|
+
});
|
|
103742
|
+
|
|
103743
|
+
// ../agent/src/integrations/n8n.ts
|
|
103744
|
+
var N8nIntegration;
|
|
103745
|
+
var init_n8n = __esm({
|
|
103746
|
+
"../agent/src/integrations/n8n.ts"() {
|
|
103747
|
+
"use strict";
|
|
103748
|
+
N8nIntegration = class {
|
|
103749
|
+
constructor(config2) {
|
|
103750
|
+
this.config = config2;
|
|
103751
|
+
}
|
|
103752
|
+
/** Check if n8n is accessible */
|
|
103753
|
+
async isAvailable() {
|
|
103754
|
+
try {
|
|
103755
|
+
const res = await fetch(`${this.config.baseUrl}/api/v1/workflows`, {
|
|
103756
|
+
headers: this.headers(),
|
|
103757
|
+
signal: AbortSignal.timeout(5e3)
|
|
103758
|
+
});
|
|
103759
|
+
return res.ok;
|
|
103760
|
+
} catch {
|
|
103761
|
+
return false;
|
|
103762
|
+
}
|
|
103763
|
+
}
|
|
103764
|
+
/** Trigger a webhook workflow */
|
|
103765
|
+
async triggerWebhook(webhookPath, data) {
|
|
103766
|
+
const url = `${this.config.baseUrl}/webhook/${webhookPath}`;
|
|
103767
|
+
const res = await fetch(url, {
|
|
103768
|
+
method: "POST",
|
|
103769
|
+
headers: { "Content-Type": "application/json" },
|
|
103770
|
+
body: JSON.stringify(data),
|
|
103771
|
+
signal: AbortSignal.timeout(3e4)
|
|
103772
|
+
});
|
|
103773
|
+
return res.json();
|
|
103774
|
+
}
|
|
103775
|
+
/** List workflows */
|
|
103776
|
+
async listWorkflows() {
|
|
103777
|
+
const res = await fetch(`${this.config.baseUrl}/api/v1/workflows`, {
|
|
103778
|
+
headers: this.headers(),
|
|
103779
|
+
signal: AbortSignal.timeout(1e4)
|
|
103780
|
+
});
|
|
103781
|
+
if (!res.ok) return [];
|
|
103782
|
+
const data = await res.json();
|
|
103783
|
+
return data.data || [];
|
|
103784
|
+
}
|
|
103785
|
+
/** Execute a workflow by ID */
|
|
103786
|
+
async executeWorkflow(id, data) {
|
|
103787
|
+
const res = await fetch(`${this.config.baseUrl}/api/v1/workflows/${id}/execute`, {
|
|
103788
|
+
method: "POST",
|
|
103789
|
+
headers: this.headers(),
|
|
103790
|
+
body: data ? JSON.stringify(data) : void 0,
|
|
103791
|
+
signal: AbortSignal.timeout(6e4)
|
|
103792
|
+
});
|
|
103793
|
+
return res.json();
|
|
103794
|
+
}
|
|
103795
|
+
headers() {
|
|
103796
|
+
const h = { "Content-Type": "application/json" };
|
|
103797
|
+
if (this.config.apiKey) h["X-N8N-API-KEY"] = this.config.apiKey;
|
|
103798
|
+
return h;
|
|
103799
|
+
}
|
|
103800
|
+
};
|
|
103801
|
+
}
|
|
103802
|
+
});
|
|
103803
|
+
|
|
103439
103804
|
// ../agent/src/tools/system-tools.ts
|
|
103440
103805
|
import { z as z28 } from "zod";
|
|
103441
103806
|
import { randomUUID as randomUUID23 } from "node:crypto";
|
|
103442
|
-
import { readFileSync as
|
|
103443
|
-
import { join as
|
|
103807
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
103808
|
+
import { join as join9 } from "node:path";
|
|
103444
103809
|
import { homedir as homedir3 } from "node:os";
|
|
103445
103810
|
function registerSystemTools(registry, store, defaultTenantId = "default") {
|
|
103446
103811
|
const tid = defaultTenantId;
|
|
@@ -103680,9 +104045,9 @@ Direct sub-agent execution from chat is not yet supported. Use the Studio UI or
|
|
|
103680
104045
|
description: "Read the current Nestor configuration from ~/.nestor/nestor.config.json.",
|
|
103681
104046
|
inputSchema: emptySchema,
|
|
103682
104047
|
handler: async (_input, _ctx) => {
|
|
103683
|
-
const configPath =
|
|
104048
|
+
const configPath = join9(homedir3(), ".nestor", "nestor.config.json");
|
|
103684
104049
|
try {
|
|
103685
|
-
const config2 =
|
|
104050
|
+
const config2 = readFileSync9(configPath, "utf-8");
|
|
103686
104051
|
return { output: config2, isError: false };
|
|
103687
104052
|
} catch {
|
|
103688
104053
|
return { output: "No config file found at ~/.nestor/nestor.config.json", isError: false };
|
|
@@ -103827,11 +104192,363 @@ ${summary}`, isError: false };
|
|
|
103827
104192
|
}
|
|
103828
104193
|
}
|
|
103829
104194
|
});
|
|
104195
|
+
const searchMissionsSchema = z28.object({
|
|
104196
|
+
query: z28.string().describe("Search query (searches titles, objectives, findings)"),
|
|
104197
|
+
type: z28.string().optional().describe("Filter by mission type (osint, research, code, audit, creation, analysis)"),
|
|
104198
|
+
limit: z28.number().optional().describe("Max results (default: 5)")
|
|
104199
|
+
});
|
|
104200
|
+
registry.register({
|
|
104201
|
+
name: "nestor_search_missions",
|
|
104202
|
+
description: "Search past missions by topic, type, or content. Returns matching missions with their findings count and quality scores. Use this to find relevant past work before starting a new mission.",
|
|
104203
|
+
inputSchema: searchMissionsSchema,
|
|
104204
|
+
handler: async (input, _ctx) => {
|
|
104205
|
+
const args2 = input;
|
|
104206
|
+
try {
|
|
104207
|
+
const results = store.searchMissions(args2.query, {
|
|
104208
|
+
type: args2.type,
|
|
104209
|
+
limit: args2.limit || 5
|
|
104210
|
+
});
|
|
104211
|
+
if (results.length === 0) {
|
|
104212
|
+
return { output: `No missions found matching "${args2.query}".`, isError: false };
|
|
104213
|
+
}
|
|
104214
|
+
const summary = results.map((m) => {
|
|
104215
|
+
const quality = m.qualityScore !== null ? ` \u2014 quality: ${(m.qualityScore * 100).toFixed(0)}%` : "";
|
|
104216
|
+
return `\u2022 [${m.status}] ${m.title} (${m.type}) \u2014 ${m.findingCount} findings, $${m.spentUsd.toFixed(4)}${quality}
|
|
104217
|
+
ID: ${m.id}
|
|
104218
|
+
Objective: ${m.objective.substring(0, 200)}`;
|
|
104219
|
+
}).join("\n\n");
|
|
104220
|
+
return {
|
|
104221
|
+
output: `Found ${results.length} mission(s) matching "${args2.query}":
|
|
104222
|
+
|
|
104223
|
+
${summary}`,
|
|
104224
|
+
isError: false
|
|
104225
|
+
};
|
|
104226
|
+
} catch (err) {
|
|
104227
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104228
|
+
return { output: `Error searching missions: ${msg}`, isError: true };
|
|
104229
|
+
}
|
|
104230
|
+
}
|
|
104231
|
+
});
|
|
104232
|
+
registry.register({
|
|
104233
|
+
name: "nestor_get_mission_report",
|
|
104234
|
+
description: "Get the full report of a specific past mission by ID. Returns the markdown report, evaluation scores, and key findings.",
|
|
104235
|
+
inputSchema: missionIdSchema,
|
|
104236
|
+
handler: async (input, _ctx) => {
|
|
104237
|
+
const args2 = input;
|
|
104238
|
+
try {
|
|
104239
|
+
const mission = store.getMission(args2.missionId);
|
|
104240
|
+
if (!mission) {
|
|
104241
|
+
return { output: `Mission "${args2.missionId}" not found.`, isError: true };
|
|
104242
|
+
}
|
|
104243
|
+
let output = `# Mission: ${mission.title}
|
|
104244
|
+
`;
|
|
104245
|
+
output += `Type: ${mission.type} | Status: ${mission.status}
|
|
104246
|
+
`;
|
|
104247
|
+
output += `Objective: ${mission.objective}
|
|
104248
|
+
`;
|
|
104249
|
+
output += `Cost: $${mission.spentUsd.toFixed(4)} | Findings: ${Array.isArray(mission.findings) ? mission.findings.length : 0}
|
|
104250
|
+
`;
|
|
104251
|
+
if (mission.evaluation && typeof mission.evaluation === "object") {
|
|
104252
|
+
const eval_ = mission.evaluation;
|
|
104253
|
+
output += `
|
|
104254
|
+
## Evaluation
|
|
104255
|
+
`;
|
|
104256
|
+
if (typeof eval_.overall === "number") output += `Overall: ${(eval_.overall * 100).toFixed(0)}%
|
|
104257
|
+
`;
|
|
104258
|
+
if (typeof eval_.completeness === "number") output += `Completeness: ${(eval_.completeness * 100).toFixed(0)}%
|
|
104259
|
+
`;
|
|
104260
|
+
if (typeof eval_.accuracy === "number") output += `Accuracy: ${(eval_.accuracy * 100).toFixed(0)}%
|
|
104261
|
+
`;
|
|
104262
|
+
if (typeof eval_.depth === "number") output += `Depth: ${(eval_.depth * 100).toFixed(0)}%
|
|
104263
|
+
`;
|
|
104264
|
+
if (typeof eval_.relevance === "number") output += `Relevance: ${(eval_.relevance * 100).toFixed(0)}%
|
|
104265
|
+
`;
|
|
104266
|
+
}
|
|
104267
|
+
if (mission.report) {
|
|
104268
|
+
output += `
|
|
104269
|
+
## Report
|
|
104270
|
+
${mission.report}`;
|
|
104271
|
+
} else {
|
|
104272
|
+
output += `
|
|
104273
|
+
## Findings Summary
|
|
104274
|
+
`;
|
|
104275
|
+
const findings = Array.isArray(mission.findings) ? mission.findings : [];
|
|
104276
|
+
for (const f of findings.slice(0, 20)) {
|
|
104277
|
+
const finding = f;
|
|
104278
|
+
output += `- [${finding.type || "finding"}] ${finding.title || "Untitled"}: ${String(finding.content || "").substring(0, 300)}
|
|
104279
|
+
`;
|
|
104280
|
+
}
|
|
104281
|
+
if (findings.length > 20) {
|
|
104282
|
+
output += `
|
|
104283
|
+
... and ${findings.length - 20} more findings.`;
|
|
104284
|
+
}
|
|
104285
|
+
}
|
|
104286
|
+
return { output, isError: false };
|
|
104287
|
+
} catch (err) {
|
|
104288
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104289
|
+
return { output: `Error getting mission report: ${msg}`, isError: true };
|
|
104290
|
+
}
|
|
104291
|
+
}
|
|
104292
|
+
});
|
|
104293
|
+
registry.register({
|
|
104294
|
+
name: "nestor_query_memory",
|
|
104295
|
+
description: "Query all memory layers (episodic: past missions, semantic: knowledge graph) for relevant information. Returns merged results sorted by relevance.",
|
|
104296
|
+
inputSchema: queryMemorySchema,
|
|
104297
|
+
handler: async (input, _ctx) => {
|
|
104298
|
+
const args2 = input;
|
|
104299
|
+
try {
|
|
104300
|
+
const memory = new LayeredMemory(store);
|
|
104301
|
+
const results = await memory.query(String(args2.query), 10);
|
|
104302
|
+
const layerStats = memory.stats();
|
|
104303
|
+
return {
|
|
104304
|
+
output: JSON.stringify({ results, layers: layerStats }, null, 2),
|
|
104305
|
+
isError: false
|
|
104306
|
+
};
|
|
104307
|
+
} catch (err) {
|
|
104308
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104309
|
+
return { output: `Memory query failed: ${msg}`, isError: true };
|
|
104310
|
+
}
|
|
104311
|
+
}
|
|
104312
|
+
});
|
|
104313
|
+
function loadIntegrationConfig() {
|
|
104314
|
+
const configPath = join9(homedir3(), ".nestor", "nestor.config.json");
|
|
104315
|
+
try {
|
|
104316
|
+
const raw = readFileSync9(configPath, "utf-8");
|
|
104317
|
+
const config2 = JSON.parse(raw);
|
|
104318
|
+
return {
|
|
104319
|
+
obsidian: config2.obsidian,
|
|
104320
|
+
n8n: config2.n8n
|
|
104321
|
+
};
|
|
104322
|
+
} catch {
|
|
104323
|
+
return {};
|
|
104324
|
+
}
|
|
104325
|
+
}
|
|
104326
|
+
const obsidianWriteNoteSchema = z28.object({
|
|
104327
|
+
title: z28.string().describe("Note title (used as filename)"),
|
|
104328
|
+
content: z28.string().describe("Markdown content of the note"),
|
|
104329
|
+
folder: z28.string().optional().describe('Subfolder within Nestor dir (e.g., "research", "missions/osint")'),
|
|
104330
|
+
tags: z28.array(z28.string()).optional().describe("Tags to add to the note frontmatter")
|
|
104331
|
+
});
|
|
104332
|
+
const obsidianSearchSchema = z28.object({
|
|
104333
|
+
query: z28.string().describe("Search query (full-text search across note titles and content)"),
|
|
104334
|
+
limit: z28.number().optional().describe("Max results (default: 10)")
|
|
104335
|
+
});
|
|
104336
|
+
const obsidianReadNoteSchema = z28.object({
|
|
104337
|
+
path: z28.string().describe('Relative path within the Nestor folder (e.g., "research/AI Frameworks.md") or absolute path')
|
|
104338
|
+
});
|
|
104339
|
+
const obsidianListNotesSchema = z28.object({
|
|
104340
|
+
folder: z28.string().optional().describe('Subfolder to list (e.g., "missions"). Omit for root Nestor folder.')
|
|
104341
|
+
});
|
|
104342
|
+
registry.register({
|
|
104343
|
+
name: "obsidian_write_note",
|
|
104344
|
+
description: 'Write a note to the connected Obsidian vault. Creates a .md file with YAML frontmatter in the Nestor subfolder. Configure vault path in nestor.config.json under "obsidian.vaultPath".',
|
|
104345
|
+
inputSchema: obsidianWriteNoteSchema,
|
|
104346
|
+
handler: async (input, _ctx) => {
|
|
104347
|
+
const args2 = input;
|
|
104348
|
+
const cfg = loadIntegrationConfig();
|
|
104349
|
+
if (!cfg.obsidian?.vaultPath) {
|
|
104350
|
+
return {
|
|
104351
|
+
output: 'Obsidian integration not configured. Add "obsidian": { "vaultPath": "/path/to/vault" } to ~/.nestor/nestor.config.json',
|
|
104352
|
+
isError: true
|
|
104353
|
+
};
|
|
104354
|
+
}
|
|
104355
|
+
try {
|
|
104356
|
+
const obsidian = new ObsidianIntegration(cfg.obsidian);
|
|
104357
|
+
if (!obsidian.isAvailable()) {
|
|
104358
|
+
return { output: `Obsidian vault not found at: ${cfg.obsidian.vaultPath}`, isError: true };
|
|
104359
|
+
}
|
|
104360
|
+
const filepath = obsidian.writeNote({
|
|
104361
|
+
title: args2.title,
|
|
104362
|
+
content: args2.content,
|
|
104363
|
+
folder: args2.folder,
|
|
104364
|
+
tags: args2.tags
|
|
104365
|
+
});
|
|
104366
|
+
return { output: `Note written: ${filepath}`, isError: false };
|
|
104367
|
+
} catch (err) {
|
|
104368
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104369
|
+
return { output: `Failed to write note: ${msg}`, isError: true };
|
|
104370
|
+
}
|
|
104371
|
+
}
|
|
104372
|
+
});
|
|
104373
|
+
registry.register({
|
|
104374
|
+
name: "obsidian_search",
|
|
104375
|
+
description: "Search notes in the connected Obsidian vault. Full-text search across all .md files in the Nestor subfolder.",
|
|
104376
|
+
inputSchema: obsidianSearchSchema,
|
|
104377
|
+
handler: async (input, _ctx) => {
|
|
104378
|
+
const args2 = input;
|
|
104379
|
+
const cfg = loadIntegrationConfig();
|
|
104380
|
+
if (!cfg.obsidian?.vaultPath) {
|
|
104381
|
+
return {
|
|
104382
|
+
output: 'Obsidian integration not configured. Add "obsidian": { "vaultPath": "/path/to/vault" } to ~/.nestor/nestor.config.json',
|
|
104383
|
+
isError: true
|
|
104384
|
+
};
|
|
104385
|
+
}
|
|
104386
|
+
try {
|
|
104387
|
+
const obsidian = new ObsidianIntegration(cfg.obsidian);
|
|
104388
|
+
if (!obsidian.isAvailable()) {
|
|
104389
|
+
return { output: `Obsidian vault not found at: ${cfg.obsidian.vaultPath}`, isError: true };
|
|
104390
|
+
}
|
|
104391
|
+
const results = obsidian.searchNotes(args2.query, args2.limit ?? 10);
|
|
104392
|
+
if (results.length === 0) {
|
|
104393
|
+
return { output: `No notes found matching "${args2.query}".`, isError: false };
|
|
104394
|
+
}
|
|
104395
|
+
return { output: JSON.stringify(results, null, 2), isError: false };
|
|
104396
|
+
} catch (err) {
|
|
104397
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104398
|
+
return { output: `Search failed: ${msg}`, isError: true };
|
|
104399
|
+
}
|
|
104400
|
+
}
|
|
104401
|
+
});
|
|
104402
|
+
registry.register({
|
|
104403
|
+
name: "obsidian_read_note",
|
|
104404
|
+
description: "Read a note from the connected Obsidian vault. Returns frontmatter and content.",
|
|
104405
|
+
inputSchema: obsidianReadNoteSchema,
|
|
104406
|
+
handler: async (input, _ctx) => {
|
|
104407
|
+
const args2 = input;
|
|
104408
|
+
const cfg = loadIntegrationConfig();
|
|
104409
|
+
if (!cfg.obsidian?.vaultPath) {
|
|
104410
|
+
return {
|
|
104411
|
+
output: 'Obsidian integration not configured. Add "obsidian": { "vaultPath": "/path/to/vault" } to ~/.nestor/nestor.config.json',
|
|
104412
|
+
isError: true
|
|
104413
|
+
};
|
|
104414
|
+
}
|
|
104415
|
+
try {
|
|
104416
|
+
const obsidian = new ObsidianIntegration(cfg.obsidian);
|
|
104417
|
+
const note2 = obsidian.readNote(args2.path);
|
|
104418
|
+
if (!note2) {
|
|
104419
|
+
return { output: `Note not found: ${args2.path}`, isError: true };
|
|
104420
|
+
}
|
|
104421
|
+
return { output: JSON.stringify(note2, null, 2), isError: false };
|
|
104422
|
+
} catch (err) {
|
|
104423
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104424
|
+
return { output: `Failed to read note: ${msg}`, isError: true };
|
|
104425
|
+
}
|
|
104426
|
+
}
|
|
104427
|
+
});
|
|
104428
|
+
registry.register({
|
|
104429
|
+
name: "obsidian_list_notes",
|
|
104430
|
+
description: "List all notes in the Obsidian vault Nestor folder, sorted by most recently modified.",
|
|
104431
|
+
inputSchema: obsidianListNotesSchema,
|
|
104432
|
+
handler: async (input, _ctx) => {
|
|
104433
|
+
const args2 = input;
|
|
104434
|
+
const cfg = loadIntegrationConfig();
|
|
104435
|
+
if (!cfg.obsidian?.vaultPath) {
|
|
104436
|
+
return {
|
|
104437
|
+
output: 'Obsidian integration not configured. Add "obsidian": { "vaultPath": "/path/to/vault" } to ~/.nestor/nestor.config.json',
|
|
104438
|
+
isError: true
|
|
104439
|
+
};
|
|
104440
|
+
}
|
|
104441
|
+
try {
|
|
104442
|
+
const obsidian = new ObsidianIntegration(cfg.obsidian);
|
|
104443
|
+
if (!obsidian.isAvailable()) {
|
|
104444
|
+
return { output: `Obsidian vault not found at: ${cfg.obsidian.vaultPath}`, isError: true };
|
|
104445
|
+
}
|
|
104446
|
+
const notes = obsidian.listNotes(args2.folder);
|
|
104447
|
+
if (notes.length === 0) {
|
|
104448
|
+
return { output: "No notes found.", isError: false };
|
|
104449
|
+
}
|
|
104450
|
+
const list = notes.map((n) => `- ${n.title} (${n.path}) \u2014 modified: ${n.modified.toISOString()}`).join("\n");
|
|
104451
|
+
return { output: `Found ${notes.length} note(s):
|
|
104452
|
+
${list}`, isError: false };
|
|
104453
|
+
} catch (err) {
|
|
104454
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104455
|
+
return { output: `Failed to list notes: ${msg}`, isError: true };
|
|
104456
|
+
}
|
|
104457
|
+
}
|
|
104458
|
+
});
|
|
104459
|
+
const n8nTriggerSchema = z28.object({
|
|
104460
|
+
webhook: z28.string().describe('Webhook path (e.g., "nestor-report" \u2014 will call {baseUrl}/webhook/nestor-report)'),
|
|
104461
|
+
data: z28.record(z28.unknown()).optional().describe("JSON data to send to the webhook")
|
|
104462
|
+
});
|
|
104463
|
+
const n8nExecuteWorkflowSchema = z28.object({
|
|
104464
|
+
workflowId: z28.string().describe("n8n workflow ID to execute"),
|
|
104465
|
+
data: z28.record(z28.unknown()).optional().describe("Input data for the workflow")
|
|
104466
|
+
});
|
|
104467
|
+
registry.register({
|
|
104468
|
+
name: "n8n_trigger",
|
|
104469
|
+
description: 'Trigger an n8n webhook workflow. Sends JSON data to a webhook endpoint. Configure n8n in nestor.config.json under "n8n": { "baseUrl": "http://localhost:5678", "apiKey": "..." }.',
|
|
104470
|
+
inputSchema: n8nTriggerSchema,
|
|
104471
|
+
handler: async (input, _ctx) => {
|
|
104472
|
+
const args2 = input;
|
|
104473
|
+
const cfg = loadIntegrationConfig();
|
|
104474
|
+
if (!cfg.n8n?.baseUrl) {
|
|
104475
|
+
return {
|
|
104476
|
+
output: 'n8n integration not configured. Add "n8n": { "baseUrl": "http://localhost:5678" } to ~/.nestor/nestor.config.json',
|
|
104477
|
+
isError: true
|
|
104478
|
+
};
|
|
104479
|
+
}
|
|
104480
|
+
try {
|
|
104481
|
+
const n8n = new N8nIntegration(cfg.n8n);
|
|
104482
|
+
const result = await n8n.triggerWebhook(args2.webhook, args2.data || {});
|
|
104483
|
+
return { output: JSON.stringify(result, null, 2), isError: false };
|
|
104484
|
+
} catch (err) {
|
|
104485
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104486
|
+
return { output: `n8n webhook trigger failed: ${msg}`, isError: true };
|
|
104487
|
+
}
|
|
104488
|
+
}
|
|
104489
|
+
});
|
|
104490
|
+
registry.register({
|
|
104491
|
+
name: "n8n_list_workflows",
|
|
104492
|
+
description: "List available n8n workflows with their ID, name, and active status.",
|
|
104493
|
+
inputSchema: emptySchema,
|
|
104494
|
+
handler: async (_input, _ctx) => {
|
|
104495
|
+
const cfg = loadIntegrationConfig();
|
|
104496
|
+
if (!cfg.n8n?.baseUrl) {
|
|
104497
|
+
return {
|
|
104498
|
+
output: 'n8n integration not configured. Add "n8n": { "baseUrl": "http://localhost:5678" } to ~/.nestor/nestor.config.json',
|
|
104499
|
+
isError: true
|
|
104500
|
+
};
|
|
104501
|
+
}
|
|
104502
|
+
try {
|
|
104503
|
+
const n8n = new N8nIntegration(cfg.n8n);
|
|
104504
|
+
const available = await n8n.isAvailable();
|
|
104505
|
+
if (!available) {
|
|
104506
|
+
return { output: `n8n is not reachable at: ${cfg.n8n.baseUrl}`, isError: true };
|
|
104507
|
+
}
|
|
104508
|
+
const workflows = await n8n.listWorkflows();
|
|
104509
|
+
if (workflows.length === 0) {
|
|
104510
|
+
return { output: "No workflows found.", isError: false };
|
|
104511
|
+
}
|
|
104512
|
+
const list = workflows.map((w) => `- [${w.active ? "ACTIVE" : "inactive"}] ${w.name} (id: ${w.id})`).join("\n");
|
|
104513
|
+
return { output: `Found ${workflows.length} workflow(s):
|
|
104514
|
+
${list}`, isError: false };
|
|
104515
|
+
} catch (err) {
|
|
104516
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104517
|
+
return { output: `Failed to list n8n workflows: ${msg}`, isError: true };
|
|
104518
|
+
}
|
|
104519
|
+
}
|
|
104520
|
+
});
|
|
104521
|
+
registry.register({
|
|
104522
|
+
name: "n8n_execute_workflow",
|
|
104523
|
+
description: "Execute an n8n workflow by its ID. Optionally pass input data.",
|
|
104524
|
+
inputSchema: n8nExecuteWorkflowSchema,
|
|
104525
|
+
handler: async (input, _ctx) => {
|
|
104526
|
+
const args2 = input;
|
|
104527
|
+
const cfg = loadIntegrationConfig();
|
|
104528
|
+
if (!cfg.n8n?.baseUrl) {
|
|
104529
|
+
return {
|
|
104530
|
+
output: 'n8n integration not configured. Add "n8n": { "baseUrl": "http://localhost:5678" } to ~/.nestor/nestor.config.json',
|
|
104531
|
+
isError: true
|
|
104532
|
+
};
|
|
104533
|
+
}
|
|
104534
|
+
try {
|
|
104535
|
+
const n8n = new N8nIntegration(cfg.n8n);
|
|
104536
|
+
const result = await n8n.executeWorkflow(args2.workflowId, args2.data);
|
|
104537
|
+
return { output: JSON.stringify(result, null, 2), isError: false };
|
|
104538
|
+
} catch (err) {
|
|
104539
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104540
|
+
return { output: `n8n workflow execution failed: ${msg}`, isError: true };
|
|
104541
|
+
}
|
|
104542
|
+
}
|
|
104543
|
+
});
|
|
103830
104544
|
}
|
|
103831
|
-
var emptySchema, createAgentSchema, runAgentSchema, deleteAgentSchema, createWorkflowSchema, searchMemorySchema, storeMemorySchema, createGuardrailSchema, createMissionSchema, missionIdSchema;
|
|
104545
|
+
var emptySchema, createAgentSchema, runAgentSchema, deleteAgentSchema, createWorkflowSchema, searchMemorySchema, storeMemorySchema, createGuardrailSchema, createMissionSchema, missionIdSchema, queryMemorySchema;
|
|
103832
104546
|
var init_system_tools = __esm({
|
|
103833
104547
|
"../agent/src/tools/system-tools.ts"() {
|
|
103834
104548
|
"use strict";
|
|
104549
|
+
init_layered_memory();
|
|
104550
|
+
init_obsidian();
|
|
104551
|
+
init_n8n();
|
|
103835
104552
|
emptySchema = z28.object({}).passthrough();
|
|
103836
104553
|
createAgentSchema = z28.object({
|
|
103837
104554
|
name: z28.string().describe("Agent name"),
|
|
@@ -103879,6 +104596,9 @@ var init_system_tools = __esm({
|
|
|
103879
104596
|
missionIdSchema = z28.object({
|
|
103880
104597
|
missionId: z28.string().describe("The mission ID")
|
|
103881
104598
|
});
|
|
104599
|
+
queryMemorySchema = z28.object({
|
|
104600
|
+
query: z28.string().describe("Natural language query to search across all memory layers (episodic: past missions, semantic: knowledge graph)")
|
|
104601
|
+
});
|
|
103882
104602
|
}
|
|
103883
104603
|
});
|
|
103884
104604
|
|
|
@@ -103886,7 +104606,7 @@ var init_system_tools = __esm({
|
|
|
103886
104606
|
import { z as z29 } from "zod";
|
|
103887
104607
|
import { promises as fs14 } from "node:fs";
|
|
103888
104608
|
import { execFile as execFile6 } from "node:child_process";
|
|
103889
|
-
import { resolve as resolve10, join as
|
|
104609
|
+
import { resolve as resolve10, join as join10 } from "node:path";
|
|
103890
104610
|
function buildSandboxConfig(context3) {
|
|
103891
104611
|
const s = context3.sandbox;
|
|
103892
104612
|
return {
|
|
@@ -104167,11 +104887,11 @@ async function fileListHandler(input, context3) {
|
|
|
104167
104887
|
const items = await fs14.readdir(dir, { withFileTypes: true });
|
|
104168
104888
|
for (const item of items) {
|
|
104169
104889
|
if (item.name.startsWith(".") || item.name === "node_modules") continue;
|
|
104170
|
-
const relativePath =
|
|
104890
|
+
const relativePath = join10(dir, item.name).replace(absolutePath, "").replace(/^[/\\]/, "");
|
|
104171
104891
|
if (item.isDirectory()) {
|
|
104172
104892
|
entries.push(`${relativePath}/`);
|
|
104173
104893
|
if (recursive) {
|
|
104174
|
-
await listDir(
|
|
104894
|
+
await listDir(join10(dir, item.name), depth + 1);
|
|
104175
104895
|
}
|
|
104176
104896
|
} else {
|
|
104177
104897
|
entries.push(relativePath);
|
|
@@ -104264,32 +104984,19 @@ async function webFetchHandler(input, context3) {
|
|
|
104264
104984
|
return { output: `Fetch failed: ${message}`, isError: true };
|
|
104265
104985
|
}
|
|
104266
104986
|
}
|
|
104267
|
-
|
|
104268
|
-
|
|
104269
|
-
|
|
104270
|
-
|
|
104271
|
-
|
|
104272
|
-
|
|
104273
|
-
|
|
104274
|
-
|
|
104275
|
-
break;
|
|
104276
|
-
case "bing":
|
|
104277
|
-
searchUrl = `https://www.bing.com/search?q=${encodeURIComponent(query)}&count=${max}`;
|
|
104278
|
-
break;
|
|
104279
|
-
case "duckduckgo":
|
|
104280
|
-
default:
|
|
104281
|
-
searchUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
|
|
104282
|
-
break;
|
|
104987
|
+
function pickUserAgent(attempt = 0) {
|
|
104988
|
+
return SEARCH_USER_AGENTS[attempt % SEARCH_USER_AGENTS.length];
|
|
104989
|
+
}
|
|
104990
|
+
async function fetchSearchHtml(searchUrl, context3, userAgent, timeoutMs = 15e3) {
|
|
104991
|
+
const controller = new AbortController();
|
|
104992
|
+
const timer2 = setTimeout(() => controller.abort(), timeoutMs);
|
|
104993
|
+
if (context3.signal) {
|
|
104994
|
+
context3.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
104283
104995
|
}
|
|
104284
104996
|
try {
|
|
104285
|
-
const controller = new AbortController();
|
|
104286
|
-
const timer2 = setTimeout(() => controller.abort(), 15e3);
|
|
104287
|
-
if (context3.signal) {
|
|
104288
|
-
context3.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
104289
|
-
}
|
|
104290
104997
|
const resp = await fetch(searchUrl, {
|
|
104291
104998
|
headers: {
|
|
104292
|
-
"User-Agent":
|
|
104999
|
+
"User-Agent": userAgent,
|
|
104293
105000
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
104294
105001
|
"Accept-Language": "en-US,en;q=0.9,fr;q=0.8"
|
|
104295
105002
|
},
|
|
@@ -104297,96 +105004,217 @@ async function webSearchHandler(input, context3) {
|
|
|
104297
105004
|
redirect: "follow"
|
|
104298
105005
|
});
|
|
104299
105006
|
clearTimeout(timer2);
|
|
104300
|
-
if (!resp.ok) {
|
|
104301
|
-
return { output: `Search failed: HTTP ${resp.status}. Try a different search engine with engine:"google" or engine:"bing".`, isError: true };
|
|
104302
|
-
}
|
|
104303
105007
|
const html = await resp.text();
|
|
104304
|
-
|
|
104305
|
-
|
|
104306
|
-
|
|
104307
|
-
|
|
104308
|
-
|
|
104309
|
-
|
|
104310
|
-
|
|
104311
|
-
|
|
104312
|
-
|
|
104313
|
-
|
|
104314
|
-
|
|
104315
|
-
|
|
104316
|
-
|
|
104317
|
-
|
|
104318
|
-
|
|
104319
|
-
|
|
104320
|
-
|
|
104321
|
-
|
|
104322
|
-
|
|
104323
|
-
|
|
104324
|
-
|
|
104325
|
-
|
|
104326
|
-
|
|
104327
|
-
|
|
104328
|
-
|
|
104329
|
-
|
|
104330
|
-
|
|
104331
|
-
|
|
104332
|
-
|
|
104333
|
-
|
|
104334
|
-
|
|
104335
|
-
|
|
104336
|
-
|
|
104337
|
-
|
|
104338
|
-
|
|
104339
|
-
|
|
104340
|
-
|
|
104341
|
-
|
|
104342
|
-
|
|
104343
|
-
|
|
104344
|
-
|
|
104345
|
-
|
|
104346
|
-
|
|
104347
|
-
|
|
104348
|
-
|
|
104349
|
-
|
|
104350
|
-
|
|
104351
|
-
|
|
105008
|
+
return { ok: resp.ok, status: resp.status, html };
|
|
105009
|
+
} catch {
|
|
105010
|
+
clearTimeout(timer2);
|
|
105011
|
+
return { ok: false, status: 0, html: "" };
|
|
105012
|
+
}
|
|
105013
|
+
}
|
|
105014
|
+
function parseDuckDuckGo(html, max) {
|
|
105015
|
+
const results = [];
|
|
105016
|
+
const linkRegex = /<a[^>]*class="result__a"[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi;
|
|
105017
|
+
const snippetRegex = /<a[^>]*class="result__snippet"[^>]*>(.*?)<\/a>/gi;
|
|
105018
|
+
let linkMatch;
|
|
105019
|
+
const links = [];
|
|
105020
|
+
while ((linkMatch = linkRegex.exec(html)) !== null && links.length < max) {
|
|
105021
|
+
let url = linkMatch[1];
|
|
105022
|
+
const uddg = url.match(/uddg=([^&]*)/);
|
|
105023
|
+
if (uddg) url = decodeURIComponent(uddg[1]);
|
|
105024
|
+
const title = linkMatch[2].replace(/<[^>]*>/g, "").trim();
|
|
105025
|
+
if (url.startsWith("http") && !url.includes("duckduckgo.com/y.js") && !url.includes("ad_domain=")) {
|
|
105026
|
+
links.push({ url, title });
|
|
105027
|
+
}
|
|
105028
|
+
}
|
|
105029
|
+
let snippetMatch;
|
|
105030
|
+
const snippets = [];
|
|
105031
|
+
while ((snippetMatch = snippetRegex.exec(html)) !== null) {
|
|
105032
|
+
snippets.push(snippetMatch[1].replace(/<[^>]*>/g, "").trim());
|
|
105033
|
+
}
|
|
105034
|
+
for (let i = 0; i < links.length; i++) {
|
|
105035
|
+
results.push({
|
|
105036
|
+
title: links[i].title,
|
|
105037
|
+
url: links[i].url,
|
|
105038
|
+
snippet: snippets[i] || ""
|
|
105039
|
+
});
|
|
105040
|
+
}
|
|
105041
|
+
return results;
|
|
105042
|
+
}
|
|
105043
|
+
function parseGoogle(html, max) {
|
|
105044
|
+
const results = [];
|
|
105045
|
+
const googleRegex = /<a[^>]*href="(https?:\/\/[^"]*)"[^>]*><h3[^>]*>([^<]+)<\/h3>/gi;
|
|
105046
|
+
let gMatch;
|
|
105047
|
+
while ((gMatch = googleRegex.exec(html)) !== null && results.length < max) {
|
|
105048
|
+
const url = gMatch[1];
|
|
105049
|
+
const title = gMatch[2].trim();
|
|
105050
|
+
if (title.length > 3 && !url.includes("google.com")) {
|
|
105051
|
+
results.push({ title, url, snippet: "" });
|
|
105052
|
+
}
|
|
105053
|
+
}
|
|
105054
|
+
if (results.length > 0) {
|
|
105055
|
+
const snippetRegex = /<div[^>]*class="[^"]*VwiC3b[^"]*"[^>]*>([\s\S]*?)<\/div>/gi;
|
|
105056
|
+
let sMatch;
|
|
105057
|
+
let idx = 0;
|
|
105058
|
+
while ((sMatch = snippetRegex.exec(html)) !== null && idx < results.length) {
|
|
105059
|
+
const snippet = sMatch[1].replace(/<[^>]*>/g, "").trim();
|
|
105060
|
+
if (snippet.length > 10) {
|
|
105061
|
+
results[idx].snippet = snippet;
|
|
105062
|
+
idx++;
|
|
104352
105063
|
}
|
|
104353
105064
|
}
|
|
104354
|
-
|
|
104355
|
-
|
|
104356
|
-
|
|
104357
|
-
|
|
104358
|
-
|
|
104359
|
-
|
|
104360
|
-
|
|
104361
|
-
|
|
104362
|
-
|
|
105065
|
+
return results;
|
|
105066
|
+
}
|
|
105067
|
+
const resultBlockRegex = /<div class="[^"]*g[^"]*"[^>]*>[\s\S]*?<a[^>]*href="(https?:\/\/[^"]*)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?(?:<span[^>]*>([\s\S]*?)<\/span>)?/gi;
|
|
105068
|
+
let blockMatch;
|
|
105069
|
+
while ((blockMatch = resultBlockRegex.exec(html)) !== null && results.length < max) {
|
|
105070
|
+
const url = blockMatch[1];
|
|
105071
|
+
const title = blockMatch[2].replace(/<[^>]*>/g, "").trim();
|
|
105072
|
+
const snippet = (blockMatch[3] || "").replace(/<[^>]*>/g, "").trim();
|
|
105073
|
+
if (title.length > 3 && !url.includes("google.com")) {
|
|
105074
|
+
results.push({ title, url, snippet });
|
|
105075
|
+
}
|
|
105076
|
+
}
|
|
105077
|
+
return results;
|
|
105078
|
+
}
|
|
105079
|
+
function parseBing(html, max) {
|
|
105080
|
+
const results = [];
|
|
105081
|
+
const bingRegex = /<li[^>]*class="b_algo"[^>]*>[\s\S]*?<a[^>]*href="([^"]*)"[^>]*>([^<]+)<\/a>[\s\S]*?<p[^>]*>([^<]*)/gi;
|
|
105082
|
+
let bingMatch;
|
|
105083
|
+
while ((bingMatch = bingRegex.exec(html)) !== null && results.length < max) {
|
|
105084
|
+
const url = bingMatch[1];
|
|
105085
|
+
const title = bingMatch[2].replace(/<[^>]*>/g, "").trim();
|
|
105086
|
+
const snippet = bingMatch[3].replace(/<[^>]*>/g, "").trim();
|
|
105087
|
+
if (title.length > 3) {
|
|
105088
|
+
results.push({ title, url, snippet });
|
|
105089
|
+
}
|
|
105090
|
+
}
|
|
105091
|
+
if (results.length === 0) {
|
|
105092
|
+
const fallbackRegex = /<li class="b_algo"[^>]*>[\s\S]*?<a[^>]*href="(https?:\/\/[^"]*)"[^>]*>([\s\S]*?)<\/a>[\s\S]*?<p[^>]*>([\s\S]*?)<\/p>/gi;
|
|
105093
|
+
let fbMatch;
|
|
105094
|
+
while ((fbMatch = fallbackRegex.exec(html)) !== null && results.length < max) {
|
|
105095
|
+
const url = fbMatch[1];
|
|
105096
|
+
const title = fbMatch[2].replace(/<[^>]*>/g, "").trim();
|
|
105097
|
+
const snippet = fbMatch[3].replace(/<[^>]*>/g, "").trim();
|
|
105098
|
+
if (title.length > 3) {
|
|
105099
|
+
results.push({ title, url, snippet });
|
|
104363
105100
|
}
|
|
104364
105101
|
}
|
|
105102
|
+
}
|
|
105103
|
+
return results;
|
|
105104
|
+
}
|
|
105105
|
+
function parseAnyLinks(html, max) {
|
|
105106
|
+
const results = [];
|
|
105107
|
+
const anyLinkRegex = /<a[^>]*href="(https?:\/\/[^"]*)"[^>]*>(.*?)<\/a>/gi;
|
|
105108
|
+
let anyMatch;
|
|
105109
|
+
while ((anyMatch = anyLinkRegex.exec(html)) !== null && results.length < max) {
|
|
105110
|
+
const url = anyMatch[1];
|
|
105111
|
+
const title = anyMatch[2].replace(/<[^>]*>/g, "").trim();
|
|
105112
|
+
if (title.length > 5 && !url.includes("duckduckgo.com") && !url.includes("google.com") && !url.includes("bing.com")) {
|
|
105113
|
+
results.push({ title, url, snippet: "" });
|
|
105114
|
+
}
|
|
105115
|
+
}
|
|
105116
|
+
return results;
|
|
105117
|
+
}
|
|
105118
|
+
async function searchSingleEngine(engineName, query, max, context3) {
|
|
105119
|
+
let searchUrl;
|
|
105120
|
+
let parser2;
|
|
105121
|
+
switch (engineName) {
|
|
105122
|
+
case "google":
|
|
105123
|
+
searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}&num=${max}`;
|
|
105124
|
+
parser2 = parseGoogle;
|
|
105125
|
+
break;
|
|
105126
|
+
case "bing":
|
|
105127
|
+
searchUrl = `https://www.bing.com/search?q=${encodeURIComponent(query)}&count=${max}`;
|
|
105128
|
+
parser2 = parseBing;
|
|
105129
|
+
break;
|
|
105130
|
+
case "duckduckgo":
|
|
105131
|
+
default:
|
|
105132
|
+
searchUrl = `https://html.duckduckgo.com/html/?q=${encodeURIComponent(query)}`;
|
|
105133
|
+
parser2 = parseDuckDuckGo;
|
|
105134
|
+
break;
|
|
105135
|
+
}
|
|
105136
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
105137
|
+
const ua = pickUserAgent(attempt);
|
|
105138
|
+
const { ok, status, html } = await fetchSearchHtml(searchUrl, context3, ua);
|
|
105139
|
+
if (!ok) {
|
|
105140
|
+
if (attempt === 0) continue;
|
|
105141
|
+
return { results: [], engineUsed: engineName, error: `HTTP ${status}` };
|
|
105142
|
+
}
|
|
105143
|
+
let results = parser2(html, max);
|
|
104365
105144
|
if (results.length === 0) {
|
|
105145
|
+
results = parseAnyLinks(html, max);
|
|
105146
|
+
}
|
|
105147
|
+
if (results.length > 0) {
|
|
105148
|
+
return { results, engineUsed: engineName };
|
|
105149
|
+
}
|
|
105150
|
+
}
|
|
105151
|
+
return { results: [], engineUsed: engineName, error: "0 results after UA rotation" };
|
|
105152
|
+
}
|
|
105153
|
+
async function webSearchHandler(input, context3) {
|
|
105154
|
+
const { query, engine, maxResults } = input;
|
|
105155
|
+
const max = maxResults || 5;
|
|
105156
|
+
const searchEngine = engine || "auto";
|
|
105157
|
+
if (searchEngine !== "auto") {
|
|
105158
|
+
try {
|
|
105159
|
+
const { results, error } = await searchSingleEngine(searchEngine, query, max, context3);
|
|
105160
|
+
if (results.length === 0) {
|
|
105161
|
+
return {
|
|
105162
|
+
output: `No results found for "${query}" on ${searchEngine}${error ? ` (${error})` : ""}. Try a different query or use engine:"auto" for multi-engine fallback. You can also try web_fetch directly on a relevant URL.`,
|
|
105163
|
+
isError: false
|
|
105164
|
+
};
|
|
105165
|
+
}
|
|
105166
|
+
const formatted = results.map(
|
|
105167
|
+
(r, i) => `${i + 1}. ${r.title}
|
|
105168
|
+
${r.url}
|
|
105169
|
+
${r.snippet}`
|
|
105170
|
+
).join("\n\n");
|
|
104366
105171
|
return {
|
|
104367
|
-
output: `
|
|
105172
|
+
output: `Search results for "${query}" (${searchEngine}):
|
|
105173
|
+
|
|
105174
|
+
${formatted}
|
|
105175
|
+
|
|
105176
|
+
Use web_fetch on any URL above to get the full page content.`,
|
|
104368
105177
|
isError: false
|
|
104369
105178
|
};
|
|
105179
|
+
} catch (err) {
|
|
105180
|
+
if (err.name === "AbortError") {
|
|
105181
|
+
return { output: `Search timed out for "${query}". Try a simpler query or different engine.`, isError: true };
|
|
105182
|
+
}
|
|
105183
|
+
return { output: `Search error: ${err.message}. Try engine:"auto" for multi-engine fallback.`, isError: true };
|
|
104370
105184
|
}
|
|
104371
|
-
|
|
104372
|
-
|
|
105185
|
+
}
|
|
105186
|
+
const fallbackChain = ["duckduckgo", "google", "bing"];
|
|
105187
|
+
const errors = [];
|
|
105188
|
+
for (const eng of fallbackChain) {
|
|
105189
|
+
try {
|
|
105190
|
+
const { results, engineUsed, error } = await searchSingleEngine(eng, query, max, context3);
|
|
105191
|
+
if (results.length > 0) {
|
|
105192
|
+
const formatted = results.map(
|
|
105193
|
+
(r, i) => `${i + 1}. ${r.title}
|
|
104373
105194
|
${r.url}
|
|
104374
105195
|
${r.snippet}`
|
|
104375
|
-
|
|
104376
|
-
|
|
104377
|
-
|
|
105196
|
+
).join("\n\n");
|
|
105197
|
+
const fallbackNote = eng !== "duckduckgo" ? `
|
|
105198
|
+
|
|
105199
|
+
(Note: Results from ${engineUsed} \u2014 earlier engines returned 0 results)` : "";
|
|
105200
|
+
return {
|
|
105201
|
+
output: `Search results for "${query}" (${engineUsed}):
|
|
104378
105202
|
|
|
104379
105203
|
${formatted}
|
|
104380
105204
|
|
|
104381
|
-
Use web_fetch on any URL above to get the full page content
|
|
104382
|
-
|
|
104383
|
-
|
|
104384
|
-
|
|
104385
|
-
|
|
104386
|
-
|
|
105205
|
+
Use web_fetch on any URL above to get the full page content.${fallbackNote}`,
|
|
105206
|
+
isError: false
|
|
105207
|
+
};
|
|
105208
|
+
}
|
|
105209
|
+
errors.push(`${eng}: ${error || "0 results"}`);
|
|
105210
|
+
} catch (err) {
|
|
105211
|
+
errors.push(`${eng}: ${err.message || "unknown error"}`);
|
|
104387
105212
|
}
|
|
104388
|
-
return { output: `Search error: ${err.message}. Try a different search engine with engine:"google" or engine:"bing".`, isError: true };
|
|
104389
105213
|
}
|
|
105214
|
+
return {
|
|
105215
|
+
output: `All search engines failed for "${query}". Tried: ${errors.join("; ")}. Try web_fetch directly on known URLs (e.g., Wikipedia, company sites, social profiles) instead of searching.`,
|
|
105216
|
+
isError: true
|
|
105217
|
+
};
|
|
104390
105218
|
}
|
|
104391
105219
|
async function messageSendHandler(input, context3) {
|
|
104392
105220
|
const { to, subject, body } = input;
|
|
@@ -104525,7 +105353,7 @@ function registerBrowserTools(registry, config2) {
|
|
|
104525
105353
|
}
|
|
104526
105354
|
return manager;
|
|
104527
105355
|
}
|
|
104528
|
-
var shellExecSchema, sandboxCache, DANGEROUS_METACHARACTERS, shellExecTool, fileReadSchema, fileReadTool, fileWriteSchema, fileWriteTool, fileListSchema, fileListTool, webFetchSchema, webFetchTool, webSearchSchema, webSearchTool, messageSendSchema, messageSendTool, messageReadSchema, messageReadTool, handoffSchema, handoffTool, undoSchema;
|
|
105356
|
+
var shellExecSchema, sandboxCache, DANGEROUS_METACHARACTERS, shellExecTool, fileReadSchema, fileReadTool, fileWriteSchema, fileWriteTool, fileListSchema, fileListTool, webFetchSchema, webFetchTool, webSearchSchema, SEARCH_USER_AGENTS, webSearchTool, messageSendSchema, messageSendTool, messageReadSchema, messageReadTool, handoffSchema, handoffTool, undoSchema;
|
|
104529
105357
|
var init_builtin = __esm({
|
|
104530
105358
|
"../agent/src/tools/builtin.ts"() {
|
|
104531
105359
|
"use strict";
|
|
@@ -104605,12 +105433,17 @@ var init_builtin = __esm({
|
|
|
104605
105433
|
};
|
|
104606
105434
|
webSearchSchema = z29.object({
|
|
104607
105435
|
query: z29.string().describe("The search query"),
|
|
104608
|
-
engine: z29.string().optional().describe(
|
|
105436
|
+
engine: z29.string().optional().describe('Search engine: google, duckduckgo, bing, or "auto" for multi-engine fallback (default). Default: auto'),
|
|
104609
105437
|
maxResults: z29.number().optional().describe("Max results to return. Default: 5")
|
|
104610
105438
|
});
|
|
105439
|
+
SEARCH_USER_AGENTS = [
|
|
105440
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
105441
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
|
|
105442
|
+
"Mozilla/5.0 (X11; Linux x86_64; rv:121.0) Gecko/20100101 Firefox/121.0"
|
|
105443
|
+
];
|
|
104611
105444
|
webSearchTool = {
|
|
104612
105445
|
name: "web_search",
|
|
104613
|
-
description:
|
|
105446
|
+
description: 'Search the internet using DuckDuckGo, Google, or Bing with automatic multi-engine fallback. Returns titles, URLs, and snippets. Use web_fetch on result URLs to read full page content. Default mode "auto" tries DuckDuckGo first, then Google, then Bing if previous engines return 0 results.',
|
|
104614
105447
|
inputSchema: webSearchSchema,
|
|
104615
105448
|
handler: webSearchHandler
|
|
104616
105449
|
};
|
|
@@ -106534,8 +107367,8 @@ var init_store2 = __esm({
|
|
|
106534
107367
|
});
|
|
106535
107368
|
|
|
106536
107369
|
// ../agent/src/memory/files.ts
|
|
106537
|
-
import { readFileSync as
|
|
106538
|
-
import { join as
|
|
107370
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "node:fs";
|
|
107371
|
+
import { join as join11, dirname as dirname5 } from "node:path";
|
|
106539
107372
|
var MemoryFiles;
|
|
106540
107373
|
var init_files = __esm({
|
|
106541
107374
|
"../agent/src/memory/files.ts"() {
|
|
@@ -106543,8 +107376,8 @@ var init_files = __esm({
|
|
|
106543
107376
|
MemoryFiles = class {
|
|
106544
107377
|
constructor(workDir) {
|
|
106545
107378
|
this.workDir = workDir;
|
|
106546
|
-
this.memoryPath =
|
|
106547
|
-
this.userPath =
|
|
107379
|
+
this.memoryPath = join11(workDir, "MEMORY.md");
|
|
107380
|
+
this.userPath = join11(workDir, "USER.md");
|
|
106548
107381
|
}
|
|
106549
107382
|
memoryPath;
|
|
106550
107383
|
userPath;
|
|
@@ -106553,8 +107386,8 @@ var init_files = __esm({
|
|
|
106553
107386
|
*/
|
|
106554
107387
|
readMemoryFile() {
|
|
106555
107388
|
try {
|
|
106556
|
-
if (!
|
|
106557
|
-
return
|
|
107389
|
+
if (!existsSync6(this.memoryPath)) return null;
|
|
107390
|
+
return readFileSync10(this.memoryPath, "utf-8");
|
|
106558
107391
|
} catch {
|
|
106559
107392
|
return null;
|
|
106560
107393
|
}
|
|
@@ -106564,8 +107397,8 @@ var init_files = __esm({
|
|
|
106564
107397
|
*/
|
|
106565
107398
|
readUserFile() {
|
|
106566
107399
|
try {
|
|
106567
|
-
if (!
|
|
106568
|
-
return
|
|
107400
|
+
if (!existsSync6(this.userPath)) return null;
|
|
107401
|
+
return readFileSync10(this.userPath, "utf-8");
|
|
106569
107402
|
} catch {
|
|
106570
107403
|
return null;
|
|
106571
107404
|
}
|
|
@@ -106575,16 +107408,16 @@ var init_files = __esm({
|
|
|
106575
107408
|
*/
|
|
106576
107409
|
appendMemory(observation) {
|
|
106577
107410
|
const dir = dirname5(this.memoryPath);
|
|
106578
|
-
if (!
|
|
106579
|
-
|
|
107411
|
+
if (!existsSync6(dir)) {
|
|
107412
|
+
mkdirSync4(dir, { recursive: true });
|
|
106580
107413
|
}
|
|
106581
107414
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
106582
107415
|
const entry = `
|
|
106583
107416
|
- [${timestamp}] ${observation}
|
|
106584
107417
|
`;
|
|
106585
|
-
if (
|
|
106586
|
-
const existing =
|
|
106587
|
-
|
|
107418
|
+
if (existsSync6(this.memoryPath)) {
|
|
107419
|
+
const existing = readFileSync10(this.memoryPath, "utf-8");
|
|
107420
|
+
writeFileSync4(this.memoryPath, existing + entry, "utf-8");
|
|
106588
107421
|
} else {
|
|
106589
107422
|
const header = `# Agent Memory
|
|
106590
107423
|
|
|
@@ -106592,7 +107425,7 @@ Persistent observations and notes.
|
|
|
106592
107425
|
|
|
106593
107426
|
## Observations
|
|
106594
107427
|
`;
|
|
106595
|
-
|
|
107428
|
+
writeFileSync4(this.memoryPath, header + entry, "utf-8");
|
|
106596
107429
|
}
|
|
106597
107430
|
}
|
|
106598
107431
|
/**
|
|
@@ -106600,8 +107433,8 @@ Persistent observations and notes.
|
|
|
106600
107433
|
*/
|
|
106601
107434
|
updateUserProfile(info) {
|
|
106602
107435
|
const dir = dirname5(this.userPath);
|
|
106603
|
-
if (!
|
|
106604
|
-
|
|
107436
|
+
if (!existsSync6(dir)) {
|
|
107437
|
+
mkdirSync4(dir, { recursive: true });
|
|
106605
107438
|
}
|
|
106606
107439
|
const lines = ["# User Profile\n"];
|
|
106607
107440
|
for (const [key, value] of Object.entries(info)) {
|
|
@@ -106610,7 +107443,7 @@ Persistent observations and notes.
|
|
|
106610
107443
|
}
|
|
106611
107444
|
}
|
|
106612
107445
|
lines.push("");
|
|
106613
|
-
|
|
107446
|
+
writeFileSync4(this.userPath, lines.join("\n"), "utf-8");
|
|
106614
107447
|
}
|
|
106615
107448
|
/**
|
|
106616
107449
|
* Parse MEMORY.md into structured entries.
|
|
@@ -107896,6 +108729,7 @@ var init_memory = __esm({
|
|
|
107896
108729
|
init_vector_store();
|
|
107897
108730
|
init_persistent_vector_store();
|
|
107898
108731
|
init_embedding_provider();
|
|
108732
|
+
init_layered_memory();
|
|
107899
108733
|
AgentMemory = class {
|
|
107900
108734
|
constructor(store, agentId) {
|
|
107901
108735
|
this.store = store;
|
|
@@ -108365,6 +109199,217 @@ ${e.reasoning}${ctx}`;
|
|
|
108365
109199
|
}
|
|
108366
109200
|
});
|
|
108367
109201
|
|
|
109202
|
+
// ../agent/src/safety/context-rotator.ts
|
|
109203
|
+
var ContextRotator;
|
|
109204
|
+
var init_context_rotator = __esm({
|
|
109205
|
+
"../agent/src/safety/context-rotator.ts"() {
|
|
109206
|
+
"use strict";
|
|
109207
|
+
ContextRotator = class {
|
|
109208
|
+
cfg;
|
|
109209
|
+
messageCount = 0;
|
|
109210
|
+
estimatedTokens = 0;
|
|
109211
|
+
constructor(config2 = {}) {
|
|
109212
|
+
this.cfg = {
|
|
109213
|
+
maxMessages: config2.maxMessages ?? 50,
|
|
109214
|
+
maxTokensEstimate: config2.maxTokensEstimate ?? 1e5,
|
|
109215
|
+
charsPerToken: config2.charsPerToken ?? 4,
|
|
109216
|
+
recentToKeep: config2.recentToKeep ?? 10
|
|
109217
|
+
};
|
|
109218
|
+
}
|
|
109219
|
+
// ── Recording ───────────────────────────────────────────────────────
|
|
109220
|
+
/** Record a message added to context. */
|
|
109221
|
+
recordMessage(content) {
|
|
109222
|
+
this.messageCount++;
|
|
109223
|
+
this.estimatedTokens += Math.ceil(content.length / this.cfg.charsPerToken);
|
|
109224
|
+
}
|
|
109225
|
+
// ── Checking ────────────────────────────────────────────────────────
|
|
109226
|
+
/** Check if context should be rotated. */
|
|
109227
|
+
shouldRotate() {
|
|
109228
|
+
if (this.messageCount >= this.cfg.maxMessages) {
|
|
109229
|
+
return {
|
|
109230
|
+
rotate: true,
|
|
109231
|
+
reason: `${this.messageCount} messages (limit: ${this.cfg.maxMessages})`
|
|
109232
|
+
};
|
|
109233
|
+
}
|
|
109234
|
+
if (this.estimatedTokens >= this.cfg.maxTokensEstimate) {
|
|
109235
|
+
return {
|
|
109236
|
+
rotate: true,
|
|
109237
|
+
reason: `~${this.estimatedTokens} tokens (limit: ${this.cfg.maxTokensEstimate})`
|
|
109238
|
+
};
|
|
109239
|
+
}
|
|
109240
|
+
return { rotate: false };
|
|
109241
|
+
}
|
|
109242
|
+
// ── Rotation helpers ────────────────────────────────────────────────
|
|
109243
|
+
/**
|
|
109244
|
+
* Build a compact summary for context rotation.
|
|
109245
|
+
*
|
|
109246
|
+
* Strategy:
|
|
109247
|
+
* - Preserve the system message verbatim
|
|
109248
|
+
* - Summarize the middle messages into a compact block
|
|
109249
|
+
* - Keep the last N recent messages verbatim
|
|
109250
|
+
*
|
|
109251
|
+
* Returns the summary text to inject as a single assistant message
|
|
109252
|
+
* between the system prompt and the recent messages.
|
|
109253
|
+
*/
|
|
109254
|
+
buildRotationSummary(messages) {
|
|
109255
|
+
const keepCount = this.cfg.recentToKeep;
|
|
109256
|
+
const system = messages.find((m) => m.role === "system");
|
|
109257
|
+
const nonSystem = messages.filter((m) => m.role !== "system");
|
|
109258
|
+
const recent = nonSystem.slice(-keepCount);
|
|
109259
|
+
const middle = nonSystem.slice(0, Math.max(0, nonSystem.length - keepCount));
|
|
109260
|
+
const parts = [];
|
|
109261
|
+
if (system) {
|
|
109262
|
+
parts.push("[System prompt preserved]");
|
|
109263
|
+
}
|
|
109264
|
+
if (middle.length > 0) {
|
|
109265
|
+
const toolCalls = middle.filter(
|
|
109266
|
+
(m) => m.content.includes("tool_use") || m.content.includes("function_call") || m.content.includes("tool_result")
|
|
109267
|
+
).length;
|
|
109268
|
+
const findings = middle.filter(
|
|
109269
|
+
(m) => m.content.includes("finding") || m.content.includes("result") || m.content.includes("discovered")
|
|
109270
|
+
).length;
|
|
109271
|
+
const errors = middle.filter(
|
|
109272
|
+
(m) => m.content.includes("error") || m.content.includes("failed")
|
|
109273
|
+
).length;
|
|
109274
|
+
parts.push(
|
|
109275
|
+
`[${middle.length} earlier messages summarized: ${toolCalls} tool interactions, ${findings} results/findings, ${errors} errors]`
|
|
109276
|
+
);
|
|
109277
|
+
}
|
|
109278
|
+
parts.push(`[${recent.length} recent messages preserved below]`);
|
|
109279
|
+
return parts.join("\n");
|
|
109280
|
+
}
|
|
109281
|
+
// ── Lifecycle ───────────────────────────────────────────────────────
|
|
109282
|
+
/** Reset counters after rotation. */
|
|
109283
|
+
reset() {
|
|
109284
|
+
this.messageCount = 0;
|
|
109285
|
+
this.estimatedTokens = 0;
|
|
109286
|
+
}
|
|
109287
|
+
/** Get current stats (useful for logging/telemetry). */
|
|
109288
|
+
getStats() {
|
|
109289
|
+
return {
|
|
109290
|
+
messageCount: this.messageCount,
|
|
109291
|
+
estimatedTokens: this.estimatedTokens
|
|
109292
|
+
};
|
|
109293
|
+
}
|
|
109294
|
+
};
|
|
109295
|
+
}
|
|
109296
|
+
});
|
|
109297
|
+
|
|
109298
|
+
// ../agent/src/safety/stuck-detector.ts
|
|
109299
|
+
var StuckDetector;
|
|
109300
|
+
var init_stuck_detector = __esm({
|
|
109301
|
+
"../agent/src/safety/stuck-detector.ts"() {
|
|
109302
|
+
"use strict";
|
|
109303
|
+
StuckDetector = class {
|
|
109304
|
+
cfg;
|
|
109305
|
+
toolCallHistory = [];
|
|
109306
|
+
findingsCount = 0;
|
|
109307
|
+
iterationsWithoutProgress = 0;
|
|
109308
|
+
consecutiveErrors = 0;
|
|
109309
|
+
startTime = Date.now();
|
|
109310
|
+
constructor(config2 = {}) {
|
|
109311
|
+
this.cfg = {
|
|
109312
|
+
maxRepeatedCalls: config2.maxRepeatedCalls ?? 3,
|
|
109313
|
+
maxIterationsWithoutProgress: config2.maxIterationsWithoutProgress ?? 5,
|
|
109314
|
+
maxConsecutiveErrors: config2.maxConsecutiveErrors ?? 3,
|
|
109315
|
+
maxSubObjectiveTimeMs: config2.maxSubObjectiveTimeMs ?? 3e5
|
|
109316
|
+
};
|
|
109317
|
+
}
|
|
109318
|
+
// ── Recording ───────────────────────────────────────────────────────
|
|
109319
|
+
/** Record a tool call for pattern detection. */
|
|
109320
|
+
recordToolCall(name, args2) {
|
|
109321
|
+
this.toolCallHistory.push({
|
|
109322
|
+
name,
|
|
109323
|
+
args: JSON.stringify(args2),
|
|
109324
|
+
timestamp: Date.now()
|
|
109325
|
+
});
|
|
109326
|
+
}
|
|
109327
|
+
/** Record a tool result (success or error). */
|
|
109328
|
+
recordResult(isError) {
|
|
109329
|
+
if (isError) {
|
|
109330
|
+
this.consecutiveErrors++;
|
|
109331
|
+
} else {
|
|
109332
|
+
this.consecutiveErrors = 0;
|
|
109333
|
+
}
|
|
109334
|
+
}
|
|
109335
|
+
/** Update findings count — call after each iteration of the agent loop. */
|
|
109336
|
+
updateProgress(currentFindings) {
|
|
109337
|
+
if (currentFindings > this.findingsCount) {
|
|
109338
|
+
this.findingsCount = currentFindings;
|
|
109339
|
+
this.iterationsWithoutProgress = 0;
|
|
109340
|
+
} else {
|
|
109341
|
+
this.iterationsWithoutProgress++;
|
|
109342
|
+
}
|
|
109343
|
+
}
|
|
109344
|
+
// ── Checking ────────────────────────────────────────────────────────
|
|
109345
|
+
/** Check if the agent appears stuck. */
|
|
109346
|
+
check() {
|
|
109347
|
+
const elapsed = Date.now() - this.startTime;
|
|
109348
|
+
if (elapsed > this.cfg.maxSubObjectiveTimeMs) {
|
|
109349
|
+
return {
|
|
109350
|
+
isStuck: true,
|
|
109351
|
+
reason: "timeout",
|
|
109352
|
+
details: `Sub-objective exceeded ${this.cfg.maxSubObjectiveTimeMs}ms (elapsed: ${elapsed}ms)`,
|
|
109353
|
+
suggestedAction: "skip"
|
|
109354
|
+
};
|
|
109355
|
+
}
|
|
109356
|
+
const windowSize = this.cfg.maxRepeatedCalls;
|
|
109357
|
+
if (this.toolCallHistory.length >= windowSize) {
|
|
109358
|
+
const recent = this.toolCallHistory.slice(-windowSize);
|
|
109359
|
+
const first2 = recent[0];
|
|
109360
|
+
const allSame = recent.every(
|
|
109361
|
+
(c) => c.name === first2.name && c.args === first2.args
|
|
109362
|
+
);
|
|
109363
|
+
if (allSame) {
|
|
109364
|
+
return {
|
|
109365
|
+
isStuck: true,
|
|
109366
|
+
reason: "repeated_calls",
|
|
109367
|
+
details: `Tool "${first2.name}" called ${windowSize}x with identical args`,
|
|
109368
|
+
suggestedAction: "retry_different"
|
|
109369
|
+
};
|
|
109370
|
+
}
|
|
109371
|
+
}
|
|
109372
|
+
if (this.iterationsWithoutProgress >= this.cfg.maxIterationsWithoutProgress) {
|
|
109373
|
+
return {
|
|
109374
|
+
isStuck: true,
|
|
109375
|
+
reason: "no_progress",
|
|
109376
|
+
details: `${this.iterationsWithoutProgress} iterations without new findings`,
|
|
109377
|
+
suggestedAction: "escalate_model"
|
|
109378
|
+
};
|
|
109379
|
+
}
|
|
109380
|
+
if (this.consecutiveErrors >= this.cfg.maxConsecutiveErrors) {
|
|
109381
|
+
return {
|
|
109382
|
+
isStuck: true,
|
|
109383
|
+
reason: "error_loop",
|
|
109384
|
+
details: `${this.consecutiveErrors} consecutive tool errors`,
|
|
109385
|
+
suggestedAction: "retry_different"
|
|
109386
|
+
};
|
|
109387
|
+
}
|
|
109388
|
+
return { isStuck: false };
|
|
109389
|
+
}
|
|
109390
|
+
// ── Lifecycle ───────────────────────────────────────────────────────
|
|
109391
|
+
/** Reset all state for reuse (e.g., new sub-objective). */
|
|
109392
|
+
reset() {
|
|
109393
|
+
this.toolCallHistory = [];
|
|
109394
|
+
this.findingsCount = 0;
|
|
109395
|
+
this.iterationsWithoutProgress = 0;
|
|
109396
|
+
this.consecutiveErrors = 0;
|
|
109397
|
+
this.startTime = Date.now();
|
|
109398
|
+
}
|
|
109399
|
+
/** Get a diagnostic snapshot (useful for logging/telemetry). */
|
|
109400
|
+
getSnapshot() {
|
|
109401
|
+
return {
|
|
109402
|
+
totalCalls: this.toolCallHistory.length,
|
|
109403
|
+
consecutiveErrors: this.consecutiveErrors,
|
|
109404
|
+
iterationsWithoutProgress: this.iterationsWithoutProgress,
|
|
109405
|
+
findingsCount: this.findingsCount,
|
|
109406
|
+
elapsedMs: Date.now() - this.startTime
|
|
109407
|
+
};
|
|
109408
|
+
}
|
|
109409
|
+
};
|
|
109410
|
+
}
|
|
109411
|
+
});
|
|
109412
|
+
|
|
108368
109413
|
// ../agent/src/runtime.ts
|
|
108369
109414
|
function estimateCost(model, inputTokens, outputTokens) {
|
|
108370
109415
|
let costs = COST_PER_1M_TOKENS[model];
|
|
@@ -108432,6 +109477,8 @@ var init_runtime = __esm({
|
|
|
108432
109477
|
init_hot_swap();
|
|
108433
109478
|
init_auto_downgrade();
|
|
108434
109479
|
init_reason_logger();
|
|
109480
|
+
init_context_rotator();
|
|
109481
|
+
init_stuck_detector();
|
|
108435
109482
|
COST_PER_1M_TOKENS = {
|
|
108436
109483
|
"claude-sonnet-4-20250514": { input: 3, output: 15 },
|
|
108437
109484
|
"claude-3-5-sonnet": { input: 3, output: 15 },
|
|
@@ -108466,6 +109513,10 @@ var init_runtime = __esm({
|
|
|
108466
109513
|
autoDowngrade = null;
|
|
108467
109514
|
/** Explainability: logs the agent's reasoning at each step. */
|
|
108468
109515
|
reasonLogger = new ReasonLogger();
|
|
109516
|
+
/** Context rotator for fresh-context pattern (Ralph methodology). */
|
|
109517
|
+
contextRotator;
|
|
109518
|
+
/** Stuck detector for loop/stall detection. */
|
|
109519
|
+
stuckDetector;
|
|
108469
109520
|
constructor(config2) {
|
|
108470
109521
|
this.config = config2;
|
|
108471
109522
|
this.hotSwap = new HotSwapAdapter(config2.adapter);
|
|
@@ -108550,6 +109601,8 @@ var init_runtime = __esm({
|
|
|
108550
109601
|
workspaceContext,
|
|
108551
109602
|
errorCorrections
|
|
108552
109603
|
});
|
|
109604
|
+
this.contextRotator = new ContextRotator(config2.contextRotation);
|
|
109605
|
+
this.stuckDetector = new StuckDetector(config2.stuckDetection);
|
|
108553
109606
|
}
|
|
108554
109607
|
/**
|
|
108555
109608
|
* Run the agent loop for a given task.
|
|
@@ -108577,6 +109630,9 @@ var init_runtime = __esm({
|
|
|
108577
109630
|
let lastOutput = "";
|
|
108578
109631
|
let exitReason = "completed";
|
|
108579
109632
|
let error;
|
|
109633
|
+
let consecutiveStucks = 0;
|
|
109634
|
+
this.contextRotator.reset();
|
|
109635
|
+
this.stuckDetector.reset();
|
|
108580
109636
|
if (this.config.dryRun) {
|
|
108581
109637
|
this.dryRunInterceptor = new DryRunInterceptor();
|
|
108582
109638
|
} else {
|
|
@@ -108901,8 +109957,51 @@ ${this.config.orgChartContext}` : this.config.orgChartContext;
|
|
|
108901
109957
|
}
|
|
108902
109958
|
]);
|
|
108903
109959
|
messages.push(...toolResultMessages);
|
|
109960
|
+
this.stuckDetector.recordToolCall(toolCall.name, toolCall.arguments);
|
|
109961
|
+
this.stuckDetector.recordResult(result2.isError ?? false);
|
|
109962
|
+
const stuckCheck = this.stuckDetector.check();
|
|
109963
|
+
if (stuckCheck.isStuck) {
|
|
109964
|
+
this.log.warn("Agent stuck detected", {
|
|
109965
|
+
reason: stuckCheck.reason,
|
|
109966
|
+
suggestion: stuckCheck.suggestedAction
|
|
109967
|
+
});
|
|
109968
|
+
messages.push({
|
|
109969
|
+
role: "user",
|
|
109970
|
+
content: `[System] You appear to be stuck (${stuckCheck.reason}). ${stuckCheck.suggestedAction === "retry_different" ? "Try a completely different approach." : stuckCheck.suggestedAction === "skip" ? "Skip this and move to the next task." : stuckCheck.suggestedAction === "escalate_model" ? "This task may require deeper analysis." : "Consider aborting this approach."}`
|
|
109971
|
+
});
|
|
109972
|
+
if (stuckCheck.reason === "timeout" || consecutiveStucks >= 2) {
|
|
109973
|
+
exitReason = "error";
|
|
109974
|
+
error = `Agent stuck: ${stuckCheck.reason} \u2014 ${stuckCheck.details ?? ""}`;
|
|
109975
|
+
break;
|
|
109976
|
+
}
|
|
109977
|
+
consecutiveStucks++;
|
|
109978
|
+
} else {
|
|
109979
|
+
consecutiveStucks = 0;
|
|
109980
|
+
}
|
|
108904
109981
|
}
|
|
108905
109982
|
if (exitReason === "aborted") break;
|
|
109983
|
+
if (exitReason === "error") break;
|
|
109984
|
+
this.contextRotator.recordMessage(response.content);
|
|
109985
|
+
const rotationCheck = this.contextRotator.shouldRotate();
|
|
109986
|
+
if (rotationCheck.rotate) {
|
|
109987
|
+
const summary = this.contextRotator.buildRotationSummary(messages);
|
|
109988
|
+
const systemMsg = messages[0];
|
|
109989
|
+
const recent = messages.slice(-3);
|
|
109990
|
+
messages.length = 0;
|
|
109991
|
+
messages.push(
|
|
109992
|
+
systemMsg,
|
|
109993
|
+
{
|
|
109994
|
+
role: "user",
|
|
109995
|
+
content: `[Context rotation \u2014 previous messages summarized]
|
|
109996
|
+
${summary}
|
|
109997
|
+
|
|
109998
|
+
Continue from where you left off.`
|
|
109999
|
+
},
|
|
110000
|
+
...recent
|
|
110001
|
+
);
|
|
110002
|
+
this.contextRotator.reset();
|
|
110003
|
+
this.log.info("Context rotated", { reason: rotationCheck.reason });
|
|
110004
|
+
}
|
|
108906
110005
|
}
|
|
108907
110006
|
if (iterations >= maxIterations && exitReason === "completed") {
|
|
108908
110007
|
exitReason = "max_iterations";
|
|
@@ -109144,6 +110243,9 @@ ${this.config.orgChartContext}` : this.config.orgChartContext;
|
|
|
109144
110243
|
let consecutiveFailures = 0;
|
|
109145
110244
|
let lastFailedToolName = "";
|
|
109146
110245
|
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
110246
|
+
let consecutiveStucks = 0;
|
|
110247
|
+
this.contextRotator.reset();
|
|
110248
|
+
this.stuckDetector.reset();
|
|
109147
110249
|
const messages = this.contextBuilder.buildMessages(
|
|
109148
110250
|
task.prompt,
|
|
109149
110251
|
this.config.initialMessages
|
|
@@ -109421,8 +110523,51 @@ ${this.config.orgChartContext}` : this.config.orgChartContext;
|
|
|
109421
110523
|
}
|
|
109422
110524
|
]);
|
|
109423
110525
|
messages.push(...toolResultMessages);
|
|
110526
|
+
this.stuckDetector.recordToolCall(toolCall.name, toolCall.arguments);
|
|
110527
|
+
this.stuckDetector.recordResult(result.isError ?? false);
|
|
110528
|
+
const stuckCheck = this.stuckDetector.check();
|
|
110529
|
+
if (stuckCheck.isStuck) {
|
|
110530
|
+
this.log.warn("Agent stuck detected (streaming)", {
|
|
110531
|
+
reason: stuckCheck.reason,
|
|
110532
|
+
suggestion: stuckCheck.suggestedAction
|
|
110533
|
+
});
|
|
110534
|
+
messages.push({
|
|
110535
|
+
role: "user",
|
|
110536
|
+
content: `[System] You appear to be stuck (${stuckCheck.reason}). ${stuckCheck.suggestedAction === "retry_different" ? "Try a completely different approach." : stuckCheck.suggestedAction === "skip" ? "Skip this and move to the next task." : stuckCheck.suggestedAction === "escalate_model" ? "This task may require deeper analysis." : "Consider aborting this approach."}`
|
|
110537
|
+
});
|
|
110538
|
+
if (stuckCheck.reason === "timeout" || consecutiveStucks >= 2) {
|
|
110539
|
+
exitReason = "error";
|
|
110540
|
+
error = `Agent stuck: ${stuckCheck.reason} \u2014 ${stuckCheck.details ?? ""}`;
|
|
110541
|
+
break;
|
|
110542
|
+
}
|
|
110543
|
+
consecutiveStucks++;
|
|
110544
|
+
} else {
|
|
110545
|
+
consecutiveStucks = 0;
|
|
110546
|
+
}
|
|
109424
110547
|
}
|
|
109425
110548
|
if (exitReason === "aborted") break;
|
|
110549
|
+
if (exitReason === "error") break;
|
|
110550
|
+
this.contextRotator.recordMessage(response.content);
|
|
110551
|
+
const rotationCheck = this.contextRotator.shouldRotate();
|
|
110552
|
+
if (rotationCheck.rotate) {
|
|
110553
|
+
const summary = this.contextRotator.buildRotationSummary(messages);
|
|
110554
|
+
const systemMsg = messages[0];
|
|
110555
|
+
const recent = messages.slice(-3);
|
|
110556
|
+
messages.length = 0;
|
|
110557
|
+
messages.push(
|
|
110558
|
+
systemMsg,
|
|
110559
|
+
{
|
|
110560
|
+
role: "user",
|
|
110561
|
+
content: `[Context rotation \u2014 previous messages summarized]
|
|
110562
|
+
${summary}
|
|
110563
|
+
|
|
110564
|
+
Continue from where you left off.`
|
|
110565
|
+
},
|
|
110566
|
+
...recent
|
|
110567
|
+
);
|
|
110568
|
+
this.contextRotator.reset();
|
|
110569
|
+
this.log.info("Context rotated (streaming)", { reason: rotationCheck.reason });
|
|
110570
|
+
}
|
|
109426
110571
|
}
|
|
109427
110572
|
if (iterations >= maxIterations && exitReason === "completed") {
|
|
109428
110573
|
exitReason = "max_iterations";
|
|
@@ -110511,8 +111656,8 @@ var init_embeddings = __esm({
|
|
|
110511
111656
|
});
|
|
110512
111657
|
|
|
110513
111658
|
// ../agent/src/rag/indexer.ts
|
|
110514
|
-
import { readFileSync as
|
|
110515
|
-
import { join as
|
|
111659
|
+
import { readFileSync as readFileSync11, statSync as statSync4, existsSync as existsSync7 } from "node:fs";
|
|
111660
|
+
import { join as join12, relative as relative4, resolve as resolve11 } from "node:path";
|
|
110516
111661
|
function matchesGlob(filePath, pattern) {
|
|
110517
111662
|
const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
110518
111663
|
return new RegExp(`^${regexStr}$`).test(filePath) || new RegExp(`(^|/)${regexStr}$`).test(filePath);
|
|
@@ -110538,14 +111683,14 @@ function walkDirectory2(dir, excludePatterns) {
|
|
|
110538
111683
|
const currentDir = stack.pop();
|
|
110539
111684
|
let entries;
|
|
110540
111685
|
try {
|
|
110541
|
-
const { readdirSync:
|
|
110542
|
-
entries =
|
|
111686
|
+
const { readdirSync: readdirSync8 } = __require("node:fs");
|
|
111687
|
+
entries = readdirSync8(currentDir);
|
|
110543
111688
|
} catch {
|
|
110544
111689
|
continue;
|
|
110545
111690
|
}
|
|
110546
111691
|
for (const entry of entries) {
|
|
110547
|
-
const fullPath =
|
|
110548
|
-
const relFromRoot =
|
|
111692
|
+
const fullPath = join12(currentDir, entry);
|
|
111693
|
+
const relFromRoot = relative4(dir, fullPath).replace(/\\/g, "/");
|
|
110549
111694
|
let excluded = false;
|
|
110550
111695
|
for (const pattern of excludePatterns) {
|
|
110551
111696
|
if (!pattern.includes("*") && !pattern.includes(".")) {
|
|
@@ -110557,7 +111702,7 @@ function walkDirectory2(dir, excludePatterns) {
|
|
|
110557
111702
|
}
|
|
110558
111703
|
if (excluded) continue;
|
|
110559
111704
|
try {
|
|
110560
|
-
const stat =
|
|
111705
|
+
const stat = statSync4(fullPath);
|
|
110561
111706
|
if (stat.isDirectory()) {
|
|
110562
111707
|
stack.push(fullPath);
|
|
110563
111708
|
} else if (stat.isFile()) {
|
|
@@ -110649,13 +111794,13 @@ var init_indexer = __esm({
|
|
|
110649
111794
|
const tenantId = opts?.tenantId ?? "default";
|
|
110650
111795
|
const onProgress = opts?.onProgress;
|
|
110651
111796
|
const absDir = resolve11(dir);
|
|
110652
|
-
if (!
|
|
111797
|
+
if (!existsSync7(absDir)) {
|
|
110653
111798
|
throw new Error(`Directory not found: ${absDir}`);
|
|
110654
111799
|
}
|
|
110655
111800
|
const allFiles = walkDirectory2(absDir, excludePatterns);
|
|
110656
111801
|
const matchingFiles = [];
|
|
110657
111802
|
for (const file of allFiles) {
|
|
110658
|
-
const relPath =
|
|
111803
|
+
const relPath = relative4(absDir, file).replace(/\\/g, "/");
|
|
110659
111804
|
if (shouldInclude(relPath, includePatterns, excludePatterns)) {
|
|
110660
111805
|
matchingFiles.push(file);
|
|
110661
111806
|
}
|
|
@@ -110671,11 +111816,11 @@ var init_indexer = __esm({
|
|
|
110671
111816
|
let totalFilesIndexed = 0;
|
|
110672
111817
|
const allChunks = [];
|
|
110673
111818
|
for (const filePath of matchingFiles) {
|
|
110674
|
-
const relPath =
|
|
111819
|
+
const relPath = relative4(absDir, filePath).replace(/\\/g, "/");
|
|
110675
111820
|
progress.currentFile = relPath;
|
|
110676
111821
|
onProgress?.(progress);
|
|
110677
111822
|
try {
|
|
110678
|
-
const content =
|
|
111823
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
110679
111824
|
if (content.includes("\0")) {
|
|
110680
111825
|
progress.processedFiles++;
|
|
110681
111826
|
continue;
|
|
@@ -110684,7 +111829,7 @@ var init_indexer = __esm({
|
|
|
110684
111829
|
progress.processedFiles++;
|
|
110685
111830
|
continue;
|
|
110686
111831
|
}
|
|
110687
|
-
const mtime =
|
|
111832
|
+
const mtime = statSync4(filePath).mtimeMs;
|
|
110688
111833
|
const chunks = this.chunker.chunk(relPath, content);
|
|
110689
111834
|
for (const chunk of chunks) {
|
|
110690
111835
|
allChunks.push({ ...chunk, fileMtime: Math.floor(mtime) });
|
|
@@ -110746,7 +111891,7 @@ ${c.content}`);
|
|
|
110746
111891
|
const tenantId = opts?.tenantId ?? "default";
|
|
110747
111892
|
const onProgress = opts?.onProgress;
|
|
110748
111893
|
const absDir = resolve11(dir);
|
|
110749
|
-
if (!
|
|
111894
|
+
if (!existsSync7(absDir)) {
|
|
110750
111895
|
throw new Error(`Directory not found: ${absDir}`);
|
|
110751
111896
|
}
|
|
110752
111897
|
const existingMtimes = this.store.getChunkMtimes(tenantId);
|
|
@@ -110757,16 +111902,16 @@ ${c.content}`);
|
|
|
110757
111902
|
const allFiles = walkDirectory2(absDir, excludePatterns);
|
|
110758
111903
|
const matchingFiles = [];
|
|
110759
111904
|
for (const file of allFiles) {
|
|
110760
|
-
const relPath =
|
|
111905
|
+
const relPath = relative4(absDir, file).replace(/\\/g, "/");
|
|
110761
111906
|
if (shouldInclude(relPath, includePatterns, excludePatterns)) {
|
|
110762
111907
|
matchingFiles.push(file);
|
|
110763
111908
|
}
|
|
110764
111909
|
}
|
|
110765
111910
|
const filesToProcess = [];
|
|
110766
111911
|
for (const filePath of matchingFiles) {
|
|
110767
|
-
const relPath =
|
|
111912
|
+
const relPath = relative4(absDir, filePath).replace(/\\/g, "/");
|
|
110768
111913
|
try {
|
|
110769
|
-
const mtime = Math.floor(
|
|
111914
|
+
const mtime = Math.floor(statSync4(filePath).mtimeMs);
|
|
110770
111915
|
const existingMtime = mtimeMap.get(relPath);
|
|
110771
111916
|
if (existingMtime === void 0 || mtime > existingMtime) {
|
|
110772
111917
|
filesToProcess.push(filePath);
|
|
@@ -110788,16 +111933,16 @@ ${c.content}`);
|
|
|
110788
111933
|
let totalChunksCreated = 0;
|
|
110789
111934
|
let totalFilesIndexed = 0;
|
|
110790
111935
|
for (const filePath of filesToProcess) {
|
|
110791
|
-
const relPath =
|
|
111936
|
+
const relPath = relative4(absDir, filePath).replace(/\\/g, "/");
|
|
110792
111937
|
progress.currentFile = relPath;
|
|
110793
111938
|
onProgress?.(progress);
|
|
110794
111939
|
try {
|
|
110795
|
-
const content =
|
|
111940
|
+
const content = readFileSync11(filePath, "utf-8");
|
|
110796
111941
|
if (content.includes("\0") || content.length > 5e5) {
|
|
110797
111942
|
progress.processedFiles++;
|
|
110798
111943
|
continue;
|
|
110799
111944
|
}
|
|
110800
|
-
const mtime = Math.floor(
|
|
111945
|
+
const mtime = Math.floor(statSync4(filePath).mtimeMs);
|
|
110801
111946
|
this.store.clearChunksByFile(relPath, tenantId);
|
|
110802
111947
|
const chunks = this.chunker.chunk(relPath, content);
|
|
110803
111948
|
const texts = chunks.map((c) => `${c.name ?? c.type} in ${c.filePath}
|
|
@@ -110980,8 +112125,8 @@ var init_search = __esm({
|
|
|
110980
112125
|
});
|
|
110981
112126
|
|
|
110982
112127
|
// ../agent/src/rag/mental-model.ts
|
|
110983
|
-
import { readFileSync as
|
|
110984
|
-
import { join as
|
|
112128
|
+
import { readFileSync as readFileSync12, statSync as statSync5, readdirSync as readdirSync3, writeFileSync as writeFileSync5, existsSync as existsSync8 } from "node:fs";
|
|
112129
|
+
import { join as join13, relative as relative5, resolve as resolve12, extname as extname3, basename as basename4, dirname as dirname6 } from "node:path";
|
|
110985
112130
|
var EXCLUDE_DIRS, SOURCE_EXTENSIONS, MentalModelBuilder;
|
|
110986
112131
|
var init_mental_model = __esm({
|
|
110987
112132
|
"../agent/src/rag/mental-model.ts"() {
|
|
@@ -111045,10 +112190,10 @@ var init_mental_model = __esm({
|
|
|
111045
112190
|
const files = await this.findSourceFiles(absRoot);
|
|
111046
112191
|
const nodes = /* @__PURE__ */ new Map();
|
|
111047
112192
|
for (const file of files) {
|
|
111048
|
-
const relPath =
|
|
112193
|
+
const relPath = relative5(absRoot, file).replace(/\\/g, "/");
|
|
111049
112194
|
let content;
|
|
111050
112195
|
try {
|
|
111051
|
-
content =
|
|
112196
|
+
content = readFileSync12(file, "utf-8");
|
|
111052
112197
|
} catch {
|
|
111053
112198
|
continue;
|
|
111054
112199
|
}
|
|
@@ -111056,14 +112201,14 @@ var init_mental_model = __esm({
|
|
|
111056
112201
|
const imports = this.extractImports(content, file, absRoot);
|
|
111057
112202
|
let mtime;
|
|
111058
112203
|
try {
|
|
111059
|
-
mtime =
|
|
112204
|
+
mtime = statSync5(file).mtimeMs;
|
|
111060
112205
|
} catch {
|
|
111061
112206
|
mtime = Date.now();
|
|
111062
112207
|
}
|
|
111063
112208
|
nodes.set(relPath, {
|
|
111064
112209
|
path: relPath,
|
|
111065
112210
|
type: "file",
|
|
111066
|
-
name:
|
|
112211
|
+
name: basename4(file),
|
|
111067
112212
|
dependencies: imports,
|
|
111068
112213
|
dependents: [],
|
|
111069
112214
|
lastModified: mtime,
|
|
@@ -111096,7 +112241,7 @@ var init_mental_model = __esm({
|
|
|
111096
112241
|
);
|
|
111097
112242
|
const hubs = [...graph.nodes.values()].sort((a, b) => b.dependents.length - a.dependents.length).slice(0, 5);
|
|
111098
112243
|
const lines = [];
|
|
111099
|
-
lines.push(`Project: ${
|
|
112244
|
+
lines.push(`Project: ${basename4(graph.rootDir)}`);
|
|
111100
112245
|
lines.push(`Language: ${graph.language}`);
|
|
111101
112246
|
if (graph.framework) lines.push(`Framework: ${graph.framework}`);
|
|
111102
112247
|
lines.push(`Files: ${moduleCount}`);
|
|
@@ -111183,15 +112328,15 @@ var init_mental_model = __esm({
|
|
|
111183
112328
|
*/
|
|
111184
112329
|
save(graph, filePath) {
|
|
111185
112330
|
const json = this.toJson(graph);
|
|
111186
|
-
|
|
112331
|
+
writeFileSync5(filePath, JSON.stringify(json, null, 2), "utf-8");
|
|
111187
112332
|
}
|
|
111188
112333
|
/**
|
|
111189
112334
|
* Load a graph from a JSON file.
|
|
111190
112335
|
*/
|
|
111191
112336
|
load(filePath) {
|
|
111192
|
-
if (!
|
|
112337
|
+
if (!existsSync8(filePath)) return null;
|
|
111193
112338
|
try {
|
|
111194
|
-
const content =
|
|
112339
|
+
const content = readFileSync12(filePath, "utf-8");
|
|
111195
112340
|
const json = JSON.parse(content);
|
|
111196
112341
|
return this.fromJson(json);
|
|
111197
112342
|
} catch {
|
|
@@ -111270,7 +112415,7 @@ var init_mental_model = __esm({
|
|
|
111270
112415
|
}
|
|
111271
112416
|
const fromDir = dirname6(fromFile);
|
|
111272
112417
|
const resolved = resolve12(fromDir, specifier);
|
|
111273
|
-
const relPath =
|
|
112418
|
+
const relPath = relative5(rootDir, resolved).replace(/\\/g, "/");
|
|
111274
112419
|
const candidates = [
|
|
111275
112420
|
relPath,
|
|
111276
112421
|
relPath + ".ts",
|
|
@@ -111291,7 +112436,7 @@ var init_mental_model = __esm({
|
|
|
111291
112436
|
for (const candidate of candidates) {
|
|
111292
112437
|
const fullPath = resolve12(rootDir, candidate);
|
|
111293
112438
|
try {
|
|
111294
|
-
const stat =
|
|
112439
|
+
const stat = statSync5(fullPath);
|
|
111295
112440
|
if (stat.isFile()) {
|
|
111296
112441
|
return candidate;
|
|
111297
112442
|
}
|
|
@@ -111301,7 +112446,7 @@ var init_mental_model = __esm({
|
|
|
111301
112446
|
return relPath;
|
|
111302
112447
|
}
|
|
111303
112448
|
resolveRelativePy(modulePath, fromFile, rootDir) {
|
|
111304
|
-
const relPath =
|
|
112449
|
+
const relPath = relative5(rootDir, resolve12(dirname6(fromFile), modulePath)).replace(/\\/g, "/");
|
|
111305
112450
|
const candidates = [
|
|
111306
112451
|
relPath + ".py",
|
|
111307
112452
|
relPath + "/__init__.py"
|
|
@@ -111309,7 +112454,7 @@ var init_mental_model = __esm({
|
|
|
111309
112454
|
for (const candidate of candidates) {
|
|
111310
112455
|
const fullPath = resolve12(rootDir, candidate);
|
|
111311
112456
|
try {
|
|
111312
|
-
if (
|
|
112457
|
+
if (statSync5(fullPath).isFile()) return candidate;
|
|
111313
112458
|
} catch {
|
|
111314
112459
|
}
|
|
111315
112460
|
}
|
|
@@ -111384,10 +112529,10 @@ var init_mental_model = __esm({
|
|
|
111384
112529
|
return "Unknown";
|
|
111385
112530
|
}
|
|
111386
112531
|
detectFramework(rootDir) {
|
|
111387
|
-
const pkgPath =
|
|
111388
|
-
if (
|
|
112532
|
+
const pkgPath = join13(rootDir, "package.json");
|
|
112533
|
+
if (existsSync8(pkgPath)) {
|
|
111389
112534
|
try {
|
|
111390
|
-
const pkg = JSON.parse(
|
|
112535
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
111391
112536
|
const deps = {
|
|
111392
112537
|
...pkg.dependencies ?? {},
|
|
111393
112538
|
...pkg.devDependencies ?? {}
|
|
@@ -111406,10 +112551,10 @@ var init_mental_model = __esm({
|
|
|
111406
112551
|
} catch {
|
|
111407
112552
|
}
|
|
111408
112553
|
}
|
|
111409
|
-
const cargoPath =
|
|
111410
|
-
if (
|
|
112554
|
+
const cargoPath = join13(rootDir, "Cargo.toml");
|
|
112555
|
+
if (existsSync8(cargoPath)) {
|
|
111411
112556
|
try {
|
|
111412
|
-
const cargo =
|
|
112557
|
+
const cargo = readFileSync12(cargoPath, "utf-8");
|
|
111413
112558
|
if (cargo.includes("actix-web")) return "Actix";
|
|
111414
112559
|
if (cargo.includes("axum")) return "Axum";
|
|
111415
112560
|
if (cargo.includes("rocket")) return "Rocket";
|
|
@@ -111417,10 +112562,10 @@ var init_mental_model = __esm({
|
|
|
111417
112562
|
} catch {
|
|
111418
112563
|
}
|
|
111419
112564
|
}
|
|
111420
|
-
const goModPath =
|
|
111421
|
-
if (
|
|
112565
|
+
const goModPath = join13(rootDir, "go.mod");
|
|
112566
|
+
if (existsSync8(goModPath)) {
|
|
111422
112567
|
try {
|
|
111423
|
-
const goMod =
|
|
112568
|
+
const goMod = readFileSync12(goModPath, "utf-8");
|
|
111424
112569
|
if (goMod.includes("gin-gonic")) return "Gin";
|
|
111425
112570
|
if (goMod.includes("echo")) return "Echo";
|
|
111426
112571
|
if (goMod.includes("fiber")) return "Fiber";
|
|
@@ -111428,10 +112573,10 @@ var init_mental_model = __esm({
|
|
|
111428
112573
|
}
|
|
111429
112574
|
}
|
|
111430
112575
|
for (const pyFile of ["requirements.txt", "pyproject.toml"]) {
|
|
111431
|
-
const pyPath =
|
|
111432
|
-
if (
|
|
112576
|
+
const pyPath = join13(rootDir, pyFile);
|
|
112577
|
+
if (existsSync8(pyPath)) {
|
|
111433
112578
|
try {
|
|
111434
|
-
const content =
|
|
112579
|
+
const content = readFileSync12(pyPath, "utf-8");
|
|
111435
112580
|
if (content.includes("django")) return "Django";
|
|
111436
112581
|
if (content.includes("flask")) return "Flask";
|
|
111437
112582
|
if (content.includes("fastapi")) return "FastAPI";
|
|
@@ -111485,16 +112630,16 @@ var init_mental_model = __esm({
|
|
|
111485
112630
|
const currentDir = stack.pop();
|
|
111486
112631
|
let entries;
|
|
111487
112632
|
try {
|
|
111488
|
-
entries =
|
|
112633
|
+
entries = readdirSync3(currentDir);
|
|
111489
112634
|
} catch {
|
|
111490
112635
|
continue;
|
|
111491
112636
|
}
|
|
111492
112637
|
for (const entry of entries) {
|
|
111493
112638
|
if (EXCLUDE_DIRS.has(entry)) continue;
|
|
111494
112639
|
if (entry.startsWith(".") && entry !== ".") continue;
|
|
111495
|
-
const fullPath =
|
|
112640
|
+
const fullPath = join13(currentDir, entry);
|
|
111496
112641
|
try {
|
|
111497
|
-
const stat =
|
|
112642
|
+
const stat = statSync5(fullPath);
|
|
111498
112643
|
if (stat.isDirectory()) {
|
|
111499
112644
|
stack.push(fullPath);
|
|
111500
112645
|
} else if (stat.isFile()) {
|
|
@@ -111527,7 +112672,7 @@ var init_rag = __esm({
|
|
|
111527
112672
|
});
|
|
111528
112673
|
|
|
111529
112674
|
// ../agent/src/testing/mock-adapter.ts
|
|
111530
|
-
import { readFileSync as
|
|
112675
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync6 } from "node:fs";
|
|
111531
112676
|
var MockLlmAdapter, RecordingAdapter;
|
|
111532
112677
|
var init_mock_adapter = __esm({
|
|
111533
112678
|
"../agent/src/testing/mock-adapter.ts"() {
|
|
@@ -111579,7 +112724,7 @@ var init_mock_adapter = __esm({
|
|
|
111579
112724
|
}
|
|
111580
112725
|
/** Create from a JSON file of recorded responses. */
|
|
111581
112726
|
static fromFile(filePath) {
|
|
111582
|
-
const data = JSON.parse(
|
|
112727
|
+
const data = JSON.parse(readFileSync13(filePath, "utf-8"));
|
|
111583
112728
|
return new _MockLlmAdapter2(data);
|
|
111584
112729
|
}
|
|
111585
112730
|
};
|
|
@@ -111611,11 +112756,11 @@ var init_mock_adapter = __esm({
|
|
|
111611
112756
|
/** Save recordings to a JSON file. */
|
|
111612
112757
|
save(filePath) {
|
|
111613
112758
|
const responses = this.recordings.map((r) => r.response);
|
|
111614
|
-
|
|
112759
|
+
writeFileSync6(filePath, JSON.stringify(responses, null, 2), "utf-8");
|
|
111615
112760
|
}
|
|
111616
112761
|
/** Load responses from a JSON file (returns the responses array for MockLlmAdapter). */
|
|
111617
112762
|
static load(filePath) {
|
|
111618
|
-
return JSON.parse(
|
|
112763
|
+
return JSON.parse(readFileSync13(filePath, "utf-8"));
|
|
111619
112764
|
}
|
|
111620
112765
|
/** Get just the response objects for replaying. */
|
|
111621
112766
|
getResponses() {
|
|
@@ -111777,8 +112922,8 @@ var init_assertions = __esm({
|
|
|
111777
112922
|
});
|
|
111778
112923
|
|
|
111779
112924
|
// ../agent/src/testing/runner.ts
|
|
111780
|
-
import { readFileSync as
|
|
111781
|
-
import { join as
|
|
112925
|
+
import { readFileSync as readFileSync14, mkdirSync as mkdirSync5, writeFileSync as writeFileSync7, rmSync, existsSync as existsSync9 } from "node:fs";
|
|
112926
|
+
import { join as join14 } from "node:path";
|
|
111782
112927
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
111783
112928
|
import { randomUUID as randomUUID27 } from "node:crypto";
|
|
111784
112929
|
function parseYaml(content) {
|
|
@@ -111880,7 +113025,7 @@ var init_runner = __esm({
|
|
|
111880
113025
|
AgentTestRunner = class {
|
|
111881
113026
|
/** Load test suite from a YAML or JSON file. */
|
|
111882
113027
|
static loadSuite(filePath) {
|
|
111883
|
-
const content =
|
|
113028
|
+
const content = readFileSync14(filePath, "utf-8");
|
|
111884
113029
|
const parsed = parseYaml(content);
|
|
111885
113030
|
return {
|
|
111886
113031
|
name: parsed.name ?? "Unnamed Suite",
|
|
@@ -111903,18 +113048,18 @@ var init_runner = __esm({
|
|
|
111903
113048
|
async runTest(testCase) {
|
|
111904
113049
|
const startTime4 = Date.now();
|
|
111905
113050
|
try {
|
|
111906
|
-
const tempDir =
|
|
111907
|
-
|
|
113051
|
+
const tempDir = join14(tmpdir3(), `nestor-test-${randomUUID27()}`);
|
|
113052
|
+
mkdirSync5(tempDir, { recursive: true });
|
|
111908
113053
|
if (testCase.files) {
|
|
111909
113054
|
for (const [path30, content] of Object.entries(testCase.files)) {
|
|
111910
|
-
const fullPath =
|
|
111911
|
-
|
|
111912
|
-
|
|
113055
|
+
const fullPath = join14(tempDir, path30);
|
|
113056
|
+
mkdirSync5(join14(fullPath, ".."), { recursive: true });
|
|
113057
|
+
writeFileSync7(fullPath, content, "utf-8");
|
|
111913
113058
|
}
|
|
111914
113059
|
}
|
|
111915
113060
|
let responses;
|
|
111916
|
-
if (testCase.recordings &&
|
|
111917
|
-
responses = JSON.parse(
|
|
113061
|
+
if (testCase.recordings && existsSync9(testCase.recordings)) {
|
|
113062
|
+
responses = JSON.parse(readFileSync14(testCase.recordings, "utf-8"));
|
|
111918
113063
|
} else {
|
|
111919
113064
|
responses = [{
|
|
111920
113065
|
content: "Test completed successfully.",
|
|
@@ -114276,121 +115421,6 @@ var init_knowledge = __esm({
|
|
|
114276
115421
|
}
|
|
114277
115422
|
});
|
|
114278
115423
|
|
|
114279
|
-
// ../agent/src/safety/stuck-detector.ts
|
|
114280
|
-
var StuckDetector;
|
|
114281
|
-
var init_stuck_detector = __esm({
|
|
114282
|
-
"../agent/src/safety/stuck-detector.ts"() {
|
|
114283
|
-
"use strict";
|
|
114284
|
-
StuckDetector = class {
|
|
114285
|
-
cfg;
|
|
114286
|
-
toolCallHistory = [];
|
|
114287
|
-
findingsCount = 0;
|
|
114288
|
-
iterationsWithoutProgress = 0;
|
|
114289
|
-
consecutiveErrors = 0;
|
|
114290
|
-
startTime = Date.now();
|
|
114291
|
-
constructor(config2 = {}) {
|
|
114292
|
-
this.cfg = {
|
|
114293
|
-
maxRepeatedCalls: config2.maxRepeatedCalls ?? 3,
|
|
114294
|
-
maxIterationsWithoutProgress: config2.maxIterationsWithoutProgress ?? 5,
|
|
114295
|
-
maxConsecutiveErrors: config2.maxConsecutiveErrors ?? 3,
|
|
114296
|
-
maxSubObjectiveTimeMs: config2.maxSubObjectiveTimeMs ?? 3e5
|
|
114297
|
-
};
|
|
114298
|
-
}
|
|
114299
|
-
// ── Recording ───────────────────────────────────────────────────────
|
|
114300
|
-
/** Record a tool call for pattern detection. */
|
|
114301
|
-
recordToolCall(name, args2) {
|
|
114302
|
-
this.toolCallHistory.push({
|
|
114303
|
-
name,
|
|
114304
|
-
args: JSON.stringify(args2),
|
|
114305
|
-
timestamp: Date.now()
|
|
114306
|
-
});
|
|
114307
|
-
}
|
|
114308
|
-
/** Record a tool result (success or error). */
|
|
114309
|
-
recordResult(isError) {
|
|
114310
|
-
if (isError) {
|
|
114311
|
-
this.consecutiveErrors++;
|
|
114312
|
-
} else {
|
|
114313
|
-
this.consecutiveErrors = 0;
|
|
114314
|
-
}
|
|
114315
|
-
}
|
|
114316
|
-
/** Update findings count — call after each iteration of the agent loop. */
|
|
114317
|
-
updateProgress(currentFindings) {
|
|
114318
|
-
if (currentFindings > this.findingsCount) {
|
|
114319
|
-
this.findingsCount = currentFindings;
|
|
114320
|
-
this.iterationsWithoutProgress = 0;
|
|
114321
|
-
} else {
|
|
114322
|
-
this.iterationsWithoutProgress++;
|
|
114323
|
-
}
|
|
114324
|
-
}
|
|
114325
|
-
// ── Checking ────────────────────────────────────────────────────────
|
|
114326
|
-
/** Check if the agent appears stuck. */
|
|
114327
|
-
check() {
|
|
114328
|
-
const elapsed = Date.now() - this.startTime;
|
|
114329
|
-
if (elapsed > this.cfg.maxSubObjectiveTimeMs) {
|
|
114330
|
-
return {
|
|
114331
|
-
isStuck: true,
|
|
114332
|
-
reason: "timeout",
|
|
114333
|
-
details: `Sub-objective exceeded ${this.cfg.maxSubObjectiveTimeMs}ms (elapsed: ${elapsed}ms)`,
|
|
114334
|
-
suggestedAction: "skip"
|
|
114335
|
-
};
|
|
114336
|
-
}
|
|
114337
|
-
const windowSize = this.cfg.maxRepeatedCalls;
|
|
114338
|
-
if (this.toolCallHistory.length >= windowSize) {
|
|
114339
|
-
const recent = this.toolCallHistory.slice(-windowSize);
|
|
114340
|
-
const first2 = recent[0];
|
|
114341
|
-
const allSame = recent.every(
|
|
114342
|
-
(c) => c.name === first2.name && c.args === first2.args
|
|
114343
|
-
);
|
|
114344
|
-
if (allSame) {
|
|
114345
|
-
return {
|
|
114346
|
-
isStuck: true,
|
|
114347
|
-
reason: "repeated_calls",
|
|
114348
|
-
details: `Tool "${first2.name}" called ${windowSize}x with identical args`,
|
|
114349
|
-
suggestedAction: "retry_different"
|
|
114350
|
-
};
|
|
114351
|
-
}
|
|
114352
|
-
}
|
|
114353
|
-
if (this.iterationsWithoutProgress >= this.cfg.maxIterationsWithoutProgress) {
|
|
114354
|
-
return {
|
|
114355
|
-
isStuck: true,
|
|
114356
|
-
reason: "no_progress",
|
|
114357
|
-
details: `${this.iterationsWithoutProgress} iterations without new findings`,
|
|
114358
|
-
suggestedAction: "escalate_model"
|
|
114359
|
-
};
|
|
114360
|
-
}
|
|
114361
|
-
if (this.consecutiveErrors >= this.cfg.maxConsecutiveErrors) {
|
|
114362
|
-
return {
|
|
114363
|
-
isStuck: true,
|
|
114364
|
-
reason: "error_loop",
|
|
114365
|
-
details: `${this.consecutiveErrors} consecutive tool errors`,
|
|
114366
|
-
suggestedAction: "retry_different"
|
|
114367
|
-
};
|
|
114368
|
-
}
|
|
114369
|
-
return { isStuck: false };
|
|
114370
|
-
}
|
|
114371
|
-
// ── Lifecycle ───────────────────────────────────────────────────────
|
|
114372
|
-
/** Reset all state for reuse (e.g., new sub-objective). */
|
|
114373
|
-
reset() {
|
|
114374
|
-
this.toolCallHistory = [];
|
|
114375
|
-
this.findingsCount = 0;
|
|
114376
|
-
this.iterationsWithoutProgress = 0;
|
|
114377
|
-
this.consecutiveErrors = 0;
|
|
114378
|
-
this.startTime = Date.now();
|
|
114379
|
-
}
|
|
114380
|
-
/** Get a diagnostic snapshot (useful for logging/telemetry). */
|
|
114381
|
-
getSnapshot() {
|
|
114382
|
-
return {
|
|
114383
|
-
totalCalls: this.toolCallHistory.length,
|
|
114384
|
-
consecutiveErrors: this.consecutiveErrors,
|
|
114385
|
-
iterationsWithoutProgress: this.iterationsWithoutProgress,
|
|
114386
|
-
findingsCount: this.findingsCount,
|
|
114387
|
-
elapsedMs: Date.now() - this.startTime
|
|
114388
|
-
};
|
|
114389
|
-
}
|
|
114390
|
-
};
|
|
114391
|
-
}
|
|
114392
|
-
});
|
|
114393
|
-
|
|
114394
115424
|
// ../agent/src/safety/circuit-breaker.ts
|
|
114395
115425
|
var CircuitBreaker;
|
|
114396
115426
|
var init_circuit_breaker = __esm({
|
|
@@ -114512,98 +115542,278 @@ var init_circuit_breaker = __esm({
|
|
|
114512
115542
|
}
|
|
114513
115543
|
});
|
|
114514
115544
|
|
|
114515
|
-
// ../agent/src/safety/
|
|
114516
|
-
var
|
|
114517
|
-
var
|
|
114518
|
-
"../agent/src/safety/
|
|
115545
|
+
// ../agent/src/safety/question-detector.ts
|
|
115546
|
+
var QuestionDetector;
|
|
115547
|
+
var init_question_detector = __esm({
|
|
115548
|
+
"../agent/src/safety/question-detector.ts"() {
|
|
114519
115549
|
"use strict";
|
|
114520
|
-
|
|
114521
|
-
|
|
114522
|
-
|
|
114523
|
-
|
|
114524
|
-
|
|
114525
|
-
this.cfg = {
|
|
114526
|
-
maxMessages: config2.maxMessages ?? 50,
|
|
114527
|
-
maxTokensEstimate: config2.maxTokensEstimate ?? 1e5,
|
|
114528
|
-
charsPerToken: config2.charsPerToken ?? 4,
|
|
114529
|
-
recentToKeep: config2.recentToKeep ?? 10
|
|
114530
|
-
};
|
|
114531
|
-
}
|
|
114532
|
-
// ── Recording ───────────────────────────────────────────────────────
|
|
114533
|
-
/** Record a message added to context. */
|
|
114534
|
-
recordMessage(content) {
|
|
114535
|
-
this.messageCount++;
|
|
114536
|
-
this.estimatedTokens += Math.ceil(content.length / this.cfg.charsPerToken);
|
|
115550
|
+
QuestionDetector = class {
|
|
115551
|
+
questionCount = 0;
|
|
115552
|
+
maxQuestionsBeforeCorrection;
|
|
115553
|
+
constructor(opts) {
|
|
115554
|
+
this.maxQuestionsBeforeCorrection = opts?.maxQuestions ?? 2;
|
|
114537
115555
|
}
|
|
114538
|
-
|
|
114539
|
-
|
|
114540
|
-
|
|
114541
|
-
|
|
114542
|
-
|
|
114543
|
-
|
|
114544
|
-
|
|
114545
|
-
|
|
115556
|
+
/**
|
|
115557
|
+
* Analyze an assistant message for question patterns.
|
|
115558
|
+
* Returns detection result with optional correction prompt.
|
|
115559
|
+
*/
|
|
115560
|
+
detect(assistantMessage) {
|
|
115561
|
+
const questions = this.extractQuestions(assistantMessage);
|
|
115562
|
+
if (questions.length === 0) {
|
|
115563
|
+
this.questionCount = 0;
|
|
115564
|
+
return { isQuestion: false, questions: [] };
|
|
114546
115565
|
}
|
|
114547
|
-
|
|
115566
|
+
this.questionCount++;
|
|
115567
|
+
if (this.questionCount >= this.maxQuestionsBeforeCorrection) {
|
|
114548
115568
|
return {
|
|
114549
|
-
|
|
114550
|
-
|
|
115569
|
+
isQuestion: true,
|
|
115570
|
+
questions,
|
|
115571
|
+
correctionPrompt: this.buildCorrectionPrompt(questions)
|
|
114551
115572
|
};
|
|
114552
115573
|
}
|
|
114553
|
-
return {
|
|
115574
|
+
return { isQuestion: true, questions };
|
|
115575
|
+
}
|
|
115576
|
+
extractQuestions(text7) {
|
|
115577
|
+
const questions = [];
|
|
115578
|
+
const questionLines = text7.split("\n").filter((line) => {
|
|
115579
|
+
const trimmed = line.trim();
|
|
115580
|
+
return trimmed.endsWith("?") && trimmed.length > 10;
|
|
115581
|
+
});
|
|
115582
|
+
questions.push(...questionLines.map((q) => q.trim()));
|
|
115583
|
+
const indirectPatterns = [
|
|
115584
|
+
/(?:could you|would you|can you|do you want|should I|shall I|do you prefer|which (?:one|option)|please (?:confirm|specify|clarify|tell me|let me know))/gi,
|
|
115585
|
+
/(?:voulez-vous|souhaitez-vous|préférez-vous|dois-je|faut-il|quel(?:le)? (?:est|sont)|pouvez-vous (?:me dire|confirmer|préciser))/gi
|
|
115586
|
+
];
|
|
115587
|
+
for (const pattern of indirectPatterns) {
|
|
115588
|
+
const matches = text7.match(pattern);
|
|
115589
|
+
if (matches) {
|
|
115590
|
+
questions.push(...matches.map((m) => m.trim()));
|
|
115591
|
+
}
|
|
115592
|
+
}
|
|
115593
|
+
return questions;
|
|
115594
|
+
}
|
|
115595
|
+
buildCorrectionPrompt(questions) {
|
|
115596
|
+
return `[System \u2014 Autonomous Mode Correction]
|
|
115597
|
+
You are in AUTONOMOUS mode. Do NOT ask questions \u2014 take action instead.
|
|
115598
|
+
|
|
115599
|
+
You asked: ${questions.slice(0, 3).map((q) => `"${q}"`).join(", ")}
|
|
115600
|
+
|
|
115601
|
+
Instead of asking:
|
|
115602
|
+
- Make a reasonable decision based on available information
|
|
115603
|
+
- If multiple options exist, choose the most common/safe one
|
|
115604
|
+
- If information is missing, use your tools to find it (web_search, file_read, etc.)
|
|
115605
|
+
- If truly blocked, document what you need and move on to the next task
|
|
115606
|
+
|
|
115607
|
+
DO NOT ask any more questions. Take action now.`;
|
|
115608
|
+
}
|
|
115609
|
+
/** Reset counter */
|
|
115610
|
+
reset() {
|
|
115611
|
+
this.questionCount = 0;
|
|
115612
|
+
}
|
|
115613
|
+
};
|
|
115614
|
+
}
|
|
115615
|
+
});
|
|
115616
|
+
|
|
115617
|
+
// ../agent/src/safety/completion-detector.ts
|
|
115618
|
+
var CompletionDetector;
|
|
115619
|
+
var init_completion_detector = __esm({
|
|
115620
|
+
"../agent/src/safety/completion-detector.ts"() {
|
|
115621
|
+
"use strict";
|
|
115622
|
+
CompletionDetector = class {
|
|
115623
|
+
noToolCallStreak = 0;
|
|
115624
|
+
minNoToolStreak;
|
|
115625
|
+
constructor(opts) {
|
|
115626
|
+
this.minNoToolStreak = opts?.minNoToolStreak ?? 2;
|
|
114554
115627
|
}
|
|
114555
|
-
// ── Rotation helpers ────────────────────────────────────────────────
|
|
114556
115628
|
/**
|
|
114557
|
-
*
|
|
114558
|
-
*
|
|
114559
|
-
* Strategy:
|
|
114560
|
-
* - Preserve the system message verbatim
|
|
114561
|
-
* - Summarize the middle messages into a compact block
|
|
114562
|
-
* - Keep the last N recent messages verbatim
|
|
114563
|
-
*
|
|
114564
|
-
* Returns the summary text to inject as a single assistant message
|
|
114565
|
-
* between the system prompt and the recent messages.
|
|
115629
|
+
* Check if the agent has truly completed its task.
|
|
115630
|
+
* Requires both heuristic indicators and an explicit signal.
|
|
114566
115631
|
*/
|
|
114567
|
-
|
|
114568
|
-
|
|
114569
|
-
|
|
114570
|
-
|
|
114571
|
-
|
|
114572
|
-
const middle = nonSystem.slice(0, Math.max(0, nonSystem.length - keepCount));
|
|
114573
|
-
const parts = [];
|
|
114574
|
-
if (system) {
|
|
114575
|
-
parts.push("[System prompt preserved]");
|
|
114576
|
-
}
|
|
114577
|
-
if (middle.length > 0) {
|
|
114578
|
-
const toolCalls = middle.filter(
|
|
114579
|
-
(m) => m.content.includes("tool_use") || m.content.includes("function_call") || m.content.includes("tool_result")
|
|
114580
|
-
).length;
|
|
114581
|
-
const findings = middle.filter(
|
|
114582
|
-
(m) => m.content.includes("finding") || m.content.includes("result") || m.content.includes("discovered")
|
|
114583
|
-
).length;
|
|
114584
|
-
const errors = middle.filter(
|
|
114585
|
-
(m) => m.content.includes("error") || m.content.includes("failed")
|
|
114586
|
-
).length;
|
|
114587
|
-
parts.push(
|
|
114588
|
-
`[${middle.length} earlier messages summarized: ${toolCalls} tool interactions, ${findings} results/findings, ${errors} errors]`
|
|
114589
|
-
);
|
|
115632
|
+
check(assistantMessage, hadToolCalls) {
|
|
115633
|
+
if (hadToolCalls) {
|
|
115634
|
+
this.noToolCallStreak = 0;
|
|
115635
|
+
} else {
|
|
115636
|
+
this.noToolCallStreak++;
|
|
114590
115637
|
}
|
|
114591
|
-
|
|
114592
|
-
|
|
115638
|
+
const heuristicScore = this.computeHeuristic(assistantMessage, hadToolCalls);
|
|
115639
|
+
const hasExplicitSignal = this.hasExplicitCompletionSignal(assistantMessage);
|
|
115640
|
+
const isComplete = heuristicScore >= 0.7 && hasExplicitSignal;
|
|
115641
|
+
return {
|
|
115642
|
+
isComplete,
|
|
115643
|
+
heuristicScore,
|
|
115644
|
+
hasExplicitSignal,
|
|
115645
|
+
reason: isComplete ? "Both heuristic and explicit signals indicate completion" : !hasExplicitSignal ? "No explicit completion signal found" : `Heuristic score too low (${heuristicScore.toFixed(2)})`
|
|
115646
|
+
};
|
|
115647
|
+
}
|
|
115648
|
+
computeHeuristic(message, hadToolCalls) {
|
|
115649
|
+
let score = 0;
|
|
115650
|
+
if (!hadToolCalls) score += 0.3;
|
|
115651
|
+
if (this.noToolCallStreak >= this.minNoToolStreak) score += 0.3;
|
|
115652
|
+
const conclusionPatterns = [
|
|
115653
|
+
/(?:in summary|en résumé|to summarize|pour résumer|in conclusion|en conclusion)/i,
|
|
115654
|
+
/(?:here are the (?:results|findings)|voici les (?:résultats|conclusions))/i,
|
|
115655
|
+
/(?:the (?:task|mission|objective|work) is (?:complete|done|finished))/i,
|
|
115656
|
+
/(?:la (?:tâche|mission) est (?:terminée|complète|finie))/i,
|
|
115657
|
+
/(?:I have (?:completed|finished|done)|j'ai (?:terminé|fini|complété))/i
|
|
115658
|
+
];
|
|
115659
|
+
const conclusionMatches = conclusionPatterns.filter((p8) => p8.test(message)).length;
|
|
115660
|
+
score += Math.min(conclusionMatches * 0.15, 0.4);
|
|
115661
|
+
return Math.min(score, 1);
|
|
115662
|
+
}
|
|
115663
|
+
hasExplicitCompletionSignal(message) {
|
|
115664
|
+
const explicitSignals = [
|
|
115665
|
+
/\[COMPLETE\]/i,
|
|
115666
|
+
/\[DONE\]/i,
|
|
115667
|
+
/\[TASK_COMPLETE\]/i,
|
|
115668
|
+
/\[MISSION_COMPLETE\]/i,
|
|
115669
|
+
/EXIT_SIGNAL:\s*COMPLETE/i,
|
|
115670
|
+
// JSON output with findings (structured output = done)
|
|
115671
|
+
/^\s*\[[\s\S]*"type"[\s\S]*"title"[\s\S]*"confidence"[\s\S]*\]\s*$/m,
|
|
115672
|
+
// Markdown report header (report = done)
|
|
115673
|
+
/^#\s+.*(?:Report|Rapport|Results|Résultats)/m
|
|
115674
|
+
];
|
|
115675
|
+
return explicitSignals.some((p8) => p8.test(message));
|
|
114593
115676
|
}
|
|
114594
|
-
|
|
114595
|
-
/** Reset counters after rotation. */
|
|
115677
|
+
/** Reset state */
|
|
114596
115678
|
reset() {
|
|
114597
|
-
this.
|
|
114598
|
-
this.estimatedTokens = 0;
|
|
115679
|
+
this.noToolCallStreak = 0;
|
|
114599
115680
|
}
|
|
114600
|
-
|
|
114601
|
-
|
|
115681
|
+
};
|
|
115682
|
+
}
|
|
115683
|
+
});
|
|
115684
|
+
|
|
115685
|
+
// ../agent/src/safety/handoff-generator.ts
|
|
115686
|
+
var HandoffGenerator;
|
|
115687
|
+
var init_handoff_generator = __esm({
|
|
115688
|
+
"../agent/src/safety/handoff-generator.ts"() {
|
|
115689
|
+
"use strict";
|
|
115690
|
+
HandoffGenerator = class {
|
|
115691
|
+
/**
|
|
115692
|
+
* Generate a handoff document from the agent's message history.
|
|
115693
|
+
* This is a heuristic extraction — no LLM call needed.
|
|
115694
|
+
*/
|
|
115695
|
+
generate(opts) {
|
|
115696
|
+
const { objective, messages, toolCalls, exitReason, costUsd } = opts;
|
|
115697
|
+
const accomplished = this.extractAccomplished(messages);
|
|
115698
|
+
const remaining = this.extractRemaining(messages, exitReason);
|
|
115699
|
+
const currentState = this.buildCurrentState(toolCalls);
|
|
115700
|
+
const decisions = this.extractDecisions(messages);
|
|
115701
|
+
const errors = toolCalls.filter((tc) => tc.isError).map((tc) => ({ error: `${tc.name}: ${tc.result.substring(0, 200)}`, resolution: "Attempted" }));
|
|
115702
|
+
const toolUsage = {};
|
|
115703
|
+
for (const tc of toolCalls) {
|
|
115704
|
+
toolUsage[tc.name] = (toolUsage[tc.name] || 0) + 1;
|
|
115705
|
+
}
|
|
115706
|
+
const nextSteps = this.suggestNextSteps(remaining, errors, exitReason);
|
|
114602
115707
|
return {
|
|
114603
|
-
|
|
114604
|
-
|
|
115708
|
+
objective,
|
|
115709
|
+
accomplished,
|
|
115710
|
+
remaining,
|
|
115711
|
+
currentState,
|
|
115712
|
+
decisions,
|
|
115713
|
+
errors,
|
|
115714
|
+
nextSteps,
|
|
115715
|
+
toolUsage,
|
|
115716
|
+
costUsd,
|
|
115717
|
+
timestamp: Date.now()
|
|
114605
115718
|
};
|
|
114606
115719
|
}
|
|
115720
|
+
/** Convert handoff to Markdown for storage/display */
|
|
115721
|
+
toMarkdown(handoff) {
|
|
115722
|
+
const sections = [
|
|
115723
|
+
`# Session Handoff`,
|
|
115724
|
+
``,
|
|
115725
|
+
`**Objective:** ${handoff.objective}`,
|
|
115726
|
+
`**Cost:** $${handoff.costUsd.toFixed(4)}`,
|
|
115727
|
+
`**Date:** ${new Date(handoff.timestamp).toISOString()}`,
|
|
115728
|
+
``,
|
|
115729
|
+
`## Accomplished`,
|
|
115730
|
+
...handoff.accomplished.map((a) => `- ${a}`),
|
|
115731
|
+
``,
|
|
115732
|
+
`## Remaining`,
|
|
115733
|
+
...handoff.remaining.map((r) => `- ${r}`),
|
|
115734
|
+
``,
|
|
115735
|
+
`## Current State`,
|
|
115736
|
+
handoff.currentState,
|
|
115737
|
+
``,
|
|
115738
|
+
`## Decisions`,
|
|
115739
|
+
...handoff.decisions.map((d) => `- **${d.decision}**: ${d.reason}`),
|
|
115740
|
+
``,
|
|
115741
|
+
`## Errors (${handoff.errors.length})`,
|
|
115742
|
+
...handoff.errors.map((e) => `- ${e.error}`),
|
|
115743
|
+
``,
|
|
115744
|
+
`## Next Steps`,
|
|
115745
|
+
...handoff.nextSteps.map((s) => `- ${s}`),
|
|
115746
|
+
``,
|
|
115747
|
+
`## Tool Usage`,
|
|
115748
|
+
...Object.entries(handoff.toolUsage).map(([name, count]) => `- ${name}: ${count}x`)
|
|
115749
|
+
];
|
|
115750
|
+
return sections.join("\n");
|
|
115751
|
+
}
|
|
115752
|
+
extractAccomplished(messages) {
|
|
115753
|
+
const accomplished = [];
|
|
115754
|
+
for (const msg of messages) {
|
|
115755
|
+
if (msg.role !== "assistant") continue;
|
|
115756
|
+
const lines = msg.content.split("\n");
|
|
115757
|
+
for (const line of lines) {
|
|
115758
|
+
if (/(?:✅|done|completed|finished|created|wrote|found|discovered|built|implemented)/i.test(line) && line.length > 10 && line.length < 200) {
|
|
115759
|
+
accomplished.push(line.replace(/^[-*•]\s*/, "").trim());
|
|
115760
|
+
}
|
|
115761
|
+
}
|
|
115762
|
+
}
|
|
115763
|
+
return accomplished.slice(0, 10);
|
|
115764
|
+
}
|
|
115765
|
+
extractRemaining(messages, exitReason) {
|
|
115766
|
+
const remaining = [];
|
|
115767
|
+
if (exitReason === "max_iterations") remaining.push("Max iterations reached \u2014 work may be incomplete");
|
|
115768
|
+
if (exitReason === "budget_exceeded") remaining.push("Budget exhausted \u2014 work may be incomplete");
|
|
115769
|
+
if (exitReason === "aborted") remaining.push("Execution was aborted");
|
|
115770
|
+
const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
|
|
115771
|
+
if (lastAssistant) {
|
|
115772
|
+
const todoPattern = /(?:todo|remaining|still need|next|à faire|reste)/i;
|
|
115773
|
+
const lines = lastAssistant.content.split("\n");
|
|
115774
|
+
for (const line of lines) {
|
|
115775
|
+
if (todoPattern.test(line) && line.length > 10 && line.length < 200) {
|
|
115776
|
+
remaining.push(line.replace(/^[-*•]\s*/, "").trim());
|
|
115777
|
+
}
|
|
115778
|
+
}
|
|
115779
|
+
}
|
|
115780
|
+
return remaining.slice(0, 10);
|
|
115781
|
+
}
|
|
115782
|
+
buildCurrentState(toolCalls) {
|
|
115783
|
+
const filesWritten = toolCalls.filter((tc) => tc.name === "file_write").map((tc) => String(tc.args.path || "")).filter(Boolean);
|
|
115784
|
+
const commandsRun = toolCalls.filter((tc) => tc.name === "shell_exec").map((tc) => String(tc.args.command || "").substring(0, 100)).filter(Boolean);
|
|
115785
|
+
let state = "";
|
|
115786
|
+
if (filesWritten.length > 0) state += `Files modified: ${filesWritten.join(", ")}
|
|
115787
|
+
`;
|
|
115788
|
+
if (commandsRun.length > 0) state += `Commands run: ${commandsRun.slice(-5).join("; ")}`;
|
|
115789
|
+
return state || "No filesystem changes detected.";
|
|
115790
|
+
}
|
|
115791
|
+
extractDecisions(messages) {
|
|
115792
|
+
const decisions = [];
|
|
115793
|
+
for (const msg of messages) {
|
|
115794
|
+
if (msg.role !== "assistant") continue;
|
|
115795
|
+
const patterns = [
|
|
115796
|
+
/I (?:chose|decided|selected|went with|picked) (.{10,80}) (?:because|since|as|due to) (.{10,120})/gi,
|
|
115797
|
+
/(?:j'ai choisi|j'ai décidé|j'ai opté pour) (.{10,80}) (?:car|parce que|puisque) (.{10,120})/gi
|
|
115798
|
+
];
|
|
115799
|
+
for (const pattern of patterns) {
|
|
115800
|
+
let match;
|
|
115801
|
+
while ((match = pattern.exec(msg.content)) !== null && decisions.length < 5) {
|
|
115802
|
+
decisions.push({ decision: match[1].trim(), reason: match[2].trim() });
|
|
115803
|
+
}
|
|
115804
|
+
}
|
|
115805
|
+
}
|
|
115806
|
+
return decisions;
|
|
115807
|
+
}
|
|
115808
|
+
suggestNextSteps(remaining, errors, exitReason) {
|
|
115809
|
+
const steps = [];
|
|
115810
|
+
if (remaining.length > 0) steps.push(`Continue with: ${remaining[0]}`);
|
|
115811
|
+
if (errors.length > 0) steps.push(`Investigate ${errors.length} error(s) from previous session`);
|
|
115812
|
+
if (exitReason === "budget_exceeded") steps.push("Consider increasing budget for next run");
|
|
115813
|
+
if (exitReason === "max_iterations") steps.push("Consider increasing max iterations or simplifying the objective");
|
|
115814
|
+
if (steps.length === 0) steps.push("Review results and plan next action");
|
|
115815
|
+
return steps;
|
|
115816
|
+
}
|
|
114607
115817
|
};
|
|
114608
115818
|
}
|
|
114609
115819
|
});
|
|
@@ -114615,16 +115825,19 @@ var init_safety = __esm({
|
|
|
114615
115825
|
init_stuck_detector();
|
|
114616
115826
|
init_circuit_breaker();
|
|
114617
115827
|
init_context_rotator();
|
|
115828
|
+
init_question_detector();
|
|
115829
|
+
init_completion_detector();
|
|
115830
|
+
init_handoff_generator();
|
|
114618
115831
|
}
|
|
114619
115832
|
});
|
|
114620
115833
|
|
|
114621
115834
|
// ../agent/src/mission/tool-factory.ts
|
|
114622
115835
|
import { randomUUID as randomUUID33 } from "node:crypto";
|
|
114623
|
-
import { writeFileSync as
|
|
115836
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync11 } from "node:fs";
|
|
114624
115837
|
import { homedir as homedir4 } from "node:os";
|
|
114625
|
-
import { join as
|
|
115838
|
+
import { join as join17 } from "node:path";
|
|
114626
115839
|
function getDefaultToolsDir() {
|
|
114627
|
-
return
|
|
115840
|
+
return join17(homedir4(), ".nestor", "tools");
|
|
114628
115841
|
}
|
|
114629
115842
|
function createDefaultToolFactory(adapter, toolsDir) {
|
|
114630
115843
|
return new ToolFactory(adapter, toolsDir ?? getDefaultToolsDir());
|
|
@@ -114637,7 +115850,7 @@ var init_tool_factory = __esm({
|
|
|
114637
115850
|
constructor(adapter, toolsDir) {
|
|
114638
115851
|
this.adapter = adapter;
|
|
114639
115852
|
this.toolsDir = toolsDir;
|
|
114640
|
-
if (!
|
|
115853
|
+
if (!existsSync11(toolsDir)) mkdirSync6(toolsDir, { recursive: true });
|
|
114641
115854
|
}
|
|
114642
115855
|
/**
|
|
114643
115856
|
* Generate a new tool from a natural language description.
|
|
@@ -114814,13 +116027,13 @@ Fix the tool code so it passes its test. Respond ONLY with valid JSON:
|
|
|
114814
116027
|
* Save tool to disk: source, compiled JS, test, and manifest.
|
|
114815
116028
|
*/
|
|
114816
116029
|
saveTool(tool) {
|
|
114817
|
-
const toolDir =
|
|
114818
|
-
if (!
|
|
114819
|
-
|
|
114820
|
-
|
|
114821
|
-
|
|
114822
|
-
|
|
114823
|
-
|
|
116030
|
+
const toolDir = join17(this.toolsDir, tool.name);
|
|
116031
|
+
if (!existsSync11(toolDir)) mkdirSync6(toolDir, { recursive: true });
|
|
116032
|
+
writeFileSync8(join17(toolDir, "tool.ts"), tool.code);
|
|
116033
|
+
writeFileSync8(join17(toolDir, "tool.js"), tool.compiledCode);
|
|
116034
|
+
writeFileSync8(join17(toolDir, "test.js"), tool.testCode);
|
|
116035
|
+
writeFileSync8(
|
|
116036
|
+
join17(toolDir, "manifest.json"),
|
|
114824
116037
|
JSON.stringify(
|
|
114825
116038
|
{
|
|
114826
116039
|
id: tool.id,
|
|
@@ -115395,13 +116608,172 @@ Analyze and return the JSON structure described in the system prompt.`;
|
|
|
115395
116608
|
}
|
|
115396
116609
|
});
|
|
115397
116610
|
|
|
116611
|
+
// ../agent/src/mission/capability-research.ts
|
|
116612
|
+
var capability_research_exports = {};
|
|
116613
|
+
__export(capability_research_exports, {
|
|
116614
|
+
CapabilityResearchEngine: () => CapabilityResearchEngine
|
|
116615
|
+
});
|
|
116616
|
+
var CapabilityResearchEngine;
|
|
116617
|
+
var init_capability_research = __esm({
|
|
116618
|
+
"../agent/src/mission/capability-research.ts"() {
|
|
116619
|
+
"use strict";
|
|
116620
|
+
CapabilityResearchEngine = class {
|
|
116621
|
+
constructor(adapter, orchestratorStore) {
|
|
116622
|
+
this.adapter = adapter;
|
|
116623
|
+
this.orchestratorStore = orchestratorStore;
|
|
116624
|
+
}
|
|
116625
|
+
cache = /* @__PURE__ */ new Map();
|
|
116626
|
+
/**
|
|
116627
|
+
* Research capabilities for a mission type.
|
|
116628
|
+
* Returns cached results if still valid, otherwise does fresh research.
|
|
116629
|
+
*/
|
|
116630
|
+
async research(type, objective) {
|
|
116631
|
+
const cached = this.cache.get(type);
|
|
116632
|
+
if (cached && Date.now() - cached.timestamp < cached.ttlMs) {
|
|
116633
|
+
return cached;
|
|
116634
|
+
}
|
|
116635
|
+
if (this.orchestratorStore) {
|
|
116636
|
+
const orch = this.orchestratorStore.get(type);
|
|
116637
|
+
if (orch.toolRecommendations.length > 0 && orch.missionCount > 3) {
|
|
116638
|
+
const fromOrch = {
|
|
116639
|
+
domain: type,
|
|
116640
|
+
recommendedTools: orch.toolRecommendations.map((t) => ({
|
|
116641
|
+
name: t,
|
|
116642
|
+
description: "",
|
|
116643
|
+
source: "builtin",
|
|
116644
|
+
priority: "recommended"
|
|
116645
|
+
})),
|
|
116646
|
+
methodologies: [],
|
|
116647
|
+
pitfalls: orch.knowledgeBase.failedPatterns.map(
|
|
116648
|
+
(p8) => `Previously failed pattern: ${p8.subObjectiveNames.join(" \u2192 ")} (used ${p8.usageCount} time${p8.usageCount > 1 ? "s" : ""}, score: ${p8.score.toFixed(2)})`
|
|
116649
|
+
),
|
|
116650
|
+
resources: [],
|
|
116651
|
+
timestamp: Date.now(),
|
|
116652
|
+
ttlMs: 7 * 24 * 60 * 60 * 1e3
|
|
116653
|
+
};
|
|
116654
|
+
this.cache.set(type, fromOrch);
|
|
116655
|
+
return fromOrch;
|
|
116656
|
+
}
|
|
116657
|
+
}
|
|
116658
|
+
const result = await this.doResearch(type, objective);
|
|
116659
|
+
this.cache.set(type, result);
|
|
116660
|
+
return result;
|
|
116661
|
+
}
|
|
116662
|
+
async doResearch(type, objective) {
|
|
116663
|
+
const domainHints = this.getDomainHints(type);
|
|
116664
|
+
const prompt = `You are a capability researcher for an AI agent platform.
|
|
116665
|
+
Given a mission type and objective, recommend the best tools, methodologies, and resources.
|
|
116666
|
+
|
|
116667
|
+
Mission type: ${type}
|
|
116668
|
+
Objective: ${objective}
|
|
116669
|
+
|
|
116670
|
+
Based on your knowledge of ${type} best practices, respond with JSON:
|
|
116671
|
+
{
|
|
116672
|
+
"recommendedTools": [
|
|
116673
|
+
{ "name": "tool_name", "description": "what it does", "source": "builtin|npm|github|api", "url": "optional", "priority": "essential|recommended|optional" }
|
|
116674
|
+
],
|
|
116675
|
+
"methodologies": [
|
|
116676
|
+
{ "name": "method name", "description": "brief description", "steps": ["step1", "step2"] }
|
|
116677
|
+
],
|
|
116678
|
+
"pitfalls": ["common mistake 1", "common mistake 2"],
|
|
116679
|
+
"resources": [
|
|
116680
|
+
{ "title": "resource title", "url": "https://...", "type": "article|repo|docs|tutorial" }
|
|
116681
|
+
]
|
|
116682
|
+
}
|
|
116683
|
+
|
|
116684
|
+
${domainHints}
|
|
116685
|
+
|
|
116686
|
+
Focus on tools that work locally (no cloud-only services) and can be used by an AI agent.
|
|
116687
|
+
Respond ONLY with JSON.`;
|
|
116688
|
+
try {
|
|
116689
|
+
const response = await this.adapter.chat([
|
|
116690
|
+
{
|
|
116691
|
+
role: "system",
|
|
116692
|
+
content: "You are a capability researcher. Respond only with JSON."
|
|
116693
|
+
},
|
|
116694
|
+
{ role: "user", content: prompt }
|
|
116695
|
+
]);
|
|
116696
|
+
let jsonStr = response.content;
|
|
116697
|
+
const codeBlockMatch = jsonStr.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
116698
|
+
if (codeBlockMatch) jsonStr = codeBlockMatch[1].trim();
|
|
116699
|
+
const match = jsonStr.match(/\{[\s\S]*\}/);
|
|
116700
|
+
if (match) jsonStr = match[0];
|
|
116701
|
+
const parsed = JSON.parse(jsonStr);
|
|
116702
|
+
return {
|
|
116703
|
+
domain: type,
|
|
116704
|
+
recommendedTools: Array.isArray(parsed.recommendedTools) ? parsed.recommendedTools : [],
|
|
116705
|
+
methodologies: Array.isArray(parsed.methodologies) ? parsed.methodologies : [],
|
|
116706
|
+
pitfalls: Array.isArray(parsed.pitfalls) ? parsed.pitfalls : [],
|
|
116707
|
+
resources: Array.isArray(parsed.resources) ? parsed.resources : [],
|
|
116708
|
+
timestamp: Date.now(),
|
|
116709
|
+
ttlMs: 7 * 24 * 60 * 60 * 1e3
|
|
116710
|
+
// 7 days
|
|
116711
|
+
};
|
|
116712
|
+
} catch {
|
|
116713
|
+
return {
|
|
116714
|
+
domain: type,
|
|
116715
|
+
recommendedTools: [],
|
|
116716
|
+
methodologies: [],
|
|
116717
|
+
pitfalls: [],
|
|
116718
|
+
resources: [],
|
|
116719
|
+
timestamp: Date.now(),
|
|
116720
|
+
ttlMs: 24 * 60 * 60 * 1e3
|
|
116721
|
+
// retry after 1 day on failure
|
|
116722
|
+
};
|
|
116723
|
+
}
|
|
116724
|
+
}
|
|
116725
|
+
/**
|
|
116726
|
+
* Inject research results into the mission planning context.
|
|
116727
|
+
* Returns a context block to prepend to the decomposition prompt.
|
|
116728
|
+
*/
|
|
116729
|
+
toContextBlock(result) {
|
|
116730
|
+
if (result.recommendedTools.length === 0 && result.methodologies.length === 0) {
|
|
116731
|
+
return "";
|
|
116732
|
+
}
|
|
116733
|
+
const sections = ["CAPABILITY RESEARCH (pre-mission analysis):"];
|
|
116734
|
+
if (result.recommendedTools.length > 0) {
|
|
116735
|
+
sections.push("Recommended tools:");
|
|
116736
|
+
for (const t of result.recommendedTools.slice(0, 8)) {
|
|
116737
|
+
sections.push(` - ${t.name} (${t.priority}): ${t.description}`);
|
|
116738
|
+
}
|
|
116739
|
+
}
|
|
116740
|
+
if (result.methodologies.length > 0) {
|
|
116741
|
+
sections.push("Recommended methodology:");
|
|
116742
|
+
const best = result.methodologies[0];
|
|
116743
|
+
sections.push(` ${best.name}: ${best.steps.join(" \u2192 ")}`);
|
|
116744
|
+
}
|
|
116745
|
+
if (result.pitfalls.length > 0) {
|
|
116746
|
+
sections.push("Known pitfalls to avoid:");
|
|
116747
|
+
for (const p8 of result.pitfalls.slice(0, 5)) {
|
|
116748
|
+
sections.push(` - ${p8}`);
|
|
116749
|
+
}
|
|
116750
|
+
}
|
|
116751
|
+
return sections.join("\n");
|
|
116752
|
+
}
|
|
116753
|
+
// ─── Private Helpers ────────────────────────────────────────────────
|
|
116754
|
+
getDomainHints(type) {
|
|
116755
|
+
const hints = {
|
|
116756
|
+
osint: "For osint missions, consider: OSINT tools like Maltego alternatives, Wayback Machine, WHOIS, social media scrapers, SIRENE, public records databases",
|
|
116757
|
+
research: "For research missions, consider: Google Scholar, arxiv, Semantic Scholar, news aggregators, RSS feeds, citation graphs",
|
|
116758
|
+
code: "For code missions, consider: linters, formatters, test frameworks, build tools, CI/CD, static analysis",
|
|
116759
|
+
audit: "For audit missions, consider: static analyzers, dependency checkers, fuzzing tools, CVE databases, SAST/DAST",
|
|
116760
|
+
creation: "For creation missions, consider: writing assistants, image generators, template engines, CMS tools, Markdown processors",
|
|
116761
|
+
analysis: "For analysis missions, consider: data processing libraries, visualization tools, statistical frameworks, ML frameworks",
|
|
116762
|
+
custom: "Consider what specialized tools and methods would best serve this specific objective."
|
|
116763
|
+
};
|
|
116764
|
+
return hints[type] || hints.custom;
|
|
116765
|
+
}
|
|
116766
|
+
};
|
|
116767
|
+
}
|
|
116768
|
+
});
|
|
116769
|
+
|
|
115398
116770
|
// ../agent/src/mission/orchestrator-store.ts
|
|
115399
116771
|
var orchestrator_store_exports = {};
|
|
115400
116772
|
__export(orchestrator_store_exports, {
|
|
115401
116773
|
OrchestratorStore: () => OrchestratorStore
|
|
115402
116774
|
});
|
|
115403
|
-
import { existsSync as
|
|
115404
|
-
import { join as
|
|
116775
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
|
|
116776
|
+
import { join as join18 } from "node:path";
|
|
115405
116777
|
import { homedir as homedir5 } from "node:os";
|
|
115406
116778
|
function arraysEqual(a, b) {
|
|
115407
116779
|
if (a.length !== b.length) return false;
|
|
@@ -115411,11 +116783,11 @@ var ORCHESTRATORS_DIR, OrchestratorStore;
|
|
|
115411
116783
|
var init_orchestrator_store = __esm({
|
|
115412
116784
|
"../agent/src/mission/orchestrator-store.ts"() {
|
|
115413
116785
|
"use strict";
|
|
115414
|
-
ORCHESTRATORS_DIR =
|
|
116786
|
+
ORCHESTRATORS_DIR = join18(homedir5(), ".nestor", "orchestrators");
|
|
115415
116787
|
OrchestratorStore = class {
|
|
115416
116788
|
constructor(baseDir = ORCHESTRATORS_DIR) {
|
|
115417
116789
|
this.baseDir = baseDir;
|
|
115418
|
-
if (!
|
|
116790
|
+
if (!existsSync12(baseDir)) mkdirSync7(baseDir, { recursive: true });
|
|
115419
116791
|
}
|
|
115420
116792
|
cache = /* @__PURE__ */ new Map();
|
|
115421
116793
|
/**
|
|
@@ -115425,9 +116797,9 @@ var init_orchestrator_store = __esm({
|
|
|
115425
116797
|
get(type) {
|
|
115426
116798
|
if (this.cache.has(type)) return this.cache.get(type);
|
|
115427
116799
|
const filePath = this.filePath(type);
|
|
115428
|
-
if (
|
|
116800
|
+
if (existsSync12(filePath)) {
|
|
115429
116801
|
try {
|
|
115430
|
-
const data = JSON.parse(
|
|
116802
|
+
const data = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
115431
116803
|
this.cache.set(type, data);
|
|
115432
116804
|
return data;
|
|
115433
116805
|
} catch {
|
|
@@ -115525,7 +116897,7 @@ var init_orchestrator_store = __esm({
|
|
|
115525
116897
|
];
|
|
115526
116898
|
return types.map((type) => {
|
|
115527
116899
|
const filePath = this.filePath(type);
|
|
115528
|
-
if (!
|
|
116900
|
+
if (!existsSync12(filePath)) return null;
|
|
115529
116901
|
const orch = this.get(type);
|
|
115530
116902
|
return {
|
|
115531
116903
|
type: orch.type,
|
|
@@ -115562,12 +116934,12 @@ var init_orchestrator_store = __esm({
|
|
|
115562
116934
|
}
|
|
115563
116935
|
// ─── Private ──────────────────────────────────────────────────────────
|
|
115564
116936
|
filePath(type) {
|
|
115565
|
-
return
|
|
116937
|
+
return join18(this.baseDir, type, "orchestrator.json");
|
|
115566
116938
|
}
|
|
115567
116939
|
save(orch) {
|
|
115568
|
-
const dir =
|
|
115569
|
-
if (!
|
|
115570
|
-
|
|
116940
|
+
const dir = join18(this.baseDir, orch.type);
|
|
116941
|
+
if (!existsSync12(dir)) mkdirSync7(dir, { recursive: true });
|
|
116942
|
+
writeFileSync9(this.filePath(orch.type), JSON.stringify(orch, null, 2));
|
|
115571
116943
|
this.cache.set(orch.type, orch);
|
|
115572
116944
|
}
|
|
115573
116945
|
createDefault(type) {
|
|
@@ -115782,8 +117154,19 @@ var init_controller = __esm({
|
|
|
115782
117154
|
async plan(input, options) {
|
|
115783
117155
|
const tenantId = options?.tenantId ?? "default";
|
|
115784
117156
|
const type = this.detectMissionType(input);
|
|
117157
|
+
let capabilityContext = "";
|
|
117158
|
+
try {
|
|
117159
|
+
const { CapabilityResearchEngine: CapabilityResearchEngine2 } = await Promise.resolve().then(() => (init_capability_research(), capability_research_exports));
|
|
117160
|
+
const researcher = new CapabilityResearchEngine2(
|
|
117161
|
+
this.agentFactory.getDefaultAdapter()
|
|
117162
|
+
// orchestratorStore could be wired here if available on the controller
|
|
117163
|
+
);
|
|
117164
|
+
const research = await researcher.research(type, input);
|
|
117165
|
+
capabilityContext = researcher.toContextBlock(research);
|
|
117166
|
+
} catch {
|
|
117167
|
+
}
|
|
115785
117168
|
const similar = await this.inventory.findSimilarMissions(input);
|
|
115786
|
-
const decomposition = await this.decompose(input, type, similar);
|
|
117169
|
+
const decomposition = await this.decompose(input, type, similar, capabilityContext);
|
|
115787
117170
|
const defaultMaxIterations = (() => {
|
|
115788
117171
|
switch (type) {
|
|
115789
117172
|
case "osint":
|
|
@@ -116151,7 +117534,7 @@ Report generation failed. Mission completed with ${mission.findings.length} find
|
|
|
116151
117534
|
return "custom";
|
|
116152
117535
|
}
|
|
116153
117536
|
// ─── Decompose ─────────────────────────────────────────────────────
|
|
116154
|
-
async decompose(input, type, similar) {
|
|
117537
|
+
async decompose(input, type, similar, capabilityContext) {
|
|
116155
117538
|
const methodGuide = {
|
|
116156
117539
|
osint: `For OSINT missions, structure sub-objectives by METHOD, not by category:
|
|
116157
117540
|
1. Entity disambiguation \u2014 multi-engine search to find the correct subject among homonyms
|
|
@@ -116214,7 +117597,8 @@ ${methodGuide[type] || methodGuide.custom}
|
|
|
116214
117597
|
${similar.length > 0 ? `Similar past missions found (learn from their structure):
|
|
116215
117598
|
${similar.map((m) => `- ${m.title} (${m.subObjectives.length} sub-objectives, score: ${m.evaluation?.overall ?? "N/A"})`).join("\n")}` : "No similar past missions found."}
|
|
116216
117599
|
|
|
116217
|
-
|
|
117600
|
+
${capabilityContext ? `${capabilityContext}
|
|
117601
|
+
` : ""}The "type" field of each sub-objective should be one of:
|
|
116218
117602
|
search \u2014 broad web search with multiple engines
|
|
116219
117603
|
scrape \u2014 detailed scrape of specific page(s)
|
|
116220
117604
|
analyze \u2014 analysis of data already collected (no external fetch)
|
|
@@ -116951,7 +118335,7 @@ var init_inventory = __esm({
|
|
|
116951
118335
|
/** Find similar past missions for planning. */
|
|
116952
118336
|
async findSimilarMissions(objective) {
|
|
116953
118337
|
try {
|
|
116954
|
-
return this.store.searchMissions(objective, 3);
|
|
118338
|
+
return this.store.searchMissions(objective, { limit: 3 });
|
|
116955
118339
|
} catch {
|
|
116956
118340
|
return [];
|
|
116957
118341
|
}
|
|
@@ -117029,8 +118413,8 @@ var init_inventory = __esm({
|
|
|
117029
118413
|
});
|
|
117030
118414
|
|
|
117031
118415
|
// ../agent/src/mission/mcp-factory.ts
|
|
117032
|
-
import { writeFileSync as
|
|
117033
|
-
import { join as
|
|
118416
|
+
import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, existsSync as existsSync13 } from "node:fs";
|
|
118417
|
+
import { join as join19 } from "node:path";
|
|
117034
118418
|
var McpFactory;
|
|
117035
118419
|
var init_mcp_factory = __esm({
|
|
117036
118420
|
"../agent/src/mission/mcp-factory.ts"() {
|
|
@@ -117038,7 +118422,7 @@ var init_mcp_factory = __esm({
|
|
|
117038
118422
|
McpFactory = class {
|
|
117039
118423
|
constructor(mcpDir) {
|
|
117040
118424
|
this.mcpDir = mcpDir;
|
|
117041
|
-
if (!
|
|
118425
|
+
if (!existsSync13(mcpDir)) mkdirSync8(mcpDir, { recursive: true });
|
|
117042
118426
|
}
|
|
117043
118427
|
/**
|
|
117044
118428
|
* Generate an MCP server that exposes one or more custom tools.
|
|
@@ -117046,13 +118430,13 @@ var init_mcp_factory = __esm({
|
|
|
117046
118430
|
*/
|
|
117047
118431
|
generate(tools, serverName) {
|
|
117048
118432
|
const safeName = this.sanitizeName(serverName);
|
|
117049
|
-
const serverDir =
|
|
117050
|
-
if (!
|
|
118433
|
+
const serverDir = join19(this.mcpDir, safeName);
|
|
118434
|
+
if (!existsSync13(serverDir)) mkdirSync8(serverDir, { recursive: true });
|
|
117051
118435
|
const serverCode = this.generateServerCode(tools, safeName);
|
|
117052
|
-
const entrypoint =
|
|
117053
|
-
|
|
117054
|
-
|
|
117055
|
-
|
|
118436
|
+
const entrypoint = join19(serverDir, "server.mjs");
|
|
118437
|
+
writeFileSync10(entrypoint, serverCode);
|
|
118438
|
+
writeFileSync10(
|
|
118439
|
+
join19(serverDir, "package.json"),
|
|
117056
118440
|
JSON.stringify(
|
|
117057
118441
|
{
|
|
117058
118442
|
name: `@nestor/mcp-${safeName}`,
|
|
@@ -117066,11 +118450,11 @@ var init_mcp_factory = __esm({
|
|
|
117066
118450
|
2
|
|
117067
118451
|
)
|
|
117068
118452
|
);
|
|
117069
|
-
|
|
117070
|
-
|
|
118453
|
+
writeFileSync10(join19(serverDir, "Dockerfile"), this.generateDockerfile(safeName));
|
|
118454
|
+
writeFileSync10(join19(serverDir, "README.md"), this.generateReadme(tools, safeName));
|
|
117071
118455
|
for (const tool of tools) {
|
|
117072
118456
|
const toolModuleCode = this.wrapToolAsModule(tool);
|
|
117073
|
-
|
|
118457
|
+
writeFileSync10(join19(serverDir, `${tool.name}.js`), toolModuleCode);
|
|
117074
118458
|
}
|
|
117075
118459
|
return {
|
|
117076
118460
|
id: safeName,
|
|
@@ -117304,8 +118688,8 @@ npx nestor-sh mcp add ${serverName} --command "node ${serverName}/server.mjs"
|
|
|
117304
118688
|
// ../agent/src/mission/docker-deployer.ts
|
|
117305
118689
|
import { execFile as execFile7 } from "node:child_process";
|
|
117306
118690
|
import { promisify as promisify5 } from "node:util";
|
|
117307
|
-
import { existsSync as
|
|
117308
|
-
import { join as
|
|
118691
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
118692
|
+
import { join as join20 } from "node:path";
|
|
117309
118693
|
var execFileAsync4, DockerDeployer;
|
|
117310
118694
|
var init_docker_deployer = __esm({
|
|
117311
118695
|
"../agent/src/mission/docker-deployer.ts"() {
|
|
@@ -117342,9 +118726,9 @@ var init_docker_deployer = __esm({
|
|
|
117342
118726
|
* Returns the image name (tag).
|
|
117343
118727
|
*/
|
|
117344
118728
|
async build(server) {
|
|
117345
|
-
const serverDir =
|
|
118729
|
+
const serverDir = join20(server.entrypoint, "..");
|
|
117346
118730
|
const imageName = `nestor-mcp-${server.name}:latest`;
|
|
117347
|
-
if (!
|
|
118731
|
+
if (!existsSync14(join20(serverDir, "Dockerfile"))) {
|
|
117348
118732
|
throw new Error(`No Dockerfile found in ${serverDir}`);
|
|
117349
118733
|
}
|
|
117350
118734
|
const container = {
|
|
@@ -117567,6 +118951,7 @@ var init_mission = __esm({
|
|
|
117567
118951
|
init_mcp_factory();
|
|
117568
118952
|
init_docker_deployer();
|
|
117569
118953
|
init_orchestrator_store();
|
|
118954
|
+
init_capability_research();
|
|
117570
118955
|
}
|
|
117571
118956
|
});
|
|
117572
118957
|
|
|
@@ -117817,6 +119202,15 @@ var init_intent = __esm({
|
|
|
117817
119202
|
}
|
|
117818
119203
|
});
|
|
117819
119204
|
|
|
119205
|
+
// ../agent/src/integrations/index.ts
|
|
119206
|
+
var init_integrations = __esm({
|
|
119207
|
+
"../agent/src/integrations/index.ts"() {
|
|
119208
|
+
"use strict";
|
|
119209
|
+
init_obsidian();
|
|
119210
|
+
init_n8n();
|
|
119211
|
+
}
|
|
119212
|
+
});
|
|
119213
|
+
|
|
117820
119214
|
// ../agent/src/index.ts
|
|
117821
119215
|
var src_exports3 = {};
|
|
117822
119216
|
__export(src_exports3, {
|
|
@@ -117831,9 +119225,11 @@ __export(src_exports3, {
|
|
|
117831
119225
|
AutoDowngradeAdapter: () => AutoDowngradeAdapter,
|
|
117832
119226
|
AutoDowngradeMonitor: () => AutoDowngradeMonitor,
|
|
117833
119227
|
BrowserManager: () => BrowserManager,
|
|
119228
|
+
CapabilityResearchEngine: () => CapabilityResearchEngine,
|
|
117834
119229
|
CircuitBreaker: () => CircuitBreaker,
|
|
117835
119230
|
ClaudeAdapter: () => ClaudeAdapter,
|
|
117836
119231
|
CodeChunker: () => CodeChunker,
|
|
119232
|
+
CompletionDetector: () => CompletionDetector,
|
|
117837
119233
|
ContextBuilder: () => ContextBuilder,
|
|
117838
119234
|
ContextRotator: () => ContextRotator,
|
|
117839
119235
|
DockerDeployer: () => DockerDeployer,
|
|
@@ -117847,11 +119243,13 @@ __export(src_exports3, {
|
|
|
117847
119243
|
GeminiAdapter: () => GeminiAdapter,
|
|
117848
119244
|
GeminiEmbeddingProvider: () => GeminiEmbeddingProvider,
|
|
117849
119245
|
GrokAdapter: () => GrokAdapter,
|
|
119246
|
+
HandoffGenerator: () => HandoffGenerator,
|
|
117850
119247
|
HotSwapAdapter: () => HotSwapAdapter,
|
|
117851
119248
|
IntentAnalyzer: () => IntentAnalyzer,
|
|
117852
119249
|
InventoryManager: () => InventoryManager,
|
|
117853
119250
|
K8sExecutor: () => K8sExecutor,
|
|
117854
119251
|
KnowledgeGraph: () => KnowledgeGraph,
|
|
119252
|
+
LayeredMemory: () => LayeredMemory,
|
|
117855
119253
|
LocalEmbeddings: () => LocalEmbeddings,
|
|
117856
119254
|
LocalSandbox: () => LocalSandbox,
|
|
117857
119255
|
McpFactory: () => McpFactory,
|
|
@@ -117863,6 +119261,8 @@ __export(src_exports3, {
|
|
|
117863
119261
|
MistralAdapter: () => MistralAdapter,
|
|
117864
119262
|
MockLlmAdapter: () => MockLlmAdapter,
|
|
117865
119263
|
ModelRouter: () => ModelRouter,
|
|
119264
|
+
N8nIntegration: () => N8nIntegration,
|
|
119265
|
+
ObsidianIntegration: () => ObsidianIntegration,
|
|
117866
119266
|
OllamaAdapter: () => OllamaAdapter,
|
|
117867
119267
|
OllamaEmbeddingProvider: () => OllamaEmbeddingProvider,
|
|
117868
119268
|
OllamaEmbeddings: () => OllamaEmbeddings,
|
|
@@ -117874,6 +119274,7 @@ __export(src_exports3, {
|
|
|
117874
119274
|
PluginManifestSchema: () => PluginManifestSchema,
|
|
117875
119275
|
PluginSandbox: () => PluginSandbox,
|
|
117876
119276
|
ProcessSandbox: () => ProcessSandbox,
|
|
119277
|
+
QuestionDetector: () => QuestionDetector,
|
|
117877
119278
|
RagIndexer: () => RagIndexer,
|
|
117878
119279
|
RagSearch: () => RagSearch,
|
|
117879
119280
|
ReasonLogger: () => ReasonLogger,
|
|
@@ -118017,6 +119418,7 @@ var init_src5 = __esm({
|
|
|
118017
119418
|
init_mission();
|
|
118018
119419
|
init_meta_tool_factory();
|
|
118019
119420
|
init_intent();
|
|
119421
|
+
init_integrations();
|
|
118020
119422
|
}
|
|
118021
119423
|
});
|
|
118022
119424
|
|
|
@@ -119286,8 +120688,8 @@ var init_experiments = __esm({
|
|
|
119286
120688
|
|
|
119287
120689
|
// ../server/src/services/telemetry-anon.ts
|
|
119288
120690
|
import { randomUUID as randomUUID38 } from "node:crypto";
|
|
119289
|
-
import { existsSync as
|
|
119290
|
-
import { join as
|
|
120691
|
+
import { existsSync as existsSync15, readFileSync as readFileSync17, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9 } from "node:fs";
|
|
120692
|
+
import { join as join21 } from "node:path";
|
|
119291
120693
|
import { homedir as homedir6 } from "node:os";
|
|
119292
120694
|
function getAnonTelemetry(config2) {
|
|
119293
120695
|
if (!_anonInstance) {
|
|
@@ -119303,8 +120705,8 @@ var init_telemetry_anon = __esm({
|
|
|
119303
120705
|
"../server/src/services/telemetry-anon.ts"() {
|
|
119304
120706
|
"use strict";
|
|
119305
120707
|
NESTOR_VERSION = "0.4.1";
|
|
119306
|
-
DATA_DIR2 =
|
|
119307
|
-
ANON_ID_FILE =
|
|
120708
|
+
DATA_DIR2 = join21(homedir6(), ".nestor");
|
|
120709
|
+
ANON_ID_FILE = join21(DATA_DIR2, "anon-telemetry-id");
|
|
119308
120710
|
DEFAULT_ENDPOINT = "https://telemetry.nestor.sh/v1/events";
|
|
119309
120711
|
FLUSH_BATCH_SIZE = 25;
|
|
119310
120712
|
AnonTelemetryService = class {
|
|
@@ -119408,18 +120810,18 @@ var init_telemetry_anon = __esm({
|
|
|
119408
120810
|
// ─── Private ──────────────────────────────────────────────────────────
|
|
119409
120811
|
getOrCreateInstallId() {
|
|
119410
120812
|
try {
|
|
119411
|
-
if (
|
|
119412
|
-
const id =
|
|
120813
|
+
if (existsSync15(ANON_ID_FILE)) {
|
|
120814
|
+
const id = readFileSync17(ANON_ID_FILE, "utf-8").trim();
|
|
119413
120815
|
if (id.length > 0) return id;
|
|
119414
120816
|
}
|
|
119415
120817
|
} catch {
|
|
119416
120818
|
}
|
|
119417
120819
|
const newId = randomUUID38();
|
|
119418
120820
|
try {
|
|
119419
|
-
if (!
|
|
119420
|
-
|
|
120821
|
+
if (!existsSync15(DATA_DIR2)) {
|
|
120822
|
+
mkdirSync9(DATA_DIR2, { recursive: true });
|
|
119421
120823
|
}
|
|
119422
|
-
|
|
120824
|
+
writeFileSync11(ANON_ID_FILE, newId + "\n", "utf-8");
|
|
119423
120825
|
} catch {
|
|
119424
120826
|
}
|
|
119425
120827
|
return newId;
|
|
@@ -149749,7 +151151,7 @@ var init_admin2 = __esm({
|
|
|
149749
151151
|
"../server/src/routes/admin.ts"() {
|
|
149750
151152
|
"use strict";
|
|
149751
151153
|
init_rate_limit();
|
|
149752
|
-
SERVER_VERSION3 = "
|
|
151154
|
+
SERVER_VERSION3 = "3.0.0";
|
|
149753
151155
|
startTime3 = Date.now();
|
|
149754
151156
|
}
|
|
149755
151157
|
});
|
|
@@ -151437,19 +152839,19 @@ var init_providers = __esm({
|
|
|
151437
152839
|
|
|
151438
152840
|
// ../server/src/services/key-vault.ts
|
|
151439
152841
|
import { createCipheriv as createCipheriv2, createDecipheriv as createDecipheriv2, randomBytes as randomBytes7, scryptSync as scryptSync2 } from "node:crypto";
|
|
151440
|
-
import { readFileSync as
|
|
151441
|
-
import { join as
|
|
152842
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, existsSync as existsSync16, mkdirSync as mkdirSync10 } from "node:fs";
|
|
152843
|
+
import { join as join22, dirname as dirname8 } from "node:path";
|
|
151442
152844
|
import { homedir as homedir7 } from "node:os";
|
|
151443
152845
|
function getVaultKey() {
|
|
151444
|
-
if (!
|
|
152846
|
+
if (!existsSync16(KEY_FILE)) {
|
|
151445
152847
|
const dir = dirname8(KEY_FILE);
|
|
151446
|
-
if (!
|
|
151447
|
-
|
|
152848
|
+
if (!existsSync16(dir)) {
|
|
152849
|
+
mkdirSync10(dir, { recursive: true });
|
|
151448
152850
|
}
|
|
151449
152851
|
const masterSecret = randomBytes7(32).toString("hex");
|
|
151450
|
-
|
|
152852
|
+
writeFileSync12(KEY_FILE, masterSecret, { mode: 384 });
|
|
151451
152853
|
}
|
|
151452
|
-
const secret =
|
|
152854
|
+
const secret = readFileSync18(KEY_FILE, "utf-8").trim();
|
|
151453
152855
|
return scryptSync2(secret, "nestor-vault-salt", 32);
|
|
151454
152856
|
}
|
|
151455
152857
|
function encryptApiKey(plainKey) {
|
|
@@ -151501,7 +152903,7 @@ var init_key_vault = __esm({
|
|
|
151501
152903
|
"../server/src/services/key-vault.ts"() {
|
|
151502
152904
|
"use strict";
|
|
151503
152905
|
ALGORITHM2 = "aes-256-gcm";
|
|
151504
|
-
KEY_FILE =
|
|
152906
|
+
KEY_FILE = join22(homedir7(), ".nestor", ".vault-key");
|
|
151505
152907
|
PROVIDER_ENV_MAP = {
|
|
151506
152908
|
claude: "ANTHROPIC_API_KEY",
|
|
151507
152909
|
openai: "OPENAI_API_KEY",
|
|
@@ -151886,6 +153288,36 @@ function missionRoutes(store, logger) {
|
|
|
151886
153288
|
next(err);
|
|
151887
153289
|
}
|
|
151888
153290
|
});
|
|
153291
|
+
router.get("/search", async (req, res, next) => {
|
|
153292
|
+
try {
|
|
153293
|
+
const q = req.query.q || "";
|
|
153294
|
+
const type = req.query.type;
|
|
153295
|
+
const status = req.query.status;
|
|
153296
|
+
const from2 = req.query.from;
|
|
153297
|
+
const to = req.query.to;
|
|
153298
|
+
const limit = req.query.limit ? parseInt(req.query.limit, 10) : 20;
|
|
153299
|
+
const results = store.searchMissions(q, { type, status, from: from2, to, limit });
|
|
153300
|
+
res.json({
|
|
153301
|
+
data: results,
|
|
153302
|
+
meta: {
|
|
153303
|
+
count: results.length,
|
|
153304
|
+
query: q,
|
|
153305
|
+
filters: { type, status, from: from2, to }
|
|
153306
|
+
}
|
|
153307
|
+
});
|
|
153308
|
+
} catch (err) {
|
|
153309
|
+
next(err);
|
|
153310
|
+
}
|
|
153311
|
+
});
|
|
153312
|
+
router.get("/stats", async (req, res, next) => {
|
|
153313
|
+
try {
|
|
153314
|
+
const tenantId = req.tenantId || "default";
|
|
153315
|
+
const stats = store.getMissionStats(tenantId);
|
|
153316
|
+
res.json({ data: stats });
|
|
153317
|
+
} catch (err) {
|
|
153318
|
+
next(err);
|
|
153319
|
+
}
|
|
153320
|
+
});
|
|
151889
153321
|
router.get("/:id", async (req, res, next) => {
|
|
151890
153322
|
try {
|
|
151891
153323
|
const mission = store.getMission(req.params.id);
|
|
@@ -152456,8 +153888,8 @@ var init_seed_defaults = __esm({
|
|
|
152456
153888
|
|
|
152457
153889
|
// ../server/src/app.ts
|
|
152458
153890
|
import express from "express";
|
|
152459
|
-
import { resolve as resolve13, join as
|
|
152460
|
-
import { existsSync as
|
|
153891
|
+
import { resolve as resolve13, join as join23 } from "node:path";
|
|
153892
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
152461
153893
|
import compression from "compression";
|
|
152462
153894
|
import cors from "cors";
|
|
152463
153895
|
function getStudioState() {
|
|
@@ -152787,7 +154219,7 @@ function createApp(config2) {
|
|
|
152787
154219
|
}
|
|
152788
154220
|
for (const dir of candidateDirs) {
|
|
152789
154221
|
try {
|
|
152790
|
-
if (
|
|
154222
|
+
if (existsSync17(join23(dir, "index.html"))) {
|
|
152791
154223
|
studioDistDir = dir;
|
|
152792
154224
|
break;
|
|
152793
154225
|
}
|
|
@@ -152811,7 +154243,7 @@ function createApp(config2) {
|
|
|
152811
154243
|
if (!studioState.enabled) {
|
|
152812
154244
|
return res.status(403).json({ error: { code: "STUDIO_DISABLED", message: "Studio is disabled." } });
|
|
152813
154245
|
}
|
|
152814
|
-
res.sendFile(
|
|
154246
|
+
res.sendFile(join23(studioDistDir, "index.html"));
|
|
152815
154247
|
});
|
|
152816
154248
|
app.use("/studio", express.static(studioDistDir, { index: false }));
|
|
152817
154249
|
app.use(express.static(studioDistDir, { index: false }));
|
|
@@ -152819,7 +154251,7 @@ function createApp(config2) {
|
|
|
152819
154251
|
if (!studioState.enabled) {
|
|
152820
154252
|
return res.status(403).json({ error: { code: "STUDIO_DISABLED", message: "Studio is disabled." } });
|
|
152821
154253
|
}
|
|
152822
|
-
res.sendFile(
|
|
154254
|
+
res.sendFile(join23(studioDistDir, "index.html"));
|
|
152823
154255
|
});
|
|
152824
154256
|
} else {
|
|
152825
154257
|
app.get("/studio", (_req, res) => {
|
|
@@ -152847,7 +154279,7 @@ function createApp(config2) {
|
|
|
152847
154279
|
if (!studioState.enabled) {
|
|
152848
154280
|
return res.status(403).json({ error: { code: "STUDIO_DISABLED", message: "Studio is disabled." } });
|
|
152849
154281
|
}
|
|
152850
|
-
res.sendFile(
|
|
154282
|
+
res.sendFile(join23(studioDistDir, "index.html"));
|
|
152851
154283
|
});
|
|
152852
154284
|
}
|
|
152853
154285
|
app.use(errorHandler());
|
|
@@ -152926,7 +154358,7 @@ var init_app = __esm({
|
|
|
152926
154358
|
});
|
|
152927
154359
|
|
|
152928
154360
|
// ../server/src/services/config-watcher.ts
|
|
152929
|
-
import { watch, existsSync as
|
|
154361
|
+
import { watch, existsSync as existsSync18 } from "node:fs";
|
|
152930
154362
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
152931
154363
|
var ConfigWatcher;
|
|
152932
154364
|
var init_config_watcher = __esm({
|
|
@@ -152950,7 +154382,7 @@ var init_config_watcher = __esm({
|
|
|
152950
154382
|
*/
|
|
152951
154383
|
async start() {
|
|
152952
154384
|
await this.reload();
|
|
152953
|
-
if (
|
|
154385
|
+
if (existsSync18(this.configPath)) {
|
|
152954
154386
|
this.watcher = watch(this.configPath, { persistent: false }, (eventType) => {
|
|
152955
154387
|
if (eventType === "change") {
|
|
152956
154388
|
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
@@ -152986,7 +154418,7 @@ var init_config_watcher = __esm({
|
|
|
152986
154418
|
// ─── Internal ───────────────────────────────────────────────────────
|
|
152987
154419
|
async reload() {
|
|
152988
154420
|
try {
|
|
152989
|
-
if (!
|
|
154421
|
+
if (!existsSync18(this.configPath)) {
|
|
152990
154422
|
console.warn("[config-watcher] Config file not found, keeping current config.");
|
|
152991
154423
|
return;
|
|
152992
154424
|
}
|
|
@@ -153030,8 +154462,8 @@ __export(telemetry_exports, {
|
|
|
153030
154462
|
resetTelemetry: () => resetTelemetry
|
|
153031
154463
|
});
|
|
153032
154464
|
import { randomUUID as randomUUID42 } from "node:crypto";
|
|
153033
|
-
import { existsSync as
|
|
153034
|
-
import { join as
|
|
154465
|
+
import { existsSync as existsSync19, readFileSync as readFileSync19, writeFileSync as writeFileSync13, appendFileSync as appendFileSync2, mkdirSync as mkdirSync11 } from "node:fs";
|
|
154466
|
+
import { join as join24 } from "node:path";
|
|
153035
154467
|
import { homedir as homedir8 } from "node:os";
|
|
153036
154468
|
function getTelemetry(config2) {
|
|
153037
154469
|
if (!_instance2) {
|
|
@@ -153046,10 +154478,10 @@ var VERSION2, DATA_DIR3, TELEMETRY_ID_FILE, TELEMETRY_LOG_FILE, DEFAULT_FLUSH_TH
|
|
|
153046
154478
|
var init_telemetry2 = __esm({
|
|
153047
154479
|
"../server/src/services/telemetry.ts"() {
|
|
153048
154480
|
"use strict";
|
|
153049
|
-
VERSION2 = "
|
|
153050
|
-
DATA_DIR3 =
|
|
153051
|
-
TELEMETRY_ID_FILE =
|
|
153052
|
-
TELEMETRY_LOG_FILE =
|
|
154481
|
+
VERSION2 = "3.0.0";
|
|
154482
|
+
DATA_DIR3 = join24(homedir8(), ".nestor");
|
|
154483
|
+
TELEMETRY_ID_FILE = join24(DATA_DIR3, "telemetry-id");
|
|
154484
|
+
TELEMETRY_LOG_FILE = join24(DATA_DIR3, "telemetry.jsonl");
|
|
153053
154485
|
DEFAULT_FLUSH_THRESHOLD = 10;
|
|
153054
154486
|
TelemetryService = class {
|
|
153055
154487
|
enabled;
|
|
@@ -153142,8 +154574,8 @@ var init_telemetry2 = __esm({
|
|
|
153142
154574
|
// ─── Private ─────────────────────────────────────────────────────────
|
|
153143
154575
|
getOrCreateInstallId() {
|
|
153144
154576
|
try {
|
|
153145
|
-
if (
|
|
153146
|
-
const id =
|
|
154577
|
+
if (existsSync19(TELEMETRY_ID_FILE)) {
|
|
154578
|
+
const id = readFileSync19(TELEMETRY_ID_FILE, "utf-8").trim();
|
|
153147
154579
|
if (id.length > 0) return id;
|
|
153148
154580
|
}
|
|
153149
154581
|
} catch {
|
|
@@ -153151,14 +154583,14 @@ var init_telemetry2 = __esm({
|
|
|
153151
154583
|
const newId = randomUUID42();
|
|
153152
154584
|
try {
|
|
153153
154585
|
this.ensureDataDir();
|
|
153154
|
-
|
|
154586
|
+
writeFileSync13(TELEMETRY_ID_FILE, newId + "\n", "utf-8");
|
|
153155
154587
|
} catch {
|
|
153156
154588
|
}
|
|
153157
154589
|
return newId;
|
|
153158
154590
|
}
|
|
153159
154591
|
ensureDataDir() {
|
|
153160
|
-
if (!
|
|
153161
|
-
|
|
154592
|
+
if (!existsSync19(DATA_DIR3)) {
|
|
154593
|
+
mkdirSync11(DATA_DIR3, { recursive: true });
|
|
153162
154594
|
}
|
|
153163
154595
|
}
|
|
153164
154596
|
startFlushTimer() {
|
|
@@ -157365,7 +158797,7 @@ var init_src6 = __esm({
|
|
|
157365
158797
|
await this._handle.listen();
|
|
157366
158798
|
const authMode = config2.apiKey ? "API key" : "open (no auth)";
|
|
157367
158799
|
console.log(`
|
|
157368
|
-
Nestor Server
|
|
158800
|
+
Nestor Server v3.0.0`);
|
|
157369
158801
|
console.log(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
157370
158802
|
console.log(` HTTP : http://${this._host}:${this._port}`);
|
|
157371
158803
|
console.log(` WS : ws://${this._host}:${this._port}/ws`);
|
|
@@ -164744,7 +166176,7 @@ var require_dist12 = __commonJS({
|
|
|
164744
166176
|
});
|
|
164745
166177
|
|
|
164746
166178
|
// ../skill-tester/src/yaml-loader.ts
|
|
164747
|
-
import { readFileSync as
|
|
166179
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
164748
166180
|
function parseYamlContent(content) {
|
|
164749
166181
|
try {
|
|
164750
166182
|
return JSON.parse(content);
|
|
@@ -164860,7 +166292,7 @@ var init_yaml_loader = __esm({
|
|
|
164860
166292
|
* Load a YAML test suite from a file path.
|
|
164861
166293
|
*/
|
|
164862
166294
|
static loadFile(filePath) {
|
|
164863
|
-
const content =
|
|
166295
|
+
const content = readFileSync20(filePath, "utf-8");
|
|
164864
166296
|
return _YamlTestLoader.parse(content);
|
|
164865
166297
|
}
|
|
164866
166298
|
/**
|
|
@@ -164939,8 +166371,8 @@ var init_yaml_loader = __esm({
|
|
|
164939
166371
|
});
|
|
164940
166372
|
|
|
164941
166373
|
// ../skill-tester/src/runner.ts
|
|
164942
|
-
import { readFileSync as
|
|
164943
|
-
import { join as
|
|
166374
|
+
import { readFileSync as readFileSync21, readdirSync as readdirSync5, statSync as statSync6, existsSync as existsSync21, mkdirSync as mkdirSync13, rmSync as rmSync2 } from "node:fs";
|
|
166375
|
+
import { join as join26, resolve as resolve14 } from "node:path";
|
|
164944
166376
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
164945
166377
|
import { randomUUID as randomUUID44 } from "node:crypto";
|
|
164946
166378
|
var SequentialMockAdapter, SkillTestRunner;
|
|
@@ -165015,25 +166447,25 @@ var init_runner2 = __esm({
|
|
|
165015
166447
|
try {
|
|
165016
166448
|
const mockResponses = test.mockResponses.length > 0 ? test.mockResponses : [{ content: "Test completed." }];
|
|
165017
166449
|
const adapter = new SequentialMockAdapter(mockResponses);
|
|
165018
|
-
const tempDir =
|
|
165019
|
-
|
|
166450
|
+
const tempDir = join26(tmpdir4(), `nestor-skill-test-${randomUUID44()}`);
|
|
166451
|
+
mkdirSync13(tempDir, { recursive: true });
|
|
165020
166452
|
try {
|
|
165021
166453
|
const agentModule = await Promise.resolve().then(() => (init_src5(), src_exports3));
|
|
165022
166454
|
const events = new agentModule.RuntimeEventBus();
|
|
165023
166455
|
const tools = new agentModule.ToolRegistry();
|
|
165024
166456
|
const toolExecutor = new agentModule.ToolExecutor();
|
|
165025
166457
|
let instructions = "You are a helpful AI assistant.";
|
|
165026
|
-
if (test.skill &&
|
|
165027
|
-
instructions =
|
|
166458
|
+
if (test.skill && existsSync21(test.skill)) {
|
|
166459
|
+
instructions = readFileSync21(test.skill, "utf-8");
|
|
165028
166460
|
} else if (test.skill) {
|
|
165029
166461
|
const skillPaths = [
|
|
165030
|
-
|
|
165031
|
-
|
|
165032
|
-
|
|
166462
|
+
join26(process.cwd(), ".nestor", "skills", test.skill, "SKILL.md"),
|
|
166463
|
+
join26(process.cwd(), "skills", test.skill, "SKILL.md"),
|
|
166464
|
+
join26(process.cwd(), "skills-registry", test.skill, "SKILL.md")
|
|
165033
166465
|
];
|
|
165034
166466
|
for (const p8 of skillPaths) {
|
|
165035
|
-
if (
|
|
165036
|
-
instructions =
|
|
166467
|
+
if (existsSync21(p8)) {
|
|
166468
|
+
instructions = readFileSync21(p8, "utf-8");
|
|
165037
166469
|
break;
|
|
165038
166470
|
}
|
|
165039
166471
|
}
|
|
@@ -165243,23 +166675,23 @@ var init_runner2 = __esm({
|
|
|
165243
166675
|
discoverTestFiles(dir) {
|
|
165244
166676
|
const files = [];
|
|
165245
166677
|
const resolvedDir = resolve14(dir);
|
|
165246
|
-
if (!
|
|
165247
|
-
const stat =
|
|
166678
|
+
if (!existsSync21(resolvedDir)) return files;
|
|
166679
|
+
const stat = statSync6(resolvedDir);
|
|
165248
166680
|
if (!stat.isDirectory()) {
|
|
165249
166681
|
return [resolvedDir];
|
|
165250
166682
|
}
|
|
165251
166683
|
function walk(current) {
|
|
165252
166684
|
let entries;
|
|
165253
166685
|
try {
|
|
165254
|
-
entries =
|
|
166686
|
+
entries = readdirSync5(current);
|
|
165255
166687
|
} catch {
|
|
165256
166688
|
return;
|
|
165257
166689
|
}
|
|
165258
166690
|
for (const entry of entries) {
|
|
165259
166691
|
if (entry === "node_modules" || entry === ".git" || entry === "dist") continue;
|
|
165260
|
-
const fullPath =
|
|
166692
|
+
const fullPath = join26(current, entry);
|
|
165261
166693
|
try {
|
|
165262
|
-
const s =
|
|
166694
|
+
const s = statSync6(fullPath);
|
|
165263
166695
|
if (s.isDirectory()) {
|
|
165264
166696
|
walk(fullPath);
|
|
165265
166697
|
} else if (s.isFile() && entry.endsWith(".test.yaml")) {
|
|
@@ -167127,7 +168559,7 @@ var init_server = __esm({
|
|
|
167127
168559
|
MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
167128
168560
|
SERVER_INFO = {
|
|
167129
168561
|
name: "nestor",
|
|
167130
|
-
version: "
|
|
168562
|
+
version: "3.0.0"
|
|
167131
168563
|
};
|
|
167132
168564
|
SERVER_CAPABILITIES = {
|
|
167133
168565
|
tools: { listChanged: false },
|
|
@@ -167313,10 +168745,10 @@ __export(shell_exports, {
|
|
|
167313
168745
|
registerShellCommand: () => registerShellCommand
|
|
167314
168746
|
});
|
|
167315
168747
|
import * as readline3 from "node:readline";
|
|
167316
|
-
import { existsSync as
|
|
167317
|
-
import { join as
|
|
168748
|
+
import { existsSync as existsSync22, readFileSync as readFileSync22, writeFileSync as writeFileSync15, mkdirSync as mkdirSync14, appendFileSync as appendFileSync3 } from "node:fs";
|
|
168749
|
+
import { join as join27, resolve as resolve15, dirname as dirname10, basename as basename5 } from "node:path";
|
|
167318
168750
|
import { homedir as homedir9 } from "node:os";
|
|
167319
|
-
import { readdirSync as
|
|
168751
|
+
import { readdirSync as readdirSync6 } from "node:fs";
|
|
167320
168752
|
import { randomUUID as randomUUID50 } from "node:crypto";
|
|
167321
168753
|
import chalk11 from "chalk";
|
|
167322
168754
|
import * as p5 from "@clack/prompts";
|
|
@@ -167347,8 +168779,8 @@ async function startShell() {
|
|
|
167347
168779
|
currentRouter: null,
|
|
167348
168780
|
verbose: false
|
|
167349
168781
|
};
|
|
167350
|
-
if (!
|
|
167351
|
-
|
|
168782
|
+
if (!existsSync22(NESTOR_DIR)) {
|
|
168783
|
+
mkdirSync14(NESTOR_DIR, { recursive: true });
|
|
167352
168784
|
}
|
|
167353
168785
|
refreshAgentNameCache(session);
|
|
167354
168786
|
const history = loadHistory();
|
|
@@ -167571,7 +169003,7 @@ function printWelcome() {
|
|
|
167571
169003
|
console.log(chalk11.cyan(` | .\` | | _| \\__ \\ | | | (_) | | / _ \\__ \\ | __ |`));
|
|
167572
169004
|
console.log(chalk11.cyan(` |_|\\_| |___| |___/ |_| \\___/ |_|_\\ (_) |___/ |_||_|`));
|
|
167573
169005
|
console.log("");
|
|
167574
|
-
console.log(chalk11.dim(" Interactive Shell \u2014
|
|
169006
|
+
console.log(chalk11.dim(" Interactive Shell \u2014 v3.0.0"));
|
|
167575
169007
|
console.log(chalk11.dim(" Type /help for commands, /exit to quit."));
|
|
167576
169008
|
console.log(chalk11.dim(" Multiline: end a line with \\ or use ``` code blocks."));
|
|
167577
169009
|
console.log("");
|
|
@@ -168068,8 +169500,8 @@ function cmdExport(args2, session) {
|
|
|
168068
169500
|
timestamp: new Date(e.timestamp).toISOString()
|
|
168069
169501
|
}))
|
|
168070
169502
|
};
|
|
168071
|
-
const filename =
|
|
168072
|
-
|
|
169503
|
+
const filename = join27(NESTOR_DIR, `conversation-${timestamp}.json`);
|
|
169504
|
+
writeFileSync15(filename, JSON.stringify(data, null, 2), "utf-8");
|
|
168073
169505
|
console.log(chalk11.green(`Exported to: ${filename}`));
|
|
168074
169506
|
return;
|
|
168075
169507
|
}
|
|
@@ -168093,8 +169525,8 @@ function cmdExport(args2, session) {
|
|
|
168093
169525
|
lines.push(entry.content);
|
|
168094
169526
|
lines.push("");
|
|
168095
169527
|
}
|
|
168096
|
-
const filename =
|
|
168097
|
-
|
|
169528
|
+
const filename = join27(NESTOR_DIR, `conversation-${timestamp}.md`);
|
|
169529
|
+
writeFileSync15(filename, lines.join("\n"), "utf-8");
|
|
168098
169530
|
console.log(chalk11.green(`Exported to: ${filename}`));
|
|
168099
169531
|
return;
|
|
168100
169532
|
}
|
|
@@ -168130,8 +169562,8 @@ ${rows}
|
|
|
168130
169562
|
<div class="stats">Tokens: ${fmtNum(session.totalTokensIn)} in / ${fmtNum(session.totalTokensOut)} out | Cost: $${session.totalCostUsd.toFixed(4)} | Tool calls: ${session.totalToolCalls}</div>
|
|
168131
169563
|
</body>
|
|
168132
169564
|
</html>`;
|
|
168133
|
-
const filename =
|
|
168134
|
-
|
|
169565
|
+
const filename = join27(NESTOR_DIR, `conversation-${timestamp}.html`);
|
|
169566
|
+
writeFileSync15(filename, html, "utf-8");
|
|
168135
169567
|
console.log(chalk11.green(`Exported to: ${filename}`));
|
|
168136
169568
|
}
|
|
168137
169569
|
}
|
|
@@ -168638,7 +170070,7 @@ function createUnifiedDiff(oldText, newText, fileName) {
|
|
|
168638
170070
|
function showDiffForStep(filePath, newContent) {
|
|
168639
170071
|
try {
|
|
168640
170072
|
const absolutePath = resolve15(process.cwd(), filePath);
|
|
168641
|
-
const oldContent =
|
|
170073
|
+
const oldContent = readFileSync22(absolutePath, "utf-8");
|
|
168642
170074
|
const diff = createUnifiedDiff(oldContent, newContent, filePath);
|
|
168643
170075
|
for (const line of diff.split("\n")) {
|
|
168644
170076
|
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
@@ -169051,8 +170483,8 @@ function completeFilePath(partial) {
|
|
|
169051
170483
|
dir = dirname10(resolved);
|
|
169052
170484
|
prefix = partial.substring(0, Math.max(partial.lastIndexOf("/"), partial.lastIndexOf("\\")) + 1);
|
|
169053
170485
|
}
|
|
169054
|
-
const entries =
|
|
169055
|
-
const base =
|
|
170486
|
+
const entries = readdirSync6(dir, { withFileTypes: true });
|
|
170487
|
+
const base = basename5(partial);
|
|
169056
170488
|
const matches = [];
|
|
169057
170489
|
for (const entry of entries) {
|
|
169058
170490
|
if (entry.name.startsWith(".")) continue;
|
|
@@ -169082,8 +170514,8 @@ function refreshAgentNameCache(session) {
|
|
|
169082
170514
|
}
|
|
169083
170515
|
function loadHistory() {
|
|
169084
170516
|
try {
|
|
169085
|
-
if (
|
|
169086
|
-
const raw =
|
|
170517
|
+
if (existsSync22(HISTORY_FILE)) {
|
|
170518
|
+
const raw = readFileSync22(HISTORY_FILE, "utf-8");
|
|
169087
170519
|
return raw.split("\n").filter((l) => l.trim().length > 0).slice(-MAX_HISTORY);
|
|
169088
170520
|
}
|
|
169089
170521
|
} catch {
|
|
@@ -169094,8 +170526,8 @@ function saveHistory(rl) {
|
|
|
169094
170526
|
}
|
|
169095
170527
|
function appendToHistoryFile(line) {
|
|
169096
170528
|
try {
|
|
169097
|
-
if (!
|
|
169098
|
-
|
|
170529
|
+
if (!existsSync22(NESTOR_DIR)) {
|
|
170530
|
+
mkdirSync14(NESTOR_DIR, { recursive: true });
|
|
169099
170531
|
}
|
|
169100
170532
|
appendFileSync3(HISTORY_FILE, line + "\n", "utf-8");
|
|
169101
170533
|
} catch {
|
|
@@ -169437,8 +170869,8 @@ var init_shell = __esm({
|
|
|
169437
170869
|
init_config2();
|
|
169438
170870
|
init_spinner();
|
|
169439
170871
|
init_table();
|
|
169440
|
-
NESTOR_DIR =
|
|
169441
|
-
HISTORY_FILE =
|
|
170872
|
+
NESTOR_DIR = join27(homedir9(), ".nestor");
|
|
170873
|
+
HISTORY_FILE = join27(NESTOR_DIR, "shell_history");
|
|
169442
170874
|
MAX_HISTORY = 1e3;
|
|
169443
170875
|
SLASH_COMMANDS = {
|
|
169444
170876
|
"/help": { description: "Show all commands", usage: "/help" },
|
|
@@ -169490,8 +170922,8 @@ var init_shell = __esm({
|
|
|
169490
170922
|
|
|
169491
170923
|
// src/index.ts
|
|
169492
170924
|
import { Command } from "commander";
|
|
169493
|
-
import { existsSync as
|
|
169494
|
-
import { join as
|
|
170925
|
+
import { existsSync as existsSync27, readFileSync as readFileSync27 } from "node:fs";
|
|
170926
|
+
import { join as join30 } from "node:path";
|
|
169495
170927
|
import { homedir as homedir12 } from "node:os";
|
|
169496
170928
|
|
|
169497
170929
|
// src/commands/start.ts
|
|
@@ -169511,7 +170943,7 @@ var BANNER = `
|
|
|
169511
170943
|
function registerStartCommand(program2) {
|
|
169512
170944
|
program2.command("start").description("Start the Nestor server").option("-p, --port <port>", "Server port").option("-H, --host <host>", "Server host").option("--no-studio", "Disable the Studio web UI").action(async (options) => {
|
|
169513
170945
|
console.log(chalk.cyan(BANNER));
|
|
169514
|
-
console.log(chalk.dim(`
|
|
170946
|
+
console.log(chalk.dim(` v3.0.0
|
|
169515
170947
|
`));
|
|
169516
170948
|
let config2 = readConfigFile();
|
|
169517
170949
|
if (!config2) {
|
|
@@ -169636,8 +171068,8 @@ init_config();
|
|
|
169636
171068
|
init_config2();
|
|
169637
171069
|
import * as p from "@clack/prompts";
|
|
169638
171070
|
import chalk2 from "chalk";
|
|
169639
|
-
import { mkdirSync as
|
|
169640
|
-
import { join as
|
|
171071
|
+
import { mkdirSync as mkdirSync12, existsSync as existsSync20 } from "node:fs";
|
|
171072
|
+
import { join as join25 } from "node:path";
|
|
169641
171073
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
169642
171074
|
function generateApiKey() {
|
|
169643
171075
|
return `nst_${randomBytes8(32).toString("hex")}`;
|
|
@@ -169656,7 +171088,7 @@ function generateBetaKey() {
|
|
|
169656
171088
|
}
|
|
169657
171089
|
async function initializeDatabase(dataDir) {
|
|
169658
171090
|
const { NestorStore: NestorStore2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
|
|
169659
|
-
const dbPath =
|
|
171091
|
+
const dbPath = join25(dataDir, "nestor.db");
|
|
169660
171092
|
const store = await NestorStore2.open(dbPath);
|
|
169661
171093
|
store.close();
|
|
169662
171094
|
}
|
|
@@ -169704,31 +171136,31 @@ async function runNonInteractiveInstall() {
|
|
|
169704
171136
|
console.log();
|
|
169705
171137
|
const config2 = buildConfigFromDefaults();
|
|
169706
171138
|
const dataDir = expandPath(config2.server.dataDir);
|
|
169707
|
-
if (!
|
|
169708
|
-
|
|
171139
|
+
if (!existsSync20(dataDir)) {
|
|
171140
|
+
mkdirSync12(dataDir, { recursive: true });
|
|
169709
171141
|
console.log(chalk2.green(" Created data directory:"), dataDir);
|
|
169710
171142
|
}
|
|
169711
171143
|
for (const sub of ["adapters", "skills", "plugins", "logs"]) {
|
|
169712
|
-
const subDir =
|
|
169713
|
-
if (!
|
|
169714
|
-
|
|
171144
|
+
const subDir = join25(dataDir, sub);
|
|
171145
|
+
if (!existsSync20(subDir)) {
|
|
171146
|
+
mkdirSync12(subDir, { recursive: true });
|
|
169715
171147
|
}
|
|
169716
171148
|
}
|
|
169717
171149
|
writeConfigFile(config2);
|
|
169718
171150
|
console.log(chalk2.green(" Configuration saved:"), getConfigPath());
|
|
169719
171151
|
try {
|
|
169720
171152
|
await initializeDatabase(dataDir);
|
|
169721
|
-
console.log(chalk2.green(" Database initialized:"),
|
|
171153
|
+
console.log(chalk2.green(" Database initialized:"), join25(dataDir, "nestor.db"));
|
|
169722
171154
|
} catch (err) {
|
|
169723
171155
|
console.log(chalk2.yellow(" Database initialization skipped (install @nestor/db first)"));
|
|
169724
171156
|
}
|
|
169725
171157
|
const apiKey = generateApiKey();
|
|
169726
|
-
const apiKeyPath =
|
|
169727
|
-
const { writeFileSync:
|
|
169728
|
-
|
|
171158
|
+
const apiKeyPath = join25(dataDir, ".api-keys");
|
|
171159
|
+
const { writeFileSync: writeFileSync18 } = await import("node:fs");
|
|
171160
|
+
writeFileSync18(apiKeyPath, JSON.stringify({ admin: apiKey }, null, 2) + "\n", "utf-8");
|
|
169729
171161
|
console.log(chalk2.green(" Admin API key created"));
|
|
169730
171162
|
const betaKey = generateBetaKey();
|
|
169731
|
-
|
|
171163
|
+
writeFileSync18(join25(dataDir, "license.key"), betaKey, "utf-8");
|
|
169732
171164
|
console.log(chalk2.green(" Beta key created"));
|
|
169733
171165
|
console.log();
|
|
169734
171166
|
console.log(chalk2.bold("Admin API Key:"), chalk2.yellow(apiKey));
|
|
@@ -169824,9 +171256,9 @@ async function runInteractiveInstall() {
|
|
|
169824
171256
|
const dataDir = expandPath(config2.server.dataDir);
|
|
169825
171257
|
const spinner3 = p.spinner();
|
|
169826
171258
|
spinner3.start("Creating directories...");
|
|
169827
|
-
for (const dir of [dataDir,
|
|
169828
|
-
if (!
|
|
169829
|
-
|
|
171259
|
+
for (const dir of [dataDir, join25(dataDir, "adapters"), join25(dataDir, "skills"), join25(dataDir, "plugins"), join25(dataDir, "logs")]) {
|
|
171260
|
+
if (!existsSync20(dir)) {
|
|
171261
|
+
mkdirSync12(dir, { recursive: true });
|
|
169830
171262
|
}
|
|
169831
171263
|
}
|
|
169832
171264
|
spinner3.stop("Directories created");
|
|
@@ -169851,14 +171283,14 @@ async function runInteractiveInstall() {
|
|
|
169851
171283
|
const spinner4 = p.spinner();
|
|
169852
171284
|
spinner4.start("Generating admin API key...");
|
|
169853
171285
|
const apiKey = generateApiKey();
|
|
169854
|
-
const apiKeyPath =
|
|
169855
|
-
const { writeFileSync:
|
|
169856
|
-
|
|
171286
|
+
const apiKeyPath = join25(dataDir, ".api-keys");
|
|
171287
|
+
const { writeFileSync: writeFileSync18 } = await import("node:fs");
|
|
171288
|
+
writeFileSync18(apiKeyPath, JSON.stringify({ admin: apiKey }, null, 2) + "\n", "utf-8");
|
|
169857
171289
|
spinner4.stop("Admin API key created!");
|
|
169858
171290
|
const spinner5 = p.spinner();
|
|
169859
171291
|
spinner5.start("Generating beta key...");
|
|
169860
171292
|
const betaKey = generateBetaKey();
|
|
169861
|
-
|
|
171293
|
+
writeFileSync18(join25(dataDir, "license.key"), betaKey, "utf-8");
|
|
169862
171294
|
spinner5.stop("Beta key created!");
|
|
169863
171295
|
p.note(
|
|
169864
171296
|
`Config file: ${getConfigPath()}
|
|
@@ -171151,23 +172583,23 @@ init_db();
|
|
|
171151
172583
|
init_spinner();
|
|
171152
172584
|
init_table();
|
|
171153
172585
|
import {
|
|
171154
|
-
existsSync as
|
|
171155
|
-
readFileSync as
|
|
171156
|
-
writeFileSync as
|
|
171157
|
-
mkdirSync as
|
|
172586
|
+
existsSync as existsSync23,
|
|
172587
|
+
readFileSync as readFileSync23,
|
|
172588
|
+
writeFileSync as writeFileSync16,
|
|
172589
|
+
mkdirSync as mkdirSync15,
|
|
171158
172590
|
unlinkSync,
|
|
171159
172591
|
appendFileSync as appendFileSync4
|
|
171160
172592
|
} from "node:fs";
|
|
171161
|
-
import { join as
|
|
172593
|
+
import { join as join28 } from "node:path";
|
|
171162
172594
|
import { homedir as homedir10 } from "node:os";
|
|
171163
172595
|
import { fork as fork2 } from "node:child_process";
|
|
171164
172596
|
import "node:readline";
|
|
171165
172597
|
import chalk12 from "chalk";
|
|
171166
|
-
var NESTOR_DIR2 =
|
|
171167
|
-
var PID_FILE =
|
|
171168
|
-
var LOG_DIR =
|
|
171169
|
-
var LOG_FILE =
|
|
171170
|
-
var HEARTBEAT_FILE =
|
|
172598
|
+
var NESTOR_DIR2 = join28(homedir10(), ".nestor");
|
|
172599
|
+
var PID_FILE = join28(NESTOR_DIR2, "daemon.pid");
|
|
172600
|
+
var LOG_DIR = join28(NESTOR_DIR2, "logs");
|
|
172601
|
+
var LOG_FILE = join28(LOG_DIR, "daemon.log");
|
|
172602
|
+
var HEARTBEAT_FILE = join28(NESTOR_DIR2, "daemon.heartbeat");
|
|
171171
172603
|
var HEARTBEAT_INTERVAL_MS2 = 3e4;
|
|
171172
172604
|
function registerDaemonCommand(program2) {
|
|
171173
172605
|
const daemon = program2.command("daemon").description("Manage the Nestor background daemon");
|
|
@@ -171203,7 +172635,7 @@ async function startForeground() {
|
|
|
171203
172635
|
console.log(chalk12.cyan(` | .\` | | _| \\__ \\ | | | (_) | | / _ \\__ \\ | __ |`));
|
|
171204
172636
|
console.log(chalk12.cyan(` |_|\\_| |___| |___/ |_| \\___/ |_|_\\ (_) |___/ |_||_|`));
|
|
171205
172637
|
console.log("");
|
|
171206
|
-
console.log(chalk12.dim(" Daemon Mode \u2014
|
|
172638
|
+
console.log(chalk12.dim(" Daemon Mode \u2014 v3.0.0"));
|
|
171207
172639
|
console.log(chalk12.dim(` PID: ${process.pid}`));
|
|
171208
172640
|
console.log(chalk12.dim(` Log: ${LOG_FILE}`));
|
|
171209
172641
|
console.log("");
|
|
@@ -171335,13 +172767,13 @@ async function showStatus() {
|
|
|
171335
172767
|
console.log("");
|
|
171336
172768
|
}
|
|
171337
172769
|
async function showLogs(follow, lineCount) {
|
|
171338
|
-
if (!
|
|
172770
|
+
if (!existsSync23(LOG_FILE)) {
|
|
171339
172771
|
console.log(chalk12.yellow("No daemon log file found."));
|
|
171340
172772
|
console.log(chalk12.dim(`Expected at: ${LOG_FILE}`));
|
|
171341
172773
|
return;
|
|
171342
172774
|
}
|
|
171343
172775
|
if (!follow) {
|
|
171344
|
-
const content =
|
|
172776
|
+
const content = readFileSync23(LOG_FILE, "utf-8");
|
|
171345
172777
|
const lines = content.split("\n").filter((l) => l.length > 0);
|
|
171346
172778
|
const lastLines = lines.slice(-lineCount);
|
|
171347
172779
|
if (lastLines.length === 0) {
|
|
@@ -171355,19 +172787,19 @@ async function showLogs(follow, lineCount) {
|
|
|
171355
172787
|
}
|
|
171356
172788
|
console.log(chalk12.dim(`Following ${LOG_FILE}... (Ctrl+C to stop)`));
|
|
171357
172789
|
console.log("");
|
|
171358
|
-
if (
|
|
171359
|
-
const content =
|
|
172790
|
+
if (existsSync23(LOG_FILE)) {
|
|
172791
|
+
const content = readFileSync23(LOG_FILE, "utf-8");
|
|
171360
172792
|
const lines = content.split("\n").filter((l) => l.length > 0);
|
|
171361
172793
|
const lastLines = lines.slice(-10);
|
|
171362
172794
|
for (const line of lastLines) {
|
|
171363
172795
|
console.log(colorizeLogLine(line));
|
|
171364
172796
|
}
|
|
171365
172797
|
}
|
|
171366
|
-
let lastSize =
|
|
172798
|
+
let lastSize = existsSync23(LOG_FILE) ? readFileSync23(LOG_FILE).length : 0;
|
|
171367
172799
|
const watcher = setInterval(() => {
|
|
171368
172800
|
try {
|
|
171369
|
-
if (!
|
|
171370
|
-
const content =
|
|
172801
|
+
if (!existsSync23(LOG_FILE)) return;
|
|
172802
|
+
const content = readFileSync23(LOG_FILE, "utf-8");
|
|
171371
172803
|
if (content.length > lastSize) {
|
|
171372
172804
|
const newContent = content.substring(lastSize);
|
|
171373
172805
|
const newLines = newContent.split("\n").filter((l) => l.length > 0);
|
|
@@ -171566,9 +172998,9 @@ var DaemonRunner = class {
|
|
|
171566
172998
|
try {
|
|
171567
172999
|
const store = await getStore();
|
|
171568
173000
|
try {
|
|
171569
|
-
const { randomUUID:
|
|
173001
|
+
const { randomUUID: randomUUID52 } = await import("node:crypto");
|
|
171570
173002
|
store.createRun({
|
|
171571
|
-
id:
|
|
173003
|
+
id: randomUUID52(),
|
|
171572
173004
|
workflowId: `agent:${agentId}`,
|
|
171573
173005
|
workflowVersion: 1,
|
|
171574
173006
|
status: result.exitReason === "completed" ? "completed" : "failed",
|
|
@@ -171604,9 +173036,9 @@ var DaemonRunner = class {
|
|
|
171604
173036
|
try {
|
|
171605
173037
|
const store = await getStore();
|
|
171606
173038
|
try {
|
|
171607
|
-
const { randomUUID:
|
|
173039
|
+
const { randomUUID: randomUUID52 } = await import("node:crypto");
|
|
171608
173040
|
store.createRun({
|
|
171609
|
-
id:
|
|
173041
|
+
id: randomUUID52(),
|
|
171610
173042
|
workflowId: `agent:${agentId}`,
|
|
171611
173043
|
workflowVersion: 1,
|
|
171612
173044
|
status: "failed",
|
|
@@ -171679,7 +173111,7 @@ var DaemonRunner = class {
|
|
|
171679
173111
|
activeAgents: this.runningAgents.size,
|
|
171680
173112
|
nextScheduledRun: void 0
|
|
171681
173113
|
};
|
|
171682
|
-
|
|
173114
|
+
writeFileSync16(HEARTBEAT_FILE, JSON.stringify(data), "utf-8");
|
|
171683
173115
|
} catch {
|
|
171684
173116
|
}
|
|
171685
173117
|
}
|
|
@@ -171697,8 +173129,8 @@ async function importDaemonAgentModule() {
|
|
|
171697
173129
|
}
|
|
171698
173130
|
function readHeartbeat() {
|
|
171699
173131
|
try {
|
|
171700
|
-
if (
|
|
171701
|
-
const raw =
|
|
173132
|
+
if (existsSync23(HEARTBEAT_FILE)) {
|
|
173133
|
+
const raw = readFileSync23(HEARTBEAT_FILE, "utf-8");
|
|
171702
173134
|
return JSON.parse(raw);
|
|
171703
173135
|
}
|
|
171704
173136
|
} catch {
|
|
@@ -171707,8 +173139,8 @@ function readHeartbeat() {
|
|
|
171707
173139
|
}
|
|
171708
173140
|
function readPid() {
|
|
171709
173141
|
try {
|
|
171710
|
-
if (
|
|
171711
|
-
const raw =
|
|
173142
|
+
if (existsSync23(PID_FILE)) {
|
|
173143
|
+
const raw = readFileSync23(PID_FILE, "utf-8").trim();
|
|
171712
173144
|
const pid = parseInt(raw, 10);
|
|
171713
173145
|
return isNaN(pid) ? null : pid;
|
|
171714
173146
|
}
|
|
@@ -171718,11 +173150,11 @@ function readPid() {
|
|
|
171718
173150
|
}
|
|
171719
173151
|
function writePid(pid) {
|
|
171720
173152
|
ensureDirs();
|
|
171721
|
-
|
|
173153
|
+
writeFileSync16(PID_FILE, String(pid), "utf-8");
|
|
171722
173154
|
}
|
|
171723
173155
|
function cleanupPid() {
|
|
171724
173156
|
try {
|
|
171725
|
-
if (
|
|
173157
|
+
if (existsSync23(PID_FILE)) {
|
|
171726
173158
|
unlinkSync(PID_FILE);
|
|
171727
173159
|
}
|
|
171728
173160
|
} catch {
|
|
@@ -171762,11 +173194,11 @@ function colorizeLogLine(line) {
|
|
|
171762
173194
|
return line;
|
|
171763
173195
|
}
|
|
171764
173196
|
function ensureDirs() {
|
|
171765
|
-
if (!
|
|
171766
|
-
|
|
173197
|
+
if (!existsSync23(NESTOR_DIR2)) {
|
|
173198
|
+
mkdirSync15(NESTOR_DIR2, { recursive: true });
|
|
171767
173199
|
}
|
|
171768
|
-
if (!
|
|
171769
|
-
|
|
173200
|
+
if (!existsSync23(LOG_DIR)) {
|
|
173201
|
+
mkdirSync15(LOG_DIR, { recursive: true });
|
|
171770
173202
|
}
|
|
171771
173203
|
}
|
|
171772
173204
|
function delay(ms) {
|
|
@@ -171807,13 +173239,13 @@ if (process.argv[2] === "__daemon_child__" || process.env.NESTOR_DAEMON_MODE ===
|
|
|
171807
173239
|
|
|
171808
173240
|
// src/commands/watch.ts
|
|
171809
173241
|
init_db();
|
|
171810
|
-
import { existsSync as
|
|
171811
|
-
import { join as
|
|
173242
|
+
import { existsSync as existsSync24, readFileSync as readFileSync24 } from "node:fs";
|
|
173243
|
+
import { join as join29 } from "node:path";
|
|
171812
173244
|
import { homedir as homedir11 } from "node:os";
|
|
171813
173245
|
import chalk13 from "chalk";
|
|
171814
|
-
var NESTOR_DIR3 =
|
|
171815
|
-
var HEARTBEAT_FILE2 =
|
|
171816
|
-
var LOG_FILE2 =
|
|
173246
|
+
var NESTOR_DIR3 = join29(homedir11(), ".nestor");
|
|
173247
|
+
var HEARTBEAT_FILE2 = join29(NESTOR_DIR3, "daemon.heartbeat");
|
|
173248
|
+
var LOG_FILE2 = join29(NESTOR_DIR3, "logs", "daemon.log");
|
|
171817
173249
|
var REFRESH_INTERVAL_MS = 1e3;
|
|
171818
173250
|
var MAX_EVENTS = 12;
|
|
171819
173251
|
function registerWatchCommand(program2) {
|
|
@@ -171881,8 +173313,8 @@ async function refreshData(state) {
|
|
|
171881
173313
|
} catch {
|
|
171882
173314
|
}
|
|
171883
173315
|
try {
|
|
171884
|
-
if (
|
|
171885
|
-
const raw =
|
|
173316
|
+
if (existsSync24(HEARTBEAT_FILE2)) {
|
|
173317
|
+
const raw = readFileSync24(HEARTBEAT_FILE2, "utf-8");
|
|
171886
173318
|
const heartbeat = JSON.parse(raw);
|
|
171887
173319
|
state.daemonRunning = isDaemonAlive(heartbeat.pid);
|
|
171888
173320
|
state.daemonPid = heartbeat.pid;
|
|
@@ -171899,8 +173331,8 @@ async function refreshData(state) {
|
|
|
171899
173331
|
}
|
|
171900
173332
|
function loadRecentEvents(state) {
|
|
171901
173333
|
try {
|
|
171902
|
-
if (!
|
|
171903
|
-
const content =
|
|
173334
|
+
if (!existsSync24(LOG_FILE2)) return;
|
|
173335
|
+
const content = readFileSync24(LOG_FILE2, "utf-8");
|
|
171904
173336
|
const lines = content.split("\n").filter((l) => l.length > 0);
|
|
171905
173337
|
const recent = lines.slice(-MAX_EVENTS);
|
|
171906
173338
|
state.recentEvents = recent.map((line) => {
|
|
@@ -172226,10 +173658,10 @@ function registerTelemetryCommand(program2) {
|
|
|
172226
173658
|
}
|
|
172227
173659
|
console.log(` ${chalk15.dim("Flush:")} every ${(config2.telemetry?.flushIntervalMs ?? 6e4) / 1e3}s`);
|
|
172228
173660
|
}
|
|
172229
|
-
const { join:
|
|
173661
|
+
const { join: join31 } = __require("node:path");
|
|
172230
173662
|
const { homedir: homedir13 } = __require("node:os");
|
|
172231
|
-
const telemetryFile =
|
|
172232
|
-
const idFile =
|
|
173663
|
+
const telemetryFile = join31(homedir13(), ".nestor", "telemetry.jsonl");
|
|
173664
|
+
const idFile = join31(homedir13(), ".nestor", "telemetry-id");
|
|
172233
173665
|
if (fs28.existsSync(idFile)) {
|
|
172234
173666
|
const installId = fs28.readFileSync(idFile, "utf-8").trim();
|
|
172235
173667
|
console.log(` ${chalk15.dim("Install ID:")} ${installId.slice(0, 8)}...`);
|
|
@@ -172270,7 +173702,7 @@ function setTelemetryEnabled(enabled) {
|
|
|
172270
173702
|
init_db();
|
|
172271
173703
|
init_table();
|
|
172272
173704
|
import chalk16 from "chalk";
|
|
172273
|
-
import { readFileSync as
|
|
173705
|
+
import { readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
|
|
172274
173706
|
function registerMemoryCommand(program2) {
|
|
172275
173707
|
const memory = program2.command("memory").description("Manage agent persistent memories");
|
|
172276
173708
|
memory.command("list").description("List memories for an agent").option("-a, --agent <id>", "Agent ID to filter by").option("-t, --type <type>", "Memory type filter (observation, fact, instruction, reflection)").option("-l, --limit <n>", "Maximum results", "20").action(async (options) => {
|
|
@@ -172474,13 +173906,13 @@ Memory Statistics for ${agentId}`));
|
|
|
172474
173906
|
}
|
|
172475
173907
|
}
|
|
172476
173908
|
const json = JSON.stringify(allMemories, null, 2);
|
|
172477
|
-
|
|
173909
|
+
writeFileSync17(outputFile, json, "utf-8");
|
|
172478
173910
|
console.log(chalk16.green(`Exported ${allMemories.length} memories to ${outputFile}`));
|
|
172479
173911
|
});
|
|
172480
173912
|
memory.command("import <file>").description("Import memories from JSON").action(async (file) => {
|
|
172481
173913
|
const store = await getStore();
|
|
172482
173914
|
try {
|
|
172483
|
-
const raw =
|
|
173915
|
+
const raw = readFileSync25(file, "utf-8");
|
|
172484
173916
|
const entries = JSON.parse(raw);
|
|
172485
173917
|
if (!Array.isArray(entries)) {
|
|
172486
173918
|
console.error(chalk16.red("Invalid format: expected a JSON array of memory entries."));
|
|
@@ -173000,8 +174432,8 @@ ${results.length} results for "${query}":
|
|
|
173000
174432
|
}
|
|
173001
174433
|
|
|
173002
174434
|
// src/commands/test.ts
|
|
173003
|
-
import { resolve as resolve17, relative as
|
|
173004
|
-
import { readdirSync as
|
|
174435
|
+
import { resolve as resolve17, relative as relative6 } from "node:path";
|
|
174436
|
+
import { readdirSync as readdirSync7, statSync as statSync7, existsSync as existsSync25 } from "node:fs";
|
|
173005
174437
|
import chalk20 from "chalk";
|
|
173006
174438
|
var TEST_FILE_PATTERNS = [
|
|
173007
174439
|
".nestor-test.yaml",
|
|
@@ -173014,7 +174446,7 @@ function registerTestCommand(program2) {
|
|
|
173014
174446
|
program2.command("test [path]").description("Run skill and agent test suites").option("-l, --list", "List discovered test files", false).option("-f, --file <file>", "Run a specific test file").option("-r, --record <file>", "Record LLM responses to a fixture file").option("-p, --prompt <prompt>", "Prompt for recording mode").option("-v, --verbose", "Show detailed test output", false).option("-j, --json", "Output results as JSON (for CI)", false).action(async (path30, opts) => {
|
|
173015
174447
|
if (opts.file) {
|
|
173016
174448
|
const filePath = resolve17(opts.file);
|
|
173017
|
-
if (!
|
|
174449
|
+
if (!existsSync25(filePath)) {
|
|
173018
174450
|
console.error(chalk20.red(`File not found: ${opts.file}`));
|
|
173019
174451
|
process.exit(1);
|
|
173020
174452
|
}
|
|
@@ -173045,7 +174477,7 @@ function registerTestCommand(program2) {
|
|
|
173045
174477
|
];
|
|
173046
174478
|
const allFiles = [];
|
|
173047
174479
|
for (const testDir of defaultTestDirs) {
|
|
173048
|
-
if (
|
|
174480
|
+
if (existsSync25(testDir)) {
|
|
173049
174481
|
allFiles.push(...discoverTestFiles(testDir));
|
|
173050
174482
|
}
|
|
173051
174483
|
}
|
|
@@ -173064,7 +174496,7 @@ function discoverTestFiles(dir) {
|
|
|
173064
174496
|
function walk(current) {
|
|
173065
174497
|
let entries;
|
|
173066
174498
|
try {
|
|
173067
|
-
entries =
|
|
174499
|
+
entries = readdirSync7(current);
|
|
173068
174500
|
} catch {
|
|
173069
174501
|
return;
|
|
173070
174502
|
}
|
|
@@ -173072,7 +174504,7 @@ function discoverTestFiles(dir) {
|
|
|
173072
174504
|
if (entry === "node_modules" || entry === ".git" || entry === "dist") continue;
|
|
173073
174505
|
const fullPath = resolve17(current, entry);
|
|
173074
174506
|
try {
|
|
173075
|
-
const stat =
|
|
174507
|
+
const stat = statSync7(fullPath);
|
|
173076
174508
|
if (stat.isDirectory()) {
|
|
173077
174509
|
walk(fullPath);
|
|
173078
174510
|
} else if (stat.isFile()) {
|
|
@@ -173088,7 +174520,7 @@ function discoverTestFiles(dir) {
|
|
|
173088
174520
|
}
|
|
173089
174521
|
}
|
|
173090
174522
|
}
|
|
173091
|
-
if (
|
|
174523
|
+
if (existsSync25(dir) && !statSync7(dir).isDirectory()) {
|
|
173092
174524
|
return [dir];
|
|
173093
174525
|
}
|
|
173094
174526
|
walk(dir);
|
|
@@ -173097,7 +174529,7 @@ function discoverTestFiles(dir) {
|
|
|
173097
174529
|
async function listTestFiles(dir) {
|
|
173098
174530
|
const files = discoverTestFiles(dir);
|
|
173099
174531
|
const nestorTestDir = resolve17(dir, ".nestor", "tests");
|
|
173100
|
-
if (
|
|
174532
|
+
if (existsSync25(nestorTestDir)) {
|
|
173101
174533
|
files.push(...discoverTestFiles(nestorTestDir));
|
|
173102
174534
|
}
|
|
173103
174535
|
const unique = [...new Set(files)];
|
|
@@ -173109,7 +174541,7 @@ async function listTestFiles(dir) {
|
|
|
173109
174541
|
Discovered ${unique.length} test file(s):
|
|
173110
174542
|
`));
|
|
173111
174543
|
for (const file of unique) {
|
|
173112
|
-
console.log(chalk20.dim(" ") + chalk20.cyan(
|
|
174544
|
+
console.log(chalk20.dim(" ") + chalk20.cyan(relative6(dir, file)));
|
|
173113
174545
|
}
|
|
173114
174546
|
console.log("");
|
|
173115
174547
|
}
|
|
@@ -173153,7 +174585,7 @@ async function runSkillTestsByName(skillName, verbose, json) {
|
|
|
173153
174585
|
];
|
|
173154
174586
|
const matchingFiles = [];
|
|
173155
174587
|
for (const dir of searchDirs) {
|
|
173156
|
-
if (!
|
|
174588
|
+
if (!existsSync25(dir)) continue;
|
|
173157
174589
|
const files = discoverTestFiles(dir);
|
|
173158
174590
|
for (const file of files) {
|
|
173159
174591
|
if (file.includes(skillName)) {
|
|
@@ -173179,7 +174611,7 @@ Running ${files.length} test suite(s) (legacy runner)...
|
|
|
173179
174611
|
let totalFailed = 0;
|
|
173180
174612
|
const startTime4 = Date.now();
|
|
173181
174613
|
for (const file of files) {
|
|
173182
|
-
const relPath =
|
|
174614
|
+
const relPath = relative6(process.cwd(), file);
|
|
173183
174615
|
let suite;
|
|
173184
174616
|
try {
|
|
173185
174617
|
suite = agentModule.AgentTestRunner.loadSuite(file);
|
|
@@ -173349,11 +174781,289 @@ function registerGuardrailCommand(program2) {
|
|
|
173349
174781
|
});
|
|
173350
174782
|
}
|
|
173351
174783
|
|
|
174784
|
+
// src/commands/loop.ts
|
|
174785
|
+
init_db();
|
|
174786
|
+
init_config2();
|
|
174787
|
+
import chalk22 from "chalk";
|
|
174788
|
+
import { randomUUID as randomUUID51 } from "node:crypto";
|
|
174789
|
+
import { readFileSync as readFileSync26, existsSync as existsSync26 } from "node:fs";
|
|
174790
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
174791
|
+
function runShellCommand(cmd, cwd) {
|
|
174792
|
+
try {
|
|
174793
|
+
const stdout = execSync3(cmd, {
|
|
174794
|
+
cwd,
|
|
174795
|
+
encoding: "utf-8",
|
|
174796
|
+
timeout: 6e4,
|
|
174797
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
174798
|
+
});
|
|
174799
|
+
return { stdout: stdout.trim(), exitCode: 0 };
|
|
174800
|
+
} catch (err) {
|
|
174801
|
+
const execErr = err;
|
|
174802
|
+
return {
|
|
174803
|
+
stdout: (execErr.stdout ?? "").toString().trim(),
|
|
174804
|
+
exitCode: execErr.status ?? 1
|
|
174805
|
+
};
|
|
174806
|
+
}
|
|
174807
|
+
}
|
|
174808
|
+
function getGitDiff(cwd) {
|
|
174809
|
+
try {
|
|
174810
|
+
const diff = execSync3("git diff --stat", { cwd, encoding: "utf-8", timeout: 1e4 });
|
|
174811
|
+
return diff.trim() || "(no changes)";
|
|
174812
|
+
} catch {
|
|
174813
|
+
return "(git not available)";
|
|
174814
|
+
}
|
|
174815
|
+
}
|
|
174816
|
+
function getTestOutput(verifyCmd, cwd) {
|
|
174817
|
+
const result = runShellCommand(verifyCmd, cwd);
|
|
174818
|
+
const maxLen = 2e3;
|
|
174819
|
+
const output = result.stdout.length > maxLen ? result.stdout.slice(-maxLen) : result.stdout;
|
|
174820
|
+
return output;
|
|
174821
|
+
}
|
|
174822
|
+
function buildIterationPrompt(opts) {
|
|
174823
|
+
const parts = [];
|
|
174824
|
+
parts.push(`# Objective (iteration ${opts.iteration}/${opts.maxIterations})`);
|
|
174825
|
+
parts.push("");
|
|
174826
|
+
parts.push(opts.spec);
|
|
174827
|
+
parts.push("");
|
|
174828
|
+
if (opts.previousResults.length > 0) {
|
|
174829
|
+
parts.push("## Previous iterations summary");
|
|
174830
|
+
for (const r of opts.previousResults) {
|
|
174831
|
+
const status = r.specMet ? "PASSED" : "FAILED";
|
|
174832
|
+
parts.push(`- Iteration ${r.iteration}: ${status} (${r.exitReason}) \u2014 ${r.output.slice(0, 200)}`);
|
|
174833
|
+
}
|
|
174834
|
+
parts.push("");
|
|
174835
|
+
}
|
|
174836
|
+
if (opts.gitDiff !== "(no changes)" && opts.gitDiff !== "(git not available)") {
|
|
174837
|
+
parts.push("## Current state (git diff --stat)");
|
|
174838
|
+
parts.push("```");
|
|
174839
|
+
parts.push(opts.gitDiff);
|
|
174840
|
+
parts.push("```");
|
|
174841
|
+
parts.push("");
|
|
174842
|
+
}
|
|
174843
|
+
if (opts.verifyOutput) {
|
|
174844
|
+
parts.push("## Verification output (from last check)");
|
|
174845
|
+
parts.push("```");
|
|
174846
|
+
parts.push(opts.verifyOutput);
|
|
174847
|
+
parts.push("```");
|
|
174848
|
+
parts.push("");
|
|
174849
|
+
}
|
|
174850
|
+
parts.push("## Instructions");
|
|
174851
|
+
parts.push("Analyze the current state and work towards achieving the objective.");
|
|
174852
|
+
parts.push("If previous iterations failed, try a DIFFERENT approach.");
|
|
174853
|
+
parts.push("When done, commit your changes with a descriptive message.");
|
|
174854
|
+
return parts.join("\n");
|
|
174855
|
+
}
|
|
174856
|
+
function registerLoopCommand(program2) {
|
|
174857
|
+
program2.command("loop").description("Run an agent in a fresh-context loop until a specification is met (Ralph pattern)").option("--agent <name>", "Agent name to use", "default").option("--spec <text>", "Specification to achieve").option("--spec-file <path>", "File containing the specification").option("--verify <command>", "Shell command to verify spec (exit 0 = done)").option("--max-iterations <n>", "Max loop iterations", "10").option("--budget <usd>", "Max total budget in USD", "5").option("--cwd <path>", "Working directory", process.cwd()).action(async (opts) => {
|
|
174858
|
+
let spec = opts.spec;
|
|
174859
|
+
if (!spec && opts.specFile) {
|
|
174860
|
+
if (!existsSync26(opts.specFile)) {
|
|
174861
|
+
console.error(chalk22.red(`Spec file not found: ${opts.specFile}`));
|
|
174862
|
+
process.exit(1);
|
|
174863
|
+
}
|
|
174864
|
+
spec = readFileSync26(opts.specFile, "utf-8").trim();
|
|
174865
|
+
}
|
|
174866
|
+
if (!spec) {
|
|
174867
|
+
console.error(chalk22.red("Must provide --spec or --spec-file"));
|
|
174868
|
+
process.exit(1);
|
|
174869
|
+
}
|
|
174870
|
+
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
174871
|
+
const totalBudget = parseFloat(opts.budget);
|
|
174872
|
+
const cwd = opts.cwd;
|
|
174873
|
+
console.log(chalk22.cyan("\n Ralph Fresh-Context Loop"));
|
|
174874
|
+
console.log(chalk22.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
174875
|
+
console.log(chalk22.white(" Spec:"), spec.slice(0, 100) + (spec.length > 100 ? "..." : ""));
|
|
174876
|
+
console.log(chalk22.white(" Agent:"), opts.agent);
|
|
174877
|
+
console.log(chalk22.white(" Max iterations:"), maxIterations);
|
|
174878
|
+
console.log(chalk22.white(" Budget:"), `$${totalBudget}`);
|
|
174879
|
+
if (opts.verify) {
|
|
174880
|
+
console.log(chalk22.white(" Verify cmd:"), opts.verify);
|
|
174881
|
+
}
|
|
174882
|
+
console.log("");
|
|
174883
|
+
let agentModule;
|
|
174884
|
+
try {
|
|
174885
|
+
agentModule = await Promise.resolve().then(() => (init_src5(), src_exports3));
|
|
174886
|
+
} catch {
|
|
174887
|
+
console.error(chalk22.red("Failed to import @nestor/agent. Is it installed?"));
|
|
174888
|
+
process.exit(1);
|
|
174889
|
+
}
|
|
174890
|
+
const store = await getStore();
|
|
174891
|
+
let resolvedAgent;
|
|
174892
|
+
try {
|
|
174893
|
+
const agents = store.listAgents();
|
|
174894
|
+
resolvedAgent = agents.find((a) => a.name === opts.agent);
|
|
174895
|
+
} catch {
|
|
174896
|
+
}
|
|
174897
|
+
const agent = resolvedAgent ?? {
|
|
174898
|
+
id: randomUUID51(),
|
|
174899
|
+
name: opts.agent,
|
|
174900
|
+
adapterType: "claude",
|
|
174901
|
+
adapterConfig: {
|
|
174902
|
+
model: "claude-sonnet-4-20250514",
|
|
174903
|
+
instructions: "You are an expert software engineer. Complete the given specification precisely."
|
|
174904
|
+
},
|
|
174905
|
+
trustLevel: "community",
|
|
174906
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
174907
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
174908
|
+
};
|
|
174909
|
+
let router;
|
|
174910
|
+
try {
|
|
174911
|
+
router = await agentModule.createDefaultRouter();
|
|
174912
|
+
} catch {
|
|
174913
|
+
console.error(chalk22.red("Could not initialize LLM adapter. Is the API key configured?"));
|
|
174914
|
+
process.exit(1);
|
|
174915
|
+
}
|
|
174916
|
+
const summary = {
|
|
174917
|
+
totalIterations: 0,
|
|
174918
|
+
totalCostUsd: 0,
|
|
174919
|
+
totalTokens: 0,
|
|
174920
|
+
totalDurationMs: 0,
|
|
174921
|
+
specMet: false,
|
|
174922
|
+
iterationResults: []
|
|
174923
|
+
};
|
|
174924
|
+
for (let i = 1; i <= maxIterations; i++) {
|
|
174925
|
+
if (summary.totalCostUsd >= totalBudget) {
|
|
174926
|
+
console.log(chalk22.yellow(`
|
|
174927
|
+
Budget exhausted ($${summary.totalCostUsd.toFixed(4)} / $${totalBudget}). Stopping.`));
|
|
174928
|
+
break;
|
|
174929
|
+
}
|
|
174930
|
+
const remainingBudget = totalBudget - summary.totalCostUsd;
|
|
174931
|
+
console.log(chalk22.cyan(`
|
|
174932
|
+
\u2500\u2500 Iteration ${i}/${maxIterations} `) + chalk22.dim(`(budget remaining: $${remainingBudget.toFixed(4)}) \u2500\u2500`));
|
|
174933
|
+
if (opts.verify && i > 1) {
|
|
174934
|
+
const verifyResult = runShellCommand(opts.verify, cwd);
|
|
174935
|
+
if (verifyResult.exitCode === 0) {
|
|
174936
|
+
console.log(chalk22.green(" Specification met! Verify command exited 0."));
|
|
174937
|
+
summary.specMet = true;
|
|
174938
|
+
break;
|
|
174939
|
+
}
|
|
174940
|
+
}
|
|
174941
|
+
const gitDiff = getGitDiff(cwd);
|
|
174942
|
+
const verifyOutput = opts.verify ? getTestOutput(opts.verify, cwd) : "";
|
|
174943
|
+
const prompt = buildIterationPrompt({
|
|
174944
|
+
spec,
|
|
174945
|
+
iteration: i,
|
|
174946
|
+
maxIterations,
|
|
174947
|
+
previousResults: summary.iterationResults,
|
|
174948
|
+
gitDiff,
|
|
174949
|
+
verifyOutput
|
|
174950
|
+
});
|
|
174951
|
+
const modelId = agent.adapterConfig.model ?? "claude-sonnet-4-20250514";
|
|
174952
|
+
const adapter = router.resolve(modelId);
|
|
174953
|
+
const events = new agentModule.RuntimeEventBus();
|
|
174954
|
+
const tools = new agentModule.ToolRegistry();
|
|
174955
|
+
agentModule.registerBuiltinTools(tools);
|
|
174956
|
+
const runtime = new agentModule.AgentRuntime({
|
|
174957
|
+
adapter,
|
|
174958
|
+
tools,
|
|
174959
|
+
toolExecutor: new agentModule.ToolExecutor(),
|
|
174960
|
+
role: {
|
|
174961
|
+
name: agent.name,
|
|
174962
|
+
description: agent.description ?? "Loop agent",
|
|
174963
|
+
instructions: agent.adapterConfig.instructions ?? "You are an expert software engineer.",
|
|
174964
|
+
constraints: []
|
|
174965
|
+
},
|
|
174966
|
+
workingDir: cwd,
|
|
174967
|
+
events,
|
|
174968
|
+
maxCostUsd: remainingBudget,
|
|
174969
|
+
maxIterations: 50,
|
|
174970
|
+
db: store,
|
|
174971
|
+
modelRouter: router,
|
|
174972
|
+
// Enable context rotation within each iteration too
|
|
174973
|
+
contextRotation: { maxMessages: 40, maxTokensEstimate: 8e4 },
|
|
174974
|
+
stuckDetection: { maxRepeatedCalls: 3, maxConsecutiveErrors: 3 }
|
|
174975
|
+
});
|
|
174976
|
+
const taskId = randomUUID51();
|
|
174977
|
+
let iterOutput = "";
|
|
174978
|
+
try {
|
|
174979
|
+
for await (const event of runtime.runStreaming({
|
|
174980
|
+
prompt,
|
|
174981
|
+
agentId: agent.id,
|
|
174982
|
+
taskId
|
|
174983
|
+
})) {
|
|
174984
|
+
switch (event.type) {
|
|
174985
|
+
case "token":
|
|
174986
|
+
process.stdout.write(event.text);
|
|
174987
|
+
iterOutput += event.text;
|
|
174988
|
+
break;
|
|
174989
|
+
case "tool_start":
|
|
174990
|
+
console.log(chalk22.dim(`
|
|
174991
|
+
[tool] ${event.name}`));
|
|
174992
|
+
break;
|
|
174993
|
+
case "tool_result":
|
|
174994
|
+
if (!event.success) {
|
|
174995
|
+
console.log(chalk22.red(` [tool error] ${event.name}: ${event.result.slice(0, 100)}`));
|
|
174996
|
+
}
|
|
174997
|
+
break;
|
|
174998
|
+
case "error":
|
|
174999
|
+
console.log(chalk22.red(`
|
|
175000
|
+
[error] ${event.message}`));
|
|
175001
|
+
break;
|
|
175002
|
+
case "done": {
|
|
175003
|
+
const r = event.result;
|
|
175004
|
+
const iterResult = {
|
|
175005
|
+
iteration: i,
|
|
175006
|
+
output: r.output || iterOutput,
|
|
175007
|
+
exitReason: r.exitReason,
|
|
175008
|
+
costUsd: r.usage.estimatedCostUsd,
|
|
175009
|
+
tokens: r.usage.totalTokens,
|
|
175010
|
+
durationMs: r.durationMs,
|
|
175011
|
+
specMet: false
|
|
175012
|
+
};
|
|
175013
|
+
summary.totalCostUsd += r.usage.estimatedCostUsd;
|
|
175014
|
+
summary.totalTokens += r.usage.totalTokens;
|
|
175015
|
+
summary.totalDurationMs += r.durationMs;
|
|
175016
|
+
summary.totalIterations = i;
|
|
175017
|
+
if (opts.verify) {
|
|
175018
|
+
const postVerify = runShellCommand(opts.verify, cwd);
|
|
175019
|
+
iterResult.specMet = postVerify.exitCode === 0;
|
|
175020
|
+
}
|
|
175021
|
+
summary.iterationResults.push(iterResult);
|
|
175022
|
+
console.log("");
|
|
175023
|
+
console.log(chalk22.dim(` [${r.exitReason}] ${r.usage.totalTokens} tokens, $${r.usage.estimatedCostUsd.toFixed(4)}, ${(r.durationMs / 1e3).toFixed(1)}s`));
|
|
175024
|
+
if (iterResult.specMet) {
|
|
175025
|
+
console.log(chalk22.green(" Specification met!"));
|
|
175026
|
+
summary.specMet = true;
|
|
175027
|
+
}
|
|
175028
|
+
break;
|
|
175029
|
+
}
|
|
175030
|
+
}
|
|
175031
|
+
}
|
|
175032
|
+
} catch (err) {
|
|
175033
|
+
console.error(chalk22.red(`
|
|
175034
|
+
Iteration ${i} crashed: ${err instanceof Error ? err.message : String(err)}`));
|
|
175035
|
+
summary.iterationResults.push({
|
|
175036
|
+
iteration: i,
|
|
175037
|
+
output: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
175038
|
+
exitReason: "error",
|
|
175039
|
+
costUsd: 0,
|
|
175040
|
+
tokens: 0,
|
|
175041
|
+
durationMs: 0,
|
|
175042
|
+
specMet: false
|
|
175043
|
+
});
|
|
175044
|
+
}
|
|
175045
|
+
if (summary.specMet) break;
|
|
175046
|
+
}
|
|
175047
|
+
console.log(chalk22.cyan("\n \u2500\u2500 Loop Summary \u2500\u2500"));
|
|
175048
|
+
console.log(chalk22.white(" Iterations:"), summary.totalIterations);
|
|
175049
|
+
console.log(chalk22.white(" Total cost:"), `$${summary.totalCostUsd.toFixed(4)}`);
|
|
175050
|
+
console.log(chalk22.white(" Total tokens:"), summary.totalTokens.toLocaleString());
|
|
175051
|
+
console.log(chalk22.white(" Total time:"), `${(summary.totalDurationMs / 1e3).toFixed(1)}s`);
|
|
175052
|
+
console.log(
|
|
175053
|
+
chalk22.white(" Spec met:"),
|
|
175054
|
+
summary.specMet ? chalk22.green("YES") : chalk22.red("NO")
|
|
175055
|
+
);
|
|
175056
|
+
console.log("");
|
|
175057
|
+
await store.close();
|
|
175058
|
+
process.exit(summary.specMet ? 0 : 1);
|
|
175059
|
+
});
|
|
175060
|
+
}
|
|
175061
|
+
|
|
173352
175062
|
// src/index.ts
|
|
173353
175063
|
function checkBetaAccess() {
|
|
173354
|
-
const licenseFile =
|
|
173355
|
-
if (!
|
|
173356
|
-
const key =
|
|
175064
|
+
const licenseFile = join30(homedir12(), ".nestor", "license.key");
|
|
175065
|
+
if (!existsSync27(licenseFile)) return false;
|
|
175066
|
+
const key = readFileSync27(licenseFile, "utf-8").trim();
|
|
173357
175067
|
return /^NESTOR-BETA-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(key);
|
|
173358
175068
|
}
|
|
173359
175069
|
var command2 = process.argv[2];
|
|
@@ -173373,7 +175083,7 @@ if (command2 && !["--help", "-h", "--version", "-V", "install"].includes(command
|
|
|
173373
175083
|
}
|
|
173374
175084
|
}
|
|
173375
175085
|
var program = new Command();
|
|
173376
|
-
program.name("nestor-sh").description("Nestor AI Agent Platform \u2014 orchestrate, secure and monitor AI agents").version("
|
|
175086
|
+
program.name("nestor-sh").description("Nestor AI Agent Platform \u2014 orchestrate, secure and monitor AI agents").version("3.0.0");
|
|
173377
175087
|
registerStartCommand(program);
|
|
173378
175088
|
registerInstallCommand(program);
|
|
173379
175089
|
registerAgentCommand(program);
|
|
@@ -173393,6 +175103,7 @@ registerTemplateCommand(program);
|
|
|
173393
175103
|
registerRagCommand(program);
|
|
173394
175104
|
registerTestCommand(program);
|
|
173395
175105
|
registerGuardrailCommand(program);
|
|
175106
|
+
registerLoopCommand(program);
|
|
173396
175107
|
var args = process.argv.slice(2);
|
|
173397
175108
|
if (args.length === 0) {
|
|
173398
175109
|
Promise.resolve().then(() => (init_shell(), shell_exports)).then(({ registerShellCommand: _ }) => {
|