nestor-sh 2.7.0 → 2.9.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 +1222 -228
- package/dist/studio/assets/index-BFlEVRQQ.js +596 -0
- package/dist/studio/assets/index-CgYCHAo_.js +596 -0
- package/dist/studio/assets/index-CqlwLAqD.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
|
*/
|
|
@@ -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 = "2.
|
|
11504
|
+
SERVER_VERSION = "2.9.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 = "2.9.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 ? "2.
|
|
20347
|
+
return nativeModule ? "2.9.0" : null;
|
|
20277
20348
|
}
|
|
20278
20349
|
function validateSsrf(url, allowPrivate = false) {
|
|
20279
20350
|
if (nativeModule) {
|
|
@@ -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: existsSync27 } = __require("node:fs");
|
|
102214
|
+
if (existsSync27(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: existsSync27 } = __require("node:fs");
|
|
102230
|
+
if (existsSync27(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: existsSync27 } = __require("node:fs");
|
|
102246
|
+
if (existsSync27(p8)) return p8;
|
|
102176
102247
|
} catch {
|
|
102177
102248
|
}
|
|
102178
102249
|
}
|
|
@@ -103436,6 +103507,114 @@ 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
|
+
|
|
103439
103618
|
// ../agent/src/tools/system-tools.ts
|
|
103440
103619
|
import { z as z28 } from "zod";
|
|
103441
103620
|
import { randomUUID as randomUUID23 } from "node:crypto";
|
|
@@ -103827,11 +104006,130 @@ ${summary}`, isError: false };
|
|
|
103827
104006
|
}
|
|
103828
104007
|
}
|
|
103829
104008
|
});
|
|
104009
|
+
const searchMissionsSchema = z28.object({
|
|
104010
|
+
query: z28.string().describe("Search query (searches titles, objectives, findings)"),
|
|
104011
|
+
type: z28.string().optional().describe("Filter by mission type (osint, research, code, audit, creation, analysis)"),
|
|
104012
|
+
limit: z28.number().optional().describe("Max results (default: 5)")
|
|
104013
|
+
});
|
|
104014
|
+
registry.register({
|
|
104015
|
+
name: "nestor_search_missions",
|
|
104016
|
+
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.",
|
|
104017
|
+
inputSchema: searchMissionsSchema,
|
|
104018
|
+
handler: async (input, _ctx) => {
|
|
104019
|
+
const args2 = input;
|
|
104020
|
+
try {
|
|
104021
|
+
const results = store.searchMissions(args2.query, {
|
|
104022
|
+
type: args2.type,
|
|
104023
|
+
limit: args2.limit || 5
|
|
104024
|
+
});
|
|
104025
|
+
if (results.length === 0) {
|
|
104026
|
+
return { output: `No missions found matching "${args2.query}".`, isError: false };
|
|
104027
|
+
}
|
|
104028
|
+
const summary = results.map((m) => {
|
|
104029
|
+
const quality = m.qualityScore !== null ? ` \u2014 quality: ${(m.qualityScore * 100).toFixed(0)}%` : "";
|
|
104030
|
+
return `\u2022 [${m.status}] ${m.title} (${m.type}) \u2014 ${m.findingCount} findings, $${m.spentUsd.toFixed(4)}${quality}
|
|
104031
|
+
ID: ${m.id}
|
|
104032
|
+
Objective: ${m.objective.substring(0, 200)}`;
|
|
104033
|
+
}).join("\n\n");
|
|
104034
|
+
return {
|
|
104035
|
+
output: `Found ${results.length} mission(s) matching "${args2.query}":
|
|
104036
|
+
|
|
104037
|
+
${summary}`,
|
|
104038
|
+
isError: false
|
|
104039
|
+
};
|
|
104040
|
+
} catch (err) {
|
|
104041
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104042
|
+
return { output: `Error searching missions: ${msg}`, isError: true };
|
|
104043
|
+
}
|
|
104044
|
+
}
|
|
104045
|
+
});
|
|
104046
|
+
registry.register({
|
|
104047
|
+
name: "nestor_get_mission_report",
|
|
104048
|
+
description: "Get the full report of a specific past mission by ID. Returns the markdown report, evaluation scores, and key findings.",
|
|
104049
|
+
inputSchema: missionIdSchema,
|
|
104050
|
+
handler: async (input, _ctx) => {
|
|
104051
|
+
const args2 = input;
|
|
104052
|
+
try {
|
|
104053
|
+
const mission = store.getMission(args2.missionId);
|
|
104054
|
+
if (!mission) {
|
|
104055
|
+
return { output: `Mission "${args2.missionId}" not found.`, isError: true };
|
|
104056
|
+
}
|
|
104057
|
+
let output = `# Mission: ${mission.title}
|
|
104058
|
+
`;
|
|
104059
|
+
output += `Type: ${mission.type} | Status: ${mission.status}
|
|
104060
|
+
`;
|
|
104061
|
+
output += `Objective: ${mission.objective}
|
|
104062
|
+
`;
|
|
104063
|
+
output += `Cost: $${mission.spentUsd.toFixed(4)} | Findings: ${Array.isArray(mission.findings) ? mission.findings.length : 0}
|
|
104064
|
+
`;
|
|
104065
|
+
if (mission.evaluation && typeof mission.evaluation === "object") {
|
|
104066
|
+
const eval_ = mission.evaluation;
|
|
104067
|
+
output += `
|
|
104068
|
+
## Evaluation
|
|
104069
|
+
`;
|
|
104070
|
+
if (typeof eval_.overall === "number") output += `Overall: ${(eval_.overall * 100).toFixed(0)}%
|
|
104071
|
+
`;
|
|
104072
|
+
if (typeof eval_.completeness === "number") output += `Completeness: ${(eval_.completeness * 100).toFixed(0)}%
|
|
104073
|
+
`;
|
|
104074
|
+
if (typeof eval_.accuracy === "number") output += `Accuracy: ${(eval_.accuracy * 100).toFixed(0)}%
|
|
104075
|
+
`;
|
|
104076
|
+
if (typeof eval_.depth === "number") output += `Depth: ${(eval_.depth * 100).toFixed(0)}%
|
|
104077
|
+
`;
|
|
104078
|
+
if (typeof eval_.relevance === "number") output += `Relevance: ${(eval_.relevance * 100).toFixed(0)}%
|
|
104079
|
+
`;
|
|
104080
|
+
}
|
|
104081
|
+
if (mission.report) {
|
|
104082
|
+
output += `
|
|
104083
|
+
## Report
|
|
104084
|
+
${mission.report}`;
|
|
104085
|
+
} else {
|
|
104086
|
+
output += `
|
|
104087
|
+
## Findings Summary
|
|
104088
|
+
`;
|
|
104089
|
+
const findings = Array.isArray(mission.findings) ? mission.findings : [];
|
|
104090
|
+
for (const f of findings.slice(0, 20)) {
|
|
104091
|
+
const finding = f;
|
|
104092
|
+
output += `- [${finding.type || "finding"}] ${finding.title || "Untitled"}: ${String(finding.content || "").substring(0, 300)}
|
|
104093
|
+
`;
|
|
104094
|
+
}
|
|
104095
|
+
if (findings.length > 20) {
|
|
104096
|
+
output += `
|
|
104097
|
+
... and ${findings.length - 20} more findings.`;
|
|
104098
|
+
}
|
|
104099
|
+
}
|
|
104100
|
+
return { output, isError: false };
|
|
104101
|
+
} catch (err) {
|
|
104102
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104103
|
+
return { output: `Error getting mission report: ${msg}`, isError: true };
|
|
104104
|
+
}
|
|
104105
|
+
}
|
|
104106
|
+
});
|
|
104107
|
+
registry.register({
|
|
104108
|
+
name: "nestor_query_memory",
|
|
104109
|
+
description: "Query all memory layers (episodic: past missions, semantic: knowledge graph) for relevant information. Returns merged results sorted by relevance.",
|
|
104110
|
+
inputSchema: queryMemorySchema,
|
|
104111
|
+
handler: async (input, _ctx) => {
|
|
104112
|
+
const args2 = input;
|
|
104113
|
+
try {
|
|
104114
|
+
const memory = new LayeredMemory(store);
|
|
104115
|
+
const results = await memory.query(String(args2.query), 10);
|
|
104116
|
+
const layerStats = memory.stats();
|
|
104117
|
+
return {
|
|
104118
|
+
output: JSON.stringify({ results, layers: layerStats }, null, 2),
|
|
104119
|
+
isError: false
|
|
104120
|
+
};
|
|
104121
|
+
} catch (err) {
|
|
104122
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
104123
|
+
return { output: `Memory query failed: ${msg}`, isError: true };
|
|
104124
|
+
}
|
|
104125
|
+
}
|
|
104126
|
+
});
|
|
103830
104127
|
}
|
|
103831
|
-
var emptySchema, createAgentSchema, runAgentSchema, deleteAgentSchema, createWorkflowSchema, searchMemorySchema, storeMemorySchema, createGuardrailSchema, createMissionSchema, missionIdSchema;
|
|
104128
|
+
var emptySchema, createAgentSchema, runAgentSchema, deleteAgentSchema, createWorkflowSchema, searchMemorySchema, storeMemorySchema, createGuardrailSchema, createMissionSchema, missionIdSchema, queryMemorySchema;
|
|
103832
104129
|
var init_system_tools = __esm({
|
|
103833
104130
|
"../agent/src/tools/system-tools.ts"() {
|
|
103834
104131
|
"use strict";
|
|
104132
|
+
init_layered_memory();
|
|
103835
104133
|
emptySchema = z28.object({}).passthrough();
|
|
103836
104134
|
createAgentSchema = z28.object({
|
|
103837
104135
|
name: z28.string().describe("Agent name"),
|
|
@@ -103879,6 +104177,9 @@ var init_system_tools = __esm({
|
|
|
103879
104177
|
missionIdSchema = z28.object({
|
|
103880
104178
|
missionId: z28.string().describe("The mission ID")
|
|
103881
104179
|
});
|
|
104180
|
+
queryMemorySchema = z28.object({
|
|
104181
|
+
query: z28.string().describe("Natural language query to search across all memory layers (episodic: past missions, semantic: knowledge graph)")
|
|
104182
|
+
});
|
|
103882
104183
|
}
|
|
103883
104184
|
});
|
|
103884
104185
|
|
|
@@ -107896,6 +108197,7 @@ var init_memory = __esm({
|
|
|
107896
108197
|
init_vector_store();
|
|
107897
108198
|
init_persistent_vector_store();
|
|
107898
108199
|
init_embedding_provider();
|
|
108200
|
+
init_layered_memory();
|
|
107899
108201
|
AgentMemory = class {
|
|
107900
108202
|
constructor(store, agentId) {
|
|
107901
108203
|
this.store = store;
|
|
@@ -108365,6 +108667,217 @@ ${e.reasoning}${ctx}`;
|
|
|
108365
108667
|
}
|
|
108366
108668
|
});
|
|
108367
108669
|
|
|
108670
|
+
// ../agent/src/safety/context-rotator.ts
|
|
108671
|
+
var ContextRotator;
|
|
108672
|
+
var init_context_rotator = __esm({
|
|
108673
|
+
"../agent/src/safety/context-rotator.ts"() {
|
|
108674
|
+
"use strict";
|
|
108675
|
+
ContextRotator = class {
|
|
108676
|
+
cfg;
|
|
108677
|
+
messageCount = 0;
|
|
108678
|
+
estimatedTokens = 0;
|
|
108679
|
+
constructor(config2 = {}) {
|
|
108680
|
+
this.cfg = {
|
|
108681
|
+
maxMessages: config2.maxMessages ?? 50,
|
|
108682
|
+
maxTokensEstimate: config2.maxTokensEstimate ?? 1e5,
|
|
108683
|
+
charsPerToken: config2.charsPerToken ?? 4,
|
|
108684
|
+
recentToKeep: config2.recentToKeep ?? 10
|
|
108685
|
+
};
|
|
108686
|
+
}
|
|
108687
|
+
// ── Recording ───────────────────────────────────────────────────────
|
|
108688
|
+
/** Record a message added to context. */
|
|
108689
|
+
recordMessage(content) {
|
|
108690
|
+
this.messageCount++;
|
|
108691
|
+
this.estimatedTokens += Math.ceil(content.length / this.cfg.charsPerToken);
|
|
108692
|
+
}
|
|
108693
|
+
// ── Checking ────────────────────────────────────────────────────────
|
|
108694
|
+
/** Check if context should be rotated. */
|
|
108695
|
+
shouldRotate() {
|
|
108696
|
+
if (this.messageCount >= this.cfg.maxMessages) {
|
|
108697
|
+
return {
|
|
108698
|
+
rotate: true,
|
|
108699
|
+
reason: `${this.messageCount} messages (limit: ${this.cfg.maxMessages})`
|
|
108700
|
+
};
|
|
108701
|
+
}
|
|
108702
|
+
if (this.estimatedTokens >= this.cfg.maxTokensEstimate) {
|
|
108703
|
+
return {
|
|
108704
|
+
rotate: true,
|
|
108705
|
+
reason: `~${this.estimatedTokens} tokens (limit: ${this.cfg.maxTokensEstimate})`
|
|
108706
|
+
};
|
|
108707
|
+
}
|
|
108708
|
+
return { rotate: false };
|
|
108709
|
+
}
|
|
108710
|
+
// ── Rotation helpers ────────────────────────────────────────────────
|
|
108711
|
+
/**
|
|
108712
|
+
* Build a compact summary for context rotation.
|
|
108713
|
+
*
|
|
108714
|
+
* Strategy:
|
|
108715
|
+
* - Preserve the system message verbatim
|
|
108716
|
+
* - Summarize the middle messages into a compact block
|
|
108717
|
+
* - Keep the last N recent messages verbatim
|
|
108718
|
+
*
|
|
108719
|
+
* Returns the summary text to inject as a single assistant message
|
|
108720
|
+
* between the system prompt and the recent messages.
|
|
108721
|
+
*/
|
|
108722
|
+
buildRotationSummary(messages) {
|
|
108723
|
+
const keepCount = this.cfg.recentToKeep;
|
|
108724
|
+
const system = messages.find((m) => m.role === "system");
|
|
108725
|
+
const nonSystem = messages.filter((m) => m.role !== "system");
|
|
108726
|
+
const recent = nonSystem.slice(-keepCount);
|
|
108727
|
+
const middle = nonSystem.slice(0, Math.max(0, nonSystem.length - keepCount));
|
|
108728
|
+
const parts = [];
|
|
108729
|
+
if (system) {
|
|
108730
|
+
parts.push("[System prompt preserved]");
|
|
108731
|
+
}
|
|
108732
|
+
if (middle.length > 0) {
|
|
108733
|
+
const toolCalls = middle.filter(
|
|
108734
|
+
(m) => m.content.includes("tool_use") || m.content.includes("function_call") || m.content.includes("tool_result")
|
|
108735
|
+
).length;
|
|
108736
|
+
const findings = middle.filter(
|
|
108737
|
+
(m) => m.content.includes("finding") || m.content.includes("result") || m.content.includes("discovered")
|
|
108738
|
+
).length;
|
|
108739
|
+
const errors = middle.filter(
|
|
108740
|
+
(m) => m.content.includes("error") || m.content.includes("failed")
|
|
108741
|
+
).length;
|
|
108742
|
+
parts.push(
|
|
108743
|
+
`[${middle.length} earlier messages summarized: ${toolCalls} tool interactions, ${findings} results/findings, ${errors} errors]`
|
|
108744
|
+
);
|
|
108745
|
+
}
|
|
108746
|
+
parts.push(`[${recent.length} recent messages preserved below]`);
|
|
108747
|
+
return parts.join("\n");
|
|
108748
|
+
}
|
|
108749
|
+
// ── Lifecycle ───────────────────────────────────────────────────────
|
|
108750
|
+
/** Reset counters after rotation. */
|
|
108751
|
+
reset() {
|
|
108752
|
+
this.messageCount = 0;
|
|
108753
|
+
this.estimatedTokens = 0;
|
|
108754
|
+
}
|
|
108755
|
+
/** Get current stats (useful for logging/telemetry). */
|
|
108756
|
+
getStats() {
|
|
108757
|
+
return {
|
|
108758
|
+
messageCount: this.messageCount,
|
|
108759
|
+
estimatedTokens: this.estimatedTokens
|
|
108760
|
+
};
|
|
108761
|
+
}
|
|
108762
|
+
};
|
|
108763
|
+
}
|
|
108764
|
+
});
|
|
108765
|
+
|
|
108766
|
+
// ../agent/src/safety/stuck-detector.ts
|
|
108767
|
+
var StuckDetector;
|
|
108768
|
+
var init_stuck_detector = __esm({
|
|
108769
|
+
"../agent/src/safety/stuck-detector.ts"() {
|
|
108770
|
+
"use strict";
|
|
108771
|
+
StuckDetector = class {
|
|
108772
|
+
cfg;
|
|
108773
|
+
toolCallHistory = [];
|
|
108774
|
+
findingsCount = 0;
|
|
108775
|
+
iterationsWithoutProgress = 0;
|
|
108776
|
+
consecutiveErrors = 0;
|
|
108777
|
+
startTime = Date.now();
|
|
108778
|
+
constructor(config2 = {}) {
|
|
108779
|
+
this.cfg = {
|
|
108780
|
+
maxRepeatedCalls: config2.maxRepeatedCalls ?? 3,
|
|
108781
|
+
maxIterationsWithoutProgress: config2.maxIterationsWithoutProgress ?? 5,
|
|
108782
|
+
maxConsecutiveErrors: config2.maxConsecutiveErrors ?? 3,
|
|
108783
|
+
maxSubObjectiveTimeMs: config2.maxSubObjectiveTimeMs ?? 3e5
|
|
108784
|
+
};
|
|
108785
|
+
}
|
|
108786
|
+
// ── Recording ───────────────────────────────────────────────────────
|
|
108787
|
+
/** Record a tool call for pattern detection. */
|
|
108788
|
+
recordToolCall(name, args2) {
|
|
108789
|
+
this.toolCallHistory.push({
|
|
108790
|
+
name,
|
|
108791
|
+
args: JSON.stringify(args2),
|
|
108792
|
+
timestamp: Date.now()
|
|
108793
|
+
});
|
|
108794
|
+
}
|
|
108795
|
+
/** Record a tool result (success or error). */
|
|
108796
|
+
recordResult(isError) {
|
|
108797
|
+
if (isError) {
|
|
108798
|
+
this.consecutiveErrors++;
|
|
108799
|
+
} else {
|
|
108800
|
+
this.consecutiveErrors = 0;
|
|
108801
|
+
}
|
|
108802
|
+
}
|
|
108803
|
+
/** Update findings count — call after each iteration of the agent loop. */
|
|
108804
|
+
updateProgress(currentFindings) {
|
|
108805
|
+
if (currentFindings > this.findingsCount) {
|
|
108806
|
+
this.findingsCount = currentFindings;
|
|
108807
|
+
this.iterationsWithoutProgress = 0;
|
|
108808
|
+
} else {
|
|
108809
|
+
this.iterationsWithoutProgress++;
|
|
108810
|
+
}
|
|
108811
|
+
}
|
|
108812
|
+
// ── Checking ────────────────────────────────────────────────────────
|
|
108813
|
+
/** Check if the agent appears stuck. */
|
|
108814
|
+
check() {
|
|
108815
|
+
const elapsed = Date.now() - this.startTime;
|
|
108816
|
+
if (elapsed > this.cfg.maxSubObjectiveTimeMs) {
|
|
108817
|
+
return {
|
|
108818
|
+
isStuck: true,
|
|
108819
|
+
reason: "timeout",
|
|
108820
|
+
details: `Sub-objective exceeded ${this.cfg.maxSubObjectiveTimeMs}ms (elapsed: ${elapsed}ms)`,
|
|
108821
|
+
suggestedAction: "skip"
|
|
108822
|
+
};
|
|
108823
|
+
}
|
|
108824
|
+
const windowSize = this.cfg.maxRepeatedCalls;
|
|
108825
|
+
if (this.toolCallHistory.length >= windowSize) {
|
|
108826
|
+
const recent = this.toolCallHistory.slice(-windowSize);
|
|
108827
|
+
const first2 = recent[0];
|
|
108828
|
+
const allSame = recent.every(
|
|
108829
|
+
(c) => c.name === first2.name && c.args === first2.args
|
|
108830
|
+
);
|
|
108831
|
+
if (allSame) {
|
|
108832
|
+
return {
|
|
108833
|
+
isStuck: true,
|
|
108834
|
+
reason: "repeated_calls",
|
|
108835
|
+
details: `Tool "${first2.name}" called ${windowSize}x with identical args`,
|
|
108836
|
+
suggestedAction: "retry_different"
|
|
108837
|
+
};
|
|
108838
|
+
}
|
|
108839
|
+
}
|
|
108840
|
+
if (this.iterationsWithoutProgress >= this.cfg.maxIterationsWithoutProgress) {
|
|
108841
|
+
return {
|
|
108842
|
+
isStuck: true,
|
|
108843
|
+
reason: "no_progress",
|
|
108844
|
+
details: `${this.iterationsWithoutProgress} iterations without new findings`,
|
|
108845
|
+
suggestedAction: "escalate_model"
|
|
108846
|
+
};
|
|
108847
|
+
}
|
|
108848
|
+
if (this.consecutiveErrors >= this.cfg.maxConsecutiveErrors) {
|
|
108849
|
+
return {
|
|
108850
|
+
isStuck: true,
|
|
108851
|
+
reason: "error_loop",
|
|
108852
|
+
details: `${this.consecutiveErrors} consecutive tool errors`,
|
|
108853
|
+
suggestedAction: "retry_different"
|
|
108854
|
+
};
|
|
108855
|
+
}
|
|
108856
|
+
return { isStuck: false };
|
|
108857
|
+
}
|
|
108858
|
+
// ── Lifecycle ───────────────────────────────────────────────────────
|
|
108859
|
+
/** Reset all state for reuse (e.g., new sub-objective). */
|
|
108860
|
+
reset() {
|
|
108861
|
+
this.toolCallHistory = [];
|
|
108862
|
+
this.findingsCount = 0;
|
|
108863
|
+
this.iterationsWithoutProgress = 0;
|
|
108864
|
+
this.consecutiveErrors = 0;
|
|
108865
|
+
this.startTime = Date.now();
|
|
108866
|
+
}
|
|
108867
|
+
/** Get a diagnostic snapshot (useful for logging/telemetry). */
|
|
108868
|
+
getSnapshot() {
|
|
108869
|
+
return {
|
|
108870
|
+
totalCalls: this.toolCallHistory.length,
|
|
108871
|
+
consecutiveErrors: this.consecutiveErrors,
|
|
108872
|
+
iterationsWithoutProgress: this.iterationsWithoutProgress,
|
|
108873
|
+
findingsCount: this.findingsCount,
|
|
108874
|
+
elapsedMs: Date.now() - this.startTime
|
|
108875
|
+
};
|
|
108876
|
+
}
|
|
108877
|
+
};
|
|
108878
|
+
}
|
|
108879
|
+
});
|
|
108880
|
+
|
|
108368
108881
|
// ../agent/src/runtime.ts
|
|
108369
108882
|
function estimateCost(model, inputTokens, outputTokens) {
|
|
108370
108883
|
let costs = COST_PER_1M_TOKENS[model];
|
|
@@ -108432,6 +108945,8 @@ var init_runtime = __esm({
|
|
|
108432
108945
|
init_hot_swap();
|
|
108433
108946
|
init_auto_downgrade();
|
|
108434
108947
|
init_reason_logger();
|
|
108948
|
+
init_context_rotator();
|
|
108949
|
+
init_stuck_detector();
|
|
108435
108950
|
COST_PER_1M_TOKENS = {
|
|
108436
108951
|
"claude-sonnet-4-20250514": { input: 3, output: 15 },
|
|
108437
108952
|
"claude-3-5-sonnet": { input: 3, output: 15 },
|
|
@@ -108466,6 +108981,10 @@ var init_runtime = __esm({
|
|
|
108466
108981
|
autoDowngrade = null;
|
|
108467
108982
|
/** Explainability: logs the agent's reasoning at each step. */
|
|
108468
108983
|
reasonLogger = new ReasonLogger();
|
|
108984
|
+
/** Context rotator for fresh-context pattern (Ralph methodology). */
|
|
108985
|
+
contextRotator;
|
|
108986
|
+
/** Stuck detector for loop/stall detection. */
|
|
108987
|
+
stuckDetector;
|
|
108469
108988
|
constructor(config2) {
|
|
108470
108989
|
this.config = config2;
|
|
108471
108990
|
this.hotSwap = new HotSwapAdapter(config2.adapter);
|
|
@@ -108550,6 +109069,8 @@ var init_runtime = __esm({
|
|
|
108550
109069
|
workspaceContext,
|
|
108551
109070
|
errorCorrections
|
|
108552
109071
|
});
|
|
109072
|
+
this.contextRotator = new ContextRotator(config2.contextRotation);
|
|
109073
|
+
this.stuckDetector = new StuckDetector(config2.stuckDetection);
|
|
108553
109074
|
}
|
|
108554
109075
|
/**
|
|
108555
109076
|
* Run the agent loop for a given task.
|
|
@@ -108577,6 +109098,9 @@ var init_runtime = __esm({
|
|
|
108577
109098
|
let lastOutput = "";
|
|
108578
109099
|
let exitReason = "completed";
|
|
108579
109100
|
let error;
|
|
109101
|
+
let consecutiveStucks = 0;
|
|
109102
|
+
this.contextRotator.reset();
|
|
109103
|
+
this.stuckDetector.reset();
|
|
108580
109104
|
if (this.config.dryRun) {
|
|
108581
109105
|
this.dryRunInterceptor = new DryRunInterceptor();
|
|
108582
109106
|
} else {
|
|
@@ -108901,8 +109425,51 @@ ${this.config.orgChartContext}` : this.config.orgChartContext;
|
|
|
108901
109425
|
}
|
|
108902
109426
|
]);
|
|
108903
109427
|
messages.push(...toolResultMessages);
|
|
109428
|
+
this.stuckDetector.recordToolCall(toolCall.name, toolCall.arguments);
|
|
109429
|
+
this.stuckDetector.recordResult(result2.isError ?? false);
|
|
109430
|
+
const stuckCheck = this.stuckDetector.check();
|
|
109431
|
+
if (stuckCheck.isStuck) {
|
|
109432
|
+
this.log.warn("Agent stuck detected", {
|
|
109433
|
+
reason: stuckCheck.reason,
|
|
109434
|
+
suggestion: stuckCheck.suggestedAction
|
|
109435
|
+
});
|
|
109436
|
+
messages.push({
|
|
109437
|
+
role: "user",
|
|
109438
|
+
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."}`
|
|
109439
|
+
});
|
|
109440
|
+
if (stuckCheck.reason === "timeout" || consecutiveStucks >= 2) {
|
|
109441
|
+
exitReason = "error";
|
|
109442
|
+
error = `Agent stuck: ${stuckCheck.reason} \u2014 ${stuckCheck.details ?? ""}`;
|
|
109443
|
+
break;
|
|
109444
|
+
}
|
|
109445
|
+
consecutiveStucks++;
|
|
109446
|
+
} else {
|
|
109447
|
+
consecutiveStucks = 0;
|
|
109448
|
+
}
|
|
108904
109449
|
}
|
|
108905
109450
|
if (exitReason === "aborted") break;
|
|
109451
|
+
if (exitReason === "error") break;
|
|
109452
|
+
this.contextRotator.recordMessage(response.content);
|
|
109453
|
+
const rotationCheck = this.contextRotator.shouldRotate();
|
|
109454
|
+
if (rotationCheck.rotate) {
|
|
109455
|
+
const summary = this.contextRotator.buildRotationSummary(messages);
|
|
109456
|
+
const systemMsg = messages[0];
|
|
109457
|
+
const recent = messages.slice(-3);
|
|
109458
|
+
messages.length = 0;
|
|
109459
|
+
messages.push(
|
|
109460
|
+
systemMsg,
|
|
109461
|
+
{
|
|
109462
|
+
role: "user",
|
|
109463
|
+
content: `[Context rotation \u2014 previous messages summarized]
|
|
109464
|
+
${summary}
|
|
109465
|
+
|
|
109466
|
+
Continue from where you left off.`
|
|
109467
|
+
},
|
|
109468
|
+
...recent
|
|
109469
|
+
);
|
|
109470
|
+
this.contextRotator.reset();
|
|
109471
|
+
this.log.info("Context rotated", { reason: rotationCheck.reason });
|
|
109472
|
+
}
|
|
108906
109473
|
}
|
|
108907
109474
|
if (iterations >= maxIterations && exitReason === "completed") {
|
|
108908
109475
|
exitReason = "max_iterations";
|
|
@@ -109144,6 +109711,9 @@ ${this.config.orgChartContext}` : this.config.orgChartContext;
|
|
|
109144
109711
|
let consecutiveFailures = 0;
|
|
109145
109712
|
let lastFailedToolName = "";
|
|
109146
109713
|
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
109714
|
+
let consecutiveStucks = 0;
|
|
109715
|
+
this.contextRotator.reset();
|
|
109716
|
+
this.stuckDetector.reset();
|
|
109147
109717
|
const messages = this.contextBuilder.buildMessages(
|
|
109148
109718
|
task.prompt,
|
|
109149
109719
|
this.config.initialMessages
|
|
@@ -109421,8 +109991,51 @@ ${this.config.orgChartContext}` : this.config.orgChartContext;
|
|
|
109421
109991
|
}
|
|
109422
109992
|
]);
|
|
109423
109993
|
messages.push(...toolResultMessages);
|
|
109994
|
+
this.stuckDetector.recordToolCall(toolCall.name, toolCall.arguments);
|
|
109995
|
+
this.stuckDetector.recordResult(result.isError ?? false);
|
|
109996
|
+
const stuckCheck = this.stuckDetector.check();
|
|
109997
|
+
if (stuckCheck.isStuck) {
|
|
109998
|
+
this.log.warn("Agent stuck detected (streaming)", {
|
|
109999
|
+
reason: stuckCheck.reason,
|
|
110000
|
+
suggestion: stuckCheck.suggestedAction
|
|
110001
|
+
});
|
|
110002
|
+
messages.push({
|
|
110003
|
+
role: "user",
|
|
110004
|
+
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."}`
|
|
110005
|
+
});
|
|
110006
|
+
if (stuckCheck.reason === "timeout" || consecutiveStucks >= 2) {
|
|
110007
|
+
exitReason = "error";
|
|
110008
|
+
error = `Agent stuck: ${stuckCheck.reason} \u2014 ${stuckCheck.details ?? ""}`;
|
|
110009
|
+
break;
|
|
110010
|
+
}
|
|
110011
|
+
consecutiveStucks++;
|
|
110012
|
+
} else {
|
|
110013
|
+
consecutiveStucks = 0;
|
|
110014
|
+
}
|
|
109424
110015
|
}
|
|
109425
110016
|
if (exitReason === "aborted") break;
|
|
110017
|
+
if (exitReason === "error") break;
|
|
110018
|
+
this.contextRotator.recordMessage(response.content);
|
|
110019
|
+
const rotationCheck = this.contextRotator.shouldRotate();
|
|
110020
|
+
if (rotationCheck.rotate) {
|
|
110021
|
+
const summary = this.contextRotator.buildRotationSummary(messages);
|
|
110022
|
+
const systemMsg = messages[0];
|
|
110023
|
+
const recent = messages.slice(-3);
|
|
110024
|
+
messages.length = 0;
|
|
110025
|
+
messages.push(
|
|
110026
|
+
systemMsg,
|
|
110027
|
+
{
|
|
110028
|
+
role: "user",
|
|
110029
|
+
content: `[Context rotation \u2014 previous messages summarized]
|
|
110030
|
+
${summary}
|
|
110031
|
+
|
|
110032
|
+
Continue from where you left off.`
|
|
110033
|
+
},
|
|
110034
|
+
...recent
|
|
110035
|
+
);
|
|
110036
|
+
this.contextRotator.reset();
|
|
110037
|
+
this.log.info("Context rotated (streaming)", { reason: rotationCheck.reason });
|
|
110038
|
+
}
|
|
109426
110039
|
}
|
|
109427
110040
|
if (iterations >= maxIterations && exitReason === "completed") {
|
|
109428
110041
|
exitReason = "max_iterations";
|
|
@@ -114276,121 +114889,6 @@ var init_knowledge = __esm({
|
|
|
114276
114889
|
}
|
|
114277
114890
|
});
|
|
114278
114891
|
|
|
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
114892
|
// ../agent/src/safety/circuit-breaker.ts
|
|
114395
114893
|
var CircuitBreaker;
|
|
114396
114894
|
var init_circuit_breaker = __esm({
|
|
@@ -114512,98 +115010,278 @@ var init_circuit_breaker = __esm({
|
|
|
114512
115010
|
}
|
|
114513
115011
|
});
|
|
114514
115012
|
|
|
114515
|
-
// ../agent/src/safety/
|
|
114516
|
-
var
|
|
114517
|
-
var
|
|
114518
|
-
"../agent/src/safety/
|
|
115013
|
+
// ../agent/src/safety/question-detector.ts
|
|
115014
|
+
var QuestionDetector;
|
|
115015
|
+
var init_question_detector = __esm({
|
|
115016
|
+
"../agent/src/safety/question-detector.ts"() {
|
|
114519
115017
|
"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);
|
|
115018
|
+
QuestionDetector = class {
|
|
115019
|
+
questionCount = 0;
|
|
115020
|
+
maxQuestionsBeforeCorrection;
|
|
115021
|
+
constructor(opts) {
|
|
115022
|
+
this.maxQuestionsBeforeCorrection = opts?.maxQuestions ?? 2;
|
|
114537
115023
|
}
|
|
114538
|
-
|
|
114539
|
-
|
|
114540
|
-
|
|
114541
|
-
|
|
114542
|
-
|
|
114543
|
-
|
|
114544
|
-
|
|
114545
|
-
|
|
115024
|
+
/**
|
|
115025
|
+
* Analyze an assistant message for question patterns.
|
|
115026
|
+
* Returns detection result with optional correction prompt.
|
|
115027
|
+
*/
|
|
115028
|
+
detect(assistantMessage) {
|
|
115029
|
+
const questions = this.extractQuestions(assistantMessage);
|
|
115030
|
+
if (questions.length === 0) {
|
|
115031
|
+
this.questionCount = 0;
|
|
115032
|
+
return { isQuestion: false, questions: [] };
|
|
114546
115033
|
}
|
|
114547
|
-
|
|
115034
|
+
this.questionCount++;
|
|
115035
|
+
if (this.questionCount >= this.maxQuestionsBeforeCorrection) {
|
|
114548
115036
|
return {
|
|
114549
|
-
|
|
114550
|
-
|
|
115037
|
+
isQuestion: true,
|
|
115038
|
+
questions,
|
|
115039
|
+
correctionPrompt: this.buildCorrectionPrompt(questions)
|
|
114551
115040
|
};
|
|
114552
115041
|
}
|
|
114553
|
-
return {
|
|
115042
|
+
return { isQuestion: true, questions };
|
|
115043
|
+
}
|
|
115044
|
+
extractQuestions(text7) {
|
|
115045
|
+
const questions = [];
|
|
115046
|
+
const questionLines = text7.split("\n").filter((line) => {
|
|
115047
|
+
const trimmed = line.trim();
|
|
115048
|
+
return trimmed.endsWith("?") && trimmed.length > 10;
|
|
115049
|
+
});
|
|
115050
|
+
questions.push(...questionLines.map((q) => q.trim()));
|
|
115051
|
+
const indirectPatterns = [
|
|
115052
|
+
/(?: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,
|
|
115053
|
+
/(?:voulez-vous|souhaitez-vous|préférez-vous|dois-je|faut-il|quel(?:le)? (?:est|sont)|pouvez-vous (?:me dire|confirmer|préciser))/gi
|
|
115054
|
+
];
|
|
115055
|
+
for (const pattern of indirectPatterns) {
|
|
115056
|
+
const matches = text7.match(pattern);
|
|
115057
|
+
if (matches) {
|
|
115058
|
+
questions.push(...matches.map((m) => m.trim()));
|
|
115059
|
+
}
|
|
115060
|
+
}
|
|
115061
|
+
return questions;
|
|
115062
|
+
}
|
|
115063
|
+
buildCorrectionPrompt(questions) {
|
|
115064
|
+
return `[System \u2014 Autonomous Mode Correction]
|
|
115065
|
+
You are in AUTONOMOUS mode. Do NOT ask questions \u2014 take action instead.
|
|
115066
|
+
|
|
115067
|
+
You asked: ${questions.slice(0, 3).map((q) => `"${q}"`).join(", ")}
|
|
115068
|
+
|
|
115069
|
+
Instead of asking:
|
|
115070
|
+
- Make a reasonable decision based on available information
|
|
115071
|
+
- If multiple options exist, choose the most common/safe one
|
|
115072
|
+
- If information is missing, use your tools to find it (web_search, file_read, etc.)
|
|
115073
|
+
- If truly blocked, document what you need and move on to the next task
|
|
115074
|
+
|
|
115075
|
+
DO NOT ask any more questions. Take action now.`;
|
|
115076
|
+
}
|
|
115077
|
+
/** Reset counter */
|
|
115078
|
+
reset() {
|
|
115079
|
+
this.questionCount = 0;
|
|
115080
|
+
}
|
|
115081
|
+
};
|
|
115082
|
+
}
|
|
115083
|
+
});
|
|
115084
|
+
|
|
115085
|
+
// ../agent/src/safety/completion-detector.ts
|
|
115086
|
+
var CompletionDetector;
|
|
115087
|
+
var init_completion_detector = __esm({
|
|
115088
|
+
"../agent/src/safety/completion-detector.ts"() {
|
|
115089
|
+
"use strict";
|
|
115090
|
+
CompletionDetector = class {
|
|
115091
|
+
noToolCallStreak = 0;
|
|
115092
|
+
minNoToolStreak;
|
|
115093
|
+
constructor(opts) {
|
|
115094
|
+
this.minNoToolStreak = opts?.minNoToolStreak ?? 2;
|
|
114554
115095
|
}
|
|
114555
|
-
// ── Rotation helpers ────────────────────────────────────────────────
|
|
114556
115096
|
/**
|
|
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.
|
|
115097
|
+
* Check if the agent has truly completed its task.
|
|
115098
|
+
* Requires both heuristic indicators and an explicit signal.
|
|
114566
115099
|
*/
|
|
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
|
-
);
|
|
115100
|
+
check(assistantMessage, hadToolCalls) {
|
|
115101
|
+
if (hadToolCalls) {
|
|
115102
|
+
this.noToolCallStreak = 0;
|
|
115103
|
+
} else {
|
|
115104
|
+
this.noToolCallStreak++;
|
|
114590
115105
|
}
|
|
114591
|
-
|
|
114592
|
-
|
|
115106
|
+
const heuristicScore = this.computeHeuristic(assistantMessage, hadToolCalls);
|
|
115107
|
+
const hasExplicitSignal = this.hasExplicitCompletionSignal(assistantMessage);
|
|
115108
|
+
const isComplete = heuristicScore >= 0.7 && hasExplicitSignal;
|
|
115109
|
+
return {
|
|
115110
|
+
isComplete,
|
|
115111
|
+
heuristicScore,
|
|
115112
|
+
hasExplicitSignal,
|
|
115113
|
+
reason: isComplete ? "Both heuristic and explicit signals indicate completion" : !hasExplicitSignal ? "No explicit completion signal found" : `Heuristic score too low (${heuristicScore.toFixed(2)})`
|
|
115114
|
+
};
|
|
115115
|
+
}
|
|
115116
|
+
computeHeuristic(message, hadToolCalls) {
|
|
115117
|
+
let score = 0;
|
|
115118
|
+
if (!hadToolCalls) score += 0.3;
|
|
115119
|
+
if (this.noToolCallStreak >= this.minNoToolStreak) score += 0.3;
|
|
115120
|
+
const conclusionPatterns = [
|
|
115121
|
+
/(?:in summary|en résumé|to summarize|pour résumer|in conclusion|en conclusion)/i,
|
|
115122
|
+
/(?:here are the (?:results|findings)|voici les (?:résultats|conclusions))/i,
|
|
115123
|
+
/(?:the (?:task|mission|objective|work) is (?:complete|done|finished))/i,
|
|
115124
|
+
/(?:la (?:tâche|mission) est (?:terminée|complète|finie))/i,
|
|
115125
|
+
/(?:I have (?:completed|finished|done)|j'ai (?:terminé|fini|complété))/i
|
|
115126
|
+
];
|
|
115127
|
+
const conclusionMatches = conclusionPatterns.filter((p8) => p8.test(message)).length;
|
|
115128
|
+
score += Math.min(conclusionMatches * 0.15, 0.4);
|
|
115129
|
+
return Math.min(score, 1);
|
|
115130
|
+
}
|
|
115131
|
+
hasExplicitCompletionSignal(message) {
|
|
115132
|
+
const explicitSignals = [
|
|
115133
|
+
/\[COMPLETE\]/i,
|
|
115134
|
+
/\[DONE\]/i,
|
|
115135
|
+
/\[TASK_COMPLETE\]/i,
|
|
115136
|
+
/\[MISSION_COMPLETE\]/i,
|
|
115137
|
+
/EXIT_SIGNAL:\s*COMPLETE/i,
|
|
115138
|
+
// JSON output with findings (structured output = done)
|
|
115139
|
+
/^\s*\[[\s\S]*"type"[\s\S]*"title"[\s\S]*"confidence"[\s\S]*\]\s*$/m,
|
|
115140
|
+
// Markdown report header (report = done)
|
|
115141
|
+
/^#\s+.*(?:Report|Rapport|Results|Résultats)/m
|
|
115142
|
+
];
|
|
115143
|
+
return explicitSignals.some((p8) => p8.test(message));
|
|
114593
115144
|
}
|
|
114594
|
-
|
|
114595
|
-
/** Reset counters after rotation. */
|
|
115145
|
+
/** Reset state */
|
|
114596
115146
|
reset() {
|
|
114597
|
-
this.
|
|
114598
|
-
this.estimatedTokens = 0;
|
|
115147
|
+
this.noToolCallStreak = 0;
|
|
114599
115148
|
}
|
|
114600
|
-
|
|
114601
|
-
|
|
115149
|
+
};
|
|
115150
|
+
}
|
|
115151
|
+
});
|
|
115152
|
+
|
|
115153
|
+
// ../agent/src/safety/handoff-generator.ts
|
|
115154
|
+
var HandoffGenerator;
|
|
115155
|
+
var init_handoff_generator = __esm({
|
|
115156
|
+
"../agent/src/safety/handoff-generator.ts"() {
|
|
115157
|
+
"use strict";
|
|
115158
|
+
HandoffGenerator = class {
|
|
115159
|
+
/**
|
|
115160
|
+
* Generate a handoff document from the agent's message history.
|
|
115161
|
+
* This is a heuristic extraction — no LLM call needed.
|
|
115162
|
+
*/
|
|
115163
|
+
generate(opts) {
|
|
115164
|
+
const { objective, messages, toolCalls, exitReason, costUsd } = opts;
|
|
115165
|
+
const accomplished = this.extractAccomplished(messages);
|
|
115166
|
+
const remaining = this.extractRemaining(messages, exitReason);
|
|
115167
|
+
const currentState = this.buildCurrentState(toolCalls);
|
|
115168
|
+
const decisions = this.extractDecisions(messages);
|
|
115169
|
+
const errors = toolCalls.filter((tc) => tc.isError).map((tc) => ({ error: `${tc.name}: ${tc.result.substring(0, 200)}`, resolution: "Attempted" }));
|
|
115170
|
+
const toolUsage = {};
|
|
115171
|
+
for (const tc of toolCalls) {
|
|
115172
|
+
toolUsage[tc.name] = (toolUsage[tc.name] || 0) + 1;
|
|
115173
|
+
}
|
|
115174
|
+
const nextSteps = this.suggestNextSteps(remaining, errors, exitReason);
|
|
114602
115175
|
return {
|
|
114603
|
-
|
|
114604
|
-
|
|
115176
|
+
objective,
|
|
115177
|
+
accomplished,
|
|
115178
|
+
remaining,
|
|
115179
|
+
currentState,
|
|
115180
|
+
decisions,
|
|
115181
|
+
errors,
|
|
115182
|
+
nextSteps,
|
|
115183
|
+
toolUsage,
|
|
115184
|
+
costUsd,
|
|
115185
|
+
timestamp: Date.now()
|
|
114605
115186
|
};
|
|
114606
115187
|
}
|
|
115188
|
+
/** Convert handoff to Markdown for storage/display */
|
|
115189
|
+
toMarkdown(handoff) {
|
|
115190
|
+
const sections = [
|
|
115191
|
+
`# Session Handoff`,
|
|
115192
|
+
``,
|
|
115193
|
+
`**Objective:** ${handoff.objective}`,
|
|
115194
|
+
`**Cost:** $${handoff.costUsd.toFixed(4)}`,
|
|
115195
|
+
`**Date:** ${new Date(handoff.timestamp).toISOString()}`,
|
|
115196
|
+
``,
|
|
115197
|
+
`## Accomplished`,
|
|
115198
|
+
...handoff.accomplished.map((a) => `- ${a}`),
|
|
115199
|
+
``,
|
|
115200
|
+
`## Remaining`,
|
|
115201
|
+
...handoff.remaining.map((r) => `- ${r}`),
|
|
115202
|
+
``,
|
|
115203
|
+
`## Current State`,
|
|
115204
|
+
handoff.currentState,
|
|
115205
|
+
``,
|
|
115206
|
+
`## Decisions`,
|
|
115207
|
+
...handoff.decisions.map((d) => `- **${d.decision}**: ${d.reason}`),
|
|
115208
|
+
``,
|
|
115209
|
+
`## Errors (${handoff.errors.length})`,
|
|
115210
|
+
...handoff.errors.map((e) => `- ${e.error}`),
|
|
115211
|
+
``,
|
|
115212
|
+
`## Next Steps`,
|
|
115213
|
+
...handoff.nextSteps.map((s) => `- ${s}`),
|
|
115214
|
+
``,
|
|
115215
|
+
`## Tool Usage`,
|
|
115216
|
+
...Object.entries(handoff.toolUsage).map(([name, count]) => `- ${name}: ${count}x`)
|
|
115217
|
+
];
|
|
115218
|
+
return sections.join("\n");
|
|
115219
|
+
}
|
|
115220
|
+
extractAccomplished(messages) {
|
|
115221
|
+
const accomplished = [];
|
|
115222
|
+
for (const msg of messages) {
|
|
115223
|
+
if (msg.role !== "assistant") continue;
|
|
115224
|
+
const lines = msg.content.split("\n");
|
|
115225
|
+
for (const line of lines) {
|
|
115226
|
+
if (/(?:✅|done|completed|finished|created|wrote|found|discovered|built|implemented)/i.test(line) && line.length > 10 && line.length < 200) {
|
|
115227
|
+
accomplished.push(line.replace(/^[-*•]\s*/, "").trim());
|
|
115228
|
+
}
|
|
115229
|
+
}
|
|
115230
|
+
}
|
|
115231
|
+
return accomplished.slice(0, 10);
|
|
115232
|
+
}
|
|
115233
|
+
extractRemaining(messages, exitReason) {
|
|
115234
|
+
const remaining = [];
|
|
115235
|
+
if (exitReason === "max_iterations") remaining.push("Max iterations reached \u2014 work may be incomplete");
|
|
115236
|
+
if (exitReason === "budget_exceeded") remaining.push("Budget exhausted \u2014 work may be incomplete");
|
|
115237
|
+
if (exitReason === "aborted") remaining.push("Execution was aborted");
|
|
115238
|
+
const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
|
|
115239
|
+
if (lastAssistant) {
|
|
115240
|
+
const todoPattern = /(?:todo|remaining|still need|next|à faire|reste)/i;
|
|
115241
|
+
const lines = lastAssistant.content.split("\n");
|
|
115242
|
+
for (const line of lines) {
|
|
115243
|
+
if (todoPattern.test(line) && line.length > 10 && line.length < 200) {
|
|
115244
|
+
remaining.push(line.replace(/^[-*•]\s*/, "").trim());
|
|
115245
|
+
}
|
|
115246
|
+
}
|
|
115247
|
+
}
|
|
115248
|
+
return remaining.slice(0, 10);
|
|
115249
|
+
}
|
|
115250
|
+
buildCurrentState(toolCalls) {
|
|
115251
|
+
const filesWritten = toolCalls.filter((tc) => tc.name === "file_write").map((tc) => String(tc.args.path || "")).filter(Boolean);
|
|
115252
|
+
const commandsRun = toolCalls.filter((tc) => tc.name === "shell_exec").map((tc) => String(tc.args.command || "").substring(0, 100)).filter(Boolean);
|
|
115253
|
+
let state = "";
|
|
115254
|
+
if (filesWritten.length > 0) state += `Files modified: ${filesWritten.join(", ")}
|
|
115255
|
+
`;
|
|
115256
|
+
if (commandsRun.length > 0) state += `Commands run: ${commandsRun.slice(-5).join("; ")}`;
|
|
115257
|
+
return state || "No filesystem changes detected.";
|
|
115258
|
+
}
|
|
115259
|
+
extractDecisions(messages) {
|
|
115260
|
+
const decisions = [];
|
|
115261
|
+
for (const msg of messages) {
|
|
115262
|
+
if (msg.role !== "assistant") continue;
|
|
115263
|
+
const patterns = [
|
|
115264
|
+
/I (?:chose|decided|selected|went with|picked) (.{10,80}) (?:because|since|as|due to) (.{10,120})/gi,
|
|
115265
|
+
/(?:j'ai choisi|j'ai décidé|j'ai opté pour) (.{10,80}) (?:car|parce que|puisque) (.{10,120})/gi
|
|
115266
|
+
];
|
|
115267
|
+
for (const pattern of patterns) {
|
|
115268
|
+
let match;
|
|
115269
|
+
while ((match = pattern.exec(msg.content)) !== null && decisions.length < 5) {
|
|
115270
|
+
decisions.push({ decision: match[1].trim(), reason: match[2].trim() });
|
|
115271
|
+
}
|
|
115272
|
+
}
|
|
115273
|
+
}
|
|
115274
|
+
return decisions;
|
|
115275
|
+
}
|
|
115276
|
+
suggestNextSteps(remaining, errors, exitReason) {
|
|
115277
|
+
const steps = [];
|
|
115278
|
+
if (remaining.length > 0) steps.push(`Continue with: ${remaining[0]}`);
|
|
115279
|
+
if (errors.length > 0) steps.push(`Investigate ${errors.length} error(s) from previous session`);
|
|
115280
|
+
if (exitReason === "budget_exceeded") steps.push("Consider increasing budget for next run");
|
|
115281
|
+
if (exitReason === "max_iterations") steps.push("Consider increasing max iterations or simplifying the objective");
|
|
115282
|
+
if (steps.length === 0) steps.push("Review results and plan next action");
|
|
115283
|
+
return steps;
|
|
115284
|
+
}
|
|
114607
115285
|
};
|
|
114608
115286
|
}
|
|
114609
115287
|
});
|
|
@@ -114615,6 +115293,9 @@ var init_safety = __esm({
|
|
|
114615
115293
|
init_stuck_detector();
|
|
114616
115294
|
init_circuit_breaker();
|
|
114617
115295
|
init_context_rotator();
|
|
115296
|
+
init_question_detector();
|
|
115297
|
+
init_completion_detector();
|
|
115298
|
+
init_handoff_generator();
|
|
114618
115299
|
}
|
|
114619
115300
|
});
|
|
114620
115301
|
|
|
@@ -116951,7 +117632,7 @@ var init_inventory = __esm({
|
|
|
116951
117632
|
/** Find similar past missions for planning. */
|
|
116952
117633
|
async findSimilarMissions(objective) {
|
|
116953
117634
|
try {
|
|
116954
|
-
return this.store.searchMissions(objective, 3);
|
|
117635
|
+
return this.store.searchMissions(objective, { limit: 3 });
|
|
116955
117636
|
} catch {
|
|
116956
117637
|
return [];
|
|
116957
117638
|
}
|
|
@@ -117834,6 +118515,7 @@ __export(src_exports3, {
|
|
|
117834
118515
|
CircuitBreaker: () => CircuitBreaker,
|
|
117835
118516
|
ClaudeAdapter: () => ClaudeAdapter,
|
|
117836
118517
|
CodeChunker: () => CodeChunker,
|
|
118518
|
+
CompletionDetector: () => CompletionDetector,
|
|
117837
118519
|
ContextBuilder: () => ContextBuilder,
|
|
117838
118520
|
ContextRotator: () => ContextRotator,
|
|
117839
118521
|
DockerDeployer: () => DockerDeployer,
|
|
@@ -117847,11 +118529,13 @@ __export(src_exports3, {
|
|
|
117847
118529
|
GeminiAdapter: () => GeminiAdapter,
|
|
117848
118530
|
GeminiEmbeddingProvider: () => GeminiEmbeddingProvider,
|
|
117849
118531
|
GrokAdapter: () => GrokAdapter,
|
|
118532
|
+
HandoffGenerator: () => HandoffGenerator,
|
|
117850
118533
|
HotSwapAdapter: () => HotSwapAdapter,
|
|
117851
118534
|
IntentAnalyzer: () => IntentAnalyzer,
|
|
117852
118535
|
InventoryManager: () => InventoryManager,
|
|
117853
118536
|
K8sExecutor: () => K8sExecutor,
|
|
117854
118537
|
KnowledgeGraph: () => KnowledgeGraph,
|
|
118538
|
+
LayeredMemory: () => LayeredMemory,
|
|
117855
118539
|
LocalEmbeddings: () => LocalEmbeddings,
|
|
117856
118540
|
LocalSandbox: () => LocalSandbox,
|
|
117857
118541
|
McpFactory: () => McpFactory,
|
|
@@ -117874,6 +118558,7 @@ __export(src_exports3, {
|
|
|
117874
118558
|
PluginManifestSchema: () => PluginManifestSchema,
|
|
117875
118559
|
PluginSandbox: () => PluginSandbox,
|
|
117876
118560
|
ProcessSandbox: () => ProcessSandbox,
|
|
118561
|
+
QuestionDetector: () => QuestionDetector,
|
|
117877
118562
|
RagIndexer: () => RagIndexer,
|
|
117878
118563
|
RagSearch: () => RagSearch,
|
|
117879
118564
|
ReasonLogger: () => ReasonLogger,
|
|
@@ -149749,7 +150434,7 @@ var init_admin2 = __esm({
|
|
|
149749
150434
|
"../server/src/routes/admin.ts"() {
|
|
149750
150435
|
"use strict";
|
|
149751
150436
|
init_rate_limit();
|
|
149752
|
-
SERVER_VERSION3 = "
|
|
150437
|
+
SERVER_VERSION3 = "2.9.0";
|
|
149753
150438
|
startTime3 = Date.now();
|
|
149754
150439
|
}
|
|
149755
150440
|
});
|
|
@@ -151886,6 +152571,36 @@ function missionRoutes(store, logger) {
|
|
|
151886
152571
|
next(err);
|
|
151887
152572
|
}
|
|
151888
152573
|
});
|
|
152574
|
+
router.get("/search", async (req, res, next) => {
|
|
152575
|
+
try {
|
|
152576
|
+
const q = req.query.q || "";
|
|
152577
|
+
const type = req.query.type;
|
|
152578
|
+
const status = req.query.status;
|
|
152579
|
+
const from2 = req.query.from;
|
|
152580
|
+
const to = req.query.to;
|
|
152581
|
+
const limit = req.query.limit ? parseInt(req.query.limit, 10) : 20;
|
|
152582
|
+
const results = store.searchMissions(q, { type, status, from: from2, to, limit });
|
|
152583
|
+
res.json({
|
|
152584
|
+
data: results,
|
|
152585
|
+
meta: {
|
|
152586
|
+
count: results.length,
|
|
152587
|
+
query: q,
|
|
152588
|
+
filters: { type, status, from: from2, to }
|
|
152589
|
+
}
|
|
152590
|
+
});
|
|
152591
|
+
} catch (err) {
|
|
152592
|
+
next(err);
|
|
152593
|
+
}
|
|
152594
|
+
});
|
|
152595
|
+
router.get("/stats", async (req, res, next) => {
|
|
152596
|
+
try {
|
|
152597
|
+
const tenantId = req.tenantId || "default";
|
|
152598
|
+
const stats = store.getMissionStats(tenantId);
|
|
152599
|
+
res.json({ data: stats });
|
|
152600
|
+
} catch (err) {
|
|
152601
|
+
next(err);
|
|
152602
|
+
}
|
|
152603
|
+
});
|
|
151889
152604
|
router.get("/:id", async (req, res, next) => {
|
|
151890
152605
|
try {
|
|
151891
152606
|
const mission = store.getMission(req.params.id);
|
|
@@ -153046,7 +153761,7 @@ var VERSION2, DATA_DIR3, TELEMETRY_ID_FILE, TELEMETRY_LOG_FILE, DEFAULT_FLUSH_TH
|
|
|
153046
153761
|
var init_telemetry2 = __esm({
|
|
153047
153762
|
"../server/src/services/telemetry.ts"() {
|
|
153048
153763
|
"use strict";
|
|
153049
|
-
VERSION2 = "
|
|
153764
|
+
VERSION2 = "2.9.0";
|
|
153050
153765
|
DATA_DIR3 = join23(homedir8(), ".nestor");
|
|
153051
153766
|
TELEMETRY_ID_FILE = join23(DATA_DIR3, "telemetry-id");
|
|
153052
153767
|
TELEMETRY_LOG_FILE = join23(DATA_DIR3, "telemetry.jsonl");
|
|
@@ -157365,7 +158080,7 @@ var init_src6 = __esm({
|
|
|
157365
158080
|
await this._handle.listen();
|
|
157366
158081
|
const authMode = config2.apiKey ? "API key" : "open (no auth)";
|
|
157367
158082
|
console.log(`
|
|
157368
|
-
Nestor Server
|
|
158083
|
+
Nestor Server v2.9.0`);
|
|
157369
158084
|
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
158085
|
console.log(` HTTP : http://${this._host}:${this._port}`);
|
|
157371
158086
|
console.log(` WS : ws://${this._host}:${this._port}/ws`);
|
|
@@ -167127,7 +167842,7 @@ var init_server = __esm({
|
|
|
167127
167842
|
MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
167128
167843
|
SERVER_INFO = {
|
|
167129
167844
|
name: "nestor",
|
|
167130
|
-
version: "2.
|
|
167845
|
+
version: "2.9.0"
|
|
167131
167846
|
};
|
|
167132
167847
|
SERVER_CAPABILITIES = {
|
|
167133
167848
|
tools: { listChanged: false },
|
|
@@ -167571,7 +168286,7 @@ function printWelcome() {
|
|
|
167571
168286
|
console.log(chalk11.cyan(` | .\` | | _| \\__ \\ | | | (_) | | / _ \\__ \\ | __ |`));
|
|
167572
168287
|
console.log(chalk11.cyan(` |_|\\_| |___| |___/ |_| \\___/ |_|_\\ (_) |___/ |_||_|`));
|
|
167573
168288
|
console.log("");
|
|
167574
|
-
console.log(chalk11.dim(" Interactive Shell \u2014
|
|
168289
|
+
console.log(chalk11.dim(" Interactive Shell \u2014 v2.9.0"));
|
|
167575
168290
|
console.log(chalk11.dim(" Type /help for commands, /exit to quit."));
|
|
167576
168291
|
console.log(chalk11.dim(" Multiline: end a line with \\ or use ``` code blocks."));
|
|
167577
168292
|
console.log("");
|
|
@@ -169490,7 +170205,7 @@ var init_shell = __esm({
|
|
|
169490
170205
|
|
|
169491
170206
|
// src/index.ts
|
|
169492
170207
|
import { Command } from "commander";
|
|
169493
|
-
import { existsSync as
|
|
170208
|
+
import { existsSync as existsSync26, readFileSync as readFileSync26 } from "node:fs";
|
|
169494
170209
|
import { join as join29 } from "node:path";
|
|
169495
170210
|
import { homedir as homedir12 } from "node:os";
|
|
169496
170211
|
|
|
@@ -169511,7 +170226,7 @@ var BANNER = `
|
|
|
169511
170226
|
function registerStartCommand(program2) {
|
|
169512
170227
|
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
170228
|
console.log(chalk.cyan(BANNER));
|
|
169514
|
-
console.log(chalk.dim(`
|
|
170229
|
+
console.log(chalk.dim(` v2.9.0
|
|
169515
170230
|
`));
|
|
169516
170231
|
let config2 = readConfigFile();
|
|
169517
170232
|
if (!config2) {
|
|
@@ -171203,7 +171918,7 @@ async function startForeground() {
|
|
|
171203
171918
|
console.log(chalk12.cyan(` | .\` | | _| \\__ \\ | | | (_) | | / _ \\__ \\ | __ |`));
|
|
171204
171919
|
console.log(chalk12.cyan(` |_|\\_| |___| |___/ |_| \\___/ |_|_\\ (_) |___/ |_||_|`));
|
|
171205
171920
|
console.log("");
|
|
171206
|
-
console.log(chalk12.dim(" Daemon Mode \u2014
|
|
171921
|
+
console.log(chalk12.dim(" Daemon Mode \u2014 v2.9.0"));
|
|
171207
171922
|
console.log(chalk12.dim(` PID: ${process.pid}`));
|
|
171208
171923
|
console.log(chalk12.dim(` Log: ${LOG_FILE}`));
|
|
171209
171924
|
console.log("");
|
|
@@ -171566,9 +172281,9 @@ var DaemonRunner = class {
|
|
|
171566
172281
|
try {
|
|
171567
172282
|
const store = await getStore();
|
|
171568
172283
|
try {
|
|
171569
|
-
const { randomUUID:
|
|
172284
|
+
const { randomUUID: randomUUID52 } = await import("node:crypto");
|
|
171570
172285
|
store.createRun({
|
|
171571
|
-
id:
|
|
172286
|
+
id: randomUUID52(),
|
|
171572
172287
|
workflowId: `agent:${agentId}`,
|
|
171573
172288
|
workflowVersion: 1,
|
|
171574
172289
|
status: result.exitReason === "completed" ? "completed" : "failed",
|
|
@@ -171604,9 +172319,9 @@ var DaemonRunner = class {
|
|
|
171604
172319
|
try {
|
|
171605
172320
|
const store = await getStore();
|
|
171606
172321
|
try {
|
|
171607
|
-
const { randomUUID:
|
|
172322
|
+
const { randomUUID: randomUUID52 } = await import("node:crypto");
|
|
171608
172323
|
store.createRun({
|
|
171609
|
-
id:
|
|
172324
|
+
id: randomUUID52(),
|
|
171610
172325
|
workflowId: `agent:${agentId}`,
|
|
171611
172326
|
workflowVersion: 1,
|
|
171612
172327
|
status: "failed",
|
|
@@ -173349,11 +174064,289 @@ function registerGuardrailCommand(program2) {
|
|
|
173349
174064
|
});
|
|
173350
174065
|
}
|
|
173351
174066
|
|
|
174067
|
+
// src/commands/loop.ts
|
|
174068
|
+
init_db();
|
|
174069
|
+
init_config2();
|
|
174070
|
+
import chalk22 from "chalk";
|
|
174071
|
+
import { randomUUID as randomUUID51 } from "node:crypto";
|
|
174072
|
+
import { readFileSync as readFileSync25, existsSync as existsSync25 } from "node:fs";
|
|
174073
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
174074
|
+
function runShellCommand(cmd, cwd) {
|
|
174075
|
+
try {
|
|
174076
|
+
const stdout = execSync3(cmd, {
|
|
174077
|
+
cwd,
|
|
174078
|
+
encoding: "utf-8",
|
|
174079
|
+
timeout: 6e4,
|
|
174080
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
174081
|
+
});
|
|
174082
|
+
return { stdout: stdout.trim(), exitCode: 0 };
|
|
174083
|
+
} catch (err) {
|
|
174084
|
+
const execErr = err;
|
|
174085
|
+
return {
|
|
174086
|
+
stdout: (execErr.stdout ?? "").toString().trim(),
|
|
174087
|
+
exitCode: execErr.status ?? 1
|
|
174088
|
+
};
|
|
174089
|
+
}
|
|
174090
|
+
}
|
|
174091
|
+
function getGitDiff(cwd) {
|
|
174092
|
+
try {
|
|
174093
|
+
const diff = execSync3("git diff --stat", { cwd, encoding: "utf-8", timeout: 1e4 });
|
|
174094
|
+
return diff.trim() || "(no changes)";
|
|
174095
|
+
} catch {
|
|
174096
|
+
return "(git not available)";
|
|
174097
|
+
}
|
|
174098
|
+
}
|
|
174099
|
+
function getTestOutput(verifyCmd, cwd) {
|
|
174100
|
+
const result = runShellCommand(verifyCmd, cwd);
|
|
174101
|
+
const maxLen = 2e3;
|
|
174102
|
+
const output = result.stdout.length > maxLen ? result.stdout.slice(-maxLen) : result.stdout;
|
|
174103
|
+
return output;
|
|
174104
|
+
}
|
|
174105
|
+
function buildIterationPrompt(opts) {
|
|
174106
|
+
const parts = [];
|
|
174107
|
+
parts.push(`# Objective (iteration ${opts.iteration}/${opts.maxIterations})`);
|
|
174108
|
+
parts.push("");
|
|
174109
|
+
parts.push(opts.spec);
|
|
174110
|
+
parts.push("");
|
|
174111
|
+
if (opts.previousResults.length > 0) {
|
|
174112
|
+
parts.push("## Previous iterations summary");
|
|
174113
|
+
for (const r of opts.previousResults) {
|
|
174114
|
+
const status = r.specMet ? "PASSED" : "FAILED";
|
|
174115
|
+
parts.push(`- Iteration ${r.iteration}: ${status} (${r.exitReason}) \u2014 ${r.output.slice(0, 200)}`);
|
|
174116
|
+
}
|
|
174117
|
+
parts.push("");
|
|
174118
|
+
}
|
|
174119
|
+
if (opts.gitDiff !== "(no changes)" && opts.gitDiff !== "(git not available)") {
|
|
174120
|
+
parts.push("## Current state (git diff --stat)");
|
|
174121
|
+
parts.push("```");
|
|
174122
|
+
parts.push(opts.gitDiff);
|
|
174123
|
+
parts.push("```");
|
|
174124
|
+
parts.push("");
|
|
174125
|
+
}
|
|
174126
|
+
if (opts.verifyOutput) {
|
|
174127
|
+
parts.push("## Verification output (from last check)");
|
|
174128
|
+
parts.push("```");
|
|
174129
|
+
parts.push(opts.verifyOutput);
|
|
174130
|
+
parts.push("```");
|
|
174131
|
+
parts.push("");
|
|
174132
|
+
}
|
|
174133
|
+
parts.push("## Instructions");
|
|
174134
|
+
parts.push("Analyze the current state and work towards achieving the objective.");
|
|
174135
|
+
parts.push("If previous iterations failed, try a DIFFERENT approach.");
|
|
174136
|
+
parts.push("When done, commit your changes with a descriptive message.");
|
|
174137
|
+
return parts.join("\n");
|
|
174138
|
+
}
|
|
174139
|
+
function registerLoopCommand(program2) {
|
|
174140
|
+
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) => {
|
|
174141
|
+
let spec = opts.spec;
|
|
174142
|
+
if (!spec && opts.specFile) {
|
|
174143
|
+
if (!existsSync25(opts.specFile)) {
|
|
174144
|
+
console.error(chalk22.red(`Spec file not found: ${opts.specFile}`));
|
|
174145
|
+
process.exit(1);
|
|
174146
|
+
}
|
|
174147
|
+
spec = readFileSync25(opts.specFile, "utf-8").trim();
|
|
174148
|
+
}
|
|
174149
|
+
if (!spec) {
|
|
174150
|
+
console.error(chalk22.red("Must provide --spec or --spec-file"));
|
|
174151
|
+
process.exit(1);
|
|
174152
|
+
}
|
|
174153
|
+
const maxIterations = parseInt(opts.maxIterations, 10);
|
|
174154
|
+
const totalBudget = parseFloat(opts.budget);
|
|
174155
|
+
const cwd = opts.cwd;
|
|
174156
|
+
console.log(chalk22.cyan("\n Ralph Fresh-Context Loop"));
|
|
174157
|
+
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"));
|
|
174158
|
+
console.log(chalk22.white(" Spec:"), spec.slice(0, 100) + (spec.length > 100 ? "..." : ""));
|
|
174159
|
+
console.log(chalk22.white(" Agent:"), opts.agent);
|
|
174160
|
+
console.log(chalk22.white(" Max iterations:"), maxIterations);
|
|
174161
|
+
console.log(chalk22.white(" Budget:"), `$${totalBudget}`);
|
|
174162
|
+
if (opts.verify) {
|
|
174163
|
+
console.log(chalk22.white(" Verify cmd:"), opts.verify);
|
|
174164
|
+
}
|
|
174165
|
+
console.log("");
|
|
174166
|
+
let agentModule;
|
|
174167
|
+
try {
|
|
174168
|
+
agentModule = await Promise.resolve().then(() => (init_src5(), src_exports3));
|
|
174169
|
+
} catch {
|
|
174170
|
+
console.error(chalk22.red("Failed to import @nestor/agent. Is it installed?"));
|
|
174171
|
+
process.exit(1);
|
|
174172
|
+
}
|
|
174173
|
+
const store = await getStore();
|
|
174174
|
+
let resolvedAgent;
|
|
174175
|
+
try {
|
|
174176
|
+
const agents = store.listAgents();
|
|
174177
|
+
resolvedAgent = agents.find((a) => a.name === opts.agent);
|
|
174178
|
+
} catch {
|
|
174179
|
+
}
|
|
174180
|
+
const agent = resolvedAgent ?? {
|
|
174181
|
+
id: randomUUID51(),
|
|
174182
|
+
name: opts.agent,
|
|
174183
|
+
adapterType: "claude",
|
|
174184
|
+
adapterConfig: {
|
|
174185
|
+
model: "claude-sonnet-4-20250514",
|
|
174186
|
+
instructions: "You are an expert software engineer. Complete the given specification precisely."
|
|
174187
|
+
},
|
|
174188
|
+
trustLevel: "community",
|
|
174189
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
174190
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
174191
|
+
};
|
|
174192
|
+
let router;
|
|
174193
|
+
try {
|
|
174194
|
+
router = await agentModule.createDefaultRouter();
|
|
174195
|
+
} catch {
|
|
174196
|
+
console.error(chalk22.red("Could not initialize LLM adapter. Is the API key configured?"));
|
|
174197
|
+
process.exit(1);
|
|
174198
|
+
}
|
|
174199
|
+
const summary = {
|
|
174200
|
+
totalIterations: 0,
|
|
174201
|
+
totalCostUsd: 0,
|
|
174202
|
+
totalTokens: 0,
|
|
174203
|
+
totalDurationMs: 0,
|
|
174204
|
+
specMet: false,
|
|
174205
|
+
iterationResults: []
|
|
174206
|
+
};
|
|
174207
|
+
for (let i = 1; i <= maxIterations; i++) {
|
|
174208
|
+
if (summary.totalCostUsd >= totalBudget) {
|
|
174209
|
+
console.log(chalk22.yellow(`
|
|
174210
|
+
Budget exhausted ($${summary.totalCostUsd.toFixed(4)} / $${totalBudget}). Stopping.`));
|
|
174211
|
+
break;
|
|
174212
|
+
}
|
|
174213
|
+
const remainingBudget = totalBudget - summary.totalCostUsd;
|
|
174214
|
+
console.log(chalk22.cyan(`
|
|
174215
|
+
\u2500\u2500 Iteration ${i}/${maxIterations} `) + chalk22.dim(`(budget remaining: $${remainingBudget.toFixed(4)}) \u2500\u2500`));
|
|
174216
|
+
if (opts.verify && i > 1) {
|
|
174217
|
+
const verifyResult = runShellCommand(opts.verify, cwd);
|
|
174218
|
+
if (verifyResult.exitCode === 0) {
|
|
174219
|
+
console.log(chalk22.green(" Specification met! Verify command exited 0."));
|
|
174220
|
+
summary.specMet = true;
|
|
174221
|
+
break;
|
|
174222
|
+
}
|
|
174223
|
+
}
|
|
174224
|
+
const gitDiff = getGitDiff(cwd);
|
|
174225
|
+
const verifyOutput = opts.verify ? getTestOutput(opts.verify, cwd) : "";
|
|
174226
|
+
const prompt = buildIterationPrompt({
|
|
174227
|
+
spec,
|
|
174228
|
+
iteration: i,
|
|
174229
|
+
maxIterations,
|
|
174230
|
+
previousResults: summary.iterationResults,
|
|
174231
|
+
gitDiff,
|
|
174232
|
+
verifyOutput
|
|
174233
|
+
});
|
|
174234
|
+
const modelId = agent.adapterConfig.model ?? "claude-sonnet-4-20250514";
|
|
174235
|
+
const adapter = router.resolve(modelId);
|
|
174236
|
+
const events = new agentModule.RuntimeEventBus();
|
|
174237
|
+
const tools = new agentModule.ToolRegistry();
|
|
174238
|
+
agentModule.registerBuiltinTools(tools);
|
|
174239
|
+
const runtime = new agentModule.AgentRuntime({
|
|
174240
|
+
adapter,
|
|
174241
|
+
tools,
|
|
174242
|
+
toolExecutor: new agentModule.ToolExecutor(),
|
|
174243
|
+
role: {
|
|
174244
|
+
name: agent.name,
|
|
174245
|
+
description: agent.description ?? "Loop agent",
|
|
174246
|
+
instructions: agent.adapterConfig.instructions ?? "You are an expert software engineer.",
|
|
174247
|
+
constraints: []
|
|
174248
|
+
},
|
|
174249
|
+
workingDir: cwd,
|
|
174250
|
+
events,
|
|
174251
|
+
maxCostUsd: remainingBudget,
|
|
174252
|
+
maxIterations: 50,
|
|
174253
|
+
db: store,
|
|
174254
|
+
modelRouter: router,
|
|
174255
|
+
// Enable context rotation within each iteration too
|
|
174256
|
+
contextRotation: { maxMessages: 40, maxTokensEstimate: 8e4 },
|
|
174257
|
+
stuckDetection: { maxRepeatedCalls: 3, maxConsecutiveErrors: 3 }
|
|
174258
|
+
});
|
|
174259
|
+
const taskId = randomUUID51();
|
|
174260
|
+
let iterOutput = "";
|
|
174261
|
+
try {
|
|
174262
|
+
for await (const event of runtime.runStreaming({
|
|
174263
|
+
prompt,
|
|
174264
|
+
agentId: agent.id,
|
|
174265
|
+
taskId
|
|
174266
|
+
})) {
|
|
174267
|
+
switch (event.type) {
|
|
174268
|
+
case "token":
|
|
174269
|
+
process.stdout.write(event.text);
|
|
174270
|
+
iterOutput += event.text;
|
|
174271
|
+
break;
|
|
174272
|
+
case "tool_start":
|
|
174273
|
+
console.log(chalk22.dim(`
|
|
174274
|
+
[tool] ${event.name}`));
|
|
174275
|
+
break;
|
|
174276
|
+
case "tool_result":
|
|
174277
|
+
if (!event.success) {
|
|
174278
|
+
console.log(chalk22.red(` [tool error] ${event.name}: ${event.result.slice(0, 100)}`));
|
|
174279
|
+
}
|
|
174280
|
+
break;
|
|
174281
|
+
case "error":
|
|
174282
|
+
console.log(chalk22.red(`
|
|
174283
|
+
[error] ${event.message}`));
|
|
174284
|
+
break;
|
|
174285
|
+
case "done": {
|
|
174286
|
+
const r = event.result;
|
|
174287
|
+
const iterResult = {
|
|
174288
|
+
iteration: i,
|
|
174289
|
+
output: r.output || iterOutput,
|
|
174290
|
+
exitReason: r.exitReason,
|
|
174291
|
+
costUsd: r.usage.estimatedCostUsd,
|
|
174292
|
+
tokens: r.usage.totalTokens,
|
|
174293
|
+
durationMs: r.durationMs,
|
|
174294
|
+
specMet: false
|
|
174295
|
+
};
|
|
174296
|
+
summary.totalCostUsd += r.usage.estimatedCostUsd;
|
|
174297
|
+
summary.totalTokens += r.usage.totalTokens;
|
|
174298
|
+
summary.totalDurationMs += r.durationMs;
|
|
174299
|
+
summary.totalIterations = i;
|
|
174300
|
+
if (opts.verify) {
|
|
174301
|
+
const postVerify = runShellCommand(opts.verify, cwd);
|
|
174302
|
+
iterResult.specMet = postVerify.exitCode === 0;
|
|
174303
|
+
}
|
|
174304
|
+
summary.iterationResults.push(iterResult);
|
|
174305
|
+
console.log("");
|
|
174306
|
+
console.log(chalk22.dim(` [${r.exitReason}] ${r.usage.totalTokens} tokens, $${r.usage.estimatedCostUsd.toFixed(4)}, ${(r.durationMs / 1e3).toFixed(1)}s`));
|
|
174307
|
+
if (iterResult.specMet) {
|
|
174308
|
+
console.log(chalk22.green(" Specification met!"));
|
|
174309
|
+
summary.specMet = true;
|
|
174310
|
+
}
|
|
174311
|
+
break;
|
|
174312
|
+
}
|
|
174313
|
+
}
|
|
174314
|
+
}
|
|
174315
|
+
} catch (err) {
|
|
174316
|
+
console.error(chalk22.red(`
|
|
174317
|
+
Iteration ${i} crashed: ${err instanceof Error ? err.message : String(err)}`));
|
|
174318
|
+
summary.iterationResults.push({
|
|
174319
|
+
iteration: i,
|
|
174320
|
+
output: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
174321
|
+
exitReason: "error",
|
|
174322
|
+
costUsd: 0,
|
|
174323
|
+
tokens: 0,
|
|
174324
|
+
durationMs: 0,
|
|
174325
|
+
specMet: false
|
|
174326
|
+
});
|
|
174327
|
+
}
|
|
174328
|
+
if (summary.specMet) break;
|
|
174329
|
+
}
|
|
174330
|
+
console.log(chalk22.cyan("\n \u2500\u2500 Loop Summary \u2500\u2500"));
|
|
174331
|
+
console.log(chalk22.white(" Iterations:"), summary.totalIterations);
|
|
174332
|
+
console.log(chalk22.white(" Total cost:"), `$${summary.totalCostUsd.toFixed(4)}`);
|
|
174333
|
+
console.log(chalk22.white(" Total tokens:"), summary.totalTokens.toLocaleString());
|
|
174334
|
+
console.log(chalk22.white(" Total time:"), `${(summary.totalDurationMs / 1e3).toFixed(1)}s`);
|
|
174335
|
+
console.log(
|
|
174336
|
+
chalk22.white(" Spec met:"),
|
|
174337
|
+
summary.specMet ? chalk22.green("YES") : chalk22.red("NO")
|
|
174338
|
+
);
|
|
174339
|
+
console.log("");
|
|
174340
|
+
await store.close();
|
|
174341
|
+
process.exit(summary.specMet ? 0 : 1);
|
|
174342
|
+
});
|
|
174343
|
+
}
|
|
174344
|
+
|
|
173352
174345
|
// src/index.ts
|
|
173353
174346
|
function checkBetaAccess() {
|
|
173354
174347
|
const licenseFile = join29(homedir12(), ".nestor", "license.key");
|
|
173355
|
-
if (!
|
|
173356
|
-
const key =
|
|
174348
|
+
if (!existsSync26(licenseFile)) return false;
|
|
174349
|
+
const key = readFileSync26(licenseFile, "utf-8").trim();
|
|
173357
174350
|
return /^NESTOR-BETA-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(key);
|
|
173358
174351
|
}
|
|
173359
174352
|
var command2 = process.argv[2];
|
|
@@ -173373,7 +174366,7 @@ if (command2 && !["--help", "-h", "--version", "-V", "install"].includes(command
|
|
|
173373
174366
|
}
|
|
173374
174367
|
}
|
|
173375
174368
|
var program = new Command();
|
|
173376
|
-
program.name("nestor-sh").description("Nestor AI Agent Platform \u2014 orchestrate, secure and monitor AI agents").version("2.
|
|
174369
|
+
program.name("nestor-sh").description("Nestor AI Agent Platform \u2014 orchestrate, secure and monitor AI agents").version("2.9.0");
|
|
173377
174370
|
registerStartCommand(program);
|
|
173378
174371
|
registerInstallCommand(program);
|
|
173379
174372
|
registerAgentCommand(program);
|
|
@@ -173393,6 +174386,7 @@ registerTemplateCommand(program);
|
|
|
173393
174386
|
registerRagCommand(program);
|
|
173394
174387
|
registerTestCommand(program);
|
|
173395
174388
|
registerGuardrailCommand(program);
|
|
174389
|
+
registerLoopCommand(program);
|
|
173396
174390
|
var args = process.argv.slice(2);
|
|
173397
174391
|
if (args.length === 0) {
|
|
173398
174392
|
Promise.resolve().then(() => (init_shell(), shell_exports)).then(({ registerShellCommand: _ }) => {
|